Merge lp:~jcsackett/launchpad/unknown-blueprints-service-597738 into lp:launchpad
- unknown-blueprints-service-597738
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11571 |
Proposed branch: | lp:~jcsackett/launchpad/unknown-blueprints-service-597738 |
Merge into: | lp:launchpad |
Prerequisite: | lp:~jcsackett/launchpad/deprecate-remaining-official-bools |
Diff against target: |
914 lines (+464/-59) 13 files modified
lib/lp/blueprints/browser/configure.zcml (+4/-8) lib/lp/blueprints/browser/specificationtarget.py (+82/-7) lib/lp/blueprints/browser/tests/test_specificationtarget.py (+127/-3) lib/lp/blueprints/stories/blueprints/xx-creation.txt (+71/-29) lib/lp/blueprints/stories/blueprints/xx-productseries.txt (+12/-0) lib/lp/blueprints/stories/standalone/xx-batching.txt (+19/-5) lib/lp/blueprints/stories/standalone/xx-index.txt (+14/-0) lib/lp/blueprints/stories/standalone/xx-overview.txt (+11/-0) lib/lp/blueprints/stories/standalone/xx-views.txt (+21/-4) lib/lp/blueprints/templates/unknown-specs.pt (+93/-0) lib/lp/registry/browser/distribution.py (+7/-0) lib/lp/registry/browser/product.py (+1/-1) lib/lp/registry/browser/tests/pillar-views.txt (+2/-2) |
To merge this branch: | bzr merge lp:~jcsackett/launchpad/unknown-blueprints-service-597738 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | ui | Approve | |
Guilherme Salgado (community) | ui* | Approve | |
Brad Crittenden (community) | code | Approve | |
Registry Administrators | ui | Pending | |
Review via email: mp+35018@code.launchpad.net |
Commit message
Updates the blueprints service page to use the blueprints_usage enum and display relevant data for each setting.
Description of the change
= Summary =
Updates the blueprints service to present relevant data for the UNKNOWN, EXTERNAL, and NOT_APPLICABLE settings of the blueprints_usage enum.
== Proposed fix ==
Modify the template and/or view to detect the usage status and present the correct template/info.
== Pre-implementation notes ==
Spoke with Curtis Hovey (sinzui) and Brad Crittenden (bac).
== Implementation details ==
The HasSpecificatio
The new template presents slightly different information based on the enum case, and provides some basic controls if relevant (e.g. configure blueprints if the user has edit permissions).
== Tests ==
bin/test -vvcm lp.blueprints.
== Demo and Q/A ==
In Launchpad.dev, signed in as admin, go to http://
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
Lint output corrupted by merged dependent branch.
j.c.sackett (jcsackett) wrote : | # |
Brad Crittenden (bac) wrote : | # |
Hi Jon,
Thanks for working on this branch. I was unaware of the added
complexity of the whole template/
I like the work you've done here modulo the on-going discussion with
Gary about a better approach to the template swapping. I'm marking
the MP 'needs information' so I can track the change you come up with,
but the branch is very close.
You mentioned lint being confused by the dependency on another
branch. Even so, please ensure these are not for real:
./lib/lp/
251: E301 expected 1 blank line, found 0
254: E301 expected 1 blank line, found 0
./lib/lp/
38: Line has trailing whitespace.
./lib/lp/
582: source exceeds 78 characters.
584: source exceeds 78 characters.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -15,6 +15,7 @@
>
> from operator import itemgetter
>
> +from z3c.ptcompat import ViewPageTemplat
> from zope.component import queryMultiAdapter
>
> from canonical.config import config
> @@ -32,6 +33,8 @@
> Link,
> )
> from canonical.
> +from lp.app.enums import service_
> +from lp.app.
> from lp.blueprints.
> SpecificationFi
> SpecificationSort,
> @@ -133,6 +136,50 @@
> is_sprint = False
> has_drivers = False
>
> + # Templates for the various conditions of blueprints:
> + # * On Launchpad
> + # * External
> + # * Disabled
> + # * Unknown
> + default_template = ViewPageTemplat
> + '../templates/
> + not_launchpad_
> + '../templates/
> +
> + @property
> + def template(self):
> + # If the template has been defined in the zcml, use that, as this
> + # isn't the base service page for blueprints.
> + if hasattr(self, 'index'):
As we discussed on IRC, you should use 'safe_hasattr' from
canonical.
see if there is a more obvious technique for this. Sadly I cannot
offer any other solution.
> + return super(HasSpecif
> +
> + # Sprints and Persons don't have a usage enum for blueprints, so we
> + # have to fallback to the default.
> + if (ISprint.
> + or IPerson.
> + return self.default_
> +
> + # ProjectGroups are a special case, as their products may be a
> + # combination of usage settings. Use the default, since it handles
> + # fetching anything that's set for ProjectGroups, and it's not correct
> + # to say anything about the ProjectGroup's usage.
> + if IProjectGroup.
> + return self.default_
> +
> + # If specific...
Curtis Hovey (sinzui) wrote : | # |
On Fri, 2010-09-10 at 15:59 +0000, Brad Crittenden wrote:
> > + @property
> > + def template(self):
> > + # If the template has been defined in the zcml, use that,
> as this
> > + # isn't the base service page for blueprints.
> > + if hasattr(self, 'index'):
>
> As we discussed on IRC, you should use 'safe_hasattr' from
> canonical.
> see if there is a more obvious technique for this. Sadly I cannot
> offer any other solution.
We can abandon safe_hasattr when lpnet is on lucid. This issue was fixed
several years ago in pythong 2.6
Brad Crittenden (bac) wrote : | # |
Good changes Jon. Please alphabetize the imports where you added safe_hasattr.
Guilherme Salgado (salgado) wrote : | # |
The UI changes look good to me, although it's confusing that the new template (lib/lp/
Curtis Hovey (sinzui) wrote : | # |
Gentleman, I think the reviewers and developers need to consider why we are making this change, and what we are really changing.
Our goal is to communicate to the user where certain kinds of activities are performed--which site does the user need to visit to complete his goal. Launchpad blueprints is a branded service that provides specifications and documentation. The page needs to be talking about "specifications and documentation" because other sites, most often wikis, do not use the term blueprints. We want to allow the users to provide a URL when they choose external (maybe repurpose the wiki field). The page is not really stating that Launchpad Blueprints allows the user to track specifications and documentation...I see no reason why I would turn the feature on since it does not have a sentence stating the basics of what it is for. Specification itself may be too technical. This app is essentially about planning and documenting features.
My other concern is about what has specifications--a SpecificationTa
jcsackett, Salgado, take a look at the branch again from the perspective of kinds of users who are interested in feature planning.
j.c.sackett (jcsackett) wrote : | # |
I just discovered I missed comments within the diff; sorry for not replying on Friday.
> If the enum is wrong, how will the user get here? The tab will be
> disabled, so we assume they have a direct URL?
I'm not sure the tab will be disabled--if no blueprints existed, and the enum said "EXTERNAL" or something, you could still get to this view so the "${Pillar} does not use blueprints" could be displayed. In this instance if it's set to EXTERNAL but people have been using the blueprints, we continue to display the blueprints information when you get here.
The involvement menu link is disabled, but the tab along the top of the page is still there, and of course nothing stops someone from going to blueprints.
Now, I'm open to debate on whether we should just turn off the feature regardless of whether or not blueprints are already being used--I erred on the side of not allowing someone to just delete a bunch of work, but that might not be the decision we want to make here.
>> self.verify_
>
> You didn't introduce it, but please fix the typo of s/verify_
Uhm, not sure what you mean--looks like it already is verify_involvement?
>> def test_adaptable_
>> @@ -87,6 +92,63 @@
>> '<div id="involvement" class="portlet involvement">' in view())
>>
>>
>> +class TestHasSpecific
>> + """Tests the selection of templates based on blueprints usage."""
>> +
>> + layer = DatabaseFunctio
>> +
>> + def setUp(self):
>> + super(TestHasSp
>> + self.user = self.factory.
>> + self.product = self.factory.
>> + self.naked_product = removeSecurityP
>> + login_person(
>
> While I'm not a prude, I must ask why all of the nakedness here?
> Couldn't you just make self.user be the owner of the product and
> proceed fully clothed?
I wanted to test what a non-owner user has happen here; that may be the same thing as self.user, but it may not--I've been bitten by using owners/admins in the past.
> Why use 'specifications' just the once? The page says 'blueprints'
> six times and it is unclear that specifications and blueprints are the
> same from the context.
>
> Should we be more frank:
>
> <project> tracks specifications somewhere besides Launchpad, but we
> don't know where. You should check with the project maintainers for
> the location.
>
> I'm just throwing that out for thought. I'm not sure I even like the idea.
I'm playing with this right now related to the UI review; neither blueprints nor specifications are totally right for the copy, per Curtis's comments.
But consider your comments thrown into the mix.
> Thanks for the additional drive-by fixes.
I do what I can. :-P
Curtis Hovey (sinzui) wrote : | # |
On Mon, 2010-09-13 at 19:54 +0000, j.c.sackett wrote:
>
> The involvement menu link is disabled, but the tab along the top of
> the page is still there, and of course nothing stops someone from
> going to blueprints.
>
> Now, I'm open to debate on whether we should just turn off the feature
> regardless of whether or not blueprints are already being used--I
> erred on the side of not allowing someone to just delete a bunch of
> work, but that might not be the decision we want to make here.
The complaint from project owners is that users created blueprints on
Launchpad instead of the upstream wiki. The project owner can enable
blueprints if he chooses to. Blueprints should be off until enabled.
I went to firefox in lp.dev and saw blueprints on. As the owner of the
project I decided to turn blueprints off, so I choses the configure
link, and was surprised that Blueprints was off. More over, Saving the
state of unknown did not disable blueprints. The state of the app does
not work like answers. Remember, we are working on other applications to
ensure all apps have the same kind of behaviours.
--
__Curtis C. Hovey_________
http://
j.c.sackett (jcsackett) wrote : | # |
> The complaint from project owners is that users created blueprints on
> Launchpad instead of the upstream wiki. The project owner can enable
> blueprints if he chooses to. Blueprints should be off until enabled.
>
> I went to firefox in lp.dev and saw blueprints on. As the owner of the
> project I decided to turn blueprints off, so I choses the configure
> link, and was surprised that Blueprints was off. More over, Saving the
> state of unknown did not disable blueprints. The state of the app does
> not work like answers. Remember, we are working on other applications to
> ensure all apps have the same kind of behaviours.
I can buy this argument, totally. I've removed the conditional that prioritizes the existence of blueprints over the status of the enum.
j.c.sackett (jcsackett) wrote : | # |
> Our goal is to communicate to the user where certain kinds of activities are
> performed--which site does the user need to visit to complete his goal.
> Launchpad blueprints is a branded service that provides specifications and
> documentation. The page needs to be talking about "specifications and
> documentation" because other sites, most often wikis, do not use the term
> blueprints.
Given this, I've updated the template to refer to the notion of documentation, and feature planning. In the instance where blueprints is set active, I've left the template alone, as the process of setting it up tells the user that Blueprints is a branded way that Launchpad does feature planning and documentation.
> We want to allow the users to provide a URL when they choose
> external (maybe repurpose the wiki field).
Based on our prior conversation I thought we were going to simply default to "There is a wiki for this project, which may be used for this purpose," with a link to the wiki if it exists, rather than introducing an extra db field to track an external specification tracking url.
I do think it may be worthwhile to circle back and add that field and get that information (for blueprints, answers, and translations) so we can provide a specific external place, but I think those changes are out of scope for this branch.
> The page is not really stating that
> Launchpad Blueprints allows the user to track specifications and
> documentation...I see no reason why I would turn the feature on since it does
> not have a sentence stating the basics of what it is for.
Agreed; I've updated what the page says to address this.
An incremental diff with these changes is attached.
I'll be addressing your other concerns re: specificationtarget next.
1 | === modified file 'lib/lp/blueprints/browser/specificationtarget.py' |
2 | --- lib/lp/blueprints/browser/specificationtarget.py 2010-09-13 20:21:10 +0000 |
3 | +++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 15:49:09 +0000 |
4 | @@ -26,6 +26,7 @@ |
5 | canonical_url, |
6 | LaunchpadView, |
7 | ) |
8 | +from canonical.launchpad.webapp.authorization import check_permission |
9 | from canonical.launchpad.webapp.batching import BatchNavigator |
10 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
11 | from canonical.launchpad.webapp.menu import ( |
12 | @@ -219,6 +220,15 @@ |
13 | size=config.launchpad.default_batch_size) |
14 | |
15 | @property |
16 | + def can_configure_blueprints(self): |
17 | + """Can the user configure blueprints for the `ISpecificationTarget`.""" |
18 | + target = self.context |
19 | + if IProduct.providedBy(target) or IDistribution.providedBy(target): |
20 | + return check_permission('launchpad.Edit', self.context) |
21 | + else: |
22 | + return False |
23 | + |
24 | + @property |
25 | def label(self): |
26 | mapping = {'name': self.context.displayname} |
27 | if self.is_person: |
28 | |
29 | === modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py' |
30 | --- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-09 18:11:41 +0000 |
31 | +++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 15:47:58 +0000 |
32 | @@ -18,7 +18,10 @@ |
33 | login_person, |
34 | TestCaseWithFactory, |
35 | ) |
36 | -from lp.testing.views import create_view |
37 | +from lp.testing.views import ( |
38 | + create_view, |
39 | + create_initialized_view, |
40 | + ) |
41 | |
42 | |
43 | class TestRegisterABlueprintButtonView(TestCaseWithFactory): |
44 | @@ -149,6 +152,38 @@ |
45 | view.template.filename) |
46 | |
47 | |
48 | +class TestHasSpecificationsConfiguration(TestCaseWithFactory): |
49 | + |
50 | + layer = DatabaseFunctionalLayer |
51 | + |
52 | + def test_cannot_configure_blueprints_product_no_edit_permission(self): |
53 | + product = self.factory.makeProduct() |
54 | + view = create_initialized_view(product, '+specs') |
55 | + self.assertEqual(False, view.can_configure_blueprints) |
56 | + |
57 | + def test_can_configure_blueprints_product_with_edit_permission(self): |
58 | + product = self.factory.makeProduct() |
59 | + login_person(product.owner) |
60 | + view = create_initialized_view(product, '+specs') |
61 | + self.assertEqual(True, view.can_configure_blueprints) |
62 | + |
63 | + def test_cannot_configure_blueprints_distribution_no_edit_permission(self): |
64 | + distribution = self.factory.makeDistribution() |
65 | + view = create_initialized_view(distribution, '+specs') |
66 | + self.assertEqual(False, view.can_configure_blueprints) |
67 | + |
68 | + def test_can_configure_blueprints_distribution_with_edit_permission(self): |
69 | + distribution = self.factory.makeDistribution() |
70 | + login_person(distribution.owner) |
71 | + view = create_initialized_view(distribution, '+specs') |
72 | + self.assertEqual(True, view.can_configure_blueprints) |
73 | + |
74 | + def test_cannot_configure_blueprints_projectgroup_with_edit_permission(self): |
75 | + project_group = self.factory.makeProject() |
76 | + login_person(project_group.owner) |
77 | + view = create_initialized_view(project_group, '+specs') |
78 | + self.assertEqual(False, view.can_configure_blueprints) |
79 | + |
80 | def test_suite(): |
81 | suite = unittest.TestSuite() |
82 | suite.addTest(unittest.TestLoader().loadTestsFromName(__name__)) |
83 | |
84 | === modified file 'lib/lp/blueprints/templates/unknown-specs.pt' |
85 | --- lib/lp/blueprints/templates/unknown-specs.pt 2010-09-10 16:34:26 +0000 |
86 | +++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 16:02:38 +0000 |
87 | @@ -11,49 +11,47 @@ |
88 | |
89 | <div metal:fill-slot="main" |
90 | tal:define="specs view/specs; |
91 | - has_any_specs view/has_any_specifications"> |
92 | - |
93 | - <div tal:condition="view/context/blueprints_usage/enumvalue:EXTERNAL"> |
94 | - <strong><tal:project replace="view/context/displayname" /> does not use Launchpad |
95 | - for specification tracking.</strong> |
96 | - </div> |
97 | - |
98 | - <div tal:condition="view/context/blueprints_usage/enumvalue:NOT_APPLICABLE"> |
99 | - <strong><tal:project replace="view/context/displayname" /> does not track |
100 | - specifications.</strong> |
101 | - </div> |
102 | - |
103 | - <div tal:condition="view/context/blueprints_usage/enumvalue:UNKNOWN"> |
104 | - <strong> |
105 | - Launchpad does not know how |
106 | - <tal:project replace="view/context/displayname" /> uses specifications. |
107 | - </strong> |
108 | - </div> |
109 | - |
110 | - <div tal:condition="view/context/wikiurl"> |
111 | - <p><tal:project replace="view/context/displayname" /> has a wiki, which |
112 | - may be used for specifications.</p> |
113 | - |
114 | - <p> |
115 | + has_any_specs view/has_any_specifications; |
116 | + blueprints_usage view/context/blueprints_usage"> |
117 | + <div class="top-portlet"> |
118 | + <div id="specs-unknown"> |
119 | + <strong> |
120 | + <p tal:condition="blueprints_usage/enumvalue:EXTERNAL"> |
121 | + <tal:project replace="view/context/displayname" /> does not use Launchpad |
122 | + for planning or documentation. |
123 | + </p> |
124 | + <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE"> |
125 | + <tal:project replace="view/context/displayname" /> does not track |
126 | + use feature planning or documentation. |
127 | + </p> |
128 | + <p tal:condition="blueprints_usage/enumvalue:UNKNOWN"> |
129 | + Launchpad does not know how |
130 | + <tal:project replace="view/context/displayname" /> tracks feature |
131 | + planning or documentation. |
132 | + </p> |
133 | + </strong> |
134 | + |
135 | + <p id="wiki-fallback" |
136 | + tal:define="wiki view/context/wikiurl" |
137 | + tal:condition="wiki"> |
138 | + <tal:project replace="view/context/displayname" /> has a wiki, which |
139 | + may be used for feature plannning and documentation.<br /> |
140 | <a tal:attributes="href view/context/wikiurl"> |
141 | - <tal:project replace="view/context/displayname" /> wiki |
142 | + <tal:project replace="view/context/displayname" /> wiki |
143 | </a> |
144 | </p> |
145 | - |
146 | </div> |
147 | - <ul class="horizontal"> |
148 | - <li> |
149 | - <a class="info sprite" |
150 | - href="https://help.launchpad.net/BlueprintDocumentation"> |
151 | - Read more about tracking blueprints |
152 | - </a> |
153 | - </li> |
154 | - </ul> |
155 | - <ul class="horiztonal"> |
156 | - <li> |
157 | - <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" /> |
158 | - </li> |
159 | - </ul> |
160 | + |
161 | + <p id="configure-support" |
162 | + tal:condition="view/can_configure_blueprints"> |
163 | + Launchpad Blueprints allow your project to track feature planning and |
164 | + documentation.<br /> |
165 | + <a class="info sprite" |
166 | + href="https://help.launchpad.net/BlueprintDocumentation"> |
167 | + Read more about tracking feature planning with blueprints</a><br /> |
168 | + <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" /><br /> |
169 | + </p> |
170 | + </div> |
171 | </div> |
172 | </body> |
173 | </html> |
j.c.sackett (jcsackett) wrote : | # |
> My other concern is about what has specifications--a SpecificationTa
> reviewed the bugs branch a few days ago and saw only IProduct was supports
> well.
Valid; I've done some digging and have some explanations below.
> A BugTarget may be a product, projectgroup, distribution, and
> distributionsou
> everyone needs to visit an example of each to ensure the message is correct
> and the actions that can be takes are presented. eg, a project group's use of
> an app is implicit in the settings of its products.
So, I think products are now handled right.
Project groups are tricky--right now I have it defaulting to the old style, so that any blueprints made for any project will be shown. Given how the earlier question of whether or not to continue showing blueprints if has_any_
Distributions were mostly handled right; the issue with say, Kubuntu was the configure_
DSPs aren't valid for blueprints; it's always NOT_APPLICABLE and the links are all disabled. You can't even get to it by going to blueprints.
Attached is a diff of this round of changes.
1 | === modified file 'lib/lp/blueprints/browser/specificationtarget.py' |
2 | --- lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 15:50:16 +0000 |
3 | +++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 19:02:37 +0000 |
4 | @@ -138,6 +138,7 @@ |
5 | is_project = False |
6 | is_series = False |
7 | is_sprint = False |
8 | + has_wiki = False |
9 | has_drivers = False |
10 | |
11 | # Templates for the various conditions of blueprints: |
12 | @@ -153,9 +154,9 @@ |
13 | @property |
14 | def template(self): |
15 | # Check for the magical "index" added by the browser:page template |
16 | - # machinery. If it exists this is actually the |
17 | + # machinery. If it exists this is actually the |
18 | # zope.app.pagetemplate.simpleviewclass.simple class that is magically |
19 | - # mixed in by the browser:page zcml directive the template defined in |
20 | + # mixed in by the browser:page zcml directive the template defined in |
21 | # the directive should be used. |
22 | if safe_hasattr(self, 'index'): |
23 | return super(HasSpecificationsView, self).template |
24 | @@ -167,11 +168,15 @@ |
25 | return self.default_template |
26 | |
27 | # ProjectGroups are a special case, as their products may be a |
28 | - # combination of usage settings. Use the default, since it handles |
29 | - # fetching anything that's set for ProjectGroups, and it's not correct |
30 | - # to say anything about the ProjectGroup's usage. |
31 | + # combination of usage settings. To deal with this, check all |
32 | + # products, and if a product has LAUNCHPAD for the enum, the |
33 | + # group does; if not, assume external. |
34 | if IProjectGroup.providedBy(self.context): |
35 | - return self.default_template |
36 | + for product in self.context.products: |
37 | + if service_uses_launchpad(product.blueprints_usage): |
38 | + return self.default_template |
39 | + else: |
40 | + return self.not_launchpad_template |
41 | |
42 | # Otherwise, determine usage and provide the correct template. |
43 | service_usage = IServiceUsage(self.context) |
44 | @@ -188,14 +193,19 @@ |
45 | def initialize(self): |
46 | if IPerson.providedBy(self.context): |
47 | self.is_person = True |
48 | - elif (IDistribution.providedBy(self.context) or |
49 | - IProduct.providedBy(self.context)): |
50 | - self.is_target = True |
51 | - self.is_pillar = True |
52 | + elif IDistribution.providedBy(self.context): |
53 | + self.is_target = True |
54 | + self.is_pillar = True |
55 | + self.show_series = True |
56 | + elif IProduct.providedBy(self.context): |
57 | + self.is_target = True |
58 | + self.is_pillar = True |
59 | + self.has_wiki = True |
60 | self.show_series = True |
61 | elif IProjectGroup.providedBy(self.context): |
62 | self.is_project = True |
63 | self.is_pillar = True |
64 | + self.has_wiki = True |
65 | self.show_target = True |
66 | self.show_series = True |
67 | elif IProjectGroupSeries.providedBy(self.context): |
68 | @@ -221,7 +231,8 @@ |
69 | |
70 | @property |
71 | def can_configure_blueprints(self): |
72 | - """Can the user configure blueprints for the `ISpecificationTarget`.""" |
73 | + """Can the user configure blueprints for the `ISpecificationTarget`. |
74 | + """ |
75 | target = self.context |
76 | if IProduct.providedBy(target) or IDistribution.providedBy(target): |
77 | return check_permission('launchpad.Edit', self.context) |
78 | |
79 | === modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py' |
80 | --- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 15:50:16 +0000 |
81 | +++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 19:01:23 +0000 |
82 | @@ -13,6 +13,7 @@ |
83 | ISpecificationTarget, |
84 | ) |
85 | from lp.app.enums import ServiceUsage |
86 | +from lp.blueprints.browser.specificationtarget import HasSpecificationsView |
87 | from lp.blueprints.publisher import BlueprintsLayer |
88 | from lp.testing import ( |
89 | login_person, |
90 | @@ -103,53 +104,65 @@ |
91 | def setUp(self): |
92 | super(TestHasSpecificationsTemplates, self).setUp() |
93 | self.user = self.factory.makePerson() |
94 | - self.product = self.factory.makeProduct() |
95 | - self.naked_product = removeSecurityProxy(self.product) |
96 | login_person(self.user) |
97 | |
98 | - def test_not_configured(self): |
99 | - self.naked_product.blueprints_usage = ServiceUsage.UNKNOWN |
100 | - view = create_view( |
101 | - self.product, |
102 | - '+specs', |
103 | - layer=BlueprintsLayer, |
104 | - principal=self.user) |
105 | - self.assertEqual( |
106 | - view.not_launchpad_template.filename, |
107 | - view.template.filename) |
108 | - |
109 | - def test_external(self): |
110 | - self.naked_product.blueprints_usage = ServiceUsage.EXTERNAL |
111 | - view = create_view( |
112 | - self.product, |
113 | - '+specs', |
114 | - layer=BlueprintsLayer, |
115 | - principal=self.user) |
116 | - self.assertEqual( |
117 | - view.not_launchpad_template.filename, |
118 | - view.template.filename) |
119 | - |
120 | - def test_not_applicable(self): |
121 | - self.naked_product.blueprints_usage = ServiceUsage.NOT_APPLICABLE |
122 | - view = create_view( |
123 | - self.product, |
124 | - '+specs', |
125 | - layer=BlueprintsLayer, |
126 | - principal=self.user) |
127 | - self.assertEqual( |
128 | - view.not_launchpad_template.filename, |
129 | - view.template.filename) |
130 | - |
131 | - def test_on_launchpad(self): |
132 | - self.naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD |
133 | - view = create_view( |
134 | - self.product, |
135 | - '+specs', |
136 | - layer=BlueprintsLayer, |
137 | - principal=self.user) |
138 | - self.assertEqual( |
139 | - view.default_template.filename, |
140 | - view.template.filename) |
141 | + def _test_templates_for_configuration(self, target, context=None): |
142 | + if context is None: |
143 | + context = target |
144 | + naked_target = removeSecurityProxy(target) |
145 | + test_configurations = [ |
146 | + ServiceUsage.UNKNOWN, |
147 | + ServiceUsage.EXTERNAL, |
148 | + ServiceUsage.NOT_APPLICABLE, |
149 | + ServiceUsage.LAUNCHPAD, |
150 | + ] |
151 | + correct_templates = [ |
152 | + HasSpecificationsView.not_launchpad_template.filename, |
153 | + HasSpecificationsView.not_launchpad_template.filename, |
154 | + HasSpecificationsView.not_launchpad_template.filename, |
155 | + HasSpecificationsView.default_template.filename, |
156 | + ] |
157 | + used_templates = list() |
158 | + for config in test_configurations: |
159 | + naked_target.blueprints_usage = config |
160 | + view = create_view( |
161 | + context, |
162 | + '+specs', |
163 | + layer=BlueprintsLayer, |
164 | + principal=self.user) |
165 | + used_templates.append(view.template.filename) |
166 | + self.assertEqual(correct_templates, used_templates) |
167 | + |
168 | + def test_product(self): |
169 | + product = self.factory.makeProduct() |
170 | + self._test_templates_for_configuration(product) |
171 | + |
172 | + def test_product_series(self): |
173 | + product = self.factory.makeProduct() |
174 | + product_series = self.factory.makeProductSeries(product=product) |
175 | + self._test_templates_for_configuration( |
176 | + target=product, |
177 | + context=product_series) |
178 | + |
179 | + def test_distribution(self): |
180 | + distribution = self.factory.makeDistribution() |
181 | + self._test_templates_for_configuration(distribution) |
182 | + |
183 | + def test_distroseries(self): |
184 | + distribution = self.factory.makeDistribution() |
185 | + distro_series = self.factory.makeDistroSeries( |
186 | + distribution=distribution) |
187 | + self._test_templates_for_configuration( |
188 | + target=distribution, |
189 | + context=distro_series) |
190 | + |
191 | + def test_projectgroup(self): |
192 | + project = self.factory.makeProject() |
193 | + product1 = self.factory.makeProduct(project=project) |
194 | + product2 = self.factory.makeProduct(project=project) |
195 | + self._test_templates_for_configuration( |
196 | + target=product1, |
197 | + context=project) |
198 | |
199 | |
200 | class TestHasSpecificationsConfiguration(TestCaseWithFactory): |
201 | @@ -167,7 +180,7 @@ |
202 | view = create_initialized_view(product, '+specs') |
203 | self.assertEqual(True, view.can_configure_blueprints) |
204 | |
205 | - def test_cannot_configure_blueprints_distribution_no_edit_permission(self): |
206 | + def test_cant_configure_blueprints_distribution_no_edit_permission(self): |
207 | distribution = self.factory.makeDistribution() |
208 | view = create_initialized_view(distribution, '+specs') |
209 | self.assertEqual(False, view.can_configure_blueprints) |
210 | @@ -178,12 +191,13 @@ |
211 | view = create_initialized_view(distribution, '+specs') |
212 | self.assertEqual(True, view.can_configure_blueprints) |
213 | |
214 | - def test_cannot_configure_blueprints_projectgroup_with_edit_permission(self): |
215 | + def test_cannot_configure_blueprints_projectgroup(self): |
216 | project_group = self.factory.makeProject() |
217 | login_person(project_group.owner) |
218 | view = create_initialized_view(project_group, '+specs') |
219 | self.assertEqual(False, view.can_configure_blueprints) |
220 | |
221 | + |
222 | def test_suite(): |
223 | suite = unittest.TestSuite() |
224 | suite.addTest(unittest.TestLoader().loadTestsFromName(__name__)) |
225 | |
226 | === modified file 'lib/lp/blueprints/templates/unknown-specs.pt' |
227 | --- lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 16:03:51 +0000 |
228 | +++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 19:07:36 +0000 |
229 | @@ -9,29 +9,38 @@ |
230 | |
231 | <body> |
232 | |
233 | -<div metal:fill-slot="main" |
234 | - tal:define="specs view/specs; |
235 | - has_any_specs view/has_any_specifications; |
236 | - blueprints_usage view/context/blueprints_usage"> |
237 | +<div metal:fill-slot="main"> |
238 | <div class="top-portlet"> |
239 | <div id="specs-unknown"> |
240 | <strong> |
241 | - <p tal:condition="blueprints_usage/enumvalue:EXTERNAL"> |
242 | - <tal:project replace="view/context/displayname" /> does not use Launchpad |
243 | - for planning or documentation. |
244 | - </p> |
245 | - <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE"> |
246 | - <tal:project replace="view/context/displayname" /> does not track |
247 | - use feature planning or documentation. |
248 | - </p> |
249 | - <p tal:condition="blueprints_usage/enumvalue:UNKNOWN"> |
250 | - Launchpad does not know how |
251 | - <tal:project replace="view/context/displayname" /> tracks feature |
252 | - planning or documentation. |
253 | - </p> |
254 | + <div tal:omit-tag tal:condition="view/is_project"> |
255 | + <p> |
256 | + Launchpad does not know how |
257 | + <tal:project replace="view/context/displayname" /> tracks feature |
258 | + planning or documentation. |
259 | + </p> |
260 | + </div> |
261 | + <div tal:omit-tag tal:condition="not:view/is_project"> |
262 | + <div tal:omit-tag tal:define="blueprints_usage view/context/blueprints_usage"> |
263 | + <p tal:condition="blueprints_usage/enumvalue:EXTERNAL"> |
264 | + <tal:project replace="view/context/displayname" /> does not use Launchpad |
265 | + for planning or documentation. |
266 | + </p> |
267 | + <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE"> |
268 | + <tal:project replace="view/context/displayname" /> does not track |
269 | + use feature planning or documentation. |
270 | + </p> |
271 | + <p tal:condition="blueprints_usage/enumvalue:UNKNOWN"> |
272 | + Launchpad does not know how |
273 | + <tal:project replace="view/context/displayname" /> tracks feature |
274 | + planning or documentation. |
275 | + </p> |
276 | + </div> |
277 | + </div> |
278 | </strong> |
279 | |
280 | - <p id="wiki-fallback" |
281 | + <div tal:omit-tag tal:condition="view/has_wiki"> |
282 | + <p id="wiki-fallback" |
283 | tal:define="wiki view/context/wikiurl" |
284 | tal:condition="wiki"> |
285 | <tal:project replace="view/context/displayname" /> has a wiki, which |
286 | @@ -40,8 +49,9 @@ |
287 | <tal:project replace="view/context/displayname" /> wiki |
288 | </a> |
289 | </p> |
290 | + </div> |
291 | </div> |
292 | - |
293 | + |
294 | <p id="configure-support" |
295 | tal:condition="view/can_configure_blueprints"> |
296 | Launchpad Blueprints allow your project to track feature planning and |
297 | |
298 | === modified file 'lib/lp/registry/browser/distribution.py' |
299 | --- lib/lp/registry/browser/distribution.py 2010-09-13 12:09:30 +0000 |
300 | +++ lib/lp/registry/browser/distribution.py 2010-09-14 19:01:42 +0000 |
301 | @@ -342,6 +342,7 @@ |
302 | 'announcements', |
303 | 'ppas', |
304 | 'configure_answers', |
305 | + 'configure_blueprints', |
306 | ] |
307 | |
308 | @enabled_with_permission('launchpad.Edit') |
309 | @@ -453,6 +454,12 @@ |
310 | summary = 'Allow users to ask questions on this project' |
311 | return Link('+edit', text, summary, icon='edit') |
312 | |
313 | + @enabled_with_permission('launchpad.Edit') |
314 | + def configure_blueprints(self): |
315 | + text = 'Configure blueprints' |
316 | + summary = 'Enable tracking of specifications and meetings' |
317 | + return Link('+edit', text, summary, icon='edit') |
318 | + |
319 | |
320 | class DerivativeDistributionOverviewMenu(DistributionOverviewMenu): |
321 |
Curtis Hovey (sinzui) wrote : | # |
On Tue, 2010-09-14 at 19:16 +0000, j.c.sackett wrote:
> Project groups are tricky--right now I have it defaulting to the old
> style, so that any blueprints made for any project will be shown.
> Given how the earlier question of whether or not to continue showing
> blueprints if has_any_
> appropriate to only turn this on if at least one product has LAUNCHPAD
> as its usage setting.
I do not this this rule is right. PillarView that renders the
Involvement Portlet and the "Register a Blueprint" link only shows the
link on a project group if a sub project has enabled it. The involvement
portlet is controlled by the pillar owners and its links are enabled to
attack contributors to the applications the pillar uses.
We do not want the Involvement portlet to contradict the app page, which
is the reason we need to disable apps for unknown, not applicable, and
external (most of the time). Answers uses the involvement portlet to
decide the state to ensure it never contradicts. I expect all the apps
do do something similar so that we have a DRY implementation.
involvement = getMultiAdapter(
if service_
# enable the listing.
--
__Curtis C. Hovey_________
http://
j.c.sackett (jcsackett) wrote : | # |
I like the multiAdapter strategy you showed and have put it in place of the for loop.
Guilherme Salgado (salgado) wrote : | # |
Hi Jon,
This looks good, and I only have a couple suggestions.
s/use// @ "[...] does not track use feature planning or documentation."
And for bonus points you could change the 'Configure blueprints' text according to the state the context is in. For example, in the UNKNOWN state, it could be changed to "Tell us how <project> tracks feature planning and documentation". Similarly, for the EXTERNAL (and maybe NOT_APPLICABLE as well?) state, it could be "Use Launchpad to track feature planning and documentation", although that's a bit of a lie as there are other things you can do by following that link (but then it should be a worthy tradeoff if our goal is to get people to use blueprints).
Curtis Hovey (sinzui) wrote : | # |
I think this is good to land, with the fixing of some link test. I suspect there are some other issues we will see when this feature is used with real data.
Thanks Salgado for seeing the problem in the enums. I see a similar issue with this link test: 'Enable tracking of specifications and meetings'. meetings, AKA sprints do not belong to projects or teams. They are always available because they are a cross-project/
Curtis Hovey (sinzui) wrote : | # |
Doh. My comment was intended to be UI approve.
Preview Diff
1 | === modified file 'lib/lp/blueprints/browser/configure.zcml' |
2 | --- lib/lp/blueprints/browser/configure.zcml 2010-07-27 17:17:59 +0000 |
3 | +++ lib/lp/blueprints/browser/configure.zcml 2010-09-17 14:23:57 +0000 |
4 | @@ -53,8 +53,7 @@ |
5 | name="+mdz-specs.csv" |
6 | attribute="mdzCsv"/> |
7 | <browser:page |
8 | - name="+specs" |
9 | - template="../templates/hasspecifications-specs.pt"/> |
10 | + name="+specs"/> |
11 | <browser:page |
12 | name="+portlet-latestspecs" |
13 | template="../templates/specificationtarget-portlet-latestspecs.pt"/> |
14 | @@ -500,8 +499,7 @@ |
15 | class="lp.blueprints.browser.specificationtarget.HasSpecificationsView" |
16 | permission="zope.Public"> |
17 | <browser:page |
18 | - name="+specs" |
19 | - template="../templates/hasspecifications-specs.pt"/> |
20 | + name="+specs"/> |
21 | </browser:pages> |
22 | <browser:page |
23 | for="lp.blueprints.interfaces.specificationtarget.ISpecificationGoal" |
24 | @@ -532,8 +530,7 @@ |
25 | class="lp.blueprints.browser.specificationtarget.HasSpecificationsView" |
26 | permission="zope.Public"> |
27 | <browser:page |
28 | - name="+specs" |
29 | - template="../templates/hasspecifications-specs.pt"/> |
30 | + name="+specs"/> |
31 | <browser:page |
32 | name="+portlet-latestspecs" |
33 | template="../templates/specificationtarget-portlet-latestspecs.pt"/> |
34 | @@ -583,8 +580,7 @@ |
35 | facet="specifications" |
36 | permission="zope.Public"> |
37 | <browser:page |
38 | - name="+specs" |
39 | - template="../templates/hasspecifications-specs.pt"/> |
40 | + name="+specs"/> |
41 | <browser:page |
42 | name="+portlet-latestspecs" |
43 | template="../templates/specificationtarget-portlet-latestspecs.pt"/> |
44 | |
45 | === modified file 'lib/lp/blueprints/browser/specificationtarget.py' |
46 | --- lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 23:42:27 +0000 |
47 | +++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-17 14:23:57 +0000 |
48 | @@ -15,7 +15,11 @@ |
49 | |
50 | from operator import itemgetter |
51 | |
52 | -from zope.component import queryMultiAdapter |
53 | +from z3c.ptcompat import ViewPageTemplateFile |
54 | +from zope.component import ( |
55 | + getMultiAdapter, |
56 | + queryMultiAdapter, |
57 | + ) |
58 | |
59 | from canonical.config import config |
60 | from canonical.launchpad import _ |
61 | @@ -25,13 +29,19 @@ |
62 | canonical_url, |
63 | LaunchpadView, |
64 | ) |
65 | +from canonical.launchpad.webapp.authorization import check_permission |
66 | from canonical.launchpad.webapp.batching import BatchNavigator |
67 | from canonical.launchpad.webapp.breadcrumb import Breadcrumb |
68 | from canonical.launchpad.webapp.menu import ( |
69 | enabled_with_permission, |
70 | Link, |
71 | ) |
72 | -from canonical.lazr.utils import smartquote |
73 | +from canonical.lazr.utils import ( |
74 | + safe_hasattr, |
75 | + smartquote, |
76 | + ) |
77 | +from lp.app.enums import service_uses_launchpad |
78 | +from lp.app.interfaces.launchpad import IServiceUsage |
79 | from lp.blueprints.interfaces.specification import ( |
80 | SpecificationFilter, |
81 | SpecificationSort, |
82 | @@ -131,21 +141,76 @@ |
83 | is_project = False |
84 | is_series = False |
85 | is_sprint = False |
86 | + has_wiki = False |
87 | has_drivers = False |
88 | |
89 | + # Templates for the various conditions of blueprints: |
90 | + # * On Launchpad |
91 | + # * External |
92 | + # * Disabled |
93 | + # * Unknown |
94 | + default_template = ViewPageTemplateFile( |
95 | + '../templates/hasspecifications-specs.pt') |
96 | + not_launchpad_template = ViewPageTemplateFile( |
97 | + '../templates/unknown-specs.pt') |
98 | + |
99 | + @property |
100 | + def template(self): |
101 | + # Check for the magical "index" added by the browser:page template |
102 | + # machinery. If it exists this is actually the |
103 | + # zope.app.pagetemplate.simpleviewclass.simple class that is magically |
104 | + # mixed in by the browser:page zcml directive the template defined in |
105 | + # the directive should be used. |
106 | + if safe_hasattr(self, 'index'): |
107 | + return super(HasSpecificationsView, self).template |
108 | + |
109 | + # Sprints and Persons don't have a usage enum for blueprints, so we |
110 | + # have to fallback to the default. |
111 | + if (ISprint.providedBy(self.context) |
112 | + or IPerson.providedBy(self.context)): |
113 | + return self.default_template |
114 | + |
115 | + # ProjectGroups are a special case, as their products may be a |
116 | + # combination of usage settings. To deal with this, check all |
117 | + # products via the involvment menu. |
118 | + if (IProjectGroup.providedBy(self.context) |
119 | + or IProjectGroupSeries.providedBy(self.context)): |
120 | + involvement = getMultiAdapter( |
121 | + (self.context, self.request), |
122 | + name='+get-involved') |
123 | + if service_uses_launchpad(involvement.blueprints_usage): |
124 | + return self.default_template |
125 | + else: |
126 | + return self.not_launchpad_template |
127 | + |
128 | + # Otherwise, determine usage and provide the correct template. |
129 | + service_usage = IServiceUsage(self.context) |
130 | + if service_uses_launchpad(service_usage.blueprints_usage): |
131 | + return self.default_template |
132 | + else: |
133 | + return self.not_launchpad_template |
134 | + |
135 | + def render(self): |
136 | + return self.template() |
137 | + |
138 | # XXX: jsk: 2007-07-12 bug=173972: This method might be improved by |
139 | # replacing the conditional execution with polymorphism. |
140 | def initialize(self): |
141 | if IPerson.providedBy(self.context): |
142 | self.is_person = True |
143 | - elif (IDistribution.providedBy(self.context) or |
144 | - IProduct.providedBy(self.context)): |
145 | - self.is_target = True |
146 | - self.is_pillar = True |
147 | + elif IDistribution.providedBy(self.context): |
148 | + self.is_target = True |
149 | + self.is_pillar = True |
150 | + self.show_series = True |
151 | + elif IProduct.providedBy(self.context): |
152 | + self.is_target = True |
153 | + self.is_pillar = True |
154 | + self.has_wiki = True |
155 | self.show_series = True |
156 | elif IProjectGroup.providedBy(self.context): |
157 | self.is_project = True |
158 | self.is_pillar = True |
159 | + self.has_wiki = True |
160 | self.show_target = True |
161 | self.show_series = True |
162 | elif IProjectGroupSeries.providedBy(self.context): |
163 | @@ -170,6 +235,16 @@ |
164 | size=config.launchpad.default_batch_size) |
165 | |
166 | @property |
167 | + def can_configure_blueprints(self): |
168 | + """Can the user configure blueprints for the `ISpecificationTarget`. |
169 | + """ |
170 | + target = self.context |
171 | + if IProduct.providedBy(target) or IDistribution.providedBy(target): |
172 | + return check_permission('launchpad.Edit', self.context) |
173 | + else: |
174 | + return False |
175 | + |
176 | + @property |
177 | def label(self): |
178 | mapping = {'name': self.context.displayname} |
179 | if self.is_person: |
180 | @@ -199,7 +274,7 @@ |
181 | 'distroseries', |
182 | 'direction_approved', |
183 | 'man_days', |
184 | - 'delivery' |
185 | + 'delivery', |
186 | ] |
187 | def dbschema(item): |
188 | """Format a dbschema sortably for a spreadsheet.""" |
189 | |
190 | === modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py' |
191 | --- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-15 00:07:54 +0000 |
192 | +++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-17 14:23:57 +0000 |
193 | @@ -1,8 +1,12 @@ |
194 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
195 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
196 | # GNU Affero General Public License version 3 (see the file LICENSE). |
197 | |
198 | __metaclass__ = type |
199 | |
200 | +import unittest |
201 | + |
202 | +from zope.security.proxy import removeSecurityProxy |
203 | + |
204 | from canonical.launchpad.webapp.batching import BatchNavigator |
205 | from canonical.launchpad.testing.pages import find_tag_by_id |
206 | from canonical.testing.layers import DatabaseFunctionalLayer |
207 | @@ -10,6 +14,8 @@ |
208 | IHasSpecifications, |
209 | ISpecificationTarget, |
210 | ) |
211 | +from lp.app.enums import ServiceUsage |
212 | +from lp.blueprints.browser.specificationtarget import HasSpecificationsView |
213 | from lp.blueprints.publisher import BlueprintsLayer |
214 | from lp.testing import ( |
215 | login_person, |
216 | @@ -53,7 +59,7 @@ |
217 | self.verify_view(context, 'sprints/%s' % context.name) |
218 | |
219 | |
220 | -class TestHasSpecificationsView(TestCaseWithFactory): |
221 | +class TestHasSpecificationsViewInvolvement(TestCaseWithFactory): |
222 | """Test specification menus links.""" |
223 | layer = DatabaseFunctionalLayer |
224 | |
225 | @@ -71,10 +77,17 @@ |
226 | |
227 | def test_specificationtarget(self): |
228 | context = self.factory.makeProduct(name='almond') |
229 | + naked_product = removeSecurityProxy(context) |
230 | + naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD |
231 | self.verify_involvment(context) |
232 | |
233 | def test_adaptable_to_specificationtarget(self): |
234 | + # A project should adapt to the products within to determine |
235 | + # involvment. |
236 | context = self.factory.makeProject(name='hazelnut') |
237 | + product = self.factory.makeProduct(project=context) |
238 | + naked_product = removeSecurityProxy(product) |
239 | + naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD |
240 | self.verify_involvment(context) |
241 | |
242 | def test_sprint(self): |
243 | @@ -128,4 +141,115 @@ |
244 | find_tag_by_id(content, 'upper-batch-nav-batchnav-next')['class']) |
245 | self.assertEqual('next', |
246 | find_tag_by_id(content, 'lower-batch-nav-batchnav-next')['class']) |
247 | - |
248 | + |
249 | + |
250 | +class TestHasSpecificationsTemplates(TestCaseWithFactory): |
251 | + """Tests the selection of templates based on blueprints usage.""" |
252 | + |
253 | + layer = DatabaseFunctionalLayer |
254 | + |
255 | + def setUp(self): |
256 | + super(TestHasSpecificationsTemplates, self).setUp() |
257 | + self.user = self.factory.makePerson() |
258 | + login_person(self.user) |
259 | + |
260 | + def _test_templates_for_configuration(self, target, context=None): |
261 | + if context is None: |
262 | + context = target |
263 | + naked_target = removeSecurityProxy(target) |
264 | + test_configurations = [ |
265 | + ServiceUsage.UNKNOWN, |
266 | + ServiceUsage.EXTERNAL, |
267 | + ServiceUsage.NOT_APPLICABLE, |
268 | + ServiceUsage.LAUNCHPAD, |
269 | + ] |
270 | + correct_templates = [ |
271 | + HasSpecificationsView.not_launchpad_template.filename, |
272 | + HasSpecificationsView.not_launchpad_template.filename, |
273 | + HasSpecificationsView.not_launchpad_template.filename, |
274 | + HasSpecificationsView.default_template.filename, |
275 | + ] |
276 | + used_templates = list() |
277 | + for config in test_configurations: |
278 | + naked_target.blueprints_usage = config |
279 | + view = create_view( |
280 | + context, |
281 | + '+specs', |
282 | + layer=BlueprintsLayer, |
283 | + principal=self.user) |
284 | + used_templates.append(view.template.filename) |
285 | + self.assertEqual(correct_templates, used_templates) |
286 | + |
287 | + def test_product(self): |
288 | + product = self.factory.makeProduct() |
289 | + self._test_templates_for_configuration(product) |
290 | + |
291 | + def test_product_series(self): |
292 | + product = self.factory.makeProduct() |
293 | + product_series = self.factory.makeProductSeries(product=product) |
294 | + self._test_templates_for_configuration( |
295 | + target=product, |
296 | + context=product_series) |
297 | + |
298 | + def test_distribution(self): |
299 | + distribution = self.factory.makeDistribution() |
300 | + self._test_templates_for_configuration(distribution) |
301 | + |
302 | + def test_distroseries(self): |
303 | + distribution = self.factory.makeDistribution() |
304 | + distro_series = self.factory.makeDistroSeries( |
305 | + distribution=distribution) |
306 | + self._test_templates_for_configuration( |
307 | + target=distribution, |
308 | + context=distro_series) |
309 | + |
310 | + def test_projectgroup(self): |
311 | + project = self.factory.makeProject() |
312 | + product1 = self.factory.makeProduct(project=project) |
313 | + product2 = self.factory.makeProduct(project=project) |
314 | + self._test_templates_for_configuration( |
315 | + target=product1, |
316 | + context=project) |
317 | + |
318 | + |
319 | +class TestHasSpecificationsConfiguration(TestCaseWithFactory): |
320 | + |
321 | + layer = DatabaseFunctionalLayer |
322 | + |
323 | + def test_cannot_configure_blueprints_product_no_edit_permission(self): |
324 | + product = self.factory.makeProduct() |
325 | + view = create_initialized_view(product, '+specs') |
326 | + self.assertEqual(False, view.can_configure_blueprints) |
327 | + |
328 | + def test_can_configure_blueprints_product_with_edit_permission(self): |
329 | + product = self.factory.makeProduct() |
330 | + login_person(product.owner) |
331 | + view = create_initialized_view(product, '+specs') |
332 | + self.assertEqual(True, view.can_configure_blueprints) |
333 | + |
334 | + def test_cant_configure_blueprints_distribution_no_edit_permission(self): |
335 | + distribution = self.factory.makeDistribution() |
336 | + view = create_initialized_view(distribution, '+specs') |
337 | + self.assertEqual(False, view.can_configure_blueprints) |
338 | + |
339 | + def test_can_configure_blueprints_distribution_with_edit_permission(self): |
340 | + distribution = self.factory.makeDistribution() |
341 | + login_person(distribution.owner) |
342 | + view = create_initialized_view(distribution, '+specs') |
343 | + self.assertEqual(True, view.can_configure_blueprints) |
344 | + |
345 | + def test_cannot_configure_blueprints_projectgroup(self): |
346 | + project_group = self.factory.makeProject() |
347 | + login_person(project_group.owner) |
348 | + view = create_initialized_view(project_group, '+specs') |
349 | + self.assertEqual(False, view.can_configure_blueprints) |
350 | + |
351 | + |
352 | +def test_suite(): |
353 | + suite = unittest.TestSuite() |
354 | + suite.addTest(unittest.TestLoader().loadTestsFromName(__name__)) |
355 | + return suite |
356 | + |
357 | + |
358 | +if __name__ == '__main__': |
359 | + unittest.TextTestRunner().run(test_suite()) |
360 | |
361 | === modified file 'lib/lp/blueprints/stories/blueprints/xx-creation.txt' |
362 | --- lib/lp/blueprints/stories/blueprints/xx-creation.txt 2010-08-26 02:30:06 +0000 |
363 | +++ lib/lp/blueprints/stories/blueprints/xx-creation.txt 2010-09-17 14:23:57 +0000 |
364 | @@ -1,7 +1,26 @@ |
365 | -= Creating Blueprints = |
366 | - |
367 | - |
368 | -== Introduction == |
369 | +Creating Blueprints |
370 | +=================== |
371 | + |
372 | +Set Up |
373 | +------ |
374 | +A number of tests in this need a product with blueprints enabled, so we'll |
375 | +enable them on bazaar, firefox, and jokosher. |
376 | + |
377 | + >>> from zope.component import getUtility |
378 | + >>> from lp.app.enums import ServiceUsage |
379 | + >>> from lp.registry.interfaces.product import IProductSet |
380 | + >>> login('admin@canonical.com') |
381 | + >>> bazaar = getUtility(IProductSet).getByName('bzr') |
382 | + >>> bazaar.blueprints_usage = ServiceUsage.LAUNCHPAD |
383 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
384 | + >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD |
385 | + >>> jokosher = getUtility(IProductSet).getByName('jokosher') |
386 | + >>> jokosher.blueprints_usage = ServiceUsage.LAUNCHPAD |
387 | + >>> transaction.commit() |
388 | + >>> logout() |
389 | + |
390 | +Introduction |
391 | +------------ |
392 | |
393 | Users can register blueprints from many locations in Launchpad. To start with, |
394 | it's possible to register a blueprint from the Blueprints home page. However, |
395 | @@ -15,7 +34,9 @@ |
396 | * a sprint |
397 | |
398 | |
399 | -== The blueprint registration form == |
400 | +The blueprint registration form |
401 | +------------------------------- |
402 | + |
403 | |
404 | Launchpad provides a dedicated form page for users to register blueprints. |
405 | Generally speaking, users can navigate to this form from each of the supported |
406 | @@ -28,13 +49,15 @@ |
407 | we'll use extra care when its necessary to demonstrate that they both exist. |
408 | |
409 | |
410 | -== Navigating to the blueprint registration form == |
411 | +Navigating to the blueprint registration form |
412 | +--------------------------------------------- |
413 | |
414 | We'll start by demonstrating that users can navigate to the blueprint |
415 | registration form from each of the supported locations. |
416 | |
417 | |
418 | -=== From the Blueprints home page === |
419 | +From the Blueprints home page |
420 | +............................. |
421 | |
422 | Starting from the Blueprints home page: |
423 | |
424 | @@ -46,7 +69,8 @@ |
425 | <a href="+new" id="addspec"> <img alt="Register a blueprint"... |
426 | |
427 | |
428 | -=== From a distribution === |
429 | +From a distribution |
430 | +................... |
431 | |
432 | Starting from the Ubuntu distribution page: |
433 | |
434 | @@ -68,7 +92,8 @@ |
435 | class="menu-link-new...>Register a blueprint</a> |
436 | |
437 | |
438 | -=== From a distribution series === |
439 | +From a distribution series |
440 | +.......................... |
441 | |
442 | Starting from the Ubuntu Hoary distribution series page: |
443 | |
444 | @@ -83,7 +108,8 @@ |
445 | class="menu-link-new...>Register a blueprint</a> |
446 | |
447 | |
448 | -=== From a product === |
449 | +From a product |
450 | +.............. |
451 | |
452 | Starting from the Bazaar product page: |
453 | |
454 | @@ -109,7 +135,8 @@ |
455 | >>> print extract_text(find_main_content(user_browser.contents)) |
456 | Register a new blueprint... |
457 | |
458 | -=== From a product series === |
459 | +From a product series |
460 | +..................... |
461 | |
462 | Starting from the Mozilla Firefox product series page: |
463 | |
464 | @@ -125,7 +152,8 @@ |
465 | class="menu-link-new...>Register a blueprint</a> |
466 | |
467 | |
468 | -=== From a project === |
469 | +From a project |
470 | +.............. |
471 | |
472 | Starting from the Mozilla project page: |
473 | |
474 | @@ -140,7 +168,8 @@ |
475 | class="menu-link-new...>Register a blueprint</a> |
476 | |
477 | |
478 | -=== From a sprint === |
479 | +From a sprint |
480 | +............. |
481 | |
482 | Starting from the Future Mega Meeting sprint page: |
483 | |
484 | @@ -155,14 +184,16 @@ |
485 | class="menu-link-new...>Register a blueprint</a> |
486 | |
487 | |
488 | -== Registering a blueprint == |
489 | +Registering a blueprint |
490 | +----------------------- |
491 | |
492 | The blueprint registration form allows users to register a blueprint. The |
493 | appearance and behaviour of the form depends on where the user has navigated |
494 | from. |
495 | |
496 | |
497 | -=== Registering a blueprint from the Blueprints home page === |
498 | +Registering a blueprint from the Blueprints home page |
499 | +..................................................... |
500 | |
501 | We'll start from the default blueprint registration form: |
502 | |
503 | @@ -216,7 +247,8 @@ |
504 | Network Magic: Auto Network Detection... |
505 | |
506 | |
507 | -=== Registering a blueprint from a distribution === |
508 | +Registering a blueprint from a distribution |
509 | +........................................... |
510 | |
511 | When a blueprint is registered from a distribution, the new blueprint is |
512 | automatically targeted to the distribution. |
513 | @@ -239,7 +271,8 @@ |
514 | Network Magic: Auto Network Detection... |
515 | |
516 | |
517 | -=== Registering a blueprint from a distribution series === |
518 | +Registering a blueprint from a distribution series |
519 | +.................................................. |
520 | |
521 | When a blueprint is registered from a distribution series, the new blueprint |
522 | is automatically targeted to the parent distribution. In addition, Launchpad |
523 | @@ -326,7 +359,8 @@ |
524 | Series goal: Accepted for hoary |
525 | |
526 | |
527 | -=== Registering a blueprint from a product === |
528 | +Registering a blueprint from a product |
529 | +...................................... |
530 | |
531 | When a blueprint is registered from a product, the new blueprint is |
532 | automatically targeted to the product. |
533 | @@ -355,7 +389,8 @@ |
534 | SVG Support... |
535 | |
536 | |
537 | -=== Registering a blueprint from a product series === |
538 | +Registering a blueprint from a product series |
539 | +............................................. |
540 | |
541 | When a blueprint is registered from a product series, the new blueprint is |
542 | automatically targeted to the parent product. In addition, Launchpad allows |
543 | @@ -437,7 +472,8 @@ |
544 | Series goal: Accepted for 1.0 |
545 | |
546 | |
547 | -=== Registering a blueprint from a project === |
548 | +Registering a blueprint from a project |
549 | +...................................... |
550 | |
551 | Let's register a blueprint from the Mozilla project: |
552 | |
553 | @@ -470,7 +506,8 @@ |
554 | SVG Support... |
555 | |
556 | |
557 | -=== Registering a blueprint from a sprint === |
558 | +Registering a blueprint from a sprint |
559 | +..................................... |
560 | |
561 | When a blueprint is registered from a sprint, the new blueprint is |
562 | automatically proposed as a topic for discussion at the sprint. |
563 | @@ -532,7 +569,8 @@ |
564 | <...darcs-imports-2... |
565 | |
566 | |
567 | -=== Proposing any blueprint as a sprint topic during registration === |
568 | +Proposing any blueprint as a sprint topic during registration |
569 | +............................................................. |
570 | |
571 | While blueprints can be registered from sprints directly, it's also possible |
572 | to propose any blueprint for discussion at a sprint during registration. |
573 | @@ -559,10 +597,11 @@ |
574 | <...spec-for-sprint...> |
575 | |
576 | |
577 | -== Restrictions when registering blueprints == |
578 | - |
579 | - |
580 | -=== Names must be unique === |
581 | +Restrictions when registering blueprints |
582 | +---------------------------------------- |
583 | + |
584 | +Names must be unique |
585 | +.................... |
586 | |
587 | It's not possible to register a blueprint with the same name as an existing |
588 | blueprint. |
589 | @@ -602,7 +641,8 @@ |
590 | There is 1 error...already in use by another blueprint... |
591 | |
592 | |
593 | -=== Names must be valid === |
594 | +Names must be valid |
595 | +................... |
596 | |
597 | Blueprint names must conform to a set pattern: |
598 | |
599 | @@ -635,7 +675,8 @@ |
600 | Network Magic: Automatic Network Detection... |
601 | |
602 | |
603 | -=== URLs must be unique === |
604 | +URLs must be unique |
605 | +................... |
606 | |
607 | It's not possible to register a blueprint with the same URL as an existing |
608 | blueprint: |
609 | @@ -653,7 +694,8 @@ |
610 | There is 1 error...already registered by another blueprint... |
611 | |
612 | |
613 | -=== Registering blueprints from other locations === |
614 | +Registering blueprints from other locations |
615 | +........................................... |
616 | |
617 | There are some locations in Launchpad from which it's not possible to register |
618 | a blueprint. To start with, it's not possible to register a blueprint from an |
619 | |
620 | === modified file 'lib/lp/blueprints/stories/blueprints/xx-productseries.txt' |
621 | --- lib/lp/blueprints/stories/blueprints/xx-productseries.txt 2010-08-26 02:30:06 +0000 |
622 | +++ lib/lp/blueprints/stories/blueprints/xx-productseries.txt 2010-09-17 14:23:57 +0000 |
623 | @@ -2,6 +2,18 @@ |
624 | Targeting to ProductSeries |
625 | ========================== |
626 | |
627 | +A number of tests in this need a product with blueprints enabled, so we'll |
628 | +enable them on firefox. |
629 | + |
630 | + >>> from zope.component import getUtility |
631 | + >>> from lp.app.enums import ServiceUsage |
632 | + >>> from lp.registry.interfaces.product import IProductSet |
633 | + >>> login('admin@canonical.com') |
634 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
635 | + >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD |
636 | + >>> transaction.commit() |
637 | + >>> logout() |
638 | + |
639 | In terms of feature management for release series and for distroseriess, we |
640 | want to target one of these specs to the 1.0 series. We will use the "e4x" |
641 | specification. |
642 | |
643 | === modified file 'lib/lp/blueprints/stories/standalone/xx-batching.txt' |
644 | --- lib/lp/blueprints/stories/standalone/xx-batching.txt 2009-09-22 10:48:09 +0000 |
645 | +++ lib/lp/blueprints/stories/standalone/xx-batching.txt 2010-09-17 14:23:57 +0000 |
646 | @@ -24,11 +24,25 @@ |
647 | >>> browser.url |
648 | 'http://launchpad.dev/big-project' |
649 | |
650 | -In the beginning, a project has no blueprints: |
651 | - |
652 | - >>> browser.open("http://blueprints.launchpad.dev/big-project") |
653 | - >>> print extract_text(find_main_content(browser.contents)) |
654 | - Blueprints...Register the first blueprint in this project!... |
655 | +In the beginning, a project hasn't had blueprints set up: |
656 | + |
657 | + >>> browser.open("http://blueprints.launchpad.dev/big-project") |
658 | + >>> print extract_text(find_main_content(browser.contents)) |
659 | + Blueprints...does not know how...Configure blueprints... |
660 | + |
661 | +But it's easy to change that. |
662 | + |
663 | + >>> browser.open("http://blueprints.launchpad.dev/big-project/+configure-blueprints") |
664 | + >>> browser.getControl(name='field.blueprints_usage').value = ['LAUNCHPAD'] |
665 | + >>> browser.getControl('Change').click() |
666 | + >>> browser.url |
667 | + 'http://blueprints.launchpad.dev/big-project' |
668 | + |
669 | +Initially the newly enabled feature has no blueprints. |
670 | + |
671 | + >>> browser.open("http://blueprints.launchpad.dev/big-project") |
672 | + >>> print extract_text(find_main_content(browser.contents)) |
673 | + Blueprints...first blueprint in this project!... |
674 | |
675 | We'll go ahead and add just a single blueprint: |
676 | |
677 | |
678 | === modified file 'lib/lp/blueprints/stories/standalone/xx-index.txt' |
679 | --- lib/lp/blueprints/stories/standalone/xx-index.txt 2009-09-22 16:42:19 +0000 |
680 | +++ lib/lp/blueprints/stories/standalone/xx-index.txt 2010-09-17 14:23:57 +0000 |
681 | @@ -1,6 +1,20 @@ |
682 | ===================== |
683 | Blueprints index page |
684 | ===================== |
685 | +Enabling Firefox |
686 | +---------------- |
687 | + |
688 | +For a product to work in blueprints, it must be active, so we'll activate |
689 | +blueprints for firefox. |
690 | + |
691 | + >>> from zope.component import getUtility |
692 | + >>> from lp.app.enums import ServiceUsage |
693 | + >>> from lp.registry.interfaces.product import IProductSet |
694 | + >>> login('admin@canonical.com') |
695 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
696 | + >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD |
697 | + >>> transaction.commit() |
698 | + >>> logout() |
699 | |
700 | The blueprints index page allows users to search for blueprints within |
701 | a single Launchpad project or across all projects. |
702 | |
703 | === modified file 'lib/lp/blueprints/stories/standalone/xx-overview.txt' |
704 | --- lib/lp/blueprints/stories/standalone/xx-overview.txt 2009-11-26 03:06:58 +0000 |
705 | +++ lib/lp/blueprints/stories/standalone/xx-overview.txt 2010-09-17 14:23:57 +0000 |
706 | @@ -7,6 +7,17 @@ |
707 | blueprints, and also other pages where users can browse through complete |
708 | lists of blueprints for a given product or distribution. |
709 | |
710 | +A number of tests in this need a product with blueprints enabled, so we'll |
711 | +enable them on firefox. |
712 | + |
713 | + >>> from zope.component import getUtility |
714 | + >>> from lp.app.enums import ServiceUsage |
715 | + >>> from lp.registry.interfaces.product import IProductSet |
716 | + >>> login('admin@canonical.com') |
717 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
718 | + >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD |
719 | + >>> transaction.commit() |
720 | + >>> logout() |
721 | |
722 | Viewing lists of blueprints |
723 | =========================== |
724 | |
725 | === modified file 'lib/lp/blueprints/stories/standalone/xx-views.txt' |
726 | --- lib/lp/blueprints/stories/standalone/xx-views.txt 2009-09-22 10:48:09 +0000 |
727 | +++ lib/lp/blueprints/stories/standalone/xx-views.txt 2010-09-17 14:23:57 +0000 |
728 | @@ -1,6 +1,22 @@ |
729 | -== Blueprint views == |
730 | - |
731 | -=== Viewing current blueprints === |
732 | +Blueprint views |
733 | +=============== |
734 | + |
735 | +Set Up |
736 | +------ |
737 | +A number of tests in this need a product with blueprints enabled, so we'll |
738 | +enable them on firefox. |
739 | + |
740 | + >>> from zope.component import getUtility |
741 | + >>> from lp.app.enums import ServiceUsage |
742 | + >>> from lp.registry.interfaces.product import IProductSet |
743 | + >>> login('admin@canonical.com') |
744 | + >>> firefox = getUtility(IProductSet).getByName('firefox') |
745 | + >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD |
746 | + >>> transaction.commit() |
747 | + >>> logout() |
748 | + |
749 | +Viewing current blueprints |
750 | +-------------------------- |
751 | |
752 | We should be able to see a "+specs" and a "+portlet-latestspecs" view |
753 | on sprint, product, person, project and distribution. |
754 | @@ -143,7 +159,8 @@ |
755 | ...Activating Usplash... |
756 | |
757 | |
758 | -=== Viewing all blueprints === |
759 | +Viewing all blueprints |
760 | +---------------------- |
761 | |
762 | From time to time it's useful to review the complete list of all blueprints |
763 | associated with a blueprint target, including those blueprints that have |
764 | |
765 | === added file 'lib/lp/blueprints/templates/unknown-specs.pt' |
766 | --- lib/lp/blueprints/templates/unknown-specs.pt 1970-01-01 00:00:00 +0000 |
767 | +++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-17 14:23:57 +0000 |
768 | @@ -0,0 +1,93 @@ |
769 | +<html |
770 | + xmlns="http://www.w3.org/1999/xhtml" |
771 | + xmlns:tal="http://xml.zope.org/namespaces/tal" |
772 | + xmlns:metal="http://xml.zope.org/namespaces/metal" |
773 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
774 | + metal:use-macro="view/macro:page/main_side" |
775 | + i18n:domain="launchpad" |
776 | +> |
777 | + |
778 | +<body> |
779 | + |
780 | +<div metal:fill-slot="main"> |
781 | + <div class="top-portlet"> |
782 | + <div id="specs-unknown"> |
783 | + <strong> |
784 | + <div tal:omit-tag tal:condition="view/is_project"> |
785 | + <p> |
786 | + Launchpad does not know how |
787 | + <tal:project replace="view/context/displayname" /> tracks feature |
788 | + planning or documentation. |
789 | + </p> |
790 | + </div> |
791 | + |
792 | + <div tal:omit-tag tal:condition="view/is_series"> |
793 | + <div |
794 | + tal:omit-tag |
795 | + tal:define="target python:view.context.product and view.context.product or view.context.distribution; |
796 | + blueprints_usage target/blueprints_usage"> |
797 | + <p tal:condition="blueprints_usage/enumvalue:EXTERNAL"> |
798 | + <tal:project replace="target/displayname" />'s |
799 | + <tal:project replace="view/context/displayname" /> series does |
800 | + not use Launchpap for planning or documentation. |
801 | + </p> |
802 | + <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE"> |
803 | + <tal:project replace="target/displayname" />'s |
804 | + <tal:project replace="view/context/displayname" /> series does not track |
805 | + feature planning or documentation. |
806 | + </p> |
807 | + <p tal:condition="blueprints_usage/enumvalue:UNKNOWN"> |
808 | + Launchpad does not know how |
809 | + <tal:project replace="target/displayname" />'s |
810 | + <tal:project replace="view/context/displayname" /> series tracks feature |
811 | + planning or documentation. |
812 | + </p> |
813 | + </div> |
814 | + </div> |
815 | + |
816 | + <div tal:omit-tag |
817 | + tal:condition="python:not (view.is_project or view.is_series)"> |
818 | + <div tal:omit-tag tal:define="blueprints_usage view/context/blueprints_usage"> |
819 | + <p tal:condition="blueprints_usage/enumvalue:EXTERNAL"> |
820 | + <tal:project replace="view/context/displayname" /> does not use launchpad |
821 | + for planning or documentation. |
822 | + </p> |
823 | + <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE"> |
824 | + <tal:project replace="view/context/displayname" /> does not track |
825 | + feature planning or documentation. |
826 | + </p> |
827 | + <p tal:condition="blueprints_usage/enumvalue:UNKNOWN"> |
828 | + launchpad does not know how |
829 | + <tal:project replace="view/context/displayname" /> tracks feature |
830 | + planning or documentation. |
831 | + </p> |
832 | + </div> |
833 | + </div> |
834 | + </strong> |
835 | + |
836 | + <div tal:omit-tag tal:condition="view/has_wiki"> |
837 | + <p id="wiki-fallback" |
838 | + tal:define="wiki view/context/wikiurl" |
839 | + tal:condition="wiki"> |
840 | + <tal:project replace="view/context/displayname" /> has a wiki, which |
841 | + may be used for feature plannning and documentation.<br /> |
842 | + <a tal:attributes="href view/context/wikiurl"> |
843 | + <tal:project replace="view/context/displayname" /> wiki |
844 | + </a> |
845 | + </p> |
846 | + </div> |
847 | + </div> |
848 | + |
849 | + <p id="configure-support" |
850 | + tal:condition="view/can_configure_blueprints"> |
851 | + Launchpad Blueprints allow your project to track feature planning and |
852 | + documentation.<br /> |
853 | + <a class="info sprite" |
854 | + href="https://help.launchpad.net/BlueprintDocumentation"> |
855 | + Read more about tracking feature planning with blueprints</a><br /> |
856 | + <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" /><br /> |
857 | + </p> |
858 | + </div> |
859 | +</div> |
860 | +</body> |
861 | +</html> |
862 | |
863 | === modified file 'lib/lp/registry/browser/distribution.py' |
864 | --- lib/lp/registry/browser/distribution.py 2010-09-11 19:54:26 +0000 |
865 | +++ lib/lp/registry/browser/distribution.py 2010-09-17 14:23:57 +0000 |
866 | @@ -342,6 +342,7 @@ |
867 | 'announcements', |
868 | 'ppas', |
869 | 'configure_answers', |
870 | + 'configure_blueprints', |
871 | ] |
872 | |
873 | @enabled_with_permission('launchpad.Edit') |
874 | @@ -453,6 +454,12 @@ |
875 | summary = 'Allow users to ask questions on this project' |
876 | return Link('+edit', text, summary, icon='edit') |
877 | |
878 | + @enabled_with_permission('launchpad.Edit') |
879 | + def configure_blueprints(self): |
880 | + text = 'Configure blueprints' |
881 | + summary = 'Enable tracking of feature planning.' |
882 | + return Link('+edit', text, summary, icon='edit') |
883 | + |
884 | |
885 | class DerivativeDistributionOverviewMenu(DistributionOverviewMenu): |
886 | |
887 | |
888 | === modified file 'lib/lp/registry/browser/product.py' |
889 | --- lib/lp/registry/browser/product.py 2010-09-03 15:02:39 +0000 |
890 | +++ lib/lp/registry/browser/product.py 2010-09-17 14:23:57 +0000 |
891 | @@ -547,7 +547,7 @@ |
892 | @enabled_with_permission('launchpad.Edit') |
893 | def configure_blueprints(self): |
894 | text = 'Configure blueprints' |
895 | - summary = 'Enable tracking of specifications and meetings' |
896 | + summary = 'Enable tracking of feature planning.' |
897 | return Link('+configure-blueprints', text, summary, icon='edit') |
898 | |
899 | @enabled_with_permission('launchpad.Edit') |
900 | |
901 | === modified file 'lib/lp/registry/browser/tests/pillar-views.txt' |
902 | --- lib/lp/registry/browser/tests/pillar-views.txt 2010-09-14 21:50:32 +0000 |
903 | +++ lib/lp/registry/browser/tests/pillar-views.txt 2010-09-17 14:23:57 +0000 |
904 | @@ -36,8 +36,8 @@ |
905 | True |
906 | >>> print view.answers_usage.name |
907 | LAUNCHPAD |
908 | - >>> view.translations_usage.name |
909 | - 'UNKNOWN' |
910 | + >>> print view.translations_usage.name |
911 | + UNKNOWN |
912 | >>> print view.blueprints_usage.name |
913 | UNKNOWN |
914 | >>> print view.codehosting_usage.name |
Sorry about the bad update. Just in case of divergences between the prereq and this post update from devel, here's the diff:
=== modified file 'lib/lp/ blueprints/ browser/ configure. zcml' blueprints/ browser/ configure. zcml 2010-07-27 17:17:59 +0000 blueprints/ browser/ configure. zcml 2010-09-08 20:11:11 +0000
name= "+mdz-specs. csv"
attribute ="mdzCsv" />
<browser: page "../templates/ hasspecificatio ns-specs. pt"/>
<browser: page
name= "+portlet- latestspecs"
template= "../templates/ specificationta rget-portlet- latestspecs. pt"/>
class= "lp.blueprints. browser. specificationta rget.HasSpecifi cationsView"
permissio n="zope. Public" >
< browser: page "../templates/ hasspecificatio ns-specs. pt"/>
</browser: pages>
<browser: page
for= "lp.blueprints. interfaces. specificationta rget.ISpecifica tionGoal"
class= "lp.blueprints. browser. specificationta rget.HasSpecifi cationsView"
permissio n="zope. Public" >
< browser: page "../templates/ hasspecificatio ns-specs. pt"/>
< browser: page
name= "+portlet- latestspecs"
template= "../templates/ specificationta rget-portlet- latestspecs. pt"/>
facet= "specifications "
permission= "zope.Public" >
<browser: page "../templates/ hasspecificatio ns-specs. pt"/>
<browser: page
name= "+portlet- latestspecs"
template= "../templates/ specificationta rget-portlet- latestspecs. pt"/>
--- lib/lp/
+++ lib/lp/
@@ -53,8 +53,7 @@
- name="+specs"
- template=
+ name="+specs"/>
@@ -500,8 +499,7 @@
- name="+specs"
- template=
+ name="+specs"/>
@@ -532,8 +530,7 @@
- name="+specs"
- template=
+ name="+specs"/>
@@ -583,8 +580,7 @@
- name="+specs"
- template=
+ name="+specs"/>
=== modified file 'lib/lp/ blueprints/ browser/ specificationta rget.py' blueprints/ browser/ specificationta rget.py 2010-08-24 10:45:57 +0000 blueprints/ browser/ specificationta rget.py 2010-09-09 18:06:03 +0000
--- lib/lp/
+++ lib/lp/
@@ -15,6 +15,7 @@
from operator import itemgetter
+from z3c.ptcompat import ViewPageTemplat eFile
from zope.component import queryMultiAdapter
from canonical.config import config lazr.utils import smartquote uses_launchpad interfaces. launchpad import IServiceUsage interfaces. specification import ( ionFilter, ionSort,
@@ -32,6 +33,8 @@
Link,
)
from canonical.
+from lp.app.enums import service_
+from lp.app.
from lp.blueprints.
Specificat
Specificat
@@ -133,6 +136,50 @@
is_sprint = False
has_drivers = False
+ # Templates for the various conditions of blueprints: eFile( hasspecificatio ns-specs. pt') template = ViewPageTemplat eFile( unknown- specs.pt' )
+ # * On Launchpad
+ # * External
+ # * Disabled
+ # * Unknown
+ default_template = ViewPageTemplat
+ '../templates/
+ not_launchpad_
+ '../templates/
+
+ @property
+ def template(self):
+ # If the template has been defined in the zcml, use that, as t...