Merge lp:~gmb/launchpad/ajax-dupefinder-has-risen-from-the-grave into lp:launchpad

Proposed by Graham Binns
Status: Merged
Approved by: Gavin Panella
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~gmb/launchpad/ajax-dupefinder-has-risen-from-the-grave
Merge into: lp:launchpad
Prerequisite: lp:~gmb/launchpad/prince-of-ajax-dupefinding-bug-358510
Diff against target: 838 lines (+351/-135)
10 files modified
lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js (+71/-57)
lib/lp/bugs/browser/bugtarget.py (+58/-20)
lib/lp/bugs/browser/configure.zcml (+7/-0)
lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+19/-0)
lib/lp/bugs/interfaces/bug.py (+9/-2)
lib/lp/bugs/templates/bugtarget-filebug-inline-form.pt (+11/-0)
lib/lp/bugs/templates/bugtarget-filebug-search.pt (+103/-33)
lib/lp/bugs/templates/bugtarget-filebug-show-similar.pt (+7/-2)
lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt (+2/-2)
lib/lp/bugs/templates/bugtarget-macros-filebug.pt (+64/-19)
To merge this branch: bzr merge lp:~gmb/launchpad/ajax-dupefinder-has-risen-from-the-grave
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Abstain
Gavin Panella (community) code js Approve
Michael Nelson (community) ui Approve
Review via email: mp+15599@code.launchpad.net

Commit message

The +filebug dupe finder should now work in-line asynchronously and in-line for Products.

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

This branch does the final connecting up of JS to make the Asynchronous Dupefinder stuff work.

How to test this branch (UI):

 1. Go to launchpad.dev/firefox/+filebug
 2. Enter a simple title (say, 'a') and hit "next"
 3. You should get a list of possible duplicates for that bug.
 4. You should be able to interact with those dupes in the same way as you always do (subscribe, mark as affecting you, etc).
 5. You should also be able to file a new bug if you need to in the same way as you do currently.

Issues that I'm aware of at this stage:

 1. The animations seem to happen at the wrong time, so slide_ins get played after the element has already been rendered as closed. I'm looking into this.
 2. The way that the dupes appear in the page isn't exactly pretty. I'm open to suggestions about how this should work.

Revision history for this message
Michael Nelson (michael.nelson) wrote :

> This branch does the final connecting up of JS to make the Asynchronous
> Dupefinder stuff work.

This is great! I've got three points below that I think are worth addressing - see what you think.

>
> How to test this branch (UI):
>
> 1. Go to launchpad.dev/firefox/+filebug
> 2. Enter a simple title (say, 'a') and hit "next"
> 3. You should get a list of possible duplicates for that bug.
> 4. You should be able to interact with those dupes in the same way as you
> always do (subscribe, mark as affecting you, etc).
> 5. You should also be able to file a new bug if you need to in the same way
> as you do currently.
>
> Issues that I'm aware of at this stage:
>
> 1. The animations seem to happen at the wrong time, so slide_ins get played
> after the element has already been rendered as closed. I'm looking into this.

OK - at first I thought this was a feature :-) (as I try not to read the MP before trying out the feature).

> 2. The way that the dupes appear in the page isn't exactly pretty. I'm open
> to suggestions about how this should work.

It would be nice to see them slide in... once you have the results, add them to the dom, but hidden, and then slide out the div containing them all?

My first thought is that the phrase: 'Is "a" one of these bugs?' seems strange. Even with a proper summary, 'Is "A bunch of soyuz source pages are missing headings" one of these bugs?' seems strange. Do you know the history of that decision? My guess would be that, since the summary entered by the user *wasn't* displayed anywhere else once they had searched, it needed to be included on the page somewhere. But now with your changes here, that the summary and input box remain on the page (at least for the JS version), I think we could just say something like 'Do any of the following bugs describe the bug you are trying to report?'. What do you think? It's great that they can now still see their search and update it if desired!

Secondly, currently in your branch it seems that you cannot have the 'No, I need to report a new bug' section and one of the other potential duplicates open at the same time. For example, as a person I:

1. enter a summary and hit Next,
2. briefly read the titles, but nothing rings a bell, so I click 'No, I need to report a new bug",
3. I start adding the further information, and then realise, hey I want to check one of those bugs above, as it actually could be the same,
4. I click on the expander and zap, my new bug description disappears.

Of course, the info I entered is still there, and I'll see it again if I click again on "No, I need to report a new bug", but I don't see any reason for it disappearing in the first place? (with less justification, the same goes in reverse, closing the expanded dups when I click on 'No, I need to report a new bug.').

Third, just an implementation detail, but hitting Enter while the focus in the summary field doesn't filter - it re-loads the page, which I'm assuming is not what you want.

Thanks for resurrecting this work! It'll be a great improvement :)

review: Needs Information (ui)
Revision history for this message
Graham Binns (gmb) wrote :
Download full text (4.4 KiB)

On Thu, Dec 03, 2009 at 11:22:46AM -0000, Michael Nelson wrote:
> Review: Needs Information ui
> > This branch does the final connecting up of JS to make the
> > Asynchronous Dupefinder stuff work.
>
> This is great! I've got three points below that I think are worth
> addressing - see what you think.
>
> >
> > How to test this branch (UI):
> >
> > 1. Go to launchpad.dev/firefox/+filebug 2. Enter a simple title
> > (say, 'a') and hit "next" 3. You should get a list of possible
> > duplicates for that bug. 4. You should be able to interact with
> > those dupes in the same way as you always do (subscribe, mark as
> > affecting you, etc). 5. You should also be able to file a new bug
> > if you need to in the same way as you do currently.
> >
> > Issues that I'm aware of at this stage:
> >
> > 1. The animations seem to happen at the wrong time, so slide_ins
> > get played after the element has already been rendered as closed.
> > I'm looking into this.
>
> OK - at first I thought this was a feature :-) (as I try not to read
> the MP before trying out the feature).
>

I've tried to fix this by making the animation duration as short as
possible but that doesn't work terribly well. I think that I can fix it
using some event jiggery-pokery, and I'm still working on that (see next
comment).

>
> > 2. The way that the dupes appear in the page isn't exactly pretty.
> > I'm open to suggestions about how this should work.
>
> It would be nice to see them slide in... once you have the results,
> add them to the dom, but hidden, and then slide out the div containing
> them all?

I'm still working on getting this right. Please feel free to read the
rest of my response and I'll get back to you about this and the above
item.

> My first thought is that the phrase: 'Is "a" one of these bugs?' seems
> strange. Even with a proper summary, 'Is "A bunch of soyuz source
> pages are missing headings" one of these bugs?' seems strange. Do you
> know the history of that decision? My guess would be that, since the
> summary entered by the user *wasn't* displayed anywhere else once they
> had searched, it needed to be included on the page somewhere. But now
> with your changes here, that the summary and input box remain on the
> page (at least for the JS version), I think we could just say
> something like 'Do any of the following bugs describe the bug you are
> trying to report?'. What do you think? It's great that they can now
> still see their search and update it if desired!

Right, I agree. That text is a hold-over, so I've updated the view and
the templates to make it a bit more meaningful in an inline dupe list.
It'll remain the same should someone use the non-ajax version of the
page, though.

> Secondly, currently in your branch it seems that you cannot have the
> 'No, I need to report a new bug' section and one of the other
> potential duplicates open at the same time. For example, as a person
> I:
>
> 1. enter a summary and hit Next, 2. briefly read the titles, but
> nothing rings a bell, so I click 'No, I need to report a new bug", 3.
> I start adding the further information, and then realise, hey I want
> to check one of those bugs ab...

Read more...

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (6.3 KiB)

> On Thu, Dec 03, 2009 at 11:22:46AM -0000, Michael Nelson wrote:
> > Review: Needs Information ui
> > > This branch does the final connecting up of JS to make the
> > > Asynchronous Dupefinder stuff work.
> >
> > This is great! I've got three points below that I think are worth
> > addressing - see what you think.
> >
> > >
> > > How to test this branch (UI):
> > >
> > > 1. Go to launchpad.dev/firefox/+filebug 2. Enter a simple title
> > > (say, 'a') and hit "next" 3. You should get a list of possible
> > > duplicates for that bug. 4. You should be able to interact with
> > > those dupes in the same way as you always do (subscribe, mark as
> > > affecting you, etc). 5. You should also be able to file a new bug
> > > if you need to in the same way as you do currently.
> > >
> > > Issues that I'm aware of at this stage:
> > >
> > > 1. The animations seem to happen at the wrong time, so slide_ins
> > > get played after the element has already been rendered as closed.
> > > I'm looking into this.
> >
> > OK - at first I thought this was a feature :-) (as I try not to read
> > the MP before trying out the feature).
> >
>
> I've tried to fix this by making the animation duration as short as
> possible but that doesn't work terribly well. I think that I can fix it
> using some event jiggery-pokery, and I'm still working on that (see next
> comment).
>

I think this is great as it is currently.

My initial comment above was related to when you initially search, and the results appear expanded and then slide in quickly. I wasn't sure why there was any animation there at all (rather than just displaying the closed items) - or whether that was the actual issue you were referring to, and if so, why it is not simple to ensure there is no animation triggered there (as the closed state should just be a css class?)

But as I said, I actually thought it was a feature (bit of bling ;) ).

> >
> > > 2. The way that the dupes appear in the page isn't exactly pretty.
> > > I'm open to suggestions about how this should work.
> >
> > It would be nice to see them slide in... once you have the results,
> > add them to the dom, but hidden, and then slide out the div containing
> > them all?
>
> I'm still working on getting this right. Please feel free to read the
> rest of my response and I'll get back to you about this and the above
> item.

Great, but don't worry if there's no time, it's just a 'would be nice' ;).

>
> > My first thought is that the phrase: 'Is "a" one of these bugs?' seems
> > strange. Even with a proper summary, 'Is "A bunch of soyuz source
> > pages are missing headings" one of these bugs?' seems strange. Do you
> > know the history of that decision? My guess would be that, since the
> > summary entered by the user *wasn't* displayed anywhere else once they
> > had searched, it needed to be included on the page somewhere. But now
> > with your changes here, that the summary and input box remain on the
> > page (at least for the JS version), I think we could just say
> > something like 'Do any of the following bugs describe the bug you are
> > trying to report?'. What do you think? It's great that they can now
> > still see their...

Read more...

review: Approve (ui)
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (6.9 KiB)

Hi Graham,

Not a lot to say. I have a few suggestions, but that's all they
are. Looking good :)

Gavin.

> === modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
> --- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-08 10:57:44 +0000
> +++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-09 12:13:43 +0000
> @@ -81,13 +81,6 @@
> * @param e The Event triggering this function.
> */
> function show_bug_reporting_form(e) {
> - if (Y.Lang.isValue(bug_already_reported_expanders)) {
> - // Collapse any duplicate-details divs.
> - Y.each(bug_already_reported_expanders, function(expander) {
> - collapse_bug_details(expander);
> - });
> - }
> -
> // If the bug reporting form is in a hidden container, as it is on
> // the AJAX dupe search, show it.
> var filebug_form_container = Y.get('#filebug-form-container');

Salgado, in his YUI() -> LPS branch, changed a lot of Y.get() calls to
Y.one(). I guess that means things break non-silently when the targets
don't exist. Might be a good idea here too.

> @@ -117,8 +110,37 @@
> * display them in-line.
> */
> function search_for_and_display_dupes() {
> - function show_failure_message() {
> - Y.get('#possible-duplicates').set(INNER_HTML, 'FAIL');
> + function show_failure_message(transaction_id, response, args) {
> + // If the request failed due to a timeout, display a message
> + // explaining how the user may be able to work around it.
> + var error_message = '';
> + if (response.status == 503) {
> + // We treat 503 (service unavailable) as a timeout because
> + // that's what timeouts in LP return.
> + error_message =
> + "<p>Searching for your bug in Launchpad took too long. " +
> + "Try reducing the number of words in the summary " +
> + "field and click \"Check again\" to retry your search. " +
> + "Alternatively, you can enter the details of your bug " +
> + "below.</p>";
> + } else {
> + // Any other error code gets a generic message.
> + error_message =
> + "<p>An error occured whilst trying to find bugs matching " +
> + "the summary you entered. Click \"Check again\" to retry " +
> + "your search. Alternatively, you can enter the " +
> + "details of your bug below.</p>";
> + }
> +
> + Y.get('#possible-duplicates').set(INNER_HTML, error_message);

It works, but I think using innerHTML is bad except for a few
cases. Consider having error_message as just a message, then create a
<p> node and .set('text', error_message). Don't stress too much
though.

> +
> + Y.get('#spinner').setStyle(DISPLAY, NONE);

Consider using the "unseen" CSS class instead, and remove it to make
the spinner visible. Doesn't matter too much though. I'm sure I've
heard arguments that classes are better than manipulating style
directly, but I can't summon them right now.

> + show_bug_reporting_form();
> +
> + Y.get(Y.DOM.byId('field.t...

Read more...

review: Approve (js)
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (5.2 KiB)

Hi,

The first part of the code review. Only a suggestion about using a
view argument to canonical_url(). Other than that, it all looks good.

Gavin.

> === modified file 'lib/lp/bugs/browser/bugtarget.py'
> --- lib/lp/bugs/browser/bugtarget.py 2009-11-26 11:32:54 +0000
> +++ lib/lp/bugs/browser/bugtarget.py 2009-12-09 12:13:43 +0000
> @@ -56,6 +56,7 @@
> from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
> from canonical.launchpad.interfaces.temporaryblobstorage import (
> ITemporaryStorageManager)
> +from canonical.launchpad.webapp import urlappend
> from canonical.launchpad.webapp.breadcrumb import Breadcrumb
> from canonical.launchpad.webapp.interfaces import ILaunchBag, NotFoundError
> from lp.bugs.interfaces.bug import (
> @@ -368,7 +369,6 @@
>
> def validate(self, data):
> """Make sure the package name, if provided, exists in the distro."""
> -
> # The comment field is only required if filing a new bug.
> if self.submit_bug_action.submitted():
> comment = data.get('comment')
> @@ -700,6 +700,26 @@
> """Override this method in base classes to show the filebug form."""
> raise NotImplementedError
>
> + @property
> + def inline_filebug_form_url(self):
> + """The URL to the inline filebug form.
> +
> + If a token was passed to this view, it will be be passed through
> + to the inline bug filing form via the returned URL.
> + """
> + url = urlappend(canonical_url(self.context), '+filebug-inline-form')

No need for urlappend here; pass view="+filebug-inline-form" to
canonical_url().

> + if self.extra_data_token is not None:
> + url = urlappend(url, self.extra_data_token)
> + return url
> +
> + @property
> + def duplicate_search_url(self):
> + """The URL to the inline duplicate search view."""
> + url = urlappend(canonical_url(self.context), '+filebug-show-similar')

Again with the view argument to canonical_url().

> + if self.extra_data_token is not None:
> + url = urlappend(url, self.extra_data_token)
> + return url
> +
> def publishTraverse(self, request, name):
> """See IBrowserPublisher."""
> if self.extra_data_token is not None:
> @@ -831,6 +851,11 @@
> return guidelines
>
>
> +class FileBugInlineFormView(FileBugViewBase):
> + """A browser view for displaying the inline filebug form."""
> + schema = IBugAddForm
> +
> +
> class FileBugAdvancedView(FileBugViewBase):
> """Browser view for filing a bug.
>
> @@ -851,7 +876,14 @@
> """
> schema = IBugAddForm
>
> + # XXX: Brad Bollenbach 2006-10-04: This assignment to actions is a
> + # hack to make the action decorator Just Work across inheritance.
> + actions = FileBugViewBase.actions
> + custom_widget('title', TextWidget, displayWidth=40)
> + custom_widget('tags', BugTagsWidget)
> +
> _MATCHING_BUGS_LIMIT = 10
> + show_summary_in_results = False
>
> @property
> def search_context(self):
> @@ -894,22 +926,37 @@
>
> return matching_bugs
>
> + @property
> + def show_duplicate_list(self)...

Read more...

review: Abstain (code)
Revision history for this message
Graham Binns (gmb) wrote :
Download full text (7.4 KiB)

> > === modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
> > --- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-08 10:57:44 +0000
> > +++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-09 12:13:43 +0000
> > @@ -81,13 +81,6 @@
> > * @param e The Event triggering this function.
> > */
> > function show_bug_reporting_form(e) {
> > - if (Y.Lang.isValue(bug_already_reported_expanders)) {
> > - // Collapse any duplicate-details divs.
> > - Y.each(bug_already_reported_expanders, function(expander) {
> > - collapse_bug_details(expander);
> > - });
> > - }
> > -
> > // If the bug reporting form is in a hidden container, as it is on
> > // the AJAX dupe search, show it.
> > var filebug_form_container = Y.get('#filebug-form-container');
>
> Salgado, in his YUI() -> LPS branch, changed a lot of Y.get() calls to
> Y.one(). I guess that means things break non-silently when the targets
> don't exist. Might be a good idea here too.
>

Right. Fixed.

> > @@ -117,8 +110,37 @@
> > * display them in-line.
> > */
> > function search_for_and_display_dupes() {
> > - function show_failure_message() {
> > - Y.get('#possible-duplicates').set(INNER_HTML, 'FAIL');
> > + function show_failure_message(transaction_id, response, args) {
> > + // If the request failed due to a timeout, display a message
> > + // explaining how the user may be able to work around it.
> > + var error_message = '';
> > + if (response.status == 503) {
> > + // We treat 503 (service unavailable) as a timeout because
> > + // that's what timeouts in LP return.
> > + error_message =
> > + "<p>Searching for your bug in Launchpad took too long. " +
> > + "Try reducing the number of words in the summary " +
> > + "field and click \"Check again\" to retry your search. " +
> > + "Alternatively, you can enter the details of your bug " +
> > + "below.</p>";
> > + } else {
> > + // Any other error code gets a generic message.
> > + error_message =
> > + "<p>An error occured whilst trying to find bugs matching " +
> > + "the summary you entered. Click \"Check again\" to retry " +
> > + "your search. Alternatively, you can enter the " +
> > + "details of your bug below.</p>";
> > + }
> > +
> > + Y.get('#possible-duplicates').set(INNER_HTML, error_message);
>
> It works, but I think using innerHTML is bad except for a few
> cases. Consider having error_message as just a message, then create a
> <p> node and .set('text', error_message). Don't stress too much
> though.
>

I agree. Fixed.

> > +
> > + Y.get('#spinner').setStyle(DISPLAY, NONE);
>
> Consider using the "unseen" CSS class instead, and remove it to make
> the spinner visible. Doesn't matter too much though. I'm sure I've
> heard arguments that classes are better than manipulating style
> directly, but I can't summon them right now.
> o

I've heard similar ...

Read more...

Revision history for this message
Graham Binns (gmb) wrote :

> > === modified file 'lib/lp/bugs/browser/bugtarget.py'
> > --- lib/lp/bugs/browser/bugtarget.py 2009-11-26 11:32:54 +0000
> > +++ lib/lp/bugs/browser/bugtarget.py 2009-12-09 12:13:43 +0000
> > @@ -56,6 +56,7 @@
> > from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
> > from canonical.launchpad.interfaces.temporaryblobstorage import (
> > ITemporaryStorageManager)
> > +from canonical.launchpad.webapp import urlappend
> > from canonical.launchpad.webapp.breadcrumb import Breadcrumb
> > from canonical.launchpad.webapp.interfaces import ILaunchBag, NotFoundError
> > from lp.bugs.interfaces.bug import (
> > @@ -368,7 +369,6 @@
> >
> > def validate(self, data):
> > """Make sure the package name, if provided, exists in the distro."""
> > -
> > # The comment field is only required if filing a new bug.
> > if self.submit_bug_action.submitted():
> > comment = data.get('comment')
> > @@ -700,6 +700,26 @@
> > """Override this method in base classes to show the filebug form."""
> > raise NotImplementedError
> >
> > + @property
> > + def inline_filebug_form_url(self):
> > + """The URL to the inline filebug form.
> > +
> > + If a token was passed to this view, it will be be passed through
> > + to the inline bug filing form via the returned URL.
> > + """
> > + url = urlappend(canonical_url(self.context), '+filebug-inline-form')
>
> No need for urlappend here; pass view="+filebug-inline-form" to
> canonical_url().
>

Right. No idea what I was smoking when I wrote that.

> > + if self.extra_data_token is not None:
> > + url = urlappend(url, self.extra_data_token)
> > + return url
> > +
> > + @property
> > + def duplicate_search_url(self):
> > + """The URL to the inline duplicate search view."""
> > + url = urlappend(canonical_url(self.context), '+filebug-show-similar')
>
> Again with the view argument to canonical_url().
>

Yep.

Revision history for this message
Graham Binns (gmb) wrote :
Download full text (5.8 KiB)

Oops. Incremental diff for the first two parts of the code / js reviews:

=== modified file 'lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js'
--- lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-09 13:05:28 +0000
+++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-09 13:20:27 +0000
@@ -16,7 +16,8 @@
     INNER_HTML = 'innerHTML',
     LAZR_CLOSED = 'lazr-closed',
     NONE = 'none',
- SRC = 'src';
+ SRC = 'src',
+ UNSEEN = 'unseen';

 var bugs = Y.namespace('bugs');

@@ -118,34 +119,36 @@
             // We treat 503 (service unavailable) as a timeout because
             // that's what timeouts in LP return.
             error_message =
- "<p>Searching for your bug in Launchpad took too long. " +
+ "Searching for your bug in Launchpad took too long. " +
                 "Try reducing the number of words in the summary " +
                 "field and click \"Check again\" to retry your search. " +
                 "Alternatively, you can enter the details of your bug " +
- "below.</p>";
+ "below.";
         } else {
             // Any other error code gets a generic message.
             error_message =
- "<p>An error occured whilst trying to find bugs matching " +
+ "An error occured whilst trying to find bugs matching " +
                 "the summary you entered. Click \"Check again\" to retry " +
                 "your search. Alternatively, you can enter the " +
- "details of your bug below.</p>";
+ "details of your bug below.";
         }

- Y.one('#possible-duplicates').set(INNER_HTML, error_message);
+ var error_node = Y.Node.create('<p></p>');
+ error_node.set('text', error_message);
+ Y.one('#possible-duplicates').appendChild(error_node);

- Y.one('#spinner').setStyle(DISPLAY, NONE);
+ Y.one('#spinner').addClass(UNSEEN);
         show_bug_reporting_form();

         Y.one(Y.DOM.byId('field.title')).set(
             'value', search_field.get('value'));
         search_button.set('value', 'Check again');
- search_button.setStyle(DISPLAY, INLINE);
+ search_button.removeClass(UNSEEN);
     }

     function on_success(transaction_id, response, args) {
         // Hide the spinner and show the duplicates.
- Y.one('#spinner').setStyle(DISPLAY, NONE);
+ Y.one('#spinner').addClass(UNSEEN);

         var duplicate_div = Y.one('#possible-duplicates');
         duplicate_div.set(INNER_HTML, response.responseText);
@@ -163,13 +166,13 @@

         // Copy the value from the search field into the title field
         // on the filebug form.
- Y.one(Y.DOM.byId('field.title')).set(
+ Y.one('#bug_reporting_form input[name=field.title]').set(
             'value', search_field.get('value'));

         // Finally, change the label on the search button and show it
         // again.
         search_button.set('value', 'Check again');
- search_button.setStyle(DISPLAY, INLINE);
+ search_button.removeClass(UNSEEN);
     }

     var search_term = encodeURI(search_fi...

Read more...

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (16.8 KiB)

The rest of the code review. A few minor things, but nothing serious. Rock on :) Get this landed, people want to buy you beer :)

> === modified file 'lib/lp/bugs/browser/configure.zcml'
> --- lib/lp/bugs/browser/configure.zcml 2009-12-09 12:13:11 +0000
> +++ lib/lp/bugs/browser/configure.zcml 2009-12-09 12:13:43 +0000
> @@ -95,6 +95,13 @@
> facet="bugs"
> permission="launchpad.AnyPerson"/>
> <browser:page
> + name="+filebug-inline-form"
> + for="lp.bugs.interfaces.bugtarget.IBugTarget"
> + class="lp.bugs.browser.bugtarget.FilebugShowSimilarBugsView"
> + template="../templates/bugtarget-filebug-inline-form.pt"
> + facet="bugs"
> + permission="launchpad.AnyPerson"/>
> + <browser:page
> name="+manage-official-tags"
> for="lp.bugs.interfaces.bugtarget.IOfficialBugTagTargetRestricted"
> class="lp.bugs.browser.bugtarget.OfficialBugTagsManageView"
>
> === modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
> --- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-11-26 09:18:22 +0000
> +++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-09 12:13:43 +0000
> @@ -43,6 +43,25 @@
> u'Test description.'
>
>
> +== URLs to additional FileBug elements ==
> +
> +FileBugViewBase provides properties that return the URLs of further
> +useful parts of the +filebug process.
> +
> +The inline_filebug_form_url property returns the URL of the inline
> +filebug form so that it may be loaded asynchronously.
> +
> + >>> print filebug_view.inline_filebug_form_url
> + http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-inline-form
> +
> +Similarly, the duplicate_search_url property returns the base URL for
> +the duplicate search view, which can be used to load the list of
> +possible duplicates for a bug asynchronously.
> +
> + >>> print filebug_view.duplicate_search_url
> + http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-show-similar
> +
> +
> == Adding extra info to filed bugs ==
>
> It's possible for bug reporting tools to upload a file with debug
>
> === modified file 'lib/lp/bugs/interfaces/bug.py'
> --- lib/lp/bugs/interfaces/bug.py 2009-11-26 09:18:22 +0000
> +++ lib/lp/bugs/interfaces/bug.py 2009-12-09 12:13:43 +0000
> @@ -26,6 +26,7 @@
> from zope.interface import Interface, Attribute
> from zope.schema import (
> Bool, Bytes, Choice, Datetime, Int, List, Object, Text, TextLine)
> +from zope.schema.vocabulary import SimpleVocabulary
> from zope.security.interfaces import Unauthorized
>
> from canonical.launchpad import _
> @@ -789,6 +790,11 @@
> "A sequence of IBugTaskDeltas, one IBugTaskDelta or None.")
>
>
> +# A simple vocabulary for the subscribe_to_existing_bug form field.
> +SUBSCRIBE_TO_BUG_VOCABULARY = SimpleVocabulary.fromItems(
> + [('yes', True), ('no', False)])
> +
> +
> class IBugAddForm(IBug):
> """Information we need to create a bug"""
> id = Int(title=_("Bug #"), required=False)
> @@ -842,8 +848,9 @@
> assignee = PublicPersonChoice(
> title=_('Assign to'), required=False,
> ...

review: Approve (code)
Revision history for this message
Graham Binns (gmb) wrote :

> > > + <td colspan="2">
> > > + <p>
> > > + Please describe the bug in a few words, for example,
> > "weather
> > > + applet crashes on logout":
> > > + </p>
> >
> > I don't think the <p></p> is needed here (i.e. <td>s can have contain
> > inline content)... unless it affects layout.
> >

It does. The paragraphing is deliberate.

> > > + </td>
> > > + </tr>
> > > + <tal:title_widget tal:define="widget nocall:view/widgets/title">
> > > + <tal:comment replace="nothing">
> > > + The desire to have more control over the styling of this
> > widget
> > > + prevents us from using the widget_row macro here.
> > > + </tal:comment>
> > > + <tr>
> > > + <td tal:define="field_name widget/context/__name__;
> > > + error python:view.getFieldError(field_name);
> > > + error_class python:error and 'error' or
> > None;"
> >
> > I think you can say this now:
> >
> > ('error' if error else None)
> >
> > (Woohoo!)

Indeed. Fixed.

> > > + <p>
> > > + <a id="filebug-form-url"
> > > + tal:attributes="href view/inline_filebug_form_url"
> > > + style="display: none;"></a>
> > > + <a id="duplicate-search-url"
> > > + tal:attributes="href view/duplicate_search_url"
> > > + style="display: none;"></a>
> > > + </p>
> >
> > Maybe it's simpler to put the display:none on the <p>? I think empty
> > paragraphs still influence layout.

Fixed.

Revision history for this message
Gavin Panella (allenap) :
review: Approve (code js)
Revision history for this message
Edwin Grubbs (edwin-grubbs) :
review: Abstain (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-09 17:25:34 +0000
3+++ lib/canonical/launchpad/javascript/bugs/filebug-dupefinder.js 2009-12-09 17:25:37 +0000
4@@ -16,7 +16,8 @@
5 INNER_HTML = 'innerHTML',
6 LAZR_CLOSED = 'lazr-closed',
7 NONE = 'none',
8- SRC = 'src';
9+ SRC = 'src',
10+ UNSEEN = 'unseen';
11
12 var bugs = Y.namespace('bugs');
13
14@@ -81,32 +82,25 @@
15 * @param e The Event triggering this function.
16 */
17 function show_bug_reporting_form(e) {
18- if (Y.Lang.isValue(bug_already_reported_expanders)) {
19- // Collapse any duplicate-details divs.
20- Y.each(bug_already_reported_expanders, function(expander) {
21- collapse_bug_details(expander);
22- });
23- }
24-
25 // If the bug reporting form is in a hidden container, as it is on
26 // the AJAX dupe search, show it.
27- var filebug_form_container = Y.get('#filebug-form-container');
28+ var filebug_form_container = Y.one('#filebug-form-container');
29 if (Y.Lang.isValue(filebug_form_container)) {
30 filebug_form_container.setStyle(DISPLAY, BLOCK);
31 }
32
33 // Show the bug reporting form.
34- var bug_reporting_form = Y.get('#bug_reporting_form');
35+ var bug_reporting_form = Y.one('#bug_reporting_form');
36 bug_reporting_form.setStyle(DISPLAY, BLOCK);
37
38- Y.get(Y.DOM.byId('field.actions.submit_bug')).focus();
39+ Y.one(Y.DOM.byId('field.actions.submit_bug')).focus();
40
41 // Focus the relevant elements of the form based on
42 // whether the package drop-down is displayed.
43 var bugtarget_package_btn = Y.one(
44 Y.DOM.byId('field.bugtarget.option.package'));
45 if (Y.Lang.isValue(bugtarget_package_btn)) {
46- Y.get(Y.DOM.byId('field.bugtarget.package')).focus();
47+ Y.one(Y.DOM.byId('field.bugtarget.package')).focus();
48 } else {
49 Y.one(Y.DOM.byId('field.comment')).focus();
50 }
51@@ -117,56 +111,80 @@
52 * display them in-line.
53 */
54 function search_for_and_display_dupes() {
55- function show_failure_message() {
56- Y.get('#possible-duplicates').set(INNER_HTML, 'FAIL');
57+ function show_failure_message(transaction_id, response, args) {
58+ // If the request failed due to a timeout, display a message
59+ // explaining how the user may be able to work around it.
60+ var error_message = '';
61+ if (response.status == 503) {
62+ // We treat 503 (service unavailable) as a timeout because
63+ // that's what timeouts in LP return.
64+ error_message =
65+ "Searching for your bug in Launchpad took too long. " +
66+ "Try reducing the number of words in the summary " +
67+ "field and click \"Check again\" to retry your search. " +
68+ "Alternatively, you can enter the details of your bug " +
69+ "below.";
70+ } else {
71+ // Any other error code gets a generic message.
72+ error_message =
73+ "An error occured whilst trying to find bugs matching " +
74+ "the summary you entered. Click \"Check again\" to retry " +
75+ "your search. Alternatively, you can enter the " +
76+ "details of your bug below.";
77+ }
78+
79+ var error_node = Y.Node.create('<p></p>');
80+ error_node.set('text', error_message);
81+ Y.one('#possible-duplicates').appendChild(error_node);
82+
83+ Y.one('#spinner').addClass(UNSEEN);
84+ show_bug_reporting_form();
85+
86+ Y.one(Y.DOM.byId('field.title')).set(
87+ 'value', search_field.get('value'));
88+ search_button.set('value', 'Check again');
89+ search_button.removeClass(UNSEEN);
90 }
91
92 function on_success(transaction_id, response, args) {
93 // Hide the spinner and show the duplicates.
94- Y.get('#spinner').setStyle(DISPLAY, NONE);
95+ Y.one('#spinner').addClass(UNSEEN);
96
97- var duplicate_div = Y.get('#possible-duplicates');
98+ var duplicate_div = Y.one('#possible-duplicates');
99 duplicate_div.set(INNER_HTML, response.responseText);
100
101 bug_already_reported_expanders = Y.all(
102 'img.bug-already-reported-expander');
103 if (Y.Lang.isValue(bug_already_reported_expanders)) {
104- // If there are duplicates shown, change the title of the page
105- // and set up the JavaScript of the duplicates that have been
106- // returned.
107+ // If there are duplicates shown, set up the JavaScript of
108+ // the duplicates that have been returned.
109 Y.bugs.setup_dupes();
110- Y.get('#page-title').set(
111- INNER_HTML,
112- 'Is the bug you&rsquo;re reporting one of these?');
113 } else {
114- // Otherwise, set the title to one that doesn't suggest
115- // there were dupes returned and show the bug reporting
116- // form.
117- Y.get('#page-title').set(INNER_HTML, 'Report a bug');
118+ // Otherwise, show the bug reporting form.
119 show_bug_reporting_form();
120 }
121
122 // Copy the value from the search field into the title field
123 // on the filebug form.
124- Y.get(Y.DOM.byId('field.title')).set(
125+ Y.one('#bug_reporting_form input[name=field.title]').set(
126 'value', search_field.get('value'));
127
128 // Finally, change the label on the search button and show it
129 // again.
130 search_button.set('value', 'Check again');
131- search_button.setStyle(DISPLAY, INLINE);
132+ search_button.removeClass(UNSEEN);
133 }
134
135 var search_term = encodeURI(search_field.get('value'));
136- var search_url_base = Y.get(
137+ var search_url_base = Y.one(
138 '#duplicate-search-url').getAttribute('href');
139 var search_url = search_url_base + '?title=' + search_term;
140
141 // Hide the button, show the spinner and clear the contents of the
142 // possible duplicates div.
143- search_button.setStyle(DISPLAY, NONE);
144- Y.get('#spinner').setStyle(DISPLAY, INLINE);
145- Y.get('#possible-duplicates').set(INNER_HTML, '');
146+ search_button.addClass(UNSEEN);
147+ Y.one('#spinner').removeClass(UNSEEN);
148+ Y.one('#possible-duplicates').set(INNER_HTML, '');
149
150 config = {on: {success: on_success,
151 failure: show_failure_message}};
152@@ -183,7 +201,7 @@
153 // Grab the bug id and title from the "Yes, this is my bug" form.
154 var bug_id = form.one(
155 'input.bug-already-reported-as').get('value');
156- var bug_title = Y.get('#bug-' + bug_id + '-title').get(INNER_HTML);
157+ var bug_title = Y.one('#bug-' + bug_id + '-title').get(INNER_HTML);
158
159 if (bug_title.length > 35) {
160 // Truncate the bug title if it's more than 35 characters long.
161@@ -242,7 +260,7 @@
162 var weight = radio_button.get('checked') ? 'bold' : 'normal';
163 radio_button.get('parentNode').setStyle('fontWeight', weight);
164 });
165- });
166+ }
167
168 return subscribe_form_overlay;
169 }
170@@ -253,42 +271,42 @@
171 * +filebug search form.
172 */
173 function set_up_dupe_finder(transaction_id, response, args) {
174- var filebug_form_container = Y.get('#filebug-form-container');
175+ var filebug_form_container = Y.one('#filebug-form-container');
176 filebug_form_container.set(INNER_HTML, response.responseText);
177
178 // Activate the extra options collapsible section on the bug
179 // reporting form.
180- var bug_reporting_form = Y.get('#bug_reporting_form');
181+ var bug_reporting_form = Y.one('#bug_reporting_form');
182 if (Y.Lang.isValue(bug_reporting_form)) {
183 activateCollapsibles();
184 }
185
186- search_button = Y.get(Y.DOM.byId('field.actions.search'));
187+ search_button = Y.one(Y.DOM.byId('field.actions.search'));
188
189 // Change the name and id of the search field so that it doesn't
190 // confuse the view when we submit a bug report.
191- search_field = Y.get(Y.DOM.byId('field.title'));
192+ search_field = Y.one(Y.DOM.byId('field.title'));
193 search_field.set('name', 'field.search');
194 search_field.set('id', 'field.search');
195
196- // Disable the form so that hitting "enter" in the Summary
197- // field no longer sends us through to the next page.
198- // Y.on('submit', function(e) { e.halt(); }, '#my-form')
199-
200 // Update the label on the search button so that it no longer
201 // says "Continue".
202 search_button.set('value', 'Next');
203- search_button.set('type', 'button');
204
205- // Set up the handlers for the search button and the input
206- // field.
207- search_button.on('click', search_for_and_display_dupes);
208+ // Set up the handler for the search form.
209+ search_form = Y.one('#filebug-search-form');
210+ search_form.on('submit', function(e) {
211+ // Prevent the event from propagating; we don't want to reload
212+ // the page.
213+ e.halt();
214+ search_for_and_display_dupes();
215+ });
216 }
217
218 Y.bugs.setup_dupes = function() {
219 bug_already_reported_expanders = Y.all(
220 'img.bug-already-reported-expander');
221- bug_reporting_form = Y.get('#bug_reporting_form');
222+ bug_reporting_form = Y.one('#bug_reporting_form');
223
224 if (Y.Lang.isValue(bug_already_reported_expanders)) {
225 // Collapse all the details divs, since we don't want them
226@@ -338,7 +356,7 @@
227
228 // If the bug reporting form is shown, hide it.
229 if (bug_reporting_form.getStyle(DISPLAY) == BLOCK) {
230- bug_reporting_form.setStyle(DISPLAY, NONE);
231+ bug_reporting_form.addClass(UNSEEN);
232 }
233 }
234 });
235@@ -352,19 +370,14 @@
236
237 image.set(SRC, EXPANDER_EXPANDED);
238 }
239-
240- // If the bug reporting form is shown, hide it.
241- if (bug_reporting_form.getStyle(DISPLAY) == BLOCK) {
242- bug_reporting_form.setStyle(DISPLAY, NONE);
243- }
244 });
245 });
246
247 // Hide the bug reporting form.
248- bug_reporting_form.setStyle(DISPLAY, NONE);
249+ bug_reporting_form.addClass(UNSEEN);
250 }
251
252- bug_not_reported_button = Y.get('#bug-not-already-reported');
253+ bug_not_reported_button = Y.one('#bug-not-already-reported');
254 if (Y.Lang.isValue(bug_not_reported_button)) {
255 // The bug_not_reported_button won't show up if there aren't any
256 // possible duplicates.
257@@ -386,6 +399,7 @@
258 };
259
260 Y.bugs.setup_dupe_finder = function() {
261+ Y.log("In setup_dupe_finder");
262 Y.on('domready', function() {
263 config = {on: {success: set_up_dupe_finder,
264 failure: function() {}}};
265@@ -393,7 +407,7 @@
266 // Load the filebug form asynchronously. If this fails we
267 // degrade to the standard mode for bug filing, clicking through
268 // to the second part of the bug filing form.
269- var filebug_form_url_element = Y.get(
270+ var filebug_form_url_element = Y.one(
271 '#filebug-form-url');
272 if (Y.Lang.isValue(filebug_form_url_element)) {
273 var filebug_form_url = filebug_form_url_element.getAttribute(
274@@ -404,4 +418,4 @@
275 };
276
277 }, "0.1", {"requires": [
278- "base", "oop", "node", "event", "lazr.formoverlay", "lazr.effects"]});
279+ "base", "io", "oop", "node", "event", "lazr.formoverlay", "lazr.effects"]});
280
281=== modified file 'lib/lp/bugs/browser/bugtarget.py'
282--- lib/lp/bugs/browser/bugtarget.py 2009-11-19 13:03:51 +0000
283+++ lib/lp/bugs/browser/bugtarget.py 2009-12-09 17:25:37 +0000
284@@ -56,6 +56,7 @@
285 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
286 from canonical.launchpad.interfaces.temporaryblobstorage import (
287 ITemporaryStorageManager)
288+from canonical.launchpad.webapp import urlappend
289 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
290 from canonical.launchpad.webapp.interfaces import ILaunchBag, NotFoundError
291 from lp.bugs.interfaces.bug import (
292@@ -359,6 +360,11 @@
293 if 'field.actions' in key] != [] or
294 self.user.inTeam(bug_supervisor))
295
296+ @property
297+ def use_asynchronous_dupefinder(self):
298+ """Return True if the asynchronous dupe finder can be used."""
299+ return IProduct.providedBy(self.context)
300+
301 def getPackageNameFieldCSSClass(self):
302 """Return the CSS class for the packagename field."""
303 if self.widget_errors.get("packagename"):
304@@ -368,7 +374,6 @@
305
306 def validate(self, data):
307 """Make sure the package name, if provided, exists in the distro."""
308-
309 # The comment field is only required if filing a new bug.
310 if self.submit_bug_action.submitted():
311 comment = data.get('comment')
312@@ -700,6 +705,26 @@
313 """Override this method in base classes to show the filebug form."""
314 raise NotImplementedError
315
316+ @property
317+ def inline_filebug_form_url(self):
318+ """The URL to the inline filebug form.
319+
320+ If a token was passed to this view, it will be be passed through
321+ to the inline bug filing form via the returned URL.
322+ """
323+ url = canonical_url(self.context, view_name='+filebug-inline-form')
324+ if self.extra_data_token is not None:
325+ url = urlappend(url, self.extra_data_token)
326+ return url
327+
328+ @property
329+ def duplicate_search_url(self):
330+ """The URL to the inline duplicate search view."""
331+ url = canonical_url(self.context, view_name='+filebug-show-similar')
332+ if self.extra_data_token is not None:
333+ url = urlappend(url, self.extra_data_token)
334+ return url
335+
336 def publishTraverse(self, request, name):
337 """See IBrowserPublisher."""
338 if self.extra_data_token is not None:
339@@ -831,6 +856,11 @@
340 return guidelines
341
342
343+class FileBugInlineFormView(FileBugViewBase):
344+ """A browser view for displaying the inline filebug form."""
345+ schema = IBugAddForm
346+
347+
348 class FileBugAdvancedView(FileBugViewBase):
349 """Browser view for filing a bug.
350
351@@ -851,7 +881,14 @@
352 """
353 schema = IBugAddForm
354
355+ # XXX: Brad Bollenbach 2006-10-04: This assignment to actions is a
356+ # hack to make the action decorator Just Work across inheritance.
357+ actions = FileBugViewBase.actions
358+ custom_widget('title', TextWidget, displayWidth=40)
359+ custom_widget('tags', BugTagsWidget)
360+
361 _MATCHING_BUGS_LIMIT = 10
362+ show_summary_in_results = False
363
364 @property
365 def search_context(self):
366@@ -894,22 +931,37 @@
367
368 return matching_bugs
369
370+ @property
371+ def show_duplicate_list(self):
372+ """Return whether or not to show the duplicate list.
373+
374+ We only show the dupes if:
375+ - The context uses Malone AND
376+ - There are dupes to show AND
377+ - There are no widget errors.
378+ """
379+ return (
380+ self.contextUsesMalone and
381+ len(self.similar_bugs) > 0 and
382+ len(self.widget_errors) == 0)
383+
384
385 class FileBugGuidedView(FilebugShowSimilarBugsView):
386- # XXX: Brad Bollenbach 2006-10-04: This assignment to actions is a
387- # hack to make the action decorator Just Work across inheritance.
388- actions = FileBugViewBase.actions
389- custom_widget('title', TextWidget, displayWidth=65)
390- custom_widget('tags', BugTagsWidget)
391
392 _SEARCH_FOR_DUPES = ViewPageTemplateFile(
393 "../templates/bugtarget-filebug-search.pt")
394 _FILEBUG_FORM = ViewPageTemplateFile(
395 "../templates/bugtarget-filebug-submit-bug.pt")
396
397+ # XXX 2009-07-17 Graham Binns
398+ # As above, this assignment to actions is to make sure that the
399+ # actions from the ancestor views are preserved, otherwise they
400+ # get nuked.
401+ actions = FilebugShowSimilarBugsView.actions
402 template = _SEARCH_FOR_DUPES
403
404 focused_element_id = 'field.title'
405+ show_summary_in_results = True
406
407 @safe_action
408 @action("Continue", name="search", validator="validate_search")
409@@ -948,20 +1000,6 @@
410 except InputErrors:
411 return None
412
413- @property
414- def show_duplicate_list(self):
415- """Return whether or not to show the duplicate list.
416-
417- We only show the dupes if:
418- - The context uses Malone AND
419- - There are dupes to show AND
420- - There are no widget errors.
421- """
422- return (
423- self.contextUsesMalone and
424- len(self.similar_bugs) > 0 and
425- len(self.widget_errors) == 0)
426-
427 def validate_search(self, action, data):
428 """Make sure some keywords are provided."""
429 try:
430
431=== modified file 'lib/lp/bugs/browser/configure.zcml'
432--- lib/lp/bugs/browser/configure.zcml 2009-11-26 17:28:13 +0000
433+++ lib/lp/bugs/browser/configure.zcml 2009-12-09 17:25:37 +0000
434@@ -95,6 +95,13 @@
435 facet="bugs"
436 permission="launchpad.AnyPerson"/>
437 <browser:page
438+ name="+filebug-inline-form"
439+ for="lp.bugs.interfaces.bugtarget.IBugTarget"
440+ class="lp.bugs.browser.bugtarget.FilebugShowSimilarBugsView"
441+ template="../templates/bugtarget-filebug-inline-form.pt"
442+ facet="bugs"
443+ permission="launchpad.AnyPerson"/>
444+ <browser:page
445 name="+manage-official-tags"
446 for="lp.bugs.interfaces.bugtarget.IOfficialBugTagTargetRestricted"
447 class="lp.bugs.browser.bugtarget.OfficialBugTagsManageView"
448
449=== modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt'
450--- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-08-13 15:12:16 +0000
451+++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2009-12-09 17:25:37 +0000
452@@ -43,6 +43,25 @@
453 u'Test description.'
454
455
456+== URLs to additional FileBug elements ==
457+
458+FileBugViewBase provides properties that return the URLs of further
459+useful parts of the +filebug process.
460+
461+The inline_filebug_form_url property returns the URL of the inline
462+filebug form so that it may be loaded asynchronously.
463+
464+ >>> print filebug_view.inline_filebug_form_url
465+ http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-inline-form
466+
467+Similarly, the duplicate_search_url property returns the base URL for
468+the duplicate search view, which can be used to load the list of
469+possible duplicates for a bug asynchronously.
470+
471+ >>> print filebug_view.duplicate_search_url
472+ http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug-show-similar
473+
474+
475 == Adding extra info to filed bugs ==
476
477 It's possible for bug reporting tools to upload a file with debug
478
479=== modified file 'lib/lp/bugs/interfaces/bug.py'
480--- lib/lp/bugs/interfaces/bug.py 2009-11-25 23:09:12 +0000
481+++ lib/lp/bugs/interfaces/bug.py 2009-12-09 17:25:37 +0000
482@@ -26,6 +26,7 @@
483 from zope.interface import Interface, Attribute
484 from zope.schema import (
485 Bool, Bytes, Choice, Datetime, Int, List, Object, Text, TextLine)
486+from zope.schema.vocabulary import SimpleVocabulary
487 from zope.security.interfaces import Unauthorized
488
489 from canonical.launchpad import _
490@@ -789,6 +790,11 @@
491 "A sequence of IBugTaskDeltas, one IBugTaskDelta or None.")
492
493
494+# A simple vocabulary for the subscribe_to_existing_bug form field.
495+SUBSCRIBE_TO_BUG_VOCABULARY = SimpleVocabulary.fromItems(
496+ [('yes', True), ('no', False)])
497+
498+
499 class IBugAddForm(IBug):
500 """Information we need to create a bug"""
501 id = Int(title=_("Bug #"), required=False)
502@@ -842,8 +848,9 @@
503 assignee = PublicPersonChoice(
504 title=_('Assign to'), required=False,
505 vocabulary='ValidAssignee')
506- subscribe_to_existing_bug = Bool(
507- title=_(u'Subscribe to this bug'),
508+ subscribe_to_existing_bug = Choice(
509+ title=u'Subscribe to this bug',
510+ vocabulary=SUBSCRIBE_TO_BUG_VOCABULARY,
511 required=True, default=False)
512
513
514
515=== added file 'lib/lp/bugs/templates/bugtarget-filebug-inline-form.pt'
516--- lib/lp/bugs/templates/bugtarget-filebug-inline-form.pt 1970-01-01 00:00:00 +0000
517+++ lib/lp/bugs/templates/bugtarget-filebug-inline-form.pt 2009-12-09 17:25:37 +0000
518@@ -0,0 +1,11 @@
519+<tal:root
520+ xmlns:tal="http://xml.zope.org/namespaces/tal"
521+ xmlns:metal="http://xml.zope.org/namespaces/metal"
522+ omit-tag="">
523+
524+ <tal:filebug-form define="launchpad_form_id string:filebug-form">
525+ <metal:display-similar-bugs
526+ use-macro="context/@@+filebug-macros/inline-filebug-form" />
527+ </tal:filebug-form>
528+
529+</tal:root>
530
531=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
532--- lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-08-18 16:15:18 +0000
533+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt 2009-12-09 17:25:37 +0000
534@@ -1,10 +1,29 @@
535-<filebug-search
536+<html
537 xmlns="http://www.w3.org/1999/xhtml"
538 xmlns:tal="http://xml.zope.org/namespaces/tal"
539 xmlns:metal="http://xml.zope.org/namespaces/metal"
540 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
541 metal:use-macro="view/macro:page/main_only">
542
543+ <metal:block fill-slot="head_epilogue">
544+ <script type="text/javascript"
545+ tal:condition="devmode"
546+ tal:content="string:var yui_base='${yui}';"></script>
547+ <script type="text/javascript"
548+ tal:condition="devmode"
549+ tal:define="lp_js string:${icingroot}/build"
550+ tal:attributes="src string:${lp_js}/bugs/filebug-dupefinder.js"></script>
551+
552+ <tal:dupe-finder-js condition="view/use_asynchronous_dupefinder">
553+ <script type="text/javascript">
554+ LPS.use(
555+ 'base', 'node', 'oop', 'event', 'bugs.dupe_finder', function(Y) {
556+ Y.bugs.setup_dupe_finder();
557+ });
558+ </script>
559+ </tal:dupe-finder-js>
560+ </metal:block>
561+
562 <div metal:fill-slot="heading">
563 <div tal:condition="view/isPrivate" id="privacy" class="aside private">
564 This report will be private, though you can disclose it later.
565@@ -23,38 +42,89 @@
566 </tal:does-not-use-malone>
567
568 <tal:uses-malone tal:condition="view/contextUsesMalone">
569- <div class="top-portlet">
570+ <div class="top-portlet"
571+ tal:define="launchpad_form_id string:filebug-search-form">
572 <div metal:use-macro="context/@@launchpad_form/form">
573- <table class="form" metal:fill-slot="widgets">
574- <tal:product_widget
575- tal:define="widget nocall:view/widgets/product|nothing"
576- tal:condition="widget">
577- <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" />
578- </tal:product_widget>
579- <tal:bugtarget
580- tal:define="widget nocall:view/widgets/bugtarget|nothing"
581- tal:condition="widget">
582- <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" />
583- </tal:bugtarget>
584- <tal:hidden_tags tal:replace="structure view/widgets/tags/hidden" />
585- <tr>
586- <td colspan="2">
587- Please describe the bug in a few words, for example,
588- "weather applet crashes on logout":
589- </td>
590- </tr>
591- <tal:title_widget tal:define="widget nocall:view/widgets/title">
592- <metal:widget use-macro="context/@@launchpad_form/widget_row" />
593- </tal:title_widget>
594- </table>
595- <div class="actions" metal:fill-slot="buttons">
596- <input tal:replace="structure view/actions/field.actions.search/render" />
597- </div>
598- </div>
599+
600+ <table metal:fill-slot="widgets">
601+
602+ <tal:product_widget
603+ tal:define="widget nocall:view/widgets/product|nothing"
604+ tal:condition="widget">
605+ <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" />
606+ </tal:product_widget>
607+
608+ <tal:bugtarget
609+ tal:define="widget nocall:view/widgets/bugtarget|nothing"
610+ tal:condition="widget">
611+ <metal:widget metal:use-macro="context/@@launchpad_form/widget_row" />
612+ </tal:bugtarget>
613+
614+ <tal:hidden_tags tal:replace="structure view/widgets/tags/hidden" />
615+
616+ <tr>
617+ <td colspan="2">
618+ <p>
619+ Please describe the bug in a few words, for example, "weather
620+ applet crashes on logout":
621+ </p>
622+ </td>
623+ </tr>
624+ <tal:title_widget tal:define="widget nocall:view/widgets/title">
625+ <tal:comment replace="nothing">
626+ The desire to have more control over the styling of this widget
627+ prevents us from using the widget_row macro here.
628+ </tal:comment>
629+ <tr>
630+ <td tal:define="field_name widget/context/__name__;
631+ error python:view.getFieldError(field_name);
632+ error_class python:('error' if error else None);"
633+ tal:attributes="class error_class"
634+ style="text-align: left; padding-left: 5em;">
635+ <div>
636+ <label tal:attributes="for widget/name"
637+ tal:content="string:${widget/label}:">Label</label>
638+ <input tal:replace="structure widget" />
639+ </div>
640+ <div class="message" tal:condition="error"
641+ tal:content="structure error">Error message</div>
642+ <p class="formHelp"
643+ tal:condition="widget/hint"
644+ tal:content="widget/hint">Some Help Text
645+ </p>
646+ </td>
647+ <td style="text-align: left;">
648+ <input tal:replace="structure view/actions/field.actions.search/render" />
649+ <span id="spinner" style="text-align: center" class="unseen">
650+ <img src="/@@/spinner" />
651+ </span>
652+ </td>
653+ </tr>
654+ </tal:title_widget>
655+ </table>
656+
657+ <div metal:fill-slot="buttons">
658+ <tal:comment replace="nothing">
659+ We add this to hide the standard action buttons.
660+ </tal:comment>
661 </div>
662- </tal:uses-malone>
663-
664+ </div>
665 </div>
666- </div>
667-
668-</filebug-search>
669+ </tal:uses-malone>
670+ </div>
671+
672+ <div id="possible-duplicates" style="text-align: left;">
673+ </div>
674+ <div id="filebug-form-container" style="display: none;">
675+ </div>
676+
677+ <p style="display: none" tal:condition="view/use_asynchronous_dupefinder">
678+ <a id="filebug-form-url"
679+ tal:attributes="href view/inline_filebug_form_url"></a>
680+ <a id="duplicate-search-url"
681+ tal:attributes="href view/duplicate_search_url"></a>
682+ </p>
683+
684+ </div>
685+
686+</html>
687
688=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-show-similar.pt'
689--- lib/lp/bugs/templates/bugtarget-filebug-show-similar.pt 2009-07-17 17:59:07 +0000
690+++ lib/lp/bugs/templates/bugtarget-filebug-show-similar.pt 2009-12-09 17:25:37 +0000
691@@ -3,7 +3,12 @@
692 xmlns:metal="http://xml.zope.org/namespaces/metal"
693 omit-tag="">
694
695- <metal:display-similar-bugs
696- use-macro="context/@@+filebug-macros/display-similar-bugs" />
697+ <tal:similar-bugs-list condition="view/similar_bugs">
698+ <metal:display-similar-bugs
699+ use-macro="context/@@+filebug-macros/show-similar-bugs-and-filebug-button" />
700+ </tal:similar-bugs-list>
701+ <tal:no-similar-bugs condition="not: view/similar_bugs">
702+ <p>No similar bug reports were found.</p>
703+ </tal:no-similar-bugs>
704
705 </tal:root>
706
707=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt'
708--- lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt 2009-12-09 17:25:34 +0000
709+++ lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt 2009-12-09 17:25:37 +0000
710@@ -1,4 +1,4 @@
711-<filebug-submit
712+<div
713 xmlns="http://www.w3.org/1999/xhtml"
714 xmlns:tal="http://xml.zope.org/namespaces/tal"
715 xmlns:metal="http://xml.zope.org/namespaces/metal"
716@@ -64,4 +64,4 @@
717 </div>
718 </div>
719
720-</filebug-submit>
721+</div>
722
723=== modified file 'lib/lp/bugs/templates/bugtarget-macros-filebug.pt'
724--- lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2009-10-01 12:09:37 +0000
725+++ lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2009-12-09 17:25:37 +0000
726@@ -100,7 +100,7 @@
727
728 <tr>
729 <td colspan="2">
730- <fieldset class="collapsible collapsed">
731+ <fieldset id="filebug-extra-options" class="collapsible collapsed">
732 <legend>Extra options</legend>
733 <table>
734 <tal:status
735@@ -361,8 +361,19 @@
736 </metal:bug_reporting_guidelines>
737
738 <metal:similar-bugs define-macro="display-similar-bugs">
739- <p id="filebug-query-heading">Is "<strong><span tal:replace="view/search_text">my submitted bug
740- summary</span></strong>" one of these bugs?</p>
741+ <tal:results-intro-with-summary condition="view/show_summary_in_results">
742+ <p id="filebug-query-heading">
743+ Is "<strong><span tal:replace="view/search_text">my submitted bug summary</span></strong>"
744+ one of these bugs?
745+ </p>
746+ </tal:results-intro-with-summary>
747+ <tal:results-intro-without-summary
748+ condition="not: view/show_summary_in_results">
749+ <p id="filebug-query-heading">
750+ Do any of the following bugs describe the bug you're trying to
751+ report?
752+ </p>
753+ </tal:results-intro-without-summary>
754 <ul id="similar-bugs">
755 <li tal:repeat="bug view/similar_bugs" class="similar-bug">
756 <table tal:define="bugtask python:view.getRelevantBugTask(bug)">
757@@ -461,8 +472,9 @@
758 tal:condition="error_message" tal:content="error_message" class="error message" />
759 </metal:similar-bugs>
760
761-<metal:similar-bugs-and-filebug-form
762- define-macro="show-similar-bugs-and-filebug-form">
763+<metal:similar-bugs-and-filebug-button
764+ define-macro="show-similar-bugs-and-filebug-button">
765+
766 <tal:similar-bugs condition="view/show_duplicate_list">
767 <metal:similar-bugs
768 use-macro="context/@@+filebug-macros/display-similar-bugs" />
769@@ -477,22 +489,55 @@
770 value="No, I need to report a new bug" />
771 </p>
772 </tal:similar-bugs>
773+</metal:similar-bugs-and-filebug-button>
774+
775+<metal:similar-bugs-and-filebug-form
776+ define-macro="show-similar-bugs-and-filebug-form">
777+
778+ <metal:similar-bugs
779+ use-macro="context/@@+filebug-macros/show-similar-bugs-and-filebug-button" />
780+
781 <tal:submit-new-bug>
782- <div id="bug_reporting_form">
783- <metal:form use-macro="context/@@launchpad_form/form">
784- <a name="form-start" />
785- <metal:widgets metal:fill-slot="widgets">
786- <table class="form">
787- <metal:basic_filebug_widgets
788- metal:use-macro="context/@@+filebug-macros/basic_filebug_widgets" />
789- </table>
790- </metal:widgets>
791- <div class="actions" metal:fill-slot="buttons">
792- <input tal:replace="structure view/submit_bug_action/render" />
793- </div>
794- </metal:form>
795- </div>
796+ <metal:basic_filebug_widgets
797+ metal:use-macro="context/@@+filebug-macros/inline-filebug-form" />
798 </tal:submit-new-bug>
799 </metal:similar-bugs-and-filebug-form>
800
801+<metal:inline-filebug-form define-macro="inline-filebug-form">
802+ <div id="bug_reporting_form">
803+ <metal:form use-macro="context/@@launchpad_form/form">
804+ <a name="form-start" />
805+ <metal:widgets metal:fill-slot="widgets">
806+ <table class="form">
807+ <metal:basic_filebug_widgets
808+ metal:use-macro="context/@@+filebug-macros/basic_filebug_widgets" />
809+ </table>
810+ </metal:widgets>
811+ <div class="actions" metal:fill-slot="buttons">
812+ <input tal:replace="structure view/submit_bug_action/render" />
813+ </div>
814+ </metal:form>
815+ </div>
816+</metal:inline-filebug-form>
817+
818+<metal:filebug-form define-macro="simple-filebug-form">
819+ <form action="."
820+ tal:attributes="action view/action_url"
821+ name="launchpadform"
822+ method="post"
823+ enctype="multipart/form-data"
824+ accept-charset="UTF-8"
825+ id="filebug-form">
826+
827+ <table class="form">
828+ <metal:basic_filebug_widgets
829+ metal:use-macro="context/@@+filebug-macros/basic_filebug_widgets" />
830+ </table>
831+ <div class="actions">
832+ <input tal:replace="structure view/submit_bug_action/render" />
833+ </div>
834+
835+ </form>
836+</metal:filebug-form>
837+
838 </tal:root>