Merge lp:~adiroiban/launchpad/bug-435165 into lp:launchpad

Proposed by Adi Roiban
Status: Merged
Approved by: Henning Eggers
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~adiroiban/launchpad/bug-435165
Merge into: lp:launchpad
Diff against target: 320 lines (+194/-25)
3 files modified
lib/lp/translations/browser/potemplate.py (+29/-4)
lib/lp/translations/stories/standalone/xx-potemplate-index.txt (+162/-19)
lib/lp/translations/templates/potemplate-index.pt (+3/-2)
To merge this branch: bzr merge lp:~adiroiban/launchpad/bug-435165
Reviewer Review Type Date Requested Status
Henning Eggers (community) code Approve
Review via email: mp+16084@code.launchpad.net

Commit message

From the template index page, add a link to the listing of all templates from the same source package.

To post a comment you must log in.
Revision history for this message
Adi Roiban (adiroiban) wrote :

= Bug 435165 =

Currently, if I go to a template page of a source page with multiple templates, the list of templates shown under the "Related templates" section is truncated if the list is long. I think this is a nice feature, which avoids unnecessary clutter.

Example: https://translations.edge.launchpad.net/ubuntu/karmic/+source/kdelibs/+pots/kdelibs

At the same time though, there is currently no easy way to navigate to the full list of templates.

== Proposed fix ==

Instead of the ellipsis from the end, add a text like "and _3 other templates_." which links to a full list.

== Tests ==
lp-test -t rosetta-potemplate-index

== Demo and Q/A ==

Screenshot for UI review: http://launchpadlibrarian.net/36610283/others.jpg

It is not trivial to add and enable new potemplates from LP UI as they are imported by a cron script.

The story describes a way of adding new templates and testing the behaviour.

= Launchpad lint =

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

Linting changed files:
  lib/lp/translations/browser/potemplate.py
  lib/lp/translations/stories/standalone/xx-rosetta-potemplate-index.txt

Revision history for this message
Henning Eggers (henninge) wrote :

Hi Adi,
thanks for doing this and improving the page handling in this way. This is really a nice addition. As discussed on IRC, please remove the request/URL/5 from the template. Also, I don't like checking conditions on more_templates_by_source, although has done elsewhere, as you pointed out. I suggest that you generate the fragment in the view using canonical_url() and including it in the template using "tal:replace="structure: view/...". You agreed to do that.

Just some small comments on comments. ;)

> +Visiting any template from the same page, user see link to the other
> +templates.
> +

"the user sees a link" ... ;)

> +For five template, the page displays the first four templates in
> +alphabetical order, and a link to the page listing all templates.
> +
> +Another template is added to the same source package to test this behaviour.
> +

Just write "Another template is added to the same source package." No need to mention that this is a test because we already know it is ... ;) This appears twice.

Thank you for adding all the formatting fixes and making our code look prettier. Good job!

Henning

review: Needs Fixing (code)
Revision history for this message
Adi Roiban (adiroiban) wrote :
Download full text (5.8 KiB)

Thanks for the review!

Here is the latest diff.

=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2009-12-14 15:50:40 +0000
+++ lib/lp/translations/browser/potemplate.py 2009-12-21 19:26:06 +0000
@@ -286,12 +286,25 @@
         by_source_count = self.context.relatives_by_source.count()
         if (by_source_count > self.SHOW_RELATED_TEMPLATES):
             other = by_source_count - self.SHOW_RELATED_TEMPLATES
+ if (self.context.distroseries):
+ # XXX adiroiban 2009-12-21 bug=499058: A canonical_url for
+ # SourcePackageName is needed to avoid hardcoding this URL.
+ url = (canonical_url(
+ self.context.distroseries, rootsite="translations") +
+ "/+source/" + self.context.sourcepackagename.name +
+ "/+translations")
+ else:
+ url = canonical_url(
+ self.context.productseries,
+ rootsite="translations",
+ view_name="+templates")
             if other == 1:
- return "one other template"
+ return " and <a href=\"%s\">one other template</a>." % url
             else:
- return "%d other templates" % other
+ return " and <a href=\"%s\">%d other templates</a>." % (
+ url, other)
         else:
- return None
+ return ""

     @property
     def related_templates_by_name(self):

=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-14 15:50:40 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-21 19:44:38 +0000
@@ -114,7 +114,7 @@
 When the branch or the source package contains less than five templates
 they are all displayed on the template page.

-A source package with for templates are created.
+A source package with five templates is created.

     >>> from zope.component import getUtility
     >>> from canonical.launchpad.interfaces.launchpad import (
@@ -140,7 +140,7 @@
     ... sourcepackagename=package.sourcepackagename, name='fifth')
     >>> logout()

-Visiting any template from the same page, user see link to the other
+Visiting any template from the same page, user sees link to the other
 templates.

     >>> browser.open(
@@ -155,7 +155,7 @@
 For five template, the page displays the first four templates in
 alphabetical order, and a link to the page listing all templates.

-Another template is added to the same source package to test this behaviour.
+Another template is added to the same source package.

     >>> login('<email address hidden>')
     >>> template = factory.makePOTemplate(
@@ -179,7 +179,7 @@
 alphabetical order, and a link to the page listing
 all templates, stating the number of other templates.

-Another template is added to the same source package to test this behaviour.
+Another template is added to the same source package.

     >>> login('<email address hidden>')
     >>> template = factory.makePOTemplate(
@@ -204,6 +204,48 @@
 ...

Read more...

Revision history for this message
Henning Eggers (henninge) wrote :

Great work, Adi!

This will be able to land but I still have a few minor comments, sorry. ;)

> === modified file 'lib/lp/translations/browser/potemplate.py'
> --- lib/lp/translations/browser/potemplate.py 2009-12-09 01:03:22 +0000
> +++ lib/lp/translations/browser/potemplate.py 2009-12-21 19:47:13 +0000
> @@ -282,9 +282,29 @@
> return by_source
>
> @property
> - def has_more_templates_by_source(self):
> + def more_templates_by_source(self):

Please add a docstring. I also wonder if the fact that this returns a string and not "a list of templates" should be reflected in the function name.

[...]
The implementation is very good!

> === modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
> --- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-10 12:46:11 +0000
> +++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-21 19:47:13 +0000
[...]
> +Visiting any template from the same page, user sees link to the other
> +templates.

I am sorry but this sentence is still missing a word or two. My last suggestion was probably missleading as you are talking about multiple links. So a complete sentence would be:
"..., the user sees links to the other templates."

> +
> + >>> browser.open(
> + ... ("http://translations.launchpad.dev/"
> + ... "ubuntu/hoary/+source/%s/+pots/%s") % (
> + ... package.name, template.name))
> + >>> relatives = find_tag_by_id(
> + ... browser.contents, 'potemplate-relatives')
> + >>> print extract_text(relatives)
> + Other templates here: first, forth, second, third
> +
> +For five template, the page displays the first four templates in
> +alphabetical order, and a link to the page listing all templates.

"five templates" ;-) Rember, only two plural forms in English ... ;)

[...]

> +For more than five template, the page displays the first four templates in

"five templates" ... ;)

[...]

> --- lib/lp/translations/templates/potemplate-index.pt 2009-12-07 21:58:53 +0000
> +++ lib/lp/translations/templates/potemplate-index.pt 2009-12-21 19:47:13 +0000
> @@ -47,8 +47,9 @@
> tal:content="pot/name">A POTemplate
> </a><tal:comma condition="not: repeat/pot/end">,
> </tal:comma></tal:relatives_by_source><!--
> - --><tal:more condition="view/has_more_templates_by_source"
> - >...</tal:more>
> + --><tal:more-templates content="structure view/more_templates_by_source">

I am sorry, I gave you the wrong hint here. It should be "replace" instead of "content". No need to have the <tal:more-templates> tag in the page.

> + and <a href="#">17 other templates</a>
> + </tal:more-templates>
> </p>
> </div>
>

Thanks again for the branch!

Henning

review: Approve (code)
Revision history for this message
Adi Roiban (adiroiban) wrote :

Hi Henning,

Here is the latest diff.

I went for "more_templates_by_source_link" . What do you say?

=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2009-12-21 19:39:06 +0000
+++ lib/lp/translations/browser/potemplate.py 2009-12-22 08:42:31 +0000
@@ -282,7 +282,7 @@
         return by_source

     @property
- def more_templates_by_source(self):
+ def more_templates_by_source_link(self):
         by_source_count = self.context.relatives_by_source.count()
         if (by_source_count > self.SHOW_RELATED_TEMPLATES):
             other = by_source_count - self.SHOW_RELATED_TEMPLATES

=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-21 19:45:09 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-22 08:40:58 +0000
@@ -140,7 +140,7 @@
     ... sourcepackagename=package.sourcepackagename, name='fifth')
     >>> logout()

-Visiting any template from the same page, user sees link to the other
+Visiting any template from the same page, the user sees links to the other
 templates.

     >>> browser.open(
@@ -152,7 +152,7 @@
     >>> print extract_text(relatives)
     Other templates here: first, forth, second, third

-For five template, the page displays the first four templates in
+For five templates, the page displays the first four templates in
 alphabetical order, and a link to the page listing all templates.

 Another template is added to the same source package.
@@ -175,7 +175,7 @@

     >>> browser.getLink('one other template').click()

-For more than five template, the page displays the first four templates in
+For more than five templates, the page displays the first four templates in
 alphabetical order, and a link to the page listing
 all templates, stating the number of other templates.

=== modified file 'lib/lp/translations/templates/potemplate-index.pt'
--- lib/lp/translations/templates/potemplate-index.pt 2009-12-21 19:39:06 +0000
+++ lib/lp/translations/templates/potemplate-index.pt 2009-12-22 08:42:05 +0000
@@ -47,7 +47,7 @@
              tal:content="pot/name">A POTemplate
           </a><tal:comma condition="not: repeat/pot/end">,
         </tal:comma></tal:relatives_by_source><!--
- --><tal:more-templates content="structure view/more_templates_by_source">
+ --><tal:more-templates replace="structure view/more_templates_by_source_link">
           and <a href="#">17 other templates</a>
           </tal:more-templates>
       </p>

Revision history for this message
Adi Roiban (adiroiban) wrote :

Here is the dot.

=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py 2009-12-22 08:48:01 +0000
+++ lib/lp/translations/browser/potemplate.py 2009-12-22 10:10:01 +0000
@@ -299,9 +299,9 @@
                     rootsite="translations",
                     view_name="+templates")
             if other == 1:
- return " and <a href=\"%s\">one other template</a>." % url
+ return " and <a href=\"%s\">one other template</a>" % url
             else:
- return " and <a href=\"%s\">%d other templates</a>." % (
+ return " and <a href=\"%s\">%d other templates</a>" % (
                     url, other)
         else:
             return ""

=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-22 08:48:01 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-22 10:11:38 +0000
@@ -74,7 +74,7 @@
     >>> alternate_notice = find_tag_by_id(anon_browser.contents,
     ... 'potemplate-relatives')
     >>> print extract_text(alternate_notice)
- Other templates here: evolution-2.2
+ Other templates here: evolution-2.2.

 The notice links to the alternate template.

@@ -150,7 +150,7 @@
     >>> relatives = find_tag_by_id(
     ... browser.contents, 'potemplate-relatives')
     >>> print extract_text(relatives)
- Other templates here: first, forth, second, third
+ Other templates here: first, forth, second, third.

 For five templates, the page displays the first four templates in
 alphabetical order, and a link to the page listing all templates.

=== modified file 'lib/lp/translations/templates/potemplate-index.pt'
--- lib/lp/translations/templates/potemplate-index.pt 2009-12-22 08:48:01 +0000
+++ lib/lp/translations/templates/potemplate-index.pt 2009-12-22 10:10:14 +0000
@@ -49,7 +49,7 @@
         </tal:comma></tal:relatives_by_source><!--
           --><tal:more-templates replace="structure view/more_templates_by_source_link">
           and <a href="#">17 other templates</a>
- </tal:more-templates>
+ </tal:more-templates>.
       </p>
     </div>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/browser/potemplate.py'
2--- lib/lp/translations/browser/potemplate.py 2009-12-09 01:03:22 +0000
3+++ lib/lp/translations/browser/potemplate.py 2009-12-22 10:31:17 +0000
4@@ -282,9 +282,29 @@
5 return by_source
6
7 @property
8- def has_more_templates_by_source(self):
9+ def more_templates_by_source_link(self):
10 by_source_count = self.context.relatives_by_source.count()
11- return by_source_count > self.SHOW_RELATED_TEMPLATES
12+ if (by_source_count > self.SHOW_RELATED_TEMPLATES):
13+ other = by_source_count - self.SHOW_RELATED_TEMPLATES
14+ if (self.context.distroseries):
15+ # XXX adiroiban 2009-12-21 bug=499058: A canonical_url for
16+ # SourcePackageName is needed to avoid hardcoding this URL.
17+ url = (canonical_url(
18+ self.context.distroseries, rootsite="translations") +
19+ "/+source/" + self.context.sourcepackagename.name +
20+ "/+translations")
21+ else:
22+ url = canonical_url(
23+ self.context.productseries,
24+ rootsite="translations",
25+ view_name="+templates")
26+ if other == 1:
27+ return " and <a href=\"%s\">one other template</a>" % url
28+ else:
29+ return " and <a href=\"%s\">%d other templates</a>" % (
30+ url, other)
31+ else:
32+ return ""
33
34 @property
35 def related_templates_by_name(self):
36@@ -438,7 +458,7 @@
37 warning = (
38 "A file could not be uploaded because its "
39 "name matched multiple existing uploads, for "
40- "different templates." )
41+ "different templates.")
42 ul_conflicts = (
43 "The conflicting file name was:<br /> "
44 "<ul><li>%s</li></ul>" % cgi.escape(conflicts[0]))
45@@ -482,9 +502,12 @@
46
47
48 class POTemplateViewPreferred(POTemplateView):
49+ """View class that shows only users preferred templates."""
50+
51 def pofiles(self):
52 return POTemplateView.pofiles(self, preferred_only=True)
53
54+
55 class POTemplateEditView(LaunchpadEditFormView):
56 """View class that lets you edit a POTemplate object."""
57
58@@ -548,7 +571,7 @@
59 if what == 'all':
60 export_potemplate = True
61
62- pofiles = self.context.pofiles
63+ pofiles = self.context.pofiles
64 elif what == 'some':
65 pofiles = []
66 export_potemplate = 'potemplate' in self.request.form
67@@ -580,6 +603,7 @@
68 """Return a list of PO files available for export."""
69
70 class BrowserPOFile:
71+
72 def __init__(self, value, browsername):
73 self.value = value
74 self.browsername = browsername
75@@ -699,6 +723,7 @@
76
77 class POTemplateBreadcrumb(Breadcrumb):
78 """Breadcrumb for `IPOTemplate`."""
79+
80 @property
81 def text(self):
82 return smartquote('Template "%s"' % self.context.name)
83
84=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-index.txt'
85--- lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-10 12:46:11 +0000
86+++ lib/lp/translations/stories/standalone/xx-potemplate-index.txt 2009-12-22 10:31:17 +0000
87@@ -44,23 +44,6 @@
88 Xhosa 22 ... ... ... &mdash;
89 Zulu 22 ... ... &mdash; &mdash;
90
91-When products have more than one template, the page informs the user
92-that there are alternates that may be translated.
93-
94- >>> anon_browser.open("http://translations.launchpad.dev/"
95- ... "evolution/trunk/+pots/evolution-2.2-test")
96- >>> alternate_notice = find_tag_by_id(anon_browser.contents,
97- ... 'potemplate-relatives')
98- >>> print extract_text(alternate_notice)
99- Other templates here: evolution-2.2
100-
101-The notice links to the alternate template.
102-
103- >>> print alternate_notice
104- <p...>
105- <span>Other templates here:</span>
106- <a href="/evolution/trunk/+pots/evolution-2.2">evolution-2.2</a>...
107- </p>
108
109 English is not translatable. We do not display English in the list of
110 languages when the user speaks English or even when English
111@@ -80,13 +63,36 @@
112 Finnish ... ... ... ... P\xf6ll\xe4
113
114
115+Finding related templates
116+-------------------------
117+
118+When products have more than one template, the page informs the user
119+that there are alternates that may be translated.
120+
121+ >>> anon_browser.open("http://translations.launchpad.dev/"
122+ ... "evolution/trunk/+pots/evolution-2.2-test")
123+ >>> alternate_notice = find_tag_by_id(anon_browser.contents,
124+ ... 'potemplate-relatives')
125+ >>> print extract_text(alternate_notice)
126+ Other templates here: evolution-2.2.
127+
128+The notice links to the alternate template.
129+
130+ >>> print alternate_notice
131+ <p...>
132+ <span>Other templates here:</span>
133+ <a href="/evolution/trunk/+pots/evolution-2.2">evolution-2.2</a>...
134+ </p>
135+
136+
137 DistroSeries and ProductSeries links to related templates
138 ---------------------------------------------------------
139
140 We are presented not only with links to alternate templates from the same
141 source, but also with links to the same template in (other) distroseries.
142
143- >>> anon_browser.open('http://translations.launchpad.dev/evolution/trunk/+pots/evolution-2.2')
144+ >>> anon_browser.open('http://translations.launchpad.dev/evolution'
145+ ... '/trunk/+pots/evolution-2.2')
146 >>> alternate_notice = find_tag_by_id(anon_browser.contents,
147 ... 'potemplate-rel-by-name')
148 >>> print extract_text(alternate_notice)
149@@ -96,7 +102,8 @@
150
151 The template in the other distroseries links back to this one.
152
153- >>> anon_browser.open('http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
154+ >>> anon_browser.open('http://translations.launchpad.dev/ubuntu'
155+ ... '/hoary/+source/evolution/+pots/evolution-2.2')
156 >>> alternate_notice = find_tag_by_id(anon_browser.contents,
157 ... 'potemplate-rel-by-name')
158 >>> print extract_text(alternate_notice)
159@@ -104,6 +111,141 @@
160 >>> print alternate_notice
161 <p...<a href="/evolution/trunk/+pots/evolution-2.2"...
162
163+When the branch or the source package contains less than five templates
164+they are all displayed on the template page.
165+
166+A source package with five templates is created.
167+
168+ >>> from zope.component import getUtility
169+ >>> from canonical.launchpad.interfaces.launchpad import (
170+ ... ILaunchpadCelebrities)
171+ >>> login('admin@canonical.com')
172+ >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
173+ >>> hoary = ubuntu.getSeries('hoary')
174+ >>> package = factory.makeSourcePackage(distroseries=hoary)
175+ >>> template = factory.makePOTemplate(
176+ ... distroseries=hoary,
177+ ... sourcepackagename=package.sourcepackagename, name='first')
178+ >>> template = factory.makePOTemplate(
179+ ... distroseries=hoary,
180+ ... sourcepackagename=package.sourcepackagename, name='second')
181+ >>> template = factory.makePOTemplate(
182+ ... distroseries=hoary,
183+ ... sourcepackagename=package.sourcepackagename, name='third')
184+ >>> template = factory.makePOTemplate(
185+ ... distroseries=hoary,
186+ ... sourcepackagename=package.sourcepackagename, name='forth')
187+ >>> template = factory.makePOTemplate(
188+ ... distroseries=hoary,
189+ ... sourcepackagename=package.sourcepackagename, name='fifth')
190+ >>> logout()
191+
192+Visiting any template from the same page, the user sees links to the other
193+templates.
194+
195+ >>> browser.open(
196+ ... ("http://translations.launchpad.dev/"
197+ ... "ubuntu/hoary/+source/%s/+pots/%s") % (
198+ ... package.name, template.name))
199+ >>> relatives = find_tag_by_id(
200+ ... browser.contents, 'potemplate-relatives')
201+ >>> print extract_text(relatives)
202+ Other templates here: first, forth, second, third.
203+
204+For five templates, the page displays the first four templates in
205+alphabetical order, and a link to the page listing all templates.
206+
207+Another template is added to the same source package.
208+
209+ >>> login('admin@canonical.com')
210+ >>> template = factory.makePOTemplate(
211+ ... distroseries=hoary,
212+ ... sourcepackagename=package.sourcepackagename, name='sixth')
213+ >>> logout()
214+
215+ >>> browser.open(
216+ ... ("http://translations.launchpad.dev/"
217+ ... "ubuntu/hoary/+source/%s/+pots/%s") % (
218+ ... package.name, template.name))
219+ >>> relatives = find_tag_by_id(
220+ ... browser.contents, 'potemplate-relatives')
221+ >>> print extract_text(relatives)
222+ Other templates here: fifth, first, forth, second
223+ and one other template.
224+
225+ >>> browser.getLink('one other template').click()
226+
227+For more than five templates, the page displays the first four templates in
228+alphabetical order, and a link to the page listing
229+all templates, stating the number of other templates.
230+
231+Another template is added to the same source package.
232+
233+ >>> login('admin@canonical.com')
234+ >>> template = factory.makePOTemplate(
235+ ... distroseries=hoary,
236+ ... sourcepackagename=package.sourcepackagename, name='seventh')
237+ >>> logout()
238+
239+ >>> browser.open((
240+ ... "http://translations.launchpad.dev/"
241+ ... "ubuntu/hoary/+source/%s/+pots/%s") % (
242+ ... package.name, template.name))
243+ >>> relatives = find_tag_by_id(
244+ ... browser.contents, 'potemplate-relatives')
245+ >>> print extract_text(relatives)
246+ Other templates here: fifth, first, forth, second
247+ and 2 other templates.
248+
249+ >>> browser.getLink('2 other templates').click()
250+ >>> browser.url == ((
251+ ... 'http://translations.launchpad.dev/'
252+ ... 'ubuntu/hoary/+source/%s/+translations') % (
253+ ... package.name))
254+ True
255+
256+The "other templates" link for product series templates is leading to a
257+page showing all templates for that product series.
258+
259+A product series with 7 templates is created.
260+
261+ >>> from zope.component import getUtility
262+ >>> from canonical.launchpad.interfaces.launchpad import (
263+ ... ILaunchpadCelebrities)
264+ >>> login('admin@canonical.com')
265+ >>> product = factory.makeProduct(name="fusa", official_rosetta=True)
266+ >>> product_trunk = product.getSeries('trunk')
267+ >>> template = factory.makePOTemplate(
268+ ... productseries=product_trunk, name='first')
269+ >>> template = factory.makePOTemplate(
270+ ... productseries=product_trunk, name='second')
271+ >>> template = factory.makePOTemplate(
272+ ... productseries=product_trunk, name='third')
273+ >>> template = factory.makePOTemplate(
274+ ... productseries=product_trunk, name='forth')
275+ >>> template = factory.makePOTemplate(
276+ ... productseries=product_trunk, name='fifth')
277+ >>> template = factory.makePOTemplate(
278+ ... productseries=product_trunk, name='sixth')
279+ >>> template = factory.makePOTemplate(
280+ ... productseries=product_trunk, name='seventh')
281+ >>> logout()
282+
283+ >>> browser.open((
284+ ... "http://translations.launchpad.dev/"
285+ ... "fusa/trunk/+pots/%s") % template.name)
286+ >>> relatives = find_tag_by_id(
287+ ... browser.contents, 'potemplate-relatives')
288+ >>> print extract_text(relatives)
289+ Other templates here: fifth, first, forth, second
290+ and 2 other templates.
291+
292+ >>> browser.getLink('2 other templates').click()
293+ >>> browser.url == (
294+ ... 'http://translations.launchpad.dev/'
295+ ... 'fusa/trunk/+templates')
296+ True
297+
298
299 Administering templates
300 -----------------------
301@@ -164,3 +306,4 @@
302 >>> print admin_browser.url
303 http://translations.launchpad.dev/ubuntu/+settings
304
305+
306
307=== modified file 'lib/lp/translations/templates/potemplate-index.pt'
308--- lib/lp/translations/templates/potemplate-index.pt 2009-12-07 21:58:53 +0000
309+++ lib/lp/translations/templates/potemplate-index.pt 2009-12-22 10:31:17 +0000
310@@ -47,8 +47,9 @@
311 tal:content="pot/name">A POTemplate
312 </a><tal:comma condition="not: repeat/pot/end">,
313 </tal:comma></tal:relatives_by_source><!--
314- --><tal:more condition="view/has_more_templates_by_source"
315- >...</tal:more>
316+ --><tal:more-templates replace="structure view/more_templates_by_source_link">
317+ and <a href="#">17 other templates</a>
318+ </tal:more-templates>.
319 </p>
320 </div>
321