launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19575
[Merge] lp:~cjwatson/launchpad/git-subscription-traversal into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-subscription-traversal into lp:launchpad.
Commit message:
Add GitRepository:+subscription traversal.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1503749 in Launchpad itself: "Can not remove subscribers from git repos"
https://bugs.launchpad.net/launchpad/+bug/1503749
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-subscription-traversal/+merge/273720
Add GitRepository:+subscription traversal. This was an oversight in https://code.launchpad.net/~cjwatson/launchpad/git-subscriptions-browser/+merge/256900, and fixing it makes it possible to unsubscribe a person from a repository using the web UI (the API already works fine for this).
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-subscription-traversal into lp:launchpad.
=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py 2015-09-24 09:58:26 +0000
+++ lib/lp/code/browser/gitrepository.py 2015-10-07 16:12:34 +0000
@@ -26,6 +26,7 @@
copy_field,
use_template,
)
+from zope.component import getUtility
from zope.event import notify
from zope.formlib import form
from zope.interface import (
@@ -64,7 +65,10 @@
from lp.code.interfaces.gitnamespace import get_git_namespace
from lp.code.interfaces.gitref import IGitRefBatchNavigator
from lp.code.interfaces.gitrepository import IGitRepository
-from lp.registry.interfaces.person import IPerson
+from lp.registry.interfaces.person import (
+ IPerson,
+ IPersonSet,
+ )
from lp.registry.vocabularies import UserTeamsParticipationPlusSelfVocabulary
from lp.services.config import config
from lp.services.database.constants import UTC_NOW
@@ -149,6 +153,14 @@
return ref
raise NotFoundError
+ @stepthrough("+subscription")
+ def traverse_subscription(self, name):
+ """Traverses to an `IGitSubcription`."""
+ person = getUtility(IPersonSet).getByName(name)
+
+ if person is not None:
+ return self.context.getSubscription(person)
+
@stepthrough("+merge")
def traverse_merge_proposal(self, id):
"""Traverse to an `IBranchMergeProposal`."""
=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py 2015-09-18 15:41:08 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py 2015-10-07 16:12:34 +0000
@@ -27,6 +27,7 @@
from lp.code.interfaces.githosting import IGitHostingClient
from lp.code.interfaces.revision import IRevisionSet
from lp.registry.enums import BranchSharingPolicy
+from lp.registry.interfaces.accesspolicy import IAccessPolicySource
from lp.registry.interfaces.person import PersonVisibility
from lp.services.database.constants import UTC_NOW
from lp.services.webapp.publisher import canonical_url
@@ -133,6 +134,7 @@
A repository may be associated with a private team as follows:
- the owner is a private team
+ - a subscriber is a private team
A logged in user who is not authorised to see the private team(s) still
needs to be able to view the repository. The private team will be
@@ -176,6 +178,85 @@
browser = self._getBrowser()
self.assertRaises(NotFound, browser.open, url)
+ def test_view_repository_with_private_subscriber(self):
+ # A repository with a private subscriber is rendered.
+ private_subscriber = self.factory.makeTeam(
+ name="privateteam", visibility=PersonVisibility.PRIVATE)
+ repository = self.factory.makeGitRepository()
+ with person_logged_in(repository.owner):
+ self.factory.makeGitSubscription(
+ repository, private_subscriber, repository.owner)
+ # Ensure the repository subscriber is rendered.
+ url = canonical_url(repository, rootsite='code')
+ user = self.factory.makePerson()
+ browser = self._getBrowser(user)
+ browser.open(url)
+ soup = BeautifulSoup(browser.contents)
+ self.assertIsNotNone(
+ soup.find('div', attrs={'id': 'subscriber-privateteam'}))
+
+ def test_anonymous_view_repository_with_private_subscriber(self):
+ # Private repository subscribers are not rendered for anon users.
+ private_subscriber = self.factory.makeTeam(
+ name="privateteam", visibility=PersonVisibility.PRIVATE)
+ repository = self.factory.makeGitRepository()
+ with person_logged_in(private_subscriber):
+ self.factory.makeGitSubscription(
+ repository, private_subscriber, repository.owner)
+ # Viewing the repository doesn't show the private subscriber.
+ url = canonical_url(repository, rootsite='code')
+ browser = self._getBrowser()
+ browser.open(url)
+ soup = BeautifulSoup(browser.contents)
+ self.assertIsNone(
+ soup.find('div', attrs={'id': 'subscriber-privateteam'}))
+
+ def test_unsubscribe_private_repository(self):
+ # Unsubscribing from a repository with a policy grant still allows
+ # the repository to be seen.
+ project = self.factory.makeProduct()
+ owner = self.factory.makePerson()
+ subscriber = self.factory.makePerson()
+ [ap] = getUtility(IAccessPolicySource).find(
+ [(project, InformationType.USERDATA)])
+ self.factory.makeAccessPolicyGrant(
+ policy=ap, grantee=subscriber, grantor=project.owner)
+ repository = self.factory.makeGitRepository(
+ target=project, owner=owner, name=u"repo",
+ information_type=InformationType.USERDATA)
+ with person_logged_in(owner):
+ self.factory.makeGitSubscription(repository, subscriber, owner)
+ base_url = canonical_url(repository, rootsite='code')
+ expected_title = '%s : Git : Code : %s' % (
+ repository.identity, project.displayname)
+ url = '%s/+subscription/%s' % (base_url, subscriber.name)
+ browser = self._getBrowser(user=subscriber)
+ browser.open(url)
+ browser.getControl('Unsubscribe').click()
+ self.assertEqual(base_url, browser.url)
+ self.assertEqual(expected_title, browser.title)
+
+ def test_unsubscribe_private_repository_no_access(self):
+ # Unsubscribing from a repository with no access will redirect to
+ # the context of the repository.
+ project = self.factory.makeProduct()
+ owner = self.factory.makePerson()
+ subscriber = self.factory.makePerson()
+ repository = self.factory.makeGitRepository(
+ target=project, owner=owner,
+ information_type=InformationType.USERDATA)
+ with person_logged_in(owner):
+ self.factory.makeGitSubscription(repository, subscriber, owner)
+ base_url = canonical_url(repository, rootsite='code')
+ project_url = canonical_url(project, rootsite='code')
+ url = '%s/+subscription/%s' % (base_url, subscriber.name)
+ expected_title = "Code : %s" % project.displayname
+ browser = self._getBrowser(user=subscriber)
+ browser.open(url)
+ browser.getControl('Unsubscribe').click()
+ self.assertEqual(project_url, browser.url)
+ self.assertEqual(expected_title, browser.title)
+
class TestGitRepositoryBranches(BrowserTestCase):
"""Test the listing of branches in a Git repository."""
Follow ups