[Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master
The proposal to merge ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master has been updated. Status: Approved => Merged For more details, see: https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/399750 -- Your team Launchpad code reviewers is subscribed to branch ~pappacena/launchpad:ocirecipe-subscription. ___ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp
[Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master
The proposal to merge ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master has been updated. Status: Needs review => Approved For more details, see: https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/399750 -- Your team Launchpad code reviewers is subscribed to branch ~pappacena/launchpad:ocirecipe-subscription. ___ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp
Re: [Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master
Diff comments: > diff --git a/lib/lp/oci/browser/ocirecipesubscription.py > b/lib/lp/oci/browser/ocirecipesubscription.py > new file mode 100644 > index 000..2b83696 > --- /dev/null > +++ b/lib/lp/oci/browser/ocirecipesubscription.py > @@ -0,0 +1,176 @@ > +# Copyright 2020-2021 Canonical Ltd. This software is licensed under the > +# GNU Affero General Public License version 3 (see the file LICENSE). > + > +"""OCI recipe subscription views.""" > + > +from __future__ import absolute_import, print_function, unicode_literals > + > +__metaclass__ = type > +__all__ = [ > +'OCIRecipePortletSubscribersContent' > +] > + > +from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription Oops... > +from zope.component import getUtility > +from zope.formlib.form import action > +from zope.security.interfaces import ForbiddenAttribute > + > +from lp.app.browser.launchpadform import ( > +LaunchpadEditFormView, > +LaunchpadFormView, > +) > +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, > +) > + > + > +class OCIRecipePortletSubscribersContent(LaunchpadView): > +"""View for the contents for the subscribers portlet.""" > + > +def subscriptions(self): > +"""Return a decorated list of OCI recipe 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. > +subscriptions = list(self.context.subscriptions) > +person_ids = [sub.person.id for sub in subscriptions] > +list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( > +person_ids, need_validity=True)) > +if self.user is not None: > +subscribers = [ > +subscription.person for subscription in subscriptions] > +precache_permission_for_objects( > +self.request, "launchpad.LimitedView", subscribers) > + > +visible_subscriptions = [ > +subscription for subscription in subscriptions > +if check_permission("launchpad.LimitedView", > subscription.person)] > +return sorted( > +visible_subscriptions, > +key=lambda subscription: subscription.person.displayname) > + > + > +class RedirectToOCIRecipeMixin: > +@property > +def next_url(self): > +if self.ocirecipe.visibleByUser(self.user): > +return canonical_url(self.ocirecipe) > +# If the subscriber can no longer see the OCI recipe, tries to > +# redirect to the pillar page. > +try: > +pillar = self.ocirecipe.pillar > +if pillar is not None and pillar.userCanLimitedView(self.user): > +return canonical_url(pillar) > +except ForbiddenAttribute: > +pass > +# If not possible, redirect user back to its own page. > +return canonical_url(self.user) > + > +cancel_url = next_url > + > + > +class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin, > +LaunchpadEditFormView): > +"""The view for editing OCI recipe subscriptions.""" > +schema = IOCIRecipeSubscription > +field_names = [] > + > +@property > +def page_title(self): > +return ( > +"Edit subscription to OCI recipe %s" % > +self.ocirecipe.displayname) > + > +@property > +def label(self): > +return ( > +"Edit subscription to OCI recipe for %s" % > +self.person.displayname) > + > +def initialize(self): > +self.ocirecipe = self.context.recipe > +self.person = self.context.person > +super(OCIRecipeSubscriptionEditView, self).initialize() > + > +@action("Unsubscribe", name="unsubscribe") > +def unsubscribe_action(self, action, data): > +"""Unsubscribe the team from the OCI recipe.""" > +self.ocirecipe.unsubscribe(self.person, self.user) > +self.request.response.addNotification( > +"%s has been unsubscribed from this OCI recipe." > +% self.person.displayname) > + > + > +class _OCIRecipeSubscriptionCreationView(RedirectToOCIRecipeMixin, > + LaunchpadFormView): > +"""Contains the common functionality of the Add and Edit views.""" > + > +schema = IOCIRecipeSubscription > +field_names = [] > + > +def initialize(self): > +self.ocirecipe = self.context > +super(_OCIRecipeSubscriptionCreationView, self).initialize() > + > + > +class OCIRecipeSubscriptionAddView(_OCIRecipeSubscriptionCreationView): > + > +page_title = label = "Subscribe to OCI recipe" > + > +@action("Subscribe") > +
Re: [Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master
Review: Approve Diff comments: > diff --git a/lib/lp/oci/browser/ocirecipesubscription.py > b/lib/lp/oci/browser/ocirecipesubscription.py > new file mode 100644 > index 000..2b83696 > --- /dev/null > +++ b/lib/lp/oci/browser/ocirecipesubscription.py > @@ -0,0 +1,176 @@ > +# Copyright 2020-2021 Canonical Ltd. This software is licensed under the > +# GNU Affero General Public License version 3 (see the file LICENSE). > + > +"""OCI recipe subscription views.""" > + > +from __future__ import absolute_import, print_function, unicode_literals > + > +__metaclass__ = type > +__all__ = [ > +'OCIRecipePortletSubscribersContent' > +] > + > +from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription format-imports? > +from zope.component import getUtility > +from zope.formlib.form import action > +from zope.security.interfaces import ForbiddenAttribute > + > +from lp.app.browser.launchpadform import ( > +LaunchpadEditFormView, > +LaunchpadFormView, > +) > +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, > +) > + > + > +class OCIRecipePortletSubscribersContent(LaunchpadView): > +"""View for the contents for the subscribers portlet.""" > + > +def subscriptions(self): > +"""Return a decorated list of OCI recipe 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. > +subscriptions = list(self.context.subscriptions) > +person_ids = [sub.person.id for sub in subscriptions] > +list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( > +person_ids, need_validity=True)) > +if self.user is not None: > +subscribers = [ > +subscription.person for subscription in subscriptions] > +precache_permission_for_objects( > +self.request, "launchpad.LimitedView", subscribers) > + > +visible_subscriptions = [ > +subscription for subscription in subscriptions > +if check_permission("launchpad.LimitedView", > subscription.person)] > +return sorted( > +visible_subscriptions, > +key=lambda subscription: subscription.person.displayname) > + > + > +class RedirectToOCIRecipeMixin: > +@property > +def next_url(self): > +if self.ocirecipe.visibleByUser(self.user): > +return canonical_url(self.ocirecipe) > +# If the subscriber can no longer see the OCI recipe, tries to > +# redirect to the pillar page. > +try: > +pillar = self.ocirecipe.pillar > +if pillar is not None and pillar.userCanLimitedView(self.user): > +return canonical_url(pillar) > +except ForbiddenAttribute: > +pass > +# If not possible, redirect user back to its own page. > +return canonical_url(self.user) > + > +cancel_url = next_url > + > + > +class OCIRecipeSubscriptionEditView(RedirectToOCIRecipeMixin, > +LaunchpadEditFormView): > +"""The view for editing OCI recipe subscriptions.""" > +schema = IOCIRecipeSubscription > +field_names = [] > + > +@property > +def page_title(self): > +return ( > +"Edit subscription to OCI recipe %s" % > +self.ocirecipe.displayname) > + > +@property > +def label(self): > +return ( > +"Edit subscription to OCI recipe for %s" % > +self.person.displayname) > + > +def initialize(self): > +self.ocirecipe = self.context.recipe > +self.person = self.context.person > +super(OCIRecipeSubscriptionEditView, self).initialize() > + > +@action("Unsubscribe", name="unsubscribe") > +def unsubscribe_action(self, action, data): > +"""Unsubscribe the team from the OCI recipe.""" > +self.ocirecipe.unsubscribe(self.person, self.user) > +self.request.response.addNotification( > +"%s has been unsubscribed from this OCI recipe." > +% self.person.displayname) > + > + > +class _OCIRecipeSubscriptionCreationView(RedirectToOCIRecipeMixin, > + LaunchpadFormView): > +"""Contains the common functionality of the Add and Edit views.""" > + > +schema = IOCIRecipeSubscription > +field_names = [] > + > +def initialize(self): > +self.ocirecipe = self.context > +super(_OCIRecipeSubscriptionCreationView, self).initialize() > + > + > +class OCIRecipeSubscriptionAddView(_OCIRecipeSubscriptionCreationView): > + > +page_title = label = "Subscribe to OCI recipe" > + > +
[Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master with ~pappacena/launchpad:ocirecipe-subscription as a prerequisite. Commit message: OCI recipe subscription UI flow Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/399750 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master. diff --git a/lib/lp/oci/browser/configure.zcml b/lib/lp/oci/browser/configure.zcml index 6c254fb..53db0d2 100644 --- a/lib/lp/oci/browser/configure.zcml +++ b/lib/lp/oci/browser/configure.zcml @@ -1,4 +1,4 @@ - @@ -129,5 +129,44 @@ for="lp.oci.interfaces.ocipushrule.IOCIPushRule" path_expression="string:+push-rule/${id}" attribute_to_parent="recipe" /> + + + + + + + + + diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py index 3b79cec..801b005 100644 --- a/lib/lp/oci/browser/ocirecipe.py +++ b/lib/lp/oci/browser/ocirecipe.py @@ -42,6 +42,7 @@ from zope.schema import ( TextLine, ValidationError, ) +from zope.security.interfaces import Unauthorized from lp.app.browser.launchpadform import ( action, @@ -76,6 +77,7 @@ from lp.oci.interfaces.ociregistrycredentials import ( OCIRegistryCredentialsAlreadyExist, user_can_edit_credentials_for_owner, ) +from lp.registry.interfaces.person import IPersonSet from lp.services.features import getFeatureFlag from lp.services.propertycache import cachedproperty from lp.services.webapp import ( @@ -121,6 +123,13 @@ class OCIRecipeNavigation(WebhookTargetNavigationMixin, Navigation): id = int(id) return getUtility(IOCIPushRuleSet).getByID(id) +@stepthrough("+subscription") +def traverse_subscription(self, name): +"""Traverses to an `IOCIRecipeSubscription`.""" +person = getUtility(IPersonSet).getByName(name) +if person is not None: +return self.context.getSubscription(person) + class OCIRecipeBreadcrumb(NameBreadcrumb): @@ -164,7 +173,8 @@ class OCIRecipeContextMenu(ContextMenu): facet = 'overview' -links = ('request_builds', 'edit_push_rules') +links = ('request_builds', 'edit_push_rules', + 'add_subscriber', 'subscription') @enabled_with_permission('launchpad.Edit') def request_builds(self): @@ -175,6 +185,23 @@ class OCIRecipeContextMenu(ContextMenu): return Link( '+edit-push-rules', 'Edit push rules', icon='edit') +@enabled_with_permission("launchpad.AnyPerson") +def subscription(self): +if self.context.getSubscription(self.user) is not None: +url = "+subscription/%s" % self.user.name +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") + class OCIProjectRecipesView(LaunchpadView): """Default view for the list of OCI recipes of an OCI project.""" @@ -233,6 +260,13 @@ class OCIRecipeView(LaunchpadView): return len(self.push_rules) > 0 @property +def user_can_see_source(self): +try: +return self.context.git_ref.repository.visibleByUser(self.user) +except Unauthorized: +return False + +@property def person_picker(self): field = copy_field( IOCIRecipe["owner"], diff --git a/lib/lp/oci/browser/ocirecipesubscription.py b/lib/lp/oci/browser/ocirecipesubscription.py new file mode 100644 index 000..2b83696 --- /dev/null +++ b/lib/lp/oci/browser/ocirecipesubscription.py @@ -0,0 +1,176 @@ +# Copyright 2020-2021 Canonical Ltd. This software is licensed under the +# GNU Affero General Public License version 3 (see the file LICENSE). + +"""OCI recipe subscription views.""" + +from __future__ import absolute_import, print_function, unicode_literals + +__metaclass__ = type +__all__ = [ +'OCIRecipePortletSubscribersContent' +] + +from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription +from zope.component import getUtility +from zope.formlib.form import action +from zope.security.interfaces import ForbiddenAttribute + +from lp.app.browser.launchpadform import ( +LaunchpadEditFormView, +LaunchpadFormView, +) +from lp.registry.interfaces.person import IPersonSet +from lp.services.webapp import ( +canonical_url, +LaunchpadView, +) +from lp.services.webapp.authorization import ( +check_permission, +