[Launchpad-reviewers] [Merge] ~pappacena/launchpad:ocirecipe-subscription-ui into launchpad:master

2021-04-06 Thread noreply
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

2021-04-06 Thread Thiago F. Pappacena
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

2021-04-06 Thread Thiago F. Pappacena



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

2021-04-06 Thread Colin Watson
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

2021-03-16 Thread Thiago F. Pappacena
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,
+