Merge lp:~wallyworld/launchpad/blueprint-subscription-forms into lp:launchpad

Proposed by Ian Booth
Status: Merged
Approved by: Ian Booth
Approved revision: no longer in the source branch.
Merged at revision: 13273
Proposed branch: lp:~wallyworld/launchpad/blueprint-subscription-forms
Merge into: lp:launchpad
Prerequisite: lp:~wallyworld/launchpad/blueprint-subscriptions-tales-refactor
Diff against target: 596 lines (+188/-155)
8 files modified
lib/lp/blueprints/browser/configure.zcml (+8/-2)
lib/lp/blueprints/browser/specification.py (+12/-49)
lib/lp/blueprints/browser/specificationsubscription.py (+79/-16)
lib/lp/blueprints/stories/standalone/subscribing.txt (+60/-19)
lib/lp/blueprints/templates/specification-subscriber-row.pt (+2/-2)
lib/lp/blueprints/templates/specification-subscription-delete.pt (+18/-0)
lib/lp/blueprints/templates/specification-subscription.pt (+2/-64)
lib/lp/blueprints/tests/test_webservice.py (+7/-3)
To merge this branch: bzr merge lp:~wallyworld/launchpad/blueprint-subscription-forms
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+65184@code.launchpad.net

Commit message

[r=benji][bug=50875] Allow teams to be unsubscribed from blueprints and fix (modernise) form infrastructure.

Description of the change

This branch builds on the blueprint refactoring work in the previous branch to add support for unsubscribing teams from blueprints. It also replaces old blueprint form code with the preferred LaunchpadFormView based infrastructure.

== Implementation ==

Blueprint subscription forms were still implemented using the old form infrastructure which relied on processing html posts in the view initialise() method. The code was modernised to use @action and LaunchpadFormView for managing subscriptions. A few artifacts of the implementation:
- replace two different forms and code used to modify a subscription with a single implementation
- split the "all in one" form used for managing one's own subscriptions into separate add/modify/delete forms
- use the same forms as above to handle subscribing someone else
- consistent use of "Subscribe", "Unsubscribe" etc for the form buttons as opposed to "Subscribe"/"Continue" in different places
- better informational messages - the name of the "someone else" is included in the info message when a different user of subscribed or unsubscribed

The above html changes will make it easier to do the ajax implementation consistent with the rest of lp. To reiterate - this branch does the work to provide the html forms based implementation.

For both the current logged in user, and any allowed teams, unsubscribing is done by clicking the red "remove" icon to the right of the user name. Editing a subscription is done by clicking the icon to the left of the user name.

== Demo ==

http://people.canonical.com/~ianb/blueprint-subscription-portal1.png

The screenshot shows the result of subscribing the Launchpad Administrators team. See the info message and the entry in the portal. The admins team can be unsubscribed hence the remove icon, but the other team and user shown in the portal cannot be subscribed so there's no option to do it.

== Tests ==

Blueprint subscriptions are tested by doc tests in subscribing.txt. These were modified to account for the changed button names as well as adding a test for unsubscribing teams and tests for the better informational messages.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/blueprints/browser/configure.zcml
  lib/lp/blueprints/browser/specification.py
  lib/lp/blueprints/browser/specificationsubscription.py
  lib/lp/blueprints/stories/standalone/subscribing.txt
  lib/lp/blueprints/templates/specification-subscriber-row.pt
  lib/lp/blueprints/templates/specification-subscription-delete.pt
  lib/lp/blueprints/templates/specification-subscription.pt

./lib/lp/blueprints/browser/specification.py
     203: E251 no spaces around keyword / parameter equals
./lib/lp/blueprints/stories/standalone/subscribing.txt
     128: source exceeds 78 characters.
     246: source exceeds 78 characters.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. I had a couple of thoughts while reading over
it, but nothing that would keep us from landing it.

In specificationsubscription.py (line 221 of the diff), would it be
better to say "Modify subscription to %s" (adding the "to")?

I also noticed that the elements of __all__ in
specificationsubscription.py aren't quite in alphabetical order.

This test confuses me:

Or we can click the icon next to their name to get to the subscription edit
page.

    >>> browser.getLink(url='/+subscription/stub').click()
    >>> browser.getControl(name='field.essential').value = 'no'
    >>> browser.getControl('Change').click()

I don't see any assertions. Maybe instead of the second and third lines
there should be a check to be sure the current URL is what is expected.

review: Approve (code)
Revision history for this message
Ian Booth (wallyworld) wrote :

Hi Benji

Thanks for the review.

>
> In specificationsubscription.py (line 221 of the diff), would it be
> better to say "Modify subscription to %s" (adding the "to")?
>
> I also noticed that the elements of __all__ in
> specificationsubscription.py aren't quite in alphabetical order.
>

Will fix.

> This test confuses me:
>
> Or we can click the icon next to their name to get to the subscription edit
> page.
>
> >>> browser.getLink(url='/+subscription/stub').click()
> >>> browser.getControl(name='field.essential').value = 'no'
> >>> browser.getControl('Change').click()
>
> I don't see any assertions. Maybe instead of the second and third lines
> there should be a check to be sure the current URL is what is expected.
>

There was an existing doc test that simply did the above that I reused.
Yes, it should have an assert (as should the existing test). I'll add
one in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/blueprints/browser/configure.zcml'
2--- lib/lp/blueprints/browser/configure.zcml 2011-06-21 12:03:49 +0000
3+++ lib/lp/blueprints/browser/configure.zcml 2011-06-21 12:03:51 +0000
4@@ -335,10 +335,16 @@
5 <browser:page
6 for="lp.blueprints.interfaces.specification.ISpecification"
7 name="+subscribe"
8- class="lp.blueprints.browser.specification.SpecificationSubscriptionView"
9+ class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionAddView"
10 permission="launchpad.AnyPerson"
11 template="../templates/specification-subscription.pt"/>
12 <browser:page
13+ for="lp.blueprints.interfaces.specification.ISpecificationSubscription"
14+ name="+unsubscribe"
15+ class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionDeleteView"
16+ permission="launchpad.Edit"
17+ template="../templates/specification-subscription-delete.pt"/>
18+ <browser:page
19 for="lp.blueprints.interfaces.specification.ISpecification"
20 name="+givefeedback"
21 class="lp.blueprints.browser.specificationfeedback.SpecificationFeedbackClearingView"
22@@ -347,7 +353,7 @@
23 <browser:page
24 name="+addsubscriber"
25 for="lp.blueprints.interfaces.specification.ISpecification"
26- class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionAddView"
27+ class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionAddSubscriberView"
28 permission="launchpad.AnyPerson"
29 template="../../app/templates/generic-edit.pt"/>
30 <browser:page
31
32=== modified file 'lib/lp/blueprints/browser/specification.py'
33--- lib/lp/blueprints/browser/specification.py 2011-06-21 12:03:49 +0000
34+++ lib/lp/blueprints/browser/specification.py 2011-06-21 12:03:51 +0000
35@@ -15,27 +15,26 @@
36 'NewSpecificationFromSprintView',
37 'SpecificationActionMenu',
38 'SpecificationContextMenu',
39- 'SpecificationNavigation',
40- 'SpecificationView',
41- 'SpecificationSimpleView',
42 'SpecificationEditMilestoneView',
43 'SpecificationEditPeopleView',
44 'SpecificationEditPriorityView',
45 'SpecificationEditStatusView',
46 'SpecificationEditView',
47 'SpecificationEditWhiteboardView',
48+ 'SpecificationGoalDecideView',
49 'SpecificationGoalProposeView',
50- 'SpecificationGoalDecideView',
51 'SpecificationLinkBranchView',
52+ 'SpecificationNavigation',
53 'SpecificationProductSeriesGoalProposeView',
54 'SpecificationRetargetingView',
55+ 'SpecificationSetView',
56+ 'SpecificationSimpleView',
57 'SpecificationSprintAddView',
58- 'SpecificationSubscriptionView',
59 'SpecificationSupersedingView',
60 'SpecificationTreePNGView',
61 'SpecificationTreeImageTag',
62 'SpecificationTreeDotOutput',
63- 'SpecificationSetView',
64+ 'SpecificationView',
65 ]
66
67 from operator import attrgetter
68@@ -473,15 +472,14 @@
69 """Return the 'Edit Subscription' Link."""
70 user = self.user
71 if user is None:
72- text = 'Edit subscription'
73- icon = 'edit'
74- elif self.context.isSubscribed(user):
75- text = 'Update subscription'
76- icon = 'edit'
77+ return Link('+subscribe', 'Edit subscription', icon='edit')
78+
79+ if self.context.isSubscribed(user):
80+ return Link(
81+ '+subscription/%s' % user.name,
82+ 'Update subscription', icon='edit')
83 else:
84- text = 'Subscribe'
85- icon = 'add'
86- return Link('+subscribe', text, icon=icon)
87+ return Link('+subscribe', 'Subscribe', icon='add')
88
89 @enabled_with_permission('launchpad.AnyPerson')
90 def linkbug(self):
91@@ -539,13 +537,6 @@
92 return []
93 return list(self.context.getFeedbackRequests(self.user))
94
95- @property
96- def subscription(self):
97- """whether the current user has a subscription to the spec."""
98- if self.user is None:
99- return None
100- return self.context.subscription(self.user)
101-
102 @cachedproperty
103 def has_dep_tree(self):
104 return self.context.dependencies or self.context.blocked_specs
105@@ -578,25 +569,6 @@
106 if not self.user:
107 return
108
109- request = self.request
110- if request.method == 'POST':
111- # establish if a subscription form was posted.
112- sub = request.form.get('subscribe')
113- upd = request.form.get('update')
114- unsub = request.form.get('unsubscribe')
115- essential = request.form.get('essential') == 'yes'
116- if sub is not None:
117- self.context.subscribe(self.user, self.user, essential)
118- self.notices.append(
119- "You have subscribed to this blueprint.")
120- elif upd is not None:
121- self.context.subscribe(self.user, self.user, essential)
122- self.notices.append('Your subscription has been updated.')
123- elif unsub is not None:
124- self.context.unsubscribe(self.user, self.user)
125- self.notices.append(
126- "You have unsubscribed from this blueprint.")
127-
128 if self.feedbackrequests:
129 msg = "You have %d feedback request(s) on this blueprint."
130 msg %= len(self.feedbackrequests)
131@@ -680,15 +652,6 @@
132 header='Change approval of basic direction')
133
134
135-class SpecificationSubscriptionView(SpecificationView):
136-
137- @property
138- def label(self):
139- if self.subscription is not None:
140- return "Modify subscription"
141- return "Subscribe to blueprint"
142-
143-
144 class SpecificationEditSchema(ISpecification):
145 """Provide overrides for the implementaion and definition status."""
146
147
148=== modified file 'lib/lp/blueprints/browser/specificationsubscription.py'
149--- lib/lp/blueprints/browser/specificationsubscription.py 2011-06-21 12:03:49 +0000
150+++ lib/lp/blueprints/browser/specificationsubscription.py 2011-06-21 12:03:51 +0000
151@@ -6,6 +6,7 @@
152 __metaclass__ = type
153 __all__ = [
154 'SpecificationSubscriptionAddView',
155+ 'SpecificationSubscriptionAddSubscriberView',
156 'SpecificationSubscriptionEditView',
157 ]
158
159@@ -29,41 +30,106 @@
160 from lp.blueprints.interfaces.specificationsubscription import (
161 ISpecificationSubscription,
162 )
163-from lp.registry.model.person import person_sort_key
164 from lp.services.propertycache import cachedproperty
165
166
167 class SpecificationSubscriptionAddView(LaunchpadFormView):
168+ """Used to subscribe the current user to a blueprint."""
169
170 schema = ISpecificationSubscription
171+ field_names = ['essential']
172+ label = 'Subscribe to blueprint'
173+
174+ @property
175+ def cancel_url(self):
176+ return canonical_url(self.context)
177+
178+ next_url = cancel_url
179+
180+ def _subscribe(self, person, essential):
181+ self.context.subscribe(person, self.user, essential)
182+
183+ @action(_('Subscribe'), name='subscribe')
184+ def subscribe_action(self, action, data):
185+ self._subscribe(self.user, data['essential'])
186+ self.request.response.addInfoNotification(
187+ "You have subscribed to this blueprint.")
188+
189+
190+class SpecificationSubscriptionAddSubscriberView(
191+ SpecificationSubscriptionAddView):
192+ """Used to subscribe someone else to a blueprint."""
193+
194 field_names = ['person', 'essential']
195 label = 'Subscribe someone else'
196 for_input = True
197
198- @action(_('Continue'), name='continue')
199- def continue_action(self, action, data):
200- self.context.subscribe(data['person'], self.user, data['essential'])
201- self.next_url = canonical_url(self.context)
202+ @action(_('Subscribe'), name='subscribe')
203+ def subscribe_action(self, action, data):
204+ person = data['person']
205+ self._subscribe(person, data['essential'])
206+ self.request.response.addInfoNotification(
207+ "%s has been subscribed to this blueprint." % person.displayname)
208+
209+
210+class SpecificationSubscriptionDeleteView(LaunchpadFormView):
211+ """Used to unsubscribe someone from a blueprint."""
212+
213+ schema = ISpecificationSubscription
214+ field_names = []
215+
216+ @property
217+ def label(self):
218+ return ("Unsubscribe %s from %s"
219+ % (self.context.person.displayname,
220+ self.context.specification.title))
221+
222+ page_title = label
223
224 @property
225 def cancel_url(self):
226- return canonical_url(self.context)
227+ return canonical_url(self.context.specification)
228+
229+ next_url = cancel_url
230+
231+ @action('Unsubscribe', name='unsubscribe')
232+ def unsubscribe_action(self, action, data):
233+ self.context.specification.unsubscribe(self.context.person, self.user)
234+ if self.context.person == self.user:
235+ self.request.response.addInfoNotification(
236+ "You have unsubscribed from this blueprint.")
237+ else:
238+ self.request.response.addInfoNotification(
239+ "%s has been unsubscribed from this blueprint."
240+ % self.context.person.displayname)
241
242
243 class SpecificationSubscriptionEditView(LaunchpadEditFormView):
244
245 schema = ISpecificationSubscription
246 field_names = ['essential']
247- label = 'Edit subscription'
248+
249+ @property
250+ def label(self):
251+ return "Modify subscription to %s" % self.context.specification.title
252+
253+ @property
254+ def cancel_url(self):
255+ return canonical_url(self.context.specification)
256+
257+ next_url = cancel_url
258
259 @action(_('Change'), name='change')
260 def change_action(self, action, data):
261 self.updateContextFromData(data)
262- self.next_url = canonical_url(self.context.specification)
263-
264- @property
265- def cancel_url(self):
266- return canonical_url(self.context.specification)
267+ is_current_user_subscription = self.user == self.context.person
268+ if is_current_user_subscription:
269+ self.request.response.addInfoNotification(
270+ "Your subscription has been updated.")
271+ else:
272+ self.request.response.addInfoNotification(
273+ "The subscription for %s has been updated."
274+ % self.context.person.displayname)
275
276
277 class SpecificationPortletSubcribersContents(LaunchpadView):
278@@ -81,12 +147,9 @@
279 The list is sorted such that subscriptions you can unsubscribe appear
280 before all other subscriptions.
281 """
282- sort_key = lambda sub: person_sort_key(sub.person)
283- subscriptions = sorted(self.context.subscriptions, key=sort_key)
284-
285 can_unsubscribe = []
286 cannot_unsubscribe = []
287- for subscription in subscriptions:
288+ for subscription in self.context.subscriptions:
289 if not check_permission('launchpad.View', subscription.person):
290 continue
291 if subscription.person == self.user:
292
293=== modified file 'lib/lp/blueprints/stories/standalone/subscribing.txt'
294--- lib/lp/blueprints/stories/standalone/subscribing.txt 2011-06-07 04:39:03 +0000
295+++ lib/lp/blueprints/stories/standalone/subscribing.txt 2011-06-21 12:03:51 +0000
296@@ -68,10 +68,6 @@
297 the link to modify the subscription. It should currently be checked.
298
299 >>> submod_link.click()
300- >>> print browser.title
301- Modify subscription : Support E4X in EcmaScript :
302- Blueprints : Mozilla Firefox
303-
304 >>> essential = browser.getControl('essential')
305 >>> essential.selected
306 True
307@@ -79,7 +75,7 @@
308 We will unset the essential flag and resubmit:
309
310 >>> essential.selected = False
311- >>> browser.getControl('Update').click()
312+ >>> browser.getControl('Change').click()
313 >>> 'Your subscription has been updated' in browser.contents
314 True
315
316@@ -99,15 +95,13 @@
317 True
318
319 We don't really want to be subscribed, so lets unsubscribe from that
320-spec. We load the subscription page, and now the button says
321-"Unsubscribe".
322-
323- >>> browser.getLink('Update subscription').click()
324- >>> unsubit = browser.getControl(name='unsubscribe')
325- >>> unsubit.value
326- 'Unsubscribe'
327-
328+spec. We click the remove icon in the subscribers list, and now the
329+unsubscribe confirmation page loads.
330+
331+ >>> unsubit = browser.getLink(id='unsubscribe-subscriber-13')
332 >>> unsubit.click()
333+ >>> confirm = browser.getControl('Unsubscribe')
334+ >>> confirm.click()
335 >>> 'You have unsubscribed from this blueprint.' in browser.contents
336 True
337
338@@ -130,7 +124,9 @@
339 'http://blueprints.launchpad.dev/firefox/+spec/e4x'
340
341 >>> browser.getControl('Subscriber').value = 'stub'
342- >>> browser.getControl('Continue').click()
343+ >>> browser.getControl('Subscribe').click()
344+ >>> 'Stuart Bishop has been subscribed to this blueprint.' in browser.contents
345+ True
346
347 When we subscribe someone else to a blueprint, they get notified by
348 email.
349@@ -157,7 +153,18 @@
350 >>> browser.getLink('Subscribe someone else').click()
351 >>> browser.getControl('Subscriber').value = 'stub'
352 >>> browser.getControl(name='field.essential').value = 'yes'
353- >>> browser.getControl('Continue').click()
354+ >>> browser.getControl('Subscribe').click()
355+
356+We now check that the subscriptions portlet is showing the correct information
357+based on the subscription change we have made above.
358+
359+ >>> subscribers = find_tags_by_class(browser.contents, 'subscriber')
360+ >>> for subscriber in subscribers:
361+ ... a_tags = subscriber.findAll('a')
362+ ... img = a_tags[0].first('img')
363+ ... print img['src'],
364+ ... print a_tags[1].string
365+ /@@/subscriber-essential Stuart Bishop
366
367 When we change a user's subscription, they get notified by email. Teams
368 can be subscribed to a blueprint too
369@@ -166,6 +173,29 @@
370 >>> '[Participation essential]' in last_email.get_payload()
371 True
372
373+We can click the icon next to a user's name to get to the subscription edit
374+page.
375+
376+ >>> browser.getLink(url='/+subscription/stub').click()
377+ >>> browser.getControl(name='field.essential').value = None
378+ >>> browser.getControl('Change').click()
379+
380+We now check that the subscriptions portlet is showing the correct information
381+based on the subscription change we have made above.
382+
383+ >>> subscribers = find_tags_by_class(browser.contents, 'subscriber')
384+ >>> for subscriber in subscribers:
385+ ... a_tags = subscriber.findAll('a')
386+ ... img = a_tags[0].first('img')
387+ ... print img['src'],
388+ ... print a_tags[1].string
389+ /@@/subscriber-inessential Stuart Bishop
390+
391+And check the email notification too.
392+
393+ >>> last_email = pop_notifications()[-1]
394+ >>> '[Participation non-essential]' in last_email.get_payload()
395+ True
396
397 Subscribing teams
398 -----------------
399@@ -197,7 +227,7 @@
400 >>> browser.getLink('Subscribe someone else').click()
401 >>> browser.getControl('Subscriber').value = 'admins'
402 >>> browser.getControl(name='field.essential').value = None
403- >>> browser.getControl('Continue').click()
404+ >>> browser.getControl('Subscribe').click()
405
406 We created a subscription for the Launchpad Admins, but because the team
407 does not have a preferred email address, an email is sent to each active
408@@ -213,7 +243,7 @@
409 >>> browser.getLink('Subscribe someone else').click()
410 >>> browser.getControl('Subscriber').value = 'admins'
411 >>> browser.getControl(name='field.essential').value = 'yes'
412- >>> browser.getControl('Continue').click()
413+ >>> browser.getControl('Subscribe').click()
414
415 We modified the Launchpad Admins team's subscription and again, an email
416 is sent to each active member.
417@@ -230,7 +260,18 @@
418 >>> browser.getLink('Subscribe someone else').click()
419 >>> browser.getControl('Subscriber').value = 'ubuntu-team'
420 >>> browser.getControl(name='field.essential').value = None
421- >>> browser.getControl('Continue').click()
422+ >>> browser.getControl('Subscribe').click()
423+
424+Because the current logged in user carlos is a member of the admins team it is
425+possible to unsubscribe the team. We click the remove icon in the subscribers
426+list, and now the unsubscribe confirmation page loads.
427+
428+ >>> unsubit = browser.getLink(id='unsubscribe-subscriber-25')
429+ >>> unsubit.click()
430+ >>> confirm = browser.getControl('Unsubscribe')
431+ >>> confirm.click()
432+ >>> 'Launchpad Administrators has been unsubscribed from this blueprint.' in browser.contents
433+ True
434
435 We subscribe the Ubuntu Team and an email is sent to the team's
436 preferred email address.
437@@ -245,7 +286,7 @@
438 >>> browser.getLink('Subscribe someone else').click()
439 >>> browser.getControl('Subscriber').value = 'ubuntu-team'
440 >>> browser.getControl(name='field.essential').value = 'yes'
441- >>> browser.getControl('Continue').click()
442+ >>> browser.getControl('Subscribe').click()
443
444 We modified the Ubuntu Team's subscription and again, an email is sent
445 to the team's preferred email address.
446
447=== modified file 'lib/lp/blueprints/templates/specification-subscriber-row.pt'
448--- lib/lp/blueprints/templates/specification-subscriber-row.pt 2011-06-21 12:03:49 +0000
449+++ lib/lp/blueprints/templates/specification-subscriber-row.pt 2011-06-21 12:03:51 +0000
450@@ -39,10 +39,10 @@
451 />
452 <a tal:define="user request/lp:person"
453 tal:condition="python: subscription.canBeUnsubscribedByUser(user)"
454- href="+subscribe"
455 tal:attributes="
456 title string:Unsubscribe ${subscription/person/fmt:displayname};
457- id string:unsubscribe-${subscription/css_name};"
458+ id string:unsubscribe-${subscription/css_name};
459+ href string:${subscription/fmt:url}/+unsubscribe;"
460 >
461 <img class="unsub-icon" src="/@@/remove" alt="Remove"
462 tal:attributes="id string:unsubscribe-icon-${subscription/css_name}" />
463
464=== added file 'lib/lp/blueprints/templates/specification-subscription-delete.pt'
465--- lib/lp/blueprints/templates/specification-subscription-delete.pt 1970-01-01 00:00:00 +0000
466+++ lib/lp/blueprints/templates/specification-subscription-delete.pt 2011-06-21 12:03:51 +0000
467@@ -0,0 +1,18 @@
468+<html
469+ xmlns="http://www.w3.org/1999/xhtml"
470+ xmlns:tal="http://xml.zope.org/namespaces/tal"
471+ xmlns:metal="http://xml.zope.org/namespaces/metal"
472+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
473+ metal:use-macro="view/macro:page/main_only"
474+ i18n:domain="launchpad">
475+ <body>
476+ <div metal:fill-slot="main">
477+ <p>
478+ Are you sure you want to delete the subscription for
479+ <strong tal:content="context/person/displayname">Person</strong>?
480+ </p>
481+ <div metal:use-macro="context/@@launchpad_form/form">
482+ </div>
483+ </div>
484+ </body>
485+</html>
486
487=== modified file 'lib/lp/blueprints/templates/specification-subscription.pt'
488--- lib/lp/blueprints/templates/specification-subscription.pt 2011-05-15 19:31:31 +0000
489+++ lib/lp/blueprints/templates/specification-subscription.pt 2011-06-21 12:03:51 +0000
490@@ -15,70 +15,8 @@
491
492 <div metal:fill-slot="main">
493
494- <div class="top-portlet" tal:condition="view/subscription">
495-
496- <p>
497- Choose &#8220;Unsubscribe&#8221; to remove your subscription to this
498- blueprint, or &#8220;Cancel&#8221; to return to the blueprint page.
499- </p>
500-
501- </div>
502-
503- <div class="top-portlet" tal:condition="not: view/subscription">
504-
505- <p>
506- Choose &#8220;Subscribe&#8221; to subscribe to this blueprint,
507- or &#8220;Cancel&#8221; to return to the blueprint page.
508- </p>
509-
510- </div>
511-
512- <form action="." method="POST">
513-
514- <div class="field">
515-
516- <tal:subscribed condition="view/subscription">
517- <input type="checkbox" id="essential" name="essential"
518- value="yes" checked="yes"
519- tal:condition="view/subscription/essential" />
520- <input type="checkbox" id="essential" name="essential"
521- value="yes"
522- tal:condition="not: view/subscription/essential" />
523- </tal:subscribed>
524- <tal:not_subscribed condition="not: view/subscription">
525- <input type="checkbox" id="essential" name="essential"
526- value="yes" />
527- </tal:not_subscribed>
528-
529- <label for="essential">Participation essential</label>
530-
531- <p class="formHelp">
532- Check this box only if you are required to be included in all discussions about
533- this feature when you are attending sprints or meetings with this
534- feature on the agenda.
535- </p>
536-
537- </div>
538-
539- <div class="actions">
540- <input tal:condition="not: view/subscription"
541- type="submit"
542- name="subscribe"
543- value="Subscribe" />
544- <input tal:condition="view/subscription"
545- type="submit"
546- name="update"
547- value="Update" />
548- <input tal:condition="view/subscription"
549- type="submit"
550- name="unsubscribe"
551- value="Unsubscribe" />
552- <input type="submit"
553- name="cancel"
554- value="Cancel" />
555- </div>
556- </form>
557-
558+ <div metal:use-macro="context/@@launchpad_form/form">
559+ </div>
560 </div>
561
562 </body>
563
564=== modified file 'lib/lp/blueprints/tests/test_webservice.py'
565--- lib/lp/blueprints/tests/test_webservice.py 2011-06-21 12:03:49 +0000
566+++ lib/lp/blueprints/tests/test_webservice.py 2011-06-21 12:03:51 +0000
567@@ -29,14 +29,18 @@
568
569 class SpecificationWebserviceTestCase(TestCaseWithFactory):
570
571+ def getLaunchpadlib(self):
572+ user = self.factory.makePerson()
573+ return launchpadlib_for("testing", user, version='devel')
574+
575 def getSpecOnWebservice(self, spec_object):
576- launchpadlib = self.factory.makeLaunchpadService()
577+ launchpadlib = self.getLaunchpadlib()
578 return launchpadlib.load(
579 '/%s/+spec/%s' % (spec_object.target.name, spec_object.name))
580
581 def getPillarOnWebservice(self, pillar_obj):
582 # XXX: 2010-11-26, salgado, bug=681767: Can't use relative URLs here.
583- launchpadlib = self.factory.makeLaunchpadService()
584+ launchpadlib = self.getLaunchpadlib()
585 return launchpadlib.load(
586 str(launchpadlib._root_uri) + '/' + pillar_obj.name)
587
588@@ -300,7 +304,7 @@
589 db_spec = self.factory.makeBlueprint()
590 db_person = self.factory.makePerson()
591 db_spec.subscribe(person=db_person)
592- launchpad = self.factory.makeLaunchpadService()
593+ launchpad = self.factory.makeLaunchpadService(person=db_person)
594
595 spec = ws_object(launchpad, db_spec)
596 person = ws_object(launchpad, db_person)