launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18333
Re: [Merge] lp:~cjwatson/launchpad/git-subscriptions-browser into lp:launchpad
Review: Approve code
The permissions are a bit dodgy, but probably not fatal.
Diff comments:
> === modified file 'lib/lp/code/browser/configure.zcml'
> --- lib/lp/code/browser/configure.zcml 2015-04-19 12:56:32 +0000
> +++ lib/lp/code/browser/configure.zcml 2015-04-21 10:09:18 +0000
> @@ -768,6 +768,35 @@
> name="++repository-management"
> template="../templates/gitrepository-management.pt"/>
> </browser:pages>
> + <browser:page
> + for="lp.code.interfaces.gitrepository.IGitRepository"
> + permission="zope.Public"
> + name="+portlet-subscribers"
> + template="../templates/gitrepository-portlet-subscribers.pt"/>
> + <browser:page
> + for="lp.code.interfaces.gitrepository.IGitRepository"
> + class="lp.code.browser.gitsubscription.GitRepositoryPortletSubscribersContent"
> + permission="zope.Public"
> + name="+repository-portlet-subscriber-content"
> + template="../templates/gitrepository-portlet-subscribers-content.pt"/>
> + <browser:page
> + for="lp.code.interfaces.gitrepository.IGitRepository"
> + class="lp.code.browser.gitsubscription.GitSubscriptionAddView"
> + permission="launchpad.AnyPerson"
> + name="+subscribe"
> + template="../../app/templates/generic-edit.pt"/>
> + <browser:page
> + for="lp.code.interfaces.gitrepository.IGitRepository"
> + class="lp.code.browser.gitsubscription.GitSubscriptionAddOtherView"
> + permission="launchpad.AnyPerson"
> + name="+addsubscriber"
> + template="../../app/templates/generic-edit.pt"/>
> + <browser:page
> + for="lp.code.interfaces.gitrepository.IGitRepository"
> + class="lp.code.browser.gitsubscription.GitSubscriptionEditOwnView"
> + permission="launchpad.AnyPerson"
> + name="+edit-subscription"
> + template="../templates/gitrepository-edit-subscription.pt"/>
All of these should really require launchpad.View, but launchpad.AnyPerson makes it a bit awkward.
> <adapter
> provides="lp.services.webapp.interfaces.IBreadcrumb"
> for="lp.code.interfaces.gitrepository.IGitRepository"
> @@ -804,6 +833,21 @@
> name="+ref-listing"
> template="../templates/gitref-listing.pt"/>
>
> + <browser:defaultView
> + for="lp.code.interfaces.gitsubscription.IGitSubscription"
> + name="+index"/>
> + <browser:page
> + for="lp.code.interfaces.gitsubscription.IGitSubscription"
> + class="lp.code.browser.gitsubscription.GitSubscriptionEditView"
> + permission="launchpad.Edit"
> + name="+index"
> + template="../templates/gitsubscription-edit.pt"/>
> + <browser:url
> + for="lp.code.interfaces.gitsubscription.IGitSubscription"
> + path_expression="string:+subscription/${person/name}"
> + attribute_to_parent="repository"
> + rootsite="code"/>
> +
> <browser:menus
> classes="ProductBranchesMenu"
> module="lp.code.browser.branchlisting"/>
>
> === modified file 'lib/lp/code/browser/gitrepository.py'
> --- lib/lp/code/browser/gitrepository.py 2015-03-24 13:10:52 +0000
> +++ lib/lp/code/browser/gitrepository.py 2015-04-21 10:09:18 +0000
> @@ -23,6 +23,7 @@
> from lp.services.config import config
> from lp.services.webapp import (
> ContextMenu,
> + enabled_with_permission,
> LaunchpadView,
> Link,
> Navigation,
> @@ -83,7 +84,24 @@
>
> usedfor = IGitRepository
> facet = "branches"
> - links = ["source"]
> + links = ["add_subscriber", "source", "subscription"]
> +
> + @enabled_with_permission("launchpad.AnyPerson")
> + def subscription(self):
> + if self.context.hasSubscription(self.user):
> + url = "+edit-subscription"
> + text = "Edit your subscription"
> + icon = "edit"
> + else:
> + url = "+subscribe"
> + text = "Subscribe yourself"
> + icon = "add"
> + return Link(url, text, icon=icon)
> +
> + @enabled_with_permission("launchpad.AnyPerson")
> + def add_subscriber(self):
> + text = "Subscribe someone else"
> + return Link("+addsubscriber", text, icon="add")
>
> def source(self):
> """Return a link to the branch's browsing interface."""
>
> === added file 'lib/lp/code/browser/gitsubscription.py'
> --- lib/lp/code/browser/gitsubscription.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/browser/gitsubscription.py 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,293 @@
> +# Copyright 2015 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +__metaclass__ = type
> +
> +__all__ = [
> + 'GitRepositoryPortletSubscribersContent',
> + 'GitSubscriptionAddOtherView',
> + 'GitSubscriptionAddView',
> + 'GitSubscriptionEditOwnView',
> + 'GitSubscriptionEditView',
> + ]
> +
> +from zope.component import getUtility
> +
> +from lp.app.browser.launchpadform import (
> + action,
> + LaunchpadEditFormView,
> + LaunchpadFormView,
> + )
> +from lp.app.interfaces.services import IService
> +from lp.code.enums import BranchSubscriptionNotificationLevel
> +from lp.code.interfaces.gitsubscription import IGitSubscription
> +from lp.registry.interfaces.person import IPersonSet
> +from lp.services.webapp import (
> + canonical_url,
> + LaunchpadView,
> + )
> +from lp.services.webapp.authorization import (
> + check_permission,
> + precache_permission_for_objects,
> + )
> +from lp.services.webapp.escaping import structured
> +
> +
> +class GitRepositoryPortletSubscribersContent(LaunchpadView):
> + """View for the contents for the subscribers portlet."""
> +
> + def subscriptions(self):
> + """Return a decorated list of Git repository subscriptions."""
> +
> + # Cache permissions so private subscribers can be rendered.
> + # The security adaptor will do the job also but we don't want or
> + # need the expense of running several complex SQL queries.
> + person_ids = [sub.person_id for sub in self.context.subscriptions]
> + list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
> + person_ids, need_validity=True))
> + if self.user is not None:
> + subscribers = [
> + subscription.person
> + for subscription in self.context.subscriptions]
> + precache_permission_for_objects(
> + self.request, "launchpad.LimitedView", subscribers)
> +
> + visible_subscriptions = [
> + subscription for subscription in self.context.subscriptions
> + if check_permission("launchpad.LimitedView", subscription.person)]
> + return sorted(
> + visible_subscriptions,
> + key=lambda subscription: subscription.person.displayname)
> +
> +
> +class _GitSubscriptionView(LaunchpadFormView):
> + """Contains the common functionality of the Add and Edit views."""
> +
> + schema = IGitSubscription
> + field_names = ['notification_level', 'max_diff_lines', 'review_level']
> +
> + LEVELS_REQUIRING_LINES_SPECIFICATION = (
> + BranchSubscriptionNotificationLevel.DIFFSONLY,
> + BranchSubscriptionNotificationLevel.FULL)
> +
> + @property
> + def user_is_subscribed(self):
> + # Since it is technically possible to get to this page when
> + # the user is not subscribed by hacking the URL, we should
> + # handle the case nicely.
> + return self.context.getSubscription(self.user) is not None
> +
> + @property
> + def next_url(self):
> + return canonical_url(self.context)
> +
> + cancel_url = next_url
> +
> + def add_notification_message(self, initial, notification_level,
> + max_diff_lines, review_level):
> + if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
> + lines_message = "<li>%s</li>" % max_diff_lines.description
> + else:
> + lines_message = ""
> +
> + format_str = "%%s<ul><li>%%s</li>%s<li>%%s</li></ul>" % lines_message
> + message = structured(
> + format_str, initial, notification_level.description,
> + review_level.description)
> + self.request.response.addNotification(message)
> +
> + def optional_max_diff_lines(self, notification_level, max_diff_lines):
> + if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
> + return max_diff_lines
> + else:
> + return None
> +
> +
> +class GitSubscriptionAddView(_GitSubscriptionView):
> +
> + subscribing_self = True
> +
> + page_title = label = "Subscribe to repository"
> +
> + @action("Subscribe")
> + def subscribe(self, action, data):
> + # To catch the stale post problem, check that the user is not
> + # subscribed before continuing.
> + if self.context.hasSubscription(self.user):
> + self.request.response.addNotification(
> + "You are already subscribed to this repository.")
> + else:
> + notification_level = data["notification_level"]
> + max_diff_lines = self.optional_max_diff_lines(
> + notification_level, data["max_diff_lines"])
> + review_level = data["review_level"]
> +
> + self.context.subscribe(
> + self.user, notification_level, max_diff_lines, review_level,
> + self.user)
> +
> + self.add_notification_message(
> + "You have subscribed to this repository with: ",
> + notification_level, max_diff_lines, review_level)
> +
> +
> +class GitSubscriptionEditOwnView(_GitSubscriptionView):
> +
> + @property
> + def label(self):
> + return "Edit subscription to repository"
> +
> + @property
> + def page_title(self):
> + return "Edit subscription to repository %s" % self.context.displayname
> +
> + @property
> + def initial_values(self):
> + subscription = self.context.getSubscription(self.user)
> + if subscription is None:
> + # This is the case of URL hacking or stale page.
> + return {}
> + else:
> + return {"notification_level": subscription.notification_level,
> + "max_diff_lines": subscription.max_diff_lines,
> + "review_level": subscription.review_level}
> +
> + @action("Change")
> + def change_details(self, action, data):
> + # Be proactive in the checking to catch the stale post problem.
> + if self.context.hasSubscription(self.user):
> + subscription = self.context.getSubscription(self.user)
> + subscription.notification_level = data["notification_level"]
> + subscription.max_diff_lines = self.optional_max_diff_lines(
> + subscription.notification_level,
> + data["max_diff_lines"])
> + subscription.review_level = data["review_level"]
> +
> + self.add_notification_message(
> + "Subscription updated to: ",
> + subscription.notification_level,
> + subscription.max_diff_lines,
> + subscription.review_level)
> + else:
> + self.request.response.addNotification(
> + "You are not subscribed to this repository.")
> +
> + @action("Unsubscribe")
> + def unsubscribe(self, action, data):
> + # Be proactive in the checking to catch the stale post problem.
> + if self.context.hasSubscription(self.user):
> + self.context.unsubscribe(self.user, self.user)
> + self.request.response.addNotification(
> + "You have unsubscribed from this repository.")
> + else:
> + self.request.response.addNotification(
> + "You are not subscribed to this repository.")
> +
> +
> +class GitSubscriptionAddOtherView(_GitSubscriptionView):
> + """View used to subscribe someone other than the current user."""
> +
> + field_names = [
> + "person", "notification_level", "max_diff_lines", "review_level"]
> + for_input = True
> +
> + # Since we are subscribing other people, the current user
> + # is never considered subscribed.
> + user_is_subscribed = False
> + subscribing_self = False
> +
> + page_title = label = "Subscribe to repository"
> +
> + def validate(self, data):
> + if "person" in data:
> + person = data["person"]
> + subscription = self.context.getSubscription(person)
> + if subscription is None and not self.context.userCanBeSubscribed(
> + person):
> + self.setFieldError(
> + "person",
> + "Open and delegated teams cannot be subscribed to "
> + "private repositories.")
> +
> + @action("Subscribe", name="subscribe_action")
> + def subscribe_action(self, action, data):
> + """Subscribe the specified user to the repository.
> +
> + The user must be a member of a team in order to subscribe that team
> + to the repository. Launchpad Admins are special and they can
> + subscribe any team.
> + """
> + notification_level = data["notification_level"]
> + max_diff_lines = self.optional_max_diff_lines(
> + notification_level, data["max_diff_lines"])
> + review_level = data["review_level"]
> + person = data["person"]
> + subscription = self.context.getSubscription(person)
> + if subscription is None:
> + self.context.subscribe(
> + person, notification_level, max_diff_lines, review_level,
> + self.user)
> + self.add_notification_message(
> + "%s has been subscribed to this repository with: "
> + % person.displayname, notification_level, max_diff_lines,
> + review_level)
> + else:
> + self.add_notification_message(
> + "%s was already subscribed to this repository with: "
> + % person.displayname,
> + subscription.notification_level, subscription.max_diff_lines,
> + review_level)
> +
> +
> +class GitSubscriptionEditView(LaunchpadEditFormView):
> + """The view for editing repository subscriptions.
> +
> + Used when traversed to the repository subscription itself rather than
> + through the repository action item to edit the user's own subscription.
> + This is the only current way to edit a team repository subscription.
> + """
> + schema = IGitSubscription
> + field_names = ["notification_level", "max_diff_lines", "review_level"]
> +
> + @property
> + def page_title(self):
> + return (
> + "Edit subscription to repository %s" % self.repository.displayname)
> +
> + @property
> + def label(self):
> + return (
> + "Edit subscription to repository for %s" % self.person.displayname)
> +
> + def initialize(self):
> + self.repository = self.context.repository
> + self.person = self.context.person
> + super(GitSubscriptionEditView, self).initialize()
> +
> + @action("Change", name="change")
> + def change_action(self, action, data):
> + """Update the repository subscription."""
> + self.updateContextFromData(data)
> +
> + @action("Unsubscribe", name="unsubscribe")
> + def unsubscribe_action(self, action, data):
> + """Unsubscribe the team from the repository."""
> + self.repository.unsubscribe(self.person, self.user)
> + self.request.response.addNotification(
> + "%s has been unsubscribed from this repository."
> + % self.person.displayname)
> +
> + @property
> + def next_url(self):
> + url = canonical_url(self.repository)
> + # If the subscriber can no longer see the repository, redirect them
> + # away.
> + service = getUtility(IService, "sharing")
> + _, _, repositories, _ = service.getVisibleArtifacts(
> + self.person, gitrepositories=[self.repository],
> + ignore_permissions=True)
> + if not repositories:
> + url = canonical_url(self.repository.target)
> + return url
> +
> + cancel_url = next_url
>
> === added file 'lib/lp/code/browser/tests/test_gitsubscription.py'
> --- lib/lp/code/browser/tests/test_gitsubscription.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/browser/tests/test_gitsubscription.py 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,58 @@
> +# Copyright 2015 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""Unit tests for GitSubscriptions."""
> +
> +__metaclass__ = type
> +
> +from lp.app.enums import InformationType
> +from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
> +from lp.services.features.testing import FeatureFixture
> +from lp.testing import (
> + person_logged_in,
> + TestCaseWithFactory,
> + )
> +from lp.testing.layers import DatabaseFunctionalLayer
> +from lp.testing.views import create_initialized_view
> +
> +
> +class TestGitSubscriptionAddOtherView(TestCaseWithFactory):
> +
> + layer = DatabaseFunctionalLayer
> +
> + def setUp(self):
> + super(TestGitSubscriptionAddOtherView, self).setUp()
> + self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
> +
> + def test_cannot_subscribe_open_team_to_private_repository(self):
> + owner = self.factory.makePerson()
> + repository = self.factory.makeGitRepository(
> + information_type=InformationType.USERDATA, owner=owner)
> + team = self.factory.makeTeam()
> + form = {
> + 'field.person': team.name,
> + 'field.notification_level': 'NOEMAIL',
> + 'field.max_diff_lines': 'NODIFF',
> + 'field.review_level': 'NOEMAIL',
> + 'field.actions.subscribe_action': 'Subscribe'}
> + with person_logged_in(owner):
> + view = create_initialized_view(
> + repository, '+addsubscriber', pricipal=owner, form=form)
> + self.assertContentEqual(
> + ['Open and delegated teams cannot be subscribed to private '
> + 'repositories.'], view.errors)
> +
> + def test_can_subscribe_open_team_to_public_repository(self):
> + owner = self.factory.makePerson()
> + repository = self.factory.makeGitRepository(owner=owner)
> + team = self.factory.makeTeam()
> + form = {
> + 'field.person': team.name,
> + 'field.notification_level': 'NOEMAIL',
> + 'field.max_diff_lines': 'NODIFF',
> + 'field.review_level': 'NOEMAIL',
> + 'field.actions.subscribe_action': 'Subscribe'}
> + with person_logged_in(owner):
> + view = create_initialized_view(
> + repository, '+addsubscriber', pricipal=owner, form=form)
> + self.assertContentEqual([], view.errors)
>
> === modified file 'lib/lp/code/interfaces/gitsubscription.py'
> --- lib/lp/code/interfaces/gitsubscription.py 2015-04-15 18:34:25 +0000
> +++ lib/lp/code/interfaces/gitsubscription.py 2015-04-21 10:09:18 +0000
> @@ -43,7 +43,7 @@
> export_as_webservice_entry(as_of="beta")
>
> id = Int(title=_("ID"), readonly=True, required=True)
> - personID = Int(title=_("Person ID"), required=True, readonly=True)
> + person_id = Int(title=_("Person ID"), required=True, readonly=True)
> person = exported(
> PersonChoice(
> title=_("Person"), required=True, vocabulary="ValidPersonOrTeam",
>
> === modified file 'lib/lp/code/model/gitrepository.py'
> --- lib/lp/code/model/gitrepository.py 2015-04-17 00:01:15 +0000
> +++ lib/lp/code/model/gitrepository.py 2015-04-21 10:09:18 +0000
> @@ -139,8 +139,9 @@
> This method is registered as a subscriber to `IObjectModifiedEvent`
> events on Git repositories.
> """
> - repository.date_last_modified = UTC_NOW
> - send_git_repository_modified_notifications(repository, event)
> + if event.edited_fields:
> + repository.date_last_modified = UTC_NOW
> + send_git_repository_modified_notifications(repository, event)
>
>
> class GitRepository(StormBase, GitIdentityMixin):
>
> === modified file 'lib/lp/code/model/tests/test_gitrepository.py'
> --- lib/lp/code/model/tests/test_gitrepository.py 2015-04-17 00:01:15 +0000
> +++ lib/lp/code/model/tests/test_gitrepository.py 2015-04-21 10:09:18 +0000
> @@ -13,6 +13,7 @@
> from lazr.lifecycle.event import ObjectModifiedEvent
> import pytz
> from testtools.matchers import (
> + EndsWith,
> MatchesSetwise,
> MatchesStructure,
> )
> @@ -26,7 +27,12 @@
> PUBLIC_INFORMATION_TYPES,
> )
> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
> -from lp.code.enums import GitObjectType
> +from lp.code.enums import (
> + BranchSubscriptionDiffSize,
> + BranchSubscriptionNotificationLevel,
> + CodeReviewNotificationLevel,
> + GitObjectType,
> + )
> from lp.code.errors import (
> GitFeatureDisabled,
> GitRepositoryCreatorNotMemberOfOwnerTeam,
> @@ -1423,3 +1429,107 @@
> self.assertEqual(401, response.status)
> with person_logged_in(ANONYMOUS):
> self.assertEqual(owner_db, repository_db.owner)
> +
> + def test_subscribe(self):
> + # A user can subscribe to a repository.
> + repository_db = self.factory.makeGitRepository()
> + subscriber_db = self.factory.makePerson()
> + webservice = webservice_for_person(
> + subscriber_db, permission=OAuthPermission.WRITE_PUBLIC)
> + webservice.default_api_version = "devel"
> + with person_logged_in(ANONYMOUS):
> + repository_url = api_url(repository_db)
> + subscriber_url = api_url(subscriber_db)
> + response = webservice.named_post(
> + repository_url, "subscribe", person=subscriber_url,
> + notification_level=u"Branch attribute notifications only",
> + max_diff_lines=u"Don't send diffs", code_review_level=u"No email")
> + self.assertEqual(200, response.status)
> + with person_logged_in(ANONYMOUS):
> + subscription_db = repository_db.getSubscription(subscriber_db)
> + self.assertIsNotNone(subscription_db)
> + self.assertThat(
> + response.jsonBody()["self_link"],
> + EndsWith(api_url(subscription_db)))
> +
> + def _makeSubscription(self, repository, subscriber):
> + with person_logged_in(subscriber):
> + return repository.subscribe(
> + person=subscriber,
> + notification_level=(
> + BranchSubscriptionNotificationLevel.ATTRIBUTEONLY),
> + max_diff_lines=BranchSubscriptionDiffSize.NODIFF,
> + code_review_level=CodeReviewNotificationLevel.NOEMAIL,
> + subscribed_by=subscriber)
> +
> + def test_getSubscription(self):
> + # It is possible to get a single subscription via the webservice.
> + repository_db = self.factory.makeGitRepository()
> + subscriber_db = self.factory.makePerson()
> + subscription_db = self._makeSubscription(repository_db, subscriber_db)
> + with person_logged_in(subscriber_db):
> + repository_url = api_url(repository_db)
> + subscriber_url = api_url(subscriber_db)
> + subscription_url = api_url(subscription_db)
> + webservice = webservice_for_person(
> + subscriber_db, permission=OAuthPermission.WRITE_PUBLIC)
> + webservice.default_api_version = "devel"
> + response = webservice.named_get(
> + repository_url, "getSubscription", person=subscriber_url)
> + self.assertEqual(200, response.status)
> + self.assertThat(
> + response.jsonBody()["self_link"], EndsWith(subscription_url))
> +
> + def test_edit_subscription(self):
> + # An existing subscription can be edited via the webservice, by
> + # subscribing the same person again with different details.
> + repository_db = self.factory.makeGitRepository()
> + subscriber_db = self.factory.makePerson()
> + self._makeSubscription(repository_db, subscriber_db)
> + with person_logged_in(subscriber_db):
> + repository_url = api_url(repository_db)
> + subscriber_url = api_url(subscriber_db)
> + webservice = webservice_for_person(
> + subscriber_db, permission=OAuthPermission.WRITE_PUBLIC)
> + webservice.default_api_version = "devel"
> + response = webservice.named_post(
> + repository_url, "subscribe", person=subscriber_url,
> + notification_level=u"No email",
> + max_diff_lines=u"Send entire diff",
> + code_review_level=u"Status changes only")
> + self.assertEqual(200, response.status)
> + with person_logged_in(subscriber_db):
> + self.assertThat(
> + repository_db.getSubscription(subscriber_db),
> + MatchesStructure.byEquality(
> + person=subscriber_db,
> + notification_level=(
> + BranchSubscriptionNotificationLevel.NOEMAIL),
> + max_diff_lines=BranchSubscriptionDiffSize.WHOLEDIFF,
> + review_level=CodeReviewNotificationLevel.STATUS,
> + ))
> + repository = webservice.get(repository_url).jsonBody()
> + subscribers = webservice.get(
> + repository["subscribers_collection_link"]).jsonBody()
> + self.assertEqual(2, len(subscribers["entries"]))
> + with person_logged_in(subscriber_db):
> + self.assertContentEqual(
> + [repository_db.owner.name, subscriber_db.name],
> + [subscriber["name"] for subscriber in subscribers["entries"]])
> +
> + def test_unsubscribe(self):
> + # It is possible to unsubscribe via the webservice.
> + repository_db = self.factory.makeGitRepository()
> + subscriber_db = self.factory.makePerson()
> + self._makeSubscription(repository_db, subscriber_db)
> + with person_logged_in(subscriber_db):
> + repository_url = api_url(repository_db)
> + subscriber_url = api_url(subscriber_db)
> + webservice = webservice_for_person(
> + subscriber_db, permission=OAuthPermission.WRITE_PUBLIC)
> + webservice.default_api_version = "devel"
> + response = webservice.named_post(
> + repository_url, "unsubscribe", person=subscriber_url)
> + self.assertEqual(200, response.status)
> + with person_logged_in(subscriber_db):
> + self.assertNotIn(subscriber_db, repository_db.subscribers)
>
> === added file 'lib/lp/code/templates/gitrepository-edit-subscription.pt'
> --- lib/lp/code/templates/gitrepository-edit-subscription.pt 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/templates/gitrepository-edit-subscription.pt 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,45 @@
> +<html
> + xmlns="http://www.w3.org/1999/xhtml"
> + xmlns:tal="http://xml.zope.org/namespaces/tal"
> + xmlns:metal="http://xml.zope.org/namespaces/metal"
> + xmlns:i18n="http://xml.zope.org/namespaces/i18n"
> + metal:use-macro="view/macro:page/main_only"
> + i18n:domain="launchpad">
> +
> + <body>
> +
> +<div metal:fill-slot="main">
> +
> +<tal:subscribed condition="view/user_is_subscribed">
> +
> + <div metal:use-macro="context/@@launchpad_form/form">
> + <metal:extra fill-slot="extra_info">
> + <p class="documentDescription">
> + If you unsubscribe from a repository it will no longer show up on
> + your personal pages.
> + </p>
> + </metal:extra>
> + </div>
> +
> +</tal:subscribed>
> +
> +<tal:not_subscribed condition="not: view/user_is_subscribed">
> +
> + <tal:comment condition="nothing">
> + This occurs if the user is hacking the URLs,
> + and should never be linked to from a valid page.
> +
> + It could occur from a stale page as well if the user
> + is using a tabbed browser and hasn't refreshed the page.
> + </tal:comment>
> +
> + <p class="documentDescription">
> + You are not currently subscribed to this repository.
> + </p>
> +
> +</tal:not_subscribed>
> +
> +</div>
> +
> +</body>
> +</html>
>
> === modified file 'lib/lp/code/templates/gitrepository-index.pt'
> --- lib/lp/code/templates/gitrepository-index.pt 2015-03-20 14:54:23 +0000
> +++ lib/lp/code/templates/gitrepository-index.pt 2015-04-21 10:09:18 +0000
> @@ -19,6 +19,7 @@
>
> <metal:side fill-slot="side">
> <div tal:replace="structure context/@@+global-actions" />
> + <tal:subscribers replace="structure context/@@+portlet-subscribers" />
> </metal:side>
>
> <tal:registering metal:fill-slot="registering">
>
> === added file 'lib/lp/code/templates/gitrepository-portlet-subscribers-content.pt'
> --- lib/lp/code/templates/gitrepository-portlet-subscribers-content.pt 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/templates/gitrepository-portlet-subscribers-content.pt 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,31 @@
> +<div
> + tal:omit-tag=""
> + xmlns:tal="http://xml.zope.org/namespaces/tal"
> + xmlns:metal="http://xml.zope.org/namespaces/metal"
> + xmlns:i18n="http://xml.zope.org/namespaces/i18n">
> + <div class="section repository-subscribers">
> + <div
> + tal:condition="view/subscriptions"
> + tal:repeat="subscription view/subscriptions"
> + tal:attributes="id string:subscriber-${subscription/person/name}">
> + <a tal:condition="subscription/person/name|nothing"
> + tal:attributes="href subscription/person/fmt:url">
> +
> + <tal:block replace="structure subscription/person/fmt:icon" />
> + <tal:block replace="subscription/person/fmt:displayname/fmt:shorten/20" />
> + </a>
> +
> + <a tal:condition="subscription/required:launchpad.Edit"
> + tal:attributes="
> + href subscription/fmt:url;
> + title string:Edit subscription ${subscription/person/fmt:displayname};
> + id string:editsubscription-${subscription/person/name}">
> + <img class="editsub-icon" src="/@@/edit"
> + tal:attributes="id string:editsubscription-icon-${subscription/person/name}" />
> + </a>
> + </div>
> + <div id="none-subscribers" tal:condition="not:view/subscriptions">
> + No subscribers.
> + </div>
> + </div>
> +</div>
>
> === added file 'lib/lp/code/templates/gitrepository-portlet-subscribers.pt'
> --- lib/lp/code/templates/gitrepository-portlet-subscribers.pt 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/templates/gitrepository-portlet-subscribers.pt 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,29 @@
> +<div
> + xmlns:tal="http://xml.zope.org/namespaces/tal"
> + xmlns:metal="http://xml.zope.org/namespaces/metal"
> + xmlns:i18n="http://xml.zope.org/namespaces/i18n"
> + class="portlet" id="portlet-subscribers">
> + <div tal:define="context_menu view/context/menu:context">
> + <div>
> + <div class="section">
> + <div
> + tal:define="link context_menu/subscription"
> + tal:condition="link/enabled"
> + id="selfsubscriptioncontainer">
> + <a class="sprite add subscribe-self"
> + tal:attributes="href link/url"
> + tal:content="link/text" />
> + </div>
> + <div
> + tal:define="link context_menu/add_subscriber"
> + tal:condition="link/enabled"
> + tal:content="structure link/render" />
> + </div>
> + </div>
> +
> + <h2>Subscribers</h2>
> + <div id="repository-subscribers-outer">
> + <div tal:replace="structure context/@@+repository-portlet-subscriber-content" />
> + </div>
> + </div>
> +</div>
>
> === added file 'lib/lp/code/templates/gitsubscription-edit.pt'
> --- lib/lp/code/templates/gitsubscription-edit.pt 1970-01-01 00:00:00 +0000
> +++ lib/lp/code/templates/gitsubscription-edit.pt 2015-04-21 10:09:18 +0000
> @@ -0,0 +1,25 @@
> +<html
> + xmlns="http://www.w3.org/1999/xhtml"
> + xmlns:tal="http://xml.zope.org/namespaces/tal"
> + xmlns:metal="http://xml.zope.org/namespaces/metal"
> + xmlns:i18n="http://xml.zope.org/namespaces/i18n"
> + metal:use-macro="view/macro:page/main_only"
> + i18n:domain="launchpad"
> +>
> + <body>
> +
> +<div metal:fill-slot="main">
> +
> + <div metal:use-macro="context/@@launchpad_form/form" >
> + <metal:extra fill-slot="extra_info">
> + <p class="documentDescription">
> + If you unsubscribe from a repository it will no longer show up on
> + your personal pages.
> + </p>
> + </metal:extra>
> + </div>
> +
> +</div>
> +
> +</body>
> +</html>
>
--
https://code.launchpad.net/~cjwatson/launchpad/git-subscriptions-browser/+merge/256900
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
References