Merge lp:~jcsackett/launchpad/unknown-blueprints-service-597738 into lp:launchpad

Proposed by j.c.sackett
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
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 HasSpecificationsView has been updated to select a different template for contexts of Product, Distribution, and DistributionSeries in the event that the blueprints_usage enum indicates the context doesn't use Launchpad.

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.browser

== Demo and Q/A ==

In Launchpad.dev, signed in as admin, go to http://blueprints.launchpad.dev/thunderbird. Try each configuration option and confirm that the presentation makes sense.

Linting changed files:
  lib/lp/answers/browser/questiontarget.py
  lib/lp/answers/doc/question.txt
  lib/lp/answers/doc/questionsets.txt
  lib/lp/app/enums.py
  lib/lp/blueprints/browser/configure.zcml
  lib/lp/blueprints/browser/specificationtarget.py
  lib/lp/blueprints/browser/tests/test_specificationtarget.py
  lib/lp/blueprints/templates/unknown-specs.pt
  lib/lp/registry/browser/distribution.py
  lib/lp/registry/browser/pillar.py
  lib/lp/registry/browser/productseries.py
  lib/lp/registry/browser/tests/distribution-views.txt
  lib/lp/registry/browser/tests/pillar-views.txt
  lib/lp/registry/browser/tests/product-views.txt
  lib/lp/registry/browser/tests/productseries-views.txt
  lib/lp/registry/browser/tests/projectgroup-views.txt
  lib/lp/registry/doc/distribution.txt
  lib/lp/registry/doc/product.txt
  lib/lp/registry/templates/distribution-index.pt
  lib/lp/registry/templates/distribution-search.pt
  lib/lp/registry/templates/distroseries-index.pt
  lib/lp/registry/templates/product-index.pt
  lib/lp/registry/templates/productseries-index.pt
  lib/lp/registry/templates/project-index.pt

Lint output corrupted by merged dependent branch.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :
Download full text (11.9 KiB)

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'
--- lib/lp/blueprints/browser/configure.zcml 2010-07-27 17:17:59 +0000
+++ lib/lp/blueprints/browser/configure.zcml 2010-09-08 20:11:11 +0000
@@ -53,8 +53,7 @@
             name="+mdz-specs.csv"
             attribute="mdzCsv"/>
         <browser:page
- name="+specs"
- template="../templates/hasspecifications-specs.pt"/>
+ name="+specs"/>
         <browser:page
             name="+portlet-latestspecs"
             template="../templates/specificationtarget-portlet-latestspecs.pt"/>
@@ -500,8 +499,7 @@
             class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"
             permission="zope.Public">
             <browser:page
- name="+specs"
- template="../templates/hasspecifications-specs.pt"/>
+ name="+specs"/>
         </browser:pages>
         <browser:page
             for="lp.blueprints.interfaces.specificationtarget.ISpecificationGoal"
@@ -532,8 +530,7 @@
             class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"
             permission="zope.Public">
             <browser:page
- name="+specs"
- template="../templates/hasspecifications-specs.pt"/>
+ name="+specs"/>
             <browser:page
                 name="+portlet-latestspecs"
                 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
@@ -583,8 +580,7 @@
         facet="specifications"
         permission="zope.Public">
         <browser:page
- name="+specs"
- template="../templates/hasspecifications-specs.pt"/>
+ name="+specs"/>
         <browser:page
             name="+portlet-latestspecs"
             template="../templates/specificationtarget-portlet-latestspecs.pt"/>

=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2010-08-24 10:45:57 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-09 18:06:03 +0000
@@ -15,6 +15,7 @@

 from operator import itemgetter

+from z3c.ptcompat import ViewPageTemplateFile
 from zope.component import queryMultiAdapter

 from canonical.config import config
@@ -32,6 +33,8 @@
     Link,
     )
 from canonical.lazr.utils import smartquote
+from lp.app.enums import service_uses_launchpad
+from lp.app.interfaces.launchpad import IServiceUsage
 from lp.blueprints.interfaces.specification import (
     SpecificationFilter,
     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 = ViewPageTemplateFile(
+ '../templates/hasspecifications-specs.pt')
+ not_launchpad_template = ViewPageTemplateFile(
+ '../templates/unknown-specs.pt')
+
+ @property
+ def template(self):
+ # If the template has been defined in the zcml, use that, as t...

Revision history for this message
Brad Crittenden (bac) wrote :
Download full text (10.3 KiB)

Hi Jon,

Thanks for working on this branch. I was unaware of the added
complexity of the whole template/page/portlet mess.

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/blueprints/browser/specificationtarget.py
     251: E301 expected 1 blank line, found 0
     254: E301 expected 1 blank line, found 0
./lib/lp/blueprints/templates/unknown-specs.pt
      38: Line has trailing whitespace.
./lib/lp/registry/doc/distribution.txt
     582: source exceeds 78 characters.
     584: source exceeds 78 characters.

> === modified file 'lib/lp/blueprints/browser/specificationtarget.py'
> --- lib/lp/blueprints/browser/specificationtarget.py 2010-08-24 10:45:57 +0000
> +++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-10 14:48:00 +0000
> @@ -15,6 +15,7 @@
>
> from operator import itemgetter
>
> +from z3c.ptcompat import ViewPageTemplateFile
> from zope.component import queryMultiAdapter
>
> from canonical.config import config
> @@ -32,6 +33,8 @@
> Link,
> )
> from canonical.lazr.utils import smartquote
> +from lp.app.enums import service_uses_launchpad
> +from lp.app.interfaces.launchpad import IServiceUsage
> from lp.blueprints.interfaces.specification import (
> SpecificationFilter,
> 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 = ViewPageTemplateFile(
> + '../templates/hasspecifications-specs.pt')
> + not_launchpad_template = ViewPageTemplateFile(
> + '../templates/unknown-specs.pt')
> +
> + @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.lazr.utils. Also, as I suggested please check around and
see if there is a more obvious technique for this. Sadly I cannot
offer any other solution.

> + return super(HasSpecificationsView, self).template
> +
> + # Sprints and Persons don't have a usage enum for blueprints, so we
> + # have to fallback to the default.
> + if (ISprint.providedBy(self.context)
> + or IPerson.providedBy(self.context)):
> + return self.default_template
> +
> + # 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.providedBy(self.context):
> + return self.default_template
> +
> + # If specific...

review: Needs Information (code)
Revision history for this message
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.lazr.utils. Also, as I suggested please check around and
> 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

Revision history for this message
Brad Crittenden (bac) wrote :

Good changes Jon. Please alphabetize the imports where you added safe_hasattr.

review: Approve (code)
Revision history for this message
Guilherme Salgado (salgado) wrote :

The UI changes look good to me, although it's confusing that the new template (lib/lp/blueprints/templates/unknown-specs.pt) uses the term "specification[s]" when the rest of the page uses "blueprint[s]". I think we should use only one of them, and most likely the preferred would be "blueprint[s]" as that's what is used in the top heading.

review: Needs Fixing (ui*)
Revision history for this message
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 SpecificationTarget. I reviewed the bugs branch a few days ago and saw only IProduct was supports well. A BugTarget may be a product, projectgroup, distribution, and distributionsourcepackage. I think these are also SpediciationTargets, so 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. I took a brief look at this branch in launchpad.dev and am pretty sure thunderbird is somewhat right, kubuntu, firefox and mozilla are wrong.

jcsackett, Salgado, take a look at the branch again from the perspective of kinds of users who are interested in feature planning.

review: Needs Fixing
Revision history for this message
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.launchpad.net/$PILLAR.

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_involvment(context)
>
> You didn't introduce it, but please fix the typo of s/verify_involment/verify_involvement/

Uhm, not sure what you mean--looks like it already is verify_involvement?

>> def test_adaptable_to_specificationtarget(self):
>> @@ -87,6 +92,63 @@
>> '<div id="involvement" class="portlet involvement">' in view())
>>
>>
>> +class TestHasSpecificationsTemplates(TestCaseWithFactory):
>> + """Tests the selection of templates based on blueprints usage."""
>> +
>> + layer = DatabaseFunctionalLayer
>> +
>> + def setUp(self):
>> + super(TestHasSpecificationsTemplates, self).setUp()
>> + self.user = self.factory.makePerson()
>> + self.product = self.factory.makeProduct()
>> + self.naked_product = removeSecurityProxy(self.product)
>> + login_person(self.user)
>
> 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

Revision history for this message
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.launchpad.net/$PILLAR.
>
> 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://launchpad.net/

Revision history for this message
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.

Revision history for this message
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.

=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2010-09-13 20:21:10 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 15:49:09 +0000
@@ -26,6 +26,7 @@
26 canonical_url,26 canonical_url,
27 LaunchpadView,27 LaunchpadView,
28 )28 )
29from canonical.launchpad.webapp.authorization import check_permission
29from canonical.launchpad.webapp.batching import BatchNavigator30from canonical.launchpad.webapp.batching import BatchNavigator
30from canonical.launchpad.webapp.breadcrumb import Breadcrumb31from canonical.launchpad.webapp.breadcrumb import Breadcrumb
31from canonical.launchpad.webapp.menu import (32from canonical.launchpad.webapp.menu import (
@@ -219,6 +220,15 @@
219 size=config.launchpad.default_batch_size)220 size=config.launchpad.default_batch_size)
220221
221 @property222 @property
223 def can_configure_blueprints(self):
224 """Can the user configure blueprints for the `ISpecificationTarget`."""
225 target = self.context
226 if IProduct.providedBy(target) or IDistribution.providedBy(target):
227 return check_permission('launchpad.Edit', self.context)
228 else:
229 return False
230
231 @property
222 def label(self):232 def label(self):
223 mapping = {'name': self.context.displayname}233 mapping = {'name': self.context.displayname}
224 if self.is_person:234 if self.is_person:
225235
=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
--- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-09 18:11:41 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 15:47:58 +0000
@@ -18,7 +18,10 @@
18 login_person,18 login_person,
19 TestCaseWithFactory,19 TestCaseWithFactory,
20 )20 )
21from lp.testing.views import create_view21from lp.testing.views import (
22 create_view,
23 create_initialized_view,
24 )
2225
2326
24class TestRegisterABlueprintButtonView(TestCaseWithFactory):27class TestRegisterABlueprintButtonView(TestCaseWithFactory):
@@ -149,6 +152,38 @@
149 view.template.filename)152 view.template.filename)
150153
151154
155class TestHasSpecificationsConfiguration(TestCaseWithFactory):
156
157 layer = DatabaseFunctionalLayer
158
159 def test_cannot_configure_blueprints_product_no_edit_permission(self):
160 product = self.factory.makeProduct()
161 view = create_initialized_view(product, '+specs')
162 self.assertEqual(False, view.can_configure_blueprints)
163
164 def test_can_configure_blueprints_product_with_edit_permission(self):
165 product = self.factory.makeProduct()
166 login_person(product.owner)
167 view = create_initialized_view(product, '+specs')
168 self.assertEqual(True, view.can_configure_blueprints)
169
170 def test_cannot_configure_blueprints_distribution_no_edit_permission(self):
171 distribution = self.factory.makeDistribution()
172 view = create_initialized_view(distribution, '+specs')
173 self.assertEqual(False, view.can_configure_blueprints)
174
175 def test_can_configure_blueprints_distribution_with_edit_permission(self):
176 distribution = self.factory.makeDistribution()
177 login_person(distribution.owner)
178 view = create_initialized_view(distribution, '+specs')
179 self.assertEqual(True, view.can_configure_blueprints)
180
181 def test_cannot_configure_blueprints_projectgroup_with_edit_permission(self):
182 project_group = self.factory.makeProject()
183 login_person(project_group.owner)
184 view = create_initialized_view(project_group, '+specs')
185 self.assertEqual(False, view.can_configure_blueprints)
186
152def test_suite():187def test_suite():
153 suite = unittest.TestSuite()188 suite = unittest.TestSuite()
154 suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))189 suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))
155190
=== modified file 'lib/lp/blueprints/templates/unknown-specs.pt'
--- lib/lp/blueprints/templates/unknown-specs.pt 2010-09-10 16:34:26 +0000
+++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 16:02:38 +0000
@@ -11,49 +11,47 @@
1111
12<div metal:fill-slot="main"12<div metal:fill-slot="main"
13 tal:define="specs view/specs;13 tal:define="specs view/specs;
14 has_any_specs view/has_any_specifications">14 has_any_specs view/has_any_specifications;
1515 blueprints_usage view/context/blueprints_usage">
16 <div tal:condition="view/context/blueprints_usage/enumvalue:EXTERNAL">16 <div class="top-portlet">
17 <strong><tal:project replace="view/context/displayname" /> does not use Launchpad17 <div id="specs-unknown">
18 for specification tracking.</strong>18 <strong>
19 </div>19 <p tal:condition="blueprints_usage/enumvalue:EXTERNAL">
2020 <tal:project replace="view/context/displayname" /> does not use Launchpad
21 <div tal:condition="view/context/blueprints_usage/enumvalue:NOT_APPLICABLE">21 for planning or documentation.
22 <strong><tal:project replace="view/context/displayname" /> does not track22 </p>
23 specifications.</strong>23 <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE">
24 </div>24 <tal:project replace="view/context/displayname" /> does not track
2525 use feature planning or documentation.
26 <div tal:condition="view/context/blueprints_usage/enumvalue:UNKNOWN">26 </p>
27 <strong>27 <p tal:condition="blueprints_usage/enumvalue:UNKNOWN">
28 Launchpad does not know how28 Launchpad does not know how
29 <tal:project replace="view/context/displayname" /> uses specifications.29 <tal:project replace="view/context/displayname" /> tracks feature
30 </strong>30 planning or documentation.
31 </div>31 </p>
3232 </strong>
33 <div tal:condition="view/context/wikiurl">33
34 <p><tal:project replace="view/context/displayname" /> has a wiki, which34 <p id="wiki-fallback"
35 may be used for specifications.</p>35 tal:define="wiki view/context/wikiurl"
3636 tal:condition="wiki">
37 <p>37 <tal:project replace="view/context/displayname" /> has a wiki, which
38 may be used for feature plannning and documentation.<br />
38 <a tal:attributes="href view/context/wikiurl">39 <a tal:attributes="href view/context/wikiurl">
39 <tal:project replace="view/context/displayname" /> wiki40 <tal:project replace="view/context/displayname" /> wiki
40 </a>41 </a>
41 </p>42 </p>
42
43 </div>43 </div>
44 <ul class="horizontal">44
45 <li>45 <p id="configure-support"
46 <a class="info sprite"46 tal:condition="view/can_configure_blueprints">
47 href="https://help.launchpad.net/BlueprintDocumentation">47 Launchpad Blueprints allow your project to track feature planning and
48 Read more about tracking blueprints48 documentation.<br />
49 </a>49 <a class="info sprite"
50 </li>50 href="https://help.launchpad.net/BlueprintDocumentation">
51 </ul>51 Read more about tracking feature planning with blueprints</a><br />
52 <ul class="horiztonal">52 <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" /><br />
53 <li>53 </p>
54 <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" />54 </div>
55 </li>
56 </ul>
57</div>55</div>
58</body>56</body>
59</html>57</html>
Revision history for this message
j.c.sackett (jcsackett) wrote :

> My other concern is about what has specifications--a SpecificationTarget. I
> 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
> distributionsourcepackage. I think these are also SpediciationTargets, so
> 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_specifications was True, I think it is appropriate to only turn this on if at least one product has LAUNCHPAD as its usage setting.

Distributions were mostly handled right; the issue with say, Kubuntu was the configure_blueprints link, which I fixed.

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.launchpad.net for the DSP in question; it takes you to the overview. (see https://blueprints.edge.launchpad.net/ubuntu/+source/firefox or https://blueprints.launchpad.dev/ubuntu/+source/mozilla-firefox)

Attached is a diff of this round of changes.

=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 15:50:16 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 19:02:37 +0000
@@ -138,6 +138,7 @@
138 is_project = False138 is_project = False
139 is_series = False139 is_series = False
140 is_sprint = False140 is_sprint = False
141 has_wiki = False
141 has_drivers = False142 has_drivers = False
142143
143 # Templates for the various conditions of blueprints:144 # Templates for the various conditions of blueprints:
@@ -153,9 +154,9 @@
153 @property154 @property
154 def template(self):155 def template(self):
155 # Check for the magical "index" added by the browser:page template156 # Check for the magical "index" added by the browser:page template
156 # machinery. If it exists this is actually the 157 # machinery. If it exists this is actually the
157 # zope.app.pagetemplate.simpleviewclass.simple class that is magically158 # zope.app.pagetemplate.simpleviewclass.simple class that is magically
158 # mixed in by the browser:page zcml directive the template defined in 159 # mixed in by the browser:page zcml directive the template defined in
159 # the directive should be used.160 # the directive should be used.
160 if safe_hasattr(self, 'index'):161 if safe_hasattr(self, 'index'):
161 return super(HasSpecificationsView, self).template162 return super(HasSpecificationsView, self).template
@@ -167,11 +168,15 @@
167 return self.default_template168 return self.default_template
168169
169 # ProjectGroups are a special case, as their products may be a170 # ProjectGroups are a special case, as their products may be a
170 # combination of usage settings. Use the default, since it handles171 # combination of usage settings. To deal with this, check all
171 # fetching anything that's set for ProjectGroups, and it's not correct172 # products, and if a product has LAUNCHPAD for the enum, the
172 # to say anything about the ProjectGroup's usage.173 # group does; if not, assume external.
173 if IProjectGroup.providedBy(self.context):174 if IProjectGroup.providedBy(self.context):
174 return self.default_template175 for product in self.context.products:
176 if service_uses_launchpad(product.blueprints_usage):
177 return self.default_template
178 else:
179 return self.not_launchpad_template
175180
176 # Otherwise, determine usage and provide the correct template.181 # Otherwise, determine usage and provide the correct template.
177 service_usage = IServiceUsage(self.context)182 service_usage = IServiceUsage(self.context)
@@ -188,14 +193,19 @@
188 def initialize(self):193 def initialize(self):
189 if IPerson.providedBy(self.context):194 if IPerson.providedBy(self.context):
190 self.is_person = True195 self.is_person = True
191 elif (IDistribution.providedBy(self.context) or196 elif IDistribution.providedBy(self.context):
192 IProduct.providedBy(self.context)):197 self.is_target = True
193 self.is_target = True198 self.is_pillar = True
194 self.is_pillar = True199 self.show_series = True
200 elif IProduct.providedBy(self.context):
201 self.is_target = True
202 self.is_pillar = True
203 self.has_wiki = True
195 self.show_series = True204 self.show_series = True
196 elif IProjectGroup.providedBy(self.context):205 elif IProjectGroup.providedBy(self.context):
197 self.is_project = True206 self.is_project = True
198 self.is_pillar = True207 self.is_pillar = True
208 self.has_wiki = True
199 self.show_target = True209 self.show_target = True
200 self.show_series = True210 self.show_series = True
201 elif IProjectGroupSeries.providedBy(self.context):211 elif IProjectGroupSeries.providedBy(self.context):
@@ -221,7 +231,8 @@
221231
222 @property232 @property
223 def can_configure_blueprints(self):233 def can_configure_blueprints(self):
224 """Can the user configure blueprints for the `ISpecificationTarget`."""234 """Can the user configure blueprints for the `ISpecificationTarget`.
235 """
225 target = self.context236 target = self.context
226 if IProduct.providedBy(target) or IDistribution.providedBy(target):237 if IProduct.providedBy(target) or IDistribution.providedBy(target):
227 return check_permission('launchpad.Edit', self.context)238 return check_permission('launchpad.Edit', self.context)
228239
=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
--- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 15:50:16 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-14 19:01:23 +0000
@@ -13,6 +13,7 @@
13 ISpecificationTarget,13 ISpecificationTarget,
14 )14 )
15from lp.app.enums import ServiceUsage15from lp.app.enums import ServiceUsage
16from lp.blueprints.browser.specificationtarget import HasSpecificationsView
16from lp.blueprints.publisher import BlueprintsLayer17from lp.blueprints.publisher import BlueprintsLayer
17from lp.testing import (18from lp.testing import (
18 login_person,19 login_person,
@@ -103,53 +104,65 @@
103 def setUp(self):104 def setUp(self):
104 super(TestHasSpecificationsTemplates, self).setUp()105 super(TestHasSpecificationsTemplates, self).setUp()
105 self.user = self.factory.makePerson()106 self.user = self.factory.makePerson()
106 self.product = self.factory.makeProduct()
107 self.naked_product = removeSecurityProxy(self.product)
108 login_person(self.user)107 login_person(self.user)
109108
110 def test_not_configured(self):109 def _test_templates_for_configuration(self, target, context=None):
111 self.naked_product.blueprints_usage = ServiceUsage.UNKNOWN110 if context is None:
112 view = create_view(111 context = target
113 self.product,112 naked_target = removeSecurityProxy(target)
114 '+specs',113 test_configurations = [
115 layer=BlueprintsLayer,114 ServiceUsage.UNKNOWN,
116 principal=self.user)115 ServiceUsage.EXTERNAL,
117 self.assertEqual(116 ServiceUsage.NOT_APPLICABLE,
118 view.not_launchpad_template.filename,117 ServiceUsage.LAUNCHPAD,
119 view.template.filename)118 ]
120119 correct_templates = [
121 def test_external(self):120 HasSpecificationsView.not_launchpad_template.filename,
122 self.naked_product.blueprints_usage = ServiceUsage.EXTERNAL121 HasSpecificationsView.not_launchpad_template.filename,
123 view = create_view(122 HasSpecificationsView.not_launchpad_template.filename,
124 self.product,123 HasSpecificationsView.default_template.filename,
125 '+specs',124 ]
126 layer=BlueprintsLayer,125 used_templates = list()
127 principal=self.user)126 for config in test_configurations:
128 self.assertEqual(127 naked_target.blueprints_usage = config
129 view.not_launchpad_template.filename,128 view = create_view(
130 view.template.filename)129 context,
131130 '+specs',
132 def test_not_applicable(self):131 layer=BlueprintsLayer,
133 self.naked_product.blueprints_usage = ServiceUsage.NOT_APPLICABLE132 principal=self.user)
134 view = create_view(133 used_templates.append(view.template.filename)
135 self.product,134 self.assertEqual(correct_templates, used_templates)
136 '+specs',135
137 layer=BlueprintsLayer,136 def test_product(self):
138 principal=self.user)137 product = self.factory.makeProduct()
139 self.assertEqual(138 self._test_templates_for_configuration(product)
140 view.not_launchpad_template.filename,139
141 view.template.filename)140 def test_product_series(self):
142141 product = self.factory.makeProduct()
143 def test_on_launchpad(self):142 product_series = self.factory.makeProductSeries(product=product)
144 self.naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD143 self._test_templates_for_configuration(
145 view = create_view(144 target=product,
146 self.product,145 context=product_series)
147 '+specs',146
148 layer=BlueprintsLayer,147 def test_distribution(self):
149 principal=self.user)148 distribution = self.factory.makeDistribution()
150 self.assertEqual(149 self._test_templates_for_configuration(distribution)
151 view.default_template.filename,150
152 view.template.filename)151 def test_distroseries(self):
152 distribution = self.factory.makeDistribution()
153 distro_series = self.factory.makeDistroSeries(
154 distribution=distribution)
155 self._test_templates_for_configuration(
156 target=distribution,
157 context=distro_series)
158
159 def test_projectgroup(self):
160 project = self.factory.makeProject()
161 product1 = self.factory.makeProduct(project=project)
162 product2 = self.factory.makeProduct(project=project)
163 self._test_templates_for_configuration(
164 target=product1,
165 context=project)
153166
154167
155class TestHasSpecificationsConfiguration(TestCaseWithFactory):168class TestHasSpecificationsConfiguration(TestCaseWithFactory):
@@ -167,7 +180,7 @@
167 view = create_initialized_view(product, '+specs')180 view = create_initialized_view(product, '+specs')
168 self.assertEqual(True, view.can_configure_blueprints)181 self.assertEqual(True, view.can_configure_blueprints)
169182
170 def test_cannot_configure_blueprints_distribution_no_edit_permission(self):183 def test_cant_configure_blueprints_distribution_no_edit_permission(self):
171 distribution = self.factory.makeDistribution()184 distribution = self.factory.makeDistribution()
172 view = create_initialized_view(distribution, '+specs')185 view = create_initialized_view(distribution, '+specs')
173 self.assertEqual(False, view.can_configure_blueprints)186 self.assertEqual(False, view.can_configure_blueprints)
@@ -178,12 +191,13 @@
178 view = create_initialized_view(distribution, '+specs')191 view = create_initialized_view(distribution, '+specs')
179 self.assertEqual(True, view.can_configure_blueprints)192 self.assertEqual(True, view.can_configure_blueprints)
180193
181 def test_cannot_configure_blueprints_projectgroup_with_edit_permission(self):194 def test_cannot_configure_blueprints_projectgroup(self):
182 project_group = self.factory.makeProject()195 project_group = self.factory.makeProject()
183 login_person(project_group.owner)196 login_person(project_group.owner)
184 view = create_initialized_view(project_group, '+specs')197 view = create_initialized_view(project_group, '+specs')
185 self.assertEqual(False, view.can_configure_blueprints)198 self.assertEqual(False, view.can_configure_blueprints)
186199
200
187def test_suite():201def test_suite():
188 suite = unittest.TestSuite()202 suite = unittest.TestSuite()
189 suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))203 suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))
190204
=== modified file 'lib/lp/blueprints/templates/unknown-specs.pt'
--- lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 16:03:51 +0000
+++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-14 19:07:36 +0000
@@ -9,29 +9,38 @@
99
10<body>10<body>
1111
12<div metal:fill-slot="main"12<div metal:fill-slot="main">
13 tal:define="specs view/specs;
14 has_any_specs view/has_any_specifications;
15 blueprints_usage view/context/blueprints_usage">
16 <div class="top-portlet">13 <div class="top-portlet">
17 <div id="specs-unknown">14 <div id="specs-unknown">
18 <strong>15 <strong>
19 <p tal:condition="blueprints_usage/enumvalue:EXTERNAL">16 <div tal:omit-tag tal:condition="view/is_project">
20 <tal:project replace="view/context/displayname" /> does not use Launchpad17 <p>
21 for planning or documentation.18 Launchpad does not know how
22 </p>19 <tal:project replace="view/context/displayname" /> tracks feature
23 <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE">20 planning or documentation.
24 <tal:project replace="view/context/displayname" /> does not track21 </p>
25 use feature planning or documentation.22 </div>
26 </p>23 <div tal:omit-tag tal:condition="not:view/is_project">
27 <p tal:condition="blueprints_usage/enumvalue:UNKNOWN">24 <div tal:omit-tag tal:define="blueprints_usage view/context/blueprints_usage">
28 Launchpad does not know how25 <p tal:condition="blueprints_usage/enumvalue:EXTERNAL">
29 <tal:project replace="view/context/displayname" /> tracks feature26 <tal:project replace="view/context/displayname" /> does not use Launchpad
30 planning or documentation.27 for planning or documentation.
31 </p>28 </p>
29 <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE">
30 <tal:project replace="view/context/displayname" /> does not track
31 use feature planning or documentation.
32 </p>
33 <p tal:condition="blueprints_usage/enumvalue:UNKNOWN">
34 Launchpad does not know how
35 <tal:project replace="view/context/displayname" /> tracks feature
36 planning or documentation.
37 </p>
38 </div>
39 </div>
32 </strong>40 </strong>
3341
34 <p id="wiki-fallback" 42 <div tal:omit-tag tal:condition="view/has_wiki">
43 <p id="wiki-fallback"
35 tal:define="wiki view/context/wikiurl"44 tal:define="wiki view/context/wikiurl"
36 tal:condition="wiki">45 tal:condition="wiki">
37 <tal:project replace="view/context/displayname" /> has a wiki, which46 <tal:project replace="view/context/displayname" /> has a wiki, which
@@ -40,8 +49,9 @@
40 <tal:project replace="view/context/displayname" /> wiki49 <tal:project replace="view/context/displayname" /> wiki
41 </a>50 </a>
42 </p>51 </p>
52 </div>
43 </div>53 </div>
44 54
45 <p id="configure-support"55 <p id="configure-support"
46 tal:condition="view/can_configure_blueprints">56 tal:condition="view/can_configure_blueprints">
47 Launchpad Blueprints allow your project to track feature planning and57 Launchpad Blueprints allow your project to track feature planning and
4858
=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py 2010-09-13 12:09:30 +0000
+++ lib/lp/registry/browser/distribution.py 2010-09-14 19:01:42 +0000
@@ -342,6 +342,7 @@
342 'announcements',342 'announcements',
343 'ppas',343 'ppas',
344 'configure_answers',344 'configure_answers',
345 'configure_blueprints',
345 ]346 ]
346347
347 @enabled_with_permission('launchpad.Edit')348 @enabled_with_permission('launchpad.Edit')
@@ -453,6 +454,12 @@
453 summary = 'Allow users to ask questions on this project'454 summary = 'Allow users to ask questions on this project'
454 return Link('+edit', text, summary, icon='edit')455 return Link('+edit', text, summary, icon='edit')
455456
457 @enabled_with_permission('launchpad.Edit')
458 def configure_blueprints(self):
459 text = 'Configure blueprints'
460 summary = 'Enable tracking of specifications and meetings'
461 return Link('+edit', text, summary, icon='edit')
462
456463
457class DerivativeDistributionOverviewMenu(DistributionOverviewMenu):464class DerivativeDistributionOverviewMenu(DistributionOverviewMenu):
458465
Revision history for this message
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_specifications was True, I think it is
> 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(
            (self.context, self.request), name='+get-involved')
        if service_uses_launchpad(involvement.answers_usage):
            # enable the listing.

--
__Curtis C. Hovey_________
http://launchpad.net/

Revision history for this message
j.c.sackett (jcsackett) wrote :

I like the multiAdapter strategy you showed and have put it in place of the for loop.

Revision history for this message
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).

review: Approve (ui*)
Revision history for this message
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/cross-team feature. The link is enabling the project to use Launcpads feature planning and documentation tracking features.

Revision history for this message
Curtis Hovey (sinzui) wrote :

Doh. My comment was intended to be UI approve.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/blueprints/browser/configure.zcml'
--- lib/lp/blueprints/browser/configure.zcml 2010-07-27 17:17:59 +0000
+++ lib/lp/blueprints/browser/configure.zcml 2010-09-17 14:23:57 +0000
@@ -53,8 +53,7 @@
53 name="+mdz-specs.csv"53 name="+mdz-specs.csv"
54 attribute="mdzCsv"/>54 attribute="mdzCsv"/>
55 <browser:page55 <browser:page
56 name="+specs"56 name="+specs"/>
57 template="../templates/hasspecifications-specs.pt"/>
58 <browser:page57 <browser:page
59 name="+portlet-latestspecs"58 name="+portlet-latestspecs"
60 template="../templates/specificationtarget-portlet-latestspecs.pt"/>59 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
@@ -500,8 +499,7 @@
500 class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"499 class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"
501 permission="zope.Public">500 permission="zope.Public">
502 <browser:page501 <browser:page
503 name="+specs"502 name="+specs"/>
504 template="../templates/hasspecifications-specs.pt"/>
505 </browser:pages>503 </browser:pages>
506 <browser:page504 <browser:page
507 for="lp.blueprints.interfaces.specificationtarget.ISpecificationGoal"505 for="lp.blueprints.interfaces.specificationtarget.ISpecificationGoal"
@@ -532,8 +530,7 @@
532 class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"530 class="lp.blueprints.browser.specificationtarget.HasSpecificationsView"
533 permission="zope.Public">531 permission="zope.Public">
534 <browser:page532 <browser:page
535 name="+specs"533 name="+specs"/>
536 template="../templates/hasspecifications-specs.pt"/>
537 <browser:page534 <browser:page
538 name="+portlet-latestspecs"535 name="+portlet-latestspecs"
539 template="../templates/specificationtarget-portlet-latestspecs.pt"/>536 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
@@ -583,8 +580,7 @@
583 facet="specifications"580 facet="specifications"
584 permission="zope.Public">581 permission="zope.Public">
585 <browser:page582 <browser:page
586 name="+specs"583 name="+specs"/>
587 template="../templates/hasspecifications-specs.pt"/>
588 <browser:page584 <browser:page
589 name="+portlet-latestspecs"585 name="+portlet-latestspecs"
590 template="../templates/specificationtarget-portlet-latestspecs.pt"/>586 template="../templates/specificationtarget-portlet-latestspecs.pt"/>
591587
=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2010-09-14 23:42:27 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2010-09-17 14:23:57 +0000
@@ -15,7 +15,11 @@
1515
16from operator import itemgetter16from operator import itemgetter
1717
18from zope.component import queryMultiAdapter18from z3c.ptcompat import ViewPageTemplateFile
19from zope.component import (
20 getMultiAdapter,
21 queryMultiAdapter,
22 )
1923
20from canonical.config import config24from canonical.config import config
21from canonical.launchpad import _25from canonical.launchpad import _
@@ -25,13 +29,19 @@
25 canonical_url,29 canonical_url,
26 LaunchpadView,30 LaunchpadView,
27 )31 )
32from canonical.launchpad.webapp.authorization import check_permission
28from canonical.launchpad.webapp.batching import BatchNavigator33from canonical.launchpad.webapp.batching import BatchNavigator
29from canonical.launchpad.webapp.breadcrumb import Breadcrumb34from canonical.launchpad.webapp.breadcrumb import Breadcrumb
30from canonical.launchpad.webapp.menu import (35from canonical.launchpad.webapp.menu import (
31 enabled_with_permission,36 enabled_with_permission,
32 Link,37 Link,
33 )38 )
34from canonical.lazr.utils import smartquote39from canonical.lazr.utils import (
40 safe_hasattr,
41 smartquote,
42 )
43from lp.app.enums import service_uses_launchpad
44from lp.app.interfaces.launchpad import IServiceUsage
35from lp.blueprints.interfaces.specification import (45from lp.blueprints.interfaces.specification import (
36 SpecificationFilter,46 SpecificationFilter,
37 SpecificationSort,47 SpecificationSort,
@@ -131,21 +141,76 @@
131 is_project = False141 is_project = False
132 is_series = False142 is_series = False
133 is_sprint = False143 is_sprint = False
144 has_wiki = False
134 has_drivers = False145 has_drivers = False
135146
147 # Templates for the various conditions of blueprints:
148 # * On Launchpad
149 # * External
150 # * Disabled
151 # * Unknown
152 default_template = ViewPageTemplateFile(
153 '../templates/hasspecifications-specs.pt')
154 not_launchpad_template = ViewPageTemplateFile(
155 '../templates/unknown-specs.pt')
156
157 @property
158 def template(self):
159 # Check for the magical "index" added by the browser:page template
160 # machinery. If it exists this is actually the
161 # zope.app.pagetemplate.simpleviewclass.simple class that is magically
162 # mixed in by the browser:page zcml directive the template defined in
163 # the directive should be used.
164 if safe_hasattr(self, 'index'):
165 return super(HasSpecificationsView, self).template
166
167 # Sprints and Persons don't have a usage enum for blueprints, so we
168 # have to fallback to the default.
169 if (ISprint.providedBy(self.context)
170 or IPerson.providedBy(self.context)):
171 return self.default_template
172
173 # ProjectGroups are a special case, as their products may be a
174 # combination of usage settings. To deal with this, check all
175 # products via the involvment menu.
176 if (IProjectGroup.providedBy(self.context)
177 or IProjectGroupSeries.providedBy(self.context)):
178 involvement = getMultiAdapter(
179 (self.context, self.request),
180 name='+get-involved')
181 if service_uses_launchpad(involvement.blueprints_usage):
182 return self.default_template
183 else:
184 return self.not_launchpad_template
185
186 # Otherwise, determine usage and provide the correct template.
187 service_usage = IServiceUsage(self.context)
188 if service_uses_launchpad(service_usage.blueprints_usage):
189 return self.default_template
190 else:
191 return self.not_launchpad_template
192
193 def render(self):
194 return self.template()
195
136 # XXX: jsk: 2007-07-12 bug=173972: This method might be improved by196 # XXX: jsk: 2007-07-12 bug=173972: This method might be improved by
137 # replacing the conditional execution with polymorphism.197 # replacing the conditional execution with polymorphism.
138 def initialize(self):198 def initialize(self):
139 if IPerson.providedBy(self.context):199 if IPerson.providedBy(self.context):
140 self.is_person = True200 self.is_person = True
141 elif (IDistribution.providedBy(self.context) or201 elif IDistribution.providedBy(self.context):
142 IProduct.providedBy(self.context)):202 self.is_target = True
143 self.is_target = True203 self.is_pillar = True
144 self.is_pillar = True204 self.show_series = True
205 elif IProduct.providedBy(self.context):
206 self.is_target = True
207 self.is_pillar = True
208 self.has_wiki = True
145 self.show_series = True209 self.show_series = True
146 elif IProjectGroup.providedBy(self.context):210 elif IProjectGroup.providedBy(self.context):
147 self.is_project = True211 self.is_project = True
148 self.is_pillar = True212 self.is_pillar = True
213 self.has_wiki = True
149 self.show_target = True214 self.show_target = True
150 self.show_series = True215 self.show_series = True
151 elif IProjectGroupSeries.providedBy(self.context):216 elif IProjectGroupSeries.providedBy(self.context):
@@ -170,6 +235,16 @@
170 size=config.launchpad.default_batch_size)235 size=config.launchpad.default_batch_size)
171236
172 @property237 @property
238 def can_configure_blueprints(self):
239 """Can the user configure blueprints for the `ISpecificationTarget`.
240 """
241 target = self.context
242 if IProduct.providedBy(target) or IDistribution.providedBy(target):
243 return check_permission('launchpad.Edit', self.context)
244 else:
245 return False
246
247 @property
173 def label(self):248 def label(self):
174 mapping = {'name': self.context.displayname}249 mapping = {'name': self.context.displayname}
175 if self.is_person:250 if self.is_person:
@@ -199,7 +274,7 @@
199 'distroseries',274 'distroseries',
200 'direction_approved',275 'direction_approved',
201 'man_days',276 'man_days',
202 'delivery'277 'delivery',
203 ]278 ]
204 def dbschema(item):279 def dbschema(item):
205 """Format a dbschema sortably for a spreadsheet."""280 """Format a dbschema sortably for a spreadsheet."""
206281
=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
--- lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-15 00:07:54 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py 2010-09-17 14:23:57 +0000
@@ -1,8 +1,12 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
55
6import unittest
7
8from zope.security.proxy import removeSecurityProxy
9
6from canonical.launchpad.webapp.batching import BatchNavigator10from canonical.launchpad.webapp.batching import BatchNavigator
7from canonical.launchpad.testing.pages import find_tag_by_id11from canonical.launchpad.testing.pages import find_tag_by_id
8from canonical.testing.layers import DatabaseFunctionalLayer12from canonical.testing.layers import DatabaseFunctionalLayer
@@ -10,6 +14,8 @@
10 IHasSpecifications,14 IHasSpecifications,
11 ISpecificationTarget,15 ISpecificationTarget,
12 )16 )
17from lp.app.enums import ServiceUsage
18from lp.blueprints.browser.specificationtarget import HasSpecificationsView
13from lp.blueprints.publisher import BlueprintsLayer19from lp.blueprints.publisher import BlueprintsLayer
14from lp.testing import (20from lp.testing import (
15 login_person,21 login_person,
@@ -53,7 +59,7 @@
53 self.verify_view(context, 'sprints/%s' % context.name)59 self.verify_view(context, 'sprints/%s' % context.name)
5460
5561
56class TestHasSpecificationsView(TestCaseWithFactory):62class TestHasSpecificationsViewInvolvement(TestCaseWithFactory):
57 """Test specification menus links."""63 """Test specification menus links."""
58 layer = DatabaseFunctionalLayer64 layer = DatabaseFunctionalLayer
5965
@@ -71,10 +77,17 @@
7177
72 def test_specificationtarget(self):78 def test_specificationtarget(self):
73 context = self.factory.makeProduct(name='almond')79 context = self.factory.makeProduct(name='almond')
80 naked_product = removeSecurityProxy(context)
81 naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD
74 self.verify_involvment(context)82 self.verify_involvment(context)
7583
76 def test_adaptable_to_specificationtarget(self):84 def test_adaptable_to_specificationtarget(self):
85 # A project should adapt to the products within to determine
86 # involvment.
77 context = self.factory.makeProject(name='hazelnut')87 context = self.factory.makeProject(name='hazelnut')
88 product = self.factory.makeProduct(project=context)
89 naked_product = removeSecurityProxy(product)
90 naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD
78 self.verify_involvment(context)91 self.verify_involvment(context)
7992
80 def test_sprint(self):93 def test_sprint(self):
@@ -128,4 +141,115 @@
128 find_tag_by_id(content, 'upper-batch-nav-batchnav-next')['class'])141 find_tag_by_id(content, 'upper-batch-nav-batchnav-next')['class'])
129 self.assertEqual('next',142 self.assertEqual('next',
130 find_tag_by_id(content, 'lower-batch-nav-batchnav-next')['class'])143 find_tag_by_id(content, 'lower-batch-nav-batchnav-next')['class'])
131 144
145
146class TestHasSpecificationsTemplates(TestCaseWithFactory):
147 """Tests the selection of templates based on blueprints usage."""
148
149 layer = DatabaseFunctionalLayer
150
151 def setUp(self):
152 super(TestHasSpecificationsTemplates, self).setUp()
153 self.user = self.factory.makePerson()
154 login_person(self.user)
155
156 def _test_templates_for_configuration(self, target, context=None):
157 if context is None:
158 context = target
159 naked_target = removeSecurityProxy(target)
160 test_configurations = [
161 ServiceUsage.UNKNOWN,
162 ServiceUsage.EXTERNAL,
163 ServiceUsage.NOT_APPLICABLE,
164 ServiceUsage.LAUNCHPAD,
165 ]
166 correct_templates = [
167 HasSpecificationsView.not_launchpad_template.filename,
168 HasSpecificationsView.not_launchpad_template.filename,
169 HasSpecificationsView.not_launchpad_template.filename,
170 HasSpecificationsView.default_template.filename,
171 ]
172 used_templates = list()
173 for config in test_configurations:
174 naked_target.blueprints_usage = config
175 view = create_view(
176 context,
177 '+specs',
178 layer=BlueprintsLayer,
179 principal=self.user)
180 used_templates.append(view.template.filename)
181 self.assertEqual(correct_templates, used_templates)
182
183 def test_product(self):
184 product = self.factory.makeProduct()
185 self._test_templates_for_configuration(product)
186
187 def test_product_series(self):
188 product = self.factory.makeProduct()
189 product_series = self.factory.makeProductSeries(product=product)
190 self._test_templates_for_configuration(
191 target=product,
192 context=product_series)
193
194 def test_distribution(self):
195 distribution = self.factory.makeDistribution()
196 self._test_templates_for_configuration(distribution)
197
198 def test_distroseries(self):
199 distribution = self.factory.makeDistribution()
200 distro_series = self.factory.makeDistroSeries(
201 distribution=distribution)
202 self._test_templates_for_configuration(
203 target=distribution,
204 context=distro_series)
205
206 def test_projectgroup(self):
207 project = self.factory.makeProject()
208 product1 = self.factory.makeProduct(project=project)
209 product2 = self.factory.makeProduct(project=project)
210 self._test_templates_for_configuration(
211 target=product1,
212 context=project)
213
214
215class TestHasSpecificationsConfiguration(TestCaseWithFactory):
216
217 layer = DatabaseFunctionalLayer
218
219 def test_cannot_configure_blueprints_product_no_edit_permission(self):
220 product = self.factory.makeProduct()
221 view = create_initialized_view(product, '+specs')
222 self.assertEqual(False, view.can_configure_blueprints)
223
224 def test_can_configure_blueprints_product_with_edit_permission(self):
225 product = self.factory.makeProduct()
226 login_person(product.owner)
227 view = create_initialized_view(product, '+specs')
228 self.assertEqual(True, view.can_configure_blueprints)
229
230 def test_cant_configure_blueprints_distribution_no_edit_permission(self):
231 distribution = self.factory.makeDistribution()
232 view = create_initialized_view(distribution, '+specs')
233 self.assertEqual(False, view.can_configure_blueprints)
234
235 def test_can_configure_blueprints_distribution_with_edit_permission(self):
236 distribution = self.factory.makeDistribution()
237 login_person(distribution.owner)
238 view = create_initialized_view(distribution, '+specs')
239 self.assertEqual(True, view.can_configure_blueprints)
240
241 def test_cannot_configure_blueprints_projectgroup(self):
242 project_group = self.factory.makeProject()
243 login_person(project_group.owner)
244 view = create_initialized_view(project_group, '+specs')
245 self.assertEqual(False, view.can_configure_blueprints)
246
247
248def test_suite():
249 suite = unittest.TestSuite()
250 suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))
251 return suite
252
253
254if __name__ == '__main__':
255 unittest.TextTestRunner().run(test_suite())
132256
=== modified file 'lib/lp/blueprints/stories/blueprints/xx-creation.txt'
--- lib/lp/blueprints/stories/blueprints/xx-creation.txt 2010-08-26 02:30:06 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-creation.txt 2010-09-17 14:23:57 +0000
@@ -1,7 +1,26 @@
1= Creating Blueprints =1Creating Blueprints
22===================
33
4== Introduction ==4Set Up
5------
6A number of tests in this need a product with blueprints enabled, so we'll
7enable them on bazaar, firefox, and jokosher.
8
9 >>> from zope.component import getUtility
10 >>> from lp.app.enums import ServiceUsage
11 >>> from lp.registry.interfaces.product import IProductSet
12 >>> login('admin@canonical.com')
13 >>> bazaar = getUtility(IProductSet).getByName('bzr')
14 >>> bazaar.blueprints_usage = ServiceUsage.LAUNCHPAD
15 >>> firefox = getUtility(IProductSet).getByName('firefox')
16 >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
17 >>> jokosher = getUtility(IProductSet).getByName('jokosher')
18 >>> jokosher.blueprints_usage = ServiceUsage.LAUNCHPAD
19 >>> transaction.commit()
20 >>> logout()
21
22Introduction
23------------
524
6Users can register blueprints from many locations in Launchpad. To start with,25Users can register blueprints from many locations in Launchpad. To start with,
7it's possible to register a blueprint from the Blueprints home page. However,26it's possible to register a blueprint from the Blueprints home page. However,
@@ -15,7 +34,9 @@
15 * a sprint34 * a sprint
1635
1736
18== The blueprint registration form ==37The blueprint registration form
38-------------------------------
39
1940
20Launchpad provides a dedicated form page for users to register blueprints.41Launchpad provides a dedicated form page for users to register blueprints.
21Generally speaking, users can navigate to this form from each of the supported42Generally speaking, users can navigate to this form from each of the supported
@@ -28,13 +49,15 @@
28we'll use extra care when its necessary to demonstrate that they both exist.49we'll use extra care when its necessary to demonstrate that they both exist.
2950
3051
31== Navigating to the blueprint registration form ==52Navigating to the blueprint registration form
53---------------------------------------------
3254
33We'll start by demonstrating that users can navigate to the blueprint55We'll start by demonstrating that users can navigate to the blueprint
34registration form from each of the supported locations.56registration form from each of the supported locations.
3557
3658
37=== From the Blueprints home page ===59From the Blueprints home page
60.............................
3861
39Starting from the Blueprints home page:62Starting from the Blueprints home page:
4063
@@ -46,7 +69,8 @@
46 <a href="+new" id="addspec"> <img alt="Register a blueprint"...69 <a href="+new" id="addspec"> <img alt="Register a blueprint"...
4770
4871
49=== From a distribution ===72From a distribution
73...................
5074
51Starting from the Ubuntu distribution page:75Starting from the Ubuntu distribution page:
5276
@@ -68,7 +92,8 @@
68 class="menu-link-new...>Register a blueprint</a>92 class="menu-link-new...>Register a blueprint</a>
6993
7094
71=== From a distribution series ===95From a distribution series
96..........................
7297
73Starting from the Ubuntu Hoary distribution series page:98Starting from the Ubuntu Hoary distribution series page:
7499
@@ -83,7 +108,8 @@
83 class="menu-link-new...>Register a blueprint</a>108 class="menu-link-new...>Register a blueprint</a>
84109
85110
86=== From a product ===111From a product
112..............
87113
88Starting from the Bazaar product page:114Starting from the Bazaar product page:
89115
@@ -109,7 +135,8 @@
109 >>> print extract_text(find_main_content(user_browser.contents))135 >>> print extract_text(find_main_content(user_browser.contents))
110 Register a new blueprint...136 Register a new blueprint...
111137
112=== From a product series ===138From a product series
139.....................
113140
114Starting from the Mozilla Firefox product series page:141Starting from the Mozilla Firefox product series page:
115142
@@ -125,7 +152,8 @@
125 class="menu-link-new...>Register a blueprint</a>152 class="menu-link-new...>Register a blueprint</a>
126153
127154
128=== From a project ===155From a project
156..............
129157
130Starting from the Mozilla project page:158Starting from the Mozilla project page:
131159
@@ -140,7 +168,8 @@
140 class="menu-link-new...>Register a blueprint</a>168 class="menu-link-new...>Register a blueprint</a>
141169
142170
143=== From a sprint ===171From a sprint
172.............
144173
145Starting from the Future Mega Meeting sprint page:174Starting from the Future Mega Meeting sprint page:
146175
@@ -155,14 +184,16 @@
155 class="menu-link-new...>Register a blueprint</a>184 class="menu-link-new...>Register a blueprint</a>
156185
157186
158== Registering a blueprint ==187Registering a blueprint
188-----------------------
159189
160The blueprint registration form allows users to register a blueprint. The190The blueprint registration form allows users to register a blueprint. The
161appearance and behaviour of the form depends on where the user has navigated191appearance and behaviour of the form depends on where the user has navigated
162from.192from.
163193
164194
165=== Registering a blueprint from the Blueprints home page ===195Registering a blueprint from the Blueprints home page
196.....................................................
166197
167We'll start from the default blueprint registration form:198We'll start from the default blueprint registration form:
168199
@@ -216,7 +247,8 @@
216 Network Magic: Auto Network Detection...247 Network Magic: Auto Network Detection...
217248
218249
219=== Registering a blueprint from a distribution ===250Registering a blueprint from a distribution
251...........................................
220252
221When a blueprint is registered from a distribution, the new blueprint is253When a blueprint is registered from a distribution, the new blueprint is
222automatically targeted to the distribution.254automatically targeted to the distribution.
@@ -239,7 +271,8 @@
239 Network Magic: Auto Network Detection...271 Network Magic: Auto Network Detection...
240272
241273
242=== Registering a blueprint from a distribution series ===274Registering a blueprint from a distribution series
275..................................................
243276
244When a blueprint is registered from a distribution series, the new blueprint277When a blueprint is registered from a distribution series, the new blueprint
245is automatically targeted to the parent distribution. In addition, Launchpad278is automatically targeted to the parent distribution. In addition, Launchpad
@@ -326,7 +359,8 @@
326 Series goal: Accepted for hoary359 Series goal: Accepted for hoary
327360
328361
329=== Registering a blueprint from a product ===362Registering a blueprint from a product
363......................................
330364
331When a blueprint is registered from a product, the new blueprint is365When a blueprint is registered from a product, the new blueprint is
332automatically targeted to the product.366automatically targeted to the product.
@@ -355,7 +389,8 @@
355 SVG Support...389 SVG Support...
356390
357391
358=== Registering a blueprint from a product series ===392Registering a blueprint from a product series
393.............................................
359394
360When a blueprint is registered from a product series, the new blueprint is395When a blueprint is registered from a product series, the new blueprint is
361automatically targeted to the parent product. In addition, Launchpad allows396automatically targeted to the parent product. In addition, Launchpad allows
@@ -437,7 +472,8 @@
437 Series goal: Accepted for 1.0472 Series goal: Accepted for 1.0
438473
439474
440=== Registering a blueprint from a project ===475Registering a blueprint from a project
476......................................
441477
442Let's register a blueprint from the Mozilla project:478Let's register a blueprint from the Mozilla project:
443479
@@ -470,7 +506,8 @@
470 SVG Support...506 SVG Support...
471507
472508
473=== Registering a blueprint from a sprint ===509Registering a blueprint from a sprint
510.....................................
474511
475When a blueprint is registered from a sprint, the new blueprint is512When a blueprint is registered from a sprint, the new blueprint is
476automatically proposed as a topic for discussion at the sprint.513automatically proposed as a topic for discussion at the sprint.
@@ -532,7 +569,8 @@
532 <...darcs-imports-2...569 <...darcs-imports-2...
533570
534571
535=== Proposing any blueprint as a sprint topic during registration ===572Proposing any blueprint as a sprint topic during registration
573.............................................................
536574
537While blueprints can be registered from sprints directly, it's also possible575While blueprints can be registered from sprints directly, it's also possible
538to propose any blueprint for discussion at a sprint during registration.576to propose any blueprint for discussion at a sprint during registration.
@@ -559,10 +597,11 @@
559 <...spec-for-sprint...>597 <...spec-for-sprint...>
560598
561599
562== Restrictions when registering blueprints ==600Restrictions when registering blueprints
563601----------------------------------------
564602
565=== Names must be unique ===603Names must be unique
604....................
566605
567It's not possible to register a blueprint with the same name as an existing606It's not possible to register a blueprint with the same name as an existing
568blueprint.607blueprint.
@@ -602,7 +641,8 @@
602 There is 1 error...already in use by another blueprint...641 There is 1 error...already in use by another blueprint...
603642
604643
605=== Names must be valid ===644Names must be valid
645...................
606646
607Blueprint names must conform to a set pattern:647Blueprint names must conform to a set pattern:
608648
@@ -635,7 +675,8 @@
635 Network Magic: Automatic Network Detection...675 Network Magic: Automatic Network Detection...
636676
637677
638=== URLs must be unique ===678URLs must be unique
679...................
639680
640It's not possible to register a blueprint with the same URL as an existing681It's not possible to register a blueprint with the same URL as an existing
641blueprint:682blueprint:
@@ -653,7 +694,8 @@
653 There is 1 error...already registered by another blueprint...694 There is 1 error...already registered by another blueprint...
654695
655696
656=== Registering blueprints from other locations ===697Registering blueprints from other locations
698...........................................
657699
658There are some locations in Launchpad from which it's not possible to register700There are some locations in Launchpad from which it's not possible to register
659a blueprint. To start with, it's not possible to register a blueprint from an701a blueprint. To start with, it's not possible to register a blueprint from an
660702
=== modified file 'lib/lp/blueprints/stories/blueprints/xx-productseries.txt'
--- lib/lp/blueprints/stories/blueprints/xx-productseries.txt 2010-08-26 02:30:06 +0000
+++ lib/lp/blueprints/stories/blueprints/xx-productseries.txt 2010-09-17 14:23:57 +0000
@@ -2,6 +2,18 @@
2Targeting to ProductSeries2Targeting to ProductSeries
3==========================3==========================
44
5A number of tests in this need a product with blueprints enabled, so we'll
6enable them on firefox.
7
8 >>> from zope.component import getUtility
9 >>> from lp.app.enums import ServiceUsage
10 >>> from lp.registry.interfaces.product import IProductSet
11 >>> login('admin@canonical.com')
12 >>> firefox = getUtility(IProductSet).getByName('firefox')
13 >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
14 >>> transaction.commit()
15 >>> logout()
16
5In terms of feature management for release series and for distroseriess, we17In terms of feature management for release series and for distroseriess, we
6want to target one of these specs to the 1.0 series. We will use the "e4x"18want to target one of these specs to the 1.0 series. We will use the "e4x"
7specification.19specification.
820
=== modified file 'lib/lp/blueprints/stories/standalone/xx-batching.txt'
--- lib/lp/blueprints/stories/standalone/xx-batching.txt 2009-09-22 10:48:09 +0000
+++ lib/lp/blueprints/stories/standalone/xx-batching.txt 2010-09-17 14:23:57 +0000
@@ -24,11 +24,25 @@
24 >>> browser.url24 >>> browser.url
25 'http://launchpad.dev/big-project'25 'http://launchpad.dev/big-project'
2626
27In the beginning, a project has no blueprints:27In the beginning, a project hasn't had blueprints set up:
2828
29 >>> browser.open("http://blueprints.launchpad.dev/big-project")29 >>> browser.open("http://blueprints.launchpad.dev/big-project")
30 >>> print extract_text(find_main_content(browser.contents))30 >>> print extract_text(find_main_content(browser.contents))
31 Blueprints...Register the first blueprint in this project!...31 Blueprints...does not know how...Configure blueprints...
32
33But it's easy to change that.
34
35 >>> browser.open("http://blueprints.launchpad.dev/big-project/+configure-blueprints")
36 >>> browser.getControl(name='field.blueprints_usage').value = ['LAUNCHPAD']
37 >>> browser.getControl('Change').click()
38 >>> browser.url
39 'http://blueprints.launchpad.dev/big-project'
40
41Initially the newly enabled feature has no blueprints.
42
43 >>> browser.open("http://blueprints.launchpad.dev/big-project")
44 >>> print extract_text(find_main_content(browser.contents))
45 Blueprints...first blueprint in this project!...
32 46
33We'll go ahead and add just a single blueprint:47We'll go ahead and add just a single blueprint:
3448
3549
=== modified file 'lib/lp/blueprints/stories/standalone/xx-index.txt'
--- lib/lp/blueprints/stories/standalone/xx-index.txt 2009-09-22 16:42:19 +0000
+++ lib/lp/blueprints/stories/standalone/xx-index.txt 2010-09-17 14:23:57 +0000
@@ -1,6 +1,20 @@
1=====================1=====================
2Blueprints index page2Blueprints index page
3=====================3=====================
4Enabling Firefox
5----------------
6
7For a product to work in blueprints, it must be active, so we'll activate
8blueprints for firefox.
9
10 >>> from zope.component import getUtility
11 >>> from lp.app.enums import ServiceUsage
12 >>> from lp.registry.interfaces.product import IProductSet
13 >>> login('admin@canonical.com')
14 >>> firefox = getUtility(IProductSet).getByName('firefox')
15 >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
16 >>> transaction.commit()
17 >>> logout()
418
5The blueprints index page allows users to search for blueprints within19The blueprints index page allows users to search for blueprints within
6a single Launchpad project or across all projects.20a single Launchpad project or across all projects.
721
=== modified file 'lib/lp/blueprints/stories/standalone/xx-overview.txt'
--- lib/lp/blueprints/stories/standalone/xx-overview.txt 2009-11-26 03:06:58 +0000
+++ lib/lp/blueprints/stories/standalone/xx-overview.txt 2010-09-17 14:23:57 +0000
@@ -7,6 +7,17 @@
7blueprints, and also other pages where users can browse through complete7blueprints, and also other pages where users can browse through complete
8lists of blueprints for a given product or distribution.8lists of blueprints for a given product or distribution.
99
10A number of tests in this need a product with blueprints enabled, so we'll
11enable them on firefox.
12
13 >>> from zope.component import getUtility
14 >>> from lp.app.enums import ServiceUsage
15 >>> from lp.registry.interfaces.product import IProductSet
16 >>> login('admin@canonical.com')
17 >>> firefox = getUtility(IProductSet).getByName('firefox')
18 >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
19 >>> transaction.commit()
20 >>> logout()
1021
11Viewing lists of blueprints22Viewing lists of blueprints
12===========================23===========================
1324
=== modified file 'lib/lp/blueprints/stories/standalone/xx-views.txt'
--- lib/lp/blueprints/stories/standalone/xx-views.txt 2009-09-22 10:48:09 +0000
+++ lib/lp/blueprints/stories/standalone/xx-views.txt 2010-09-17 14:23:57 +0000
@@ -1,6 +1,22 @@
1== Blueprint views ==1Blueprint views
22===============
3=== Viewing current blueprints ===3
4Set Up
5------
6A number of tests in this need a product with blueprints enabled, so we'll
7enable them on firefox.
8
9 >>> from zope.component import getUtility
10 >>> from lp.app.enums import ServiceUsage
11 >>> from lp.registry.interfaces.product import IProductSet
12 >>> login('admin@canonical.com')
13 >>> firefox = getUtility(IProductSet).getByName('firefox')
14 >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
15 >>> transaction.commit()
16 >>> logout()
17
18Viewing current blueprints
19--------------------------
420
5We should be able to see a "+specs" and a "+portlet-latestspecs" view21We should be able to see a "+specs" and a "+portlet-latestspecs" view
6on sprint, product, person, project and distribution.22on sprint, product, person, project and distribution.
@@ -143,7 +159,8 @@
143 ...Activating Usplash...159 ...Activating Usplash...
144160
145161
146=== Viewing all blueprints ===162Viewing all blueprints
163----------------------
147164
148From time to time it's useful to review the complete list of all blueprints165From time to time it's useful to review the complete list of all blueprints
149associated with a blueprint target, including those blueprints that have166associated with a blueprint target, including those blueprints that have
150167
=== added file 'lib/lp/blueprints/templates/unknown-specs.pt'
--- lib/lp/blueprints/templates/unknown-specs.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/blueprints/templates/unknown-specs.pt 2010-09-17 14:23:57 +0000
@@ -0,0 +1,93 @@
1<html
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 metal:use-macro="view/macro:page/main_side"
7 i18n:domain="launchpad"
8>
9
10<body>
11
12<div metal:fill-slot="main">
13 <div class="top-portlet">
14 <div id="specs-unknown">
15 <strong>
16 <div tal:omit-tag tal:condition="view/is_project">
17 <p>
18 Launchpad does not know how
19 <tal:project replace="view/context/displayname" /> tracks feature
20 planning or documentation.
21 </p>
22 </div>
23
24 <div tal:omit-tag tal:condition="view/is_series">
25 <div
26 tal:omit-tag
27 tal:define="target python:view.context.product and view.context.product or view.context.distribution;
28 blueprints_usage target/blueprints_usage">
29 <p tal:condition="blueprints_usage/enumvalue:EXTERNAL">
30 <tal:project replace="target/displayname" />'s
31 <tal:project replace="view/context/displayname" /> series does
32 not use Launchpap for planning or documentation.
33 </p>
34 <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE">
35 <tal:project replace="target/displayname" />'s
36 <tal:project replace="view/context/displayname" /> series does not track
37 feature planning or documentation.
38 </p>
39 <p tal:condition="blueprints_usage/enumvalue:UNKNOWN">
40 Launchpad does not know how
41 <tal:project replace="target/displayname" />'s
42 <tal:project replace="view/context/displayname" /> series tracks feature
43 planning or documentation.
44 </p>
45 </div>
46 </div>
47
48 <div tal:omit-tag
49 tal:condition="python:not (view.is_project or view.is_series)">
50 <div tal:omit-tag tal:define="blueprints_usage view/context/blueprints_usage">
51 <p tal:condition="blueprints_usage/enumvalue:EXTERNAL">
52 <tal:project replace="view/context/displayname" /> does not use launchpad
53 for planning or documentation.
54 </p>
55 <p tal:condition="blueprints_usage/enumvalue:NOT_APPLICABLE">
56 <tal:project replace="view/context/displayname" /> does not track
57 feature planning or documentation.
58 </p>
59 <p tal:condition="blueprints_usage/enumvalue:UNKNOWN">
60 launchpad does not know how
61 <tal:project replace="view/context/displayname" /> tracks feature
62 planning or documentation.
63 </p>
64 </div>
65 </div>
66 </strong>
67
68 <div tal:omit-tag tal:condition="view/has_wiki">
69 <p id="wiki-fallback"
70 tal:define="wiki view/context/wikiurl"
71 tal:condition="wiki">
72 <tal:project replace="view/context/displayname" /> has a wiki, which
73 may be used for feature plannning and documentation.<br />
74 <a tal:attributes="href view/context/wikiurl">
75 <tal:project replace="view/context/displayname" /> wiki
76 </a>
77 </p>
78 </div>
79 </div>
80
81 <p id="configure-support"
82 tal:condition="view/can_configure_blueprints">
83 Launchpad Blueprints allow your project to track feature planning and
84 documentation.<br />
85 <a class="info sprite"
86 href="https://help.launchpad.net/BlueprintDocumentation">
87 Read more about tracking feature planning with blueprints</a><br />
88 <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" /><br />
89 </p>
90 </div>
91</div>
92</body>
93</html>
094
=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py 2010-09-11 19:54:26 +0000
+++ lib/lp/registry/browser/distribution.py 2010-09-17 14:23:57 +0000
@@ -342,6 +342,7 @@
342 'announcements',342 'announcements',
343 'ppas',343 'ppas',
344 'configure_answers',344 'configure_answers',
345 'configure_blueprints',
345 ]346 ]
346347
347 @enabled_with_permission('launchpad.Edit')348 @enabled_with_permission('launchpad.Edit')
@@ -453,6 +454,12 @@
453 summary = 'Allow users to ask questions on this project'454 summary = 'Allow users to ask questions on this project'
454 return Link('+edit', text, summary, icon='edit')455 return Link('+edit', text, summary, icon='edit')
455456
457 @enabled_with_permission('launchpad.Edit')
458 def configure_blueprints(self):
459 text = 'Configure blueprints'
460 summary = 'Enable tracking of feature planning.'
461 return Link('+edit', text, summary, icon='edit')
462
456463
457class DerivativeDistributionOverviewMenu(DistributionOverviewMenu):464class DerivativeDistributionOverviewMenu(DistributionOverviewMenu):
458465
459466
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2010-09-03 15:02:39 +0000
+++ lib/lp/registry/browser/product.py 2010-09-17 14:23:57 +0000
@@ -547,7 +547,7 @@
547 @enabled_with_permission('launchpad.Edit')547 @enabled_with_permission('launchpad.Edit')
548 def configure_blueprints(self):548 def configure_blueprints(self):
549 text = 'Configure blueprints'549 text = 'Configure blueprints'
550 summary = 'Enable tracking of specifications and meetings'550 summary = 'Enable tracking of feature planning.'
551 return Link('+configure-blueprints', text, summary, icon='edit')551 return Link('+configure-blueprints', text, summary, icon='edit')
552552
553 @enabled_with_permission('launchpad.Edit')553 @enabled_with_permission('launchpad.Edit')
554554
=== modified file 'lib/lp/registry/browser/tests/pillar-views.txt'
--- lib/lp/registry/browser/tests/pillar-views.txt 2010-09-14 21:50:32 +0000
+++ lib/lp/registry/browser/tests/pillar-views.txt 2010-09-17 14:23:57 +0000
@@ -36,8 +36,8 @@
36 True36 True
37 >>> print view.answers_usage.name37 >>> print view.answers_usage.name
38 LAUNCHPAD38 LAUNCHPAD
39 >>> view.translations_usage.name39 >>> print view.translations_usage.name
40 'UNKNOWN'40 UNKNOWN
41 >>> print view.blueprints_usage.name41 >>> print view.blueprints_usage.name
42 UNKNOWN42 UNKNOWN
43 >>> print view.codehosting_usage.name43 >>> print view.codehosting_usage.name