Merge lp:~michael.nelson/launchpad/409187-trivial-ui-fixes-for-p3a-access into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/409187-trivial-ui-fixes-for-p3a-access
Merge into: lp:launchpad
Diff against target: 932 lines
19 files modified
lib/canonical/launchpad/icing/style-3-0.css (+3/-0)
lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js (+54/-0)
lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html (+46/-0)
lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js (+147/-0)
lib/canonical/launchpad/pagetitles.py (+0/-8)
lib/lp/soyuz/browser/archivesubscription.py (+28/-3)
lib/lp/soyuz/browser/distroarchseriesbinarypackage.py (+0/-9)
lib/lp/soyuz/browser/tests/archivesubscription-views.txt (+23/-7)
lib/lp/soyuz/browser/tests/test_breadcrumbs.py (+34/-0)
lib/lp/soyuz/configure.zcml (+6/-1)
lib/lp/soyuz/interfaces/archivesubscriber.py (+3/-0)
lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt (+3/-3)
lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt (+1/-0)
lib/lp/soyuz/templates/archive-subscriber-edit.pt (+0/-6)
lib/lp/soyuz/templates/archive-subscribers.pt (+19/-13)
lib/lp/soyuz/templates/person-archive-subscription.pt (+9/-14)
lib/lp/soyuz/templates/person-archive-subscriptions.pt (+4/-8)
lib/lp/soyuz/windmill/testing.py (+18/-0)
lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py (+96/-0)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/409187-trivial-ui-fixes-for-p3a-access
Reviewer Review Type Date Requested Status
Graham Binns (community) js Approve
Barry Warsaw (community) ui* Approve
Guilherme Salgado (community) Approve
Review via email: mp+12035@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

= Summary =

This branch deals with a number of UI issues related to private PPA
subscriptions. Please refer tot bug 409187 for the details.

== Proposed fix ==

== Pre-implementation notes ==

All before-after screenshots and notes are on bug 409187, as well as a
screencast of the new interaction.

== Implementation details ==

I have not added a windmill test for the new interaction yet, but will
do soon. If I need to do it now I will, but otherwise I'll focus on
blueprint templates for the rest of today.

== Tests ==

bin/test -vv -t archivesubscription-views.txt -t
TestArchiveSubscriptionBreadcrumb -t stories/ppa

== Demo and Q/A ==

For screenshots/casts, see bug 409187.

To demo locally, run the following in a harness:

ppa = getUtility(IPersonSet).getByName('cprov').archive
ppa.private = True
ppa.buildd_secret = 'blah'
import transaction;transaction.commit()

and then navigate to:
https://launchpad.dev/~cprov/+archive/ppa

Click on 'Manage access', add yourself (Celso) and others. Try editing.

Next go to:
https://launchpad.dev/~cprov and click on 'View your private ppa
subscriptions'. Click on the subscription listed there etc.

For QA, the soyuz-team ppa can be used.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/templates/person-archive-subscriptions.pt
  lib/lp/soyuz/templates/archive-subscribers.pt
  lib/canonical/launchpad/pagetitles.py
  lib/lp/soyuz/templates/person-archive-subscription.pt
  lib/lp/soyuz/browser/tests/test_breadcrumbs.py
  lib/lp/soyuz/browser/tests/archivesubscription-views.txt
  lib/lp/soyuz/interfaces/archivesubscriber.py
  lib/lp/soyuz/configure.zcml
  lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt
  lib/canonical/launchpad/icing/style-3-0.css
  lib/lp/soyuz/browser/archivesubscription.py
  lib/lp/soyuz/browser/configure.zcml
  lib/lp/soyuz/templates/archive-subscriber-edit.pt

== Pylint notices ==

lib/lp/soyuz/interfaces/archivesubscriber.py
    20: [F0401] Unable to import 'lazr.enum' (No module named enum)
    26: [F0401] Unable to import 'lazr.restful.declarations' (No module
named restful)
    27: [F0401] Unable to import 'lazr.restful.fields' (No module named
restful)

--
Michael

Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (9.0 KiB)

Hi Michael,

Your changes look good. I have a few suggestions and I'd like somebody
else to review the JS changes, as I don't feel comfortable doing so.

 review approve

> === modified file 'lib/lp/soyuz/browser/archivesubscription.py'
> --- lib/lp/soyuz/browser/archivesubscription.py 2009-08-12 08:40:41 +0000
> +++ lib/lp/soyuz/browser/archivesubscription.py 2009-09-14 08:08:51 +0000
> @@ -9,6 +9,8 @@
>
> __all__ = [
> 'ArchiveSubscribersView',
> + 'PersonalArchiveSubscriptionBreadcrumb',
> + 'PersonArchiveSubscriptionView',
> 'PersonArchiveSubscriptionsView',
> 'traverse_archive_subscription_for_subscriber'
> ]
> @@ -34,10 +36,11 @@
> IArchiveAuthTokenSet)
> from lp.soyuz.interfaces.archivesubscriber import (
> IArchiveSubscriberSet, IPersonalArchiveSubscription)
> +from canonical.launchpad.webapp.breadcrumb import Breadcrumb
> from canonical.launchpad.webapp.launchpadform import (
> action, custom_widget, LaunchpadFormView, LaunchpadEditFormView)
> from canonical.launchpad.webapp.publisher import (
> - canonical_url, LaunchpadView)
> + canonical_url, LaunchpadView, Navigation)
> from canonical.widgets import DateWidget
> from canonical.widgets.popup import PersonPickerWidget
>
> @@ -65,6 +68,20 @@
> """See `IPersonalArchiveSubscription`."""
> return "Access to %s" % self.archive.displayname
>
> + @property
> + def title(self):

I know of page_title and label, which are used for this purpose, did you mean
one of them or am I not aware of this third one?

> + """Required for default headings in templates."""
> + return self.displayname
> +
> +
> +class PersonalArchiveSubscriptionNavigation(Navigation):
> + """Navigation for `IPersonalArchiveSubscription`.
> +
> + Without this, a breadcrumb will not be created for personal
> + archive subscription objects.

This is no longer needed, actually -- a fix for the bug (423898) is included
in my breadcrumbs branch that is on PQM now.

> + """
> + usedfor = IPersonalArchiveSubscription
> +
> def traverse_archive_subscription_for_subscriber(subscriber, archive_id):
> """Return the subscription for a subscriber to an archive."""
> subscription = None
> @@ -79,6 +96,14 @@
> return PersonalArchiveSubscription(subscriber, archive)
>
>
> +class PersonalArchiveSubscriptionBreadcrumb(Breadcrumb):
> + """Builds a breadcrumb for `PersonalArchiveSubscription`."""
> +
> + @property
> + def text(self):
> + return self.context.displayname

You can use webapp.breadcrumbs.DisplaynameBreadcrumb in the zcml and get rid
of this adapter altogether. It'd be great if you could do a quick check for
other places where that generic adapter could replace other specific ones. :)

> +
> +
> class IArchiveSubscriberUI(Interface):
> """A custom interface for user interaction with archive subscriptions.
>
> === modified file 'lib/lp/soyuz/browser/tests/test_breadcrumbs.py'
> --- lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-02 16:32:06 +0000
> +++ lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-11 10:52:54 +0000
> @@ -11,6 +11,8 @@
> from canonical.launc...

Read more...

review: Approve
Revision history for this message
Barry Warsaw (barry) wrote :

Looks very good, and the movie really helped! The only thing that's weird for me is leaving the date blank to mean never expire. There's no need to hold up the branch for this, so I'll just ramble a bit.

If possible, the date picker maybe should have a "Never expire" button. You'd use this both in the initial date choice but also if you then decide to extend a user's access to "forever". Also, if access never expires, it would be nice if the date field actually said that, instead of being blank.

With these changes, you'd be able to get rid of the helpful text explaining what a blank field means.

If you think these are worthwhile changes to make, file a bug and fix it later. But the ui as it is looks otherwise great.

review: Approve (ui*)
Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (10.2 KiB)

Guilherme Salgado wrote:
> Review: Approve
> Hi Michael,
>
> Your changes look good. I have a few suggestions and I'd like somebody
> else to review the JS changes, as I don't feel comfortable doing so.
>

Great, thanks Salgado.

> review approve
>
>> === modified file 'lib/lp/soyuz/browser/archivesubscription.py'
>> --- lib/lp/soyuz/browser/archivesubscription.py 2009-08-12 08:40:41 +0000
>> +++ lib/lp/soyuz/browser/archivesubscription.py 2009-09-14 08:08:51 +0000
>> @@ -9,6 +9,8 @@
>>
>> __all__ = [
>> 'ArchiveSubscribersView',
>> + 'PersonalArchiveSubscriptionBreadcrumb',
>> + 'PersonArchiveSubscriptionView',
>> 'PersonArchiveSubscriptionsView',
>> 'traverse_archive_subscription_for_subscriber'
>> ]
>> @@ -34,10 +36,11 @@
>> IArchiveAuthTokenSet)
>> from lp.soyuz.interfaces.archivesubscriber import (
>> IArchiveSubscriberSet, IPersonalArchiveSubscription)
>> +from canonical.launchpad.webapp.breadcrumb import Breadcrumb
>> from canonical.launchpad.webapp.launchpadform import (
>> action, custom_widget, LaunchpadFormView, LaunchpadEditFormView)
>> from canonical.launchpad.webapp.publisher import (
>> - canonical_url, LaunchpadView)
>> + canonical_url, LaunchpadView, Navigation)
>> from canonical.widgets import DateWidget
>> from canonical.widgets.popup import PersonPickerWidget
>>
>> @@ -65,6 +68,20 @@
>> """See `IPersonalArchiveSubscription`."""
>> return "Access to %s" % self.archive.displayname
>>
>> + @property
>> + def title(self):
>
> I know of page_title and label, which are used for this purpose, did you mean
> one of them or am I not aware of this third one?

No, the helper class to which this belongs (PersonalArchiveSubscription)
is used as the context for these views, and so like normal content
classes, it needs a title attribute for when the base template tries to
access context.title.

>
>> + """Required for default headings in templates."""
>> + return self.displayname
>> +
>> +
>> +class PersonalArchiveSubscriptionNavigation(Navigation):
>> + """Navigation for `IPersonalArchiveSubscription`.
>> +
>> + Without this, a breadcrumb will not be created for personal
>> + archive subscription objects.
>
> This is no longer needed, actually -- a fix for the bug (423898) is included
> in my breadcrumbs branch that is on PQM now.

Great, removed.

>
>> + """
>> + usedfor = IPersonalArchiveSubscription
>> +
>> def traverse_archive_subscription_for_subscriber(subscriber, archive_id):
>> """Return the subscription for a subscriber to an archive."""
>> subscription = None
>> @@ -79,6 +96,14 @@
>> return PersonalArchiveSubscription(subscriber, archive)
>>
>>
>> +class PersonalArchiveSubscriptionBreadcrumb(Breadcrumb):
>> + """Builds a breadcrumb for `PersonalArchiveSubscription`."""
>> +
>> + @property
>> + def text(self):
>> + return self.context.displayname
>
> You can use webapp.breadcrumbs.DisplaynameBreadcrumb in the zcml and get rid
> of this adapter altogether. It'd be great if you could do a quick check for
> other places where that generic adapter could replace other speci...

=== added file 'lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js'
--- lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js 2009-09-29 07:26:40 +0000
@@ -0,0 +1,54 @@
1/* Copyright 2009 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 *
4 * Enhancements for adding ppa subscribers.
5 *
6 * @module ArchiveSubscribersIndex
7 * @requires event, node, oop
8 */
9YUI.add('soyuz.archivesubscribers_index', function(Y) {
10
11var soyuz = Y.namespace('soyuz');
12
13/*
14 * Setup the style and click handler for the add subscriber link.
15 *
16 * @method setup_archivesubscribers_index
17 */
18Y.soyuz.setup_archivesubscribers_index = function() {
19 // If there are no errors then we hide the add-subscriber row and
20 // potentially the whole table if there are no subscribers.
21 if (Y.Lang.isNull(Y.get('p.error.message'))) {
22
23 // Hide the add-subscriber row.
24 var add_subscriber_row = Y.get(
25 '#archive-subscribers .add-subscriber');
26 add_subscriber_row.setStyle('display', 'none');
27
28 // If there are no subscribers, then hide the complete section.
29 var subscribers = Y.get('#subscribers');
30 if (Y.Lang.isObject(Y.get('#no-subscribers'))) {
31 subscribers.setStyle('display', 'none');
32 }
33 }
34
35 // Add a link to open the add-subscriber row.
36 var placeholder = Y.get('#add-subscriber-placeholder');
37 placeholder.set(
38 'innerHTML',
39 '<a class="js-action sprite add" href="#">Add access</a>');
40
41 // Unfortunately we can't use the lazr slider, as it uses display:block
42 // which breaks table rows (they use display:table-row).
43 function show_add_subscriber(e) {
44 e.preventDefault();
45 subscribers.setStyle('display', 'block');
46 add_subscriber_row.setStyle('display', 'table-row');
47 }
48
49 Y.on('click', show_add_subscriber,
50 '#add-subscriber-placeholder a');
51};
52
53}, '0.1', {requires: ['oop', 'node', 'event']});
54
055
=== added file 'lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html'
--- lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html 2009-09-29 09:28:33 +0000
@@ -0,0 +1,46 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2<html>
3 <head>
4 <title>Launchpad ArchiveSubscriberIndex</title>
5
6 <!-- YUI 3.0 Setup -->
7 <script type="text/javascript" src="../../../icing/yui/current/build/yui/yui.js"></script>
8 <link rel="stylesheet" href="../../../icing/yui/current/build/cssreset/reset.css"/>
9 <link rel="stylesheet" href="../../../icing/yui/current/build/cssfonts/fonts.css"/>
10 <link rel="stylesheet" href="../../../icing/yui/current/build/cssbase/base.css"/>
11 <link rel="stylesheet" href="../../test.css" />
12
13 <!-- The module under test -->
14 <script type="text/javascript" src="../archivesubscribers_index.js"></script>
15
16 <!-- The test suite -->
17 <script type="text/javascript" src="archivesubscribers_index.js"></script>
18</head>
19<body class="yui-skin-sam">
20 <h1>Testing the ArchiveSubscribersIndex javascript</h1>
21
22 <h2>Errors</h2>
23 <div id="errors">
24 </div>
25
26 <h2>Add subscriber place-holder</h2>
27 <div id="add-subscriber-placeholder">
28 </div>
29
30 <h2>Subscribers</h2>
31 <div id="subscribers">
32 <table id="archive-subscribers">
33 <thead>
34 <tr>
35 <th>Name</th>
36 <th>Expires</th>
37 <th colspan="2">Comment</th>
38 </tr>
39 </thead>
40 <tbody>
41 </tbody>
42 </table>
43 </div>
44 <div id="log"></div>
45</body>
46</html>
047
=== added file 'lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js'
--- lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js 2009-09-29 09:28:33 +0000
@@ -0,0 +1,134 @@
1/* Copyright 2009 Canonical Ltd. This software is licensed under the
2 GNU Affero General Public License version 3 (see the file LICENSE). */
3
4YUI({
5 base: '../../../icing/yui/current/build/',
6 filter: 'raw',
7 combine: false
8 }).use(
9 'yuitest', 'console', 'soyuz.archivesubscribers_index', function(Y) {
10
11var Assert = Y.Assert; // For easy access to isTrue(), etc.
12
13var suite = new Y.Test.Suite("ArchiveSubscriber Tests");
14
15suite.add(new Y.Test.Case({
16
17 name: 'add-subscriber',
18
19 setUp: function() {
20 this.add_subscriber_placeholder = Y.get(
21 '#add-subscriber-placeholder');
22 this.archive_subscribers_table_body = Y.get(
23 '#archive-subscribers').query('tbody');
24 this.error_div = Y.get('#errors');
25 this.subscribers_div = Y.get('#subscribers');
26
27
28 // Ensure there are no errors displayed.
29 this.error_div.set('innerHTML', '');
30
31 // Ensure the add subscriber place-holder is empty.
32 this.add_subscriber_placeholder.set('innerHTML', '');
33
34 // Ensure the table has the correct structure.
35 this.archive_subscribers_table_body.set(
36 'innerHTML', [
37 '<tr class="add-subscriber">',
38 '<td>New 1</td>',
39 '<td>New 2</td>',
40 '<td>New 3</td>',
41 '<td>Add</td>',
42 '</tr>',
43 '<tr>',
44 '<td>Existing 1</td>',
45 '<td>Existing 2</td>',
46 '<td>Existing 3</td>',
47 '<td>Edit</td>',
48 '</tr>'
49 ].join(''));
50
51 this.add_subscriber_row = Y.get(
52 '#archive-subscribers .add-subscriber');
53 },
54
55 test_add_row_displayed_by_default: function() {
56 Assert.areEqual(
57 'table-row', this.add_subscriber_row.getStyle('display'),
58 'The add subscriber row degrades to display without js.');
59 },
60
61 test_subscribers_displayed_by_default: function() {
62 Assert.areEqual(
63 'block', this.subscribers_div.getStyle('display'),
64 'The subscribers section is displayed by default without js.');
65 },
66
67 test_add_row_hidden_after_setup: function() {
68 Y.soyuz.setup_archivesubscribers_index();
69 Assert.areEqual(
70 'none', this.add_subscriber_row.getStyle('display'),
71 'The add subscriber row is hidden during setup.');
72 },
73
74 test_subscribers_section_displayed_after_setup: function() {
75 Y.soyuz.setup_archivesubscribers_index();
76 Assert.areEqual(
77 'block', this.subscribers_div.getStyle('display'),
78 'The subscribers div normally remains displayed after setup.');
79 },
80
81 test_subscribers_section_hidden_when_no_subscribers: function() {
82 // Add a paragraph with the no-subscribers id.
83 this.error_div.set('innerHTML', '<p id="no-subscribers">blah</p>');
84 Y.soyuz.setup_archivesubscribers_index();
85 Assert.areEqual(
86 'none', this.subscribers_div.getStyle('display'),
87 'The subscribers div is hidden when no subscribers yet.');
88 },
89
90 test_add_row_displayed_when_errors_present: function() {
91 // Add an error paragraph.
92 this.error_div.set('innerHTML', '<p class="error message">Blah</p>');
93 Y.soyuz.setup_archivesubscribers_index();
94 Assert.areEqual(
95 'table-row', this.add_subscriber_row.getStyle('display'),
96 'The add subscriber row is displayed if there are errors ' +
97 'present.');
98 },
99
100 test_add_access_link_added_after_setup: function() {
101 Y.soyuz.setup_archivesubscribers_index();
102 Assert.areEqual(
103 '<a class="js-action sprite add" href="#">Add access</a>',
104 this.add_subscriber_placeholder.get('innerHTML'),
105 "The 'Add access' link is created during setup.")
106 },
107
108 test_click_add_access_displays_add_row: function() {
109 Y.soyuz.setup_archivesubscribers_index();
110 var link_node = this.add_subscriber_placeholder.query('a');
111 Assert.areEqual(
112 'Add access', link_node.get('innerHTML'));
113
114 Y.Event.simulate(Y.Node.getDOMNode(link_node), 'click');
115
116 Assert.areEqual(
117 'table-row', this.add_subscriber_row.getStyle('display'),
118 "The add subscriber row is displayed after clicking " +
119 "'Add access'");
120 }
121}));
122
123Y.Test.Runner.add(suite);
124
125var yconsole = new Y.Console({
126 newestOnTop: false
127});
128yconsole.render('#log');
129
130Y.on('domready', function() {
131 Y.Test.Runner.run();
132});
133
134});
0135
=== modified file 'lib/lp/soyuz/browser/archivesubscription.py'
--- lib/lp/soyuz/browser/archivesubscription.py 2009-09-14 08:08:51 +0000
+++ lib/lp/soyuz/browser/archivesubscription.py 2009-09-29 07:21:40 +0000
@@ -9,7 +9,6 @@
99
10__all__ = [10__all__ = [
11 'ArchiveSubscribersView',11 'ArchiveSubscribersView',
12 'PersonalArchiveSubscriptionBreadcrumb',
13 'PersonArchiveSubscriptionView',12 'PersonArchiveSubscriptionView',
14 'PersonArchiveSubscriptionsView',13 'PersonArchiveSubscriptionsView',
15 'traverse_archive_subscription_for_subscriber'14 'traverse_archive_subscription_for_subscriber'
@@ -74,14 +73,6 @@
74 return self.displayname73 return self.displayname
7574
7675
77class PersonalArchiveSubscriptionNavigation(Navigation):
78 """Navigation for `IPersonalArchiveSubscription`.
79
80 Without this, a breadcrumb will not be created for personal
81 archive subscription objects.
82 """
83 usedfor = IPersonalArchiveSubscription
84
85def traverse_archive_subscription_for_subscriber(subscriber, archive_id):76def traverse_archive_subscription_for_subscriber(subscriber, archive_id):
86 """Return the subscription for a subscriber to an archive."""77 """Return the subscription for a subscriber to an archive."""
87 subscription = None78 subscription = None
@@ -96,14 +87,6 @@
96 return PersonalArchiveSubscription(subscriber, archive)87 return PersonalArchiveSubscription(subscriber, archive)
9788
9889
99class PersonalArchiveSubscriptionBreadcrumb(Breadcrumb):
100 """Builds a breadcrumb for `PersonalArchiveSubscription`."""
101
102 @property
103 def text(self):
104 return self.context.displayname
105
106
107class IArchiveSubscriberUI(Interface):90class IArchiveSubscriberUI(Interface):
108 """A custom interface for user interaction with archive subscriptions.91 """A custom interface for user interaction with archive subscriptions.
10992
11093
=== modified file 'lib/lp/soyuz/browser/configure.zcml'
--- lib/lp/soyuz/browser/configure.zcml 2009-09-28 13:56:17 +0000
+++ lib/lp/soyuz/browser/configure.zcml 2009-09-29 07:21:40 +0000
@@ -520,9 +520,6 @@
520 <browser:defaultView520 <browser:defaultView
521 for="lp.soyuz.interfaces.archivesubscriber.IPersonalArchiveSubscription"521 for="lp.soyuz.interfaces.archivesubscriber.IPersonalArchiveSubscription"
522 name="+index"/>522 name="+index"/>
523 <browser:navigation
524 module="lp.soyuz.browser.archivesubscription"
525 classes="PersonalArchiveSubscriptionNavigation"/>
526 <browser:defaultView523 <browser:defaultView
527 for="lp.soyuz.interfaces.distributionsourcepackagerelease.IDistributionSourcePackageRelease"524 for="lp.soyuz.interfaces.distributionsourcepackagerelease.IDistributionSourcePackageRelease"
528 name="+index"/>525 name="+index"/>
529526
=== modified file 'lib/lp/soyuz/browser/distroarchseriesbinarypackage.py'
--- lib/lp/soyuz/browser/distroarchseriesbinarypackage.py 2009-09-03 15:17:24 +0000
+++ lib/lp/soyuz/browser/distroarchseriesbinarypackage.py 2009-09-29 07:21:40 +0000
@@ -4,7 +4,6 @@
4__metaclass__ = type4__metaclass__ = type
55
6__all__ = [6__all__ = [
7 'DistroArchSeriesBinaryPackageBreadcrumb',
8 'DistroArchSeriesBinaryPackageNavigation',7 'DistroArchSeriesBinaryPackageNavigation',
9 'DistroArchSeriesBinaryPackageView',8 'DistroArchSeriesBinaryPackageView',
10 ]9 ]
@@ -16,14 +15,6 @@
16from canonical.lazr.utils import smartquote15from canonical.lazr.utils import smartquote
1716
1817
19class DistroArchSeriesBinaryPackageBreadcrumb(Breadcrumb):
20 """A breadcrumb for `DistroArchSeriesBinaryPackage`."""
21
22 @property
23 def text(self):
24 return self.context.name
25
26
27class DistroArchSeriesBinaryPackageOverviewMenu(ApplicationMenu):18class DistroArchSeriesBinaryPackageOverviewMenu(ApplicationMenu):
2819
29 usedfor = IDistroArchSeriesBinaryPackage20 usedfor = IDistroArchSeriesBinaryPackage
3021
=== modified file 'lib/lp/soyuz/browser/tests/test_breadcrumbs.py'
--- lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-11 10:52:54 +0000
+++ lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-29 07:21:40 +0000
@@ -76,7 +76,6 @@
76 owner, self.ppa)76 owner, self.ppa)
7777
78 def test_personal_archive_subscription(self):78 def test_personal_archive_subscription(self):
79
80 self.traversed_objects = [79 self.traversed_objects = [
81 self.root, self.ppa.owner, self.personal_archive_subscription]80 self.root, self.ppa.owner, self.personal_archive_subscription]
82 subscription_url = canonical_url(self.personal_archive_subscription)81 subscription_url = canonical_url(self.personal_archive_subscription)
8382
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2009-09-24 08:17:04 +0000
+++ lib/lp/soyuz/configure.zcml 2009-09-29 07:21:40 +0000
@@ -582,7 +582,7 @@
582 <adapter582 <adapter
583 for="lp.soyuz.interfaces.distroarchseriesbinarypackage.IDistroArchSeriesBinaryPackage"583 for="lp.soyuz.interfaces.distroarchseriesbinarypackage.IDistroArchSeriesBinaryPackage"
584 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"584 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
585 factory="lp.soyuz.browser.distroarchseriesbinarypackage.DistroArchSeriesBinaryPackageBreadcrumb"585 factory="canonical.launchpad.webapp.breadcrumb.NameBreadcrumb"
586 permission="zope.Public" />586 permission="zope.Public" />
587587
588 <!-- PublishedPackage -->588 <!-- PublishedPackage -->
@@ -650,7 +650,7 @@
650 <adapter650 <adapter
651 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"651 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
652 for="lp.soyuz.interfaces.archivesubscriber.IPersonalArchiveSubscription"652 for="lp.soyuz.interfaces.archivesubscriber.IPersonalArchiveSubscription"
653 factory="lp.soyuz.browser.archivesubscription.PersonalArchiveSubscriptionBreadcrumb"653 factory="canonical.launchpad.webapp.breadcrumb.DisplaynameBreadcrumb"
654 permission="zope.Public"/>654 permission="zope.Public"/>
655 <subscriber655 <subscriber
656 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber656 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber
657657
=== modified file 'lib/lp/soyuz/templates/archive-subscribers.pt'
--- lib/lp/soyuz/templates/archive-subscribers.pt 2009-09-18 08:09:23 +0000
+++ lib/lp/soyuz/templates/archive-subscribers.pt 2009-09-29 07:21:40 +0000
@@ -10,6 +10,11 @@
10 <metal:block fill-slot="head_epilogue">10 <metal:block fill-slot="head_epilogue">
11 <metal:yui-dependencies11 <metal:yui-dependencies
12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />
13
14 <tal:devmode condition="devmode">
15 <script type="text/javascript"
16 tal:attributes="src string:${lp_js}/soyuz/archivesubscribers_index.js"></script>
17 </tal:devmode>
13 </metal:block>18 </metal:block>
1419
15 <div metal:fill-slot="main">20 <div metal:fill-slot="main">
@@ -92,51 +97,12 @@
92 </table>97 </table>
93 </form>98 </form>
94 </div><!-- class="portlet" -->99 </div><!-- class="portlet" -->
100 <script type="text/javascript" id="setup-archivesubscribers-index">
101 YUI().use('soyuz.archivesubscribers_index', function(Y) {
102 Y.soyuz.setup_archivesubscribers_index();
103 });
104 </script>
95 </div>105 </div>
96 <script type="text/javascript">
97YUI({
98 base: '../../lib/yui/current/build/',
99 filter: 'raw'
100 }).use('node', 'event', 'lazr.effects', function(Y) {
101
102 // If there are no errors then we hide the add-subscriber row and
103 // potentially the whole table if there are no subscribers.
104 if (Y.Lang.isNull(Y.get('p.error.message'))) {
105
106 // Hide the add-subscriber row.
107 var add_subscriber_row = Y.get(
108 '#archive-subscribers .add-subscriber');
109 add_subscriber_row.setStyle('display', 'none');
110
111 // If there are no subscribers, then hide the complete section.
112 var subscribers = Y.get('#subscribers');
113 if (Y.Lang.isObject(Y.get('#no-subscribers'))) {
114 subscribers.setStyle('display', 'none');
115 }
116 }
117
118 // Add a link to open the add-subscriber row.
119 var placeholder = Y.get('#add-subscriber-placeholder');
120 placeholder.set(
121 'innerHTML', '<a class="js-action sprite add" href="#" />');
122 var add_subscriber_node = Y.get("#add-subscriber-placeholder a");
123 add_subscriber_node.set('innerHTML', 'Add access');
124
125 // Unfortunately we can't use the lazr slider, as it uses display:block
126 // which breaks table rows (they use display:table-row).
127 function show_add_subscriber(e) {
128 e.preventDefault();
129 subscribers.setStyle('display', 'block');
130 add_subscriber_row.setStyle('display', 'table-row');
131 }
132
133 Y.on('click', show_add_subscriber,
134 '#add-subscriber-placeholder a');
135
136});
137 </script>
138
139
140 </div>106 </div>
141</body>107</body>
142</html>108</html>
143109
=== added directory 'lib/lp/soyuz/windmill'
=== added file 'lib/lp/soyuz/windmill/__init__.py'
=== added file 'lib/lp/soyuz/windmill/testing.py'
--- lib/lp/soyuz/windmill/testing.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/windmill/testing.py 2009-09-29 12:09:39 +0000
@@ -0,0 +1,18 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Soyuz-specific testing infrastructure for Windmill."""
5
6__metaclass__ = type
7__all__ = [
8 'SoyuzWindmillLayer',
9 ]
10
11
12from canonical.testing.layers import BaseWindmillLayer
13
14
15class SoyuzWindmillLayer(BaseWindmillLayer):
16 """Layer for Soyuz Windmill tests."""
17
18 base_url = 'http://launchpad.dev:8085/'
019
=== added directory 'lib/lp/soyuz/windmill/tests'
=== added file 'lib/lp/soyuz/windmill/tests/__init__.py'
=== added file 'lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py'
--- lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py 2009-09-29 12:09:39 +0000
@@ -0,0 +1,96 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Test for the archive subscribers index page.."""
5
6__metaclass__ = type
7__all__ = []
8
9import transaction
10import unittest
11
12from windmill.authoring import WindmillTestClient
13
14from zope.component import getUtility
15
16from canonical.launchpad.ftests import login, logout
17from canonical.launchpad.windmill.testing.lpuser import LaunchpadUser
18from lp.registry.interfaces.distribution import IDistributionSet
19from lp.soyuz.windmill.testing import SoyuzWindmillLayer
20from lp.testing import TestCaseWithFactory
21
22WAIT_PAGELOAD = u'30000'
23WAIT_ELEMENT_COMPLETE = u'30000'
24WAIT_CHECK_CHANGE = u'1000'
25ADD_ACCESS_LINK = u'//a[@class="js-action sprite add"]'
26CHOOSE_SUBSCRIBER_LINK = u'//a[@id="show-widget-field-subscriber"]'
27SUBSCRIBER_SEARCH_FIELD = (
28 u'//div[@id="yui-pretty-overlay-modal"]//input[@name="search"]')
29SUBSCRIBER_SEARCH_BUTTON = u'//div[@id="yui-pretty-overlay-modal"]//button'
30FIRST_SUBSCRIBER_RESULT = (
31 u'//div[@id="yui-pretty-overlay-modal"]'
32 '//span[@class="yui-picker-result-title"]')
33
34
35class TestArchiveSubscribersIndex(TestCaseWithFactory):
36
37 layer = SoyuzWindmillLayer
38
39 def setUp(self):
40 """Create a private PPA."""
41 super(TestArchiveSubscribersIndex, self).setUp()
42
43 user = self.factory.makePerson(
44 name='joe-bloggs', email='joe@example.com', password='joe',
45 displayname='Joe Bloggs')
46 ubuntu = getUtility(IDistributionSet)['ubuntu']
47 self.ppa = self.factory.makeArchive(
48 owner=user, name='myppa', distribution=ubuntu)
49
50 login('foo.bar@canonical.com')
51 self.ppa.private = True
52 self.ppa.buildd_secret = 'secret'
53 logout()
54 transaction.commit()
55
56 self.lpuser = LaunchpadUser(
57 'Joe Bloggs', 'joe@example.com', 'joe')
58
59 def test_add_subscriber(self):
60 """Test adding a private PPA subscriber.."""
61 client = WindmillTestClient('Adding private PPA subscribers.')
62
63 self.lpuser.ensure_login(client)
64
65 client.open(url='http://launchpad.dev:8085/~joe-bloggs/'
66 '+archive/myppa/+subscriptions')
67 client.waits.forPageLoad(timeout=WAIT_PAGELOAD)
68
69 # Click on the JS add access action.
70 client.waits.forElement(xpath=ADD_ACCESS_LINK)
71 client.click(xpath=ADD_ACCESS_LINK)
72
73 # Open the picker, search for 'launchpad' and choose the first
74 # result
75 client.click(xpath=CHOOSE_SUBSCRIBER_LINK)
76 client.type(xpath=SUBSCRIBER_SEARCH_FIELD, text='launchpad')
77 client.click(xpath=SUBSCRIBER_SEARCH_BUTTON)
78
79 client.waits.forElement(xpath=FIRST_SUBSCRIBER_RESULT)
80 client.click(xpath=FIRST_SUBSCRIBER_RESULT)
81
82 # Add the new subscriber.
83 client.click(id='field.actions.add')
84 client.waits.forPageLoad(timeout=WAIT_PAGELOAD)
85
86 # And verify that the correct informational message is displayed.
87 # It would be nice if we could use ... here.
88 client.asserts.assertText(
89 xpath=u'//div[@class="informational message"]',
90 validator='You have granted access for Launchpad Developers '
91 'to install software from PPA named myppa for Joe '
92 'Bloggs. Members of Launchpad Developers will be '
93 'notified of the access via email.')
94
95def test_suite():
96 return unittest.TestLoader().loadTestsFromName(__name__)
Revision history for this message
Graham Binns (gmb) wrote :

Approved with the changes requested in IRC w.r.t comments in the tests.

review: Approve (js)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css 2009-09-23 10:29:52 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css 2009-09-30 14:18:17 +0000
@@ -366,6 +366,9 @@
366 vertical-align: bottom;366 vertical-align: bottom;
367 }367 }
368368
369form {
370 margin-bottom: 1em;
371}
369form h1 {372form h1 {
370 margin-bottom: 1em;373 margin-bottom: 1em;
371 }374 }
372375
=== added file 'lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js'
--- lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/archivesubscribers_index.js 2009-09-30 14:18:17 +0000
@@ -0,0 +1,54 @@
1/* Copyright 2009 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 *
4 * Enhancements for adding ppa subscribers.
5 *
6 * @module ArchiveSubscribersIndex
7 * @requires event, node, oop
8 */
9YUI.add('soyuz.archivesubscribers_index', function(Y) {
10
11var soyuz = Y.namespace('soyuz');
12
13/*
14 * Setup the style and click handler for the add subscriber link.
15 *
16 * @method setup_archivesubscribers_index
17 */
18Y.soyuz.setup_archivesubscribers_index = function() {
19 // If there are no errors then we hide the add-subscriber row and
20 // potentially the whole table if there are no subscribers.
21 if (Y.Lang.isNull(Y.get('p.error.message'))) {
22
23 // Hide the add-subscriber row.
24 var add_subscriber_row = Y.get(
25 '#archive-subscribers .add-subscriber');
26 add_subscriber_row.setStyle('display', 'none');
27
28 // If there are no subscribers, then hide the complete section.
29 var subscribers = Y.get('#subscribers');
30 if (Y.Lang.isObject(Y.get('#no-subscribers'))) {
31 subscribers.setStyle('display', 'none');
32 }
33 }
34
35 // Add a link to open the add-subscriber row.
36 var placeholder = Y.get('#add-subscriber-placeholder');
37 placeholder.set(
38 'innerHTML',
39 '<a class="js-action sprite add" href="#">Add access</a>');
40
41 // Unfortunately we can't use the lazr slider, as it uses display:block
42 // which breaks table rows (they use display:table-row).
43 function show_add_subscriber(e) {
44 e.preventDefault();
45 subscribers.setStyle('display', 'block');
46 add_subscriber_row.setStyle('display', 'table-row');
47 }
48
49 Y.on('click', show_add_subscriber,
50 '#add-subscriber-placeholder a');
51};
52
53}, '0.1', {requires: ['oop', 'node', 'event']});
54
055
=== added file 'lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html'
--- lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.html 2009-09-30 14:18:17 +0000
@@ -0,0 +1,46 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2<html>
3 <head>
4 <title>Launchpad ArchiveSubscriberIndex</title>
5
6 <!-- YUI 3.0 Setup -->
7 <script type="text/javascript" src="../../../icing/yui/current/build/yui/yui.js"></script>
8 <link rel="stylesheet" href="../../../icing/yui/current/build/cssreset/reset.css"/>
9 <link rel="stylesheet" href="../../../icing/yui/current/build/cssfonts/fonts.css"/>
10 <link rel="stylesheet" href="../../../icing/yui/current/build/cssbase/base.css"/>
11 <link rel="stylesheet" href="../../test.css" />
12
13 <!-- The module under test -->
14 <script type="text/javascript" src="../archivesubscribers_index.js"></script>
15
16 <!-- The test suite -->
17 <script type="text/javascript" src="archivesubscribers_index.js"></script>
18</head>
19<body class="yui-skin-sam">
20 <h1>Testing the ArchiveSubscribersIndex javascript</h1>
21
22 <h2>Errors</h2>
23 <div id="errors">
24 </div>
25
26 <h2>Add subscriber place-holder</h2>
27 <div id="add-subscriber-placeholder">
28 </div>
29
30 <h2>Subscribers</h2>
31 <div id="subscribers">
32 <table id="archive-subscribers">
33 <thead>
34 <tr>
35 <th>Name</th>
36 <th>Expires</th>
37 <th colspan="2">Comment</th>
38 </tr>
39 </thead>
40 <tbody>
41 </tbody>
42 </table>
43 </div>
44 <div id="log"></div>
45</body>
46</html>
047
=== added file 'lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js'
--- lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/javascript/soyuz/tests/archivesubscribers_index.js 2009-09-30 14:18:17 +0000
@@ -0,0 +1,147 @@
1/* Copyright 2009 Canonical Ltd. This software is licensed under the
2 GNU Affero General Public License version 3 (see the file LICENSE). */
3
4YUI({
5 base: '../../../icing/yui/current/build/',
6 filter: 'raw',
7 combine: false
8 }).use(
9 'yuitest', 'console', 'soyuz.archivesubscribers_index', function(Y) {
10
11var Assert = Y.Assert; // For easy access to isTrue(), etc.
12
13var suite = new Y.Test.Suite("ArchiveSubscriber Tests");
14
15suite.add(new Y.Test.Case({
16
17 name: 'add-subscriber',
18
19 setUp: function() {
20 this.add_subscriber_placeholder = Y.get(
21 '#add-subscriber-placeholder');
22 this.archive_subscribers_table_body = Y.get(
23 '#archive-subscribers').query('tbody');
24 this.error_div = Y.get('#errors');
25 this.subscribers_div = Y.get('#subscribers');
26
27
28 // Ensure there are no errors displayed.
29 this.error_div.set('innerHTML', '');
30
31 // Ensure the add subscriber place-holder is empty.
32 this.add_subscriber_placeholder.set('innerHTML', '');
33
34 // Ensure the table has the correct structure.
35 this.archive_subscribers_table_body.set(
36 'innerHTML', [
37 '<tr class="add-subscriber">',
38 '<td>New 1</td>',
39 '<td>New 2</td>',
40 '<td>New 3</td>',
41 '<td>Add</td>',
42 '</tr>',
43 '<tr>',
44 '<td>Existing 1</td>',
45 '<td>Existing 2</td>',
46 '<td>Existing 3</td>',
47 '<td>Edit</td>',
48 '</tr>'
49 ].join(''));
50
51 this.add_subscriber_row = Y.get(
52 '#archive-subscribers .add-subscriber');
53 },
54
55 test_add_row_displayed_by_default: function() {
56 // The add subscriber row is displayed when the JS is not run.
57 Assert.areEqual(
58 'table-row', this.add_subscriber_row.getStyle('display'),
59 'The add subscriber row should display when the js is not run.');
60 },
61
62 test_subscribers_displayed_by_default: function() {
63 // The subscribers section is displayed when the js is not run.
64 Assert.areEqual(
65 'block', this.subscribers_div.getStyle('display'),
66 'The subscribers section should display without js.');
67 },
68
69 test_add_row_hidden_after_setup: function() {
70 // The add subscriber row is hidden during setup.
71 Y.soyuz.setup_archivesubscribers_index();
72 Assert.areEqual(
73 'none', this.add_subscriber_row.getStyle('display'),
74 'The add subscriber row should be hidden during setup.');
75 },
76
77 test_subscribers_section_displayed_after_setup: function() {
78 // The subscribers div normally remains displayed after setup.
79 Y.soyuz.setup_archivesubscribers_index();
80 Assert.areEqual(
81 'block', this.subscribers_div.getStyle('display'),
82 'The subscribers div should remain displayed after setup.');
83 },
84
85 test_subscribers_section_hidden_when_no_subscribers: function() {
86 // The subscribers div is hidden when there are no subscribers.
87
88 // Add a paragraph with the no-subscribers id.
89 this.error_div.set('innerHTML', '<p id="no-subscribers">blah</p>');
90 Y.soyuz.setup_archivesubscribers_index();
91 Assert.areEqual(
92 'none', this.subscribers_div.getStyle('display'),
93 'The subscribers div should be hidden when there are ' +
94 'no subscribers.');
95 },
96
97 test_add_row_displayed_when_errors_present: function() {
98 // The add subscriber row is not hidden if there are validation
99 // errors.
100
101 // Add an error paragraph.
102 this.error_div.set('innerHTML', '<p class="error message">Blah</p>');
103 Y.soyuz.setup_archivesubscribers_index();
104 Assert.areEqual(
105 'table-row', this.add_subscriber_row.getStyle('display'),
106 'The add subscriber row should not be hidden if there are ' +
107 'errors present.');
108 },
109
110 test_add_access_link_added_after_setup: function() {
111 // The 'Add access' link is created during setup.
112
113 Y.soyuz.setup_archivesubscribers_index();
114 Assert.areEqual(
115 '<a class="js-action sprite add" href="#">Add access</a>',
116 this.add_subscriber_placeholder.get('innerHTML'),
117 "The 'Add access' link should be created during setup.")
118 },
119
120 test_click_add_access_displays_add_row: function() {
121 // The add subscriber row is displayed after clicking 'Add access'.
122 Y.soyuz.setup_archivesubscribers_index();
123 var link_node = this.add_subscriber_placeholder.query('a');
124 Assert.areEqual(
125 'Add access', link_node.get('innerHTML'));
126
127 Y.Event.simulate(Y.Node.getDOMNode(link_node), 'click');
128
129 Assert.areEqual(
130 'table-row', this.add_subscriber_row.getStyle('display'),
131 "The add subscriber row should be displayed after clicking " +
132 "'Add access'");
133 }
134}));
135
136Y.Test.Runner.add(suite);
137
138var yconsole = new Y.Console({
139 newestOnTop: false
140});
141yconsole.render('#log');
142
143Y.on('domready', function() {
144 Y.Test.Runner.run();
145});
146
147});
0148
=== modified file 'lib/canonical/launchpad/pagetitles.py'
--- lib/canonical/launchpad/pagetitles.py 2009-09-23 14:58:12 +0000
+++ lib/canonical/launchpad/pagetitles.py 2009-09-30 14:18:17 +0000
@@ -128,10 +128,6 @@
128128
129archive_edit_dependencies = ContextDisplayName('Edit dependencies for %s')129archive_edit_dependencies = ContextDisplayName('Edit dependencies for %s')
130130
131archive_subscriber_edit = ContextDisplayName('Edit %s')
132
133archive_subscribers = ContextDisplayName('Manage access to %s')
134
135bazaar_all_branches = 'All branches in the Launchpad Bazaar'131bazaar_all_branches = 'All branches in the Launchpad Bazaar'
136132
137bazaar_index = 'Launchpad Branches'133bazaar_index = 'Launchpad Branches'
@@ -562,10 +558,6 @@
562558
563people_requestmerge_multiple = 'Merge Launchpad accounts'559people_requestmerge_multiple = 'Merge Launchpad accounts'
564560
565person_archive_subscription = ContextDisplayName('%s')
566
567person_archive_subscriptions = 'Private PPA access'
568
569person_answer_contact_for = ContextDisplayName(561person_answer_contact_for = ContextDisplayName(
570 'Projects for which %s is an answer contact')562 'Projects for which %s is an answer contact')
571563
572564
=== modified file 'lib/lp/soyuz/browser/archivesubscription.py'
--- lib/lp/soyuz/browser/archivesubscription.py 2009-08-12 08:40:41 +0000
+++ lib/lp/soyuz/browser/archivesubscription.py 2009-09-30 14:18:17 +0000
@@ -9,6 +9,7 @@
99
10__all__ = [10__all__ = [
11 'ArchiveSubscribersView',11 'ArchiveSubscribersView',
12 'PersonArchiveSubscriptionView',
12 'PersonArchiveSubscriptionsView',13 'PersonArchiveSubscriptionsView',
13 'traverse_archive_subscription_for_subscriber'14 'traverse_archive_subscription_for_subscriber'
14 ]15 ]
@@ -34,10 +35,11 @@
34 IArchiveAuthTokenSet)35 IArchiveAuthTokenSet)
35from lp.soyuz.interfaces.archivesubscriber import (36from lp.soyuz.interfaces.archivesubscriber import (
36 IArchiveSubscriberSet, IPersonalArchiveSubscription)37 IArchiveSubscriberSet, IPersonalArchiveSubscription)
38from canonical.launchpad.webapp.breadcrumb import Breadcrumb
37from canonical.launchpad.webapp.launchpadform import (39from canonical.launchpad.webapp.launchpadform import (
38 action, custom_widget, LaunchpadFormView, LaunchpadEditFormView)40 action, custom_widget, LaunchpadFormView, LaunchpadEditFormView)
39from canonical.launchpad.webapp.publisher import (41from canonical.launchpad.webapp.publisher import (
40 canonical_url, LaunchpadView)42 canonical_url, LaunchpadView, Navigation)
41from canonical.widgets import DateWidget43from canonical.widgets import DateWidget
42from canonical.widgets.popup import PersonPickerWidget44from canonical.widgets.popup import PersonPickerWidget
4345
@@ -65,6 +67,12 @@
65 """See `IPersonalArchiveSubscription`."""67 """See `IPersonalArchiveSubscription`."""
66 return "Access to %s" % self.archive.displayname68 return "Access to %s" % self.archive.displayname
6769
70 @property
71 def title(self):
72 """Required for default headings in templates."""
73 return self.displayname
74
75
68def traverse_archive_subscription_for_subscriber(subscriber, archive_id):76def traverse_archive_subscription_for_subscriber(subscriber, archive_id):
69 """Return the subscription for a subscriber to an archive."""77 """Return the subscription for a subscriber to an archive."""
70 subscription = None78 subscription = None
@@ -111,6 +119,11 @@
111 custom_widget('subscriber', PersonPickerWidget,119 custom_widget('subscriber', PersonPickerWidget,
112 header="Select the subscriber")120 header="Select the subscriber")
113121
122 @property
123 def label(self):
124 """Return a label for the view's main heading."""
125 return "Manage access to " + self.context.title
126
114 def initialize(self):127 def initialize(self):
115 """Ensure that we are dealing with a private archive."""128 """Ensure that we are dealing with a private archive."""
116 # If this archive is not private, then we should not be129 # If this archive is not private, then we should not be
@@ -210,6 +223,11 @@
210 custom_widget('description', TextWidget, displayWidth=40)223 custom_widget('description', TextWidget, displayWidth=40)
211 custom_widget('date_expires', CustomWidgetFactory(DateWidget))224 custom_widget('date_expires', CustomWidgetFactory(DateWidget))
212225
226 @property
227 def label(self):
228 """Return a label for the view's main heading."""
229 return "Edit " + self.context.displayname
230
213 def validate_update_subscription(self, action, data):231 def validate_update_subscription(self, action, data):
214 """Ensure that the date of expiry is not in the past."""232 """Ensure that the date of expiry is not in the past."""
215 form.getWidgetsData(self.widgets, 'field', data)233 form.getWidgetsData(self.widgets, 'field', data)
@@ -241,12 +259,12 @@
241 self.context.subscriber.displayname)259 self.context.subscriber.displayname)
242 self.request.response.addNotification(notification)260 self.request.response.addNotification(notification)
243261
244 @action(u'Cancel access', name='cancel')262 @action(u'Revoke access', name='cancel')
245 def cancel_subscription(self, action, data):263 def cancel_subscription(self, action, data):
246 """Cancel the context subscription."""264 """Cancel the context subscription."""
247 self.context.cancel(self.user)265 self.context.cancel(self.user)
248266
249 notification = "You have cancelled %s's subscription to %s." % (267 notification = "You have revoked %s's access to %s." % (
250 self.context.subscriber.displayname,268 self.context.subscriber.displayname,
251 self.context.archive.displayname)269 self.context.archive.displayname)
252 self.request.response.addNotification(notification)270 self.request.response.addNotification(notification)
@@ -265,6 +283,8 @@
265class PersonArchiveSubscriptionsView(LaunchpadView):283class PersonArchiveSubscriptionsView(LaunchpadView):
266 """A view for displaying a persons archive subscriptions."""284 """A view for displaying a persons archive subscriptions."""
267285
286 label = "Private PPA access"
287
268 @cachedproperty288 @cachedproperty
269 def subscriptions_with_tokens(self):289 def subscriptions_with_tokens(self):
270 """Return all the persons archive subscriptions with the token290 """Return all the persons archive subscriptions with the token
@@ -294,6 +314,11 @@
294 tokens.314 tokens.
295 """315 """
296316
317 @property
318 def label(self):
319 """Return the label for the view's main heading."""
320 return self.context.title
321
297 def initialize(self):322 def initialize(self):
298 """Process any posted actions."""323 """Process any posted actions."""
299 super(PersonArchiveSubscriptionView, self).initialize()324 super(PersonArchiveSubscriptionView, self).initialize()
300325
=== modified file 'lib/lp/soyuz/browser/distroarchseriesbinarypackage.py'
--- lib/lp/soyuz/browser/distroarchseriesbinarypackage.py 2009-09-03 15:17:24 +0000
+++ lib/lp/soyuz/browser/distroarchseriesbinarypackage.py 2009-09-30 14:18:17 +0000
@@ -4,7 +4,6 @@
4__metaclass__ = type4__metaclass__ = type
55
6__all__ = [6__all__ = [
7 'DistroArchSeriesBinaryPackageBreadcrumb',
8 'DistroArchSeriesBinaryPackageNavigation',7 'DistroArchSeriesBinaryPackageNavigation',
9 'DistroArchSeriesBinaryPackageView',8 'DistroArchSeriesBinaryPackageView',
10 ]9 ]
@@ -16,14 +15,6 @@
16from canonical.lazr.utils import smartquote15from canonical.lazr.utils import smartquote
1716
1817
19class DistroArchSeriesBinaryPackageBreadcrumb(Breadcrumb):
20 """A breadcrumb for `DistroArchSeriesBinaryPackage`."""
21
22 @property
23 def text(self):
24 return self.context.name
25
26
27class DistroArchSeriesBinaryPackageOverviewMenu(ApplicationMenu):18class DistroArchSeriesBinaryPackageOverviewMenu(ApplicationMenu):
2819
29 usedfor = IDistroArchSeriesBinaryPackage20 usedfor = IDistroArchSeriesBinaryPackage
3021
=== modified file 'lib/lp/soyuz/browser/tests/archivesubscription-views.txt'
--- lib/lp/soyuz/browser/tests/archivesubscription-views.txt 2009-08-13 19:03:36 +0000
+++ lib/lp/soyuz/browser/tests/archivesubscription-views.txt 2009-09-30 14:18:17 +0000
@@ -30,11 +30,16 @@
30 >>> transaction.commit()30 >>> transaction.commit()
31 >>> logout()31 >>> logout()
3232
33The view includes a label property.
34
35 >>> login('celso.providelo@canonical.com')
36 >>> view = create_initialized_view(cprov.archive, name="+subscriptions")
37 >>> print view.label
38 Manage access to PPA for Celso Providelo
39
33Initially the view does not display any subscribers, as can be seen40Initially the view does not display any subscribers, as can be seen
34using the has_subscriptions property:41using the has_subscriptions property:
3542
36 >>> login('celso.providelo@canonical.com')
37 >>> view = create_initialized_view(cprov.archive, name="+subscriptions")
38 >>> view.has_subscriptions43 >>> view.has_subscriptions
39 False44 False
4045
@@ -166,8 +171,8 @@
166171
167== ArchiveSubscriptionEditView ==172== ArchiveSubscriptionEditView ==
168173
169The ArchiveSubscriptionEditView presents the expiry and description ready174The ArchiveSubsriptionEditView includes a view label for the views main
170for editing, together with Update and Cancel actions:175title.
171176
172 >>> login('celso.providelo@canonical.com')177 >>> login('celso.providelo@canonical.com')
173 >>> from lp.soyuz.interfaces.archivesubscriber import (178 >>> from lp.soyuz.interfaces.archivesubscriber import (
@@ -175,12 +180,18 @@
175 >>> spiv_subscription = getUtility(IArchiveSubscriberSet).getByArchive(180 >>> spiv_subscription = getUtility(IArchiveSubscriberSet).getByArchive(
176 ... cprov.archive).one()181 ... cprov.archive).one()
177 >>> view = create_initialized_view(spiv_subscription, name="+edit")182 >>> view = create_initialized_view(spiv_subscription, name="+edit")
183 >>> print view.label
184 Edit Andrew Bennetts's access to PPA for Celso Providelo
185
186The ArchiveSubscriptionEditView presents the expiry and description ready
187for editing, together with Update and Cancel actions:
188
178 >>> view.field_names189 >>> view.field_names
179 ['date_expires', 'description']190 ['date_expires', 'description']
180 >>> for action in view.actions:191 >>> for action in view.actions:
181 ... print action.label192 ... print action.label
182 Save193 Save
183 Cancel access194 Revoke access
184195
185The ArchiveSubscriptionEditView has a next_url helper property.196The ArchiveSubscriptionEditView has a next_url helper property.
186197
@@ -241,7 +252,7 @@
241252
242 >>> for notification in view.request.notifications:253 >>> for notification in view.request.notifications:
243 ... print notification.message254 ... print notification.message
244 You have cancelled Andrew Bennetts's subscription to PPA for255 You have revoked Andrew Bennetts's access to PPA for
245 Celso Providelo.256 Celso Providelo.
246257
247Just uncancel the subscription before continuing on.258Just uncancel the subscription before continuing on.
@@ -306,13 +317,18 @@
306This view displays a single subscription of a person, as well as the317This view displays a single subscription of a person, as well as the
307corresponding token information.318corresponding token information.
308319
309Initially the subscription does not have an active token:320The view includes a label to denifen its main heading.
310321
311 >>> from lp.soyuz.browser.archivesubscription import (322 >>> from lp.soyuz.browser.archivesubscription import (
312 ... PersonalArchiveSubscription)323 ... PersonalArchiveSubscription)
313 >>> spiv_subscription = PersonalArchiveSubscription(324 >>> spiv_subscription = PersonalArchiveSubscription(
314 ... spiv_subscription.subscriber, spiv_subscription.archive)325 ... spiv_subscription.subscriber, spiv_subscription.archive)
315 >>> view = create_initialized_view(spiv_subscription, name="+index")326 >>> view = create_initialized_view(spiv_subscription, name="+index")
327 >>> print view.label
328 Access to PPA for Celso Providelo
329
330Initially the subscription does not have an active token:
331
316 >>> print view.active_token332 >>> print view.active_token
317 None333 None
318334
319335
=== modified file 'lib/lp/soyuz/browser/tests/test_breadcrumbs.py'
--- lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-02 16:32:06 +0000
+++ lib/lp/soyuz/browser/tests/test_breadcrumbs.py 2009-09-30 14:18:17 +0000
@@ -11,6 +11,8 @@
11from canonical.launchpad.webapp.tests.breadcrumbs import (11from canonical.launchpad.webapp.tests.breadcrumbs import (
12 BaseBreadcrumbTestCase)12 BaseBreadcrumbTestCase)
13from lp.registry.interfaces.distribution import IDistributionSet13from lp.registry.interfaces.distribution import IDistributionSet
14from lp.soyuz.browser.archivesubscription import PersonalArchiveSubscription
15from lp.testing import login, login_person
1416
1517
16class TestDistroArchSeriesBreadcrumb(BaseBreadcrumbTestCase):18class TestDistroArchSeriesBreadcrumb(BaseBreadcrumbTestCase):
@@ -55,5 +57,37 @@
55 self.assertEquals(texts[-1], "0.1-1")57 self.assertEquals(texts[-1], "0.1-1")
5658
5759
60class TestArchiveSubscriptionBreadcrumb(BaseBreadcrumbTestCase):
61
62 def setUp(self):
63 super(TestArchiveSubscriptionBreadcrumb, self).setUp()
64
65 # Create a private ppa
66 self.ppa = self.factory.makeArchive()
67 login('foo.bar@canonical.com')
68 self.ppa.private = True
69 self.ppa.buildd_secret = 'secret'
70
71 owner = self.ppa.owner
72 login_person(owner)
73 self.ppa_subscription = self.ppa.newSubscription(owner, owner)
74 self.ppa_token = self.ppa.newAuthToken(owner)
75 self.personal_archive_subscription = PersonalArchiveSubscription(
76 owner, self.ppa)
77
78 def test_personal_archive_subscription(self):
79 self.traversed_objects = [
80 self.root, self.ppa.owner, self.personal_archive_subscription]
81 subscription_url = canonical_url(self.personal_archive_subscription)
82
83 urls = self._getBreadcrumbsURLs(
84 subscription_url, self.traversed_objects)
85 texts = self._getBreadcrumbsTexts(
86 subscription_url, self.traversed_objects)
87
88 self.assertEquals(subscription_url, urls[-1])
89 self.assertEquals(
90 "Access to %s" % self.ppa.displayname, texts[-1])
91
58def test_suite():92def test_suite():
59 return unittest.TestLoader().loadTestsFromName(__name__)93 return unittest.TestLoader().loadTestsFromName(__name__)
6094
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2009-09-04 18:18:39 +0000
+++ lib/lp/soyuz/configure.zcml 2009-09-30 14:18:17 +0000
@@ -582,7 +582,7 @@
582 <adapter582 <adapter
583 for="lp.soyuz.interfaces.distroarchseriesbinarypackage.IDistroArchSeriesBinaryPackage"583 for="lp.soyuz.interfaces.distroarchseriesbinarypackage.IDistroArchSeriesBinaryPackage"
584 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"584 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
585 factory="lp.soyuz.browser.distroarchseriesbinarypackage.DistroArchSeriesBinaryPackageBreadcrumb"585 factory="canonical.launchpad.webapp.breadcrumb.NameBreadcrumb"
586 permission="zope.Public" />586 permission="zope.Public" />
587587
588 <!-- PublishedPackage -->588 <!-- PublishedPackage -->
@@ -647,6 +647,11 @@
647 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber"647 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber"
648 provides="lp.soyuz.browser.archivesubscription.IArchiveSubscriberUI"648 provides="lp.soyuz.browser.archivesubscription.IArchiveSubscriberUI"
649 factory="lp.soyuz.browser.archivesubscription.archive_subscription_ui_adapter"/>649 factory="lp.soyuz.browser.archivesubscription.archive_subscription_ui_adapter"/>
650 <adapter
651 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
652 for="lp.soyuz.interfaces.archivesubscriber.IPersonalArchiveSubscription"
653 factory="canonical.launchpad.webapp.breadcrumb.DisplaynameBreadcrumb"
654 permission="zope.Public"/>
650 <subscriber655 <subscriber
651 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber656 for="lp.soyuz.interfaces.archivesubscriber.IArchiveSubscriber
652 lazr.lifecycle.interfaces.IObjectCreatedEvent"657 lazr.lifecycle.interfaces.IObjectCreatedEvent"
653658
=== modified file 'lib/lp/soyuz/interfaces/archivesubscriber.py'
--- lib/lp/soyuz/interfaces/archivesubscriber.py 2009-07-17 00:26:05 +0000
+++ lib/lp/soyuz/interfaces/archivesubscriber.py 2009-09-30 14:18:17 +0000
@@ -185,3 +185,6 @@
185185
186 displayname = TextLine(title=_("Subscription displayname"),186 displayname = TextLine(title=_("Subscription displayname"),
187 required=False)187 required=False)
188
189 title = TextLine(title=_("Subscription title"),
190 required=False)
188191
=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt 2009-08-13 19:03:36 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt 2009-09-30 14:18:17 +0000
@@ -176,7 +176,7 @@
176176
177 >>> for msg in get_feedback_messages(cprov_browser.contents):177 >>> for msg in get_feedback_messages(cprov_browser.contents):
178 ... print msg178 ... print msg
179 You have cancelled Launchpad Developers's subscription to PPA179 You have revoked Launchpad Developers's access to PPA
180 for Celso Providelo.180 for Celso Providelo.
181181
182182
@@ -242,10 +242,10 @@
242 >>> regeneration_info = find_tag_by_id(242 >>> regeneration_info = find_tag_by_id(
243 ... joe_browser.contents, 'regenerate_token')243 ... joe_browser.contents, 'regenerate_token')
244 >>> print(extract_text(regeneration_info))244 >>> print(extract_text(regeneration_info))
245 Password security245 Reset password
246 If you believe...246 If you believe...
247 Reset password
247 Note: after ...248 Note: after ...
248 Generate new password
249249
250When Joe clicks on the 'Generate new personal subscription' link then250When Joe clicks on the 'Generate new personal subscription' link then
251the page is redisplayed with new sources.list entries and a notification.251the page is redisplayed with new sources.list entries and a notification.
252252
=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt 2009-08-13 19:03:36 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt 2009-09-30 14:18:17 +0000
@@ -54,6 +54,7 @@
54 ...54 ...
55 http://launchpad.dev/+icing/.../build/lp/calendar.js55 http://launchpad.dev/+icing/.../build/lp/calendar.js
56 http://launchpad.dev/+icing/.../yui_2.7.0b/build/calendar/assets/skins/sam/calendar.css56 http://launchpad.dev/+icing/.../yui_2.7.0b/build/calendar/assets/skins/sam/calendar.css
57 ...
5758
58Initially there are no subscriptions for a newly privatized PPA (although,59Initially there are no subscriptions for a newly privatized PPA (although,
59this may need to change, to add the owner/team). A heading is displayed60this may need to change, to add the owner/team). A heading is displayed
6061
=== modified file 'lib/lp/soyuz/templates/archive-subscriber-edit.pt'
--- lib/lp/soyuz/templates/archive-subscriber-edit.pt 2009-08-05 10:02:22 +0000
+++ lib/lp/soyuz/templates/archive-subscriber-edit.pt 2009-09-30 14:18:17 +0000
@@ -12,12 +12,6 @@
12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />
13 </metal:block>13 </metal:block>
1414
15 <div metal:fill-slot="heading">
16 <h1 tal:content="CONTEXTS/fmt:pagetitle">
17 Edit access for Joe Smith
18 </h1>
19 </div>
20
21 <div metal:fill-slot="main">15 <div metal:fill-slot="main">
22 <div class="top-portlet">16 <div class="top-portlet">
23 <p>You can update the expiry or description for the granted access,17 <p>You can update the expiry or description for the granted access,
2418
=== modified file 'lib/lp/soyuz/templates/archive-subscribers.pt'
--- lib/lp/soyuz/templates/archive-subscribers.pt 2009-08-05 10:02:22 +0000
+++ lib/lp/soyuz/templates/archive-subscribers.pt 2009-09-30 14:18:17 +0000
@@ -10,24 +10,26 @@
10 <metal:block fill-slot="head_epilogue">10 <metal:block fill-slot="head_epilogue">
11 <metal:yui-dependencies11 <metal:yui-dependencies
12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />12 use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />
13
14 <tal:devmode condition="devmode">
15 <script type="text/javascript"
16 tal:attributes="src string:${lp_js}/soyuz/archivesubscribers_index.js"></script>
17 </tal:devmode>
13 </metal:block>18 </metal:block>
1419
15 <div metal:fill-slot="heading">
16 <h1 tal:content="CONTEXTS/fmt:pagetitle">
17 Manage access to Blah PPA
18 </h1>
19 </div>
20 <div metal:fill-slot="main">20 <div metal:fill-slot="main">
21 <div class="top-portlet">21 <div class="top-portlet">
22 <p>You can grant access to people or teams to install software22 <p>You can grant access to people or teams to install software
23 from your PPA.23 from your PPA.
24 </p>24 </p>
2525
26 <div class="subscribers">26 <p tal:condition="not: view/has_subscriptions" id="no-subscribers">
27 <p tal:condition="not: view/has_subscriptions" id="no-subscribers">27 No one has access to install software from this PPA.
28 No one has access to install software from this PPA.28 </p>
29 </p>29
3030 <div id="add-subscriber-placeholder"></div>
31
32 <div id="subscribers">
31 <p class="error message" tal:condition="view/errors"33 <p class="error message" tal:condition="view/errors"
32 tal:content="view/error_count" />34 tal:content="view/error_count" />
3335
@@ -44,13 +46,13 @@
44 id="archive-subscribers" class="listing">46 id="archive-subscribers" class="listing">
45 <thead>47 <thead>
46 <tr class="archive_subscriber_row">48 <tr class="archive_subscriber_row">
47 <th>Name</th>49 <th style="width:30%">Name</th>
48 <th>Expires</th>50 <th>Expires</th>
49 <th colspan="2">Comment</th>51 <th colspan="2">Comment</th>
50 </tr>52 </tr>
51 </thead>53 </thead>
52 <tbody>54 <tbody>
53 <tr>55 <tr class="add-subscriber" style="background-color:#eeeeff;">
54 <tal:single-row-form define="widgets widgets|view/widgets"56 <tal:single-row-form define="widgets widgets|view/widgets"
55 repeat="widget widgets">57 repeat="widget widgets">
5658
@@ -95,8 +97,12 @@
95 </table>97 </table>
96 </form>98 </form>
97 </div><!-- class="portlet" -->99 </div><!-- class="portlet" -->
100 <script type="text/javascript" id="setup-archivesubscribers-index">
101 YUI().use('soyuz.archivesubscribers_index', function(Y) {
102 Y.soyuz.setup_archivesubscribers_index();
103 });
104 </script>
98 </div>105 </div>
99
100 </div>106 </div>
101</body>107</body>
102</html>108</html>
103109
=== modified file 'lib/lp/soyuz/templates/person-archive-subscription.pt'
--- lib/lp/soyuz/templates/person-archive-subscription.pt 2009-08-05 10:02:22 +0000
+++ lib/lp/soyuz/templates/person-archive-subscription.pt 2009-09-30 14:18:17 +0000
@@ -8,12 +8,6 @@
8 >8 >
9<body>9<body>
1010
11 <div metal:fill-slot="heading">
12 <h1 tal:content="CONTEXTS/fmt:pagetitle">
13 Access to Joe Smith's private archive
14 </h1>
15 </div>
16
17 <div metal:fill-slot="main">11 <div metal:fill-slot="main">
1812
19 <tal:activate condition="not: view/active_token">13 <tal:activate condition="not: view/active_token">
@@ -54,22 +48,23 @@
54 </div> <!-- signing-key -->48 </div> <!-- signing-key -->
55 </div>49 </div>
56 <div id="regenerate_token" class="portlet" style="clear:both">50 <div id="regenerate_token" class="portlet" style="clear:both">
57 <h2>Password security</h2>51 <h2>Reset password</h2>
58 <p>If you believe the security of your password for this access52 <p>If you believe the security of your password for this access
59 has been compromised, you can generate a new one. After you've53 has been compromised, you reset your password. After you've
60 requested a new password, you'll see new "sources.list" entries54 requested a new password, you'll see new "sources.list" entries
61 on this page. You'll need to update them on your computer.55 on this page. You'll need to update them on your computer.
62 </p>56 </p>
63 <p>Note: after requesting a new password for your personal
64 access &mdash; and updating your sources.list with the new
65 entries &mdash; you may need to wait for up to ten minutes before
66 you can download using the new password.
67 </p>
68 <form action="" method="post">57 <form action="" method="post">
69 <button type="submit" name="regenerate" value="1">58 <button type="submit" name="regenerate" value="1">
70 Generate new password59 Reset password
71 </button>60 </button>
72 </form>61 </form>
62 <p class="discreet">
63 Note: after resetting the password for your personal
64 access &mdash; and updating your sources.list with the new
65 entries &mdash; you may need to wait for up to ten minutes before
66 you can download using the new password.
67 </p>
73 </div>68 </div>
74 </tal:active>69 </tal:active>
75 </div>70 </div>
7671
=== modified file 'lib/lp/soyuz/templates/person-archive-subscriptions.pt'
--- lib/lp/soyuz/templates/person-archive-subscriptions.pt 2009-08-05 10:02:22 +0000
+++ lib/lp/soyuz/templates/person-archive-subscriptions.pt 2009-09-30 14:18:17 +0000
@@ -7,24 +7,20 @@
7 i18n:domain="launchpad"7 i18n:domain="launchpad"
8 >8 >
9<body>9<body>
10 <div metal:fill-slot="heading">
11 <h1 tal:content="CONTEXTS/fmt:pagetitle">
12 Private PPA access
13 </h1>
14 </div>
1510
16 <div metal:fill-slot="main">11 <div metal:fill-slot="main">
17 <div class="top-portlet">12 <div class="top-portlet">
18 <tal:has_subscriptions condition="view/subscriptions_with_tokens">13 <tal:has_subscriptions condition="view/subscriptions_with_tokens">
19 <div id="current_subscriptions">14 <div id="current_subscriptions">
20 <p>Here's a list of all the private archives to which you have15 <p>All the private archives to which you have
21 been granted access.16 been granted access are listed below.
22 </p>17 </p>
23 <table summary="CONTEXTS/fmt:pagetitle" id="archive-subscriptions"18 <table summary="CONTEXTS/fmt:pagetitle" id="archive-subscriptions"
24 class="listing">19 class="listing" style="width:70%">
25 <thead>20 <thead>
26 <tr class="archive-subscription-row">21 <tr class="archive-subscription-row">
27 <th>Archive</th>22 <th>Archive</th>
23 <th></th>
28 </tr>24 </tr>
29 </thead>25 </thead>
30 <tbody>26 <tbody>
3127
=== added directory 'lib/lp/soyuz/windmill'
=== added file 'lib/lp/soyuz/windmill/__init__.py'
=== added file 'lib/lp/soyuz/windmill/testing.py'
--- lib/lp/soyuz/windmill/testing.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/windmill/testing.py 2009-09-30 14:18:17 +0000
@@ -0,0 +1,18 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Soyuz-specific testing infrastructure for Windmill."""
5
6__metaclass__ = type
7__all__ = [
8 'SoyuzWindmillLayer',
9 ]
10
11
12from canonical.testing.layers import BaseWindmillLayer
13
14
15class SoyuzWindmillLayer(BaseWindmillLayer):
16 """Layer for Soyuz Windmill tests."""
17
18 base_url = 'http://launchpad.dev:8085/'
019
=== added directory 'lib/lp/soyuz/windmill/tests'
=== added file 'lib/lp/soyuz/windmill/tests/__init__.py'
=== added file 'lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py'
--- lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/windmill/tests/test_archivesubscribersindex.py 2009-09-30 14:18:17 +0000
@@ -0,0 +1,96 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Test for the archive subscribers index page.."""
5
6__metaclass__ = type
7__all__ = []
8
9import transaction
10import unittest
11
12from windmill.authoring import WindmillTestClient
13
14from zope.component import getUtility
15
16from canonical.launchpad.ftests import login, logout
17from canonical.launchpad.windmill.testing.lpuser import LaunchpadUser
18from lp.registry.interfaces.distribution import IDistributionSet
19from lp.soyuz.windmill.testing import SoyuzWindmillLayer
20from lp.testing import TestCaseWithFactory
21
22WAIT_PAGELOAD = u'30000'
23WAIT_ELEMENT_COMPLETE = u'30000'
24WAIT_CHECK_CHANGE = u'1000'
25ADD_ACCESS_LINK = u'//a[@class="js-action sprite add"]'
26CHOOSE_SUBSCRIBER_LINK = u'//a[@id="show-widget-field-subscriber"]'
27SUBSCRIBER_SEARCH_FIELD = (
28 u'//div[@id="yui-pretty-overlay-modal"]//input[@name="search"]')
29SUBSCRIBER_SEARCH_BUTTON = u'//div[@id="yui-pretty-overlay-modal"]//button'
30FIRST_SUBSCRIBER_RESULT = (
31 u'//div[@id="yui-pretty-overlay-modal"]'
32 '//span[@class="yui-picker-result-title"]')
33
34
35class TestArchiveSubscribersIndex(TestCaseWithFactory):
36
37 layer = SoyuzWindmillLayer
38
39 def setUp(self):
40 """Create a private PPA."""
41 super(TestArchiveSubscribersIndex, self).setUp()
42
43 user = self.factory.makePerson(
44 name='joe-bloggs', email='joe@example.com', password='joe',
45 displayname='Joe Bloggs')
46 ubuntu = getUtility(IDistributionSet)['ubuntu']
47 self.ppa = self.factory.makeArchive(
48 owner=user, name='myppa', distribution=ubuntu)
49
50 login('foo.bar@canonical.com')
51 self.ppa.private = True
52 self.ppa.buildd_secret = 'secret'
53 logout()
54 transaction.commit()
55
56 self.lpuser = LaunchpadUser(
57 'Joe Bloggs', 'joe@example.com', 'joe')
58
59 def test_add_subscriber(self):
60 """Test adding a private PPA subscriber.."""
61 client = WindmillTestClient('Adding private PPA subscribers.')
62
63 self.lpuser.ensure_login(client)
64
65 client.open(url='http://launchpad.dev:8085/~joe-bloggs/'
66 '+archive/myppa/+subscriptions')
67 client.waits.forPageLoad(timeout=WAIT_PAGELOAD)
68
69 # Click on the JS add access action.
70 client.waits.forElement(xpath=ADD_ACCESS_LINK)
71 client.click(xpath=ADD_ACCESS_LINK)
72
73 # Open the picker, search for 'launchpad' and choose the first
74 # result
75 client.click(xpath=CHOOSE_SUBSCRIBER_LINK)
76 client.type(xpath=SUBSCRIBER_SEARCH_FIELD, text='launchpad')
77 client.click(xpath=SUBSCRIBER_SEARCH_BUTTON)
78
79 client.waits.forElement(xpath=FIRST_SUBSCRIBER_RESULT)
80 client.click(xpath=FIRST_SUBSCRIBER_RESULT)
81
82 # Add the new subscriber.
83 client.click(id='field.actions.add')
84 client.waits.forPageLoad(timeout=WAIT_PAGELOAD)
85
86 # And verify that the correct informational message is displayed.
87 # It would be nice if we could use ... here.
88 client.asserts.assertText(
89 xpath=u'//div[@class="informational message"]',
90 validator='You have granted access for Launchpad Developers '
91 'to install software from PPA named myppa for Joe '
92 'Bloggs. Members of Launchpad Developers will be '
93 'notified of the access via email.')
94
95def test_suite():
96 return unittest.TestLoader().loadTestsFromName(__name__)