Merge lp:~sinzui/launchpad/package-link-validation-3 into lp:launchpad
- package-link-validation-3
- Merge into devel
Proposed by
Curtis Hovey
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~sinzui/launchpad/package-link-validation-3 |
Merge into: | lp:launchpad |
Diff against target: |
1099 lines 14 files modified
lib/canonical/launchpad/icing/style-3-0.css (+3/-0) lib/lp/registry/browser/configure.zcml (+0/-3) lib/lp/registry/browser/distributionsourcepackage.py (+22/-84) lib/lp/registry/browser/packaging.py (+83/-0) lib/lp/registry/browser/tests/packaging-views.txt (+237/-0) lib/lp/registry/browser/tests/productseries-views.txt (+0/-229) lib/lp/registry/browser/tests/test_packaging.py (+1/-1) lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt (+4/-3) lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt (+7/-5) lib/lp/registry/templates/distributionsourcepackage-index.pt (+6/-5) lib/lp/registry/templates/productseries-index.pt (+2/-37) lib/lp/registry/templates/productseries-portlet-packages.pt (+42/-21) lib/lp/registry/templates/sourcepackage-index.pt (+1/-3) lib/lp/registry/templates/sourcepackage-portlet-upstream.pt (+0/-17) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/package-link-validation-3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Abel Deuring (community) | code | Approve | |
Review via email: mp+13844@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote : | # |
Revision history for this message
Curtis Hovey (sinzui) wrote : | # |
You can see the new remove icon at
http://
Revision history for this message
Abel Deuring (adeuring) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/canonical/launchpad/icing/style-3-0.css' |
2 | --- lib/canonical/launchpad/icing/style-3-0.css 2009-10-22 15:22:42 +0000 |
3 | +++ lib/canonical/launchpad/icing/style-3-0.css 2009-10-24 20:01:11 +0000 |
4 | @@ -383,6 +383,9 @@ |
5 | form table tbody th { |
6 | font-weight: bold; |
7 | } |
8 | +input[type='image'] { |
9 | + vertical-align: middle; |
10 | + } |
11 | em { |
12 | font-style: italic; |
13 | } |
14 | |
15 | === modified file 'lib/lp/registry/browser/configure.zcml' |
16 | --- lib/lp/registry/browser/configure.zcml 2009-10-21 01:55:17 +0000 |
17 | +++ lib/lp/registry/browser/configure.zcml 2009-10-24 20:01:11 +0000 |
18 | @@ -2001,9 +2001,6 @@ |
19 | facet="overview" |
20 | permission="zope.Public"> |
21 | <browser:page |
22 | - name="+portlet-upstream" |
23 | - template="../templates/sourcepackage-portlet-upstream.pt"/> |
24 | - <browser:page |
25 | name="+portlet-releases" |
26 | template="../templates/sourcepackage-portlet-releases.pt"/> |
27 | </browser:pages> |
28 | |
29 | === modified file 'lib/lp/registry/browser/distributionsourcepackage.py' |
30 | --- lib/lp/registry/browser/distributionsourcepackage.py 2009-10-23 22:09:49 +0000 |
31 | +++ lib/lp/registry/browser/distributionsourcepackage.py 2009-10-24 20:01:11 +0000 |
32 | @@ -20,20 +20,16 @@ |
33 | import pytz |
34 | |
35 | from zope.component import getUtility |
36 | -from zope.formlib import form |
37 | from zope.interface import implements, Interface |
38 | -from zope.schema import Choice |
39 | -from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary |
40 | |
41 | from canonical.cachedproperty import cachedproperty |
42 | -from canonical.launchpad import _ |
43 | from canonical.launchpad.interfaces import IBugSet |
44 | from lp.answers.interfaces.questionenums import QuestionStatus |
45 | from lp.soyuz.interfaces.archive import IArchiveSet |
46 | from lp.soyuz.interfaces.distributionsourcepackagerelease import ( |
47 | IDistributionSourcePackageRelease) |
48 | from lp.soyuz.interfaces.packagediff import IPackageDiffSet |
49 | -from lp.registry.interfaces.packaging import IPackagingUtil |
50 | +from lp.registry.browser.packaging import PackagingDeleteView |
51 | from lp.registry.interfaces.pocket import pocketsuffix |
52 | from lp.registry.interfaces.product import IDistributionSourcePackage |
53 | from lp.bugs.browser.bugtask import BugTargetTraversalMixin |
54 | @@ -42,7 +38,7 @@ |
55 | from canonical.launchpad.browser.structuralsubscription import ( |
56 | StructuralSubscriptionTargetTraversalMixin) |
57 | from canonical.launchpad.webapp import ( |
58 | - ApplicationMenu, LaunchpadEditFormView, LaunchpadFormView, LaunchpadView, |
59 | + ApplicationMenu, LaunchpadEditFormView, LaunchpadView, |
60 | Link, Navigation, StandardLaunchpadFacets, action, canonical_url, |
61 | redirection) |
62 | from canonical.launchpad.webapp.menu import ( |
63 | @@ -57,6 +53,7 @@ |
64 | |
65 | class DistributionSourcePackageBreadcrumb(Breadcrumb): |
66 | """Builds a breadcrumb for an `IDistributionSourcePackage`.""" |
67 | + |
68 | @property |
69 | def text(self): |
70 | return smartquote('"%s" package') % ( |
71 | @@ -71,6 +68,7 @@ |
72 | |
73 | |
74 | class DistributionSourcePackageLinksMixin: |
75 | + |
76 | def subscribe(self): |
77 | return Link('+subscribe', 'Subscribe to bug mail', icon='edit') |
78 | |
79 | @@ -184,6 +182,8 @@ |
80 | """Common features to all `DistributionSourcePackage` views.""" |
81 | |
82 | def releases(self): |
83 | + """All releases for this `IDistributionSourcePackage`.""" |
84 | + |
85 | def not_empty(text): |
86 | return ( |
87 | text is not None and isinstance(text, basestring) |
88 | @@ -215,11 +215,10 @@ |
89 | result_set = self.context.getPersonsByEmail(unique_emails) |
90 | # Ignore the persons who want their email addresses hidden. |
91 | self._person_data = dict( |
92 | - [(email.email,person) for (email,person) in result_set |
93 | + [(email.email, person) for (email, person) in result_set |
94 | if not person.hide_email_addresses]) |
95 | else: |
96 | self._person_data = None |
97 | - |
98 | # Collate diffs for relevant SourcePackageReleases |
99 | pkg_diffs = getUtility(IPackageDiffSet).getDiffsToReleases(sprs) |
100 | spr_diffs = {} |
101 | @@ -235,7 +234,7 @@ |
102 | |
103 | |
104 | class DistributionSourcePackageView(DistributionSourcePackageBaseView, |
105 | - LaunchpadFormView): |
106 | + PackagingDeleteView): |
107 | """View class for DistributionSourcePackage.""" |
108 | implements(IDistributionSourcePackageActionMenu) |
109 | |
110 | @@ -243,17 +242,18 @@ |
111 | def label(self): |
112 | return self.context.title |
113 | |
114 | - def setUpFields(self): |
115 | + @property |
116 | + def next_url(self): |
117 | """See `LaunchpadFormView`.""" |
118 | - # No schema is set in this form, because all fields are created with |
119 | - # custom vocabularies. So we must not call the inherited setUpField |
120 | - # method. |
121 | - self.form_fields = self._createPackagingField() |
122 | + return canonical_url(self.context) |
123 | |
124 | @property |
125 | - def can_delete_packaging(self): |
126 | - """Whether the user can delete existing packaging links.""" |
127 | - return self.user is not None |
128 | + def all_packaging(self): |
129 | + """See `PackagingDeleteView`.""" |
130 | + for sourcepackage in self.context.get_distroseries_packages(): |
131 | + packaging = sourcepackage.direct_packaging |
132 | + if packaging is not None: |
133 | + yield packaging |
134 | |
135 | @property |
136 | def all_published_in_active_distroseries(self): |
137 | @@ -267,11 +267,11 @@ |
138 | for pub in self.context.current_publishing_records: |
139 | if pub.distroseries.active: |
140 | entry = { |
141 | - "suite" : (pub.distroseries.name.capitalize() + |
142 | + "suite": (pub.distroseries.name.capitalize() + |
143 | pocketsuffix[pub.pocket]), |
144 | - "description" : "(%s): %s/%s" % ( |
145 | + "description": "(%s): %s/%s" % ( |
146 | pub.sourcepackagerelease.version, |
147 | - pub.component.name, pub.section.name) |
148 | + pub.component.name, pub.section.name), |
149 | } |
150 | results.append(entry) |
151 | return results |
152 | @@ -331,12 +331,12 @@ |
153 | versions.append( |
154 | "%s (%s)" % ( |
155 | pub.distroseries.displayname, |
156 | - pub.source_package_version |
157 | + pub.source_package_version, |
158 | ) |
159 | ) |
160 | archive_versions.append({ |
161 | 'archive': archive, |
162 | - 'versions': ", ".join(versions) |
163 | + 'versions': ", ".join(versions), |
164 | }) |
165 | |
166 | return archive_versions |
167 | @@ -350,68 +350,6 @@ |
168 | self.context.name, |
169 | ) |
170 | |
171 | - def _createPackagingField(self): |
172 | - """Create a field to specify a Packaging association. |
173 | - |
174 | - Create a contextual vocabulary that can specify one of the Packaging |
175 | - associated to this DistributionSourcePackage. |
176 | - """ |
177 | - terms = [] |
178 | - for sourcepackage in self.context.get_distroseries_packages(): |
179 | - packaging = sourcepackage.direct_packaging |
180 | - if packaging is None: |
181 | - continue |
182 | - terms.append(SimpleTerm(packaging, packaging.id)) |
183 | - return form.Fields( |
184 | - Choice(__name__='packaging', vocabulary=SimpleVocabulary(terms), |
185 | - required=True)) |
186 | - |
187 | - def _renderHiddenPackagingField(self, packaging): |
188 | - """Render a hidden input that fills in the packaging field.""" |
189 | - if not self.can_delete_packaging: |
190 | - return None |
191 | - vocabulary = self.form_fields['packaging'].field.vocabulary |
192 | - return '<input type="hidden" name="field.packaging" value="%s" />' % ( |
193 | - vocabulary.getTerm(packaging).token) |
194 | - |
195 | - def renderDeletePackagingAction(self): |
196 | - """Render a submit input for the delete_packaging_action.""" |
197 | - assert self.can_delete_packaging, 'User cannot delete Packaging.' |
198 | - return ('<input type="submit" class="button" value="Delete Link" ' |
199 | - 'style="padding: 0pt; font-size: 80%%" ' |
200 | - 'name="%s"/>' % (self.delete_packaging_action.__name__,)) |
201 | - |
202 | - def handleDeletePackagingError(self, action, data, errors): |
203 | - """Handle errors on package link deletion. |
204 | - |
205 | - If 'packaging' is not set in the form data, we assume that means the |
206 | - provided Packaging id was not found, which should only happen if the |
207 | - same Packaging object was concurrently deleted. In this case, we want |
208 | - to display a more informative error message than the default 'Invalid |
209 | - value'. |
210 | - """ |
211 | - if data.get('packaging') is None: |
212 | - self.setFieldError( |
213 | - 'packaging', |
214 | - _("This upstream association was deleted already.")) |
215 | - |
216 | - @action(_("Delete Link"), name='delete_packaging', |
217 | - failure=handleDeletePackagingError) |
218 | - def delete_packaging_action(self, action, data): |
219 | - """Delete a Packaging association.""" |
220 | - packaging = data['packaging'] |
221 | - productseries = packaging.productseries |
222 | - distroseries = packaging.distroseries |
223 | - getUtility(IPackagingUtil).deletePackaging( |
224 | - productseries, packaging.sourcepackagename, distroseries) |
225 | - self.request.response.addNotification( |
226 | - _("Removed upstream association between ${product} " |
227 | - "${productseries} and ${distroseries}.", mapping=dict( |
228 | - product=productseries.product.displayname, |
229 | - productseries=productseries.displayname, |
230 | - distroseries=distroseries.displayname))) |
231 | - self.next_url = canonical_url(self.context) |
232 | - |
233 | @cachedproperty |
234 | def active_distroseries_packages(self): |
235 | """Cached proxy call to context/get_distroseries_packages.""" |
236 | |
237 | === modified file 'lib/lp/registry/browser/packaging.py' |
238 | --- lib/lp/registry/browser/packaging.py 2009-10-21 21:16:09 +0000 |
239 | +++ lib/lp/registry/browser/packaging.py 2009-10-24 20:01:11 +0000 |
240 | @@ -5,9 +5,13 @@ |
241 | |
242 | __all__ = [ |
243 | 'PackagingAddView', |
244 | + 'PackagingDeleteView', |
245 | ] |
246 | |
247 | from zope.component import getUtility |
248 | +from zope.formlib import form |
249 | +from zope.schema import Choice |
250 | +from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary |
251 | |
252 | from canonical.launchpad import _ |
253 | from lp.registry.interfaces.packaging import ( |
254 | @@ -77,3 +81,82 @@ |
255 | getUtility(IPackagingUtil).createPackaging( |
256 | productseries, data['sourcepackagename'], data['distroseries'], |
257 | data['packaging'], owner=self.user) |
258 | + |
259 | + |
260 | +class PackagingDeleteView(LaunchpadFormView): |
261 | + """A base view that provides packaging link deletion.""" |
262 | + |
263 | + @property |
264 | + def all_packaging(self): |
265 | + """An iterator of the context's packaging links.""" |
266 | + raise NotImplementedError |
267 | + |
268 | + def setUpFields(self): |
269 | + """See `LaunchpadFormView`.""" |
270 | + # No schema is set in this form, because all fields are created with |
271 | + # custom vocabularies. So we must not call the inherited setUpField |
272 | + # method. |
273 | + self.form_fields = self._createPackagingField() |
274 | + |
275 | + @property |
276 | + def can_delete_packaging(self): |
277 | + """Whether the user can delete existing packaging links.""" |
278 | + return self.user is not None |
279 | + |
280 | + def _createPackagingField(self): |
281 | + """Create a field to specify a Packaging association. |
282 | + |
283 | + Create a contextual vocabulary that can specify one of the Packaging |
284 | + associated to this DistributionSourcePackage. |
285 | + """ |
286 | + terms = [] |
287 | + for packaging in self.all_packaging: |
288 | + terms.append(SimpleTerm(packaging, packaging.id)) |
289 | + return form.Fields( |
290 | + Choice(__name__='packaging', vocabulary=SimpleVocabulary(terms), |
291 | + required=True)) |
292 | + |
293 | + def _renderHiddenPackagingField(self, packaging): |
294 | + """Render a hidden input that fills in the packaging field.""" |
295 | + if not self.can_delete_packaging: |
296 | + return None |
297 | + vocabulary = self.form_fields['packaging'].field.vocabulary |
298 | + return '<input type="hidden" name="field.packaging" value="%s" />' % ( |
299 | + vocabulary.getTerm(packaging).token) |
300 | + |
301 | + def renderDeletePackagingAction(self): |
302 | + """Render a submit input for the delete_packaging_action.""" |
303 | + assert self.can_delete_packaging, 'User cannot delete Packaging.' |
304 | + return ('<input type="image" value="Delete Link" ' |
305 | + 'src="/@@/remove" title="Delete upsteam link" ' |
306 | + 'name="%s"/>' % self.delete_packaging_action.__name__) |
307 | + |
308 | + def handleDeletePackagingError(self, action, data, errors): |
309 | + """Handle errors on package link deletion. |
310 | + |
311 | + If 'packaging' is not set in the form data, we assume that means the |
312 | + provided Packaging id was not found, which should only happen if the |
313 | + same Packaging object was concurrently deleted. In this case, we want |
314 | + to display a more informative error message than the default 'Invalid |
315 | + value'. |
316 | + """ |
317 | + if data.get('packaging') is None: |
318 | + self.setFieldError( |
319 | + 'packaging', |
320 | + _("This upstream association was deleted already.")) |
321 | + |
322 | + @action(_("Delete Link"), name='delete_packaging', |
323 | + failure=handleDeletePackagingError) |
324 | + def delete_packaging_action(self, action, data): |
325 | + """Delete a Packaging association.""" |
326 | + packaging = data['packaging'] |
327 | + productseries = packaging.productseries |
328 | + distroseries = packaging.distroseries |
329 | + getUtility(IPackagingUtil).deletePackaging( |
330 | + productseries, packaging.sourcepackagename, distroseries) |
331 | + self.request.response.addNotification( |
332 | + _("Removed upstream association between ${product} " |
333 | + "${productseries} and ${distroseries}.", mapping=dict( |
334 | + product=productseries.product.displayname, |
335 | + productseries=productseries.displayname, |
336 | + distroseries=distroseries.displayname))) |
337 | |
338 | === added file 'lib/lp/registry/browser/tests/packaging-views.txt' |
339 | --- lib/lp/registry/browser/tests/packaging-views.txt 1970-01-01 00:00:00 +0000 |
340 | +++ lib/lp/registry/browser/tests/packaging-views.txt 2009-10-24 20:01:11 +0000 |
341 | @@ -0,0 +1,237 @@ |
342 | +Packaging views |
343 | +=============== |
344 | + |
345 | +Packaging links connect a sourcepackage to a distroseries and a productseries. |
346 | + |
347 | + |
348 | +Productseries linking packages |
349 | +------------------------------ |
350 | + |
351 | +Distro series sourcepackages can be linked to product series using the |
352 | ++addpackage named view. |
353 | + |
354 | + >>> from canonical.launchpad.interfaces.launchpad import ( |
355 | + ... ILaunchpadCelebrities) |
356 | + |
357 | + >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
358 | + >>> hoary = ubuntu.getSeries('hoary') |
359 | + >>> sourcepackagename = factory.makeSourcePackageName('hot') |
360 | + >>> sourcepackage = factory.makeSourcePackage( |
361 | + ... sourcepackagename=sourcepackagename, distroseries=hoary) |
362 | + >>> product = factory.makeProduct(name="hot", displayname='Hot') |
363 | + >>> productseries = factory.makeProductSeries( |
364 | + ... product=product, name='hotter') |
365 | + >>> productseries.sourcepackages |
366 | + [] |
367 | + |
368 | +The view has a label and requires a distro series, source package name, |
369 | +and a packaging contents. |
370 | + |
371 | + >>> view = create_view(productseries, '+addpackage') |
372 | + >>> print view.label |
373 | + Packaging of hotter in distributions |
374 | + |
375 | + >>> print view.page_title |
376 | + Packaging of hotter in distributions |
377 | + |
378 | + >>> print view.field_names |
379 | + ['distroseries', 'sourcepackagename', 'packaging'] |
380 | + |
381 | + >>> print view.cancel_url |
382 | + http://launchpad.dev/hot/hotter |
383 | + |
384 | + >>> form = { |
385 | + ... 'field.distroseries': 'ubuntu/hoary', |
386 | + ... 'field.sourcepackagename': 'hot', |
387 | + ... 'field.packaging': 'Primary Product', |
388 | + ... 'field.actions.continue': 'Continue', |
389 | + ... } |
390 | + >>> view = create_initialized_view( |
391 | + ... productseries, '+addpackage', form=form) |
392 | + >>> view.errors |
393 | + [] |
394 | + >>> for package in productseries.sourcepackages: |
395 | + ... print package.name |
396 | + hot |
397 | + |
398 | + >>> transaction.commit() |
399 | + |
400 | +It is an error to link a series to the same package and distro series twice. |
401 | + |
402 | + >>> form = { |
403 | + ... 'field.distroseries': 'ubuntu/hoary', |
404 | + ... 'field.sourcepackagename': 'hot', |
405 | + ... 'field.packaging': 'Primary Product', |
406 | + ... 'field.actions.continue': 'Continue', |
407 | + ... } |
408 | + >>> view = create_initialized_view( |
409 | + ... productseries, '+addpackage', form=form) |
410 | + >>> for error in view.errors: |
411 | + ... print error |
412 | + This series is already packaged in Hoary. |
413 | + |
414 | +Once a distro series sourcepackage is linked to a product series, no other |
415 | +product series can link to it. |
416 | + |
417 | + >>> other_productseries = factory.makeProductSeries( |
418 | + ... product=product, name='hotest') |
419 | + >>> form = { |
420 | + ... 'field.distroseries': 'ubuntu/hoary', |
421 | + ... 'field.sourcepackagename': 'hot', |
422 | + ... 'field.packaging': 'Primary Product', |
423 | + ... 'field.actions.continue': 'Continue', |
424 | + ... } |
425 | + >>> view = create_initialized_view( |
426 | + ... other_productseries, '+addpackage', form=form) |
427 | + >>> for error in view.errors: |
428 | + ... print error |
429 | + The <a href=".../hoary/+source/hot">hot</a> package in Hoary is already |
430 | + linked to another series. |
431 | + |
432 | +A source package name must be provided. |
433 | + |
434 | + >>> form = { |
435 | + ... 'field.distroseries': 'ubuntu/hoary', |
436 | + ... 'field.sourcepackagename': '', |
437 | + ... 'field.packaging': 'Primary Product', |
438 | + ... 'field.actions.continue': 'Continue', |
439 | + ... } |
440 | + >>> view = create_initialized_view( |
441 | + ... productseries, '+addpackage', form=form) |
442 | + >>> for error in view.errors: |
443 | + ... print error |
444 | + ('sourcepackagename', u'Source Package Name', RequiredMissing()) |
445 | + You must choose the source package name. |
446 | + |
447 | +The +addpackage view provides the default_distroseries property. It is None |
448 | +by default, but subclasses may change it. |
449 | + |
450 | + >>> print view.default_distroseries |
451 | + None |
452 | + |
453 | + |
454 | +Productseries linking Ubuntu packages |
455 | +------------------------------------- |
456 | + |
457 | +The +ubuntupkg named view is a subclass of the +addpackage named view. It |
458 | +allows the user to update the current linked Ubuntu package. |
459 | + |
460 | + >>> from lp.registry.browser.packaging import PackagingAddView |
461 | + |
462 | + >>> view = create_initialized_view(productseries, '+ubuntupkg') |
463 | + >>> isinstance(view, PackagingAddView) |
464 | + True |
465 | + |
466 | + >>> print view.label |
467 | + Ubuntu source packaging |
468 | + |
469 | + >>> print view.page_title |
470 | + Ubuntu source packaging |
471 | + |
472 | + >>> print view.field_names |
473 | + ['sourcepackagename'] |
474 | + |
475 | + >>> print view.cancel_url |
476 | + http://launchpad.dev/hot/hotter |
477 | + |
478 | +The view restricts the packaging to the current Ubuntu series. |
479 | + |
480 | + >>> print view.default_distroseries.name |
481 | + hoary |
482 | + |
483 | +The sourcepackagename is None if the package link was never set. The view's |
484 | +packaging history is empty, and the sourcepackagename widget is empty. |
485 | + |
486 | + >>> new_productseries = factory.makeProductSeries( |
487 | + ... product=product, name='cold') |
488 | + >>> view = create_initialized_view(new_productseries, '+ubuntupkg') |
489 | + |
490 | + >>> print view.default_sourcepackagename |
491 | + None |
492 | + |
493 | + >>> print view.widgets.get('sourcepackagename')._getFormValue() |
494 | + <BLANKLINE> |
495 | + |
496 | + >>> print view.ubuntu_history |
497 | + [] |
498 | + |
499 | +Series have been packaged in Ubuntu do have the current information and |
500 | +a history. |
501 | + |
502 | + >>> view = create_initialized_view(productseries, '+ubuntupkg') |
503 | + >>> print view.default_sourcepackagename.name |
504 | + hot |
505 | + |
506 | + >>> print view.widgets.get('sourcepackagename')._getFormValue().name |
507 | + hot |
508 | + |
509 | + >>> for packaging in view.ubuntu_history: |
510 | + ... print packaging.distroseries.name |
511 | + ... print packaging.sourcepackagename.name |
512 | + hoary hot |
513 | + |
514 | +The package in the current Ubuntu series can be updated. |
515 | + |
516 | + >>> form = { |
517 | + ... 'field.sourcepackagename': 'thunderbird', |
518 | + ... 'field.actions.continue': 'Update', |
519 | + ... } |
520 | + >>> view = create_initialized_view( |
521 | + ... productseries, '+ubuntupkg', form=form) |
522 | + >>> view.errors |
523 | + [] |
524 | + |
525 | + >>> for packaging in view.ubuntu_history: |
526 | + ... print packaging.distroseries.name |
527 | + ... print packaging.sourcepackagename.name |
528 | + hoary thunderbird |
529 | + |
530 | +It is not an error to submit the same sourcepackagename information, the |
531 | +action is ignored because there is no change |
532 | + |
533 | + >>> form = { |
534 | + ... 'field.sourcepackagename': 'thunderbird', |
535 | + ... 'field.actions.continue': 'Update', |
536 | + ... } |
537 | + >>> view = create_initialized_view( |
538 | + ... productseries, '+ubuntupkg', form=form) |
539 | + >>> view.errors |
540 | + [] |
541 | + |
542 | + >>> for packaging in view.ubuntu_history: |
543 | + ... print packaging.distroseries.name |
544 | + ... print packaging.sourcepackagename.name |
545 | + hoary thunderbird |
546 | + |
547 | +When the current Ubuntu series changes, the sourcepackagename is not known, |
548 | +and a new entry can be added to the packaging history. |
549 | + |
550 | + >>> from lp.registry.interfaces.distroseries import DistroSeriesStatus |
551 | + |
552 | + >>> login('admin@canonical.com') |
553 | + >>> hoary.status = DistroSeriesStatus.CURRENT |
554 | + >>> grumpy_series = ubuntu.getSeries('grumpy') |
555 | + >>> grumpy_series.status = DistroSeriesStatus.FROZEN |
556 | + |
557 | + >>> a_user = factory.makePerson(name="hedgehog") |
558 | + >>> login_person(a_user) |
559 | + >>> form = { |
560 | + ... 'field.sourcepackagename': 'hot', |
561 | + ... 'field.actions.continue': 'Update', |
562 | + ... } |
563 | + >>> view = create_initialized_view( |
564 | + ... productseries, '+ubuntupkg', form=form) |
565 | + >>> view.errors |
566 | + [] |
567 | + |
568 | + >>> print view.default_distroseries.name |
569 | + grumpy |
570 | + |
571 | + >>> print view.default_sourcepackagename |
572 | + None |
573 | + |
574 | + >>> for packaging in view.ubuntu_history: |
575 | + ... print packaging.distroseries.name |
576 | + ... print packaging.sourcepackagename.name |
577 | + grumpy hot |
578 | + hoary thunderbird |
579 | |
580 | === modified file 'lib/lp/registry/browser/tests/productseries-views.txt' |
581 | --- lib/lp/registry/browser/tests/productseries-views.txt 2009-10-21 17:09:39 +0000 |
582 | +++ lib/lp/registry/browser/tests/productseries-views.txt 2009-10-24 20:01:11 +0000 |
583 | @@ -323,232 +323,3 @@ |
584 | True |
585 | >>> print productseries.name |
586 | field-rabbit-20090501-193424 |
587 | - |
588 | - |
589 | -Linking packages |
590 | ----------------- |
591 | - |
592 | -Distro series sourcepackages can be linked to product series using the |
593 | -+addpackage named view. |
594 | - |
595 | - >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
596 | - >>> hoary = ubuntu.getSeries('hoary') |
597 | - >>> sourcepackagename = factory.makeSourcePackageName('hot') |
598 | - >>> sourcepackage = factory.makeSourcePackage( |
599 | - ... sourcepackagename=sourcepackagename, distroseries=hoary) |
600 | - >>> product = factory.makeProduct(name="hot", displayname='Hot') |
601 | - >>> productseries = factory.makeProductSeries( |
602 | - ... product=product, name='hotter') |
603 | - >>> productseries.sourcepackages |
604 | - [] |
605 | - |
606 | -The view has a label and requires a distro series, source package name, |
607 | -and a packaging contents. |
608 | - |
609 | - >>> view = create_view(productseries, '+addpackage') |
610 | - >>> print view.label |
611 | - Packaging of hotter in distributions |
612 | - |
613 | - >>> print view.page_title |
614 | - Packaging of hotter in distributions |
615 | - |
616 | - >>> print view.field_names |
617 | - ['distroseries', 'sourcepackagename', 'packaging'] |
618 | - |
619 | - >>> print view.cancel_url |
620 | - http://launchpad.dev/hot/hotter |
621 | - |
622 | - >>> form = { |
623 | - ... 'field.distroseries': 'ubuntu/hoary', |
624 | - ... 'field.sourcepackagename': 'hot', |
625 | - ... 'field.packaging': 'Primary Product', |
626 | - ... 'field.actions.continue': 'Continue', |
627 | - ... } |
628 | - >>> view = create_initialized_view( |
629 | - ... productseries, '+addpackage', form=form) |
630 | - >>> view.errors |
631 | - [] |
632 | - >>> for package in productseries.sourcepackages: |
633 | - ... print package.name |
634 | - hot |
635 | - |
636 | - >>> transaction.commit() |
637 | - |
638 | -It is an error to link a series to the same package and distro series twice. |
639 | - |
640 | - >>> form = { |
641 | - ... 'field.distroseries': 'ubuntu/hoary', |
642 | - ... 'field.sourcepackagename': 'hot', |
643 | - ... 'field.packaging': 'Primary Product', |
644 | - ... 'field.actions.continue': 'Continue', |
645 | - ... } |
646 | - >>> view = create_initialized_view( |
647 | - ... productseries, '+addpackage', form=form) |
648 | - >>> for error in view.errors: |
649 | - ... print error |
650 | - This series is already packaged in Hoary. |
651 | - |
652 | -Once a distro series sourcepackage is linked to a product series, no other |
653 | -product series can link to it. |
654 | - |
655 | - >>> other_productseries = factory.makeProductSeries( |
656 | - ... product=product, name='hotest') |
657 | - >>> form = { |
658 | - ... 'field.distroseries': 'ubuntu/hoary', |
659 | - ... 'field.sourcepackagename': 'hot', |
660 | - ... 'field.packaging': 'Primary Product', |
661 | - ... 'field.actions.continue': 'Continue', |
662 | - ... } |
663 | - >>> view = create_initialized_view( |
664 | - ... other_productseries, '+addpackage', form=form) |
665 | - >>> for error in view.errors: |
666 | - ... print error |
667 | - The <a href=".../hoary/+source/hot">hot</a> package in Hoary is already |
668 | - linked to another series. |
669 | - |
670 | -A source package name must be provided. |
671 | - |
672 | - >>> form = { |
673 | - ... 'field.distroseries': 'ubuntu/hoary', |
674 | - ... 'field.sourcepackagename': '', |
675 | - ... 'field.packaging': 'Primary Product', |
676 | - ... 'field.actions.continue': 'Continue', |
677 | - ... } |
678 | - >>> view = create_initialized_view( |
679 | - ... productseries, '+addpackage', form=form) |
680 | - >>> for error in view.errors: |
681 | - ... print error |
682 | - ('sourcepackagename', u'Source Package Name', RequiredMissing()) |
683 | - You must choose the source package name. |
684 | - |
685 | -The +addpackage view provides the default_distroseries property. It is None |
686 | -by default, but subclasses may change it. |
687 | - |
688 | - >>> print view.default_distroseries |
689 | - None |
690 | - |
691 | - |
692 | -Linking Ubuntu packages |
693 | ------------------------ |
694 | - |
695 | -The +ubuntupkg named view is a subclass of the +addpackage named view. It |
696 | -allows the user to update the current linked Ubuntu package. |
697 | - |
698 | - >>> from lp.registry.browser.packaging import PackagingAddView |
699 | - |
700 | - >>> view = create_initialized_view(productseries, '+ubuntupkg') |
701 | - >>> isinstance(view, PackagingAddView) |
702 | - True |
703 | - |
704 | - >>> print view.label |
705 | - Ubuntu source packaging |
706 | - |
707 | - >>> print view.page_title |
708 | - Ubuntu source packaging |
709 | - |
710 | - >>> print view.field_names |
711 | - ['sourcepackagename'] |
712 | - |
713 | - >>> print view.cancel_url |
714 | - http://launchpad.dev/hot/hotter |
715 | - |
716 | -The view restricts the packaging to the current Ubuntu series. |
717 | - |
718 | - >>> print view.default_distroseries.name |
719 | - hoary |
720 | - |
721 | -The sourcepackagename is None if the package link was never set. The view's |
722 | -packaging history is empty, and the sourcepackagename widget is empty. |
723 | - |
724 | - >>> new_productseries = factory.makeProductSeries( |
725 | - ... product=product, name='cold') |
726 | - >>> view = create_initialized_view(new_productseries, '+ubuntupkg') |
727 | - |
728 | - >>> print view.default_sourcepackagename |
729 | - None |
730 | - |
731 | - >>> print view.widgets.get('sourcepackagename')._getFormValue() |
732 | - <BLANKLINE> |
733 | - |
734 | - >>> print view.ubuntu_history |
735 | - [] |
736 | - |
737 | -Series have been packaged in Ubuntu do have the current information and |
738 | -a history. |
739 | - |
740 | - >>> view = create_initialized_view(productseries, '+ubuntupkg') |
741 | - >>> print view.default_sourcepackagename.name |
742 | - hot |
743 | - |
744 | - >>> print view.widgets.get('sourcepackagename')._getFormValue().name |
745 | - hot |
746 | - |
747 | - >>> for packaging in view.ubuntu_history: |
748 | - ... print packaging.distroseries.name |
749 | - ... print packaging.sourcepackagename.name |
750 | - hoary hot |
751 | - |
752 | -The package in the current Ubuntu series can be updated. |
753 | - |
754 | - >>> form = { |
755 | - ... 'field.sourcepackagename': 'thunderbird', |
756 | - ... 'field.actions.continue': 'Update', |
757 | - ... } |
758 | - >>> view = create_initialized_view( |
759 | - ... productseries, '+ubuntupkg', form=form) |
760 | - >>> view.errors |
761 | - [] |
762 | - |
763 | - >>> for packaging in view.ubuntu_history: |
764 | - ... print packaging.distroseries.name |
765 | - ... print packaging.sourcepackagename.name |
766 | - hoary thunderbird |
767 | - |
768 | -It is not an error to submit the same sourcepackagename information, the |
769 | -action is ignored because there is no change |
770 | - |
771 | - >>> form = { |
772 | - ... 'field.sourcepackagename': 'thunderbird', |
773 | - ... 'field.actions.continue': 'Update', |
774 | - ... } |
775 | - >>> view = create_initialized_view( |
776 | - ... productseries, '+ubuntupkg', form=form) |
777 | - >>> view.errors |
778 | - [] |
779 | - |
780 | - >>> for packaging in view.ubuntu_history: |
781 | - ... print packaging.distroseries.name |
782 | - ... print packaging.sourcepackagename.name |
783 | - hoary thunderbird |
784 | - |
785 | -When the current Ubuntu series changes, the sourcepackagename is not known, |
786 | -and a new entry can be added to the packaging history. |
787 | - |
788 | - >>> from lp.registry.interfaces.distroseries import DistroSeriesStatus |
789 | - |
790 | - >>> login('admin@canonical.com') |
791 | - >>> hoary.status = DistroSeriesStatus.CURRENT |
792 | - >>> grumpy_series = ubuntu.getSeries('grumpy') |
793 | - >>> grumpy_series.status = DistroSeriesStatus.FROZEN |
794 | - |
795 | - >>> login_person(a_user) |
796 | - >>> form = { |
797 | - ... 'field.sourcepackagename': 'hot', |
798 | - ... 'field.actions.continue': 'Update', |
799 | - ... } |
800 | - >>> view = create_initialized_view( |
801 | - ... productseries, '+ubuntupkg', form=form) |
802 | - >>> view.errors |
803 | - [] |
804 | - |
805 | - >>> print view.default_distroseries.name |
806 | - grumpy |
807 | - |
808 | - >>> print view.default_sourcepackagename |
809 | - None |
810 | - |
811 | - >>> for packaging in view.ubuntu_history: |
812 | - ... print packaging.distroseries.name |
813 | - ... print packaging.sourcepackagename.name |
814 | - grumpy hot |
815 | - hoary thunderbird |
816 | |
817 | === modified file 'lib/lp/registry/browser/tests/test_packaging.py' |
818 | --- lib/lp/registry/browser/tests/test_packaging.py 2009-10-16 15:00:55 +0000 |
819 | +++ lib/lp/registry/browser/tests/test_packaging.py 2009-10-24 20:01:11 +0000 |
820 | @@ -56,7 +56,7 @@ |
821 | user_browser = self.user_browser |
822 | user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils') |
823 | form = user_browser.getForm("delete_warty_alsa-utils_trunk") |
824 | - form.getControl("Delete Link").click() |
825 | + form.getControl(name="field.actions.delete_packaging").click() |
826 | # Check that the change was committed. |
827 | login('no-priv@canonical.com') |
828 | self.assertFalse(packaging_util.packagingEntryExists( |
829 | |
830 | === renamed file 'lib/lp/registry/stories/distribution/xx-distributionsourcepackage-packaging-concurrent-deletion.txt' => 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt' |
831 | --- lib/lp/registry/stories/distribution/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2009-08-27 20:08:53 +0000 |
832 | +++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2009-10-24 20:01:11 +0000 |
833 | @@ -1,4 +1,5 @@ |
834 | -=== Concurrent Deletion of Packaging === |
835 | +Concurrent Deletion of Packaging |
836 | +================================ |
837 | |
838 | When two browsers are used to concurrently delete the same packaging |
839 | association, only one of them can succeed. The other one does not oops |
840 | @@ -16,7 +17,7 @@ |
841 | deletion succeeds and the usual informational message is displayed. |
842 | |
843 | >>> form = first_browser.getForm("delete_warty_alsa-utils_trunk") |
844 | - >>> form.getControl("Delete Link").click() |
845 | + >>> form.getControl(name="field.actions.delete_packaging").click() |
846 | >>> content = first_browser.contents |
847 | >>> for tag in find_tags_by_class(content, 'error'): |
848 | ... print extract_text(tag) |
849 | @@ -37,7 +38,7 @@ |
850 | -- David Allouche 2007-12-07 |
851 | |
852 | >>> form = second_browser.getForm("delete_warty_alsa-utils_trunk") |
853 | - >>> form.getControl("Delete Link").click() |
854 | + >>> form.getControl(name="field.actions.delete_packaging").click() |
855 | >>> content = second_browser.contents |
856 | >>> for tag in find_tags_by_class(content, 'informational'): |
857 | ... print extract_text(tag) |
858 | |
859 | === renamed file 'lib/lp/registry/stories/distribution/xx-distributionsourcepackage-packaging.txt' => 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt' |
860 | --- lib/lp/registry/stories/distribution/xx-distributionsourcepackage-packaging.txt 2009-10-21 01:55:17 +0000 |
861 | +++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt 2009-10-24 20:01:11 +0000 |
862 | @@ -1,4 +1,5 @@ |
863 | -== Distribution Packaging == |
864 | +Distribution Packaging |
865 | +====================== |
866 | |
867 | The packaging records for a source package in a given distribution are |
868 | displayed on the page of the distribution source package. |
869 | @@ -18,15 +19,16 @@ |
870 | 1.0.9a-4 release (main) ... weeks ago |
871 | |
872 | |
873 | -=== Delete Link Button === |
874 | +Delete Link Button |
875 | +------------------ |
876 | |
877 | A button is displayed to authenticated users to delete existing |
878 | packaging links. |
879 | |
880 | >>> user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils') |
881 | >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk") |
882 | - >>> print form.getControl("Delete Link") |
883 | - <SubmitControl name='field.actions.delete_packaging' type='submit'> |
884 | + >>> print form.getControl(name="field.actions.delete_packaging") |
885 | + <ImageControl name='field.actions.delete_packaging' type='image'> |
886 | |
887 | This button is not displayed to anonymous users. |
888 | |
889 | @@ -39,7 +41,7 @@ |
890 | Clicking this button deletes the corresponding packaging association. |
891 | |
892 | >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk") |
893 | - >>> form.getControl("Delete Link").click() |
894 | + >>> form.getControl(name="field.actions.delete_packaging").click() |
895 | >>> content = user_browser.contents |
896 | >>> for tag in find_tags_by_class(content, 'error'): |
897 | ... print extract_text(tag) |
898 | |
899 | === modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt' |
900 | --- lib/lp/registry/templates/distributionsourcepackage-index.pt 2009-09-25 17:00:20 +0000 |
901 | +++ lib/lp/registry/templates/distributionsourcepackage-index.pt 2009-10-24 20:01:11 +0000 |
902 | @@ -149,10 +149,6 @@ |
903 | <tal:has_packaging condition="row/packaging"> |
904 | <img tal:replace="structure row/packaging/productseries/image:icon"/> |
905 | <a tal:replace="structure row/packaging/productseries/fmt:link"/> |
906 | - <a tal:attributes=" |
907 | - href row/series_package/menu:overview/edit_packaging/url"> |
908 | - <img src="/@@/edit"/> |
909 | - </a> |
910 | <form style="display: inline" |
911 | method="POST" |
912 | tal:condition="row/hidden_packaging_field" |
913 | @@ -161,6 +157,11 @@ |
914 | <tal:hidden replace="structure row/hidden_packaging_field" /> |
915 | <tal:action replace="structure view/renderDeletePackagingAction" /> |
916 | </form> |
917 | + <a tal:attributes=" |
918 | + href row/series_package/menu:overview/edit_packaging/url; |
919 | + title string:Edit upsteam link"> |
920 | + <img src="/@@/edit"/> |
921 | + </a> |
922 | </tal:has_packaging> |
923 | </div><!--float right--> |
924 | </td> |
925 | @@ -215,7 +216,7 @@ |
926 | </tal:rows> |
927 | </table> |
928 | <script |
929 | - tal:content="string:LP.client.cache['archive_context_url'] = '${archive/fmt:url}';"/> |
930 | + tal:content="string:LP.client.cache['archive_context_url'] = '${archive/fmt:url}';"></script> |
931 | <metal:js use-macro="archive/@@+macros/expandable-table-js"/> |
932 | |
933 | <p style="float:right; padding-top:1em"> |
934 | |
935 | === modified file 'lib/lp/registry/templates/productseries-index.pt' |
936 | --- lib/lp/registry/templates/productseries-index.pt 2009-10-20 16:13:57 +0000 |
937 | +++ lib/lp/registry/templates/productseries-index.pt 2009-10-24 20:01:11 +0000 |
938 | @@ -24,8 +24,7 @@ |
939 | </tal:heading> |
940 | |
941 | <div metal:fill-slot="main" |
942 | - tal:define="overview_menu context/menu:overview; |
943 | - sourcepackages context/sourcepackages"> |
944 | + tal:define="overview_menu context/menu:overview"> |
945 | |
946 | <div class="top-portlet"> |
947 | <div id="description" |
948 | @@ -144,41 +143,7 @@ |
949 | tal:content="structure context/@@+portlet-latestbugs" |
950 | tal:condition="context/@@+get-involved/official_malone" /> |
951 | |
952 | - <div class="portlet"> |
953 | - <h2>Distribution packaging</h2> |
954 | - |
955 | - <p id="distribution-packaging-explanation"> |
956 | - <tal:distro condition="sourcepackages"> |
957 | - This series is packaged in the following distribution series: |
958 | - </tal:distro> |
959 | - <tal:no-distro condition="not: sourcepackages"> |
960 | - This series is not packaged in any distribution series. |
961 | - </tal:no-distro> |
962 | - </p> |
963 | - |
964 | - <ul id="distribution-packaging" |
965 | - tal:condition="sourcepackages"> |
966 | - <li |
967 | - tal:repeat="package sourcepackages"> |
968 | - <a class="sprite package-source" |
969 | - tal:attributes="href package/fmt:url"><tal:distro |
970 | - tal:content="package/distroseries/distribution/displayname"> |
971 | - Ubuntu</tal:distro> |
972 | - <tal:series tal:replace="package/distroseries/displayname"> |
973 | - Warty</tal:series> |
974 | - <tal:package tal:content="package/name">firefox</tal:package></a> |
975 | - </li> |
976 | - </ul> |
977 | - |
978 | - <ul class="horizontal"> |
979 | - <li> |
980 | - <a tal:replace="structure overview_menu/ubuntupkg/fmt:icon-link" /> |
981 | - </li> |
982 | - <li> |
983 | - <a tal:replace="structure overview_menu/add_package/fmt:icon-link" /> |
984 | - </li> |
985 | - </ul> |
986 | - </div> |
987 | + <div tal:content="structure context/@@+portlet-packages" /> |
988 | </div> |
989 | |
990 | <div class="yui-u"> |
991 | |
992 | === modified file 'lib/lp/registry/templates/productseries-portlet-packages.pt' |
993 | --- lib/lp/registry/templates/productseries-portlet-packages.pt 2009-07-17 17:59:07 +0000 |
994 | +++ lib/lp/registry/templates/productseries-portlet-packages.pt 2009-10-24 20:01:11 +0000 |
995 | @@ -1,25 +1,46 @@ |
996 | -<tal:root |
997 | +<div class="portlet" id="portlet-packages" |
998 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
999 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
1000 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
1001 | - omit-tag=""> |
1002 | - |
1003 | -<div class="portlet" id="portlet-packages" |
1004 | - tal:condition="context/sourcepackages"> |
1005 | - <h2>Distribution packaging</h2> |
1006 | - |
1007 | - <div class="portletBody portletContent "> |
1008 | - This series is packaged in the following places: |
1009 | - <ul> |
1010 | - <li tal:repeat="package context/sourcepackages"> |
1011 | - <span tal:replace="package/distroseries/distribution/name" |
1012 | - /> |
1013 | - <span tal:replace="package/distroseries/name" |
1014 | - /> |
1015 | - <a tal:content="package/name" |
1016 | - tal:attributes="href package/fmt:url">apache</a> |
1017 | - </li> |
1018 | - </ul> |
1019 | - </div> |
1020 | + tal:define="sourcepackages context/sourcepackages"> |
1021 | + <h2> |
1022 | + <span class="see-all"> |
1023 | + <a tal:attributes="href context/product/menu:overview/packages/fmt:url"> |
1024 | + All packages |
1025 | + </a> |
1026 | + </span> |
1027 | + Distribution packaging |
1028 | + </h2> |
1029 | + |
1030 | + <p id="distribution-packaging-explanation"> |
1031 | + <tal:distro condition="sourcepackages"> |
1032 | + This series is packaged in the following distribution series: |
1033 | + </tal:distro> |
1034 | + <tal:no-distro condition="not: sourcepackages"> |
1035 | + This series is not packaged in any distribution series. |
1036 | + </tal:no-distro> |
1037 | + </p> |
1038 | + |
1039 | + <ul id="distribution-packaging" |
1040 | + tal:condition="sourcepackages"> |
1041 | + <li |
1042 | + tal:repeat="package sourcepackages"> |
1043 | + <a class="sprite package-source" |
1044 | + tal:attributes="href package/fmt:url"><tal:distro |
1045 | + tal:content="package/distroseries/distribution/displayname"> |
1046 | + Ubuntu</tal:distro> |
1047 | + <tal:series tal:replace="package/distroseries/displayname"> |
1048 | + Warty</tal:series> |
1049 | + <tal:package tal:content="package/name">firefox</tal:package></a> |
1050 | + </li> |
1051 | + </ul> |
1052 | + |
1053 | + <ul class="horizontal"> |
1054 | + <li> |
1055 | + <a tal:replace="structure context/menu:overview/ubuntupkg/fmt:icon-link" /> |
1056 | + </li> |
1057 | + <li> |
1058 | + <a tal:replace="structure context/menu:overview/add_package/fmt:icon-link" /> |
1059 | + </li> |
1060 | + </ul> |
1061 | </div> |
1062 | -</tal:root> |
1063 | |
1064 | === modified file 'lib/lp/registry/templates/sourcepackage-index.pt' |
1065 | --- lib/lp/registry/templates/sourcepackage-index.pt 2009-09-25 17:00:20 +0000 |
1066 | +++ lib/lp/registry/templates/sourcepackage-index.pt 2009-10-24 20:01:11 +0000 |
1067 | @@ -114,9 +114,7 @@ |
1068 | </div> |
1069 | |
1070 | <div class="yui-u"> |
1071 | - <div tal:replace="structure context/@@+portlet-upstream" /> |
1072 | - |
1073 | - <div tal:condition="current"> |
1074 | + <div class="portlet" tal:condition="current"> |
1075 | <h2>Binary packages</h2> |
1076 | |
1077 | <div id="binaries" tal:define="binaries view/binaries"> |
1078 | |
1079 | === removed file 'lib/lp/registry/templates/sourcepackage-portlet-upstream.pt' |
1080 | --- lib/lp/registry/templates/sourcepackage-portlet-upstream.pt 2009-07-17 17:59:07 +0000 |
1081 | +++ lib/lp/registry/templates/sourcepackage-portlet-upstream.pt 1970-01-01 00:00:00 +0000 |
1082 | @@ -1,17 +0,0 @@ |
1083 | -<tal:root |
1084 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
1085 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
1086 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
1087 | - omit-tag=""> |
1088 | - |
1089 | -<div class="portlet" id="portlet-upstream"> |
1090 | - <h2>Upstream release series</h2> |
1091 | - <div class="portletBody portletContent"> |
1092 | - |
1093 | - <a tal:condition="context/productseries" |
1094 | - tal:content="context/productseries/title" |
1095 | - tal:attributes="href context/productseries/fmt:url">apache head</a> |
1096 | - <i tal:condition="not: context/productseries">None linked.</i> |
1097 | - </div> |
1098 | -</div> |
1099 | -</tal:root> |
This is my fourth branch to ensure valid upstream package links. There
are many oopses relating to the creation and efforts to fix invalid packages.
The root cause is a bad DB constraint and two views that do not do the
required sanity checks: +addpackage and +ubuntupkg. The views are fixed,
but we have not provided users with a way to delete packaging links
to unbuilt packages. User need to do this to fix their mistakes. We want
to do this so that we can delete the duplicate packages.
This branch refactors portlets and forms so that my next branch can add
delete links to the product +packaing report.
lp:~sinzui/launchpad/package-link-validation-3 /bugs.launchpad .net/bugs/ 276409 soyuz). *(productseries |packaging) ' implementation: flacoste, beuno
Diff size: 565
Launchpad bug: https:/
Test command: ./bin/test -vv -t 'lp.(reg|
Pre-
Target release: 3.1.10
== Fixing upstream packaging links ==
Bug 276409 ["Delete Link" is too easy on distribution source package page] /launchpad. net/ubuntu/ +source/ hwdb-client>, the button for
On the page for a distribution source package with upstream links, such as
<https:/
deleting an upstream link is quite large and prominent, much more so than
the button for correcting the link.
A small improvement would be to change the "Delete Link" button to an
icon-only button, but that would still leave deleting much easier than
updating.
There is little value in making the delete action an editing action
because the user does not have enough information to select another
series and sourcepackage. The user will almost always delete. Fixing the
icon is all that is needed.
== Rules ==
Bug 276409 ["Delete Link" is too easy on distribution source package page]
* Add a link from the productseries packaing portlet to the product
+packages view.
* Extract the delete packaging link rules to a separate view so that
they can be reused
* Change the Delete link button to a remove icon.
== QA ==
On staging
* Visit a bzr series
* Verify the packaging portlet has a link to All packages
* Visit the bzr DSP
* Verify eack packaging link has a the remove and edit icons int this
order: (-) (/)
== Lint ==
Linting changed files: /launchpad/ icing/style- 3-0.css registry/ browser/ configure. zcml registry/ browser/ distributionsou rcepackage. py registry/ browser/ packaging. py registry/ browser/ tests/test_ packaging. py registry/ stories/ packaging/ xx-distribution sourcepackage- packaging- concurrent- deletion. txt registry/ stories/ packaging/ xx-distribution sourcepackage- packaging. txt registry/ templates/ distributionsou rcepackage- index.pt registry/ templates/ productseries- index.pt registry/ templates/ productseries- portlet- packages. pt registry/ templates/ sourcepackage- index.pt registry/ templates/ team-portlet- membership. pt
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== Test ==
* lib/lp/ registry/ browser/ tests/test_ packaging. py registry/ stories/ packaging/ xx-distribution sourc.. .
* Updated the test to use the submit's name since images do not have
labels.
* lib/lp/