Merge lp:~gmb/launchpad/ajax-dupefinder-has-risen-from-the-grave into lp:launchpad
- ajax-dupefinder-has-risen-from-the-grave
- Merge into devel
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 |
Related bugs: |
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.
Description of the change
Graham Binns (gmb) wrote : | # |
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.
> 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 :)
Graham Binns (gmb) wrote : | # |
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.
> > (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...
Michael Nelson (michael.nelson) wrote : | # |
> 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.
> > > (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...
Gavin Panella (allenap) wrote : | # |
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/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -81,13 +81,6 @@
> * @param e The Event triggering this function.
> */
> function show_bug_
> - if (Y.Lang.
> - // Collapse any duplicate-details divs.
> - Y.each(
> - collapse_
> - });
> - }
> -
> // If the bug reporting form is in a hidden container, as it is on
> // the AJAX dupe search, show it.
> var filebug_
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_
> - function show_failure_
> - Y.get('
> + function show_failure_
> + // 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('
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('
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_
> +
> + Y.get(Y.
Gavin Panella (allenap) wrote : | # |
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/
> --- lib/lp/
> +++ lib/lp/
> @@ -56,6 +56,7 @@
> from canonical.
> from canonical.
> ITemporaryStora
> +from canonical.
> from canonical.
> from canonical.
> from lp.bugs.
> @@ -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_
> comment = data.get('comment')
> @@ -700,6 +700,26 @@
> """Override this method in base classes to show the filebug form."""
> raise NotImplementedError
>
> + @property
> + def inline_
> + """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(
No need for urlappend here; pass view="+
canonical_url().
> + if self.extra_
> + url = urlappend(url, self.extra_
> + return url
> +
> + @property
> + def duplicate_
> + """The URL to the inline duplicate search view."""
> + url = urlappend(
Again with the view argument to canonical_url().
> + if self.extra_
> + url = urlappend(url, self.extra_
> + return url
> +
> def publishTraverse
> """See IBrowserPublish
> if self.extra_
> @@ -831,6 +851,11 @@
> return guidelines
>
>
> +class FileBugInlineFo
> + """A browser view for displaying the inline filebug form."""
> + schema = IBugAddForm
> +
> +
> class FileBugAdvanced
> """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
> + custom_
> + custom_
> +
> _MATCHING_
> + show_summary_
>
> @property
> def search_
> @@ -894,22 +926,37 @@
>
> return matching_bugs
>
> + @property
> + def show_duplicate_
Graham Binns (gmb) wrote : | # |
> > === modified file 'lib/canonical/
> > --- lib/canonical/
> > +++ lib/canonical/
> > @@ -81,13 +81,6 @@
> > * @param e The Event triggering this function.
> > */
> > function show_bug_
> > - if (Y.Lang.
> > - // Collapse any duplicate-details divs.
> > - Y.each(
> > - collapse_
> > - });
> > - }
> > -
> > // If the bug reporting form is in a hidden container, as it is on
> > // the AJAX dupe search, show it.
> > var filebug_
>
> 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_
> > - function show_failure_
> > - Y.get('
> > + function show_failure_
> > + // 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('
>
> 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('
>
> 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 ...
Graham Binns (gmb) wrote : | # |
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -56,6 +56,7 @@
> > from canonical.
> > from canonical.
> > ITemporaryStora
> > +from canonical.
> > from canonical.
> > from canonical.
> > from lp.bugs.
> > @@ -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_
> > comment = data.get('comment')
> > @@ -700,6 +700,26 @@
> > """Override this method in base classes to show the filebug form."""
> > raise NotImplementedError
> >
> > + @property
> > + def inline_
> > + """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(
>
> No need for urlappend here; pass view="+
> canonical_url().
>
Right. No idea what I was smoking when I wrote that.
> > + if self.extra_
> > + url = urlappend(url, self.extra_
> > + return url
> > +
> > + @property
> > + def duplicate_
> > + """The URL to the inline duplicate search view."""
> > + url = urlappend(
>
> Again with the view argument to canonical_url().
>
Yep.
Graham Binns (gmb) wrote : | # |
Oops. Incremental diff for the first two parts of the code / js reviews:
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -16,7 +16,8 @@
INNER_HTML = 'innerHTML',
LAZR_CLOSED = 'lazr-closed',
NONE = 'none',
- SRC = 'src';
+ SRC = 'src',
+ UNSEEN = 'unseen';
var bugs = Y.namespace(
@@ -118,34 +119,36 @@
// We treat 503 (service unavailable) as a timeout because
// that's what timeouts in LP return.
- "<p>Searching for your bug in Launchpad took too long. " +
+ "Searching for your bug in Launchpad took too long. " +
- "below.</p>";
+ "below.";
} else {
// Any other error code gets a generic message.
- "<p>An error occured whilst trying to find bugs matching " +
+ "An error occured whilst trying to find bugs matching " +
- "details of your bug below.</p>";
+ "details of your bug below.";
}
- Y.one('
+ var error_node = Y.Node.
+ error_node.
+ Y.one('
- Y.one('
+ Y.one('
- search_
+ search_
}
function on_success(
// Hide the spinner and show the duplicates.
- Y.one('
+ Y.one('
var duplicate_div = Y.one('
@@ -163,13 +166,13 @@
// Copy the value from the search field into the title field
// on the filebug form.
- Y.one(Y.
+ Y.one('
// Finally, change the label on the search button and show it
// again.
- search_
+ search_
}
var search_term = encodeURI(
Gavin Panella (allenap) wrote : | # |
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/
> --- lib/lp/
> +++ lib/lp/
> @@ -95,6 +95,13 @@
> facet="bugs"
> permission=
> <browser:page
> + name="+
> + for="lp.
> + class="
> + template=
> + facet="bugs"
> + permission=
> + <browser:page
> name="+
> for="lp.
> class="
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -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 so that it may be loaded asynchronously.
> +
> + >>> print filebug_
> + http://
> +
> +Similarly, the duplicate_
> +the duplicate search view, which can be used to load the list of
> +possible duplicates for a bug asynchronously.
> +
> + >>> print filebug_
> + http://
> +
> +
> == Adding extra info to filed bugs ==
>
> It's possible for bug reporting tools to upload a file with debug
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -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.
> from zope.security.
>
> from canonical.launchpad import _
> @@ -789,6 +790,11 @@
> "A sequence of IBugTaskDeltas, one IBugTaskDelta or None.")
>
>
> +# A simple vocabulary for the subscribe_
> +SUBSCRIBE_
> + [('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,
> ...
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:
> > > + <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=
> > > + error python:
> > > + 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-
> > > + tal:attributes=
> > > + style="display: none;"></a>
> > > + <a id="duplicate-
> > > + tal:attributes=
> > > + 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.
Gavin Panella (allenap) : | # |
Edwin Grubbs (edwin-grubbs) : | # |
Preview Diff
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’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> |
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.