Merge lp://staging/~edwin-grubbs/launchpad/bug-99395-linking-sourcepackages-to-projects into lp://staging/launchpad
- bug-99395-linking-sourcepackages-to-projects
- Merge into devel
Status: | Merged | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Edwin Grubbs | ||||||||||||||||
Approved revision: | not available | ||||||||||||||||
Merged at revision: | not available | ||||||||||||||||
Proposed branch: | lp://staging/~edwin-grubbs/launchpad/bug-99395-linking-sourcepackages-to-projects | ||||||||||||||||
Merge into: | lp://staging/launchpad | ||||||||||||||||
Diff against target: |
473 lines (+242/-78) 7 files modified
lib/canonical/launchpad/webapp/launchpadform.py (+6/-0) lib/lp/bugs/stories/bug-also-affects/xx-also-affects-upstream-default-values.txt (+3/-2) lib/lp/registry/browser/sourcepackage.py (+134/-46) lib/lp/registry/browser/tests/sourcepackage-views.txt (+68/-22) lib/lp/registry/stories/distribution/xx-distribution-packages.txt (+11/-1) lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt (+3/-2) lib/lp/registry/templates/sourcepackage-edit-packaging.pt (+17/-5) |
||||||||||||||||
To merge this branch: | bzr merge lp://staging/~edwin-grubbs/launchpad/bug-99395-linking-sourcepackages-to-projects | ||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | code | Approve | |
Review via email: mp+19429@code.staging.launchpad.net |
Commit message
Made $sourcepackage/
Description of the change
Edwin Grubbs (edwin-grubbs) wrote : | # |
Brad Crittenden (bac) wrote : | # |
Edwin this branch looks great. I expected the multistep stuff to be much harder. Thanks for a nice branch and thorough explanations.
Edwin Grubbs (edwin-grubbs) wrote : | # |
> Edwin this branch looks great. I expected the multistep stuff to be much
> harder. Thanks for a nice branch and thorough explanations.
Hi Brad,
Here are some tests that were broken by +edit-packaging being two steps
now. I also have changes to browser/
erroneously edit the field's default without copying the field, so the
default value was propagated to other views and was not only a bad value
but also a stale storm object. I'm setting the default manually, since
passing in the render_context is more complicated for the multistep views.
-Edwin
Incremental diff:
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -37,8 +37,9 @@
Let's follow the link and specify the packaging information.
>>> user_browser.
- >>> user_browser.
- ... 'thunderbird/
+ >>> user_browser.
+ >>> user_browser.
+ >>> user_browser.
>>> user_browser.
Now the upstream product will be chosen automatically also for pmount.
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -28,6 +28,8 @@
from zope.schema.
getVocabul
+from lazr.restful.
+
from canonical.widgets import LaunchpadRadioW
from canonical.launchpad import helpers
@@ -143,8 +145,8 @@
class SourcePackageCh
"""A view to set the `IProductSeries` of a sourcepackage."""
- schema = IProductSeries
- _field_names = ['product']
+ schema = Interface
+ _field_names = []
step_name = 'sourcepackage_
template = ViewPageTemplat
@@ -158,7 +160,12 @@
series = self.context.
if series is not None:
- self.form_
+ default = series.product
+ else:
+ default = None
+ product_field = copy_field(
+ IProductSeries[
+ self.form_fields += Fields(
@property
def cancel_url(self):
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -22,35 +22,67 @@
>>> print view.page_title
Link to an upstream project
- >>> print view.cancel...
Preview Diff
1 | === modified file 'lib/canonical/launchpad/webapp/launchpadform.py' |
2 | --- lib/canonical/launchpad/webapp/launchpadform.py 2009-08-04 00:41:49 +0000 |
3 | +++ lib/canonical/launchpad/webapp/launchpadform.py 2010-02-19 01:53:17 +0000 |
4 | @@ -362,6 +362,12 @@ |
5 | # widgets. |
6 | if not IInputWidget.providedBy(widget): |
7 | return False |
8 | + |
9 | + # Do not show for readonly fields. |
10 | + context = getattr(widget, 'context', None) |
11 | + if getattr(context, 'readonly', None): |
12 | + return False |
13 | + |
14 | # Do not show the marker for required widgets or always submitted |
15 | # widgets. Everything else gets the marker. |
16 | return not (widget.required or |
17 | |
18 | === modified file 'lib/lp/bugs/stories/bug-also-affects/xx-also-affects-upstream-default-values.txt' |
19 | --- lib/lp/bugs/stories/bug-also-affects/xx-also-affects-upstream-default-values.txt 2009-06-12 16:36:02 +0000 |
20 | +++ lib/lp/bugs/stories/bug-also-affects/xx-also-affects-upstream-default-values.txt 2010-02-19 01:53:17 +0000 |
21 | @@ -37,8 +37,9 @@ |
22 | Let's follow the link and specify the packaging information. |
23 | |
24 | >>> user_browser.getLink('updating the packaging information').click() |
25 | - >>> user_browser.getControl(name='field.productseries').value = ( |
26 | - ... 'thunderbird/trunk') |
27 | + >>> user_browser.getControl(name='field.product').value = 'thunderbird' |
28 | + >>> user_browser.getControl('Continue').click() |
29 | + >>> user_browser.getControl(name='field.productseries').value = ['trunk'] |
30 | >>> user_browser.getControl('Change').click() |
31 | |
32 | Now the upstream product will be chosen automatically also for pmount. |
33 | |
34 | === modified file 'lib/lp/registry/browser/sourcepackage.py' |
35 | --- lib/lp/registry/browser/sourcepackage.py 2010-02-12 11:45:37 +0000 |
36 | +++ lib/lp/registry/browser/sourcepackage.py 2010-02-19 01:53:17 +0000 |
37 | @@ -18,11 +18,13 @@ |
38 | |
39 | from apt_pkg import ParseSrcDepends |
40 | from cgi import escape |
41 | +from z3c.ptcompat import ViewPageTemplateFile |
42 | +from zope.app.form.browser import DropdownWidget |
43 | +from zope.app.form.interfaces import IInputWidget |
44 | from zope.component import getUtility, getMultiAdapter |
45 | -from zope.app.form.interfaces import IInputWidget |
46 | -from zope.formlib.form import Fields, FormFields |
47 | +from zope.formlib.form import Fields |
48 | from zope.interface import Interface |
49 | -from zope.schema import Choice |
50 | +from zope.schema import Choice, TextLine |
51 | from zope.schema.vocabulary import ( |
52 | getVocabularyRegistry, SimpleVocabulary, SimpleTerm) |
53 | |
54 | @@ -31,6 +33,7 @@ |
55 | from canonical.widgets import LaunchpadRadioWidget |
56 | |
57 | from canonical.launchpad import helpers |
58 | +from canonical.launchpad.browser.multistep import MultiStepView, StepView |
59 | from lp.bugs.browser.bugtask import BugTargetTraversalMixin |
60 | from canonical.launchpad.browser.packagerelationship import ( |
61 | relationship_builder) |
62 | @@ -39,13 +42,15 @@ |
63 | from lp.services.worlddata.interfaces.country import ICountry |
64 | from lp.registry.interfaces.packaging import IPackaging |
65 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
66 | +from lp.registry.interfaces.product import IProductSet |
67 | +from lp.registry.interfaces.productseries import IProductSeries |
68 | +from lp.registry.interfaces.series import SeriesStatus |
69 | from lp.registry.interfaces.sourcepackage import ISourcePackage |
70 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
71 | from canonical.launchpad import _ |
72 | from canonical.launchpad.webapp import ( |
73 | action, ApplicationMenu, custom_widget, GetitemNavigation, |
74 | - LaunchpadEditFormView, LaunchpadFormView, Link, redirection, |
75 | - StandardLaunchpadFacets, stepto) |
76 | + LaunchpadFormView, Link, redirection, StandardLaunchpadFacets, stepto) |
77 | from canonical.launchpad.webapp import canonical_url |
78 | from canonical.launchpad.webapp.authorization import check_permission |
79 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
80 | @@ -138,53 +143,136 @@ |
81 | return Link('+gethelp', 'Help and support options', icon='info') |
82 | |
83 | |
84 | -class SourcePackageChangeUpstreamView(LaunchpadEditFormView): |
85 | - """A view to set the `IProductSeries` of a sourcepackage.""" |
86 | - schema = ISourcePackage |
87 | - field_names = ['productseries'] |
88 | - |
89 | - label = 'Link to an upstream project' |
90 | - page_title = label |
91 | - |
92 | - @property |
93 | - def cancel_url(self): |
94 | - return canonical_url(self.context) |
95 | - |
96 | - def setUpFields(self): |
97 | - """ See `LaunchpadFormView`. |
98 | - |
99 | - The productseries field is required by the view. |
100 | - """ |
101 | - super(SourcePackageChangeUpstreamView, self).setUpFields() |
102 | - field = copy_field(ISourcePackage['productseries'], required=True) |
103 | - self.form_fields = self.form_fields.omit('productseries') |
104 | - self.form_fields = self.form_fields + FormFields(field) |
105 | - |
106 | - def setUpWidgets(self): |
107 | - """See `LaunchpadFormView`. |
108 | - |
109 | - Set the current `IProductSeries` as the default value. |
110 | - """ |
111 | - super(SourcePackageChangeUpstreamView, self).setUpWidgets() |
112 | - if self.context.productseries is not None: |
113 | - widget = self.widgets.get('productseries') |
114 | - widget.setRenderedValue(self.context.productseries) |
115 | - |
116 | - def validate(self, data): |
117 | - productseries = data.get('productseries', None) |
118 | - if productseries is None: |
119 | - message = "You must choose a project series." |
120 | - self.setFieldError('productseries', message) |
121 | - |
122 | - @action(_("Change"), name="change") |
123 | - def change(self, action, data): |
124 | +class SourcePackageChangeUpstreamStepOne(StepView): |
125 | + """A view to set the `IProductSeries` of a sourcepackage.""" |
126 | + schema = Interface |
127 | + _field_names = [] |
128 | + |
129 | + step_name = 'sourcepackage_change_upstream_step1' |
130 | + template = ViewPageTemplateFile( |
131 | + '../templates/sourcepackage-edit-packaging.pt') |
132 | + label = 'Link to an upstream project' |
133 | + page_title = label |
134 | + step_description = 'Choose project' |
135 | + product = None |
136 | + |
137 | + def setUpFields(self): |
138 | + super(SourcePackageChangeUpstreamStepOne, self).setUpFields() |
139 | + series = self.context.productseries |
140 | + if series is not None: |
141 | + default = series.product |
142 | + else: |
143 | + default = None |
144 | + product_field = copy_field( |
145 | + IProductSeries['product'], default=default) |
146 | + self.form_fields += Fields(product_field) |
147 | + |
148 | + @property |
149 | + def cancel_url(self): |
150 | + return canonical_url(self.context) |
151 | + |
152 | + def main_action(self, data): |
153 | + """See `MultiStepView`.""" |
154 | + self.next_step = SourcePackageChangeUpstreamStepTwo |
155 | + self.request.form['product'] = data['product'] |
156 | + |
157 | + |
158 | +class SourcePackageChangeUpstreamStepTwo(StepView): |
159 | + """A view to set the `IProductSeries` of a sourcepackage.""" |
160 | + schema = IProductSeries |
161 | + _field_names = ['product'] |
162 | + |
163 | + step_name = 'sourcepackage_change_upstream_step2' |
164 | + template = ViewPageTemplateFile( |
165 | + '../templates/sourcepackage-edit-packaging.pt') |
166 | + label = 'Link to an upstream project' |
167 | + page_title = label |
168 | + step_description = 'Choose project series' |
169 | + product = None |
170 | + |
171 | + # The DropdownWidget is used, since the VocabularyPickerWidget |
172 | + # does not support visible=False to turn it into a hidden input |
173 | + # to continue passing the variable in the form. |
174 | + custom_widget('product', DropdownWidget, visible=False) |
175 | + custom_widget('productseries', LaunchpadRadioWidget) |
176 | + |
177 | + @property |
178 | + def cancel_url(self): |
179 | + return canonical_url(self.context) |
180 | + |
181 | + def setUpFields(self): |
182 | + super(SourcePackageChangeUpstreamStepTwo, self).setUpFields() |
183 | + |
184 | + # The vocabulary for the product series is overridden to just |
185 | + # include active series from the product selected in the |
186 | + # previous step. |
187 | + product_name = self.request.form['field.product'] |
188 | + self.product = getUtility(IProductSet)[product_name] |
189 | + series_list = [ |
190 | + series for series in self.product.series |
191 | + if series.status != SeriesStatus.OBSOLETE |
192 | + ] |
193 | + dev_focus = self.product.development_focus |
194 | + if dev_focus in series_list: |
195 | + series_list.remove(dev_focus) |
196 | + vocab_terms = [ |
197 | + SimpleTerm(series, series.name, series.name) |
198 | + for series in series_list |
199 | + ] |
200 | + dev_focus_term = SimpleTerm( |
201 | + dev_focus, dev_focus.name, "%s (Recommended)" % dev_focus.name) |
202 | + vocab_terms.insert(0, dev_focus_term) |
203 | + |
204 | + # If the product is not being changed, then the current |
205 | + # productseries can be the default choice. Otherwise, |
206 | + # it will not exist in the vocabulary. |
207 | + if (self.context.productseries is not None |
208 | + and self.context.productseries.product == self.product): |
209 | + series_default = self.context.productseries |
210 | + else: |
211 | + series_default = None |
212 | + |
213 | + productseries_choice = Choice( |
214 | + __name__='productseries', |
215 | + title=_("Series"), |
216 | + description=_("The series in this project."), |
217 | + vocabulary=SimpleVocabulary(vocab_terms), |
218 | + default=series_default, |
219 | + required=True) |
220 | + |
221 | + # The product selected in the previous step should be displayed, |
222 | + # but a widget can't be readonly and pass its value with the |
223 | + # form, so the real product field passes the value, and this fake |
224 | + # product field displays it. |
225 | + display_product_field = TextLine( |
226 | + __name__='fake_product', |
227 | + title=_("Project"), |
228 | + default=self.product.displayname, |
229 | + readonly=True) |
230 | + |
231 | + self.form_fields = ( |
232 | + Fields(display_product_field, productseries_choice) |
233 | + + self.form_fields) |
234 | + |
235 | + main_action_label = u'Change' |
236 | + def main_action(self, data): |
237 | productseries = data['productseries'] |
238 | + # Because it is part of a multistep view, the next_url can't |
239 | + # be set until the action is called, or it will skip the step. |
240 | + self.next_url = canonical_url(self.context) |
241 | if self.context.productseries == productseries: |
242 | # There is nothing to do. |
243 | return |
244 | self.context.setPackaging(productseries, self.user) |
245 | self.request.response.addNotification('Upstream link updated.') |
246 | - self.next_url = canonical_url(self.context) |
247 | + |
248 | + |
249 | +class SourcePackageChangeUpstreamView(MultiStepView): |
250 | + """A view to set the `IProductSeries` of a sourcepackage.""" |
251 | + page_title = SourcePackageChangeUpstreamStepOne.page_title |
252 | + label = SourcePackageChangeUpstreamStepOne.label |
253 | + total_steps = 2 |
254 | + first_step = SourcePackageChangeUpstreamStepOne |
255 | |
256 | |
257 | class SourcePackageView: |
258 | |
259 | === modified file 'lib/lp/registry/browser/tests/sourcepackage-views.txt' |
260 | --- lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-02-12 14:04:16 +0000 |
261 | +++ lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-02-19 01:53:17 +0000 |
262 | @@ -22,35 +22,67 @@ |
263 | >>> print view.page_title |
264 | Link to an upstream project |
265 | |
266 | - >>> print view.cancel_url |
267 | + >>> print view.view.cancel_url |
268 | http://launchpad.dev/youbuntu/busy/+source/bonkers |
269 | |
270 | |
271 | -The view allows the logged in user to change product series field. The value |
272 | -of the product series field is None by default because it is not required to |
273 | -create a source package. |
274 | - |
275 | - >>> view.field_names |
276 | - ['productseries'] |
277 | - |
278 | - >>> print view.widgets.get('productseries')._getFormValue() |
279 | +The view allows the logged in user to change product series field. The |
280 | +value of the product field is None by default because it is not required |
281 | +to create a source package. |
282 | + |
283 | + # The product field is added in setUpFields(). |
284 | + >>> view.view.field_names |
285 | + ['__visited_steps__'] |
286 | + >>> [form_field.__name__ for form_field in view.view.form_fields] |
287 | + ['__visited_steps__', 'product'] |
288 | + |
289 | + >>> print view.view.widgets.get('product')._getFormValue() |
290 | <BLANKLINE> |
291 | |
292 | >>> print package.productseries |
293 | None |
294 | |
295 | +This is a multistep view. In the first step, the product is specified. |
296 | + |
297 | + >>> print view.view.__class__.__name__ |
298 | + SourcePackageChangeUpstreamStepOne |
299 | + >>> print view.view.request.form |
300 | + {'field.__visited_steps__': 'sourcepackage_change_upstream_step1'} |
301 | + |
302 | >>> login_person(product.owner) |
303 | >>> form = { |
304 | - ... 'field.productseries': 'bonkers/crazy', |
305 | - ... 'field.actions.change': 'Change', |
306 | + ... 'field.product': 'bonkers', |
307 | + ... 'field.actions.continue': 'Continue', |
308 | ... } |
309 | + >>> form.update(view.view.request.form) |
310 | >>> view = create_initialized_view( |
311 | ... package, name='+edit-packaging', form=form, |
312 | ... principal=product.owner) |
313 | - >>> view.errors |
314 | + >>> view.view.errors |
315 | [] |
316 | |
317 | - >>> print view.next_url |
318 | +In the second step, one of the series of the previously selected |
319 | +product can be chosen from a list of options. |
320 | + |
321 | + >>> print view.view.__class__.__name__ |
322 | + SourcePackageChangeUpstreamStepTwo |
323 | + >>> print view.view.request.form['field.__visited_steps__'] |
324 | + sourcepackage_change_upstream_step1|sourcepackage_change_upstream_step2 |
325 | + >>> [term.token for term in view.view.widgets['productseries'].vocabulary] |
326 | + ['trunk', 'crazy'] |
327 | + |
328 | + >>> form = { |
329 | + ... 'field.__visited_steps__': 'sourcepackage_change_upstream_step2', |
330 | + ... 'field.product': 'bonkers', |
331 | + ... 'field.productseries': 'crazy', |
332 | + ... 'field.actions.continue': 'continue', |
333 | + ... } |
334 | + >>> view = create_initialized_view( |
335 | + ... package, name='+edit-packaging', form=form, |
336 | + ... principal=product.owner) |
337 | + |
338 | + >>> ignored = view.view.render() |
339 | + >>> print view.view.next_url |
340 | http://launchpad.dev/youbuntu/busy/+source/bonkers |
341 | |
342 | >>> for notification in view.request.response.notifications: |
343 | @@ -62,26 +94,40 @@ |
344 | |
345 | >>> transaction.commit() |
346 | |
347 | -The form shows the current product series if it is set. |
348 | +The form shows the current product if it is set. |
349 | |
350 | >>> view = create_initialized_view(package, name='+edit-packaging') |
351 | - >>> print view.widgets.get('productseries')._getFormValue().name |
352 | + |
353 | + >>> print view.view.widgets.get('product')._getFormValue().name |
354 | + bonkers |
355 | + |
356 | +If the same product as the current product series is selected, |
357 | +then the current product series will be the selected option. |
358 | + |
359 | + >>> form = { |
360 | + ... 'field.product': 'bonkers', |
361 | + ... 'field.actions.continue': 'Continue', |
362 | + ... } |
363 | + >>> form.update(view.view.request.form) |
364 | + >>> view = create_initialized_view( |
365 | + ... package, name='+edit-packaging', form=form, |
366 | + ... principal=product.owner) |
367 | + >>> print view.view.widgets.get('productseries')._getFormValue().name |
368 | crazy |
369 | |
370 | -The form requires a product series. An error is raised if the field is left |
371 | +The form requires a product. An error is raised if the field is left |
372 | empty. |
373 | |
374 | >>> form = { |
375 | - ... 'field.productseries': '', |
376 | - ... 'field.actions.change': 'Change', |
377 | + ... 'field.product': '', |
378 | + ... 'field.actions.continue': 'Continue', |
379 | ... } |
380 | >>> view = create_initialized_view( |
381 | ... package, name='+edit-packaging', form=form, |
382 | ... principal=product.owner) |
383 | - >>> for error in view.errors: |
384 | + >>> for error in view.view.errors: |
385 | ... print error |
386 | - ('productseries', u'Project series', RequiredMissing()) |
387 | - You must choose a project series. |
388 | + ('product', u'Project', RequiredMissing()) |
389 | |
390 | Submitting the same product series as the current packaging is not an error, |
391 | but there is no notification message that the upstream link was updated. |
392 | @@ -93,7 +139,7 @@ |
393 | >>> view = create_initialized_view( |
394 | ... package, name='+edit-packaging', form=form, |
395 | ... principal=product.owner) |
396 | - >>> view.errors |
397 | + >>> view.view.errors |
398 | [] |
399 | |
400 | >>> print view.request.response.notifications |
401 | |
402 | === modified file 'lib/lp/registry/stories/distribution/xx-distribution-packages.txt' |
403 | --- lib/lp/registry/stories/distribution/xx-distribution-packages.txt 2010-02-09 15:38:13 +0000 |
404 | +++ lib/lp/registry/stories/distribution/xx-distribution-packages.txt 2010-02-19 01:53:17 +0000 |
405 | @@ -284,8 +284,18 @@ |
406 | >>> print user_browser.url |
407 | http://launchpad.dev/ubuntu/warty/+source/iceweasel/+edit-packaging |
408 | |
409 | +In step one the project is specified. |
410 | + |
411 | >>> user_browser.getControl( |
412 | - ... name='field.productseries').value = "firefox/trunk" |
413 | + ... name='field.product').value = "firefox" |
414 | + >>> user_browser.getControl('Continue').click() |
415 | + |
416 | +In step two, one of the series for that project can be selected. |
417 | + |
418 | + >>> series_control = user_browser.getControl(name='field.productseries') |
419 | + >>> print series_control.options |
420 | + ['trunk', '1.0'] |
421 | + >>> series_control.value = ['trunk'] |
422 | >>> user_browser.getControl('Change').click() |
423 | |
424 | Go back to the source page, and now the upstream's description is shown and |
425 | |
426 | === modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt' |
427 | --- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-02-17 13:16:21 +0000 |
428 | +++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-02-19 01:53:17 +0000 |
429 | @@ -23,8 +23,9 @@ |
430 | project. He sets the upstream packaging link and sees that it is set. |
431 | |
432 | >>> user_browser.getLink('Set upstream link').click() |
433 | - >>> user_browser.getControl( |
434 | - ... name="field.productseries").value = 'thunderbird/trunk' |
435 | + >>> user_browser.getControl(name='field.product').value = 'thunderbird' |
436 | + >>> user_browser.getControl('Continue').click() |
437 | + >>> user_browser.getControl(name='field.productseries').value = ['trunk'] |
438 | >>> user_browser.getControl("Change").click() |
439 | >>> print extract_text(find_tag_by_id( |
440 | ... user_browser.contents, 'upstreams')) |
441 | |
442 | === modified file 'lib/lp/registry/templates/sourcepackage-edit-packaging.pt' |
443 | --- lib/lp/registry/templates/sourcepackage-edit-packaging.pt 2009-08-16 19:42:08 +0000 |
444 | +++ lib/lp/registry/templates/sourcepackage-edit-packaging.pt 2010-02-19 01:53:17 +0000 |
445 | @@ -10,11 +10,23 @@ |
446 | <div metal:fill-slot="main"> |
447 | |
448 | <div metal:use-macro="context/@@launchpad_form/form"> |
449 | - <p metal:fill-slot="extra_info"> |
450 | - Links from distribution packages to upstream project series let |
451 | - distribution and upstream maintainers share bugs, patches, and |
452 | - translations efficiently. |
453 | - </p> |
454 | + <div metal:fill-slot="extra_info"> |
455 | + <h2 class="legend" id="step-title">Step |
456 | + <tal:step_number tal:replace="view/step_number"/> |
457 | + (of <tal:total_steps tal:replace="view/total_steps"/>): |
458 | + <tal:step_description tal:replace="view/step_description"/> |
459 | + </h2> |
460 | + <p> |
461 | + Links from distribution packages to upstream project series let |
462 | + distribution and upstream maintainers share bugs, patches, and |
463 | + translations efficiently. |
464 | + </p> |
465 | + </div> |
466 | + |
467 | + <div metal:fill-slot="extra_bottom" tal:condition="view/product"> |
468 | + If you need a new series created, contact the owner of |
469 | + <a tal:content="structure view/product/fmt:link"/>. |
470 | + </div> |
471 | </div> |
472 | |
473 | </div> |
Summary
-------
Make $sourcepackage/ +edit-packaging a two step form since users are
confused by having to enter $project/$series.
Implementation details ------- ------- -
-------
The launchpadform was displaying "(Optional)" next to readonly widgets, canonical/ launchpad/ webapp/ launchpadform. py
which seems silly.
lib/
Converted SourcePackageCh angeUpstreamVie w to MultiStepView. lp/registry/ browser/ sourcepackage. py lp/registry/ stories/ distribution/ xx-distribution -packages. txt
lib/
lib/
Added multistep info and info on creating a new series if needed. lp/registry/ templates/ sourcepackage- edit-packaging. pt
lib/
Tests
-----
./bin/test -vv -t xx-distribution -packages. txt
Demo and Q/A
------------
* Open https:/ /launchpad. dev/ubuntu/ warty/+ source/ iceweasel/ +edit-packaging
* Enter a project name.
* Click "Continue".
* Select a series.
* Click "Change".
* Verify that the series has changed on the sourcepackage page.