Merge lp:~thumper/launchpad/ibranch-interface-smashing into lp:launchpad

Proposed by Tim Penhey
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11203
Proposed branch: lp:~thumper/launchpad/ibranch-interface-smashing
Merge into: lp:launchpad
Diff against target: 750 lines (+259/-335)
3 files modified
lib/lp/code/configure.zcml (+17/-117)
lib/lp/code/interfaces/branch.py (+239/-216)
lib/lp/code/model/branch.py (+3/-2)
To merge this branch: bzr merge lp:~thumper/launchpad/ibranch-interface-smashing
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+29969@code.launchpad.net

Commit message

Move the attributes and methods of IBranch to other interfaces based on permissions.

Description of the change

Break the IBranch interface into other interfaces based on the required permissions.

There should be no other changes necessary.

There are two slight oddities with the resulting code.

I changed the IPrivacy inheritance in IBranch to be something that Branch directly provides. This is due to the fact that we were reimplmenting (or overriding) the private attribute defined by IPrivacy in the IBranch interface.

Secondly we are using an api mutator decorator on "private" which doesn't work when the private attibute isn't available in the same interface, so for now, I've left both private and setPrivate on IBranch.

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote :

boring

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/configure.zcml'
2--- lib/lp/code/configure.zcml 2010-07-22 01:48:51 +0000
3+++ lib/lp/code/configure.zcml 2010-07-22 09:28:53 +0000
4@@ -439,129 +439,29 @@
5 <class class="lp.code.model.branch.Branch">
6 <require
7 permission="launchpad.View"
8- attributes="id
9- branch_type
10- name
11- url
12- composePublicURL
13- whiteboard
14- target
15- mirror_status_message
16- private
17- registrant
18- owner
19- description
20- author
21- reviewer
22- code_reviewer
23- isPersonTrustedReviewer
24- product
25- unique_name
26- displayname
27- sort_key
28- lifecycle_status
29- last_mirrored
30- last_mirrored_id
31- last_mirror_attempt
32- mirror_failures
33- pull_disabled
34- next_mirror_time
35- last_scanned
36- last_scanned_id
37- revision_count
38- bug_branches
39- linked_bugs
40- getLinkedBugsAndTasks
41- linkBug
42- unlinkBug
43- spec_links
44- linkSpecification
45- unlinkSpecification
46- revision_history
47- subscriptions
48- subscribers
49- date_created
50- date_last_modified
51- latest_revisions
52- landing_targets
53- landing_candidates
54- dependent_branches
55- _createMergeProposal
56- addLandingTarget
57- scheduleDiffUpdates
58- getMergeQueue
59- getRevisionsSince
60- code_is_browseable
61- browse_source_url
62- code_import
63- bzr_identity
64- canBeDeleted
65- deletionRequirements
66- associatedProductSeries
67- getProductSeriesPushingTranslations
68- associatedSuiteSourcePackages
69- branchIdentities
70- branchLinks
71- subscribe
72- getSubscription
73- hasSubscription
74- unsubscribe
75- getSubscriptionsByLevel
76- getBranchRevision
77- getMainlineBranchRevisions
78- getMergeProposals
79- getStackedBranches
80- createBranchRevision
81- getTipRevision
82- updateScannedDetails
83- getNotificationRecipients
84- getScannerData
85- getPullURL
86- getInternalBzrUrl
87- getBzrBranch
88- requestMirror
89- startMirroring
90- mirrorFailed
91- branch_format
92- repository_format
93- control_format
94- stacked_on
95- createBranchRevisionFromIDs
96- distroseries
97- sourcepackagename
98- addToLaunchBag
99- distribution
100- sourcepackage
101- codebrowse_url
102- merge_queue
103- namespace
104- pending_writes
105- commitsForDays
106- needs_upgrading
107- upgrade_pending
108- getUpgradeFormat
109- isBranchMergeable
110- visibleByUser
111- getRecipes
112- "/>
113+ interface="canonical.launchpad.interfaces.launchpad.IPrivacy
114+ lp.code.interfaces.branch.IBranchAnyone
115+ lp.code.interfaces.branch.IBranchEditableAttributes
116+ lp.code.interfaces.branch.IBranchPublic
117+ lp.code.interfaces.branch.IBranchView
118+ "/>
119 <require
120 permission="launchpad.Edit"
121- attributes="destroySelf destroySelfBreakReferences setPrivate
122- setOwner setTarget requestUpgrade branchChanged"
123- set_attributes="name url mirror_status_message
124- description lifecycle_status
125- last_mirrored last_mirrored_id last_mirror_attempt
126- mirror_failures pull_disabled next_mirror_time
127- last_scanned last_scanned_id revision_count branch_type
128- reviewer branch_format repository_format
129- control_format stacked_on merge_queue
130- merge_control_status"/>
131+ interface="lp.code.interfaces.branch.IBranchEdit"
132+ set_schema="lp.code.interfaces.branch.IBranchEditableAttributes"
133+ attributes="setPrivate"
134+ set_attributes="branch_format control_format repository_format
135+ branch_type
136+ last_scanned last_scanned_id
137+ last_mirrored last_mirrored_id next_mirror_time
138+ revision_count merge_queue mirror_failures
139+ stacked_on mirror_status_message"/>
140 <require
141 permission="launchpad.AnyPerson"
142- set_attributes="whiteboard"/>
143+ set_schema="lp.code.interfaces.branch.IBranchAnyone"/>
144 <require
145 permission="zope.Public"
146- set_attributes="date_last_modified"/>
147+ set_schema="lp.code.interfaces.branch.IBranchPublic"/>
148 </class>
149 <adapter
150 for="lp.code.interfaces.branch.IBranch"
151
152=== modified file 'lib/lp/code/interfaces/branch.py'
153--- lib/lp/code/interfaces/branch.py 2010-07-09 10:22:32 +0000
154+++ lib/lp/code/interfaces/branch.py 2010-07-22 09:28:53 +0000
155@@ -57,12 +57,17 @@
156 from canonical.launchpad import _
157 from canonical.launchpad.fields import (
158 ParticipatingPersonChoice, PublicPersonChoice, URIField, Whiteboard)
159+from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
160 from canonical.launchpad.validators import LaunchpadValidationError
161+from canonical.launchpad.webapp.interfaces import (
162+ ITableBatchNavigator, NameLookupFailed)
163+from canonical.launchpad.webapp.menu import structured
164 from lp.code.bzr import BranchFormat, ControlFormat, RepositoryFormat
165 from lp.code.enums import (
166 BranchLifecycleStatus,
167 BranchMergeControlStatus,
168- BranchSubscriptionNotificationLevel, BranchSubscriptionDiffSize,
169+ BranchSubscriptionDiffSize,
170+ BranchSubscriptionNotificationLevel,
171 CodeReviewNotificationLevel,
172 UICreatableBranchType,
173 )
174@@ -71,14 +76,9 @@
175 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
176 from lp.code.interfaces.hasbranches import IHasMergeProposals
177 from lp.code.interfaces.hasrecipes import IHasRecipes
178-from canonical.launchpad.interfaces.launchpad import (
179- ILaunchpadCelebrities, IPrivacy)
180 from lp.registry.interfaces.role import IHasOwner
181 from lp.registry.interfaces.person import IPerson
182 from lp.registry.interfaces.pocket import PackagePublishingPocket
183-from canonical.launchpad.webapp.interfaces import (
184- ITableBatchNavigator, NameLookupFailed)
185-from canonical.launchpad.webapp.menu import structured
186
187
188 DEFAULT_BRANCH_STATUS_IN_LISTING = (
189@@ -317,50 +317,31 @@
190 """A marker interface to indicate the need to show the branch menu."""
191
192
193-class IBranch(IHasOwner, IPrivacy, IHasBranchTarget, IHasMergeProposals,
194- IHasRecipes):
195- """A Bazaar branch."""
196-
197- # Mark branches as exported entries for the Launchpad API.
198- export_as_webservice_entry(plural_name='branches')
199+class IBranchPublic(Interface):
200+ """Public attributes for a branch."""
201+
202+ date_last_modified = exported(
203+ Datetime(
204+ title=_('Date Last Modified'),
205+ required=True,
206+ readonly=False))
207+
208+
209+class IBranchAnyone(Interface):
210+ """Attributes of IBranch that can be changed by launchpad.AnyPerson."""
211+
212+ whiteboard = exported(
213+ Whiteboard(
214+ title=_('Whiteboard'), required=False,
215+ description=_('Notes on the current status of the branch.')))
216+
217+
218+class IBranchView(IHasOwner, IHasBranchTarget, IHasMergeProposals,
219+ IHasRecipes):
220+ """IBranch attributes that require launchpad.View permission."""
221
222 id = Int(title=_('ID'), readonly=True, required=True)
223
224- # XXX: TimPenhey 2007-08-31
225- # The vocabulary set for branch_type is only used for the creation
226- # of branches through the automatically generated forms, and doesn't
227- # actually represent the complete range of real values that branch_type
228- # may actually hold. Import branches are not created in the same
229- # way as Hosted, Mirrored or Remote branches.
230- # There are two option:
231- # 1) define a separate schema to use in the UI (sledgehammer solution)
232- # 2) work out some way to specify a restricted vocabulary in the view
233- # Personally I'd like a LAZR way to do number 2.
234- branch_type = exported(
235- Choice(
236- title=_("Branch Type"), required=True, readonly=True,
237- vocabulary=UICreatableBranchType))
238-
239- name = exported(
240- TextLine(
241- title=_('Name'), required=True, constraint=branch_name_validator,
242- description=_(
243- "Keep very short, unique, and descriptive, because it will "
244- "be used in URLs. "
245- "Examples: main, devel, release-1.0, gnome-vfs.")))
246-
247- url = exported(
248- BranchURIField(
249- title=_('Branch URL'), required=False,
250- allowed_schemes=['http', 'https', 'ftp', 'sftp', 'bzr+ssh'],
251- allow_userinfo=False,
252- allow_query=False,
253- allow_fragment=False,
254- trailing_slash=False,
255- description=_(
256- "This is the external location where the Bazaar "
257- "branch is hosted.")))
258-
259 @operation_parameters(
260 scheme=TextLine(title=_("URL scheme"), default=u'http'))
261 @export_read_operation()
262@@ -372,57 +353,6 @@
263 accepted).
264 """
265
266- description = exported(
267- Text(
268- title=_('Description'), required=False,
269- description=_(
270- 'A short description of the changes in this branch.')))
271-
272- branch_format = exported(
273- Choice(
274- title=_("Branch Format"),
275- required=False, readonly=True,
276- vocabulary=BranchFormat))
277-
278- repository_format = exported(
279- Choice(
280- title=_("Repository Format"),
281- required=False, readonly=True,
282- vocabulary=RepositoryFormat))
283-
284- control_format = exported(
285- Choice(
286- title=_("Control Directory"),
287- required=False, readonly=True,
288- vocabulary=ControlFormat))
289-
290- whiteboard = exported(
291- Whiteboard(
292- title=_('Whiteboard'), required=False,
293- description=_('Notes on the current status of the branch.')))
294-
295- mirror_status_message = exported(
296- Text(
297- title=_('The last message we got when mirroring this branch.'),
298- required=False, readonly=True))
299-
300- # This is redefined from IPrivacy.private because the attribute is
301- # read-only. The value is guarded by setPrivate().
302- private = exported(
303- Bool(
304- title=_("Keep branch confidential"), required=False,
305- readonly=True, default=False,
306- description=_(
307- "Make this branch visible only to its subscribers.")))
308-
309- @mutator_for(private)
310- @call_with(user=REQUEST_USER)
311- @operation_parameters(
312- private=Bool(title=_("Keep branch confidential")))
313- @export_write_operation()
314- def setPrivate(private, user):
315- """Set the branch privacy for this branch."""
316-
317 # People attributes
318 registrant = exported(
319 PublicPersonChoice(
320@@ -438,44 +368,6 @@
321 description=_("Either yourself or a team you are a member of. "
322 "This controls who can modify the branch.")))
323
324- @call_with(user=REQUEST_USER)
325- @operation_parameters(
326- new_owner=Reference(
327- title=_("The new owner of the branch."),
328- schema=IPerson))
329- @export_write_operation()
330- def setOwner(new_owner, user):
331- """Set the owner of the branch to be `new_owner`."""
332-
333- @call_with(user=REQUEST_USER)
334- @operation_parameters(
335- project=Reference(
336- title=_("The project the branch belongs to."),
337- schema=Interface, required=False), # Really IProduct
338- source_package=Reference(
339- title=_("The source package the branch belongs to."),
340- schema=Interface, required=False)) # Really ISourcePackage
341- @export_write_operation()
342- def setTarget(user, project=None, source_package=None):
343- """Set the target of the branch to be `project` or `source_package`.
344-
345- Only one of `project` or `source_package` can be set, and if neither
346- is set, the branch gets moved into the junk namespace of the branch
347- owner.
348-
349- :raise: `BranchTargetError` if both project and source_package are set,
350- or if either the project or source_package fail to be adapted to an
351- IBranchTarget.
352- """
353-
354- reviewer = exported(
355- PublicPersonChoice(
356- title=_('Review Team'),
357- required=False,
358- vocabulary='ValidPersonOrTeam',
359- description=_("The reviewer of a branch is the person or team "
360- "that is responsible for reviewing proposals and "
361- "merging into this branch.")))
362
363 # Distroseries and sourcepackagename are exported together as
364 # the sourcepackage.
365@@ -505,22 +397,6 @@
366 "None if not a package branch."),
367 schema=Interface, required=False, readonly=True))
368
369- code_reviewer = Attribute(
370- "The reviewer if set, otherwise the owner of the branch.")
371-
372- @operation_parameters(
373- reviewer=Reference(
374- title=_("A person for which the reviewer status is in question."),
375- schema=IPerson))
376- @export_read_operation()
377- def isPersonTrustedReviewer(reviewer):
378- """Return true if the `reviewer` is a trusted reviewer.
379-
380- The reviewer is trusted if they are either own the branch, or are in
381- the team that owns the branch, or they are in the review team for the
382- branch.
383- """
384-
385 namespace = Attribute(
386 "The namespace of this branch, as an `IBranchNamespace`.")
387
388@@ -549,29 +425,38 @@
389 "The branch unique_name.")),
390 exported_as='display_name')
391
392- # Stats and status attributes
393- lifecycle_status = exported(
394- Choice(
395- title=_('Status'), vocabulary=BranchLifecycleStatus,
396- default=BranchLifecycleStatus.DEVELOPMENT))
397-
398- # Mirroring attributes. For more information about how these all relate to
399- # each other, look at
400- # 'lib/canonical/launchpad/doc/puller-state-table.ods'.
401+ code_reviewer = Attribute(
402+ "The reviewer if set, otherwise the owner of the branch.")
403+
404+ @operation_parameters(
405+ reviewer=Reference(
406+ title=_("A person for which the reviewer status is in question."),
407+ schema=IPerson))
408+ @export_read_operation()
409+ def isPersonTrustedReviewer(reviewer):
410+ """Return true if the `reviewer` is a trusted reviewer.
411+
412+ The reviewer is trusted if they are either own the branch, or are in
413+ the team that owns the branch, or they are in the review team for the
414+ branch.
415+ """
416+
417 last_mirrored = exported(
418 Datetime(
419 title=_("Last time this branch was successfully mirrored."),
420 required=False, readonly=True))
421 last_mirrored_id = Text(
422- title=_("Last mirrored revision ID"), required=False,
423+ title=_("Last mirrored revision ID"), required=False, readonly=True,
424 description=_("The head revision ID of the branch when last "
425 "successfully mirrored."))
426 last_mirror_attempt = exported(
427 Datetime(
428 title=_("Last time a mirror of this branch was attempted."),
429 required=False, readonly=True))
430+
431 mirror_failures = Attribute(
432 "Number of failed mirror attempts since the last successful mirror.")
433+
434 next_mirror_time = Datetime(
435 title=_("If this value is more recent than the last mirror attempt, "
436 "then the branch will be mirrored on the next mirror run."),
437@@ -596,6 +481,9 @@
438
439 stacked_on = Attribute('Stacked-on branch')
440
441+ merge_queue = Attribute(
442+ "The queue that contains the QUEUED proposals for this branch.")
443+
444 # Bug attributes
445 bug_branches = CollectionField(
446 title=_("The bug-branch link objects that link this branch "
447@@ -663,9 +551,6 @@
448 :param user: IPerson unlinking the spec.
449 """
450
451- pending_writes = Attribute(
452- "Whether there is new Bazaar data for this branch.")
453-
454 # Joins
455 revision_history = Attribute(
456 """The sequence of BranchRevision for the mainline of that branch.
457@@ -693,30 +578,8 @@
458 required=True,
459 readonly=True))
460
461- date_last_modified = exported(
462- Datetime(
463- title=_('Date Last Modified'),
464- required=True,
465- readonly=False))
466-
467- @export_destructor_operation()
468- def destroySelfBreakReferences():
469- """Delete the specified branch.
470-
471- BranchRevisions associated with this branch will also be deleted as
472- well as any items with mandatory references.
473- """
474-
475- def destroySelf(break_references=False):
476- """Delete the specified branch.
477-
478- BranchRevisions associated with this branch will also be deleted.
479-
480- :param break_references: If supplied, break any references to this
481- branch by deleting items with mandatory references and
482- NULLing other references.
483- :raise: CannotDeleteBranch if the branch cannot be deleted.
484- """
485+ pending_writes = Attribute(
486+ "Whether there is new Bazaar data for this branch.")
487
488 def latest_revisions(quantity=10):
489 """A specific number of the latest revisions in that branch."""
490@@ -815,14 +678,6 @@
491 def getStackedBranches():
492 """The branches that are stacked on this one."""
493
494- merge_queue = Attribute(
495- "The queue that contains the QUEUED proposals for this branch.")
496-
497- merge_control_status = Choice(
498- title=_('Merge Control Status'), required=True,
499- vocabulary=BranchMergeControlStatus,
500- default=BranchMergeControlStatus.NO_QUEUE)
501-
502 def getMergeQueue():
503 """The proposals that are QUEUED to land on this branch."""
504
505@@ -1111,23 +966,6 @@
506 def startMirroring():
507 """Signal that this branch is being mirrored."""
508
509- def branchChanged(stacked_on_url, last_revision_id, control_format,
510- branch_format, repository_format):
511- """Record that a branch has been changed.
512-
513- This method records the stacked on branch tip revision id and format
514- or the branch and creates a scan job if the tip revision id has
515- changed.
516-
517- :param stacked_on_url: The unique name of the branch this branch is
518- stacked on, or '' if this branch is not stacked.
519- :param last_revision_id: The tip revision ID of the branch.
520- :param control_format: The entry from ControlFormat for the branch.
521- :param branch_format: The entry from BranchFormat for the branch.
522- :param repository_format: The entry from RepositoryFormat for the
523- branch.
524- """
525-
526 def mirrorFailed(reason):
527 """Signal that a mirror attempt failed.
528
529@@ -1148,11 +986,196 @@
530 upgrade_pending = Attribute(
531 "Whether a branch has had an upgrade requested.")
532
533+ def visibleByUser(user):
534+ """Can the specified user see this branch?"""
535+
536+
537+class IBranchEditableAttributes(Interface):
538+ """IBranch attributes that can be edited.
539+
540+ These attributes need launchpad.View to see, and launchpad.Edit to change.
541+ """
542+
543+ name = exported(
544+ TextLine(
545+ title=_('Name'), required=True, constraint=branch_name_validator,
546+ description=_(
547+ "Keep very short, unique, and descriptive, because it will "
548+ "be used in URLs. "
549+ "Examples: main, devel, release-1.0, gnome-vfs.")))
550+
551+ reviewer = exported(
552+ PublicPersonChoice(
553+ title=_('Review Team'),
554+ required=False,
555+ vocabulary='ValidPersonOrTeam',
556+ description=_("The reviewer of a branch is the person or team "
557+ "that is responsible for reviewing proposals and "
558+ "merging into this branch.")))
559+
560+ url = exported(
561+ BranchURIField(
562+ title=_('Branch URL'), required=False,
563+ allowed_schemes=['http', 'https', 'ftp', 'sftp', 'bzr+ssh'],
564+ allow_userinfo=False,
565+ allow_query=False,
566+ allow_fragment=False,
567+ trailing_slash=False,
568+ description=_(
569+ "This is the external location where the Bazaar "
570+ "branch is hosted.")))
571+
572+ mirror_status_message = exported(
573+ Text(
574+ title=_('The last message we got when mirroring this branch.'),
575+ required=False, readonly=True))
576+
577+ # XXX: TimPenhey 2007-08-31
578+ # The vocabulary set for branch_type is only used for the creation
579+ # of branches through the automatically generated forms, and doesn't
580+ # actually represent the complete range of real values that branch_type
581+ # may actually hold. Import branches are not created in the same
582+ # way as Hosted, Mirrored or Remote branches.
583+ # There are two option:
584+ # 1) define a separate schema to use in the UI (sledgehammer solution)
585+ # 2) work out some way to specify a restricted vocabulary in the view
586+ # Personally I'd like a LAZR way to do number 2.
587+ branch_type = exported(
588+ Choice(
589+ title=_("Branch Type"), required=True, readonly=True,
590+ vocabulary=UICreatableBranchType))
591+
592+ description = exported(
593+ Text(
594+ title=_('Description'), required=False,
595+ description=_(
596+ 'A short description of the changes in this branch.')))
597+
598+ lifecycle_status = exported(
599+ Choice(
600+ title=_('Status'), vocabulary=BranchLifecycleStatus,
601+ default=BranchLifecycleStatus.DEVELOPMENT))
602+
603+ branch_format = exported(
604+ Choice(
605+ title=_("Branch Format"),
606+ required=False, readonly=True,
607+ vocabulary=BranchFormat))
608+
609+ repository_format = exported(
610+ Choice(
611+ title=_("Repository Format"),
612+ required=False, readonly=True,
613+ vocabulary=RepositoryFormat))
614+
615+ control_format = exported(
616+ Choice(
617+ title=_("Control Directory"),
618+ required=False, readonly=True,
619+ vocabulary=ControlFormat))
620+
621+ merge_control_status = Choice(
622+ title=_('Merge Control Status'), required=True,
623+ vocabulary=BranchMergeControlStatus,
624+ default=BranchMergeControlStatus.NO_QUEUE)
625+
626+
627+class IBranchEdit(Interface):
628+ """IBranch attributes that require launchpad.Edit permission."""
629+
630+ @call_with(user=REQUEST_USER)
631+ @operation_parameters(
632+ new_owner=Reference(
633+ title=_("The new owner of the branch."),
634+ schema=IPerson))
635+ @export_write_operation()
636+ def setOwner(new_owner, user):
637+ """Set the owner of the branch to be `new_owner`."""
638+
639+ @call_with(user=REQUEST_USER)
640+ @operation_parameters(
641+ project=Reference(
642+ title=_("The project the branch belongs to."),
643+ schema=Interface, required=False), # Really IProduct
644+ source_package=Reference(
645+ title=_("The source package the branch belongs to."),
646+ schema=Interface, required=False)) # Really ISourcePackage
647+ @export_write_operation()
648+ def setTarget(user, project=None, source_package=None):
649+ """Set the target of the branch to be `project` or `source_package`.
650+
651+ Only one of `project` or `source_package` can be set, and if neither
652+ is set, the branch gets moved into the junk namespace of the branch
653+ owner.
654+
655+ :raise: `BranchTargetError` if both project and source_package are set,
656+ or if either the project or source_package fail to be adapted to an
657+ IBranchTarget.
658+ """
659+
660 def requestUpgrade():
661 """Create an IBranchUpgradeJob to upgrade this branch."""
662
663- def visibleByUser(user):
664- """Can the specified user see this branch?"""
665+ def branchChanged(stacked_on_url, last_revision_id, control_format,
666+ branch_format, repository_format):
667+ """Record that a branch has been changed.
668+
669+ This method records the stacked on branch tip revision id and format
670+ or the branch and creates a scan job if the tip revision id has
671+ changed.
672+
673+ :param stacked_on_url: The unique name of the branch this branch is
674+ stacked on, or '' if this branch is not stacked.
675+ :param last_revision_id: The tip revision ID of the branch.
676+ :param control_format: The entry from ControlFormat for the branch.
677+ :param branch_format: The entry from BranchFormat for the branch.
678+ :param repository_format: The entry from RepositoryFormat for the
679+ branch.
680+ """
681+
682+ @export_destructor_operation()
683+ def destroySelfBreakReferences():
684+ """Delete the specified branch.
685+
686+ BranchRevisions associated with this branch will also be deleted as
687+ well as any items with mandatory references.
688+ """
689+
690+ def destroySelf(break_references=False):
691+ """Delete the specified branch.
692+
693+ BranchRevisions associated with this branch will also be deleted.
694+
695+ :param break_references: If supplied, break any references to this
696+ branch by deleting items with mandatory references and
697+ NULLing other references.
698+ :raise: CannotDeleteBranch if the branch cannot be deleted.
699+ """
700+
701+
702+class IBranch(IBranchPublic, IBranchView, IBranchEdit,
703+ IBranchEditableAttributes, IBranchAnyone):
704+ """A Bazaar branch."""
705+
706+ # Mark branches as exported entries for the Launchpad API.
707+ export_as_webservice_entry(plural_name='branches')
708+
709+ # This is redefined from IPrivacy.private because the attribute is
710+ # read-only. The value is guarded by setPrivate().
711+ private = exported(
712+ Bool(
713+ title=_("Keep branch confidential"), required=False,
714+ readonly=True, default=False,
715+ description=_(
716+ "Make this branch visible only to its subscribers.")))
717+
718+ @mutator_for(private)
719+ @call_with(user=REQUEST_USER)
720+ @operation_parameters(
721+ private=Bool(title=_("Keep branch confidential")))
722+ @export_write_operation()
723+ def setPrivate(private, user):
724+ """Set the branch privacy for this branch."""
725
726
727 class IBranchSet(Interface):
728
729=== modified file 'lib/lp/code/model/branch.py'
730--- lib/lp/code/model/branch.py 2010-07-17 22:49:11 +0000
731+++ lib/lp/code/model/branch.py 2010-07-22 09:28:53 +0000
732@@ -40,7 +40,8 @@
733
734 from canonical.launchpad import _
735 from lp.services.job.model.job import Job
736-from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
737+from canonical.launchpad.interfaces.launchpad import (
738+ ILaunchpadCelebrities, IPrivacy)
739 from canonical.launchpad.webapp import urlappend
740 from canonical.launchpad.webapp.interfaces import (
741 IStoreSelector, MAIN_STORE, SLAVE_FLAVOR)
742@@ -88,7 +89,7 @@
743 class Branch(SQLBase, BzrIdentityMixin):
744 """A sequence of ordered revisions in Bazaar."""
745
746- implements(IBranch, IBranchNavigationMenu)
747+ implements(IBranch, IBranchNavigationMenu, IPrivacy)
748 _table = 'Branch'
749
750 branch_type = EnumCol(enum=BranchType, notNull=True)