Merge lp:~sinzui/launchpad/mailing-list-simple-0 into lp:launchpad

Proposed by Curtis Hovey on 2010-12-13
Status: Merged
Approved by: Aaron Bentley on 2010-12-13
Approved revision: no longer in the source branch.
Merged at revision: 12056
Proposed branch: lp:~sinzui/launchpad/mailing-list-simple-0
Merge into: lp:launchpad
Diff against target: 905 lines (+110/-302)
15 files modified
lib/canonical/launchpad/doc/celebrities.txt (+78/-64)
lib/canonical/launchpad/interfaces/launchpad.py (+3/-5)
lib/canonical/launchpad/permissions.zcml (+0/-5)
lib/canonical/launchpad/security.py (+0/-38)
lib/canonical/launchpad/utilities/celebrities.py (+1/-1)
lib/lp/registry/browser/configure.zcml (+1/-10)
lib/lp/registry/browser/mailinglists.py (+0/-92)
lib/lp/registry/browser/person.py (+1/-1)
lib/lp/registry/browser/team.py (+1/-1)
lib/lp/registry/browser/tests/mailinglist-views.txt (+4/-4)
lib/lp/registry/doc/mailinglists.txt (+14/-12)
lib/lp/registry/model/mailinglist.py (+5/-3)
lib/lp/registry/stories/mailinglists/lifecycle.txt (+1/-1)
lib/lp/registry/templates/mailinglists-review.pt (+0/-64)
lib/lp/registry/tests/mailinglists_helper.py (+1/-1)
To merge this branch: bzr merge lp:~sinzui/launchpad/mailing-list-simple-0
Reviewer Review Type Date Requested Status
Aaron Bentley (community) 2010-12-13 Approve on 2010-12-13
Review via email: mp+43522@code.launchpad.net

Description of the Change

Fix small issues with mailing lists.

    Launchpad bug:
        https://bugs.launchpad.net/bugs/669249
        https://bugs.launchpad.net/bugs/151290
    Pre-implementation: no one
    Test command: ./bin/test -vv -t mailinglist

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    The mailing_list_experts team is synonymous with registry administrator
    team. The 'MailingListManager' permission could be Moderate or Admin using
    the registry team.

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]

--------------------------------------------------------------------

RULES

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    * Replace all uses of MailingListManager with Moderate.
    * Remove MailingListManager and its checker.

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]
    Appart from the team and the mailing list status it should also contain
    the address of that mailing list.

QA

Bug #669249 ['MailingListManager' and mailing_list_experts can be removed]
    * As a list owner verify you can visit your teams

Bug #151290 [IMailingList.__repr__ should include the mailing list's address]
    * Update the __repr__() method.

LINT

    lib/canonical/launchpad/permissions.zcml
    lib/canonical/launchpad/security.py
    lib/canonical/launchpad/doc/celebrities.txt
    lib/canonical/launchpad/interfaces/launchpad.py
    lib/canonical/launchpad/utilities/celebrities.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/mailinglists.py
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/team.py
    lib/lp/registry/browser/tests/mailinglist-views.txt
    lib/lp/registry/doc/mailinglists.txt
    lib/lp/registry/model/mailinglist.py
    lib/lp/registry/stories/mailinglists/lifecycle.txt
    lib/lp/registry/tests/mailinglists_helper.py

^ There are are many line and white space issues reported in this files.
I will fix these before I land the branch. I think this will keep the diff
readable.

IMPLEMENTATION

Both these bugs were largely fixed using find and replace.
    lib/canonical/launchpad/doc/celebrities.txt
    lib/canonical/launchpad/interfaces/launchpad.py
    lib/canonical/launchpad/utilities/celebrities.py
    lib/lp/registry/browser/person.py
    lib/lp/registry/browser/team.py
    lib/lp/registry/browser/tests/mailinglist-views.txt
    lib/lp/registry/doc/mailinglists.txt
    lib/lp/registry/model/mailinglist.py
    lib/lp/registry/stories/mailinglists/lifecycle.txt
    lib/lp/registry/tests/mailinglists_helper.py

I manually deleted some class and files after discovering that they are
not used.
    lib/canonical/launchpad/permissions.zcml
    lib/canonical/launchpad/security.py
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/browser/mailinglists.py

To post a comment you must log in.
Aaron Bentley (abentley) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/doc/celebrities.txt'
2--- lib/canonical/launchpad/doc/celebrities.txt 2010-10-09 16:36:22 +0000
3+++ lib/canonical/launchpad/doc/celebrities.txt 2010-12-13 22:53:15 +0000
4@@ -1,13 +1,15 @@
5-= Launchpad Celebrities =
6+Launchpad Celebrities
7+=====================
8
9 https://dev.launchpad.net/LaunchpadCelebrities
10
11 There are a number of special objects, some of which exist in the
12 database, which we want to give easy access to in the code. To this end,
13-there is an ILaunchpadCelebrities utility available that gives access
14-to these well-known objects through attributes.
15+there is an ILaunchpadCelebrities utility available that gives access to
16+these well-known objects through attributes.
17
18- >>> from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
19+ >>> from canonical.launchpad.interfaces.launchpad import (
20+ ... ILaunchpadCelebrities)
21 >>> from lp.services.worlddata.interfaces.language import ILanguageSet
22 >>> from lp.registry.interfaces.person import IPerson, IPersonSet
23 >>> celebs = getUtility(ILaunchpadCelebrities)
24@@ -17,7 +19,8 @@
25 True
26
27
28-== admin ==
29+admin
30+-----
31
32 The 'admins' team contain the user who have super-power over all of
33 Launchpad. This team is accessible through the admin attribute.
34@@ -28,7 +31,8 @@
35 True
36
37
38-== vcs-imports ==
39+vcs-imports
40+-----------
41
42 The vcs-imports celebrity owns the branches created by importd.
43
44@@ -37,17 +41,19 @@
45 True
46
47
48-== bazaar-experts ==
49+bazaar-experts
50+--------------
51
52-The bazaar-experts celebrity effectively has admin type privileges
53-for branches, and branch related objects.
54+The bazaar-experts celebrity effectively has admin type privileges for
55+branches, and branch related objects.
56
57 >>> bazaar_experts = personset.getByName('bazaar-experts')
58 >>> celebs.bazaar_experts == bazaar_experts
59 True
60
61
62-== english ==
63+english
64+-------
65
66 The English language is used in many places. It's the native language
67 for translation, as well as for Launchpad itself.
68@@ -57,27 +63,30 @@
69 True
70
71
72-== registry_experts ==
73+registry_experts
74+----------------
75
76-The registry_experts celebrity has permissions to perform registry gardening
77-operations.
78+The registry_experts celebrity has permissions to perform registry
79+gardening operations.
80
81 >>> registry = personset.getByName('registry')
82 >>> celebs.registry_experts == registry
83 True
84
85
86-== buildd_admin ==
87+buildd_admin
88+------------
89
90-The buildd_admin celebrity has permission to perform routine task
91-in the buildfarm.
92+The buildd_admin celebrity has permission to perform routine task in the
93+buildfarm.
94
95 >>> buildd_admin = personset.getByName('launchpad-buildd-admins')
96 >>> celebs.buildd_admin == buildd_admin
97 True
98
99
100-== bug_watch_updater ==
101+bug_watch_updater
102+-----------------
103
104 The bug_watch_updater celebrity updates the bug watches.
105
106@@ -86,7 +95,8 @@
107 True
108
109
110-== sourceforge_tracker ==
111+sourceforge_tracker
112+-------------------
113
114 For all the products using SourceForge, we have a single registered
115 tracker
116@@ -97,18 +107,20 @@
117 True
118
119
120-== janitor ==
121+janitor
122+-------
123
124 We have the Launchpad Janitor which takes care of expiring old
125-questions, team memberships when they reach their expiry date,
126-and old incomplete bugtasks.
127+questions, team memberships when they reach their expiry date, and old
128+incomplete bugtasks.
129
130 >>> janitor = personset.getByName('janitor')
131 >>> celebs.janitor == janitor
132 True
133
134
135-== launchpad ==
136+launchpad
137+---------
138
139 The Launchpad product itself.
140
141@@ -118,22 +130,8 @@
142 True
143
144
145-== mailing_list_experts ==
146-
147-There is a 'Mailing List Experts' team that has administrative power
148-over mailing lists.
149-
150- >>> mailing_list_experts = personset.getByName('mailing-list-experts')
151- >>> celebs.mailing_list_experts == mailing_list_experts
152- True
153-
154-The Launchpad Administrators team is a member of that team.
155-
156- >>> celebs.admin.inTeam(mailing_list_experts)
157- True
158-
159-
160-== obsolete_junk ==
161+obsolete_junk
162+-------------
163
164 The 'Obsolete Junk' project is used to hold undeletable objects like
165 productseries that other projects no longer want.
166@@ -143,21 +141,23 @@
167 True
168
169
170-== commercial_admin ==
171+commercial_admin
172+----------------
173
174-There is a 'Commercial Subscription Admins' team that has
175-administrative power over the license review process and
176-has the ability to de-activate projects.
177+There is a 'Commercial Subscription Admins' team that has administrative
178+power over the license review process and has the ability to de-activate
179+projects.
180
181 >>> commercial_admin = personset.getByName('commercial-admins')
182 >>> celebs.commercial_admin == commercial_admin
183 True
184
185
186-== Savannah bug tracker ==
187+Savannah bug tracker
188+--------------------
189
190-There is a 'Savannah Bug Tracker' bugtracker which represents the
191-bug tracker for all registered Savannah projects.
192+There is a 'Savannah Bug Tracker' bugtracker which represents the bug
193+tracker for all registered Savannah projects.
194
195 >>> from lp.bugs.interfaces.bugtracker import IBugTrackerSet
196 >>> savannah_tracker = getUtility(IBugTrackerSet).getByName(
197@@ -173,7 +173,8 @@
198 http://savannah.nognu.org/
199
200
201-== Gnome Bugzilla ==
202+Gnome Bugzilla
203+--------------
204
205 There is a 'Gnome Bugzilla' celebrity, which is used to represent the
206 Gnome Bugzilla instance by the checkwatches script.
207@@ -183,27 +184,29 @@
208 True
209
210
211-== PPA key guard ==
212+PPA key guard
213+-------------
214
215-There is a 'PPA key guard' celebrity which owns all PPA
216-'signing_keys'.
217+There is a 'PPA key guard' celebrity which owns all PPA 'signing_keys'.
218
219 >>> ppa_key_guard = personset.getByName('ppa-key-guard')
220 >>> celebs.ppa_key_guard == ppa_key_guard
221 True
222
223
224-== Ubuntu branches ==
225+Ubuntu branches
226+---------------
227
228-There's a celebrity that is the only Person allowed to set the official branch
229-for a source package. It's called ubuntu-branches.
230+There's a celebrity that is the only Person allowed to set the official
231+branch for a source package. It's called ubuntu-branches.
232
233 >>> ubuntu_branches = personset.getByName('ubuntu-branches')
234 >>> celebs.ubuntu_branches == ubuntu_branches
235 True
236
237
238-== Ubuntu security team ==
239+Ubuntu security team
240+--------------------
241
242 There is a celebrity representing the 'ubuntu-security' team, which is
243 mainly used for granting special permissions on the ubuntu primary
244@@ -214,24 +217,28 @@
245 True
246
247
248-== Ubuntu technical board ==
249+Ubuntu technical board
250+----------------------
251
252-There's a celebrity for the Ubuntu technical board, the 'techboard' team. It's
253-used for determining who is allowed to create new package sets.
254+There's a celebrity for the Ubuntu technical board, the 'techboard'
255+team. It's used for determining who is allowed to create new package
256+sets.
257
258 >>> ubuntu_techboard = personset.getByName('techboard')
259 >>> print ubuntu_techboard.name
260 techboard
261+
262 >>> celebs.ubuntu_techboard == ubuntu_techboard
263 True
264
265
266-== Person celebrities ==
267+Person celebrities
268+------------------
269
270-Each person celebrity has a corresponding "in_" attribute in IPersonRoles, to
271-check if a person has that role. If the attributes differ, IPersonRoles needs
272-to be synced to ILaunchpadCelebrities by adding/removing the appropriate
273-"in_" attribute(s).
274+Each person celebrity has a corresponding "in_" attribute in
275+IPersonRoles, to check if a person has that role. If the attributes
276+differ, IPersonRoles needs to be synced to ILaunchpadCelebrities by
277+adding/removing the appropriate "in_" attribute(s).
278
279 >>> from canonical.launchpad.interfaces.launchpad import IPersonRoles
280 >>> def get_person_celebrity_names():
281@@ -243,31 +250,38 @@
282 ... if name.startswith("in_"):
283 ... yield name
284
285-Treating the lists as sets and determining their difference gives us a clear
286-picture of what is missing where.
287+Treating the lists as sets and determining their difference gives us a
288+clear picture of what is missing where.
289
290 >>> person_celebrity_names = set(get_person_celebrity_names())
291 >>> person_roles_names = set(get_person_roles_names())
292 >>> print "Please add to IPersonRoles: " + (
293 ... ", ".join(list(person_celebrity_names - person_roles_names)))
294 Please add to IPersonRoles:
295+
296 >>> print "Please remove from IPersonRoles: " + (
297 ... ", ".join(list(person_roles_names - person_celebrity_names)))
298 Please remove from IPersonRoles:
299
300
301-== Detecting if a person is a celebrity ==
302+Detecting if a person is a celebrity
303+------------------------------------
304
305 We can ask if a person has celebrity status.
306
307 >>> celebs.isCelebrityPerson(ubuntu_techboard.name)
308 True
309+
310 >>> celebs.isCelebrityPerson(obsolete_junk.name)
311 False
312+
313 >>> celebs.isCelebrityPerson('admins')
314 True
315+
316 >>> celebs.isCelebrityPerson('admin')
317 False
318+
319 >>> celebs.isCelebrityPerson('janitor')
320 True
321
322+
323
324=== modified file 'lib/canonical/launchpad/interfaces/launchpad.py'
325--- lib/canonical/launchpad/interfaces/launchpad.py 2010-11-23 18:36:21 +0000
326+++ lib/canonical/launchpad/interfaces/launchpad.py 2010-12-13 22:53:15 +0000
327@@ -122,7 +122,6 @@
328 launchpad_beta_testers = Attribute("The Launchpad Beta Testers team.")
329 launchpad_developers = Attribute("The Launchpad development team.")
330 lp_translations = Attribute("The Launchpad Translations product.")
331- mailing_list_experts = Attribute("The Mailing List Experts team.")
332 obsolete_junk = Attribute("The Obsolete Junk project.")
333 ppa_key_guard = Attribute("The PPA signing keys owner.")
334 registry_experts = Attribute("The Registry Administrators team.")
335@@ -193,9 +192,6 @@
336 in_launchpad_developers = Bool(
337 title=_("True if this person is a Launchpad developer."),
338 required=True, readonly=True)
339- in_mailing_list_experts = Bool(
340- title=_("True if this person is a mailing list expert."),
341- required=True, readonly=True)
342 in_ppa_key_guard = Bool(
343 title=_("True if this person is the ppa key guard."),
344 required=True, readonly=True)
345@@ -482,7 +478,8 @@
346 def currentApproximateAge():
347 """Return a human-readable string of how old this thing is.
348
349- Values returned are things like '2 minutes', '3 hours', '1 month', etc.
350+ Values returned are things like '2 minutes', '3 hours',
351+ '1 month', etc.
352 """
353
354
355@@ -540,6 +537,7 @@
356 The set maintains the list of `IPerson` that will be contacted as well
357 as the email address to use to contact them.
358 """
359+
360 def getEmails():
361 """Return all email addresses registered, sorted alphabetically."""
362
363
364=== modified file 'lib/canonical/launchpad/permissions.zcml'
365--- lib/canonical/launchpad/permissions.zcml 2010-10-05 23:26:59 +0000
366+++ lib/canonical/launchpad/permissions.zcml 2010-12-13 22:53:15 +0000
367@@ -76,11 +76,6 @@
368 title="The role of someone who created or otherwise owns an object."
369 access_level="write" />
370
371- <permission
372- id="launchpad.MailingListManager"
373- title="The role of someone who can manage and configure a mailing list."
374- access_level="write" />
375-
376 <!-- This permission only exists to please apidoc.launchpad.dev and
377 shouldn't be used anywhere else. -->
378 <permission
379
380=== modified file 'lib/canonical/launchpad/security.py'
381--- lib/canonical/launchpad/security.py 2010-12-08 15:48:11 +0000
382+++ lib/canonical/launchpad/security.py 2010-12-13 22:53:15 +0000
383@@ -114,7 +114,6 @@
384 from lp.registry.interfaces.gpg import IGPGKey
385 from lp.registry.interfaces.irc import IIrcID
386 from lp.registry.interfaces.location import IPersonLocation
387-from lp.registry.interfaces.mailinglist import IMailingListSet
388 from lp.registry.interfaces.milestone import (
389 IMilestone,
390 IProjectGroupMilestone,
391@@ -1276,8 +1275,6 @@
392 EditByOwnersOrAdmins.checkAuthenticated(self, user))
393
394
395-# XXX: Carlos Perello Marin 2005-05-24 bug=753:
396-# This should be using SuperSpecialPermissions when implemented.
397 class AddPOTemplate(OnlyRosettaExpertsAndAdmins):
398 permission = 'launchpad.Append'
399 usedfor = IProductSeries
400@@ -1485,9 +1482,6 @@
401 usedfor = IBuilder
402
403
404-# XXX cprov 2006-07-31: As soon as we have external builders, as presumed
405-# in the original plan, we should grant some rights to the owners and
406-# that's what Edit is for.
407 class EditBuilder(AdminByBuilddAdmin):
408 permission = 'launchpad.Edit'
409 usedfor = IBuilder
410@@ -2384,38 +2378,6 @@
411 return False
412
413
414-class MailingListApprovalByExperts(AuthorizationBase):
415- permission = 'launchpad.Admin'
416- usedfor = IMailingListSet
417-
418- def checkAuthenticated(self, user):
419- return user.in_mailing_list_experts
420-
421-
422-class ConfigureTeamMailingList(AuthorizationBase):
423- permission = 'launchpad.MailingListManager'
424- usedfor = ITeam
425-
426- def checkAuthenticated(self, user):
427- """Check to see if the user can manage a mailing list.
428-
429- A team's owner or administrator, the Launchpad administrators, and
430- Launchpad mailing list experts can all manage a team's mailing list
431- through its +mailinglist page.
432-
433- :param user: The user whose permission is being checked.
434- :type user: `IPerson`
435- :return: True if the user can manage a mailing list, otherwise False.
436- :rtype: boolean
437- """
438- # The team owner, the Launchpad mailing list experts and the Launchpad
439- # administrators can all view a team's +mailinglist page.
440- team = ITeam(self.obj)
441- is_team_owner = (
442- team is not None and team in user.person.getAdministratedTeams())
443- return is_team_owner or user.in_admin or user.in_mailing_list_experts
444-
445-
446 class ViewEmailAddress(AuthorizationBase):
447 permission = 'launchpad.View'
448 usedfor = IEmailAddress
449
450=== modified file 'lib/canonical/launchpad/utilities/celebrities.py'
451--- lib/canonical/launchpad/utilities/celebrities.py 2010-11-08 12:52:43 +0000
452+++ lib/canonical/launchpad/utilities/celebrities.py 2010-12-13 22:53:15 +0000
453@@ -113,6 +113,7 @@
454
455 Unlike most other celebrities, languages are retrieved by code.
456 """
457+
458 def _getCelebrityByName(self, utility):
459 """See `CelebrityDescriptor`."""
460 return utility.getLanguageByCode(self.name)
461@@ -146,7 +147,6 @@
462 'launchpad-beta-testers')
463 launchpad_developers = PersonCelebrityDescriptor('launchpad')
464 lp_translations = CelebrityDescriptor(IProductSet, 'rosetta')
465- mailing_list_experts = PersonCelebrityDescriptor('mailing-list-experts')
466 obsolete_junk = CelebrityDescriptor(IProductSet, 'obsolete-junk')
467 ppa_key_guard = PersonCelebrityDescriptor('ppa-key-guard')
468 registry_experts = PersonCelebrityDescriptor('registry')
469
470=== modified file 'lib/lp/registry/browser/configure.zcml'
471--- lib/lp/registry/browser/configure.zcml 2010-12-09 15:20:21 +0000
472+++ lib/lp/registry/browser/configure.zcml 2010-12-13 22:53:15 +0000
473@@ -1145,7 +1145,7 @@
474 template="../templates/team-contactaddress.pt"/>
475 <browser:page
476 for="lp.registry.interfaces.person.ITeam"
477- permission="launchpad.MailingListManager"
478+ permission="launchpad.Moderate"
479 class="lp.registry.browser.team.TeamMailingListConfigurationView"
480 name="+mailinglist"
481 template="../templates/team-mailinglist.pt"/>
482@@ -1376,15 +1376,6 @@
483 MilestoneOverviewMenu
484 MilestoneOverviewNavigationMenu"
485 module="lp.registry.browser.milestone"/>
486- <browser:defaultView
487- for="lp.registry.interfaces.mailinglist.IMailingListSet"
488- name="+review"/>
489- <browser:page
490- for="lp.registry.interfaces.mailinglist.IMailingListSet"
491- class="canonical.launchpad.browser.MailingListsReviewView"
492- name="+review"
493- template="../templates/mailinglists-review.pt"
494- permission="launchpad.Admin"/>
495 <browser:page
496 name="+moderation"
497 for="lp.registry.interfaces.mailinglist.IMessageApproval"
498
499=== modified file 'lib/lp/registry/browser/mailinglists.py'
500--- lib/lp/registry/browser/mailinglists.py 2010-11-23 23:22:27 +0000
501+++ lib/lp/registry/browser/mailinglists.py 2010-12-13 22:53:15 +0000
502@@ -6,117 +6,25 @@
503 __metaclass__ = type
504 __all__ = [
505 'HeldMessageView',
506- 'MailingListsReviewView',
507 'enabled_with_active_mailing_list',
508 ]
509
510
511 from cgi import escape
512-from operator import itemgetter
513 from textwrap import TextWrapper
514 from urllib import quote
515
516 from zope.component import getUtility
517-from zope.interface import Interface
518-from zope.security.proxy import removeSecurityProxy
519
520-from canonical.launchpad import _
521 from canonical.launchpad.webapp import (
522 canonical_url,
523 LaunchpadView,
524 )
525-from canonical.launchpad.webapp.menu import structured
526-from lp.app.browser.launchpadform import (
527- action,
528- LaunchpadFormView,
529- )
530-from lp.app.browser.tales import PersonFormatterAPI
531-from lp.app.errors import UnexpectedFormData
532 from lp.registry.interfaces.mailinglist import (
533 IHeldMessageDetails,
534 IMailingListSet,
535- MailingListStatus,
536 )
537 from lp.registry.interfaces.person import ITeam
538-from lp.services.propertycache import cachedproperty
539-
540-
541-class ReviewForm(Interface):
542- """An empty marker schema for the review form."""
543-
544-
545-class MailingListsReviewView(LaunchpadFormView):
546- """Present review page for mailing list creation requests."""
547-
548- schema = ReviewForm
549- page_title = 'Pending mailing lists requests'
550-
551- @cachedproperty
552- def registered_lists(self):
553- """Return a list of mailing list information dictionaries.
554-
555- The view needs a sorted list of dictionaries of information pertaining
556- to each mailing list pending approval. The dictionaries have three
557- keys:
558-
559- * the mailing list's team
560- * the mailing list's team's name
561- * the registrant of the mailing list request
562-
563- The dictionaries are sorted by team name.
564-
565- :return: Information about all the pending mailing list requests.
566- :rtype: list of dictionaries
567- """
568- list_info = []
569- for mailing_list in self.context.registered_lists:
570- naked_team = removeSecurityProxy(mailing_list.team)
571- list_info.append(dict(
572- # Use PersonFormatterAPI so that private team names aren't
573- # redacted, which doesn't help a mailing list expert much.
574- # This just ensures that the team name is not redacted, even
575- # though the MLE may (still) not have permission to visit the
576- # team page via the link. That's a different issue and not
577- # one important enough to fix for now.
578- team_link=PersonFormatterAPI(naked_team).link(None),
579- name=naked_team.name,
580- registrant=mailing_list.registrant,
581- ))
582- return sorted(list_info, key=itemgetter('name'))
583-
584- @action(_('Submit'), name='submit')
585- def submit_action(self, action, data):
586- """Process the mailing list review form."""
587- for mailing_list in self.context.registered_lists:
588- naked_team = removeSecurityProxy(mailing_list.team)
589- # Find out which disposition the administrator chose for this
590- # mailing list. If there is no data in the form for this mailing
591- # list, just treat it as having been deferred.
592- action = self.request.form_ng.getOne('field.%s' % naked_team.name)
593- # This essentially acts like a switch statement or if/elifs. It
594- # looks the action up in a map of allowed actions, watching out
595- # for bogus input.
596- try:
597- status = dict(
598- approve=MailingListStatus.APPROVED,
599- decline=MailingListStatus.DECLINED,
600- hold=None,
601- )[action]
602- except KeyError:
603- raise UnexpectedFormData(
604- 'Invalid review action for mailing list %s: %s' %
605- (naked_team.displayname, action))
606- if status is not None:
607- mailing_list.review(self.user, status)
608- self.request.response.addInfoNotification(
609- structured(
610- '<a href="%s">%s</a> mailing list was %s',
611- canonical_url(naked_team),
612- naked_team.displayname,
613- status.title.lower()))
614- # Redirect to prevent double posts (and not require
615- # flush_database_updates() :)
616- self.next_url = canonical_url(self.context)
617
618
619 class HeldMessageView(LaunchpadView):
620
621=== modified file 'lib/lp/registry/browser/person.py'
622--- lib/lp/registry/browser/person.py 2010-12-02 19:45:50 +0000
623+++ lib/lp/registry/browser/person.py 2010-12-13 22:53:15 +0000
624@@ -1354,7 +1354,7 @@
625 self.person.displayname)
626 return Link(target, text, summary, icon='edit')
627
628- @enabled_with_permission('launchpad.MailingListManager')
629+ @enabled_with_permission('launchpad.Moderate')
630 def configure_mailing_list(self):
631 target = '+mailinglist'
632 mailing_list = self.person.mailing_list
633
634=== modified file 'lib/lp/registry/browser/team.py'
635--- lib/lp/registry/browser/team.py 2010-12-03 16:33:03 +0000
636+++ lib/lp/registry/browser/team.py 2010-12-13 22:53:15 +0000
637@@ -745,7 +745,7 @@
638 """
639 is_moderator = check_permission('launchpad.Moderate', self.context)
640 is_mailing_list_manager = check_permission(
641- 'launchpad.MailingListManager', self.context)
642+ 'launchpad.Moderate', self.context)
643 if is_moderator or is_mailing_list_manager:
644 return self.getListInState(*PURGE_STATES) is not None
645 else:
646
647=== modified file 'lib/lp/registry/browser/tests/mailinglist-views.txt'
648--- lib/lp/registry/browser/tests/mailinglist-views.txt 2010-11-01 20:39:17 +0000
649+++ lib/lp/registry/browser/tests/mailinglist-views.txt 2010-12-13 22:53:15 +0000
650@@ -27,7 +27,7 @@
651
652 >>> login('test@canonical.com')
653 >>> view = create_initialized_view(aardvarks, '+mailinglist')
654- >>> check_permission('launchpad.MailingListManager', view)
655+ >>> check_permission('launchpad.Moderate', view)
656 True
657
658 No Privileges Person is added as a member of the Aardvarks.
659@@ -39,7 +39,7 @@
660
661 >>> login('no-priv@canonical.com')
662 >>> view = create_initialized_view(aardvarks, '+mailinglist')
663- >>> check_permission('launchpad.MailingListManager', view)
664+ >>> check_permission('launchpad.Moderate', view)
665 False
666
667 Sample Person trusts No Privileges Person so she makes no-priv a team
668@@ -60,7 +60,7 @@
669
670 >>> login('no-priv@canonical.com')
671 >>> view = create_initialized_view(aardvarks, '+mailinglist')
672- >>> check_permission('launchpad.MailingListManager', view)
673+ >>> check_permission('launchpad.Moderate', view)
674 True
675
676
677@@ -81,7 +81,7 @@
678 >>> from canonical.launchpad.interfaces.launchpad import (
679 ... ILaunchpadCelebrities)
680 >>> celebrities = getUtility(ILaunchpadCelebrities)
681- >>> an_expert = list(celebrities.mailing_list_experts.allmembers)[0]
682+ >>> an_expert = list(celebrities.registry_experts.allmembers)[0]
683 >>> an_admin = list(celebrities.admin.allmembers)[0]
684
685 >>> def create_view(principal, form=None):
686
687=== modified file 'lib/lp/registry/doc/mailinglists.txt'
688--- lib/lp/registry/doc/mailinglists.txt 2010-10-19 18:44:31 +0000
689+++ lib/lp/registry/doc/mailinglists.txt 2010-12-13 22:53:15 +0000
690@@ -51,13 +51,14 @@
691 >>> verifyObject(IMailingList, list_one)
692 True
693 >>> list_one
694- <MailingList for team "team-one"; status=APPROVED at 0x...>
695+ <MailingList for team "team-one"; status=APPROVED;
696+ address=team-one@lists.launchpad.dev at 0x...>
697
698 You can always access the mailing list through its team, if the team has a
699 mailing list.
700
701 >>> team_one.mailing_list
702- <MailingList for team "team-one"; status=APPROVED at 0x...>
703+ <MailingList for team "team-one"; status=APPROVED; ...>
704 >>> print team_two.mailing_list
705 None
706
707@@ -89,7 +90,7 @@
708 >>> login(ANONYMOUS)
709 >>> list_two = list_set.new(team_two, anne)
710 >>> list_two
711- <MailingList for team "team-two"; status=APPROVED at ...>
712+ <MailingList for team "team-two"; status=APPROVED; ...>
713 >>> print list_two.address
714 team-two@lists.launchpad.dev
715
716@@ -129,8 +130,8 @@
717 state.
718
719 >>> sorted_lists(list_set.approved_lists)
720- [<MailingList for team "team-one"; status=APPROVED at ...>,
721- <MailingList for team "team-two"; status=APPROVED at ...>]
722+ [<MailingList for team "team-one"; status=APPROVED; ...>,
723+ <MailingList for team "team-two"; status=APPROVED; ...>]
724
725 >>> list_one.startConstructing()
726 >>> print list_one.status.name
727@@ -139,7 +140,7 @@
728 Once in the construction phase, a list is no longer in the approval state.
729
730 >>> sorted_lists(list_set.approved_lists)
731- [<MailingList for team "team-two"; status=APPROVED at ...>]
732+ [<MailingList for team "team-two"; status=APPROVED; ...>]
733
734 Lists should never be constructed more than once.
735
736@@ -163,7 +164,8 @@
737 and associated with the team's mailing list, so that it can be used as the
738 team's contact address.
739
740- >>> from canonical.launchpad.interfaces.emailaddress import IEmailAddressSet
741+ >>> from canonical.launchpad.interfaces.emailaddress import (
742+ ... IEmailAddressSet)
743 >>> email_set = getUtility(IEmailAddressSet)
744 >>> print email_set.getByEmail(list_one.address)
745 None
746@@ -190,9 +192,9 @@
747 You can then get the mailing list for a team, given the team name.
748
749 >>> list_set.get(team_one.name)
750- <MailingList for team "team-one"; status=ACTIVE at ...>
751+ <MailingList for team "team-one"; status=ACTIVE; ...>
752 >>> list_set.get(team_two.name)
753- <MailingList for team "team-two"; status=FAILED at ...>
754+ <MailingList for team "team-two"; status=FAILED; ...>
755
756 This method will return None for missing teams or non-team people.
757
758@@ -223,7 +225,7 @@
759 and report the deactivation results.
760
761 >>> sorted_lists(list_set.deactivated_lists)
762- [<MailingList for team "team-three"; status=DEACTIVATING at ...>]
763+ [<MailingList for team "team-three"; status=DEACTIVATING; ...>]
764 >>> list_three.transitionToStatus(MailingListStatus.INACTIVE)
765 >>> print list_three.status.name
766 INACTIVE
767@@ -303,7 +305,7 @@
768 ... new_list_for_team)
769 >>> thereminists_list = new_list_for_team(thereminists)
770 >>> thereminists_list
771- <MailingList for team "thereminists"; status=ACTIVE at ...>
772+ <MailingList for team "thereminists"; status=ACTIVE; ...>
773
774 Welcome messages
775 ================
776@@ -324,7 +326,7 @@
777 >>> print list_one.status.name
778 MODIFIED
779 >>> sorted_lists(list_set.modified_lists)
780- [<MailingList for team "team-one"; status=MODIFIED at ...>]
781+ [<MailingList for team "team-one"; status=MODIFIED; ...>]
782
783 Eventually, Mailman will get around to acting on this modification. When it
784 does so, the list's state transitions first to UPDATING so as to avoid
785
786=== modified file 'lib/lp/registry/model/mailinglist.py'
787--- lib/lp/registry/model/mailinglist.py 2010-10-26 03:51:12 +0000
788+++ lib/lp/registry/model/mailinglist.py 2010-12-13 22:53:15 +0000
789@@ -64,7 +64,9 @@
790 sqlvalues,
791 )
792 from canonical.launchpad import _
793-from canonical.launchpad.components.decoratedresultset import DecoratedResultSet
794+from canonical.launchpad.components.decoratedresultset import (
795+ DecoratedResultSet,
796+ )
797 from canonical.launchpad.database.account import Account
798 from canonical.launchpad.database.emailaddress import EmailAddress
799 from canonical.launchpad.database.message import Message
800@@ -245,8 +247,8 @@
801 return template.safe_substitute(team_name=self.team.name)
802
803 def __repr__(self):
804- return '<MailingList for team "%s"; status=%s at %#x>' % (
805- self.team.name, self.status.name, id(self))
806+ return '<MailingList for team "%s"; status=%s; address=%s at %#x>' % (
807+ self.team.name, self.status.name, self.address, id(self))
808
809 def startConstructing(self):
810 """See `IMailingList`."""
811
812=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
813--- lib/lp/registry/stories/mailinglists/lifecycle.txt 2010-11-03 22:14:42 +0000
814+++ lib/lp/registry/stories/mailinglists/lifecycle.txt 2010-12-13 22:53:15 +0000
815@@ -338,7 +338,7 @@
816 >>> from lp.registry.interfaces.person import IPersonSet
817 >>> person_set = getUtility(IPersonSet)
818 >>> test = person_set.getByName('name12')
819- >>> experts = getUtility(ILaunchpadCelebrities).mailing_list_experts
820+ >>> experts = getUtility(ILaunchpadCelebrities).registry_experts
821 >>> ignored = experts.addMember(test, reviewer=experts.teamowner)
822 >>> logout()
823 >>> transaction.commit()
824
825=== removed file 'lib/lp/registry/templates/mailinglists-review.pt'
826--- lib/lp/registry/templates/mailinglists-review.pt 2009-08-16 17:21:54 +0000
827+++ lib/lp/registry/templates/mailinglists-review.pt 1970-01-01 00:00:00 +0000
828@@ -1,64 +0,0 @@
829-<html
830- xmlns="http://www.w3.org/1999/xhtml"
831- xmlns:tal="http://xml.zope.org/namespaces/tal"
832- xmlns:metal="http://xml.zope.org/namespaces/metal"
833- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
834- metal:use-macro="view/macro:page/main_only"
835- i18n:domain="launchpad"
836- >
837-
838-<body>
839- <div metal:fill-slot="main">
840- <h1 tal:content="view/page_title" />
841- <div tal:condition="view/registered_lists">
842- <metal:form use-macro="context/@@launchpad_form/form">
843- <p metal:fill-slot="extra_top">These team administrators have
844- requested mailing lists for their teams. Your approval is
845- required before Launchpad will create the mailing list. For each
846- mailing list request, you can choose whether to <em>Approve</em>
847- or <em>Decline</em> the request. Or you may <em>Hold</em> the
848- request to defer your decision until later.
849- </p>
850- <table class="listing sortable" id="mailing-lists"
851- metal:fill-slot="widgets">
852- <thead><tr>
853- <th>Approve</th><th>Decline</th><th>Hold</th>
854- <th>Team</th><th>Requester</th>
855- </tr></thead>
856- <tr tal:repeat="mailing_list view/registered_lists"
857- style="text-align: center">
858- <tal:block tal:define=
859- "team_link mailing_list/team_link;
860- team_name mailing_list/name;
861- registrant mailing_list/registrant;
862- action_name string:field.${team_name}"
863- >
864- <td><input type="radio" value="approve"
865- tal:attributes="name action_name;
866- id string:approve.${team_name}"/>
867- </td>
868- <td><input type="radio" value="decline"
869- tal:attributes="name action_name;
870- id string:decline.${team_name}"/>
871- </td>
872- <td><input type="radio" value="hold" checked="checked"
873- tal:attributes="name action_name;
874- id string:hold.${team_name}"/>
875- </td>
876- <td>
877- <a tal:replace="structure team_link"/>
878- </td>
879- <td>
880- <a tal:replace="structure registrant/fmt:link"/>
881- </td>
882- </tal:block>
883- </tr>
884- </table>
885- </metal:form>
886- </div>
887- <p tal:condition="not: view/registered_lists" id="status"
888- >There is nothing to do.</p>
889- </div>
890-
891-</body>
892-</html>
893
894=== modified file 'lib/lp/registry/tests/mailinglists_helper.py'
895--- lib/lp/registry/tests/mailinglists_helper.py 2010-10-26 03:51:12 +0000
896+++ lib/lp/registry/tests/mailinglists_helper.py 2010-12-13 22:53:15 +0000
897@@ -181,7 +181,7 @@
898 """
899 # Any member of the mailing-list-experts team can review a list
900 # registration. It doesn't matter which one.
901- experts = getUtility(ILaunchpadCelebrities).mailing_list_experts
902+ experts = getUtility(ILaunchpadCelebrities).registry_experts
903 reviewer = list(experts.allmembers)[0]
904 list_set = getUtility(IMailingListSet)
905 team_list = list_set.new(team)