← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Jonathan Sackett has proposed merging lp:~jcsackett/launchpad/unknown-blueprints-service-597738 into lp:launchpad/devel with lp:~jcsackett/launchpad/deprecate-remaining-official-bools as a prerequisite.

Requested reviews:
  Launchpad UI Reviewers (launchpad-ui-reviewers): ui
  Launchpad code reviewers (launchpad-reviewers): code
Related bugs:
  #597738 Launchpad must state the project's official services
  https://bugs.launchpad.net/bugs/597738


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

-- 
https://code.launchpad.net/~jcsackett/launchpad/unknown-blueprints-service-597738/+merge/35018
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/unknown-blueprints-service-597738 into lp:launchpad/devel.
=== modified file 'lib/lp/answers/browser/questiontarget.py'
--- lib/lp/answers/browser/questiontarget.py	2010-08-31 11:31:04 +0000
+++ lib/lp/answers/browser/questiontarget.py	2010-09-09 18:28:00 +0000
@@ -78,6 +78,7 @@
     IQuestionTarget,
     ISearchQuestionsForm,
     )
+from lp.app.enums import service_uses_launchpad
 from lp.app.errors import NotFoundError
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.product import IProduct
@@ -204,7 +205,7 @@
             return self.default_template
         involvement = getMultiAdapter(
             (self.context, self.request), name='+get-involved')
-        if involvement.official_answers:
+        if service_uses_launchpad(involvement.answers_usage):
             # Primary contexts that officially use answers have a
             # search and listing presentation.
             return self.default_template

=== modified file 'lib/lp/answers/doc/question.txt'
--- lib/lp/answers/doc/question.txt	2010-07-28 16:56:05 +0000
+++ lib/lp/answers/doc/question.txt	2010-09-09 18:28:00 +0000
@@ -72,13 +72,13 @@
 ==============
 
 A product or distribution may be officially supported by the community using
-the Answer Tracker.  This status is set by the official_answers attribute on
+the Answer Tracker.  This status is set by the answers_usage attribute on
 the IProduct and IDistribution.
 
-    >>> ubuntu.official_answers
-    True
-    >>> firefox.official_answers
-    True
+    >>> print ubuntu.answers_usage.name
+    LAUNCHPAD
+    >>> print firefox.answers_usage.name
+    LAUNCHPAD
 
 
 IQuestion interface

=== modified file 'lib/lp/answers/doc/questionsets.txt'
--- lib/lp/answers/doc/questionsets.txt	2010-07-27 17:18:24 +0000
+++ lib/lp/answers/doc/questionsets.txt	2010-09-09 18:28:00 +0000
@@ -208,14 +208,14 @@
 order of the returned projects is based on the number of questions asked
 during the period.
 
-    >>> ubuntu.official_answers
-    True
-    >>> firefox.official_answers
-    True
-    >>> landscape.official_answers
-    False
-    >>> launchpad.official_answers
-    True
+    >>> print ubuntu.answers_usage.name
+    LAUNCHPAD
+    >>> print firefox.answers_usage.name
+    LAUNCHPAD
+    >>> print landscape.answers_usage.name
+    UNKNOWN
+    >>> print launchpad.answers_usage.name
+    LAUNCHPAD
 
     # Launchpad is not returned because the question was not asked in
     # the last 60 days.

=== modified file 'lib/lp/app/enums.py'
--- lib/lp/app/enums.py	2010-08-20 20:31:18 +0000
+++ lib/lp/app/enums.py	2010-09-09 18:28:00 +0000
@@ -1,11 +1,12 @@
 # Copyright 2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-"""Enumerations used in the lp/app modules."""
+"""Enumerations and related utilities used in the lp/app modules."""
 
 __metaclass__ = type
 __all__ = [
     'ServiceUsage',
+    'service_uses_launchpad',
     ]
 
 from lazr.enum import (
@@ -45,3 +46,7 @@
 
     The pillar does not use this type of service in Launchpad or externally.
     """)
+
+
+def service_uses_launchpad(usage_enum):
+    return usage_enum == ServiceUsage.LAUNCHPAD

=== 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-09 18:28:00 +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:28: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'):
+            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 specifications exist, ignore the usage enum.
+        if self.has_any_specifications:
+            return self.default_template
+
+        # Otherwise, determine usage and provide the correct template.
+        service_usage = IServiceUsage(self.context)
+        if service_uses_launchpad(service_usage.blueprints_usage):
+            return self.default_template
+        else:
+            return self.not_launchpad_template
+
+    def render(self):
+        return self.template()
+
     # XXX: jsk: 2007-07-12 bug=173972: This method might be improved by
     # replacing the conditional execution with polymorphism.
     def initialize(self):
@@ -199,7 +246,7 @@
             'distroseries',
             'direction_approved',
             'man_days',
-            'delivery'
+            'delivery',
             ]
         def dbschema(item):
             """Format a dbschema sortably for a spreadsheet."""

=== modified file 'lib/lp/blueprints/browser/tests/test_specificationtarget.py'
--- lib/lp/blueprints/browser/tests/test_specificationtarget.py	2010-08-20 20:31:18 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationtarget.py	2010-09-09 18:28:00 +0000
@@ -1,15 +1,18 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
 
 import unittest
 
+from zope.security.proxy import removeSecurityProxy
+
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.blueprints.interfaces.specificationtarget import (
     IHasSpecifications,
     ISpecificationTarget,
     )
+from lp.app.enums import ServiceUsage
 from lp.blueprints.publisher import BlueprintsLayer
 from lp.testing import (
     login_person,
@@ -50,7 +53,7 @@
         self.verify_view(context, 'sprints/%s' % context.name)
 
 
-class TestHasSpecificationsView(TestCaseWithFactory):
+class TestHasSpecificationsViewInvolvement(TestCaseWithFactory):
     """Test specification menus links."""
     layer = DatabaseFunctionalLayer
 
@@ -68,6 +71,8 @@
 
     def test_specificationtarget(self):
         context = self.factory.makeProduct(name='almond')
+        naked_product = removeSecurityProxy(context)
+        naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD
         self.verify_involvment(context)
 
     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)
+
+    def test_not_configured(self):
+        self.naked_product.blueprints_usage = ServiceUsage.UNKNOWN
+        view = create_view(
+            self.product,
+            '+specs',
+            layer=BlueprintsLayer,
+            principal=self.user)
+        self.assertEqual(
+            view.not_launchpad_template.filename,
+            view.template.filename)
+
+    def test_external(self):
+        self.naked_product.blueprints_usage = ServiceUsage.EXTERNAL
+        view = create_view(
+            self.product,
+            '+specs',
+            layer=BlueprintsLayer,
+            principal=self.user)
+        self.assertEqual(
+            view.not_launchpad_template.filename,
+            view.template.filename)
+
+    def test_not_applicable(self):
+        self.naked_product.blueprints_usage = ServiceUsage.NOT_APPLICABLE
+        view = create_view(
+            self.product,
+            '+specs',
+            layer=BlueprintsLayer,
+            principal=self.user)
+        self.assertEqual(
+            view.not_launchpad_template.filename,
+            view.template.filename)
+
+    def test_on_launchpad(self):
+        self.naked_product.blueprints_usage = ServiceUsage.LAUNCHPAD
+        view = create_view(
+            self.product,
+            '+specs',
+            layer=BlueprintsLayer,
+            principal=self.user)
+        self.assertEqual(
+            view.default_template.filename,
+            view.template.filename)
+
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))

=== 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-09 18:28:00 +0000
@@ -0,0 +1,59 @@
+<html
+  xmlns="http://www.w3.org/1999/xhtml";
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  metal:use-macro="view/macro:page/main_side"
+  i18n:domain="launchpad"
+>
+
+<body>
+
+<div metal:fill-slot="main"
+     tal:define="specs view/specs;
+                 has_any_specs view/has_any_specifications">
+
+    <div tal:condition="view/context/blueprints_usage/enumvalue:EXTERNAL">
+        <strong><tal:project replace="view/context/displayname" /> does not use Launchpad
+        for specification tracking.</strong>
+    </div>
+
+    <div tal:condition="view/context/blueprints_usage/enumvalue:NOT_APPLICABLE">
+        <strong><tal:project replace="view/context/displayname" /> does not track
+        specifications.</strong>
+    </div>
+
+    <div tal:condition="view/context/blueprints_usage/enumvalue:UNKNOWN">
+        <strong>
+        Launchpad does not know how
+        <tal:project replace="view/context/displayname" /> uses specifications.
+        </strong>
+    </div>
+
+    <div tal:condition="view/context/wikiurl">
+     <p><tal:project replace="view/context/displayname" /> has a wiki, which
+      may be used for specifications.</p>
+
+      <p>
+        <a tal:attributes="href view/context/wikiurl">
+        <tal:project replace="view/context/displayname" /> wiki 
+        </a>
+      </p>
+
+    </div>
+    <ul class="horizontal">
+      <li>
+        <a class="info sprite"
+          href="https://help.launchpad.net/BlueprintDocumentation";>
+          Read more about tracking blueprints
+        </a>
+      </li>
+      </ul>
+      <ul class="horiztonal">
+      <li>
+        <a tal:replace="structure context/menu:overview/configure_blueprints/fmt:link" />
+      </li>
+    </ul>
+</div>
+</body>
+</html>

=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py	2010-09-03 03:12:39 +0000
+++ lib/lp/registry/browser/distribution.py	2010-09-09 18:28:00 +0000
@@ -74,6 +74,7 @@
 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
 from canonical.launchpad.webapp.interfaces import ILaunchBag
 from canonical.widgets.image import ImageChangeWidget
+from lp.app.enums import service_uses_launchpad
 from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
 from lp.answers.browser.questiontarget import (
     QuestionTargetFacetMixin,
@@ -122,10 +123,10 @@
         """Return a string of LP apps (comma-separated) this distro uses."""
         uses = []
         href_template = """<a href="%s">%s</a>"""
-        if self.context.official_answers:
+        if service_uses_launchpad(self.context.answers_usage):
             url = canonical_url(self.context, rootsite='answers')
             uses.append(href_template % (url, 'Answers'))
-        if self.context.official_blueprints:
+        if service_uses_launchpad(self.context.blueprints_usage):
             url = canonical_url(self.context, rootsite='blueprints')
             uses.append(href_template % (url, 'Blueprints'))
         if self.context.official_malone:

=== modified file 'lib/lp/registry/browser/pillar.py'
--- lib/lp/registry/browser/pillar.py	2010-09-03 03:12:39 +0000
+++ lib/lp/registry/browser/pillar.py	2010-09-09 18:28:00 +0000
@@ -30,7 +30,10 @@
     nearest,
     )
 from canonical.launchpad.webapp.tales import MenuAPI
-from lp.app.enums import ServiceUsage
+from lp.app.enums import (
+    ServiceUsage,
+    service_uses_launchpad,
+    )
 from lp.app.interfaces.launchpad import IServiceUsage
 from lp.registry.browser.structuralsubscription import (
     StructuralSubscriptionMenuMixin,
@@ -67,7 +70,7 @@
     def ask_question(self):
         return Link(
             '+addquestion', 'Ask a question', site='answers', icon='answers',
-            enabled=self.pillar.official_answers)
+            enabled=service_uses_launchpad(self.pillar.answers_usage))
 
     def help_translate(self):
         return Link(
@@ -88,8 +91,11 @@
 
     def register_blueprint(self):
         return Link(
-            '+addspec', 'Register a blueprint', site='blueprints',
-            icon='blueprints', enabled=self.pillar.official_blueprints)
+            '+addspec',
+            'Register a blueprint',
+            site='blueprints',
+            icon='blueprints',
+            enabled=service_uses_launchpad(self.pillar.blueprints_usage))
 
 
 class PillarView(LaunchpadView):
@@ -102,8 +108,8 @@
     def __init__(self, context, request):
         super(PillarView, self).__init__(context, request)
         self.official_malone = False
-        self.official_answers = False
-        self.official_blueprints = False
+        self.answers_usage = ServiceUsage.UNKNOWN
+        self.blueprints_usage = ServiceUsage.UNKNOWN
         self.official_rosetta = False
         self.codehosting_usage = ServiceUsage.UNKNOWN
         pillar = nearest(self.context, IPillar)
@@ -116,11 +122,11 @@
         else:
             self._set_official_launchpad(pillar)
             if IDistroSeries.providedBy(self.context):
-                self.official_answers = False
                 distribution = self.context.distribution
                 self.codehosting_usage = distribution.codehosting_usage
+                self.answers_usage = ServiceUsage.NOT_APPLICABLE
             elif IDistributionSourcePackage.providedBy(self.context):
-                self.official_blueprints = False
+                self.blueprints_usage = ServiceUsage.UNKNOWN
                 self.official_rosetta = False
             else:
                 # The context is used by all apps.
@@ -132,21 +138,21 @@
         # times to build the complete set of official applications.
         if pillar.official_malone:
             self.official_malone = True
-        if pillar.official_answers:
-            self.official_answers = True
-        if pillar.official_blueprints:
-            self.official_blueprints = True
+        if service_uses_launchpad(IServiceUsage(pillar).answers_usage):
+            self.answers_usage = ServiceUsage.LAUNCHPAD
+        if service_uses_launchpad(IServiceUsage(pillar).blueprints_usage):
+            self.blueprints_usage = ServiceUsage.LAUNCHPAD
         if pillar.official_rosetta:
             self.official_rosetta = True
-        self.codehosting_usage = IServiceUsage(pillar).codehosting_usage
-
+        if service_uses_launchpad(IServiceUsage(pillar).codehosting_usage):
+            self.codehosting_usage = ServiceUsage.LAUNCHPAD
     @property
     def has_involvement(self):
         """This `IPillar` uses Launchpad."""
-        return (
-            self.official_malone or self.official_answers
-            or self.official_blueprints or self.official_rosetta
-            or self.codehosting_usage == ServiceUsage.LAUNCHPAD)
+        return (self.official_malone or self.official_rosetta
+            or service_uses_launchpad(self.answers_usage)
+            or service_uses_launchpad(self.blueprints_usage)
+            or service_uses_launchpad(self.codehosting_usage))
 
     @property
     def enabled_links(self):

=== modified file 'lib/lp/registry/browser/productseries.py'
--- lib/lp/registry/browser/productseries.py	2010-09-03 15:02:39 +0000
+++ lib/lp/registry/browser/productseries.py	2010-09-09 18:28:00 +0000
@@ -254,11 +254,11 @@
 
     def __init__(self, context, request):
         super(ProductSeriesInvolvementView, self).__init__(context, request)
+        self.answers_usage = ServiceUsage.NOT_APPLICABLE
         if self.context.branch is not None:
             self.codehosting_usage = ServiceUsage.LAUNCHPAD
         else:
             self.codehosting_usage = ServiceUsage.UNKNOWN
-        self.official_answers = False
 
     @property
     def configuration_links(self):

=== modified file 'lib/lp/registry/browser/tests/distribution-views.txt'
--- lib/lp/registry/browser/tests/distribution-views.txt	2010-08-30 18:26:19 +0000
+++ lib/lp/registry/browser/tests/distribution-views.txt	2010-09-09 18:28:00 +0000
@@ -274,11 +274,12 @@
 If the distribution officially uses the application, its portlet does appear.
 
     >>> from canonical.testing.layers import MemcachedLayer
+    >>> from lp.app.enums import ServiceUsage
 
     # When the cache regenerated, all users will see the change.
     >>> MemcachedLayer.purge()
-    >>> distribution.official_answers = True
-    >>> distribution.official_blueprints = True
+    >>> distribution.answers_usage = ServiceUsage.LAUNCHPAD
+    >>> distribution.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> distribution.official_malone = True
 
     >>> view = create_view(distribution, name='+index', principal=owner)

=== modified file 'lib/lp/registry/browser/tests/pillar-views.txt'
--- lib/lp/registry/browser/tests/pillar-views.txt	2010-08-27 17:45:57 +0000
+++ lib/lp/registry/browser/tests/pillar-views.txt	2010-09-09 18:28:00 +0000
@@ -24,7 +24,8 @@
 
 Pillars that do use launchpad applications have an involvement menu.
 
-    >>> distribution.official_answers = True
+    >>> from lp.app.enums import ServiceUsage
+    >>> distribution.answers_usage = ServiceUsage.LAUNCHPAD
     >>> distribution.official_malone = True
     >>> view = create_view(
     ...     distribution, '+get-involved', principal=distribution.owner)
@@ -33,14 +34,14 @@
 
     >>> view.official_malone
     True
-    >>> view.official_answers
-    True
+    >>> print view.answers_usage.name
+    LAUNCHPAD
     >>> view.official_rosetta
     False
-    >>> view.official_blueprints
-    False
-    >>> view.codehosting_usage.name
-    'NOT_APPLICABLE'
+    >>> print view.blueprints_usage.name
+    UNKNOWN
+    >>> print view.codehosting_usage.name
+    UNKNOWN
 
 The view provides a list of enabled links that is rendered by the template.
 
@@ -65,10 +66,10 @@
 
     >>> product = factory.makeProduct(name='bread')
     >>> login_person(product.owner)
-    >>> product.official_blueprints = True
+    >>> product.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> view = create_view(product, '+get-involved')
-    >>> view.official_blueprints
-    True
+    >>> print view.blueprints_usage.name
+    LAUNCHPAD
     >>> for link in view.enabled_links:
     ...     print link.name
     register_blueprint
@@ -114,7 +115,6 @@
 
 Changing the product's usage is reflected in the view properties.
 
-    >>> from lp.app.enums import ServiceUsage
     >>> product.translations_usage = ServiceUsage.LAUNCHPAD
     >>> view = create_view(product, '+get-involved')
     >>> for key in sorted(view.configuration_states.keys()):
@@ -189,8 +189,8 @@
     >>> product.project = project_group
 
     >>> view = create_view(project_group, '+get-involved')
-    >>> view.official_blueprints
-    True
+    >>> print view.blueprints_usage.name
+    LAUNCHPAD
 
 The offical_codehosting for a project is based on whether the project's
 development focus series has a branch.
@@ -200,23 +200,23 @@
     >>> product.official_codehosting
     False
     >>> view = create_view(product, '+get-involved')
-    >>> view.codehosting_usage.name
-    'UNKNOWN'
+    >>> print view.codehosting_usage.name
+    UNKNOWN
 
     >>> product.development_focus.branch = factory.makeBranch(
     ...     product=product)
     >>> product.official_codehosting
     True
     >>> view = create_view(product, '+get-involved')
-    >>> view.codehosting_usage.name
-    'LAUNCHPAD' 
+    >>> print view.codehosting_usage.name
+    LAUNCHPAD
 
 Project groups cannot make links to register a branch, so
 official_codehosting is always false.
 
     >>> view = create_view(project_group, '+get-involved')
-    >>> view.codehosting_usage.name
-    'NOT_APPLICABLE'
+    >>> print view.codehosting_usage.name
+    NOT_APPLICABLE
 
 DistroSeries can use this view. The distribution is used to set the links.
 
@@ -230,8 +230,9 @@
 set the links.  Despite the fact that the distribution uses blueprints,
 and translations those links are not enabled for DistributionSourcePackages.
 
+    >>> from lp.app.enums import ServiceUsage
     >>> login_person(distribution.owner)
-    >>> distribution.official_blueprints = True
+    >>> distribution.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> distribution.official_rosetta = True
     >>> package = factory.makeDistributionSourcePackage(
     ...     sourcepackagename="box",

=== modified file 'lib/lp/registry/browser/tests/product-views.txt'
--- lib/lp/registry/browser/tests/product-views.txt	2010-08-16 19:28:37 +0000
+++ lib/lp/registry/browser/tests/product-views.txt	2010-09-09 18:28:00 +0000
@@ -482,8 +482,9 @@
 The portlet are rendered when a product officially uses the Launchpad
 Answers, Blueprints, and Bugs applications.
 
-    >>> product.official_answers = True
-    >>> product.official_blueprints = True
+    >>> from lp.app.enums import ServiceUsage
+    >>> product.answers_usage = ServiceUsage.LAUNCHPAD
+    >>> product.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> product.official_malone = True
 
     >>> view = create_initialized_view(

=== modified file 'lib/lp/registry/browser/tests/productseries-views.txt'
--- lib/lp/registry/browser/tests/productseries-views.txt	2010-09-03 06:06:40 +0000
+++ lib/lp/registry/browser/tests/productseries-views.txt	2010-09-09 18:28:00 +0000
@@ -23,18 +23,19 @@
 The ProductSeries involvement view uses the ProductSeriesInvolvedMenu when
 rendering links:
 
+    >>> from lp.app.enums import ServiceUsage
     >>> login_person(product.owner)
-    >>> product.official_answers = True
-    >>> product.official_blueprints = True
+    >>> product.answers_usage = ServiceUsage.LAUNCHPAD
+    >>> product.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> product.official_malone = True
     >>> product.official_rosetta = True
     >>> view = create_view(series, '+get-involved')
 
-    # official_answers is always false for product series.
-    >>> print view.official_answers
-    False
-    >>> print view.official_blueprints
-    True
+    # answers_usage is never LAUNCHPAD for product series.
+    >>> print view.answers_usage.name
+    NOT_APPLICABLE
+    >>> print view.blueprints_usage.name
+    LAUNCHPAD
     >>> print view.official_malone
     True
     >>> print view.official_rosetta

=== modified file 'lib/lp/registry/browser/tests/projectgroup-views.txt'
--- lib/lp/registry/browser/tests/projectgroup-views.txt	2009-08-31 20:46:06 +0000
+++ lib/lp/registry/browser/tests/projectgroup-views.txt	2010-09-09 18:28:00 +0000
@@ -71,12 +71,14 @@
 The portlet are rendered when a child product officially uses the Launchpad
 Answers, Blueprints, and Bugs applications.
 
-    >>> product.official_answers = True
-    >>> product.official_blueprints = True
+    >>> from lp.app.enums import ServiceUsage
+    >>> product.answers_usage = ServiceUsage.LAUNCHPAD
+    >>> product.blueprints_usage = ServiceUsage.LAUNCHPAD
     >>> product.official_malone = True
 
     >>> view = create_view(projectgroup, name='+index', principal=owner)
     >>> content = find_tag_by_id(view.render(), 'maincontent')
+
     >>> print find_tag_by_id(content, 'portlet-latest-faqs')['id']
     portlet-latest-faqs
     >>> print find_tag_by_id(content, 'portlet-latest-questions')['id']

=== modified file 'lib/lp/registry/doc/distribution.txt'
--- lib/lp/registry/doc/distribution.txt	2010-08-23 00:51:30 +0000
+++ lib/lp/registry/doc/distribution.txt	2010-09-09 18:28:00 +0000
@@ -1,4 +1,5 @@
-= Distributions =
+Distributions
+=============
 
 From the DerivationOverview spec
 <https://launchpad.canonical.com/DerivationOverview>:
@@ -143,7 +144,8 @@
     False
 
 
-== Distribution Sorting ==
+Distribution Sorting
+--------------------
 
 If you ask for all the distributions in the DistributionSet you should get
 Ubuntu (and all flavours of it) first and the rest alphabetically:
@@ -164,7 +166,8 @@
     True
 
 
-=== Searching for DistributionSourcePackages ===
+Searching for DistributionSourcePackages
+........................................
 
 The distribution also allows you to look for source packages that match
 a certain string through the magic of fti. For instance:
@@ -232,7 +235,8 @@
     DistributionSourcePackage: alsa-utils
 
 
-=== Searching for binary packages ===
+Searching for binary packages
+.............................
 
 There are two useful functions for searching binary packages related
 to the distribution, searchBinaryPackages() and searchBinaryPackagesFTI().
@@ -291,7 +295,8 @@
     [u'mozilla-firefox']
 
 
-=== Finding distroseriess and pockets from distribution names ===
+Finding distroseriess and pockets from distribution names
+.........................................................
 
 A distribution knows what distroseriess it has. Those distroseriess have
 pockets which have suffixes used by the archive publisher. Because we
@@ -336,26 +341,27 @@
     NotFoundError: 'hoary-bullshit'
 
 
-=== Upload related stuff ===
+Upload related stuff
+....................
 
 When uploading to a distribution we need to query its uploaders. Each
 uploader record is in fact an ArchivePermission record that tells us
 what component is uploadable to by what person or group of people.
 
-   >>> from operator import attrgetter
-   >>> for permission in sorted(
-   ...     ubuntu.uploaders, key=attrgetter("id")):
-   ...     assert not permission.archive.is_ppa
-   ...     print permission.component.name
-   ...     print permission.person.displayname
-   universe
-   Ubuntu Team
-   restricted
-   Ubuntu Team
-   main
-   Ubuntu Team
-   partner
-   Canonical Partner Developers
+    >>> from operator import attrgetter
+    >>> for permission in sorted(
+    ...     ubuntu.uploaders, key=attrgetter("id")):
+    ...     assert not permission.archive.is_ppa
+    ...     print permission.component.name
+    ...     print permission.person.displayname
+    universe
+    Ubuntu Team
+    restricted
+    Ubuntu Team
+    main
+    Ubuntu Team
+    partner
+    Canonical Partner Developers
 
 When processing an upload we may want to find a file (E.g. if an
 incomplete source is uploaded).
@@ -375,7 +381,8 @@
     AssertionError: searching in an explicitly empty space is pointless
 
 
-=== Launchpad Usage ===
+Launchpad Usage
+...............
 
 A distribution can specify if it uses Malone, Rosetta, or Answers
 officially. Ubuntu uses all of them:
@@ -387,24 +394,24 @@
     True
     >>> ubuntu.official_rosetta
     True
-    >>> ubuntu.official_answers
-    True
-    >>> ubuntu.official_blueprints
-    True
+    >>> print ubuntu.answers_usage.name
+    LAUNCHPAD
+    >>> print ubuntu.blueprints_usage.name
+    LAUNCHPAD
 
 The bug_tracking_usage property currently only tracks official_malone.
 
     >>> print ubuntu.bug_tracking_usage.name
     LAUNCHPAD
 
-While the other attributes track the other official_ attributes. 
+While the other attributes track the other official_ attributes.
 
     >>> print ubuntu.official_rosetta
     True
     >>> print ubuntu.translations_usage.name
-    LAUNCHPAD 
-    >>> print ubuntu.official_answers
-    True
+    LAUNCHPAD
+    >>> print ubuntu.answers_usage.name
+    LAUNCHPAD
     >>> print ubuntu.answers_usage.name
     LAUNCHPAD
     >>> print ubuntu.official_blueprints
@@ -438,12 +445,12 @@
     False
     >>> debian.official_rosetta
     False
-    >>> debian.official_answers
-    False
+    >>> print debian.answers_usage.name
+    UNKNOWN
     >>> debian.official_codehosting
     False
-    >>> debian.official_blueprints
-    False
+    >>> print debian.blueprints_usage.name
+    UNKNOWN
 
 Gentoo only uses Malone
 
@@ -451,16 +458,17 @@
     True
     >>> print gentoo.official_rosetta
     False
-    >>> print gentoo.official_answers
-    False
+    >>> print gentoo.answers_usage.name
+    UNKNOWN
 
 Launchpad admins and the distro owner can set these fields.
 
+    >>> from lp.app.enums import ServiceUsage
     >>> login('mark@xxxxxxxxxxx')
     >>> debian = getUtility(ILaunchpadCelebrities).debian
-    >>> debian.official_blueprints = True
-    >>> debian.official_blueprints
-    True
+    >>> debian.blueprints_usage = ServiceUsage.LAUNCHPAD
+    >>> print debian.blueprints_usage.name
+    LAUNCHPAD
     >>> debian.official_malone = True
     >>> debian.official_malone
     True
@@ -471,16 +479,16 @@
     >>> debian_owner = factory.makePerson()
     >>> debian.owner = debian_owner
     >>> login_person(debian_owner)
-    >>> debian.official_blueprints = False
-    >>> debian.official_blueprints
-    False
+    >>> debian.blueprints_usage = ServiceUsage.NOT_APPLICABLE
+    >>> print debian.blueprints_usage.name
+    NOT_APPLICABLE
 
 But others can't.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
-    >>> debian.official_blueprints = True
+    >>> debian.blueprints_usage = ServiceUsage.LAUNCHPAD
     Traceback (most recent call last):
-    Unauthorized: (..., 'official_blueprints', 'launchpad.Edit')
+    Unauthorized: (..., 'blueprints_usage', 'launchpad.Edit')
     >>> debian.official_malone = True
     Traceback (most recent call last):
     Unauthorized: (..., 'official_malone', 'launchpad.Edit')
@@ -489,7 +497,8 @@
     Unauthorized: (..., 'official_rosetta', 'launchpad.Edit')
 
 
-=== Specification Listings ===
+Specification Listings
+......................
 
 We should be able to get lists of specifications in different states
 related to a distro.
@@ -497,90 +506,93 @@
 Basically, we can filter by completeness, and by whether or not the spec is
 informational.
 
- >>> kubuntu = distroset.getByName("kubuntu")
+    >>> kubuntu = distroset.getByName("kubuntu")
 
- >>> from canonical.launchpad.interfaces import SpecificationFilter
+    >>> from canonical.launchpad.interfaces import SpecificationFilter
 
 First, there should be one informational spec for kubuntu, but it is
 complete so it will not show up unless we explicitly ask for complete specs:
 
- >>> filter = [SpecificationFilter.INFORMATIONAL]
- >>> kubuntu.specifications(filter=filter).count()
- 0
- >>> filter = [SpecificationFilter.INFORMATIONAL,
- ...           SpecificationFilter.COMPLETE]
- >>> kubuntu.specifications(filter=filter).count()
- 1
+    >>> filter = [SpecificationFilter.INFORMATIONAL]
+    >>> kubuntu.specifications(filter=filter).count()
+    0
+    >>> filter = [SpecificationFilter.INFORMATIONAL,
+    ...           SpecificationFilter.COMPLETE]
+    >>> kubuntu.specifications(filter=filter).count()
+     1
 
 
 There are 2 completed specs for Kubuntu:
 
- >>> filter = [SpecificationFilter.COMPLETE]
- >>> for spec in kubuntu.specifications(filter=filter):
- ...    print spec.name, spec.is_complete
- thinclient-local-devices True
- usplash-on-hibernation True
+    >>> filter = [SpecificationFilter.COMPLETE]
+    >>> for spec in kubuntu.specifications(filter=filter):
+    ...    print spec.name, spec.is_complete
+    thinclient-local-devices True
+    usplash-on-hibernation True
 
 
 And there are four incomplete specs:
 
- >>> filter = [SpecificationFilter.INCOMPLETE]
- >>> for spec in kubuntu.specifications(filter=filter):
- ...    print spec.name, spec.is_complete
- cluster-installation False
- revu False
- kde-desktopfile-langpacks False
- krunch-desktop-plan False
+    >>> filter = [SpecificationFilter.INCOMPLETE]
+    >>> for spec in kubuntu.specifications(filter=filter):
+    ...    print spec.name, spec.is_complete
+    cluster-installation False
+    revu False
+    kde-desktopfile-langpacks False
+    krunch-desktop-plan False
 
 
 If we ask for all specs, we get them in the order of priority.
 
- >>> filter = [SpecificationFilter.ALL]
- >>> for spec in kubuntu.specifications(filter=filter):
- ...    print spec.priority.title, spec.name
- Essential cluster-installation
- High revu
- Medium thinclient-local-devices
- Low usplash-on-hibernation
- Undefined kde-desktopfile-langpacks
- Not krunch-desktop-plan
+    >>> filter = [SpecificationFilter.ALL]
+    >>> for spec in kubuntu.specifications(filter=filter):
+    ...    print spec.priority.title, spec.name
+    Essential cluster-installation
+    High revu
+    Medium thinclient-local-devices
+    Low usplash-on-hibernation
+    Undefined kde-desktopfile-langpacks
+    Not krunch-desktop-plan
 
 
 And if we ask just for specs, we get the incomplete ones.
 
- >>> for spec in kubuntu.specifications():
- ...     print spec.name, spec.is_complete
- cluster-installation False
- revu False
- kde-desktopfile-langpacks False
- krunch-desktop-plan False
+    >>> for spec in kubuntu.specifications():
+    ...     print spec.name, spec.is_complete
+    cluster-installation False
+    revu False
+    kde-desktopfile-langpacks False
+    krunch-desktop-plan False
 
 We can filter for specifications that contain specific text:
 
- >>> for spec in kubuntu.specifications(filter=['package']):
- ...     print spec.name
- revu
+    >>> for spec in kubuntu.specifications(filter=['package']):
+    ...     print spec.name
+    revu
 
 We can get only valid specs (those that are not obsolete or superseded):
 
- >>> from canonical.launchpad.interfaces import SpecificationDefinitionStatus
- >>> login('mark@xxxxxxxxxxx')
- >>> for spec in kubuntu.specifications():
- ...     # Do this here, otherwise, the change will be flush before
- ...     # updateLifecycleStatus() acts and an IntegrityError will be
- ...     # raised.
- ...     owner = spec.owner
- ...     if spec.name in ['cluster-installation', 'revu']:
- ...         spec.definition_status = SpecificationDefinitionStatus.OBSOLETE
- ...     if spec.name in ['krunch-desktop-plan']:
- ...         spec.definition_status = SpecificationDefinitionStatus.SUPERSEDED
- ...     shim = spec.updateLifecycleStatus(owner)
- >>> for spec in kubuntu.valid_specifications:
- ...     print spec.name
- kde-desktopfile-langpacks
-
-
-== Milestones ==
+    >>> from canonical.launchpad.interfaces import (
+    ...     SpecificationDefinitionStatus,
+    ...     )
+    >>> login('mark@xxxxxxxxxxx')
+    >>> for spec in kubuntu.specifications():
+    ...     # Do this here, otherwise, the change will be flush before
+    ...     # updateLifecycleStatus() acts and an IntegrityError will be
+    ...     # raised.
+    ...     owner = spec.owner
+    ...     if spec.name in ['cluster-installation', 'revu']:
+    ...         spec.definition_status = SpecificationDefinitionStatus.OBSOLETE
+    ...     if spec.name in ['krunch-desktop-plan']:
+    ...         spec.definition_status = SpecificationDefinitionStatus.SUPERSEDED
+    ...     shim = spec.updateLifecycleStatus(owner)
+    >>> for spec in kubuntu.valid_specifications:
+    ...     print spec.name
+    kde-desktopfile-langpacks
+
+
+Milestones
+----------
 
 We can use IDistribution.milestones to get all milestones associated with any
 series of a distribution.
@@ -623,7 +635,8 @@
     [u'3.1', u'3.1-rc1', u'woody-rc1']
 
 
-== Archives ==
+Archives
+--------
 
 A distribution archive (primary, partner, debug or copy) can be retrieved
 by name using IDistribution.getArchive.

=== modified file 'lib/lp/registry/doc/product.txt'
--- lib/lp/registry/doc/product.txt	2010-08-25 15:48:05 +0000
+++ lib/lp/registry/doc/product.txt	2010-09-09 18:28:00 +0000
@@ -1,4 +1,5 @@
-= Product =
+Product
+=======
 
 Launchpad keeps track of the "upstream" world as well as the "distro" world.
 The anchorpiece of the "upstream" world is the Product, which is a piece of
@@ -162,7 +163,8 @@
     Obsolete Junk
 
 
-== Translatable Products ==
+Translatable Products
+---------------------
 
 IProductSet will also tell us which products can be translated:
 
@@ -217,11 +219,11 @@
 The packaging table allows us to list source and distro source packages
 related to a certain upstream:
 
-   >>> alsa = productset.getByName('alsa-utils')
-   >>> [(sp.name, sp.distroseries.name) for sp in alsa.sourcepackages]
-   [(u'alsa-utils', u'sid'), (u'alsa-utils', u'warty')]
-   >>> [(sp.name, sp.distribution.name) for sp in alsa.distrosourcepackages]
-   [(u'alsa-utils', u'debian'), (u'alsa-utils', u'ubuntu')]
+    >>> alsa = productset.getByName('alsa-utils')
+    >>> [(sp.name, sp.distroseries.name) for sp in alsa.sourcepackages]
+    [(u'alsa-utils', u'sid'), (u'alsa-utils', u'warty')]
+    >>> [(sp.name, sp.distribution.name) for sp in alsa.distrosourcepackages]
+    [(u'alsa-utils', u'debian'), (u'alsa-utils', u'ubuntu')]
 
 The date_next_suggest_packaging attribute records the date when Launchpad can
 resume suggesting Ubuntu packages that the project provides. A value of None
@@ -270,7 +272,8 @@
     >>> from canonical.launchpad.interfaces import IBugTrackerSet
 
     >>> login_person(firefox.owner)
-    >>> gnome_bugzilla = getUtility(IBugTrackerSet).getByName('gnome-bugzilla')
+    >>> bug_tracker_set = getUtility(IBugTrackerSet)
+    >>> gnome_bugzilla = bug_tracker_set.getByName('gnome-bugzilla')
     >>> firefox.project.bugtracker = gnome_bugzilla
     >>> firefox.getExternalBugTracker() is None
     True
@@ -307,21 +310,23 @@
     True
 
 
-== Answer Tracking ==
+Answer Tracking
+---------------
 
 Firefox uses the Answer Tracker as the official application to provide
 answers to questions.
 
-    >>> firefox.official_answers
-    True
+    >>> print firefox.answers_usage.name
+    LAUNCHPAD
 
 Alsa does not use Launchpad to track answers.
 
-    >>> alsa.official_answers
-    False
-
-
-== Product Creation ==
+    >>> print alsa.answers_usage.name
+    UNKNOWN
+
+
+Product Creation
+----------------
 
 We can create new products with the createProduct() method:
 
@@ -374,20 +379,22 @@
     True
 
 
-== Specification Listings ==
+Specification Listings
+----------------------
 
 We should be able to set whether or not a Product uses specifications
-officially.  It defaults to False.
-
- >>> firefox = productset.getByName('firefox')
- >>> firefox.official_blueprints
- False
-
-We can change it to True.
-
- >>> firefox.official_blueprints = True
- >>> firefox.official_blueprints
- True
+officially.  It defaults to UNKNOWN.
+
+    >>> firefox = productset.getByName('firefox')
+    >>> print firefox.blueprints_usage.name
+    UNKNOWN
+
+We can change it to use LAUNCHPAD.
+
+    >>> from lp.app.enums import ServiceUsage
+    >>> firefox.blueprints_usage = ServiceUsage.LAUNCHPAD
+    >>> print firefox.blueprints_usage.name
+    LAUNCHPAD
 
 We should be able to get lists of specifications in different states
 related to a product.
@@ -395,39 +402,40 @@
 Basically, we can filter by completeness, and by whether or not the spec is
 informational.
 
- >>> firefox = productset.getByName('firefox')
- >>> from canonical.launchpad.interfaces import SpecificationFilter
+    >>> firefox = productset.getByName('firefox')
+    >>> from canonical.launchpad.interfaces import SpecificationFilter
 
 First, there should be only one informational spec for firefox:
 
- >>> filter = [SpecificationFilter.INFORMATIONAL]
- >>> for spec in firefox.specifications(filter=filter):
- ...    print spec.name
- extension-manager-upgrades
+    >>> filter = [SpecificationFilter.INFORMATIONAL]
+    >>> for spec in firefox.specifications(filter=filter):
+    ...    print spec.name
+    extension-manager-upgrades
 
 
 There are no completed specs for firefox:
 
- >>> filter = [SpecificationFilter.COMPLETE]
- >>> for spec in firefox.specifications(filter=filter):
- ...    print spec.name
+    >>> filter = [SpecificationFilter.COMPLETE]
+    >>> for spec in firefox.specifications(filter=filter):
+    ...    print spec.name
 
 
 And there are five incomplete specs:
 
- >>> filter = [SpecificationFilter.INCOMPLETE]
- >>> firefox.specifications(filter=filter).count()
- 5
+    >>> filter = [SpecificationFilter.INCOMPLETE]
+    >>> firefox.specifications(filter=filter).count()
+    5
 
 We can filter for specifications that contain specific text:
 
- >>> for spec in firefox.specifications(filter=['new']):
- ...     print spec.name
- canvas
- e4x
-
-
-== Milestones ==
+    >>> for spec in firefox.specifications(filter=['new']):
+    ...     print spec.name
+    canvas
+    e4x
+
+
+Milestones
+----------
 
 We can use IProduct.milestones to get all milestones associated with any
 ProductSeries of a product.
@@ -470,7 +478,8 @@
     [u'1.0.0', u'0.9.2', u'0.9.1', u'0.9', u'1.0', u'1.0-rc1']
 
 
-== Release ==
+Release
+-------
 
 All the releases for a Product can be retrieved through the releases property.
 
@@ -489,7 +498,8 @@
     0.9.1
 
 
-== Products With Branches ==
+Products With Branches
+----------------------
 
 Products are considered to officially support Launchpad as a location
 for their branches after a branch is set for the development focus
@@ -568,7 +578,8 @@
     landscape
 
 
-== Primary translatable ==
+Primary translatable
+--------------------
 
 Primary translatable series in a product should follow series where
 development is focused on.  To be able to do changes to facilitate
@@ -642,7 +653,8 @@
     1.0
 
 
-= Series list =
+Series list
+===========
 
 The series for a product are returned as a sorted list, with the
 exception that the current development focus is first.
@@ -697,7 +709,8 @@
     ...     print series.name
     trunk
 
-= Changing ownership =
+Changing ownership
+==================
 
 If the owner of a project changes, all series and productreleases
 owned by the old owner are transfered to the new owner.

=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt	2010-08-24 20:11:37 +0000
+++ lib/lp/registry/templates/distribution-index.pt	2010-09-09 18:28:00 +0000
@@ -52,7 +52,7 @@
       <div class="yui-g">
         <div class="yui-u first">
           <div tal:replace="structure context/@@+portlet-listfaqs"
-            tal:condition="context/official_answers" />
+            tal:condition="context/answers_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:replace="structure context/@@+portlet-latestbugs"
             tal:condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD" />
@@ -62,10 +62,10 @@
 
         <div class="yui-u">
           <div tal:replace="structure context/@@+portlet-latestquestions"
-            tal:condition="context/official_answers" />
+            tal:condition="context/answers_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:replace="structure context/@@+portlet-latestspecs"
-            tal:condition="context/official_blueprints" />
+            tal:condition="context/blueprints_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:replace="structure context/@@+portlet-coming-sprints" />
         </div>

=== modified file 'lib/lp/registry/templates/distribution-search.pt'
--- lib/lp/registry/templates/distribution-search.pt	2010-08-24 20:11:37 +0000
+++ lib/lp/registry/templates/distribution-search.pt	2010-09-09 18:28:00 +0000
@@ -77,7 +77,7 @@
                     </a>
                     <a
                       tal:define="link package/menu:answers/new"
-                      tal:condition="distribution/official_answers"
+                      tal:condition="distribution/answers_usage/enumvalue:LAUNCHPAD"
                       tal:attributes="href link/url">
                     <img
                       tal:attributes="alt link/text"
@@ -137,7 +137,7 @@
                       condition="binary_names"
                       content="string:(Matching binaries: ${binary_names}.)">
                       (Matching binaries: bin-one, bin-two.)
-                    </tal:matching-binaries> 
+                    </tal:matching-binaries>
                   </tal:binary-search-only>
                   </p>
                 </div>

=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
--- lib/lp/registry/templates/distroseries-index.pt	2010-08-25 16:18:24 +0000
+++ lib/lp/registry/templates/distroseries-index.pt	2010-09-09 18:28:00 +0000
@@ -83,7 +83,8 @@
 
           <div
             tal:content="structure context/@@+portlet-latestspecs"
-            tal:condition="context/@@+get-involved/official_blueprints" />
+            tal:define="blueprints_usage context/@@+get-involved/blueprints_usage"
+            condition="blueprints_usage/enumvalue:LAUNCHPAD" />
 
           <div
            tal:replace="structure context/distribution/@@+portlet-coming-sprints" />

=== modified file 'lib/lp/registry/templates/product-index.pt'
--- lib/lp/registry/templates/product-index.pt	2010-08-26 17:25:46 +0000
+++ lib/lp/registry/templates/product-index.pt	2010-09-09 18:28:00 +0000
@@ -215,7 +215,7 @@
       <div class="yui-g">
         <div class="yui-u first">
           <div tal:content="structure context/@@+portlet-listfaqs"
-            tal:condition="context/official_answers" />
+            tal:condition="context/answers_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:content="structure context/@@+portlet-latestbugs"
             tal:condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD" />
@@ -227,10 +227,10 @@
 
         <div class="yui-u">
           <div tal:content="structure context/@@+portlet-latestquestions"
-            tal:condition="context/official_answers" />
+            tal:condition="context/answers_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:content="structure context/@@+portlet-latestspecs"
-            tal:condition="context/official_blueprints" />
+            tal:condition="context/blueprints_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:content="structure context/@@+portlet-coming-sprints" />
         </div>

=== modified file 'lib/lp/registry/templates/productseries-index.pt'
--- lib/lp/registry/templates/productseries-index.pt	2010-08-25 15:24:48 +0000
+++ lib/lp/registry/templates/productseries-index.pt	2010-09-09 18:28:00 +0000
@@ -145,7 +145,7 @@
         <div class="yui-u">
           <div
             tal:content="structure context/@@+portlet-latestspecs"
-            tal:condition="context/@@+get-involved/official_blueprints" />
+            tal:condition="context/@@+get-involved/blueprints_usage/enumvalue:LAUNCHPAD" />
 
           <div tal:replace="structure context/product/@@+portlet-coming-sprints" />
 

=== modified file 'lib/lp/registry/templates/project-index.pt'
--- lib/lp/registry/templates/project-index.pt	2010-08-25 15:37:37 +0000
+++ lib/lp/registry/templates/project-index.pt	2010-09-09 18:28:00 +0000
@@ -72,11 +72,11 @@
         <tal:details replace="structure context/@@+details" />
 
         <tal:faqs content="structure context/@@+portlet-listfaqs"
-          condition="context/@@+get-involved/official_answers" />
+          condition="context/@@+get-involved/answers_usage/enumvalue:LAUNCHPAD" />
 
         <tal:has-many-project condition="view/has_many_projects">
           <tal:questions content="structure context/@@+portlet-latestquestions"
-            condition="context/@@+get-involved/official_answers" />
+            condition="context/@@+get-involved/answers_usage/enumvalue:LAUNCHPAD" />
         </tal:has-many-project>
 
         <tal:bugs content="structure context/@@+portlet-latestbugs"
@@ -84,7 +84,7 @@
 
         <tal:has-many-project condition="view/has_many_projects">
           <tal:specs content="structure context/@@+portlet-latestspecs"
-            condition="context/@@+get-involved/official_blueprints" />
+            condition="context/@@+get-involved/blueprints_usage/enumvalue:LAUNCHPAD" />
         </tal:has-many-project>
 
         <tal:contributors content="structure context/@@+portlet-top-contributors"/>
@@ -119,10 +119,10 @@
 
         <tal:has-few-project condition="not: view/has_many_projects">
           <tal:questions content="structure context/@@+portlet-latestquestions"
-            condition="context/@@+get-involved/official_answers" />
+            condition="context/@@+get-involved/answers_usage/enumvalue:LAUNCHPAD" />
 
           <tal:specs content="structure context/@@+portlet-latestspecs"
-            condition="context/@@+get-involved/official_blueprints" />
+            condition="context/@@+get-involved/blueprints_usage/enumvalue:LAUNCHPAD" />
 
           <tal:sprints content="structure context/@@+portlet-coming-sprints" />
         </tal:has-few-project>