← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jcsackett/launchpad/unknown-translations-service-643545-0 into lp:launchpad/devel

 

j.c.sackett has proposed merging lp:~jcsackett/launchpad/unknown-translations-service-643545-0 into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers): code
Related bugs:
  #643545 Launchpad must state the project's translations usage
  https://bugs.launchpad.net/bugs/643545


Summary
=======

Begins the process of moving translations to use the translations_usage enum for enabling or disabling the app for a given entity.

Proposed Fix
============

Update template displays to use translations_usage; add necessary messaging and configuration options.

Pre-Implementation Call
=======================

Discussion and documentation from Curtis Hovey (sinzui). Further discussion with Jeroen Vermeulen (jtv) clarified goals and led to filing of bug #646101, which will have to be addressed in another branch.

Implementation details
======================

As in proposed fix. ProductSeries and DistributionSeries still need some addressing, as does the overall configuration of Translations for a given product/distribution/series, but the current branch is usable.

Tests
=====

bin/test -m lp.translations

Demo and Q/A
============

Setting Product or Distribution should change the displayed data (e.g. setting NOT APPLICABLE should provide a NOT APPLICABLE message). Screenshots will be posted to this MP shortly.

Lint
====
Output:


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/canonical/launchpad/webapp/configure.zcml
  lib/canonical/launchpad/webapp/tales.py
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/browser/distribution.py
  lib/lp/registry/browser/sourcepackage.py
  lib/lp/translations/browser/configure.zcml
  lib/lp/translations/browser/distribution.py
  lib/lp/translations/browser/distroseries.py
  lib/lp/translations/browser/product.py
  lib/lp/translations/browser/productseries.py
  lib/lp/translations/interfaces/potemplate.py
  lib/lp/translations/model/potemplate.py
  lib/lp/translations/templates/distribution-translations.pt
  lib/lp/translations/templates/distroseries-translations.pt
  lib/lp/translations/templates/product-translations.pt
  lib/lp/translations/templates/productseries-translations.pt
  lib/lp/translations/templates/translations-portlet-configuration.pt
  lib/lp/translations/templates/translations-portlet-not-using-launchpad-extra.pt
  lib/lp/translations/templates/translations-portlet-not-using-launchpad.pt
  lib/lp/translations/tests/test_hastranslationtemplates.py

./lib/canonical/launchpad/webapp/configure.zcml
     668: Line has trailing whitespace.
./lib/canonical/launchpad/webapp/tales.py
     237: W602 deprecated form of raising exception
     518: W602 deprecated form of raising exception
     798: W602 deprecated form of raising exception
    1984: E231 missing whitespace after ','
    2236: E302 expected 2 blank lines, found 1
./lib/lp/translations/browser/configure.zcml
     943: Line has trailing whitespace.
./lib/lp/translations/interfaces/potemplate.py
     763: E301 expected 1 blank line, found 2
     777: E301 expected 1 blank line, found 2

Whitespacing errors and blank lines are all okay (tuples of one, comments &c).

-- 
https://code.launchpad.net/~jcsackett/launchpad/unknown-translations-service-643545-0/+merge/36464
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/unknown-translations-service-643545-0 into lp:launchpad/devel.
=== modified file 'lib/canonical/launchpad/webapp/configure.zcml'
--- lib/canonical/launchpad/webapp/configure.zcml	2010-09-20 00:36:24 +0000
+++ lib/canonical/launchpad/webapp/configure.zcml	2010-09-23 15:55:15 +0000
@@ -493,12 +493,6 @@
         name="fmt"
         />
     <adapter
-        for="lp.registry.interfaces.sourcepackage.ISourcePackage"
-        provides="zope.traversing.interfaces.IPathAdapter"
-        factory="canonical.launchpad.webapp.tales.SourcePackageFormatterAPI"
-        name="fmt"
-        />
-    <adapter
         for="lp.soyuz.interfaces.sourcepackagerelease.ISourcePackageRelease"
         provides="zope.traversing.interfaces.IPathAdapter"
         factory="canonical.launchpad.webapp.tales.SourcePackageReleaseFormatterAPI"

=== modified file 'lib/canonical/launchpad/webapp/tales.py'
--- lib/canonical/launchpad/webapp/tales.py	2010-08-30 11:46:44 +0000
+++ lib/canonical/launchpad/webapp/tales.py	2010-09-23 15:55:15 +0000
@@ -26,10 +26,11 @@
 from zope.component import adapts, getUtility, queryAdapter, getMultiAdapter
 from zope.app import zapi
 from zope.publisher.browser import BrowserView
-from zope.publisher.interfaces import IApplicationRequest
-from zope.publisher.interfaces.browser import IBrowserApplicationRequest
 from zope.traversing.interfaces import (
-    ITraversable, IPathAdapter, TraversalError)
+    ITraversable,
+    IPathAdapter,
+    TraversalError,
+    )
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import isinstance as zope_isinstance
 from zope.schema import TextLine
@@ -242,6 +243,7 @@
     This is available for all objects.  Individual operations may fail for
     objects that do not support them.
     """
+
     def __init__(self, context):
         self._context = context
 
@@ -310,6 +312,7 @@
             else:
                 return None
 
+
 def htmlmatch(formvalue, value):
     value = str(value)
     if isinstance(formvalue, list):
@@ -317,6 +320,7 @@
     else:
         return formvalue == value
 
+
 class HTMLFormOperation:
 
     implements(ITraversable)
@@ -835,13 +839,11 @@
         """Return whether the bug has a patch."""
         return self._context.bug.has_patches
 
-
     def badges(self):
-
         badges = []
         if self._context.bug.private:
             badges.append(self.icon_template % (
-                "private", "Private","sprite private"))
+                "private", "Private", "sprite private"))
 
         if self._hasMentoringOffer():
             badges.append(self.icon_template % (
@@ -859,7 +861,7 @@
             milestone_text = "milestone %s" % self._context.milestone.name
             badges.append(self.linked_icon_template % (
                 canonical_url(self._context.milestone),
-                milestone_text , "Linked to %s" % milestone_text,
+                milestone_text, "Linked to %s" % milestone_text,
                 "sprite milestone"))
 
         if self._hasPatch():
@@ -981,7 +983,6 @@
         '<img width="%(width)s" height="14" alt="%(alt)s" '
         'title="%(title)s" src="%(src)s" />')
 
-
     def icon(self):
         """Return the appropriate <img> tag for the build icon."""
         icon_map = {
@@ -989,7 +990,7 @@
             BuildStatus.FULLYBUILT: {'src': "/@@/build-success"},
             BuildStatus.FAILEDTOBUILD: {
                 'src': "/@@/build-failed",
-                'width': '16'
+                'width': '16',
                 },
             BuildStatus.MANUALDEPWAIT: {'src': "/@@/build-depwait"},
             BuildStatus.CHROOTWAIT: {'src': "/@@/build-chrootwait"},
@@ -1185,6 +1186,7 @@
             return self.hidden
         return super(TeamFormatterAPI, self).unique_displayname(view_name)
 
+
 class CustomizableFormatter(ObjectFormatterAPI):
     """A ObjectFormatterAPI that is easy to customize.
 
@@ -1322,16 +1324,6 @@
         return {'displayname': displayname}
 
 
-class SourcePackageFormatterAPI(CustomizableFormatter):
-    """Adapter for ISourcePackage objects to a formatted string."""
-
-    _link_summary_template = '%(displayname)s'
-
-    def _link_summary_values(self):
-        displayname = self._context.displayname
-        return {'displayname': displayname}
-
-
 class SourcePackageReleaseFormatterAPI(CustomizableFormatter):
 
     """Adapter for ISourcePackageRelease objects to a formatted string."""
@@ -1415,7 +1407,7 @@
             'bzr_identity': branch.bzr_identity,
             'display_name': cgi.escape(branch.displayname),
             'name': branch.name,
-            'unique_name' : branch.unique_name,
+            'unique_name': branch.unique_name,
             'url': self.url(view_name),
             }
 
@@ -1527,6 +1519,7 @@
 
 class PackageBuildFormatterAPI(ObjectFormatterAPI):
     """Adapter providing fmt support for `IPackageBuild` objects."""
+
     def _composeArchiveReference(self, archive):
         if archive.is_ppa:
             return " [%s/%s]" % (
@@ -1553,7 +1546,7 @@
 
     def _link_summary_values(self):
         """See CustomizableFormatter._link_summary_values."""
-        return {'hostname': self._context.hostname,}
+        return {'hostname': self._context.hostname}
 
 
 class MilestoneFormatterAPI(CustomizableFormatter):
@@ -2464,7 +2457,6 @@
         """See `ObjectFormatterAPI`."""
         return super(LanguageFormatterAPI, self).url(view_name, rootsite)
 
-
     def link(self, view_name, rootsite='translations'):
         """See `ObjectFormatterAPI`."""
         url = self.url(view_name, rootsite)
@@ -2501,6 +2493,7 @@
 
 
 class PackageDiffFormatterAPI(ObjectFormatterAPI):
+
     def link(self, view_name, rootsite=None):
         diff = self._context
         if not diff.date_fulfilled:

=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml	2010-09-21 03:30:43 +0000
+++ lib/lp/registry/browser/configure.zcml	2010-09-23 15:55:15 +0000
@@ -482,6 +482,12 @@
         factory="lp.registry.browser.distributionsourcepackage.DistributionSourcePackageFormatterAPI"
         name="fmt"
         />
+    <adapter
+        for="lp.registry.interfaces.sourcepackage.ISourcePackage"
+        provides="zope.traversing.interfaces.IPathAdapter"
+        factory="lp.registry.browser.sourcepackage.SourcePackageFormatterAPI"
+        name="fmt"
+        />
     <browser:url
         for="lp.registry.interfaces.commercialsubscription.ICommercialSubscription"
         path_expression="string:+commercialsubscription/${id}"

=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py	2010-09-15 16:03:14 +0000
+++ lib/lp/registry/browser/distribution.py	2010-09-23 15:55:15 +0000
@@ -343,6 +343,7 @@
         'ppas',
         'configure_answers',
         'configure_blueprints',
+        'configure_translations',
         ]
 
     @enabled_with_permission('launchpad.Edit')
@@ -457,7 +458,13 @@
     @enabled_with_permission('launchpad.Edit')
     def configure_blueprints(self):
         text = 'Configure blueprints'
-        summary = 'Enable tracking of feature planning.' 
+        summary = 'Enable tracking of feature planning.'
+        return Link('+edit', text, summary, icon='edit')
+
+    @enabled_with_permission('launchpad.Edit')
+    def configure_translations(self):
+        text = 'Configure translations'
+        summary = 'Allow users to provide translations for this project.'
         return Link('+edit', text, summary, icon='edit')
 
 
@@ -771,7 +778,7 @@
         "members",
         "official_malone",
         "official_blueprints",
-        "official_rosetta",
+        "translations_usage",
         "official_answers",
         ]
 
@@ -817,7 +824,7 @@
         'official_malone',
         'enable_bug_expiration',
         'official_blueprints',
-        'official_rosetta',
+        'translations_usage',
         'official_answers',
         'translation_focus',
         ]

=== modified file 'lib/lp/registry/browser/sourcepackage.py'
--- lib/lp/registry/browser/sourcepackage.py	2010-09-03 06:06:40 +0000
+++ lib/lp/registry/browser/sourcepackage.py	2010-09-23 15:55:15 +0000
@@ -46,6 +46,7 @@
     _,
     helpers,
     )
+from canonical.launchpad.webapp.tales import CustomizableFormatter
 from canonical.launchpad.browser.multistep import (
     MultiStepView,
     StepView,
@@ -122,6 +123,18 @@
     return '/projects/+new?%s' % query_string
 
 
+class SourcePackageFormatterAPI(CustomizableFormatter):
+    """Adapter for ISourcePackage objects to a formatted string."""
+
+    _link_permission = 'zope.Public'
+
+    _link_summary_template = '%(displayname)s'
+
+    def _link_summary_values(self):
+        displayname = self._context.displayname
+        return {'displayname': displayname}
+
+
 class SourcePackageNavigation(GetitemNavigation, BugTargetTraversalMixin):
 
     usedfor = ISourcePackage
@@ -338,7 +351,7 @@
     next_url = None
 
     main_action_label = u'Change'
-    
+
     def main_action(self, data):
         productseries = data['productseries']
         # Because it is part of a multistep view, the next_url can't

=== modified file 'lib/lp/translations/browser/configure.zcml'
--- lib/lp/translations/browser/configure.zcml	2010-07-23 09:41:07 +0000
+++ lib/lp/translations/browser/configure.zcml	2010-09-23 15:55:15 +0000
@@ -645,6 +645,15 @@
             name="+languages"
             template="../templates/productseries-translations-languages.pt"
             />
+        <browser:page
+            name="+portlet-not-using-launchpad"
+            template="../templates/translations-portlet-not-using-launchpad.pt"/>
+        <browser:page
+            name="+portlet-configuration"
+            template="../templates/translations-portlet-configuration.pt"/>
+        <browser:page
+            name="+portlet-not-using-launchpad-extra"
+            template="../templates/translations-portlet-not-using-launchpad-extra.pt"/>
     </browser:pages>
     <browser:pages
         for="lp.registry.interfaces.productseries.IProductSeries"
@@ -822,7 +831,13 @@
             template="../templates/product-portlet-obsolete-translatables.pt"/>
         <browser:page
             name="+portlet-not-using-launchpad"
-            template="../templates/product-portlet-not-using-launchpad.pt"/>
+            template="../templates/translations-portlet-not-using-launchpad.pt"/>
+        <browser:page
+            name="+portlet-configuration"
+            template="../templates/translations-portlet-configuration.pt"/>
+        <browser:page
+            name="+portlet-not-using-launchpad-extra"
+            template="../templates/translations-portlet-not-using-launchpad-extra.pt"/>
     </browser:pages>
 
 <!-- ProjectGroup views -->
@@ -915,7 +930,24 @@
         permission="launchpad.View"
         template="../templates/distribution-language-pack-admin-info.pt"
         layer="lp.translations.publisher.TranslationsLayer"/>
-
+    <browser:page
+        name="+portlet-not-using-launchpad"
+        for="lp.registry.interfaces.distribution.IDistribution"
+        permission="zope.Public"
+        template="../templates/translations-portlet-not-using-launchpad.pt"
+        layer="lp.translations.publisher.TranslationsLayer"/>
+    <browser:page
+        name="+portlet-not-using-launchpad-extra"
+        for="lp.registry.interfaces.distribution.IDistribution"
+        permission="zope.Public"
+        template="../templates/translations-portlet-not-using-launchpad-extra.pt"
+        layer="lp.translations.publisher.TranslationsLayer"/> 
+    <browser:page
+        name="+portlet-configuration"
+        for="lp.registry.interfaces.distribution.IDistribution"
+        permission="zope.Public"
+        template="../templates/translations-portlet-configuration.pt"
+        layer="lp.translations.publisher.TranslationsLayer"/>
 <!-- DistroSeries -->
 
     <browser:defaultView
@@ -943,6 +975,15 @@
         <browser:page
             name="+langchart"
             template="../templates/distroseries-langchart.pt"/>
+        <browser:page
+            name="+portlet-not-using-launchpad"
+            template="../templates/translations-portlet-not-using-launchpad.pt"/>
+        <browser:page
+            name="+portlet-configuration"
+            template="../templates/translations-portlet-configuration.pt"/>
+        <browser:page
+            name="+portlet-not-using-launchpad-extra"
+            template="../templates/translations-portlet-not-using-launchpad-extra.pt"/>
     </browser:pages>
     <browser:page
         for="lp.registry.interfaces.distroseries.IDistroSeries"

=== modified file 'lib/lp/translations/browser/distribution.py'
--- lib/lp/translations/browser/distribution.py	2010-08-24 10:45:57 +0000
+++ lib/lp/translations/browser/distribution.py	2010-09-23 15:55:15 +0000
@@ -21,7 +21,9 @@
     LaunchpadView,
     Link,
     )
+from canonical.launchpad.webapp.authorization import check_permission
 from canonical.launchpad.webapp.menu import NavigationMenu
+from lp.app.enums import service_uses_launchpad
 from lp.registry.browser.distribution import DistributionEditView
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.series import SeriesStatus
@@ -96,6 +98,20 @@
         else:
             return self.context.translation_focus
 
+    @cachedproperty
+    def show_page_content(self):
+        """Whether the main content of the page should be shown."""
+        return (service_uses_launchpad(self.context.translations_usage) or
+               self.is_translations_admin)
+
+    def can_configure_translations(self):
+        """Whether or not the user can configure translations."""
+        return check_permission("launchpad.Edit", self.context)
+
+    def is_translations_admin(self):
+        """Whether or not the user is a translations admin."""
+        return check_permission("launchpad.TranslationsAdmin", self.context)
+
     def secondary_translatable_series(self):
         """Return a list of IDistroSeries that aren't the translation_focus.
 
@@ -106,8 +122,7 @@
             for series in self.context.series
             if (series.status != SeriesStatus.OBSOLETE
                 and (self.translation_focus is None or
-                     self.translation_focus.id != series.id))
-            ]
+                     self.translation_focus.id != series.id))]
 
         return sorted(series, key=operator.attrgetter('version'),
                       reverse=True)

=== modified file 'lib/lp/translations/browser/distroseries.py'
--- lib/lp/translations/browser/distroseries.py	2010-09-11 09:37:13 +0000
+++ lib/lp/translations/browser/distroseries.py	2010-09-23 15:55:15 +0000
@@ -30,6 +30,7 @@
     LaunchpadView,
     )
 from lp.app.errors import TranslationUnavailable
+from lp.app.enums import service_uses_launchpad
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.propertycache import cachedproperty
@@ -262,6 +263,20 @@
         """Is this DistroSeries the translation focus."""
         return self.context.distribution.translation_focus == self.context
 
+    @cachedproperty
+    def show_page_content(self):
+        """Whether the main content of the page should be shown."""
+        return (service_uses_launchpad(self.context.translations_usage) or
+               self.is_translations_admin)
+
+    def can_configure_translations(self):
+        """Whether or not the user can configure translations."""
+        return check_permission("launchpad.Edit", self.context)
+
+    def is_translations_admin(self):
+        """Whether or not the user is a translations admin."""
+        return check_permission("launchpad.TranslationsAdmin", self.context)
+
 
 class DistroSeriesTranslationsMenu(NavigationMenu):
 

=== modified file 'lib/lp/translations/browser/product.py'
--- lib/lp/translations/browser/product.py	2010-09-03 13:29:58 +0000
+++ lib/lp/translations/browser/product.py	2010-09-23 15:55:15 +0000
@@ -106,7 +106,15 @@
     def show_page_content(self):
         """Whether the main content of the page should be shown."""
         return (service_uses_launchpad(self.context.translations_usage) or
-                check_permission("launchpad.TranslationsAdmin", self.context))
+               self.is_translations_admin)
+
+    def can_configure_translations(self):
+        """Whether or not the user can configure translations."""
+        return check_permission("launchpad.Edit", self.context)
+
+    def is_translations_admin(self):
+        """Whether or not the user is a translations admin."""
+        return check_permission("launchpad.TranslationsAdmin", self.context)
 
     @cachedproperty
     def primary_translatable(self):

=== modified file 'lib/lp/translations/browser/productseries.py'
--- lib/lp/translations/browser/productseries.py	2010-09-11 09:37:13 +0000
+++ lib/lp/translations/browser/productseries.py	2010-09-23 15:55:15 +0000
@@ -37,8 +37,10 @@
     Link,
     NavigationMenu,
     )
+from canonical.launchpad.webapp.authorization import check_permission
 from canonical.launchpad.webapp.menu import structured
 from canonical.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
+from lp.app.enums import service_uses_launchpad
 from lp.code.interfaces.branchjob import IRosettaUploadJobSource
 from lp.registry.interfaces.productseries import IProductSeries
 from lp.services.propertycache import cachedproperty
@@ -410,6 +412,20 @@
         """Does this ProductSeries have exactly one POTemplate."""
         return self.context.potemplate_count == 1
 
+    @cachedproperty
+    def show_page_content(self):
+        """Whether the main content of the page should be shown."""
+        return (service_uses_launchpad(self.context.translations_usage) or
+               self.is_translations_admin)
+
+    def can_configure_translations(self):
+        """Whether or not the user can configure translations."""
+        return check_permission("launchpad.Edit", self.context)
+
+    def is_translations_admin(self):
+        """Whether or not the user is a translations admin."""
+        return check_permission("launchpad.TranslationsAdmin", self.context)
+
 
 class SettingsRadioWidget(LaunchpadRadioWidgetWithDescription):
     """Remove the confusing hint under the widget."""

=== modified file 'lib/lp/translations/interfaces/potemplate.py'
--- lib/lp/translations/interfaces/potemplate.py	2010-08-25 20:04:40 +0000
+++ lib/lp/translations/interfaces/potemplate.py	2010-09-23 15:55:15 +0000
@@ -739,6 +739,10 @@
         title=_("Does this object have current translation templates?"),
         readonly=True)
 
+    has_translation_files = Bool(
+        title=_("Does this object have translation files?"),
+        readonly=True)
+
     def getTemplatesCollection():
         """Return templates as a `TranslationTemplatesCollection`.
 
@@ -804,6 +808,7 @@
         exist for it.
         """
 
+
 class ITranslationTemplatesCollection(Interface):
     """A `Collection` of `POTemplate`s."""
 

=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py	2010-09-03 13:29:58 +0000
+++ lib/lp/translations/model/potemplate.py	2010-09-23 15:55:15 +0000
@@ -1620,6 +1620,12 @@
         collection = self.getCurrentTemplatesCollection()
         return collection.joinPOFile().select(selection)
 
+    @property
+    def has_translation_files(self):
+        """See `IHasTranslationTemplates`."""
+        return bool(
+            self.getCurrentTranslationFiles(just_ids=True).any())
+
     def getObsoleteTranslationTemplates(self):
         """See `IHasTranslationTemplates`."""
         # XXX JeroenVermeulen 2010-07-15 bug=605924: This returns a list

=== modified file 'lib/lp/translations/templates/distribution-translations.pt'
--- lib/lp/translations/templates/distribution-translations.pt	2010-08-20 01:41:58 +0000
+++ lib/lp/translations/templates/distribution-translations.pt	2010-09-23 15:55:15 +0000
@@ -22,6 +22,17 @@
       </a>
       <div></div><!-- to clear-up all floats -->
     </div>
+
+    <tal:not-using-launchpad
+      condition="not: context/translations_usage/enumvalue:LAUNCHPAD">
+      <tal:message
+        replace="structure context/@@+portlet-not-using-launchpad"/>
+      <tal:translations-configuration
+        condition="view/can_configure_translations"
+        replace="structure context/@@+portlet-configuration"/>
+    </tal:not-using-launchpad>
+
+    <tal:show-page-content condition="view/show_page_content">
     <tal:translation_focus condition="view/translation_focus"
                            define="target view/translation_focus">
 
@@ -71,27 +82,27 @@
       </div>
     </tal:translation_focus>
 
-  <tal:secondary condition="view/secondary_translatable_series">
-    <h2 tal:condition="view/translation_focus">
-      Other versions of <span tal:replace="context/displayname">Ubuntu</span>
-    </h2>
-
-    <ul id="distroseries-list">
-      <li tal:repeat="distroseries view/secondary_translatable_series">
-        <a tal:attributes="href distroseries/fmt:url:translations"
-          tal:content="distroseries/named_version">Hoary (5.04)</a>
-      </li>
-    </ul>
-  </tal:secondary>
-
-  <tal:untranslatable condition="not: view/translation_focus">
-    <p>
-      This distribution does not have any series to be translated.  Once
-      <span tal:replace="context/displayname">Ubuntu</span> has created a
-      distroseries, you will be able to find or create its translations here.
-    </p>
-  </tal:untranslatable>
-
+    <tal:secondary condition="view/secondary_translatable_series">
+      <h2 tal:condition="view/translation_focus">
+        Other versions of <span tal:replace="context/displayname">Ubuntu</span>
+      </h2>
+
+      <ul id="distroseries-list">
+        <li tal:repeat="distroseries view/secondary_translatable_series">
+          <a tal:attributes="href distroseries/fmt:url:translations"
+             tal:content="distroseries/named_version">Hoary (5.04)</a>
+        </li>
+      </ul>
+    </tal:secondary>
+
+    <tal:untranslatable condition="not: view/translation_focus">
+      <p>
+        This distribution does not have any series to be translated.  Once
+        <span tal:replace="context/displayname">Ubuntu</span> has created a
+        distroseries, you will be able to find or create its translations here.
+      </p>
+    </tal:untranslatable>
+    </tal:show-page-content>
 </div>
 </body>
 </html>

=== modified file 'lib/lp/translations/templates/distroseries-translations.pt'
--- lib/lp/translations/templates/distroseries-translations.pt	2010-08-20 00:39:54 +0000
+++ lib/lp/translations/templates/distroseries-translations.pt	2010-09-23 15:55:15 +0000
@@ -21,8 +21,13 @@
         </a>
         <div></div><!-- to clear-up all floats -->
       </div>
-      <div class="top-portlet"
-           id="translation-focus"
+      <div class="top-portlet">
+        <tal:not-using-launchpad
+          condition="not: context/translations_usage/enumvalue:LAUNCHPAD">
+          <tal:message
+            replace="structure context/@@+portlet-not-using-launchpad"/>
+        </tal:not-using-launchpad>
+      <div id="translation-focus"
            tal:condition="context/distribution/translation_focus">
         <p tal:condition="not:view/is_translation_focus">
           Launchpad currently recommends translating
@@ -39,14 +44,16 @@
           </tal:distro>.
         </p>
       </div>
-      <div class="yui-g">
+      </div>
+
+      <div tal:condition="view/show_page_content" class="yui-g">
         <div class="yui-u first">
           <div class="portlet">
             <h3>Permissions</h3>
             <p>
               <tal:permissions replace="
-                  structure
-                  context/distribution/@@+portlet-translation-groups-and-permission"/>
+                structure
+                context/distribution/@@+portlet-translation-groups-and-permission"/>
             </p>
           </div>
         </div>
@@ -130,7 +137,7 @@
           </div>
         </div>
       </div>
-
+      <tal:show-page-content condition="view/show_page_content">
       <tal:stats condition="view/distroserieslanguages">
         <div class="yui-b top-portlet">
           <h2>Translation statistics</h2>
@@ -140,6 +147,7 @@
           </div>
         </div>
       </tal:stats>
+      </tal:show-page-content>
     </div>
   </body>
 </html>

=== modified file 'lib/lp/translations/templates/product-translations.pt'
--- lib/lp/translations/templates/product-translations.pt	2010-08-20 00:39:54 +0000
+++ lib/lp/translations/templates/product-translations.pt	2010-09-23 15:55:15 +0000
@@ -23,14 +23,25 @@
         <div></div><!-- to clear-up all floats -->
       </div>
       <div class="top-portlet notice">
-        <tal:official_use replace="
-            structure
-            context/@@+portlet-not-using-launchpad"/>
+
+        <tal:not-using-launchpad
+          condition="not: context/translations_usage/enumvalue:LAUNCHPAD">
+          <tal:message
+            replace="structure context/@@+portlet-not-using-launchpad"/>
+          <tal:translations-configuration
+            condition="view/can_configure_translations"
+            replace="structure context/@@+portlet-configuration"/>
+          <tal:translatable-packages
+            condition="not: view/is_translations_admin"
+            replace="structure context/@@+portlet-not-using-launchpad-extra"/>
+        </tal:not-using-launchpad>
+
         <div tal:condition="view/no_translations_available">
-        There are no translations for this project.
+          There are no translations for this project.
         </div>
       </div>
 
+      <!-- Not sure this should be shown at all if translations is off. -->
       <tal:page_content condition="view/show_page_content">
         <tal:translatable define="target view/primary_translatable">
 

=== modified file 'lib/lp/translations/templates/productseries-translations.pt'
--- lib/lp/translations/templates/productseries-translations.pt	2010-08-30 21:16:40 +0000
+++ lib/lp/translations/templates/productseries-translations.pt	2010-09-23 15:55:15 +0000
@@ -24,30 +24,37 @@
 
       <tal:no-languages condition="not:view/productserieslanguages">
         <div class="yui-b top-portlet">
-        <p>There are no translations for this release series.</p>
-
-        <p tal:condition="context/product/required:launchpad.Edit">To
-          <a href="https://help.launchpad.net/Translations/YourProject";>start
-            translating your project</a>,
-          <tal:uses-translations condition="not:
-                    context/product/translations_usage/enumvalue:LAUNCHPAD">
-            you should enable translations in your project settings, and
-          </tal:uses-translations>
-          you can either
-          <a
-                tal:define="link context/menu:navigation/translationupload"
-                tal:attributes="href link/url"
-              >manually upload</a> templates and translations, or set up
+
+          <tal:not-using-launchpad
+            condition="not: context/translations_usage/enumvalue:LAUNCHPAD">
+            <tal:message
+              replace="structure context/@@+portlet-not-using-launchpad"/>
+          </tal:not-using-launchpad>
+
+          <p>There are no translations for this release series.</p>
+
+          <p tal:condition="context/product/required:launchpad.Edit">To
+            <a href="https://help.launchpad.net/Translations/YourProject";>start
+              translating your project</a>,
+            <tal:uses-translations condition="not:
+              context/product/translations_usage/enumvalue:LAUNCHPAD">
+              you should enable translations in your project settings, and
+            </tal:uses-translations>
+            you can either
+            <a
+              tal:define="link context/menu:navigation/translationupload"
+              tal:attributes="href link/url">manually upload</a>
+              templates and translations, or set up
               <a
                 tal:define="link context/menu:navigation/settings"
-                tal:attributes="href link/url"
-              >automatic import from branches</a>
+                tal:attributes="href link/url">automatic import from branches
+              </a>
               (<a href="https://help.launchpad.net/Translations/ImportingFromBazaarBranches";>read more</a>).
-        </p>
+          </p>
         </div>
       </tal:no-languages>
 
-      <div class="yui-g">
+      <div tal:condition="view/show_page_content" class="yui-g">
         <div class="yui-u first">
           <div class="portlet">
             <h3>Permissions</h3>

=== added file 'lib/lp/translations/templates/translations-portlet-configuration.pt'
--- lib/lp/translations/templates/translations-portlet-configuration.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/translations/templates/translations-portlet-configuration.pt	2010-09-23 15:55:15 +0000
@@ -0,0 +1,25 @@
+<tal:root
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  omit-tag="">
+
+    <div id="translations-explanation">
+      <ul>
+        <li>
+          Launchpad allows communities to translate projects using imports
+          or a branch.
+        </li>
+        <li>
+          <a class="info sprite"
+             href="/+help/getting-started-for-your-project.html"
+             target="help">
+             Getting started with translating your project in Launchpad
+          </a>
+        </li>
+        <li>
+          <a tal:replace="structure context/menu:overview/configure_translations/fmt:link"/>
+        </li>
+      </ul>
+    </div>
+</tal:root>

=== added file 'lib/lp/translations/templates/translations-portlet-not-using-launchpad-extra.pt'
--- lib/lp/translations/templates/translations-portlet-not-using-launchpad-extra.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/translations/templates/translations-portlet-not-using-launchpad-extra.pt	2010-09-23 15:55:15 +0000
@@ -0,0 +1,16 @@
+<tal:root
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  omit-tag="">
+
+  <div id="ubuntu-translations"
+    tal:define="packages context/translatable_packages | nothing"
+    tal:condition="packages">
+    <tal:project replace="context/displayname" /> messages are
+    tracked in: <tal:packages repeat="package packages">
+    <tal:package replace="structure package/fmt:link" />
+      <tal:comma condition="not:repeat/package/end">, </tal:comma>
+    </tal:packages>.
+  </div>
+</tal:root>

=== renamed file 'lib/lp/translations/templates/product-portlet-not-using-launchpad.pt' => 'lib/lp/translations/templates/translations-portlet-not-using-launchpad.pt'
--- lib/lp/translations/templates/product-portlet-not-using-launchpad.pt	2010-08-30 21:16:40 +0000
+++ lib/lp/translations/templates/translations-portlet-not-using-launchpad.pt	2010-09-23 15:55:15 +0000
@@ -4,20 +4,23 @@
   xmlns:i18n="http://xml.zope.org/namespaces/i18n";
   omit-tag="">
 
-    <div id="not-translated-in-launchpad"
-         tal:condition="not: context/translations_usage/enumvalue:LAUNCHPAD">
+    <div id="not-translated-in-launchpad">
       <strong>
-        This project is not using Launchpad for translations.
+      <tal:lauchpad-unknown
+        condition="context/translations_usage/enumvalue:UNKNOWN">
+        Launchpad does not know where
+        <tal:project replace="context/displayname" /> translates its messages.
+      </tal:lauchpad-unknown>
+      <tal:launchpad-external
+        condition="context/translations_usage/enumvalue:EXTERNAL">
+        <tal:project replace="context/displayname" /> does not use Launchpad
+        to translate its messages.
+      </tal:launchpad-external>
+      <tal:launchpad-not-applicable
+        tal:condition="context/translations_usage/enumvalue:NOT_APPLICABLE">
+        <tal:project replace="context/displayname" /> does not translate its
+        messages.
+      </tal:launchpad-not-applicable>
       </strong>
-      <ul>
-        <li>
-          <a tal:replace="structure context/menu:overview/configure_translations/fmt:link"/>
-        </li>
-        <li>
-          <a href="/+help/getting-started-for-your-project.html" target="help"
-            >Getting started with translating your project in Launchpad</a>
-        </li>
-      </ul>
     </div>
-
 </tal:root>

=== modified file 'lib/lp/translations/tests/test_hastranslationtemplates.py'
--- lib/lp/translations/tests/test_hastranslationtemplates.py	2010-08-30 21:11:21 +0000
+++ lib/lp/translations/tests/test_hastranslationtemplates.py	2010-09-23 15:55:15 +0000
@@ -1,5 +1,3 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
 
@@ -29,6 +27,11 @@
         raise NotImplementedError(
             'This must be provided by an executable test.')
 
+    def createTranslationFile(self, name, priority=0):
+        """Attaches a pofile to appropriate container."""
+        raise NotImplementedError(
+            'This must be provided by an executable test.')
+
     def test_implements_interface(self):
         # Make sure container implements IHasTranslationTemplates.
         verifyObject(IHasTranslationTemplates, self.container)
@@ -156,6 +159,13 @@
         self.product_or_distro.translations_usage = ServiceUsage.EXTERNAL
         self.assertFalse(self.container.has_current_translation_templates)
 
+    def test_has_translation_files(self):
+        # has_translations_files should only return true if the object has
+        # pofiles.
+        self.assertFalse(self.container.has_translation_files)
+        self.createTranslationFile("one")
+        self.assertTrue(self.container.has_translation_files)
+
     def test_getTranslationTemplateFormats(self):
         # Check that translation_template_formats works properly.
 
@@ -202,6 +212,13 @@
         potemplate.priority = priority
         return potemplate
 
+    def createTranslationFile(self, name, priority=0):
+        potemplate = self.createTranslationTemplate(name, priority)
+        pofile = self.factory.makePOFile(
+            language_code='es',
+            potemplate=potemplate)
+        return pofile
+
     def setUp(self):
         super(TestProductSeriesHasTranslationTemplates, self).setUp()
         self.container = self.factory.makeProductSeries()
@@ -220,6 +237,13 @@
         potemplate.priority = priority
         return potemplate
 
+    def createTranslationFile(self, name, priority=0):
+        potemplate = self.createTranslationTemplate(name, priority)
+        pofile = self.factory.makePOFile(
+            language_code='es',
+            potemplate=potemplate)
+        return pofile
+
     def setUp(self):
         super(TestSourcePackageHasTranslationTemplates, self).setUp()
         self.container = self.factory.makeSourcePackage()
@@ -240,6 +264,13 @@
         potemplate.priority = priority
         return potemplate
 
+    def createTranslationFile(self, name, priority=0):
+        potemplate = self.createTranslationTemplate(name, priority)
+        pofile = self.factory.makePOFile(
+            language_code='es',
+            potemplate=potemplate)
+        return pofile
+
     def setUp(self):
         super(TestDistroSeriesHasTranslationTemplates, self).setUp()
         self.container = self.factory.makeDistroRelease()


Follow ups