Merge lp:~wallyworld/launchpad/additional-affiliation-types-798764 into lp:launchpad

Proposed by Ian Booth
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 13636
Proposed branch: lp:~wallyworld/launchpad/additional-affiliation-types-798764
Merge into: lp:launchpad
Diff against target: 886 lines (+622/-57)
7 files modified
lib/lp/app/browser/tests/test_vocabulary.py (+16/-7)
lib/lp/app/browser/vocabulary.py (+9/-3)
lib/lp/registry/configure.zcml (+28/-3)
lib/lp/registry/model/pillaraffiliation.py (+110/-21)
lib/lp/registry/tests/test_pillaraffiliation.py (+437/-15)
lib/lp/services/features/flags.py (+4/-0)
lib/lp/testing/factory.py (+18/-8)
To merge this branch: bzr merge lp:~wallyworld/launchpad/additional-affiliation-types-798764
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+70521@code.launchpad.net

This proposal supersedes a proposal from 2011-08-04.

Commit message

[r=sinzui][bug=798764,820210,820212,820213] Add affiliation adapters for question, branch, specification, distro, product, distroseries, productseries

Description of the change

This branch adds to the affiliation adaptor to provide affiliation for other entity types:
- Specification
- Question
- Distribution
- DistroSeries
- ProductSeries
- Product

== Implementation ==

Add extra affiliation adaptors for the additional context types. Checks are done for:
owner
driver
security contact
bug supervisor

Some contexts have pillars eg BugTask has a product or distribution; a distroseries has a distribution. The context is checked first. If the context doesn't match on the given attribute (eg owner), then the pillar is checked. If there is no match on owner, then driver is checked etc.

I think this mp also covers the intent of bug 81692, although that bug talks about additional checks eg registrant. Do we consider the work done here sufficient to address that bug?

== Tests ==

Add a bunch of tests to test_pillaraffiliation

== Lint ==

Linting changed files:
  lib/lp/registry/configure.zcml
  lib/lp/registry/model/pillaraffiliation.py
  lib/lp/registry/tests/test_pillaraffiliation.py

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote : Posted in a previous version of this proposal
Download full text (9.7 KiB)

> === modified file 'lib/lp/registry/model/pillaraffiliation.py'
> --- lib/lp/registry/model/pillaraffiliation.py 2011-08-04 14:31:56 +0000
> +++ lib/lp/registry/model/pillaraffiliation.py 2011-08-04 14:32:17 +0000
...
> + def getPillar(self):
> + return self.context
> +
> + def getAffiliationBadge(self, person):
> + """ Return the affiliation information for a person given a context.
> +
> + The context is a Distribution, Product etc and is associated with a
> + pillar. Checks are done to see if the person is associated with the
> + context first (owner, driver etc) and if not, then the pillar.
> + """
> + pillar = self.getPillar()
> +
> + def checkAffiliation(capability, attribute, role):
> + # Check the affiliation defined by the specified capability on the
> + # context and then pillar. Capability is IHasOwner etc. WHatever
> + # matches first (context or pillar) is used for the display name.
> + affiliated_entity = None
> + capabilityProvidedBy = getattr(capability, 'providedBy')
> + if capabilityProvidedBy(self.context):
> + if person.inTeam(getattr(self.context, attribute)):
> + affiliated_entity = self.context.displayname
> + if (affiliated_entity is None and capabilityProvidedBy(pillar)):
> + if person.inTeam(getattr(pillar, attribute)):
> + affiliated_entity = pillar.displayname
> + if affiliated_entity is None:
> + return None
> + return affiliated_entity, role

I do not see why this is an inner function. This could be simpler too if
we decide that all we care about is product or distribution. We know how to
check owner, drivers, and other roles. The other kinds of items I
see returned, notably for specification and question are probably wrong.
We do not need the interface checks if we are certain we are getting a
distro or product.

I have some doubts about the universality of these checks. I think
owner and driver are universal. bug_supervisor and security contact are
only useful in bugs and branches cases that deal with privacy and security.
eg assigning a branch reviewer, bug assignee, subscriber.

Questions might care more about answer contacts which are more likely to
be assigned. Consider Ubuntu. The owners and drivers are rarely assigned.
when I assign a question, I care about the answer contact for the package
first, then the contacts for Ubuntu.

Specifications care about the people who are assignees, drafters, approvers,
and need feedback. In most cases these people are owners and drivers. There
is a rare case for the approver who is for the series goal...

The series is the problem. Maybe a separate branch becuase it is a rule
that may not prove very important. The series driver (release manager) is
the most important person for bugs that affect a series or blueprints with
a series goal. Those driver are assumed to be a subset of owner or drivers
who have the authority to define the work for a series. I happen to know
that This is not an issue for Ubuntu or any of our major pro...

Read more...

review: Needs Information (code)
Revision history for this message
Ian Booth (wallyworld) wrote : Posted in a previous version of this proposal
Download full text (9.8 KiB)

Thanks for the review. Clearly I'm missing some knowledge.

On 05/08/11 05:22, Curtis Hovey wrote:
> Review: Needs Information code
>> === modified file 'lib/lp/registry/model/pillaraffiliation.py'
>> --- lib/lp/registry/model/pillaraffiliation.py 2011-08-04 14:31:56 +0000
>> +++ lib/lp/registry/model/pillaraffiliation.py 2011-08-04 14:32:17 +0000
> ...
>> + def getPillar(self):
>> + return self.context
>> +
>> + def getAffiliationBadge(self, person):
>> + """ Return the affiliation information for a person given a context.
>> +
>> + The context is a Distribution, Product etc and is associated with a
>> + pillar. Checks are done to see if the person is associated with the
>> + context first (owner, driver etc) and if not, then the pillar.
>> + """
>> + pillar = self.getPillar()
>> +
>> + def checkAffiliation(capability, attribute, role):
>> + # Check the affiliation defined by the specified capability on the
>> + # context and then pillar. Capability is IHasOwner etc. WHatever
>> + # matches first (context or pillar) is used for the display name.
>> + affiliated_entity = None
>> + capabilityProvidedBy = getattr(capability, 'providedBy')
>> + if capabilityProvidedBy(self.context):
>> + if person.inTeam(getattr(self.context, attribute)):
>> + affiliated_entity = self.context.displayname
>> + if (affiliated_entity is None and capabilityProvidedBy(pillar)):
>> + if person.inTeam(getattr(pillar, attribute)):
>> + affiliated_entity = pillar.displayname
>> + if affiliated_entity is None:
>> + return None
>> + return affiliated_entity, role
>
> I do not see why this is an inner function. This could be simpler too if

Me either.

> we decide that all we care about is product or distribution. We know how to
> check owner, drivers, and other roles. The other kinds of items I
> see returned, notably for specification and question are probably wrong.
> We do not need the interface checks if we are certain we are getting a
> distro or product.
>

Will we always be getting a distro or product though? If I have a
Specification, wouldn't I want to possibly first see if the person in
question is affiliated with the specification itself and then check the
affiliation with the target (product/distro) only if the specification
check turned up empty?

> I have some doubts about the universality of these checks. I think
> owner and driver are universal. bug_supervisor and security contact are
> only useful in bugs and branches cases that deal with privacy and security.
> eg assigning a branch reviewer, bug assignee, subscriber.
>

This suggests we need another piece of context information to properly
determine the affiliation - the name of the attribute that is being
updated with the person, not just the person alone. So instead of saying
"we are associating person fred with bugtask 4 in some capacity" we are
saying "we are associating person fred with bugtask 4 as an assignee"
and that this distinction possibly affect...

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

The mp diff screwed up so I resubmitted and it looks better now.

I think I have addressed the main issues raised in the review.
- the base pillaraffiliation class performs owner and driver checks
- subclasses perform additional checks as required (eg bug supervisor, security contact for bugtasks)
- drivers attribute is used to get the drivers
- answer contacts used to check for question affiliation
- tests added for distroseries and productseries to test for driver affiliation
- tests added for questions to check for answer affiliation
- other test cleanup

The affiliation adapters don't take into account the attribute being set (eg assignee, approver etc). This can be done is another branch if required.

Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (4.0 KiB)

Hi Ian.

I think this branch is good to land. I have a few remarks you may want to consider.

=== modified file 'lib/lp/registry/model/pillaraffiliation.py'
--- lib/lp/registry/model/pillaraffiliation.py 2011-08-04 13:56:55 +0000
+++ lib/lp/registry/model/pillaraffiliation.py 2011-08-05 06:14:28 +0000
...

> +@adapter(IBugTask)
> +class BugTaskPillarAffiliation(PillarAffiliation):
> + """An affiliation adapter for bug tasks."""
> + def getPillar(self):
> + return self.context.pillar
> +
> + def _getAffiliationDetails(self, person, pillar):
> + """ A person is affiliated with a bugtask based on (in order):
> + - owner of bugtask pillar
> + - driver of bugtask pillar
> + - bug supervisor of bugtask pillar
> + - security contact of bugtask pillar
> + """
> + result = super(BugTaskPillarAffiliation, self)._getAffiliationDetails(
> + person, pillar)
> + if result is not None:
> + return result
> + if person.inTeam(pillar.bug_supervisor):
> + return pillar.displayname, 'bug supervisor'
> + if person.inTeam(pillar.security_contact):
> + return pillar.displayname, 'security contact'

I think this will also work for branches. Could we do this as well?

@adapter(IBranch)
class BranchPillarAffiliation(BugTaskPillarAffiliation):
    """An affiliation adapter for branches."""

^ An extra class would not be be necessary using ZCML to register the adapter,
but we ouls still have two separate ZCML entried for both IBugTask
and IBranch.

> +@adapter(ISpecification)
> > +class SpecificationPillarAffiliation(PillarAffiliation):
> > + """An affiliation adapter for blueprints."""
> > + def getPillar(self):
> > + return (self.context.target)

You suggested that this adapter could check if the user is assigned a roll
for the specification. I think that could misinform users. Anyone can register
a specification and assign users to rolls. They are not in anyway affilliated
with the project. Most of Launchpad's visible blueprints are bogus, and they
where created by people other than ~launchpad. I think this implementation
is good.

> +@adapter(IQuestion)
> +class QuestionPillarAffiliation(PillarAffiliation):
> + """An affiliation adapter for questions."""
> + def getPillar(self):
> + return self.context.product or self.context.distribution
> +
> + def _getAffiliationDetails(self, person, pillar):
> + """ A person is affiliated with a question based on (in order):
> + - answer contact for question target
> + - owner of question target
> + - driver of question target
> + """
> + target = self.context.target
> + answer_contacts = target.answer_contacts
> + for answer_contact in answer_contacts:
> + if person.inTeam(answer_contact):
> + return target.displayname, 'answer contact'
> + return super(QuestionPillarAffiliation, self)._getAffiliationDetails(
> + person, pillar)

This looks a lot like code I wrote in 2007. In 2010 I updated security.py
to use an alternate approach that was much faster (fewer sql queries)...

Read more...

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

Thanks fir the review and suggestions.

I added a BranchAffiliationAdapter and updated the code in
QuestionAffiliationAdapter as per the suggestion in the review, plus
added new tests for the branch adapter and question adapter

On 06/08/11 00:21, Curtis Hovey wrote:
> Review: Approve code
> Hi Ian.
>
> I think this branch is good to land. I have a few remarks you may want to consider.
>
>
<snip>

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

I have put this new functionality behind a new eature flag: disclosure.personpicker_affiliation.enabled
Previously, this work was behind the existing disclosure.enhance_picker.enabled flag.

The reason is that there are questions over the performance in terms of the number of selects from the TeamParticipation table. The enhance_picker feature flag is on for production and I don't want to risk breaking lp.net with this new work.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/app/browser/tests/test_vocabulary.py'
--- lib/lp/app/browser/tests/test_vocabulary.py 2011-08-04 23:49:37 +0000
+++ lib/lp/app/browser/tests/test_vocabulary.py 2011-08-09 00:14:32 +0000
@@ -119,17 +119,19 @@
119 self.assertEqual('http://launchpad.dev/~fnord', entry.alt_title_link)119 self.assertEqual('http://launchpad.dev/~fnord', entry.alt_title_link)
120 self.assertEqual(['Team members: 1'], entry.details)120 self.assertEqual(['Team members: 1'], entry.details)
121121
122 def test_PersonPickerEntryAdapter_enhanced_picker_enabled_badges(self):122 def test_PersonPickerEntryAdapter_personpicker_affiliation_badges(self):
123 # The enhanced person picker provides affilliation information.123 # The person picker with affiliation enabled provides affilliation
124 # information.
124 person = self.factory.makePerson(email='snarf@eg.dom', name='snarf')125 person = self.factory.makePerson(email='snarf@eg.dom', name='snarf')
125 project = self.factory.makeProduct(name='fnord', owner=person)126 project = self.factory.makeProduct(name='fnord', owner=person)
126 bugtask = self.factory.makeBugTask(target=project)127 bugtask = self.factory.makeBugTask(target=project)
127 entry = IPickerEntry(person).getPickerEntry(128 entry = IPickerEntry(person).getPickerEntry(
128 bugtask, enhanced_picker_enabled=True,129 bugtask, enhanced_picker_enabled=True,
129 picker_expander_enabled=True)130 picker_expander_enabled=True,
131 personpicker_affiliation_enabled=True)
130 self.assertEqual(1, len(entry.badges))132 self.assertEqual(1, len(entry.badges))
131 self.assertEqual('/@@/product-badge', entry.badges[0]['url'])133 self.assertEqual('/@@/product-badge', entry.badges[0]['url'])
132 self.assertEqual('Affiliated with Fnord', entry.badges[0]['alt'])134 self.assertEqual('Fnord maintainer', entry.badges[0]['alt'])
133135
134136
135class TestPersonVocabulary:137class TestPersonVocabulary:
@@ -171,8 +173,9 @@
171 self.addCleanup(TestPersonVocabulary.setTestData, [])173 self.addCleanup(TestPersonVocabulary.setTestData, [])
172174
173 @staticmethod175 @staticmethod
174 def create_vocabulary_view(form):176 def create_vocabulary_view(form, context=None):
175 context = getUtility(ILaunchpadRoot)177 if context is None:
178 context = getUtility(ILaunchpadRoot)
176 query_string = urlencode(form)179 query_string = urlencode(form)
177 return create_view(180 return create_view(
178 context, '+huge-vocabulary', form=form, query_string=query_string)181 context, '+huge-vocabulary', form=form, query_string=query_string)
@@ -198,19 +201,25 @@
198 feature_flag = {201 feature_flag = {
199 'disclosure.picker_enhancements.enabled': 'on',202 'disclosure.picker_enhancements.enabled': 'on',
200 'disclosure.picker_expander.enabled': 'on',203 'disclosure.picker_expander.enabled': 'on',
204 'disclosure.personpicker_affiliation.enabled': 'on',
201 }205 }
202 flags = FeatureFixture(feature_flag)206 flags = FeatureFixture(feature_flag)
203 flags.setUp()207 flags.setUp()
204 self.addCleanup(flags.cleanUp)208 self.addCleanup(flags.cleanUp)
205 team = self.factory.makeTeam(name='pting-team')209 team = self.factory.makeTeam(name='pting-team')
206 TestPersonVocabulary.test_persons.append(team)210 TestPersonVocabulary.test_persons.append(team)
211 product = self.factory.makeProduct(owner=team)
212 bugtask = self.factory.makeBugTask(target=product)
207 form = dict(name='TestPerson', search_text='pting-team')213 form = dict(name='TestPerson', search_text='pting-team')
208 view = self.create_vocabulary_view(form)214 view = self.create_vocabulary_view(form, context=bugtask)
209 result = simplejson.loads(view())215 result = simplejson.loads(view())
210 expected = {216 expected = {
211 "alt_title": team.name,217 "alt_title": team.name,
212 "alt_title_link": "http://launchpad.dev/~%s" % team.name,218 "alt_title_link": "http://launchpad.dev/~%s" % team.name,
213 "api_uri": "/~%s" % team.name,219 "api_uri": "/~%s" % team.name,
220 "badges":
221 [{"alt": "%s maintainer" % product.displayname,
222 "url": "/@@/product-badge"}],
214 "css": "sprite team",223 "css": "sprite team",
215 "details": ['Team members: 1'],224 "details": ['Team members: 1'],
216 "link_css": "sprite new-window",225 "link_css": "sprite new-window",
217226
=== modified file 'lib/lp/app/browser/vocabulary.py'
--- lib/lp/app/browser/vocabulary.py 2011-08-04 15:18:04 +0000
+++ lib/lp/app/browser/vocabulary.py 2011-08-09 00:14:32 +0000
@@ -130,8 +130,9 @@
130 extra = super(PersonPickerEntryAdapter, self).getPickerEntry(130 extra = super(PersonPickerEntryAdapter, self).getPickerEntry(
131 associated_object)131 associated_object)
132132
133 enhanced_picker_enabled = kwarg.get('enhanced_picker_enabled', False)133 personpicker_affiliation_enabled = kwarg.get(
134 if enhanced_picker_enabled:134 'personpicker_affiliation_enabled', False)
135 if personpicker_affiliation_enabled:
135 # If the person is affiliated with the associated_object then we136 # If the person is affiliated with the associated_object then we
136 # can display a badge.137 # can display a badge.
137 badge_info = IHasAffiliation(138 badge_info = IHasAffiliation(
@@ -154,6 +155,7 @@
154 extra.description = '<email address hidden>'155 extra.description = '<email address hidden>'
155156
156 extra.metadata = get_person_picker_entry_metadata(person)157 extra.metadata = get_person_picker_entry_metadata(person)
158 enhanced_picker_enabled = kwarg.get('enhanced_picker_enabled', False)
157 if enhanced_picker_enabled:159 if enhanced_picker_enabled:
158 # We will display the person's name (launchpad id) after their160 # We will display the person's name (launchpad id) after their
159 # displayname.161 # displayname.
@@ -242,6 +244,8 @@
242 getFeatureFlag('disclosure.picker_enhancements.enabled'))244 getFeatureFlag('disclosure.picker_enhancements.enabled'))
243 self.picker_expander_enabled = bool(245 self.picker_expander_enabled = bool(
244 getFeatureFlag('disclosure.picker_expander.enabled'))246 getFeatureFlag('disclosure.picker_expander.enabled'))
247 self.personpicker_affiliation_enabled = bool(
248 getFeatureFlag('disclosure.personpicker_affiliation.enabled'))
245249
246 def __call__(self):250 def __call__(self):
247 name = self.request.form.get('name')251 name = self.request.form.get('name')
@@ -287,7 +291,9 @@
287 picker_entry = IPickerEntry(term.value).getPickerEntry(291 picker_entry = IPickerEntry(term.value).getPickerEntry(
288 self.context,292 self.context,
289 enhanced_picker_enabled=self.enhanced_picker_enabled,293 enhanced_picker_enabled=self.enhanced_picker_enabled,
290 picker_expander_enabled=self.picker_expander_enabled)294 picker_expander_enabled=self.picker_expander_enabled,
295 personpicker_affiliation_enabled=
296 self.personpicker_affiliation_enabled)
291 if picker_entry.description is not None:297 if picker_entry.description is not None:
292 if len(picker_entry.description) > MAX_DESCRIPTION_LENGTH:298 if len(picker_entry.description) > MAX_DESCRIPTION_LENGTH:
293 entry['description'] = (299 entry['description'] = (
294300
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2011-08-02 05:35:39 +0000
+++ lib/lp/registry/configure.zcml 2011-08-09 00:14:32 +0000
@@ -883,11 +883,36 @@
883 permission="zope.Public"/>883 permission="zope.Public"/>
884884
885 <adapter885 <adapter
886 for="lp.registry.interfaces.distribution.IDistribution"
887 factory="lp.registry.model.pillaraffiliation.PillarAffiliation"
888 />
889 <adapter
890 for="lp.registry.interfaces.product.IProduct"
891 factory="lp.registry.model.pillaraffiliation.PillarAffiliation"
892 />
893 <adapter
894 for="lp.bugs.interfaces.bugtask.IBugTask"
886 factory="lp.registry.model.pillaraffiliation.BugTaskPillarAffiliation"895 factory="lp.registry.model.pillaraffiliation.BugTaskPillarAffiliation"
887 />896 />
888897 <adapter
889 <adapter898 for="lp.code.interfaces.branch.IBranch"
890 factory="lp.registry.model.pillaraffiliation.PillarAffiliation"899 factory="lp.registry.model.pillaraffiliation.BranchPillarAffiliation"
900 />
901 <adapter
902 for="lp.answers.interfaces.question.IQuestion"
903 factory="lp.registry.model.pillaraffiliation.QuestionPillarAffiliation"
904 />
905 <adapter
906 for="lp.blueprints.interfaces.specification.ISpecification"
907 factory="lp.registry.model.pillaraffiliation.SpecificationPillarAffiliation"
908 />
909 <adapter
910 for="lp.registry.interfaces.distroseries.IDistroSeries"
911 factory="lp.registry.model.pillaraffiliation.DistroSeriesPillarAffiliation"
912 />
913 <adapter
914 for="lp.registry.interfaces.productseries.IProductSeries"
915 factory="lp.registry.model.pillaraffiliation.ProductSeriesPillarAffiliation"
891 />916 />
892917
893 <!-- Using918 <!-- Using
894919
=== modified file 'lib/lp/registry/model/pillaraffiliation.py'
--- lib/lp/registry/model/pillaraffiliation.py 2011-08-04 13:56:55 +0000
+++ lib/lp/registry/model/pillaraffiliation.py 2011-08-09 00:14:32 +0000
@@ -29,8 +29,11 @@
29 )29 )
3030
31from canonical.launchpad.interfaces.launchpad import IHasIcon31from canonical.launchpad.interfaces.launchpad import IHasIcon
32from lp.bugs.interfaces.bugtask import IBugTask32from lp.answers.interfaces.questionsperson import IQuestionsPerson
33from lp.registry.interfaces.distribution import IDistribution33from lp.registry.interfaces.distribution import IDistribution
34from lp.registry.interfaces.distributionsourcepackage import (
35 IDistributionSourcePackage,
36 )
3437
3538
36class IHasAffiliation(Interface):39class IHasAffiliation(Interface):
@@ -51,7 +54,9 @@
51class PillarAffiliation(object):54class PillarAffiliation(object):
52 """Default affiliation adapter.55 """Default affiliation adapter.
5356
54 No affiliation is returned.57 Subclasses may need to override getPillar() in order to provide the pillar
58 entity for which affiliation is to be determined. The default is just to
59 use the context object directly.
55 """60 """
5661
57 implements(IHasAffiliation)62 implements(IHasAffiliation)
@@ -59,32 +64,116 @@
59 def __init__(self, context):64 def __init__(self, context):
60 self.context = context65 self.context = context
6166
62 def getAffiliationBadge(self, person):67 def getPillar(self):
63 return None68 return self.context
6469
6570 def _getAffiliationDetails(self, person, pillar):
66# XXX: wallyworld 2011-05-24 bug=81692: TODO Work is required to determine71 """ Return the affiliation information for a person, if any.
67# exactly what is required in terms of figuring out affiliation..72
6873 A person is affiliated with a pillar if they are in the list of
69@adapter(IBugTask)74 drivers or are the maintainer.
70class BugTaskPillarAffiliation(PillarAffiliation):75 """
71 """An affiliation adapter for bug tasks."""76 if person.inTeam(pillar.owner):
7277 return pillar.displayname, 'maintainer'
73 def getAffiliationBadge(self, person):78 for driver in pillar.drivers:
74 pillar = self.context.pillar79 if person.inTeam(driver):
75 affiliated = person.inTeam(pillar.owner)80 return pillar.displayname, 'driver'
76 if not affiliated:81 return None
82
83 def getAffiliationBadge(self, person):
84 """ Return the affiliation badge details for a person given a context.
85 """
86 pillar = self.getPillar()
87 affiliation_details = self._getAffiliationDetails(person, pillar)
88 if not affiliation_details:
77 return None89 return None
7890
79 def getIconUrl(context, default_url):91 def getIconUrl(context, pillar, default_url):
80 if IHasIcon.providedBy(context) and context.icon is not None:92 if IHasIcon.providedBy(context) and context.icon is not None:
81 icon_url = context.icon.getURL()93 icon_url = context.icon.getURL()
82 return icon_url94 return icon_url
95 if IHasIcon.providedBy(pillar) and pillar.icon is not None:
96 icon_url = context.icon.getURL()
97 return icon_url
83 return default_url98 return default_url
8499
85 alt_text = "Affiliated with %s" % pillar.displayname100 alt_text = "%s %s" % affiliation_details
86 if IDistribution.providedBy(pillar):101 if IDistribution.providedBy(pillar):
87 icon_url = getIconUrl(pillar, "/@@/distribution-badge")102 default_icon_url = "/@@/distribution-badge"
88 else:103 else:
89 icon_url = getIconUrl(pillar, "/@@/product-badge")104 default_icon_url = "/@@/product-badge"
105 icon_url = getIconUrl(self.context, pillar, default_icon_url)
90 return BadgeDetails(icon_url, alt_text)106 return BadgeDetails(icon_url, alt_text)
107
108
109class BugTaskPillarAffiliation(PillarAffiliation):
110 """An affiliation adapter for bug tasks."""
111 def getPillar(self):
112 return self.context.pillar
113
114 def _getAffiliationDetails(self, person, pillar):
115 """ A person is affiliated with a bugtask based on (in order):
116 - owner of bugtask pillar
117 - driver of bugtask pillar
118 - bug supervisor of bugtask pillar
119 - security contact of bugtask pillar
120 """
121 result = super(BugTaskPillarAffiliation, self)._getAffiliationDetails(
122 person, pillar)
123 if result is not None:
124 return result
125 if person.inTeam(pillar.bug_supervisor):
126 return pillar.displayname, 'bug supervisor'
127 if person.inTeam(pillar.security_contact):
128 return pillar.displayname, 'security contact'
129
130
131class BranchPillarAffiliation(BugTaskPillarAffiliation):
132 """An affiliation adapter for branches."""
133 def getPillar(self):
134 return self.context.product or self.context.distribution
135
136
137class DistroSeriesPillarAffiliation(PillarAffiliation):
138 """An affiliation adapter for distroseries."""
139 def getPillar(self):
140 return self.context.distribution
141
142
143class ProductSeriesPillarAffiliation(PillarAffiliation):
144 """An affiliation adapter for productseries."""
145 def getPillar(self):
146 return self.context.product
147
148
149class SpecificationPillarAffiliation(PillarAffiliation):
150 """An affiliation adapter for blueprints."""
151 def getPillar(self):
152 return (self.context.target)
153
154
155class QuestionPillarAffiliation(PillarAffiliation):
156 """An affiliation adapter for questions."""
157 def getPillar(self):
158 return self.context.product or self.context.distribution
159
160 def _getAffiliationDetails(self, person, pillar):
161 """ A person is affiliated with a question based on (in order):
162 - answer contact for question target
163 - owner of question target
164 - driver of question target
165 """
166 target = self.context.target
167 if IDistributionSourcePackage.providedBy(target):
168 question_targets = (target, target.distribution)
169 else:
170 question_targets = (target, )
171 questions_person = IQuestionsPerson(person)
172 for target in questions_person.getDirectAnswerQuestionTargets():
173 if target in question_targets:
174 return target.displayname, 'answer contact'
175 for target in questions_person.getTeamAnswerQuestionTargets():
176 if target in question_targets:
177 return target.displayname, 'answer contact'
178 return super(QuestionPillarAffiliation, self)._getAffiliationDetails(
179 person, pillar)
91180
=== modified file 'lib/lp/registry/tests/test_pillaraffiliation.py'
--- lib/lp/registry/tests/test_pillaraffiliation.py 2011-08-04 13:56:55 +0000
+++ lib/lp/registry/tests/test_pillaraffiliation.py 2011-08-09 00:14:32 +0000
@@ -5,29 +5,451 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from storm.store import Store
9from testtools.matchers import Equals
10from zope.component import getUtility
11
8from canonical.testing.layers import DatabaseFunctionalLayer12from canonical.testing.layers import DatabaseFunctionalLayer
9from lp.registry.model.pillaraffiliation import IHasAffiliation13from lp.registry.model.pillaraffiliation import IHasAffiliation
10from lp.testing import TestCaseWithFactory14from lp.services.worlddata.interfaces.language import ILanguageSet
15from lp.testing import (
16 person_logged_in,
17 StormStatementRecorder,
18 TestCaseWithFactory,
19 )
20from lp.testing.matchers import HasQueryCount
1121
1222
13class TestPillarAffiliation(TestCaseWithFactory):23class TestPillarAffiliation(TestCaseWithFactory):
1424
15 layer = DatabaseFunctionalLayer25 layer = DatabaseFunctionalLayer
1626
17 def test_bugtask_distro_affiliation(self):27 def _check_affiliated_with_distro(self, person, distro, role):
18 # A person who owns a bugtask distro is affiliated.28 badge = IHasAffiliation(distro).getAffiliationBadge(person)
29 self.assertEqual(
30 ("/@@/distribution-badge", "Pting %s" % role), badge)
31
32 def test_distro_owner_affiliation(self):
33 # A person who owns a distro is affiliated.
34 person = self.factory.makePerson()
35 distro = self.factory.makeDistribution(owner=person, name='pting')
36 self._check_affiliated_with_distro(person, distro, 'maintainer')
37
38 def test_distro_driver_affiliation(self):
39 # A person who is a distro driver is affiliated.
40 person = self.factory.makePerson()
41 distro = self.factory.makeDistribution(driver=person, name='pting')
42 self._check_affiliated_with_distro(person, distro, 'driver')
43
44 def test_distro_team_driver_affiliation(self):
45 # A person who is a member of the distro driver team is affiliated.
46 person = self.factory.makePerson()
47 team = self.factory.makeTeam(members=[person])
48 distro = self.factory.makeDistribution(driver=team, name='pting')
49 self._check_affiliated_with_distro(person, distro, 'driver')
50
51 def test_no_distro_security_contact_affiliation(self):
52 # A person who is the security contact for a distro is not affiliated
53 # for simple distro affiliation checks.
54 person = self.factory.makePerson()
55 distro = self.factory.makeDistribution(security_contact=person)
56 self.assertIs(
57 None, IHasAffiliation(distro).getAffiliationBadge(person))
58
59 def test_no_distro_bug_supervisor_affiliation(self):
60 # A person who is the bug supervisor for a distro is not affiliated
61 # for simple distro affiliation checks.
62 person = self.factory.makePerson()
63 distro = self.factory.makeDistribution(bug_supervisor=person)
64 self.assertIs(
65 None, IHasAffiliation(distro).getAffiliationBadge(person))
66
67 def _check_affiliated_with_product(self, person, product, role):
68 badge = IHasAffiliation(product).getAffiliationBadge(person)
69 self.assertEqual(
70 ("/@@/product-badge", "Pting %s" % role), badge)
71
72 def test_product_driver_affiliation(self):
73 # A person who is the driver for a product is affiliated.
74 person = self.factory.makePerson()
75 product = self.factory.makeProduct(driver=person, name='pting')
76 self._check_affiliated_with_product(person, product, 'driver')
77
78 def test_product_team_driver_affiliation(self):
79 # A person who is a member of the product driver team is affiliated.
80 person = self.factory.makePerson()
81 team = self.factory.makeTeam(members=[person])
82 product = self.factory.makeProduct(driver=team, name='pting')
83 self._check_affiliated_with_product(person, product, 'driver')
84
85 def test_product_group_driver_affiliation(self):
86 # A person who is the driver for a product's group is affiliated.
87 person = self.factory.makePerson()
88 project = self.factory.makeProject(driver=person)
89 product = self.factory.makeProduct(project=project, name='pting')
90 self._check_affiliated_with_product(person, product, 'driver')
91
92 def test_no_product_security_contact_affiliation(self):
93 # A person who is the security contact for a product is is not
94 # affiliated for simple product affiliation checks.
95 person = self.factory.makePerson()
96 product = self.factory.makeProduct(security_contact=person)
97 self.assertIs(
98 None, IHasAffiliation(product).getAffiliationBadge(person))
99
100 def test_no_product_bug_supervisor_affiliation(self):
101 # A person who is the bug supervisor for a product is is not
102 # affiliated for simple product affiliation checks.
103 person = self.factory.makePerson()
104 product = self.factory.makeProduct(bug_supervisor=person)
105 self.assertIs(
106 None, IHasAffiliation(product).getAffiliationBadge(person))
107
108 def test_product_owner_affiliation(self):
109 # A person who owns a product is affiliated.
110 person = self.factory.makePerson()
111 product = self.factory.makeProduct(owner=person, name='pting')
112 self._check_affiliated_with_product(person, product, 'maintainer')
113
114
115class _TestBugTaskorBranchMixin:
116
117 def test_distro_security_contact_affiliation(self):
118 # A person who is the security contact for a distro is affiliated.
119 person = self.factory.makePerson()
120 distro = self.factory.makeDistribution(
121 security_contact=person, name='pting')
122 self._check_affiliated_with_distro(person, distro, 'security contact')
123
124 def test_distro_bug_supervisor_affiliation(self):
125 # A person who is the bug supervisor for a distro is affiliated.
126 person = self.factory.makePerson()
127 distro = self.factory.makeDistribution(
128 bug_supervisor=person, name='pting')
129 self._check_affiliated_with_distro(person, distro, 'bug supervisor')
130
131 def test_product_security_contact_affiliation(self):
132 # A person who is the security contact for a distro is affiliated.
133 person = self.factory.makePerson()
134 product = self.factory.makeProduct(
135 security_contact=person, name='pting')
136 self._check_affiliated_with_product(
137 person, product, 'security contact')
138
139 def test_product_bug_supervisor_affiliation(self):
140 # A person who is the bug supervisor for a distro is affiliated.
141 person = self.factory.makePerson()
142 product = self.factory.makeProduct(
143 bug_supervisor=person, name='pting')
144 self._check_affiliated_with_product(person, product, 'bug supervisor')
145
146
147class TestBugTaskPillarAffiliation(_TestBugTaskorBranchMixin,
148 TestCaseWithFactory):
149
150 layer = DatabaseFunctionalLayer
151
152 def test_correct_pillar_is_used(self):
153 bugtask = self.factory.makeBugTask()
154 badge = IHasAffiliation(bugtask)
155 self.assertEqual(bugtask.pillar, badge.getPillar())
156
157 def _check_affiliated_with_distro(self, person, target, role):
158 bugtask = self.factory.makeBugTask(target=target)
159 badge = IHasAffiliation(bugtask).getAffiliationBadge(person)
160 self.assertEqual(
161 ("/@@/distribution-badge", "Pting %s" % role), badge)
162
163 def _check_affiliated_with_product(self, person, target, role):
164 bugtask = self.factory.makeBugTask(target=target)
165 badge = IHasAffiliation(bugtask).getAffiliationBadge(person)
166 self.assertEqual(
167 ("/@@/product-badge", "Pting %s" % role), badge)
168
169 def test_product_affiliation_query_count(self):
170 # Only 4 queries are expected, selects from:
171 # - Bug, BugTask, Product, Person
172 person = self.factory.makePerson()
173 product = self.factory.makeProduct(owner=person, name='pting')
174 bugtask = self.factory.makeBugTask(target=product)
175 Store.of(bugtask).invalidate()
176 with StormStatementRecorder() as recorder:
177 IHasAffiliation(bugtask).getAffiliationBadge(person)
178 self.assertThat(recorder, HasQueryCount(Equals(4)))
179
180 def test_distro_affiliation_query_count(self):
181 # Only 4 queries are expected, selects from:
182 # - Bug, BugTask, Distribution, Person
19 person = self.factory.makePerson()183 person = self.factory.makePerson()
20 distro = self.factory.makeDistribution(owner=person, name='pting')184 distro = self.factory.makeDistribution(owner=person, name='pting')
21 bugtask = self.factory.makeBugTask(target=distro)185 bugtask = self.factory.makeBugTask(target=distro)
22 badge = IHasAffiliation(bugtask).getAffiliationBadge(person)186 Store.of(bugtask).invalidate()
23 self.assertEqual(187 with StormStatementRecorder() as recorder:
24 badge, ("/@@/distribution-badge", "Affiliated with Pting"))188 IHasAffiliation(bugtask).getAffiliationBadge(person)
25189 self.assertThat(recorder, HasQueryCount(Equals(4)))
26 def test_bugtask_product_affiliation(self):190
27 # A person who owns a bugtask product is affiliated.191
28 person = self.factory.makePerson()192class TestBranchPillarAffiliation(_TestBugTaskorBranchMixin,
29 product = self.factory.makeProduct(owner=person, name='pting')193 TestCaseWithFactory):
30 bugtask = self.factory.makeBugTask(target=product)194
31 badge = IHasAffiliation(bugtask).getAffiliationBadge(person)195 layer = DatabaseFunctionalLayer
32 self.assertEqual(196
33 badge, ("/@@/product-badge", "Affiliated with Pting"))197 def test_correct_pillar_is_used(self):
198 branch = self.factory.makeBranch()
199 badge = IHasAffiliation(branch)
200 self.assertEqual(branch.product, badge.getPillar())
201
202 def _check_affiliated_with_distro(self, person, target, role):
203 distroseries = self.factory.makeDistroSeries(distribution=target)
204 sp = self.factory.makeSourcePackage(distroseries=distroseries)
205 branch = self.factory.makeBranch(sourcepackage=sp)
206 badge = IHasAffiliation(branch).getAffiliationBadge(person)
207 self.assertEqual(
208 ("/@@/distribution-badge", "Pting %s" % role), badge)
209
210 def _check_affiliated_with_product(self, person, target, role):
211 branch = self.factory.makeBranch(product=target)
212 badge = IHasAffiliation(branch).getAffiliationBadge(person)
213 self.assertEqual(
214 ("/@@/product-badge", "Pting %s" % role), badge)
215
216
217class TestDistroSeriesPillarAffiliation(TestCaseWithFactory):
218
219 layer = DatabaseFunctionalLayer
220
221 def test_correct_pillar_is_used(self):
222 series = self.factory.makeDistroSeries()
223 badge = IHasAffiliation(series)
224 self.assertEqual(series.distribution, badge.getPillar())
225
226 def test_driver_affiliation(self):
227 # A person who is the driver for a distroseries is affiliated.
228 # Here, the affiliation is with the distribution of the series.
229 owner = self.factory.makePerson()
230 driver = self.factory.makePerson()
231 distribution = self.factory.makeDistribution(
232 owner=owner, driver=driver, name='pting')
233 distroseries = self.factory.makeDistroSeries(
234 registrant=driver, distribution=distribution)
235 badge = IHasAffiliation(distroseries).getAffiliationBadge(driver)
236 self.assertEqual(
237 ("/@@/distribution-badge", "Pting driver"), badge)
238
239 def test_distro_driver_affiliation(self):
240 # A person who is the driver for a distroseries' distro is affiliated.
241 # Here, the affiliation is with the distribution of the series.
242 owner = self.factory.makePerson()
243 driver = self.factory.makePerson()
244 distribution = self.factory.makeDistribution(
245 owner=owner, driver=driver, name='pting')
246 distroseries = self.factory.makeDistroSeries(
247 registrant=owner, distribution=distribution)
248 badge = IHasAffiliation(distroseries).getAffiliationBadge(driver)
249 self.assertEqual(
250 ("/@@/distribution-badge", "Pting driver"), badge)
251
252
253class TestProductSeriesPillarAffiliation(TestCaseWithFactory):
254
255 layer = DatabaseFunctionalLayer
256
257 def test_correct_pillar_is_used(self):
258 series = self.factory.makeProductSeries()
259 badge = IHasAffiliation(series)
260 self.assertEqual(series.product, badge.getPillar())
261
262 def test_driver_affiliation(self):
263 # A person who is the driver for a productseries is affiliated.
264 # Here, the affiliation is with the product.
265 owner = self.factory.makePerson()
266 driver = self.factory.makePerson()
267 product = self.factory.makeProduct(
268 owner=owner, driver=driver, name='pting')
269 productseries = self.factory.makeProductSeries(
270 owner=driver, product=product)
271 badge = IHasAffiliation(productseries).getAffiliationBadge(driver)
272 self.assertEqual(
273 ("/@@/product-badge", "Pting driver"), badge)
274
275 def test_product_driver_affiliation(self):
276 # A person who is the driver for a productseries' product is
277 # affiliated. Here, the affiliation is with the product.
278 owner = self.factory.makePerson()
279 driver = self.factory.makePerson()
280 product = self.factory.makeProduct(
281 owner=owner, driver=driver, name='pting')
282 productseries = self.factory.makeProductSeries(
283 owner=owner, product=product)
284 badge = IHasAffiliation(productseries).getAffiliationBadge(driver)
285 self.assertEqual(
286 ("/@@/product-badge", "Pting driver"), badge)
287
288 def test_product_group_driver_affiliation(self):
289 # A person who is the driver for a productseries' product's group is
290 # affiliated. Here, the affiliation is with the product.
291 owner = self.factory.makePerson()
292 driver = self.factory.makePerson()
293 project = self.factory.makeProject(driver=driver)
294 product = self.factory.makeProduct(
295 owner=owner, project=project, name='pting')
296 productseries = self.factory.makeProductSeries(
297 owner=owner, product=product)
298 badge = IHasAffiliation(productseries).getAffiliationBadge(driver)
299 self.assertEqual(
300 ("/@@/product-badge", "Pting driver"), badge)
301
302
303class TestQuestionPillarAffiliation(TestCaseWithFactory):
304
305 layer = DatabaseFunctionalLayer
306
307 def test_correct_pillar_is_used_for_product(self):
308 product = self.factory.makeProduct()
309 question = self.factory.makeQuestion(target=product)
310 badge = IHasAffiliation(question)
311 self.assertEqual(question.product, badge.getPillar())
312
313 def test_correct_pillar_is_used_for_distribution(self):
314 distribution = self.factory.makeDistribution()
315 question = self.factory.makeQuestion(target=distribution)
316 badge = IHasAffiliation(question)
317 self.assertEqual(question.distribution, badge.getPillar())
318
319 def test_correct_pillar_is_used_for_distro_sourcepackage(self):
320 distribution = self.factory.makeDistribution()
321 distro_sourcepackage = self.factory.makeDistributionSourcePackage(
322 distribution=distribution)
323 owner = self.factory.makePerson()
324 question = self.factory.makeQuestion(
325 target=distro_sourcepackage, owner=owner)
326 badge = IHasAffiliation(question)
327 self.assertEqual(distribution, badge.getPillar())
328
329 def test_answer_contact_affiliation_for_distro(self):
330 # A person is affiliated if they are an answer contact for a distro
331 # target. Even if they also own the distro, the answer contact
332 # affiliation takes precedence.
333 answer_contact = self.factory.makePerson()
334 english = getUtility(ILanguageSet)['en']
335 answer_contact.addLanguage(english)
336 distro = self.factory.makeDistribution(owner=answer_contact)
337 with person_logged_in(answer_contact):
338 distro.addAnswerContact(answer_contact, answer_contact)
339 question = self.factory.makeQuestion(target=distro)
340 badge = IHasAffiliation(question).getAffiliationBadge(answer_contact)
341 self.assertEqual(
342 ("/@@/distribution-badge", "%s answer contact" %
343 distro.displayname), badge)
344
345 def test_answer_contact_affiliation_for_distro_sourcepackage(self):
346 # A person is affiliated if they are an answer contact for a dsp
347 # target. Even if they also own the distro, the answer contact
348 # affiliation takes precedence.
349 answer_contact = self.factory.makePerson()
350 english = getUtility(ILanguageSet)['en']
351 answer_contact.addLanguage(english)
352 distribution = self.factory.makeDistribution(owner=answer_contact)
353 distro_sourcepackage = self.factory.makeDistributionSourcePackage(
354 distribution=distribution)
355 with person_logged_in(answer_contact):
356 distro_sourcepackage.addAnswerContact(
357 answer_contact, answer_contact)
358 question = self.factory.makeQuestion(
359 target=distro_sourcepackage, owner=answer_contact)
360 badge = IHasAffiliation(question).getAffiliationBadge(answer_contact)
361 self.assertEqual(
362 ("/@@/distribution-badge", "%s answer contact" %
363 distro_sourcepackage.displayname), badge)
364
365 def test_answer_contact_affiliation_for_distro_sourcepackage_distro(self):
366 # A person is affiliated if they are an answer contact for a dsp
367 # target's distro. Even if they also own the distro, the answer
368 # contact affiliation takes precedence.
369 answer_contact = self.factory.makePerson()
370 english = getUtility(ILanguageSet)['en']
371 answer_contact.addLanguage(english)
372 distribution = self.factory.makeDistribution(owner=answer_contact)
373 distro_sourcepackage = self.factory.makeDistributionSourcePackage(
374 distribution=distribution)
375 with person_logged_in(answer_contact):
376 distribution.addAnswerContact(answer_contact, answer_contact)
377 question = self.factory.makeQuestion(
378 target=distro_sourcepackage, owner=answer_contact)
379 badge = IHasAffiliation(question).getAffiliationBadge(answer_contact)
380 self.assertEqual(
381 ("/@@/distribution-badge", "%s answer contact" %
382 distribution.displayname), badge)
383
384 def test_answer_contact_affiliation_for_product(self):
385 # A person is affiliated if they are an answer contact for a product
386 # target. Even if they also own the product, the answer contact
387 # affiliation takes precedence.
388 answer_contact = self.factory.makePerson()
389 english = getUtility(ILanguageSet)['en']
390 answer_contact.addLanguage(english)
391 product = self.factory.makeProduct(owner=answer_contact)
392 with person_logged_in(answer_contact):
393 product.addAnswerContact(answer_contact, answer_contact)
394 question = self.factory.makeQuestion(target=product)
395 badge = IHasAffiliation(question).getAffiliationBadge(answer_contact)
396 self.assertEqual(
397 ("/@@/product-badge", "%s answer contact" %
398 product.displayname), badge)
399
400 def test_product_affiliation(self):
401 # A person is affiliated if they are affiliated with the product.
402 person = self.factory.makePerson()
403 product = self.factory.makeProduct(owner=person)
404 question = self.factory.makeQuestion(target=product)
405 badge = IHasAffiliation(question).getAffiliationBadge(person)
406 self.assertEqual(
407 ("/@@/product-badge", "%s maintainer" %
408 product.displayname), badge)
409
410 def test_distribution_affiliation(self):
411 # A person is affiliated if they are affiliated with the distribution.
412 person = self.factory.makePerson()
413 distro = self.factory.makeDistribution(owner=person)
414 question = self.factory.makeQuestion(target=distro)
415 badge = IHasAffiliation(question).getAffiliationBadge(person)
416 self.assertEqual(
417 ("/@@/distribution-badge", "%s maintainer" %
418 distro.displayname), badge)
419
420
421class TestSpecificationPillarAffiliation(TestCaseWithFactory):
422
423 layer = DatabaseFunctionalLayer
424
425 def test_correct_pillar_is_used_for_product(self):
426 product = self.factory.makeProduct()
427 specification = self.factory.makeSpecification(product=product)
428 badge = IHasAffiliation(specification)
429 self.assertEqual(specification.product, badge.getPillar())
430
431 def test_correct_pillar_is_used_for_distribution(self):
432 distro = self.factory.makeDistribution()
433 specification = self.factory.makeSpecification(distribution=distro)
434 badge = IHasAffiliation(specification)
435 self.assertEqual(specification.distribution, badge.getPillar())
436
437 def test_product_affiliation(self):
438 # A person is affiliated if they are affiliated with the pillar.
439 person = self.factory.makePerson()
440 product = self.factory.makeProduct(owner=person)
441 specification = self.factory.makeSpecification(product=product)
442 badge = IHasAffiliation(specification).getAffiliationBadge(person)
443 self.assertEqual(
444 ("/@@/product-badge", "%s maintainer" %
445 product.displayname), badge)
446
447 def test_distribution_affiliation(self):
448 # A person is affiliated if they are affiliated with the distribution.
449 person = self.factory.makePerson()
450 distro = self.factory.makeDistribution(owner=person)
451 specification = self.factory.makeSpecification(distribution=distro)
452 badge = IHasAffiliation(specification).getAffiliationBadge(person)
453 self.assertEqual(
454 ("/@@/distribution-badge", "%s maintainer" %
455 distro.displayname), badge)
34456
=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py 2011-08-05 06:40:47 +0000
+++ lib/lp/services/features/flags.py 2011-08-09 00:14:32 +0000
@@ -121,6 +121,10 @@
121 'boolean',121 'boolean',
122 ('Enables the expanding of extra details in the person picker.'),122 ('Enables the expanding of extra details in the person picker.'),
123 ''),123 ''),
124 ('disclosure.personpicker_affiliation.enabled',
125 'boolean',
126 ('Enables display of affiliation details in the person picker.'),
127 ''),
124 ('disclosure.person_affiliation_rank.enabled',128 ('disclosure.person_affiliation_rank.enabled',
125 'boolean',129 'boolean',
126 ('Enables ranking by pillar affiliation in the person picker.'),130 ('Enables ranking by pillar affiliation in the person picker.'),
127131
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-08-05 06:40:47 +0000
+++ lib/lp/testing/factory.py 2011-08-09 00:14:32 +0000
@@ -954,7 +954,7 @@
954 licenses=None, owner=None, registrant=None,954 licenses=None, owner=None, registrant=None,
955 title=None, summary=None, official_malone=None,955 title=None, summary=None, official_malone=None,
956 translations_usage=None, bug_supervisor=None,956 translations_usage=None, bug_supervisor=None,
957 driver=None):957 driver=None, security_contact=None):
958 """Create and return a new, arbitrary Product."""958 """Create and return a new, arbitrary Product."""
959 if owner is None:959 if owner is None:
960 owner = self.makePerson()960 owner = self.makePerson()
@@ -990,6 +990,8 @@
990 naked_product.bug_supervisor = bug_supervisor990 naked_product.bug_supervisor = bug_supervisor
991 if driver is not None:991 if driver is not None:
992 naked_product.driver = driver992 naked_product.driver = driver
993 if security_contact is not None:
994 naked_product.security_contact = security_contact
993 return product995 return product
994996
995 def makeProductSeries(self, product=None, name=None, owner=None,997 def makeProductSeries(self, product=None, name=None, owner=None,
@@ -1023,7 +1025,7 @@
1023 return ProxyFactory(series)1025 return ProxyFactory(series)
10241026
1025 def makeProject(self, name=None, displayname=None, title=None,1027 def makeProject(self, name=None, displayname=None, title=None,
1026 homepageurl=None, summary=None, owner=None,1028 homepageurl=None, summary=None, owner=None, driver=None,
1027 description=None):1029 description=None):
1028 """Create and return a new, arbitrary ProjectGroup."""1030 """Create and return a new, arbitrary ProjectGroup."""
1029 if owner is None:1031 if owner is None:
@@ -1038,7 +1040,7 @@
1038 description = self.getUniqueString('description')1040 description = self.getUniqueString('description')
1039 if title is None:1041 if title is None:
1040 title = self.getUniqueString('title')1042 title = self.getUniqueString('title')
1041 return getUtility(IProjectGroupSet).new(1043 project = getUtility(IProjectGroupSet).new(
1042 name=name,1044 name=name,
1043 displayname=displayname,1045 displayname=displayname,
1044 title=title,1046 title=title,
@@ -1046,6 +1048,9 @@
1046 summary=summary,1048 summary=summary,
1047 description=description,1049 description=description,
1048 owner=owner)1050 owner=owner)
1051 if driver is not None:
1052 removeSecurityProxy(project).driver = driver
1053 return project
10491054
1050 def makeSprint(self, title=None, name=None):1055 def makeSprint(self, title=None, name=None):
1051 """Make a sprint."""1056 """Make a sprint."""
@@ -2324,9 +2329,10 @@
23242329
2325 def makeDistribution(self, name=None, displayname=None, owner=None,2330 def makeDistribution(self, name=None, displayname=None, owner=None,
2326 registrant=None, members=None, title=None,2331 registrant=None, members=None, title=None,
2327 aliases=None, bug_supervisor=None,2332 aliases=None, bug_supervisor=None, driver=None,
2328 publish_root_dir=None, publish_base_url=None,2333 security_contact=None, publish_root_dir=None,
2329 publish_copy_base_url=None, no_pubconf=False):2334 publish_base_url=None, publish_copy_base_url=None,
2335 no_pubconf=False):
2330 """Make a new distribution."""2336 """Make a new distribution."""
2331 if name is None:2337 if name is None:
2332 name = self.getUniqueString(prefix="distribution")2338 name = self.getUniqueString(prefix="distribution")
@@ -2346,11 +2352,15 @@
2346 distro = getUtility(IDistributionSet).new(2352 distro = getUtility(IDistributionSet).new(
2347 name, displayname, title, description, summary, domainname,2353 name, displayname, title, description, summary, domainname,
2348 members, owner, registrant)2354 members, owner, registrant)
2355 naked_distro = removeSecurityProxy(distro)
2349 if aliases is not None:2356 if aliases is not None:
2350 removeSecurityProxy(distro).setAliases(aliases)2357 naked_distro.setAliases(aliases)
2358 if driver is not None:
2359 naked_distro.driver = driver
2351 if bug_supervisor is not None:2360 if bug_supervisor is not None:
2352 naked_distro = removeSecurityProxy(distro)
2353 naked_distro.bug_supervisor = bug_supervisor2361 naked_distro.bug_supervisor = bug_supervisor
2362 if security_contact is not None:
2363 naked_distro.security_contact = security_contact
2354 if not no_pubconf:2364 if not no_pubconf:
2355 self.makePublisherConfig(2365 self.makePublisherConfig(
2356 distro, publish_root_dir, publish_base_url,2366 distro, publish_root_dir, publish_base_url,