Merge ~cjwatson/launchpad:distroseries-urls-canonical into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 4013ff1a5a358052b99cae5abec44473e737d2e8
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:distroseries-urls-canonical
Merge into: launchpad:master
Diff against target: 4747 lines (+635/-557)
127 files modified
lib/canonical/launchpad/icing/style.css (+5/-5)
lib/lp/answers/stories/question-add.txt (+1/-1)
lib/lp/app/browser/doc/launchpad-search-pages.txt (+1/-1)
lib/lp/app/browser/launchpad.py (+1/-1)
lib/lp/app/browser/lazrjs.py (+1/-1)
lib/lp/app/stories/basics/notfound-traversals.txt (+5/-4)
lib/lp/blueprints/stories/blueprints/xx-creation.txt (+7/-6)
lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt (+6/-4)
lib/lp/blueprints/stories/standalone/xx-overview.txt (+5/-3)
lib/lp/blueprints/stories/standalone/xx-views.txt (+3/-3)
lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt (+2/-1)
lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt (+2/-2)
lib/lp/bugs/stories/bugs/xx-bugs.txt (+1/-1)
lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt (+7/-6)
lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt (+2/-1)
lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt (+3/-2)
lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt (+2/-1)
lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt (+2/-1)
lib/lp/bugs/stories/feeds/xx-bug-atom.txt (+5/-4)
lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt (+1/-1)
lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt (+2/-1)
lib/lp/bugs/stories/standalone/xx-show-distribution-cve-report.txt (+1/-1)
lib/lp/bugs/stories/standalone/xx-show-distrorelease-cve-report.txt (+2/-2)
lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt (+3/-3)
lib/lp/bugs/tests/test_searchtasks_webservice.py (+4/-2)
lib/lp/code/stories/webservice/xx-code-import.txt (+2/-3)
lib/lp/registry/browser/configure.zcml (+1/-1)
lib/lp/registry/browser/distribution.py (+7/-5)
lib/lp/registry/browser/tests/distroseries-views.txt (+2/-2)
lib/lp/registry/browser/tests/packaging-views.txt (+3/-3)
lib/lp/registry/browser/tests/sourcepackage-views.txt (+5/-5)
lib/lp/registry/browser/tests/test_distribution.py (+21/-21)
lib/lp/registry/browser/tests/test_packaging.py (+4/-3)
lib/lp/registry/browser/tests/test_sourcepackage_views.py (+1/-1)
lib/lp/registry/stories/distribution/xx-distribution-overview.txt (+9/-8)
lib/lp/registry/stories/distroseries/distroseries-admin.txt (+11/-10)
lib/lp/registry/stories/distroseries/xx-distroseries-index.txt (+7/-7)
lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt (+7/-6)
lib/lp/registry/stories/milestone/object-milestones.txt (+4/-2)
lib/lp/registry/stories/milestone/xx-create-milestone-on-distribution.txt (+5/-3)
lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt (+5/-4)
lib/lp/registry/stories/milestone/xx-milestone-description.txt (+2/-1)
lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt (+4/-3)
lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt (+2/-2)
lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt (+5/-5)
lib/lp/registry/stories/person/xx-deactivate-account.txt (+2/-1)
lib/lp/registry/stories/product/xx-product-package-pages.txt (+7/-4)
lib/lp/registry/stories/productseries/xx-productseries-delete.txt (+3/-1)
lib/lp/registry/stories/productseries/xx-productseries-index.txt (+1/-1)
lib/lp/registry/stories/webservice/xx-distribution.txt (+3/-3)
lib/lp/registry/stories/webservice/xx-distroseries.txt (+27/-25)
lib/lp/registry/stories/webservice/xx-source-package.txt (+3/-3)
lib/lp/services/feeds/stories/xx-links.txt (+2/-2)
lib/lp/services/sitesearch/tests/data/bingsearchservice-bugs-2.json (+1/-1)
lib/lp/services/sitesearch/tests/test_bing.py (+1/-1)
lib/lp/services/webapp/doc/canonical_url_examples.txt (+5/-5)
lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html (+2/-2)
lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js (+4/-4)
lib/lp/snappy/tests/test_snap.py (+1/-1)
lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt (+4/-2)
lib/lp/soyuz/browser/tests/sourcepackage-views.txt (+4/-2)
lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt (+2/-2)
lib/lp/soyuz/stories/distroseries/add-architecture.txt (+6/-5)
lib/lp/soyuz/stories/packaging/package-pages-navigation.txt (+5/-4)
lib/lp/soyuz/stories/soyuz/xx-binarypackagerelease-index.txt (+9/-7)
lib/lp/soyuz/stories/soyuz/xx-build-record.txt (+6/-6)
lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt (+5/-5)
lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt (+2/-2)
lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt (+9/-8)
lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt (+8/-7)
lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt (+7/-5)
lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt (+4/-4)
lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt (+18/-16)
lib/lp/soyuz/stories/soyuz/xx-person-packages.txt (+1/-1)
lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt (+1/-1)
lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt (+1/-1)
lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt (+4/-4)
lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt (+8/-5)
lib/lp/soyuz/stories/webservice/xx-archive.txt (+4/-4)
lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt (+1/-1)
lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt (+8/-8)
lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt (+1/-1)
lib/lp/soyuz/stories/webservice/xx-packageset.txt (+7/-7)
lib/lp/soyuz/stories/webservice/xx-packageupload.txt (+2/-2)
lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt (+1/-1)
lib/lp/testing/pages.py (+1/-1)
lib/lp/translations/browser/tests/pofile-views.txt (+1/-1)
lib/lp/translations/browser/tests/test_breadcrumbs.py (+3/-3)
lib/lp/translations/doc/canonical_url_examples.txt (+3/-3)
lib/lp/translations/doc/poexport-request.txt (+3/-2)
lib/lp/translations/stories/distribution/xx-distribution-translations.txt (+9/-9)
lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt (+7/-7)
lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt (+19/-14)
lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt (+1/-1)
lib/lp/translations/stories/importqueue/xx-translation-import-queue-targets.txt (+2/-1)
lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt (+4/-4)
lib/lp/translations/stories/navigation-links/pofile.txt (+3/-3)
lib/lp/translations/stories/navigation-links/pomsgset.txt (+3/-3)
lib/lp/translations/stories/navigation-links/potemplate.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-details.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-pofile-export.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt (+2/-2)
lib/lp/translations/stories/standalone/xx-pofile-translate-empty-strings-without-validation.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-gettext-error-middle-page.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-lang-direction.txt (+7/-6)
lib/lp/translations/stories/standalone/xx-pofile-translate-legal-warning.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt (+7/-7)
lib/lp/translations/stories/standalone/xx-pofile-translate-needs-review-flags-preserved.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt (+3/-3)
lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt (+6/-6)
lib/lp/translations/stories/standalone/xx-pofile-translate-search.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-pofile-translate.txt (+9/-8)
lib/lp/translations/stories/standalone/xx-potemplate-admin.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-potemplate-export.txt (+4/-4)
lib/lp/translations/stories/standalone/xx-potemplate-index.txt (+20/-19)
lib/lp/translations/stories/standalone/xx-rosetta-source-package-redirects.txt (+4/-4)
lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-series-templates.txt (+18/-13)
lib/lp/translations/stories/standalone/xx-serieslanguage-index.txt (+9/-9)
lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt (+8/-7)
lib/lp/translations/stories/standalone/xx-test-potlists.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-translation-help.txt (+1/-1)
lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt (+21/-18)
lib/lp/translations/stories/translationgroups/xx-translationgroups.txt (+16/-16)
lib/lp/translations/stories/translations/xx-translations.txt (+40/-35)
lib/lp/translations/stories/webservice/xx-potemplate.txt (+6/-5)
Reviewer Review Type Date Requested Status
Tom Wardill (community) Approve
Review via email: mp+391191@code.launchpad.net

Commit message

Make new-style distroseries URLs canonical

Description of the change

/<distribution>/<distroseries> is now a redirect to the canonical form of /<distribution>/+series/<distroseries>. This makes it more conceivable to use the default traversal on distributions for something more common.

To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) wrote :

That's a lot of test changes :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/canonical/launchpad/icing/style.css b/lib/canonical/launchpad/icing/style.css
2index 92f2d6f..1d73268 100644
3--- a/lib/canonical/launchpad/icing/style.css
4+++ b/lib/canonical/launchpad/icing/style.css
5@@ -812,7 +812,7 @@ input.translate {width: 90%; max-width: 60em;}
6 /* Templates listing.
7 *
8 * Examples:
9- * https://translations.launchpad.test/ubuntu/hoary/+templates
10+ * https://translations.launchpad.test/ubuntu/+series/hoary/+templates
11 * https://translations.launchpad.test/evolution/trunk/+templates
12 */
13 .inactive-template td {
14@@ -823,7 +823,7 @@ input.translate {width: 90%; max-width: 60em;}
15 /* Translations statistics and legend.
16 *
17 * Examples:
18- * https://translations.launchpad.test/ubuntu/hoary/+lang/es
19+ * https://translations.launchpad.test/ubuntu/+series/hoary/+lang/es
20 * https://translations.launchpad.test/evolution/trunk/+lang/es
21 */
22
23@@ -857,9 +857,9 @@ table.translation-stats tfoot td {
24 /* Translations help links.
25 *
26 * Examples:
27- * https://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/
28- * https://translations.launchpad.test/ubuntu/hoary/+source/evolution/+translations
29- * https://translations.launchpad.test/ubuntu/hoary/
30+ * https://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/
31+ * https://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+translations
32+ * https://translations.launchpad.test/ubuntu/+series/hoary/
33 *
34 */
35 div.translation-help-links a {
36diff --git a/lib/lp/answers/stories/question-add.txt b/lib/lp/answers/stories/question-add.txt
37index b8cfea0..614ca54 100644
38--- a/lib/lp/answers/stories/question-add.txt
39+++ b/lib/lp/answers/stories/question-add.txt
40@@ -37,7 +37,7 @@ start the creation process. Questions created this way will be
41 associated with the source package of the used application.
42
43 >>> user_browser.open(
44- ... 'http://launchpad.test/ubuntu/hoary/'
45+ ... 'http://launchpad.test/ubuntu/+series/hoary/'
46 ... '+sources/mozilla-firefox/+gethelp')
47 >>> print(user_browser.title)
48 Help and support...
49diff --git a/lib/lp/app/browser/doc/launchpad-search-pages.txt b/lib/lp/app/browser/doc/launchpad-search-pages.txt
50index 83347e4..e7f211c 100644
51--- a/lib/lp/app/browser/doc/launchpad-search-pages.txt
52+++ b/lib/lp/app/browser/doc/launchpad-search-pages.txt
53@@ -519,7 +519,7 @@ showing the matching terms in context of the page text.
54 >>> page.title
55 u'...Bug... #2 in Ubuntu Hoary: \u201cBlackhole Trash folder\u201d'
56 >>> page.url
57- 'http://bugs.launchpad.test/ubuntu/hoary/+bug/2'
58+ 'http://bugs.launchpad.test/ubuntu/+series/hoary/+bug/2'
59 >>> page.summary
60 u'...Launchpad\u2019s ...bug... tracker allows collaboration...'
61
62diff --git a/lib/lp/app/browser/launchpad.py b/lib/lp/app/browser/launchpad.py
63index d31df20..28064f9 100644
64--- a/lib/lp/app/browser/launchpad.py
65+++ b/lib/lp/app/browser/launchpad.py
66@@ -476,7 +476,7 @@ class Macro:
67 http://launchpad.net/+main-template-macros
68 http://launchpad.net/ubuntu/+main-template-macros
69 http://launchpad.net/ubuntu/+main-template-macros
70- https://blueprints.launchpad.test/ubuntu/hoary/+main-template-macros
71+ https://blueprints.launchpad.test/ubuntu/+series/hoary/+main-template-macros
72
73 Obviously, those requests wouldn't do anything useful and would instead
74 generate an OOPS.
75diff --git a/lib/lp/app/browser/lazrjs.py b/lib/lp/app/browser/lazrjs.py
76index 8288bf3..093a803 100644
77--- a/lib/lp/app/browser/lazrjs.py
78+++ b/lib/lp/app/browser/lazrjs.py
79@@ -460,7 +460,7 @@ class InlineMultiCheckboxWidget(WidgetBase):
80 :param attribute_type: The attribute type. Currently only "reference"
81 is supported. Used to determine whether to linkify the selected
82 checkbox item values. So ubuntu/hoary becomes
83- http://launchpad.net/devel/api/ubuntu/hoary
84+ http://launchpad.net/devel/api/ubuntu/+series/hoary
85 :param vocabulary: The name of the vocabulary which provides the
86 items or a vocabulary instance.
87 :param header: The text to display as the title of the popup form.
88diff --git a/lib/lp/app/stories/basics/notfound-traversals.txt b/lib/lp/app/stories/basics/notfound-traversals.txt
89index 4a6cc78..56ad01b 100644
90--- a/lib/lp/app/stories/basics/notfound-traversals.txt
91+++ b/lib/lp/app/stories/basics/notfound-traversals.txt
92@@ -52,9 +52,9 @@ Ubuntu release is still pointing to this old URL (see bug #138090).
93 http://launchpad.test/firefox
94 >>> check_redirect("/ubuntu/+source/evolution/+editbugcontact")
95 +subscribe
96- >>> check_redirect("/ubuntu/hoary/+latest-full-language-pack")
97+ >>> check_redirect("/ubuntu/+series/hoary/+latest-full-language-pack")
98 http://localhost:.../ubuntu-hoary-translations.tar.gz
99- >>> check_redirect("/ubuntu/hoary/+source/mozilla-firefox/+pots")
100+ >>> check_redirect("/ubuntu/+series/hoary/+source/mozilla-firefox/+pots")
101 http://launchpad.test/.../+pots/../+translations
102
103 Viewing a bug in the context of an upstream where the bug has already
104@@ -106,10 +106,11 @@ distroseries or sourcepackage is *targeted* to be fixed in that specific
105 release. Instead, you get redirected to the appropriate distro or
106 distrosourcepackage filebug page.
107
108- >>> check_redirect("/ubuntu/warty/+filebug", auth=True)
109+ >>> check_redirect("/ubuntu/+series/warty/+filebug", auth=True)
110 http://launchpad.test/ubuntu/+filebug
111 >>> check_redirect(
112- ... "/ubuntu/warty/+source/mozilla-firefox/+filebug", auth=True)
113+ ... "/ubuntu/+series/warty/+source/mozilla-firefox/+filebug",
114+ ... auth=True)
115 http://launchpad.test/ubuntu/+source/mozilla-firefox/+filebug
116
117 The old +filebug-advanced form now redirects to the +filebug form.
118diff --git a/lib/lp/blueprints/stories/blueprints/xx-creation.txt b/lib/lp/blueprints/stories/blueprints/xx-creation.txt
119index 8eeb212..162ee6c 100644
120--- a/lib/lp/blueprints/stories/blueprints/xx-creation.txt
121+++ b/lib/lp/blueprints/stories/blueprints/xx-creation.txt
122@@ -98,7 +98,8 @@ From a distribution series
123
124 Starting from the Ubuntu Hoary distribution series page:
125
126- >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/hoary')
127+ >>> user_browser.open(
128+ ... 'http://blueprints.launchpad.test/ubuntu/+series/hoary')
129
130 Users can also follow the textual "Register a blueprint" link:
131
132@@ -106,7 +107,7 @@ Users can also follow the textual "Register a blueprint" link:
133 ... user_browser.contents, 'menu-link-new'):
134 ... print(tag)
135 <a class="menu-link-new..."
136- href="http://blueprints.launchpad.test/ubuntu/hoary/+addspec">Register
137+ href="http://blueprints.launchpad.test/ubuntu/+series/hoary/+addspec">Register
138 a blueprint</a>
139
140
141@@ -288,7 +289,7 @@ the series.
142 Let's register a blueprint from the Ubuntu Hoary distribution series:
143
144 >>> user_browser.open(
145- ... 'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
146+ ... 'http://blueprints.launchpad.test/ubuntu/+series/hoary/+addspec')
147 >>> control('Name').value = 'networkmagic-2'
148 >>> control('Title').value = 'Network Magic: Auto Network Detection'
149 >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic2'
150@@ -297,7 +298,7 @@ Let's register a blueprint from the Ubuntu Hoary distribution series:
151 Canceling creation, brings one back to the blueprints Hoary home.
152
153 >>> user_browser.getLink('Cancel').url
154- 'http://blueprints.launchpad.test/ubuntu/hoary'
155+ 'http://blueprints.launchpad.test/ubuntu/+series/hoary'
156
157 By default, blueprints are not proposed as series goals:
158
159@@ -321,7 +322,7 @@ The new blueprint is not proposed as a series goal:
160 Let's register another blueprint from the Mozilla Firefox 1.0 product series:
161
162 >>> user_browser.open(
163- ... 'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
164+ ... 'http://blueprints.launchpad.test/ubuntu/+series/hoary/+addspec')
165 >>> control('Name').value = 'networkmagic-3'
166 >>> control('Title').value = 'Network Magic: Auto Network Detection'
167 >>> control('URL').value = 'http://wiki.ubuntu.com/NetworkMagic3'
168@@ -350,7 +351,7 @@ If the registration is performed by a user with permission to accept goals
169 for the series, the new blueprint is automatically accepted as a series goal:
170
171 >>> admin_browser.open(
172- ... 'http://blueprints.launchpad.test/ubuntu/hoary/+addspec')
173+ ... 'http://blueprints.launchpad.test/ubuntu/+series/hoary/+addspec')
174 >>> control = admin_browser.getControl
175 >>> control('Name').value = 'networkmagic-4'
176 >>> control('Title').value = 'Network Magic: Auto Network Detection'
177diff --git a/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt b/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
178index af5e43f..6a058b6 100644
179--- a/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
180+++ b/lib/lp/blueprints/stories/blueprints/xx-distrorelease.txt
181@@ -76,26 +76,28 @@ first be approved.
182
183 >>> import six
184 >>> result = six.text_type(http(r"""
185- ... GET /ubuntu/hoary/+specs HTTP/1.1
186+ ... GET /ubuntu/+series/hoary/+specs HTTP/1.1
187 ... """))
188 >>> '<td>CD Media Integrity Check' not in result
189 True
190
191 However, we can expect to find it on the approvals page.
192
193- >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy/+specs')
194+ >>> user_browser.open(
195+ ... 'http://blueprints.launchpad.test/ubuntu/+series/grumpy/+specs')
196 >>> "CD Media Integrity Check" in user_browser.contents
197 False
198
199 We will accept it:
200
201- >>> admin_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy/+setgoals')
202+ >>> admin_browser.open(
203+ ... 'http://blueprints.launchpad.test/ubuntu/+series/grumpy/+setgoals')
204 >>> 'CD Media Integrity' in admin_browser.contents
205 True
206 >>> admin_browser.getControl('CD Media Integrity Check').selected = True
207 >>> admin_browser.getControl('Accept').click()
208 >>> admin_browser.url
209- 'http://blueprints.launchpad.test/ubuntu/grumpy'
210+ 'http://blueprints.launchpad.test/ubuntu/+series/grumpy'
211 >>> 'Accepted 1 specification(s)' in admin_browser.contents
212 True
213
214diff --git a/lib/lp/blueprints/stories/standalone/xx-overview.txt b/lib/lp/blueprints/stories/standalone/xx-overview.txt
215index 51f6894..153fe4d 100644
216--- a/lib/lp/blueprints/stories/standalone/xx-overview.txt
217+++ b/lib/lp/blueprints/stories/standalone/xx-overview.txt
218@@ -147,7 +147,8 @@ those targeted to a given distribution series. Let's pick the Grumpy Groundhog
219 series as an example. To begin with, there are no blueprints listed on the
220 blueprints page for Grumpy:
221
222- >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
223+ >>> user_browser.open(
224+ ... 'http://blueprints.launchpad.test/ubuntu/+series/grumpy')
225 >>> main = find_main_content(user_browser.contents)
226 >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
227 Blueprints for Grumpy
228@@ -177,7 +178,7 @@ Let's target an existing Ubuntu blueprint to the Grumpy series:
229 We'll also target the blueprint to a milestone. First we'll create a
230 milestone:
231
232- >>> browser.open('http://launchpad.test/ubuntu/grumpy/')
233+ >>> browser.open('http://launchpad.test/ubuntu/+series/grumpy/')
234 >>> browser.getLink('Create milestone').click()
235 >>> browser.getControl('Name').value = 'drift-1'
236 >>> browser.getControl('Date Targeted').value = '2050-05-05'
237@@ -206,7 +207,8 @@ Now we'll target our chosen blueprint to the new milestone:
238 Finally, the blueprint listing for Grumpy includes an entry for our chosen
239 blueprint. It also lists the milestone to which the blueprint is targeted:
240
241- >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
242+ >>> user_browser.open(
243+ ... 'http://blueprints.launchpad.test/ubuntu/+series/grumpy')
244 >>> main = find_main_content(user_browser.contents)
245 >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
246 Blueprints for Grumpy...
247diff --git a/lib/lp/blueprints/stories/standalone/xx-views.txt b/lib/lp/blueprints/stories/standalone/xx-views.txt
248index f37bcfc..b03032c 100644
249--- a/lib/lp/blueprints/stories/standalone/xx-views.txt
250+++ b/lib/lp/blueprints/stories/standalone/xx-views.txt
251@@ -137,7 +137,7 @@ project, distro and distroseries.
252 ...
253
254 >>> print(http("""
255- ... GET /ubuntu/hoary/+documentation HTTP/1.1
256+ ... GET /ubuntu/+series/hoary/+documentation HTTP/1.1
257 ... """))
258 HTTP/1.1 200 Ok
259 ...
260@@ -243,10 +243,10 @@ blueprint targets:
261
262 * distribution series:
263
264- >>> browser.open('http://blueprints.launchpad.test/ubuntu/hoary')
265+ >>> browser.open('http://blueprints.launchpad.test/ubuntu/+series/hoary')
266 >>> browser.getLink('List all blueprints').click()
267 >>> browser.url
268- 'http://blueprints.launchpad.test/ubuntu/hoary/+specs?show=all'
269+ 'http://blueprints.launchpad.test/ubuntu/+series/hoary/+specs?show=all'
270
271 * project groups:
272
273diff --git a/lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt b/lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt
274index 3c93bc9..fab7204 100644
275--- a/lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt
276+++ b/lib/lp/bugs/stories/bug-release-management/xx-bug-release-management.txt
277@@ -208,7 +208,8 @@ in the release task.
278 LinkNotFoundError...
279
280 >>> ubuntu_hoary_edit_url = (
281- ... 'http://bugs.launchpad.test/ubuntu/hoary/+bug/2/+editstatus')
282+ ... 'http://bugs.launchpad.test/ubuntu/+series/hoary/+bug/2/'
283+ ... '+editstatus')
284 >>> user_browser.getLink(url=ubuntu_hoary_edit_url) is not None
285 True
286
287diff --git a/lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt b/lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt
288index 4f6594f..27211da 100644
289--- a/lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt
290+++ b/lib/lp/bugs/stories/bugs/xx-bug-obfuscation.txt
291@@ -11,7 +11,7 @@ description in the bug page.
292
293 >>> user_browser.open(
294 ... 'http://bugs.launchpad.test'
295- ... '/debian/sarge/+source/mozilla-firefox/+bug/3')
296+ ... '/debian/+series/sarge/+source/mozilla-firefox/+bug/3')
297 >>> user_browser.title
298 'Bug #3 ...'
299
300@@ -24,7 +24,7 @@ An anonymous cannot see the email address anywhere in the page.
301
302 >>> anon_browser.open(
303 ... 'http://bugs.launchpad.test'
304- ... '/debian/sarge/+source/mozilla-firefox/+bug/3')
305+ ... '/debian/+series/sarge/+source/mozilla-firefox/+bug/3')
306 >>> print(anon_browser.title)
307 Bug #3 ...
308
309diff --git a/lib/lp/bugs/stories/bugs/xx-bugs.txt b/lib/lp/bugs/stories/bugs/xx-bugs.txt
310index 0dd58c7..c2b8777 100644
311--- a/lib/lp/bugs/stories/bugs/xx-bugs.txt
312+++ b/lib/lp/bugs/stories/bugs/xx-bugs.txt
313@@ -1,7 +1,7 @@
314 This page checks that we can see a list of bugs on the distroseries,
315 specifically Hoary.
316
317- >>> browser.open('http://localhost/ubuntu/hoary/+bugs')
318+ >>> browser.open('http://localhost/ubuntu/+series/hoary/+bugs')
319 >>> print(browser.title)
320 Hoary (5.04) : Bugs : Ubuntu
321
322diff --git a/lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt b/lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt
323index 7d95a21..f06d582 100644
324--- a/lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt
325+++ b/lib/lp/bugs/stories/bugs/xx-distrorelease-bugs-page.txt
326@@ -3,7 +3,8 @@
327 The +bugs page for a distribution series presents some basic information the
328 bugs, as well as a listing.
329
330- >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty/+bugs')
331+ >>> anon_browser.open(
332+ ... 'http://bugs.launchpad.test/ubuntu/+series/warty/+bugs')
333 >>> anon_browser.title
334 'Warty (4.10) : Bugs : Ubuntu'
335
336@@ -14,16 +15,16 @@ The page has a link to see all open bugs.
337
338 >>> anon_browser.getLink('Open bugs').click()
339 >>> anon_browser.url
340- 'http://bugs.launchpad.test/ubuntu/warty/+bugs'
341+ 'http://bugs.launchpad.test/ubuntu/+series/warty/+bugs'
342 >>> find_tags_by_class(anon_browser.contents, 'buglisting-row') is not None
343 True
344
345 It also has a link to subscribe to bug mail.
346
347- >>> user_browser.open('http://bugs.launchpad.test/ubuntu/warty')
348+ >>> user_browser.open('http://bugs.launchpad.test/ubuntu/+series/warty')
349 >>> user_browser.getLink('Subscribe to bug mail').click()
350 >>> print(user_browser.url)
351- http://bugs.launchpad.test/ubuntu/warty/+subscribe
352+ http://bugs.launchpad.test/ubuntu/+series/warty/+subscribe
353
354
355 == Bugs Fixed Elsewhere ==
356@@ -31,7 +32,7 @@ It also has a link to subscribe to bug mail.
357 The Bugs frontpage includes the number of bugs that are fixed in some
358 other context.
359
360- >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty')
361+ >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+series/warty')
362 >>> fixed_elsewhere_link = anon_browser.getLink('Bugs fixed elsewhere')
363
364 The link takes you to the list of the bugs fixed elsewhere.
365@@ -51,7 +52,7 @@ The link takes you to the list of the bugs fixed elsewhere.
366 The bugs page displays the number of Incomplete, unattended bugs that
367 can expire when the project has enabled bug expiration.
368
369- >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/warty')
370+ >>> anon_browser.open('http://bugs.launchpad.test/ubuntu/+series/warty')
371 >>> expirable_bugs_link = anon_browser.getLink('Incomplete bugs')
372
373 The link goes to the expirable bugs page, where the anonymous user can
374diff --git a/lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt b/lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt
375index 1224187..a04d86f 100644
376--- a/lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt
377+++ b/lib/lp/bugs/stories/bugs/xx-portlets-bug-milestones.txt
378@@ -58,7 +58,8 @@ becomes visible.
379
380 And look at the portlet.
381
382- >>> anon_browser.open("http://bugs.launchpad.test/debian/sarge/+bugs")
383+ >>> anon_browser.open(
384+ ... "http://bugs.launchpad.test/debian/+series/sarge/+bugs")
385 >>> portlet = find_portlet(
386 ... anon_browser.contents, "Milestone-targeted bugs")
387 >>> print(extract_text(portlet))
388diff --git a/lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt b/lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt
389index 207adb8..b515f36 100644
390--- a/lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt
391+++ b/lib/lp/bugs/stories/bugs/xx-portlets-bug-series.txt
392@@ -23,7 +23,8 @@ Change debian to track bugs in Launchpad and the portlet becomes visible.
393 2
394 woody
395
396- >>> anon_browser.open("http://bugs.launchpad.test/debian/sarge/+bugs")
397+ >>> anon_browser.open(
398+ ... "http://bugs.launchpad.test/debian/+series/sarge/+bugs")
399 >>> portlet = find_portlet(anon_browser.contents, "Series-targeted bugs")
400 >>> print(extract_text(portlet))
401 Series-targeted bugs
402@@ -42,7 +43,7 @@ Change debian to track bugs in Launchpad and the portlet becomes visible.
403 warty
404
405 >>> print(anon_browser.getLink("hoary").url)
406- http://bugs.launchpad.test/ubuntu/hoary/+bugs
407+ http://bugs.launchpad.test/ubuntu/+series/hoary/+bugs
408
409 The same portlet is also available for project and project series
410 listings and homepages:
411diff --git a/lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt b/lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt
412index b612e76..c9a79c4 100644
413--- a/lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt
414+++ b/lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt
415@@ -75,7 +75,8 @@ Foo Bar is a member of the Ubuntu team that owns the Ubuntu
416 distribution. They decide to assign some bug to some milestones.
417 First they register a new milestone.
418
419- >>> admin_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
420+ >>> admin_browser.open(
421+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone')
422 >>> name_field = admin_browser.getControl('Name:')
423 >>> name_field.value = '5.04.rc1'
424 >>> admin_browser.getControl('Register Milestone').click()
425diff --git a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
426index 7300d25..ba1a71f 100644
427--- a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
428+++ b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
429@@ -223,7 +223,8 @@ Milestones are also presented as badges on bugs, and linked to the
430 relevant listings:
431
432 >>> browser.open(
433- ... 'http://bugs.launchpad.test/debian/sarge/+source/mozilla-firefox')
434+ ... 'http://bugs.launchpad.test/debian/+series/sarge/+source/'
435+ ... 'mozilla-firefox')
436 >>> milestone = find_tags_by_class(browser.contents, 'sprite milestone')
437 >>> print(milestone[0])
438 <a alt="milestone 3.1" class="sprite milestone"
439diff --git a/lib/lp/bugs/stories/feeds/xx-bug-atom.txt b/lib/lp/bugs/stories/feeds/xx-bug-atom.txt
440index 0620bef..b6fcb56 100644
441--- a/lib/lp/bugs/stories/feeds/xx-bug-atom.txt
442+++ b/lib/lp/bugs/stories/feeds/xx-bug-atom.txt
443@@ -263,24 +263,25 @@ This feed gets the latest bugs for a distribution series, and has the same
444 type of content as the latest bugs feed for a product.
445
446 >>> browser.open(
447- ... 'http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom')
448+ ... 'http://feeds.launchpad.test/ubuntu/+series/hoary/'
449+ ... 'latest-bugs.atom')
450 >>> validate_feed(browser.contents,
451 ... browser.headers['content-type'], browser.url)
452 No Errors
453 >>> BeautifulSoup(browser.contents, 'xml').title.contents
454 [u'Bugs in Hoary']
455 >>> browser.url
456- 'http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom'
457+ 'http://feeds.launchpad.test/ubuntu/+series/hoary/latest-bugs.atom'
458
459 >>> soup = BeautifulSoup(
460 ... browser.contents, 'xml', parse_only=SoupStrainer('id'))
461 >>> print(extract_text(soup.find('id')))
462- tag:launchpad.net,2006-10-16:/bugs/ubuntu/hoary
463+ tag:launchpad.net,2006-10-16:/bugs/ubuntu/+series/hoary
464
465 >>> self_links = parse_links(browser.contents, 'self')
466 >>> for link in self_links:
467 ... print(link)
468- <link href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom" rel="self"/>
469+ <link href="http://feeds.launchpad.test/ubuntu/+series/hoary/latest-bugs.atom" rel="self"/>
470
471 >>> entries = parse_entries(browser.contents)
472 >>> print(len(entries))
473diff --git a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt
474index 96782f6..637c64b 100644
475--- a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt
476+++ b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-guidelines.txt
477@@ -123,7 +123,7 @@ Bugs can also be reported directly against a distribution series, for
478 which the guidelines are taken from the respective distribution.
479
480 >>> user_browser.open(
481- ... 'http://launchpad.test/ubuntu/warty/+filebug')
482+ ... 'http://launchpad.test/ubuntu/+series/warty/+filebug')
483 >>> user_browser.getControl('Summary', index=0).value = "It doesn't work"
484 >>> user_browser.getControl('Continue').click()
485 >>> print(extract_text(find_tag_by_id(
486diff --git a/lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt b/lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt
487index 7e202d9..62c733c 100644
488--- a/lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt
489+++ b/lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt
490@@ -33,7 +33,8 @@ The no-redirect parameter is retained when we redirect a user to the bug
491 filing view of another context.
492
493 >>> user_browser.open(
494- ... 'http://bugs.launchpad.test/ubuntu/hoary/+filebug?no-redirect')
495+ ... 'http://bugs.launchpad.test/ubuntu/+series/hoary/+filebug'
496+ ... '?no-redirect')
497 >>> print(user_browser.url)
498 http://bugs.launchpad.test/ubuntu/+filebug?no-redirect
499
500diff --git a/lib/lp/bugs/stories/standalone/xx-show-distribution-cve-report.txt b/lib/lp/bugs/stories/standalone/xx-show-distribution-cve-report.txt
501index 1020f76..1d3eec4 100644
502--- a/lib/lp/bugs/stories/standalone/xx-show-distribution-cve-report.txt
503+++ b/lib/lp/bugs/stories/standalone/xx-show-distribution-cve-report.txt
504@@ -34,7 +34,7 @@ Instead, the links for the specific series reports are shown.
505
506 >>> browser.getLink('Breezy Badger Autotest').click()
507 >>> browser.url
508- 'http://launchpad.test/ubuntu/breezy-autotest/+cve'
509+ 'http://launchpad.test/ubuntu/+series/breezy-autotest/+cve'
510
511 >>> main = find_main_content(browser.contents)
512 >>> print(extract_text(main.h1))
513diff --git a/lib/lp/bugs/stories/standalone/xx-show-distrorelease-cve-report.txt b/lib/lp/bugs/stories/standalone/xx-show-distrorelease-cve-report.txt
514index 069e840..fe1609f 100644
515--- a/lib/lp/bugs/stories/standalone/xx-show-distrorelease-cve-report.txt
516+++ b/lib/lp/bugs/stories/standalone/xx-show-distrorelease-cve-report.txt
517@@ -1,6 +1,6 @@
518 Let's look at all CVE issues in Debian and Debian Woody
519
520- >>> browser.open('http://launchpad.test/debian/woody/+cve')
521+ >>> browser.open('http://launchpad.test/debian/+series/woody/+cve')
522 >>> print('\n'+browser.contents)
523 <BLANKLINE>
524 ...
525@@ -36,7 +36,7 @@ sourcepackage value from the bug listed on the previous report.
526
527 >>> logout()
528
529- >>> browser.open('http://launchpad.test/debian/woody/+cve')
530+ >>> browser.open('http://launchpad.test/debian/+series/woody/+cve')
531 >>> main = find_main_content(browser.contents)
532 >>> for tr in main.table.tbody.findAll('tr'):
533 ... print(extract_text(tr))
534diff --git a/lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt b/lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt
535index 74481b4..234a83d 100644
536--- a/lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt
537+++ b/lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt
538@@ -84,7 +84,7 @@ The content includes a link to the distribution CVE report.
539 Distribution Series
540 -------------------
541
542- >>> path = 'debian/woody'
543+ >>> path = 'debian/+series/woody'
544
545 If the user is not logged-in general stats are shown. There is also a
546 link to review nominations.
547@@ -147,7 +147,7 @@ reported bugs.
548 The content includes a link to the distribution CVE report.
549
550 >>> print(user_browser.getLink('CVE report').url)
551- http://bugs.launchpad.test/debian/woody/+cve
552+ http://bugs.launchpad.test/debian/+series/woody/+cve
553
554
555 Distribution Source Package
556@@ -224,7 +224,7 @@ source packages do not have a CVE reports page.
557 Source Package in Distribution Series
558 -------------------------------------
559
560- >>> path = 'debian/woody/+source/mozilla-firefox'
561+ >>> path = 'debian/+series/woody/+source/mozilla-firefox'
562
563 If the user is not logged-in general stats are shown. There is no
564 option to subscribe to bug mail.
565diff --git a/lib/lp/bugs/tests/test_searchtasks_webservice.py b/lib/lp/bugs/tests/test_searchtasks_webservice.py
566index 1a0ee93..63c9729 100644
567--- a/lib/lp/bugs/tests/test_searchtasks_webservice.py
568+++ b/lib/lp/bugs/tests/test_searchtasks_webservice.py
569@@ -33,12 +33,14 @@ class TestOmitTargetedParameter(TestCaseWithFactory):
570 'launchpad-library', 'salgado-change-anything')
571
572 def test_omit_targeted_old_default_true(self):
573- response = self.webservice.named_get('/mebuntu/inkanyamba',
574+ response = self.webservice.named_get(
575+ '/mebuntu/+series/inkanyamba',
576 'searchTasks', api_version='1.0').jsonBody()
577 self.assertEqual(response['total_size'], 0)
578
579 def test_omit_targeted_new_default_false(self):
580- response = self.webservice.named_get('/mebuntu/inkanyamba',
581+ response = self.webservice.named_get(
582+ '/mebuntu/+series/inkanyamba',
583 'searchTasks', api_version='devel').jsonBody()
584 self.assertEqual(response['total_size'], 1)
585
586diff --git a/lib/lp/code/stories/webservice/xx-code-import.txt b/lib/lp/code/stories/webservice/xx-code-import.txt
587index c8dd559..8534be5 100644
588--- a/lib/lp/code/stories/webservice/xx-code-import.txt
589+++ b/lib/lp/code/stories/webservice/xx-code-import.txt
590@@ -223,9 +223,8 @@ We can create a Git-to-Git import.
591
592 We can also create an import targetting a source package.
593
594- >>> source_package_url = (
595- ... '/' + distribution.name + '/' + distroseries.name + '/+source/'
596- ... + source_package.name)
597+ >>> source_package_url = '/%s/+series/%s/+source/%s' % (
598+ ... distribution.name, distroseries.name, source_package.name)
599 >>> new_remote_url = factory.getUniqueURL()
600 >>> response = import_webservice.named_post(source_package_url,
601 ... 'newCodeImport', branch_name='new-import', rcs_type='Git',
602diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
603index a718640..1d1b9ea 100644
604--- a/lib/lp/registry/browser/configure.zcml
605+++ b/lib/lp/registry/browser/configure.zcml
606@@ -99,7 +99,7 @@
607 />
608 <browser:url
609 for="lp.registry.interfaces.distroseries.IDistroSeries"
610- path_expression="name"
611+ path_expression="string:+series/${name}"
612 attribute_to_parent="distribution"
613 />
614 <browser:defaultView
615diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
616index 8d566e0..7876f6f 100644
617--- a/lib/lp/registry/browser/distribution.py
618+++ b/lib/lp/registry/browser/distribution.py
619@@ -189,11 +189,6 @@ class DistributionNavigation(
620
621 @stepthrough('+series')
622 def traverse_series(self, name):
623- series, _ = self._resolveSeries(name)
624- return self.redirectSubTree(
625- canonical_url(series, request=self.request), status=303)
626-
627- def traverse(self, name):
628 series, is_alias = self._resolveSeries(name)
629 if is_alias:
630 return self.redirectSubTree(
631@@ -201,6 +196,13 @@ class DistributionNavigation(
632 else:
633 return series
634
635+ def traverse(self, name):
636+ series, _ = self._resolveSeries(name)
637+ if series is None:
638+ return None
639+ return self.redirectSubTree(
640+ canonical_url(series, request=self.request), status=303)
641+
642
643 class DistributionSetNavigation(Navigation):
644
645diff --git a/lib/lp/registry/browser/tests/distroseries-views.txt b/lib/lp/registry/browser/tests/distroseries-views.txt
646index bae1765..394798b 100644
647--- a/lib/lp/registry/browser/tests/distroseries-views.txt
648+++ b/lib/lp/registry/browser/tests/distroseries-views.txt
649@@ -22,7 +22,7 @@ label, page_title, and cancel_url
650 Administer The Hoary Hedgehog Release
651
652 >>> print view.cancel_url
653- http://launchpad.test/ubuntu/hoary
654+ http://launchpad.test/ubuntu/+series/hoary
655
656 We will use a function to print the details related with the
657 distroseries being tested.
658@@ -167,7 +167,7 @@ uses the display_name, title, and description fields.
659 Edit The Hoary Hedgehog Release details
660
661 >>> print view.cancel_url
662- http://launchpad.test/ubuntu/hoary
663+ http://launchpad.test/ubuntu/+series/hoary
664
665 >>> [field.__name__ for field in view.form_fields]
666 ['display_name', 'title', 'summary', 'description']
667diff --git a/lib/lp/registry/browser/tests/packaging-views.txt b/lib/lp/registry/browser/tests/packaging-views.txt
668index 8120834..5746a18 100644
669--- a/lib/lp/registry/browser/tests/packaging-views.txt
670+++ b/lib/lp/registry/browser/tests/packaging-views.txt
671@@ -276,9 +276,9 @@ instead.)
672 >>> for link in table.findAll('a'):
673 ... if '+remove-packaging' in link['href']:
674 ... print link['href']
675- http://launchpad.test/ubuntu/grumpy/+source/hot/+remove-packaging
676- http://launchpad.test/ubuntu/hoary/+source/thunderbird/+remove-packaging
677- http://launchpad.test/ubuntu/hoary/+source/hot/+remove-packaging
678+ http://launchpad.test/ubuntu/+series/grumpy/+source/hot/+remove-packaging
679+ http://launchpad.test/ubuntu/+series/hoary/+source/thunderbird/+remove-packaging
680+ http://launchpad.test/ubuntu/+series/hoary/+source/hot/+remove-packaging
681
682 >>> [hoary_package] = [
683 ... package for series in view.series_batch.batch
684diff --git a/lib/lp/registry/browser/tests/sourcepackage-views.txt b/lib/lp/registry/browser/tests/sourcepackage-views.txt
685index d157ee5..5e74aa2 100644
686--- a/lib/lp/registry/browser/tests/sourcepackage-views.txt
687+++ b/lib/lp/registry/browser/tests/sourcepackage-views.txt
688@@ -23,7 +23,7 @@ Edit packaging view
689 Link to an upstream project
690
691 >>> print view.view.cancel_url
692- http://launchpad.test/youbuntu/busy/+source/bonkers
693+ http://launchpad.test/youbuntu/+series/busy/+source/bonkers
694
695
696 The view allows the logged in user to change product series field. The
697@@ -83,7 +83,7 @@ product can be chosen from a list of options.
698
699 >>> ignored = view.view.render()
700 >>> print view.view.next_url
701- http://launchpad.test/youbuntu/busy/+source/bonkers
702+ http://launchpad.test/youbuntu/+series/busy/+source/bonkers
703
704 >>> for notification in view.request.response.notifications:
705 ... print notification.message
706@@ -145,7 +145,7 @@ but there is no notification message that the upstream link was updated.
707 >>> print view.view
708 <...SourcePackageChangeUpstreamStepTwo object...>
709 >>> print view.view.next_url
710- http://launchpad.test/youbuntu/busy/+source/bonkers
711+ http://launchpad.test/youbuntu/+series/busy/+source/bonkers
712 >>> view.view.errors
713 []
714
715@@ -252,7 +252,7 @@ to the +edit-packaging page where the user can search for a project.
716 >>> view.errors
717 []
718 >>> print view.next_url
719- http://launchpad.test/youbuntu/busy/+source/lernid/+edit-packaging
720+ http://launchpad.test/youbuntu/+series/busy/+source/lernid/+edit-packaging
721
722
723 Upstream connections view
724@@ -351,7 +351,7 @@ to the project series.
725 Unlink an upstream project
726
727 >>> print view.cancel_url
728- http://launchpad.test/youbuntu/wonky/+source/stinkypackage
729+ http://launchpad.test/youbuntu/+series/wonky/+source/stinkypackage
730
731 >>> user = package.packaging.owner
732 >>> ignored = login_person(user)
733diff --git a/lib/lp/registry/browser/tests/test_distribution.py b/lib/lp/registry/browser/tests/test_distribution.py
734index d47eb99..aa7bf21 100644
735--- a/lib/lp/registry/browser/tests/test_distribution.py
736+++ b/lib/lp/registry/browser/tests/test_distribution.py
737@@ -57,28 +57,28 @@ class TestDistributionNavigation(TestCaseWithFactory):
738 self.assertIsInstance(view, RedirectionView)
739 self.assertEqual(expected_url, removeSecurityProxy(view).target)
740
741- def test_classic_series_url(self):
742+ def test_classic_series_url_redirects(self):
743 distroseries = self.factory.makeDistroSeries()
744- obj, _, _ = test_traverse(
745+ self.assertRedirects(
746 "http://launchpad.test/%s/%s" % (
747+ distroseries.distribution.name, distroseries.name),
748+ "http://launchpad.test/%s/+series/%s" % (
749 distroseries.distribution.name, distroseries.name))
750- self.assertEqual(distroseries, obj)
751
752- def test_classic_series_url_with_alias(self):
753+ def test_classic_series_url_with_alias_redirects(self):
754 distroseries = self.factory.makeDistroSeries()
755 distroseries.distribution.development_series_alias = "devel"
756 self.assertRedirects(
757 "http://launchpad.test/%s/devel" % distroseries.distribution.name,
758- "http://launchpad.test/%s/%s" % (
759+ "http://launchpad.test/%s/+series/%s" % (
760 distroseries.distribution.name, distroseries.name))
761
762- def test_new_series_url_redirects(self):
763+ def test_new_series_url(self):
764 distroseries = self.factory.makeDistroSeries()
765- self.assertRedirects(
766+ obj, _, _ = test_traverse(
767 "http://launchpad.test/%s/+series/%s" % (
768- distroseries.distribution.name, distroseries.name),
769- "http://launchpad.test/%s/%s" % (
770 distroseries.distribution.name, distroseries.name))
771+ self.assertEqual(distroseries, obj)
772
773 def test_new_series_url_with_alias_redirects(self):
774 distroseries = self.factory.makeDistroSeries()
775@@ -86,7 +86,7 @@ class TestDistributionNavigation(TestCaseWithFactory):
776 self.assertRedirects(
777 "http://launchpad.test/%s/+series/devel" % (
778 distroseries.distribution.name),
779- "http://launchpad.test/%s/%s" % (
780+ "http://launchpad.test/%s/+series/%s" % (
781 distroseries.distribution.name, distroseries.name))
782
783 def assertDereferences(self, url, expected_obj, environ=None):
784@@ -97,33 +97,33 @@ class TestDistributionNavigation(TestCaseWithFactory):
785 self.assertIsInstance(marshaller.dereference_url(url), RedirectionView)
786 self.assertEqual(expected_obj, marshaller.marshall_from_json_data(url))
787
788- def test_new_series_url_supports_object_lookup(self):
789- # New-style +series URLs are compatible with webservice object
790- # lookup.
791+ def test_classic_series_url_supports_object_lookup(self):
792+ # Classic series URLs (without +series) are compatible with
793+ # webservice object lookup, despite redirecting.
794 distroseries = self.factory.makeDistroSeries()
795- distroseries_url = "/%s/+series/%s" % (
796+ distroseries_url = "/%s/%s" % (
797 distroseries.distribution.name, distroseries.name)
798 self.assertDereferences(distroseries_url, distroseries)
799
800 # Objects subordinate to the redirected series work too.
801 distroarchseries = self.factory.makeDistroArchSeries(
802 distroseries=distroseries)
803- distroarchseries_url = "/%s/+series/%s/%s" % (
804+ distroarchseries_url = "/%s/%s/%s" % (
805 distroarchseries.distroseries.distribution.name,
806 distroarchseries.distroseries.name,
807 distroarchseries.architecturetag)
808 self.assertDereferences(distroarchseries_url, distroarchseries)
809
810- def test_new_series_url_supports_object_lookup_https(self):
811- # New-style +series URLs are compatible with webservice object
812- # lookup, even if the vhost is configured to use HTTPS.
813- # "SERVER_URL": None exposes a bug in lazr.restful < 0.22.2.
814+ def test_classic_series_url_supports_object_lookup_https(self):
815+ # Classic series URLs (without +series) are compatible with
816+ # webservice object lookup, even if the vhost is configured to use
817+ # HTTPS. "SERVER_URL": None exposes a bug in lazr.restful < 0.22.2.
818 self.addCleanup(allvhosts.reload)
819 self.pushConfig("vhosts", use_https=True)
820 allvhosts.reload()
821
822 distroseries = self.factory.makeDistroSeries()
823- distroseries_url = "/%s/+series/%s" % (
824+ distroseries_url = "/%s/%s" % (
825 distroseries.distribution.name, distroseries.name)
826 self.assertDereferences(
827 distroseries_url, distroseries,
828@@ -132,7 +132,7 @@ class TestDistributionNavigation(TestCaseWithFactory):
829 # Objects subordinate to the redirected series work too.
830 distroarchseries = self.factory.makeDistroArchSeries(
831 distroseries=distroseries)
832- distroarchseries_url = "/%s/+series/%s/%s" % (
833+ distroarchseries_url = "/%s/%s/%s" % (
834 distroarchseries.distroseries.distribution.name,
835 distroarchseries.distroseries.name,
836 distroarchseries.architecturetag)
837diff --git a/lib/lp/registry/browser/tests/test_packaging.py b/lib/lp/registry/browser/tests/test_packaging.py
838index 20f68dd..d68abb2 100644
839--- a/lib/lp/registry/browser/tests/test_packaging.py
840+++ b/lib/lp/registry/browser/tests/test_packaging.py
841@@ -95,8 +95,9 @@ class TestProductSeriesUbuntuPackagingView(WithScenarios, TestCaseWithFactory):
842 view = create_initialized_view(
843 other_productseries, '+ubuntupkg', form=form)
844 view_errors = [
845- 'The <a href="http://launchpad.test/ubuntu/hoary/+source/hot">'
846- 'hot</a> package in Hoary is already linked to another series.']
847+ 'The '
848+ '<a href="http://launchpad.test/ubuntu/+series/hoary/+source/hot">'
849+ 'hot</a> package in Hoary is already linked to another series.']
850 self.assertEqual(view_errors, view.errors)
851
852 def test_sourcepackagename_required(self):
853@@ -190,7 +191,7 @@ class TestBrowserDeletePackaging(TestCaseWithFactory):
854 user_browser = self.user_browser
855 user_browser.open('http://launchpad.test/ubuntu/+source/alsa-utils')
856 link = user_browser.getLink(
857- url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
858+ url='/ubuntu/+series/warty/+source/alsa-utils/+remove-packaging')
859 link.click()
860 user_browser.getControl('Unlink').click()
861 # Check that the change was committed.
862diff --git a/lib/lp/registry/browser/tests/test_sourcepackage_views.py b/lib/lp/registry/browser/tests/test_sourcepackage_views.py
863index c3cac87..ca41b2c 100644
864--- a/lib/lp/registry/browser/tests/test_sourcepackage_views.py
865+++ b/lib/lp/registry/browser/tests/test_sourcepackage_views.py
866@@ -78,7 +78,7 @@ class TestSourcePackageViewHelpers(TestCaseWithFactory):
867 params = parse_qsl(query)
868 expected_params = [
869 ('_return_url',
870- 'http://launchpad.test/zoobuntu/walrus/'
871+ 'http://launchpad.test/zoobuntu/+series/walrus/'
872 '+source/python-super-package'),
873 ('field.__visited_steps__', 'projectaddstep1'),
874 ('field.actions.continue', 'Continue'),
875diff --git a/lib/lp/registry/stories/distribution/xx-distribution-overview.txt b/lib/lp/registry/stories/distribution/xx-distribution-overview.txt
876index cb14899..cf13505 100644
877--- a/lib/lp/registry/stories/distribution/xx-distribution-overview.txt
878+++ b/lib/lp/registry/stories/distribution/xx-distribution-overview.txt
879@@ -39,7 +39,8 @@ Some distributions have listings of major versions, for example Debian:
880 If we add another milestone the list is well formatted using English grammar
881 rules.
882
883- >>> admin_browser.open("http://launchpad.test/debian/woody/+addmilestone")
884+ >>> admin_browser.open(
885+ ... "http://launchpad.test/debian/+series/woody/+addmilestone")
886 >>> admin_browser.getControl("Name:").value = "testmilestone"
887 >>> admin_browser.getControl("Register Milestone").click()
888
889@@ -53,10 +54,10 @@ rules.
890 Each series and milestone are links that take you to that
891 series and milestone page.
892
893- >>> anon_browser.getLink(url='/debian/sarge').text
894+ >>> anon_browser.getLink(url='/debian/+series/sarge').text
895 '3.1 \xe2\x80\x9cSarge\xe2\x80\x9d series'
896
897- >>> anon_browser.getLink(url='/debian/woody').text
898+ >>> anon_browser.getLink(url='/debian/+series/woody').text
899 '3.0 \xe2\x80\x9cWoody\xe2\x80\x9d series'
900
901 >>> anon_browser.getLink(url='/debian/+milestone/3.1').text
902@@ -123,7 +124,7 @@ If there is a development series alias, it becomes a redirect.
903 >>> from lp.testing import celebrity_logged_in
904 >>> from zope.component import getUtility
905
906- >>> anon_browser.open("http://launchpad.test/ubuntu/devel")
907+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/devel")
908 Traceback (most recent call last):
909 ...
910 NotFound: Object: <Distribution ...>, name: u'devel'
911@@ -131,12 +132,12 @@ If there is a development series alias, it becomes a redirect.
912 >>> with celebrity_logged_in("admin"):
913 ... ubuntu = getUtility(IDistributionSet).getByName(u"ubuntu")
914 ... ubuntu.development_series_alias = "devel"
915- >>> anon_browser.open("http://launchpad.test/ubuntu/devel")
916+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/devel")
917 >>> print(anon_browser.url)
918- http://launchpad.test/ubuntu/hoary
919- >>> anon_browser.open("http://launchpad.test/ubuntu/devel/+builds")
920+ http://launchpad.test/ubuntu/+series/hoary
921+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/devel/+builds")
922 >>> print(anon_browser.url)
923- http://launchpad.test/ubuntu/hoary/+builds
924+ http://launchpad.test/ubuntu/+series/hoary/+builds
925
926
927 Registration information
928diff --git a/lib/lp/registry/stories/distroseries/distroseries-admin.txt b/lib/lp/registry/stories/distroseries/distroseries-admin.txt
929index 33303f5..7619ea2 100644
930--- a/lib/lp/registry/stories/distroseries/distroseries-admin.txt
931+++ b/lib/lp/registry/stories/distroseries/distroseries-admin.txt
932@@ -7,12 +7,12 @@ Administrators
933 Launchpad administrators can edit distroseries via two different
934 pages: 'Change details' and 'Administer'.
935
936- >>> admin_browser.open('http://launchpad.test/ubuntu/hoary')
937+ >>> admin_browser.open('http://launchpad.test/ubuntu/+series/hoary')
938 >>> print(admin_browser.title)
939 Hoary (5.04)...
940 >>> admin_browser.getLink('Change details').click()
941 >>> print(admin_browser.url)
942- http://launchpad.test/ubuntu/hoary/+edit
943+ http://launchpad.test/ubuntu/+series/hoary/+edit
944
945 >>> print(admin_browser.title)
946 Edit The Hoary Hedgehog Release...
947@@ -25,10 +25,10 @@ pages: 'Change details' and 'Administer'.
948
949 A separate administration page is available via the 'Administer' link.
950
951- >>> admin_browser.open('http://launchpad.test/ubuntu/hoary')
952+ >>> admin_browser.open('http://launchpad.test/ubuntu/+series/hoary')
953 >>> admin_browser.getLink('Administer').click()
954 >>> print(admin_browser.url)
955- http://launchpad.test/ubuntu/hoary/+admin
956+ http://launchpad.test/ubuntu/+series/hoary/+admin
957
958 >>> print(admin_browser.title)
959 Administer The Hoary Hedgehog Release...
960@@ -39,7 +39,7 @@ A separate administration page is available via the 'Administer' link.
961 ... 'Version', index=0).value = '5.05'
962 >>> admin_browser.getControl('Change').click()
963 >>> print(admin_browser.url)
964- http://launchpad.test/ubuntu/happy
965+ http://launchpad.test/ubuntu/+series/happy
966 >>> print(admin_browser.title)
967 Happy (5.05)...
968
969@@ -54,7 +54,7 @@ Registry experts do not have access to the 'Change details' link.
970 >>> logout()
971 >>> registry_browser = setupBrowser(
972 ... auth='Basic %s:test' % email)
973- >>> registry_browser.open('http://launchpad.test/ubuntu/happy')
974+ >>> registry_browser.open('http://launchpad.test/ubuntu/+series/happy')
975 >>> registry_browser.getLink('Change details').click()
976 Traceback (most recent call last):
977 ...
978@@ -62,17 +62,18 @@ Registry experts do not have access to the 'Change details' link.
979
980 And navigating directly to +edit is thwarted.
981
982- >>> registry_browser.open('http://launchpad.test/ubuntu/happy/+edit')
983+ >>> registry_browser.open(
984+ ... 'http://launchpad.test/ubuntu/+series/happy/+edit')
985 Traceback (most recent call last):
986 ...
987 Unauthorized...
988
989 Registry experts do have access to the administration page.
990
991- >>> registry_browser.open('http://launchpad.test/ubuntu/happy')
992+ >>> registry_browser.open('http://launchpad.test/ubuntu/+series/happy')
993 >>> registry_browser.getLink('Administer').click()
994 >>> print(registry_browser.url)
995- http://launchpad.test/ubuntu/happy/+admin
996+ http://launchpad.test/ubuntu/+series/happy/+admin
997
998 >>> print(registry_browser.title)
999 Administer The Hoary Hedgehog Release...
1000@@ -83,6 +84,6 @@ Registry experts do have access to the administration page.
1001 ... 'Version', index=0).value = '5.04'
1002 >>> registry_browser.getControl('Change').click()
1003 >>> print(registry_browser.url)
1004- http://launchpad.test/ubuntu/hoary
1005+ http://launchpad.test/ubuntu/+series/hoary
1006 >>> print(registry_browser.title)
1007 Happy (5.04)...
1008diff --git a/lib/lp/registry/stories/distroseries/xx-distroseries-index.txt b/lib/lp/registry/stories/distroseries/xx-distroseries-index.txt
1009index f5eaa6f..915a3a7 100644
1010--- a/lib/lp/registry/stories/distroseries/xx-distroseries-index.txt
1011+++ b/lib/lp/registry/stories/distroseries/xx-distroseries-index.txt
1012@@ -4,7 +4,7 @@ Distribution series main page
1013 In the main page for a distribution we have a link to help translating
1014 this distribution series.
1015
1016- >>> user_browser.open('http://launchpad.test/ubuntu/hoary')
1017+ >>> user_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1018 >>> user_browser.getLink('Help translate').click()
1019 >>> print(user_browser.title)
1020 Hoary (5.04) : Translations : Ubuntu
1021@@ -16,7 +16,7 @@ Registering information
1022 The distroseries pages presents the 'registering' information besides
1023 its main 'heading'.
1024
1025- >>> anon_browser.open('http://launchpad.test/ubuntu/warty')
1026+ >>> anon_browser.open('http://launchpad.test/ubuntu/+series/warty')
1027
1028 >>> print(extract_text(
1029 ... find_tag_by_id(anon_browser.contents, 'registration')))
1030@@ -54,7 +54,7 @@ on the series' details.
1031 On series that have no source or binary packages, the portlet will
1032 change its text slightly to annouce this:
1033
1034- >>> anon_browser.open('http://launchpad.test/debian/sarge')
1035+ >>> anon_browser.open('http://launchpad.test/debian/+series/sarge')
1036 >>> print(extract_text(
1037 ... find_portlet(anon_browser.contents, 'Series information')))
1038 Series information
1039@@ -93,7 +93,7 @@ the series derived from this series:
1040 ... derived_series=child, parent_series=sarge)
1041 ... for child in children]
1042
1043- >>> anon_browser.open('http://launchpad.test/debian/sarge')
1044+ >>> anon_browser.open('http://launchpad.test/debian/+series/sarge')
1045 >>> print(extract_text(
1046 ... find_portlet(anon_browser.contents, 'Series information')))
1047 Series information
1048@@ -114,10 +114,10 @@ Distribution series bug subscriptions
1049 To receive email notifications about bugs pertaining to a distribution
1050 series, we can create structural bug subscriptions.
1051
1052- >>> admin_browser.open('http://launchpad.test/ubuntu/warty')
1053+ >>> admin_browser.open('http://launchpad.test/ubuntu/+series/warty')
1054 >>> admin_browser.getLink('Subscribe to bug mail').click()
1055 >>> print(admin_browser.url)
1056- http://launchpad.test/ubuntu/warty/+subscribe
1057+ http://launchpad.test/ubuntu/+series/warty/+subscribe
1058
1059 >>> print(admin_browser.title)
1060 Subscribe : Warty (4.10) : Bugs : Ubuntu
1061@@ -131,7 +131,7 @@ upstream packaging.
1062
1063 >>> # Note that warty's sourcecount is stale in sample data
1064 >>> # which causes -2 need linking.
1065- >>> anon_browser.open('http://launchpad.test/ubuntu/warty')
1066+ >>> anon_browser.open('http://launchpad.test/ubuntu/+series/warty')
1067 >>> print(extract_text(
1068 ... find_tag_by_id(anon_browser.contents, 'series-packaging')))
1069 Upstream packaging
1070diff --git a/lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt b/lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt
1071index 2ac0e42..2fa3a08 100644
1072--- a/lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt
1073+++ b/lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt
1074@@ -4,7 +4,7 @@ Distro series packaging
1075 The distro series packaging page is accssible to any user from the distro
1076 series +index page.
1077
1078- >>> anon_browser.open('http://launchpad.test/ubuntu/hoary')
1079+ >>> anon_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1080 >>> anon_browser.getLink('All upstream links').click()
1081 >>> print(anon_browser.title)
1082 All upstream links : ...
1083@@ -35,7 +35,7 @@ is linked, but the link to this page is not enabled.
1084
1085 >>> anon_browser.getLink('Needs upstream links')
1086 <Link text='Needs upstream links'
1087- url='http://launchpad.test/ubuntu/hoary/+needs-packaging'>
1088+ url='http://launchpad.test/ubuntu/+series/hoary/+needs-packaging'>
1089
1090 >>> anon_browser.getLink('All upstream links')
1091 Traceback (most recent call last):
1092@@ -46,7 +46,8 @@ The packaging links are batched so that users can view the thousands of
1093 links packages. Users can also hack the URL to set their own batch size.
1094
1095 >>> anon_browser.open(
1096- ... 'http://launchpad.test/ubuntu/hoary/+packaging?start=0&batch=1')
1097+ ... 'http://launchpad.test/ubuntu/+series/hoary/+packaging'
1098+ ... '?start=0&batch=1')
1099 >>> print(extract_text(find_tag_by_id(
1100 ... anon_browser.contents, 'packagings')))
1101 Source Package Upstream Project Upstream Contributor Connections
1102@@ -75,7 +76,7 @@ packages with the greatest need are listed first.
1103 >>> removeSecurityProxy(dsp).bug_count = 1
1104 >>> logout()
1105
1106- >>> anon_browser.open('http://launchpad.test/ubuntu/hoary')
1107+ >>> anon_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1108 >>> anon_browser.getLink('Needs upstream links').click()
1109 >>> print(anon_browser.title)
1110 Needs upstream links : ...
1111@@ -94,7 +95,7 @@ pages.
1112
1113 >>> anon_browser.getLink('64 strings')
1114 <Link text='64 strings'
1115- url='http://translations.launchpad.test/ubuntu/hoary/+source/pmount'>
1116+ url='http://translations.launchpad.test/ubuntu/+series/hoary/+source/pmount'>
1117
1118 >>> anon_browser.getLink('1 bug')
1119 <Link text='1 bug'
1120@@ -114,7 +115,7 @@ linked, but the link to this page is not enabled.
1121
1122 >>> anon_browser.getLink('All upstream links')
1123 <Link text='All upstream links'
1124- url='http://launchpad.test/ubuntu/hoary/+packaging'>
1125+ url='http://launchpad.test/ubuntu/+series/hoary/+packaging'>
1126
1127 >>> anon_browser.getLink('Needs upstream links')
1128 Traceback (most recent call last):
1129diff --git a/lib/lp/registry/stories/milestone/object-milestones.txt b/lib/lp/registry/stories/milestone/object-milestones.txt
1130index 5eaf227..89a7f26 100644
1131--- a/lib/lp/registry/stories/milestone/object-milestones.txt
1132+++ b/lib/lp/registry/stories/milestone/object-milestones.txt
1133@@ -54,12 +54,14 @@ Distributions
1134 Distribution Series
1135 ...................
1136
1137- >>> anon_browser.open('http://launchpad.test/debian/woody/+milestones')
1138+ >>> anon_browser.open(
1139+ ... 'http://launchpad.test/debian/+series/woody/+milestones')
1140 >>> print(all_milestones(anon_browser))
1141 Debian 3.1 ...
1142 Debian 3.1-rc1 ...
1143
1144- >>> anon_browser.open('http://launchpad.test/debian/sarge/+milestones')
1145+ >>> anon_browser.open(
1146+ ... 'http://launchpad.test/debian/+series/sarge/+milestones')
1147 >>> print(all_milestones(anon_browser))
1148 None
1149
1150diff --git a/lib/lp/registry/stories/milestone/xx-create-milestone-on-distribution.txt b/lib/lp/registry/stories/milestone/xx-create-milestone-on-distribution.txt
1151index d1a8eb1..4a86af3 100644
1152--- a/lib/lp/registry/stories/milestone/xx-create-milestone-on-distribution.txt
1153+++ b/lib/lp/registry/stories/milestone/xx-create-milestone-on-distribution.txt
1154@@ -3,7 +3,8 @@ add a milestone to the Ubuntu/hoary distroseries, which is owned by the Ubuntu
1155 Team (ubuntu-team).
1156
1157 >>> name12_browser = setupBrowser(auth='Basic test@canonical.com:test')
1158- >>> name12_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
1159+ >>> name12_browser.open(
1160+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone')
1161 Traceback (most recent call last):
1162 ...
1163 Unauthorized: ...
1164@@ -17,7 +18,8 @@ of the distribution should be able to add milestones for it, of course!
1165
1166 Now let's go back and try the add milestone page again. It works:
1167
1168- >>> name12_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
1169+ >>> name12_browser.open(
1170+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone')
1171
1172 Now, if we post to that form, we should see a success, and the page should
1173 redirect to the Ubuntu Hoary page showing the milestone we added.
1174@@ -25,7 +27,7 @@ redirect to the Ubuntu Hoary page showing the milestone we added.
1175 >>> name12_browser.getControl('Name').value = 'sounder01'
1176 >>> name12_browser.getControl('Register Milestone').click()
1177 >>> name12_browser.url
1178- 'http://launchpad.test/ubuntu/hoary'
1179+ 'http://launchpad.test/ubuntu/+series/hoary'
1180 >>> print(extract_text(
1181 ... find_tag_by_id(name12_browser.contents, 'series-hoary')))
1182 Version ...
1183diff --git a/lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt b/lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt
1184index 2a229aa..2e57645 100644
1185--- a/lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt
1186+++ b/lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt
1187@@ -38,12 +38,13 @@ see the link to add a milestone nor access the page directly.
1188 ...
1189 Unauthorized: ...
1190
1191- >>> user_browser.open('http://launchpad.test/ubuntu/hoary')
1192+ >>> user_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1193 >>> user_browser.getLink('Create milestone').click()
1194 Traceback (most recent call last):
1195 ...
1196 LinkNotFoundError
1197- >>> user_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
1198+ >>> user_browser.open(
1199+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone')
1200 Traceback (most recent call last):
1201 ...
1202 Unauthorized: ...
1203@@ -57,10 +58,10 @@ create a new milestone.
1204 >>> test_browser.url
1205 'http://launchpad.test/alsa-utils/trunk/+addmilestone'
1206
1207- >>> test_browser.open('http://launchpad.test/ubuntu/hoary')
1208+ >>> test_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1209 >>> test_browser.getLink('Create milestone').click()
1210 >>> test_browser.url
1211- 'http://launchpad.test/ubuntu/hoary/+addmilestone'
1212+ 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone'
1213
1214
1215 == Milestone bug subscriptions ==
1216diff --git a/lib/lp/registry/stories/milestone/xx-milestone-description.txt b/lib/lp/registry/stories/milestone/xx-milestone-description.txt
1217index d131f2b..cc59256 100644
1218--- a/lib/lp/registry/stories/milestone/xx-milestone-description.txt
1219+++ b/lib/lp/registry/stories/milestone/xx-milestone-description.txt
1220@@ -18,7 +18,8 @@ Let's make the sample user the owner of Ubuntu and alsa-utils for this test.
1221
1222 We can set the summary while creating a milestone for a Distribution.
1223
1224- >>> test_browser.open('http://launchpad.test/ubuntu/hoary/+addmilestone')
1225+ >>> test_browser.open(
1226+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addmilestone')
1227 >>> test_browser.getControl('Name').value = 'milestone1'
1228 >>> test_browser.getControl('Summary').value = (
1229 ... "Summary of first Ubuntu milestone.")
1230diff --git a/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt b/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt
1231index dcccb46..eb48805 100644
1232--- a/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt
1233+++ b/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt
1234@@ -17,7 +17,7 @@ Then the user click the "Delete Link" button in the first tab. The
1235 deletion succeeds and the usual informational message is displayed.
1236
1237 >>> link = first_browser.getLink(
1238- ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
1239+ ... url='/ubuntu/+series/warty/+source/alsa-utils/+remove-packaging')
1240 >>> link.click()
1241 >>> first_browser.getControl('Unlink').click()
1242 >>> content = first_browser.contents
1243@@ -33,8 +33,9 @@ second tab, and clicks the "Delete Link" button again.
1244 The packaging object has been deleted already, so this action cannot
1245 succeed.
1246
1247- >>> second_browser.getLink(
1248- ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging').click()
1249+ >>> link = second_browser.getLink(
1250+ ... url='/ubuntu/+series/warty/+source/alsa-utils/+remove-packaging')
1251+ >>> link.click()
1252 >>> content = second_browser.contents
1253 >>> for tag in find_tags_by_class(content, 'informational'):
1254 ... print(extract_text(tag))
1255diff --git a/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt b/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt
1256index a1f0e86..4b9961a 100644
1257--- a/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt
1258+++ b/lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt
1259@@ -34,7 +34,7 @@ packaging links.
1260 >>> user_browser = setupBrowser(auth='Basic test@canonical.com:test')
1261 >>> user_browser.open('http://launchpad.test/ubuntu/+source/alsa-utils')
1262 >>> link = user_browser.getLink(
1263- ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
1264+ ... url='/ubuntu/+series/warty/+source/alsa-utils/+remove-packaging')
1265 >>> print(link)
1266 <Link text='Remove upstream link'...
1267
1268@@ -49,7 +49,7 @@ This button is not displayed to anonymous users.
1269 Clicking this button deletes the corresponding packaging association.
1270
1271 >>> link = user_browser.getLink(
1272- ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
1273+ ... url='/ubuntu/+series/warty/+source/alsa-utils/+remove-packaging')
1274 >>> link.click()
1275 >>> user_browser.getControl('Unlink').click()
1276 >>> content = user_browser.contents
1277diff --git a/lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt b/lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt
1278index 0457b89..e0abe77 100644
1279--- a/lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt
1280+++ b/lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt
1281@@ -14,7 +14,7 @@ No Privileges Person visit the distroseries upstream links page for Hoary
1282 and sees that pmount is not linked.
1283
1284 >>> user_browser.open(
1285- ... 'http://launchpad.test/ubuntu/hoary/+needs-packaging')
1286+ ... 'http://launchpad.test/ubuntu/+series/hoary/+needs-packaging')
1287 >>> print(extract_text(find_tag_by_id(user_browser.contents, 'packages')))
1288 Source Package Bugs Translations
1289 pmount No bugs 64 strings ...
1290@@ -75,7 +75,7 @@ will use the data from the source package to prefill the first
1291 step of the multistep form.
1292
1293 >>> user_browser.open(
1294- ... 'http://launchpad.test/youbuntu/busy/+source/bonkers')
1295+ ... 'http://launchpad.test/youbuntu/+series/busy/+source/bonkers')
1296 >>> user_browser.getControl(
1297 ... 'Register the upstream project').selected = True
1298 >>> user_browser.getControl("Link to Upstream Project").click()
1299@@ -95,12 +95,12 @@ then finds out that the project doesn't exist, they use the
1300 "Link to Upstream Project" button to register the project.
1301
1302 >>> user_browser.open(
1303- ... 'http://launchpad.test/youbuntu/busy/+source/bonkers/')
1304+ ... 'http://launchpad.test/youbuntu/+series/busy/+source/bonkers/')
1305 >>> user_browser.getControl(
1306 ... 'Choose another upstream project').selected = True
1307 >>> user_browser.getControl("Link to Upstream Project").click()
1308 >>> print(user_browser.url)
1309- http://launchpad.test/youbuntu/busy/+source/bonkers/+edit-packaging
1310+ http://launchpad.test/youbuntu/+series/busy/+source/bonkers/+edit-packaging
1311
1312 >>> user_browser.getLink("Register the upstream project").click()
1313 >>> print(user_browser.getControl(name='field.name').value)
1314@@ -121,7 +121,7 @@ to the source package page and an informational message will be displayed.
1315 >>> user_browser.getControl(
1316 ... "Complete registration and link to bonkers package").click()
1317 >>> print(user_browser.url)
1318- http://launchpad.test/youbuntu/busy/+source/bonkers
1319+ http://launchpad.test/youbuntu/+series/busy/+source/bonkers
1320 >>> for tag in find_tags_by_class(
1321 ... user_browser.contents, 'informational message'):
1322 ... print(extract_text(tag))
1323diff --git a/lib/lp/registry/stories/person/xx-deactivate-account.txt b/lib/lp/registry/stories/person/xx-deactivate-account.txt
1324index 9b41ec4..fceff3b 100644
1325--- a/lib/lp/registry/stories/person/xx-deactivate-account.txt
1326+++ b/lib/lp/registry/stories/person/xx-deactivate-account.txt
1327@@ -10,7 +10,8 @@ demonstrate this, we'll assign a bug to the user that we're going to
1328 deactivate.
1329
1330 >>> browser = setupBrowser(auth='Basic test@canonical.com:test')
1331- >>> edit_bug_url = ("http://bugs.launchpad.test/debian/sarge/+source/"
1332+ >>> edit_bug_url = (
1333+ ... "http://bugs.launchpad.test/debian/+series/sarge/+source/"
1334 ... "mozilla-firefox/+bug/3/+editstatus")
1335 >>> browser.open(edit_bug_url)
1336 >>> bugwatch_control = browser.getControl(
1337diff --git a/lib/lp/registry/stories/product/xx-product-package-pages.txt b/lib/lp/registry/stories/product/xx-product-package-pages.txt
1338index 54261e5..3d7c44e 100644
1339--- a/lib/lp/registry/stories/product/xx-product-package-pages.txt
1340+++ b/lib/lp/registry/stories/product/xx-product-package-pages.txt
1341@@ -13,7 +13,8 @@ each.
1342 Distribution series Source package Version Project series
1343 Warty (4.10) evolution Evolution trunk series ...
1344
1345- >>> anon_browser.getLink(url='/ubuntu/warty/+source/evolution').click()
1346+ >>> anon_browser.getLink(
1347+ ... url='/ubuntu/+series/warty/+source/evolution').click()
1348 >>> print(extract_text(
1349 ... find_tag_by_id(anon_browser.contents, 'maincontent').h1))
1350 evolution source package in Warty
1351@@ -33,7 +34,8 @@ Evolution.
1352 Ubuntu Warty (4.10) evolution Remove...
1353 Ubuntu Hoary (5.04) evolution 1.0 Remove...
1354
1355- >>> evo_owner.getLink(url='/ubuntu/hoary/+source/evolution') is not None
1356+ >>> evo_owner.getLink(
1357+ ... url='/ubuntu/+series/hoary/+source/evolution') is not None
1358 True
1359
1360 Any logged in users can still see the links to create a packaging link.
1361@@ -54,8 +56,9 @@ Deleting packaging links
1362
1363 Packaging links can be deleted if they were created in error.
1364
1365- >>> evo_owner.getLink(
1366- ... url='/ubuntu/warty/+source/evolution/+remove-packaging').click()
1367+ >>> link = evo_owner.getLink(
1368+ ... url='/ubuntu/+series/warty/+source/evolution/+remove-packaging')
1369+ >>> link.click()
1370 >>> print(evo_owner.title)
1371 Unlink an upstream project...
1372 >>> evo_owner.getControl('Unlink').click()
1373diff --git a/lib/lp/registry/stories/productseries/xx-productseries-delete.txt b/lib/lp/registry/stories/productseries/xx-productseries-delete.txt
1374index b1c867d..c210700 100644
1375--- a/lib/lp/registry/stories/productseries/xx-productseries-delete.txt
1376+++ b/lib/lp/registry/stories/productseries/xx-productseries-delete.txt
1377@@ -47,7 +47,9 @@ bogus.
1378
1379 >>> owner_browser.getLink('All packages').click()
1380 >>> link = owner_browser.getLink(
1381- ... url='/ubuntu/warty/+source/mozilla-firefox/+remove-packaging')
1382+ ... url=(
1383+ ... '/ubuntu/+series/warty/+source/mozilla-firefox/'
1384+ ... '+remove-packaging'))
1385 >>> link.click()
1386 >>> owner_browser.getControl('Unlink').click()
1387
1388diff --git a/lib/lp/registry/stories/productseries/xx-productseries-index.txt b/lib/lp/registry/stories/productseries/xx-productseries-index.txt
1389index 7a4470c..3cfc7fc 100644
1390--- a/lib/lp/registry/stories/productseries/xx-productseries-index.txt
1391+++ b/lib/lp/registry/stories/productseries/xx-productseries-index.txt
1392@@ -120,7 +120,7 @@ in each Ubuntu series.
1393 Ubuntu Warty mozilla-firefox
1394
1395 >>> anon_browser.getLink('Ubuntu Warty mozilla-firefox')
1396- <Link ... url='http://launchpad.test/ubuntu/warty/+source/mozilla-firefox'>
1397+ <Link ... url='http://launchpad.test/ubuntu/+series/warty/+source/mozilla-firefox'>
1398
1399 If there are no sourcepackages, any user can see there are none:
1400
1401diff --git a/lib/lp/registry/stories/webservice/xx-distribution.txt b/lib/lp/registry/stories/webservice/xx-distribution.txt
1402index a578671..f4358f5 100644
1403--- a/lib/lp/registry/stories/webservice/xx-distribution.txt
1404+++ b/lib/lp/registry/stories/webservice/xx-distribution.txt
1405@@ -28,7 +28,7 @@ And for every distribution we publish most of its attributes.
1406 bug_reporting_guidelines: None
1407 bug_supervisor_link: None
1408 cdimage_mirrors_collection_link: 'http://.../ubuntu/cdimage_mirrors'
1409- current_series_link: 'http://.../ubuntu/hoary'
1410+ current_series_link: 'http://.../ubuntu/+series/hoary'
1411 date_created: '2006-10-16T18:31:43.415195+00:00'
1412 derivatives_collection_link: 'http://.../ubuntu/derivatives'
1413 description: 'Ubuntu is a new approach...'
1414@@ -76,7 +76,7 @@ Distribution has some custom operations.
1415 ... ubuntu['self_link'], 'getSeries',
1416 ... name_or_version='hoary').jsonBody()
1417 >>> print(series['self_link'])
1418- http://.../ubuntu/hoary
1419+ http://.../ubuntu/+series/hoary
1420
1421 Requesting a series that does not exist is results in a not found error.
1422
1423@@ -94,7 +94,7 @@ distribution that are marked as in development.
1424 ... ubuntu['self_link'], 'getDevelopmentSeries').jsonBody()
1425 >>> for entry in sorted(dev_series['entries']):
1426 ... print(entry['self_link'])
1427- http://.../ubuntu/hoary
1428+ http://.../ubuntu/+series/hoary
1429
1430 "getMilestone" returns a milestone for the given name, or None if there
1431 is no milestone for the given name.
1432diff --git a/lib/lp/registry/stories/webservice/xx-distroseries.txt b/lib/lp/registry/stories/webservice/xx-distroseries.txt
1433index e6ad345..96343b9 100644
1434--- a/lib/lp/registry/stories/webservice/xx-distroseries.txt
1435+++ b/lib/lp/registry/stories/webservice/xx-distroseries.txt
1436@@ -14,10 +14,10 @@ Via all the available series:
1437 ... ubuntu['series_collection_link']).jsonBody()
1438 >>> for entry in all_series['entries']:
1439 ... print(entry['self_link'])
1440- http://.../ubuntu/breezy-autotest
1441- http://.../ubuntu/grumpy
1442- http://.../ubuntu/hoary
1443- http://.../ubuntu/warty
1444+ http://.../ubuntu/+series/breezy-autotest
1445+ http://.../ubuntu/+series/grumpy
1446+ http://.../ubuntu/+series/hoary
1447+ http://.../ubuntu/+series/warty
1448
1449 The series are available to the anonymous API user too:
1450
1451@@ -25,17 +25,17 @@ The series are available to the anonymous API user too:
1452 ... ubuntu['series_collection_link']).jsonBody()
1453 >>> for entry in all_series['entries']:
1454 ... print(entry['self_link'])
1455- http://.../ubuntu/breezy-autotest
1456- http://.../ubuntu/grumpy
1457- http://.../ubuntu/hoary
1458- http://.../ubuntu/warty
1459+ http://.../ubuntu/+series/breezy-autotest
1460+ http://.../ubuntu/+series/grumpy
1461+ http://.../ubuntu/+series/hoary
1462+ http://.../ubuntu/+series/warty
1463
1464 Via the current series:
1465
1466 >>> current_series = webservice.get(
1467 ... ubuntu['current_series_link']).jsonBody()
1468 >>> print(current_series['self_link'])
1469- http://.../ubuntu/hoary
1470+ http://.../ubuntu/+series/hoary
1471
1472 Via the collection of development series:
1473
1474@@ -43,7 +43,7 @@ Via the collection of development series:
1475 ... ubuntu['self_link'], 'getDevelopmentSeries').jsonBody()
1476 >>> for entry in sorted(dev_series['entries']):
1477 ... print(entry['self_link'])
1478- http://.../ubuntu/hoary
1479+ http://.../ubuntu/+series/hoary
1480
1481 And via a direct query of a named series:
1482
1483@@ -51,7 +51,7 @@ And via a direct query of a named series:
1484 ... ubuntu['self_link'], 'getSeries',
1485 ... name_or_version='hoary').jsonBody()
1486 >>> print(series['self_link'])
1487- http://.../ubuntu/hoary
1488+ http://.../ubuntu/+series/hoary
1489
1490 For distroseries we publish a subset of its attributes.
1491
1492@@ -59,10 +59,12 @@ For distroseries we publish a subset of its attributes.
1493 >>> pprint_entry(current_series)
1494 active: True
1495 active_milestones_collection_link:
1496- 'http://.../ubuntu/hoary/active_milestones'
1497+ 'http://.../ubuntu/+series/hoary/active_milestones'
1498 advertise_by_hash: False
1499- all_milestones_collection_link: 'http://.../ubuntu/hoary/all_milestones'
1500- architectures_collection_link: 'http://.../ubuntu/hoary/architectures'
1501+ all_milestones_collection_link:
1502+ 'http://.../ubuntu/+series/hoary/all_milestones'
1503+ architectures_collection_link:
1504+ 'http://.../ubuntu/+series/hoary/architectures'
1505 bug_reported_acknowledgement: None
1506 bug_reporting_guidelines: None
1507 changeslist: 'hoary-changes@ubuntu.com'
1508@@ -73,21 +75,21 @@ For distroseries we publish a subset of its attributes.
1509 displayname: 'Hoary'
1510 distribution_link: 'http://.../ubuntu'
1511 driver_link: None
1512- drivers_collection_link: 'http://.../ubuntu/hoary/drivers'
1513+ drivers_collection_link: 'http://.../ubuntu/+series/hoary/drivers'
1514 fullseriesname: 'Ubuntu Hoary'
1515 include_long_descriptions: True
1516 index_compressors: [u'gzip', u'bzip2']
1517 language_pack_full_export_requested: False
1518 main_archive_link: 'http://.../ubuntu/+archive/primary'
1519 name: 'hoary'
1520- nominatedarchindep_link: 'http://.../ubuntu/hoary/i386'
1521+ nominatedarchindep_link: 'http://.../ubuntu/+series/hoary/i386'
1522 official_bug_tags: []
1523 owner_link: 'http://.../~ubuntu-team'
1524- parent_series_link: 'http://.../ubuntu/warty'
1525+ parent_series_link: 'http://.../ubuntu/+series/warty'
1526 publish_by_hash: False
1527 registrant_link: 'http://.../~mark'
1528 resource_type_link: ...
1529- self_link: 'http://.../ubuntu/hoary'
1530+ self_link: 'http://.../ubuntu/+series/hoary'
1531 status: 'Active Development'
1532 suite_names:
1533 [u'Release', u'Security', u'Updates', u'Proposed', u'Backports']
1534@@ -95,7 +97,7 @@ For distroseries we publish a subset of its attributes.
1535 supported: False
1536 title: 'The Hoary Hedgehog Release'
1537 version: '5.04'
1538- web_link: 'http://launchpad.../ubuntu/hoary'
1539+ web_link: 'http://launchpad.../ubuntu/+series/hoary'
1540
1541
1542 Getting the previous series
1543@@ -105,25 +107,25 @@ In the beta version of the API the previous series is obtained via
1544 parent_series_link:
1545
1546 >>> current_series_beta = webservice.get(
1547- ... "/ubuntu/hoary", api_version="beta").jsonBody()
1548+ ... "/ubuntu/+series/hoary", api_version="beta").jsonBody()
1549 >>> current_series_beta["parent_series_link"]
1550- u'http://.../ubuntu/warty'
1551+ u'http://.../ubuntu/+series/warty'
1552
1553 In the 1.0 version of the API the previous series is obtained via
1554 parent_series_link:
1555
1556 >>> current_series_1_0 = webservice.get(
1557- ... "/ubuntu/hoary", api_version="1.0").jsonBody()
1558+ ... "/ubuntu/+series/hoary", api_version="1.0").jsonBody()
1559 >>> current_series_1_0["parent_series_link"]
1560- u'http://.../ubuntu/warty'
1561+ u'http://.../ubuntu/+series/warty'
1562
1563 In the devel version of the API the previous series is obtained via
1564 parent_series_link:
1565
1566 >>> current_series_devel = webservice.get(
1567- ... "/ubuntu/hoary", api_version="devel").jsonBody()
1568+ ... "/ubuntu/+series/hoary", api_version="devel").jsonBody()
1569 >>> current_series_devel["previous_series_link"]
1570- u'http://.../ubuntu/warty'
1571+ u'http://.../ubuntu/+series/warty'
1572
1573
1574 Creating a milestone on the distroseries
1575diff --git a/lib/lp/registry/stories/webservice/xx-source-package.txt b/lib/lp/registry/stories/webservice/xx-source-package.txt
1576index 79b2402..435cf94 100644
1577--- a/lib/lp/registry/stories/webservice/xx-source-package.txt
1578+++ b/lib/lp/registry/stories/webservice/xx-source-package.txt
1579@@ -20,7 +20,7 @@ Getting source packages
1580 We can get source packages that are bound to a distribution series from the
1581 distribution series.
1582
1583- >>> my_series = webservice.get('/my-distro/my-series').jsonBody()
1584+ >>> my_series = webservice.get('/my-distro/+series/my-series').jsonBody()
1585 >>> evolution = webservice.named_get(
1586 ... my_series['self_link'], 'getSourcePackage',
1587 ... name='evolution').jsonBody()
1588@@ -31,13 +31,13 @@ distribution series.
1589 bug_reporting_guidelines: None
1590 displayname: 'evolution in My-distro My-series'
1591 distribution_link: 'http://.../my-distro'
1592- distroseries_link: 'http://.../my-distro/my-series'
1593+ distroseries_link: 'http://.../my-distro/+series/my-series'
1594 latest_published_component_name: None
1595 name: 'evolution'
1596 official_bug_tags: []
1597 productseries_link: None
1598 resource_type_link: ...
1599- self_link: 'http://api.../my-distro/my-series/+source/evolution'
1600+ self_link: 'http://api.../my-distro/+series/my-series/+source/evolution'
1601 web_link: 'http://.../+source/evolution'
1602
1603
1604diff --git a/lib/lp/services/feeds/stories/xx-links.txt b/lib/lp/services/feeds/stories/xx-links.txt
1605index 6f0b0ba..c8243d3 100644
1606--- a/lib/lp/services/feeds/stories/xx-links.txt
1607+++ b/lib/lp/services/feeds/stories/xx-links.txt
1608@@ -257,11 +257,11 @@ Only bug feeds should be linked to on bugs.launchpad.test.
1609 On the distroseries page on bugs.launchpad.test, we should
1610 show a link to the atom feed for that distroseries' latest bugs.
1611
1612- >>> browser.open('http://bugs.launchpad.test/ubuntu/hoary')
1613+ >>> browser.open('http://bugs.launchpad.test/ubuntu/+series/hoary')
1614 >>> soup = BeautifulSoup(browser.contents)
1615 >>> soup.head.findAll('link', type='application/atom+xml')
1616 [<link
1617- href="http://feeds.launchpad.test/ubuntu/hoary/latest-bugs.atom"
1618+ href="http://feeds.launchpad.test/ubuntu/+series/hoary/latest-bugs.atom"
1619 rel="alternate" title="Latest Bugs for Hoary"
1620 type="application/atom+xml"/>]
1621
1622diff --git a/lib/lp/services/sitesearch/tests/data/bingsearchservice-bugs-2.json b/lib/lp/services/sitesearch/tests/data/bingsearchservice-bugs-2.json
1623index 8cab9aa..a2b9246 100644
1624--- a/lib/lp/services/sitesearch/tests/data/bingsearchservice-bugs-2.json
1625+++ b/lib/lp/services/sitesearch/tests/data/bingsearchservice-bugs-2.json
1626@@ -15,7 +15,7 @@
1627 {
1628 "id": "https://api.cognitive.microsoft.com/api/v7/#WebPages.0",
1629 "name": "Bug #2 in Ubuntu Hoary: ā€œBlackhole Trash folderā€",
1630- "url": "http://bugs.launchpad.test/ubuntu/hoary/+bug/2",
1631+ "url": "http://bugs.launchpad.test/ubuntu/+series/hoary/+bug/2",
1632 "urlPingSuffix": "DevEx,5103.1",
1633 "isFamilyFriendly": true,
1634 "displayUrl": "https://help.launchpad.net/Bugs",
1635diff --git a/lib/lp/services/sitesearch/tests/test_bing.py b/lib/lp/services/sitesearch/tests/test_bing.py
1636index 61d623e..094bb87 100644
1637--- a/lib/lp/services/sitesearch/tests/test_bing.py
1638+++ b/lib/lp/services/sitesearch/tests/test_bing.py
1639@@ -267,7 +267,7 @@ class TestBingSearchService(TestCase):
1640 self.assertEqual(25, matches.total)
1641 self.assertEqual(5, len(matches))
1642 self.assertEqual([
1643- 'http://bugs.launchpad.test/ubuntu/hoary/+bug/2',
1644+ 'http://bugs.launchpad.test/ubuntu/+series/hoary/+bug/2',
1645 'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/2',
1646 'http://bugs.launchpad.test/debian/+source/mozilla-firefox/+bug/3',
1647 'http://bugs.launchpad.test/bugs/bugtrackers',
1648diff --git a/lib/lp/services/webapp/doc/canonical_url_examples.txt b/lib/lp/services/webapp/doc/canonical_url_examples.txt
1649index 6db9c6a..fcbe49d 100644
1650--- a/lib/lp/services/webapp/doc/canonical_url_examples.txt
1651+++ b/lib/lp/services/webapp/doc/canonical_url_examples.txt
1652@@ -119,12 +119,12 @@ An IDistroSeries.
1653
1654 >>> hoary = celebs.ubuntu.getSeries('hoary')
1655 >>> canonical_url(hoary)
1656- u'http://launchpad.test/ubuntu/hoary'
1657+ u'http://launchpad.test/ubuntu/+series/hoary'
1658
1659 An ISourcePackage.
1660
1661 >>> canonical_url(hoary.getSourcePackage('evolution'))
1662- u'http://launchpad.test/ubuntu/hoary/+source/evolution'
1663+ u'http://launchpad.test/ubuntu/+series/hoary/+source/evolution'
1664
1665 An IDistributionSourcePackage.
1666
1667@@ -231,7 +231,7 @@ An IBugTask on a distribution series source package.
1668
1669 >>> distro_series_task = getUtility(IBugTaskSet).get(19)
1670 >>> canonical_url(distro_series_task)
1671- u'http://bugs.launchpad.test/debian/sarge/+source/mozilla-firefox/+bug/3'
1672+ u'http://bugs.launchpad.test/debian/+series/sarge/+source/mozilla-firefox/+bug/3'
1673
1674 An IBugTask on a distribution series without a sourcepackage.
1675
1676@@ -239,7 +239,7 @@ An IBugTask on a distribution series without a sourcepackage.
1677 >>> distro_series_task.transitionToTarget(
1678 ... distro_series_task.target.distroseries, getUtility(ILaunchBag).user)
1679 >>> canonical_url(distro_series_task)
1680- u'http://bugs.launchpad.test/debian/sarge/+bug/3'
1681+ u'http://bugs.launchpad.test/debian/+series/sarge/+bug/3'
1682 >>> distro_series_task.transitionToTarget(
1683 ... temp_target, getUtility(ILaunchBag).user)
1684
1685@@ -263,7 +263,7 @@ private.)
1686 A private bugtask, as an anonymous user.
1687
1688 >>> canonical_url(distro_series_task)
1689- u'http://bugs.launchpad.test/debian/sarge/+source/mozilla-firefox/+bug/3'
1690+ u'http://bugs.launchpad.test/debian/+series/sarge/+source/mozilla-firefox/+bug/3'
1691
1692 >>> login("foo.bar@canonical.com")
1693 >>> distro_series_task.bug.setPrivate(False, getUtility(ILaunchBag).user)
1694diff --git a/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html b/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html
1695index 18266ae..a4ed005 100644
1696--- a/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html
1697+++ b/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html
1698@@ -79,7 +79,7 @@ GNU Affero General Public License version 3 (see the file LICENSE).
1699 (23456 bytes)
1700 </td>
1701 <td>
1702- <a class="sprite distribution" href="/ubuntu/hoary/i386">i386</a>
1703+ <a class="sprite distribution" href="/ubuntu/+series/hoary/i386">i386</a>
1704 </td>
1705 <td>
1706 <a href="/ubuntu" class="sprite distribution">Primary Archive for Ubuntu Linux</a>
1707@@ -94,7 +94,7 @@ GNU Affero General Public License version 3 (see the file LICENSE).
1708 in 1 minute (estimated)
1709 </td>
1710 <td>
1711- <a class="sprite distribution" href="/ubuntu/hoary/i386">i386</a>
1712+ <a class="sprite distribution" href="/ubuntu/+series/hoary/i386">i386</a>
1713 </td>
1714 <td>
1715 <a href="/ubuntu" class="sprite distribution">Primary Archive for Ubuntu Linux</a>
1716diff --git a/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js b/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js
1717index 2422400..a4391f7 100644
1718--- a/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js
1719+++ b/lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js
1720@@ -107,7 +107,7 @@ YUI.add('lp.snappy.snap.update_build_statuses.test', function (Y) {
1721 "self_link": "/~max/+snap/snap/+build/3",
1722 "id": 3,
1723 "distro_arch_series_link":
1724- "/ubuntu/hoary/amd64",
1725+ "/ubuntu/+series/hoary/amd64",
1726 "architecture_tag": "amd64",
1727 "archive_link":
1728 '<a href="/ubuntu" ' +
1729@@ -124,7 +124,7 @@ YUI.add('lp.snappy.snap.update_build_statuses.test', function (Y) {
1730 "self_link": "/~max/+snap/snap/+build/4",
1731 "id": 4,
1732 "distro_arch_series_link":
1733- "/ubuntu/hoary/i386",
1734+ "/ubuntu/+series/hoary/i386",
1735 "architecture_tag": "i386",
1736 "archive_link":
1737 '<a href="/ubuntu" ' +
1738@@ -182,7 +182,7 @@ YUI.add('lp.snappy.snap.update_build_statuses.test', function (Y) {
1739 "tag": "a",
1740 "attrs": {
1741 "class": "sprite distribution",
1742- "href": "/ubuntu/hoary/amd64"
1743+ "href": "/ubuntu/+series/hoary/amd64"
1744 },
1745 "text": "amd64"
1746 }]
1747@@ -236,7 +236,7 @@ YUI.add('lp.snappy.snap.update_build_statuses.test', function (Y) {
1748 "tag": "a",
1749 "attrs": {
1750 "class": "sprite distribution",
1751- "href": "/ubuntu/hoary/i386"
1752+ "href": "/ubuntu/+series/hoary/i386"
1753 },
1754 "text": "i386"
1755 }]
1756diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
1757index 8c87c8a..e791666 100644
1758--- a/lib/lp/snappy/tests/test_snap.py
1759+++ b/lib/lp/snappy/tests/test_snap.py
1760@@ -1148,7 +1148,7 @@ class TestSnap(TestCaseWithFactory):
1761 {
1762 "self_link": expected_snap_url + "/+build/%d" % build.id,
1763 "id": build.id,
1764- "distro_arch_series_link": "/%s/%s/%s" % (
1765+ "distro_arch_series_link": "/%s/+series/%s/%s" % (
1766 snap.distro_series.distribution.name,
1767 snap.distro_series.name,
1768 build.distro_arch_series.architecturetag),
1769diff --git a/lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt b/lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt
1770index a5b1c45..f4da8b5 100644
1771--- a/lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt
1772+++ b/lib/lp/soyuz/browser/tests/binarypackagerelease-views.txt
1773@@ -47,8 +47,10 @@ should be rendered for this dependency.
1774
1775 >>> for dep in pmount_deps:
1776 ... dep.name, dep.operator, dep.version, dep.url
1777- (u'at', u'>=', u'3.14156', u'http://launchpad.test/ubuntu/hoary/i386/at')
1778- (u'linux-2.6.12', None, u'', u'http://launchpad.test/ubuntu/hoary/i386/linux-2.6.12')
1779+ (u'at', u'>=', u'3.14156',
1780+ u'http://launchpad.test/ubuntu/+series/hoary/i386/at')
1781+ (u'linux-2.6.12', None, u'',
1782+ u'http://launchpad.test/ubuntu/+series/hoary/i386/linux-2.6.12')
1783 (u'tramp-package', None, u'', None)
1784
1785 Other relationship groups use the same mechanism.
1786diff --git a/lib/lp/soyuz/browser/tests/sourcepackage-views.txt b/lib/lp/soyuz/browser/tests/sourcepackage-views.txt
1787index d7a45d0..f15af9f 100644
1788--- a/lib/lp/soyuz/browser/tests/sourcepackage-views.txt
1789+++ b/lib/lp/soyuz/browser/tests/sourcepackage-views.txt
1790@@ -83,7 +83,8 @@ return a IPackageRelationshipSet object (see package-relationship.txt).
1791 (u'gcc-3.4-base', None, u'', None)
1792 (u'libc6', u'>=', u'2.3.2.ds1-4', None)
1793 (u'libstdc++6-dev', u'>=', u'3.4.1-4sarge1', None)
1794- (u'pmount', None, u'', u'http://launchpad.test/ubuntu/warty/+package/pmount')
1795+ (u'pmount', None, u'',
1796+ u'http://launchpad.test/ubuntu/+series/warty/+package/pmount')
1797
1798
1799 >>> firefox_parsed_dependsindep = firefox_view.builddependsindep
1800@@ -95,7 +96,8 @@ return a IPackageRelationshipSet object (see package-relationship.txt).
1801 ... dep.name, dep.operator, dep.version, dep.url
1802 (u'bacula-common', u'=', u'1.34.6-2', None)
1803 (u'bacula-director-common', u'=', u'1.34.6-2', None)
1804- (u'pmount', None, u'', u'http://launchpad.test/ubuntu/warty/+package/pmount')
1805+ (u'pmount', None, u'',
1806+ u'http://launchpad.test/ubuntu/+series/warty/+package/pmount')
1807 (u'postgresql-client', u'>=', u'7.4', None)
1808
1809 Ensure we have fixed bug 31039, by properly escape the
1810diff --git a/lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt b/lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt
1811index b157e26..229da87 100644
1812--- a/lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt
1813+++ b/lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt
1814@@ -45,7 +45,7 @@ here first:
1815
1816 >>> browser.open(
1817 ... 'http://localhost'
1818- ... '/ubuntu/breezy-autotest/+package/mozilla-firefox')
1819+ ... '/ubuntu/+series/breezy-autotest/+package/mozilla-firefox')
1820 >>> print(browser.title)
1821 mozilla-firefox : Breezy Badger Autotest (6.6.6) : Ubuntu
1822
1823@@ -248,7 +248,7 @@ As can be seen, the packaging is not linked yet. We can do that now using the
1824
1825 >>> user_browser.getLink("Set upstream link").click()
1826 >>> print(user_browser.url)
1827- http://launchpad.test/ubuntu/warty/+source/iceweasel/+edit-packaging
1828+ http://launchpad.test/ubuntu/+series/warty/+source/iceweasel/+edit-packaging
1829
1830 In step one the project is specified.
1831
1832diff --git a/lib/lp/soyuz/stories/distroseries/add-architecture.txt b/lib/lp/soyuz/stories/distroseries/add-architecture.txt
1833index 9d832f2..72887e5 100644
1834--- a/lib/lp/soyuz/stories/distroseries/add-architecture.txt
1835+++ b/lib/lp/soyuz/stories/distroseries/add-architecture.txt
1836@@ -5,7 +5,7 @@ Adding a new architecture to a distro series
1837 Launchpad admins are allowed to add a new arch (also called 'port') to a
1838 given distro series.
1839
1840- >>> admin_browser.open('http://launchpad.test/ubuntu/hoary')
1841+ >>> admin_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1842 >>> admin_browser.getLink('Add architecture').click()
1843 >>> print(admin_browser.title)
1844 Add a port of The Hoary...
1845@@ -13,7 +13,7 @@ given distro series.
1846 There is a cancel link.
1847
1848 >>> admin_browser.getLink('Cancel')
1849- <Link text='Cancel' url='http://launchpad.test/ubuntu/hoary'>
1850+ <Link text='Cancel' url='http://launchpad.test/ubuntu/+series/hoary'>
1851
1852 To register a new architecture one has to specify the architecture tag, the
1853 processor and whether or not that architecture is officially supported
1854@@ -28,7 +28,7 @@ and/or has PPA support.
1855
1856 Architecture tag is restricted to the usual Launchpad name format.
1857
1858- >>> admin_browser.open('http://launchpad.test/ubuntu/hoary')
1859+ >>> admin_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1860 >>> admin_browser.getLink('Add architecture').click()
1861 >>> admin_browser.getControl('Architecture Tag').value = 'foo bar'
1862 >>> admin_browser.getControl('Continue').click()
1863@@ -39,12 +39,13 @@ Architecture tag is restricted to the usual Launchpad name format.
1864 Other users won't see the link nor the page where a new port can be
1865 registered.
1866
1867- >>> user_browser.open('http://launchpad.test/ubuntu/hoary')
1868+ >>> user_browser.open('http://launchpad.test/ubuntu/+series/hoary')
1869 >>> user_browser.getLink('Add architecture')
1870 Traceback (most recent call last):
1871 ...
1872 LinkNotFoundError
1873- >>> user_browser.open('http://launchpad.test/ubuntu/hoary/+addport')
1874+ >>> user_browser.open(
1875+ ... 'http://launchpad.test/ubuntu/+series/hoary/+addport')
1876 Traceback (most recent call last):
1877 ...
1878 Unauthorized:...
1879diff --git a/lib/lp/soyuz/stories/packaging/package-pages-navigation.txt b/lib/lp/soyuz/stories/packaging/package-pages-navigation.txt
1880index cb254f7..720f3eb 100644
1881--- a/lib/lp/soyuz/stories/packaging/package-pages-navigation.txt
1882+++ b/lib/lp/soyuz/stories/packaging/package-pages-navigation.txt
1883@@ -85,7 +85,7 @@ A distribution series source package inherits its distribution source
1884 package's facets.
1885
1886 >>> anon_browser.open(
1887- ... 'http://launchpad.test/ubuntu/hoary/+source/alsa-utils')
1888+ ... 'http://launchpad.test/ubuntu/+series/hoary/+source/alsa-utils')
1889 >>> print_location(anon_browser.contents)
1890 Hierarchy: Ubuntu > alsa-utils package
1891 Tabs:
1892@@ -103,7 +103,7 @@ package's facets.
1893 Distribution series architectures pages inherit facets from the
1894 distribution.
1895
1896- >>> anon_browser.open('http://launchpad.test/ubuntu/hoary/i386')
1897+ >>> anon_browser.open('http://launchpad.test/ubuntu/+series/hoary/i386')
1898 >>> print_location(anon_browser.contents)
1899 Hierarchy: Ubuntu > Hoary (5.04) > i386
1900 Tabs:
1901@@ -121,7 +121,8 @@ distribution.
1902 The distribution series architecture binary packages page inherits Code,
1903 Bugs, Blueprints and Translations from the distribution.
1904
1905- >>> anon_browser.open('http://launchpad.test/ubuntu/hoary/i386/pmount')
1906+ >>> anon_browser.open(
1907+ ... 'http://launchpad.test/ubuntu/+series/hoary/i386/pmount')
1908 >>> print_location(anon_browser.contents)
1909 Hierarchy: Ubuntu > Hoary (5.04) > i386 > pmount
1910 Tabs:
1911@@ -140,7 +141,7 @@ Distribution series architecture binary package releases pages inherit
1912 facets from the distribution.
1913
1914 >>> anon_browser.open(
1915- ... 'http://launchpad.test/ubuntu/hoary/i386/pmount/0.1-1')
1916+ ... 'http://launchpad.test/ubuntu/+series/hoary/i386/pmount/0.1-1')
1917 >>> print_location(anon_browser.contents)
1918 Hierarchy: Ubuntu > Hoary (5.04) > i386 > pmount > 0.1-1
1919 Tabs:
1920diff --git a/lib/lp/soyuz/stories/soyuz/xx-binarypackagerelease-index.txt b/lib/lp/soyuz/stories/soyuz/xx-binarypackagerelease-index.txt
1921index 09d9edf..40f2505 100644
1922--- a/lib/lp/soyuz/stories/soyuz/xx-binarypackagerelease-index.txt
1923+++ b/lib/lp/soyuz/stories/soyuz/xx-binarypackagerelease-index.txt
1924@@ -61,7 +61,7 @@ Let's just check if the page is presented without errors (see bug
1925
1926 >>> browser.getLink('mozilla-firefox 0.9').click()
1927 >>> browser.url
1928- 'http://launchpad.test/ubuntu/warty/i386/mozilla-firefox/0.9'
1929+ 'http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox/0.9'
1930
1931 When rendering package relationships only existent packages contain
1932 links to within LP application, not found packages are rendered as
1933@@ -75,26 +75,27 @@ links to a binary in the context in question.
1934 ... parse_relationship_section(str(section))
1935
1936 >>> print_relation('provides')
1937- LINK: "mozilla-firefox" -> http://launchpad.test/ubuntu/warty/i386/mozilla-firefox
1938+ LINK: "mozilla-firefox" ->
1939+ http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox
1940
1941 >>> print_relation('predepends')
1942 TEXT: "foo"
1943- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/i386/pmount
1944+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/i386/pmount
1945
1946 >>> print_relation('enhances')
1947 TEXT: "bar"
1948- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/i386/pmount
1949+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/i386/pmount
1950
1951 >>> print_relation('breaks')
1952 TEXT: "baz"
1953- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/i386/pmount
1954+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/i386/pmount
1955
1956 The 'Built-Using' section contains a link to a source in the context in
1957 question.
1958
1959 >>> print_relation('builtusing')
1960 LINK: "iceweasel (= 1.0)" ->
1961- http://launchpad.test/ubuntu/warty/+source/iceweasel
1962+ http://launchpad.test/ubuntu/+series/warty/+source/iceweasel
1963
1964
1965 'Depends', 'Conflicts', 'Replaces', 'Suggests' and 'Recommends'
1966@@ -131,7 +132,8 @@ Even when there is no information to present and the package control
1967 files don't contain the field, we still present the corresponding
1968 relationship section.
1969
1970- >>> browser.open('http://launchpad.test/ubuntu/warty/i386/pmount/0.1-1')
1971+ >>> browser.open(
1972+ ... 'http://launchpad.test/ubuntu/+series/warty/i386/pmount/0.1-1')
1973 >>> print_relation('predepends')
1974 EMPTY SECTION
1975
1976diff --git a/lib/lp/soyuz/stories/soyuz/xx-build-record.txt b/lib/lp/soyuz/stories/soyuz/xx-build-record.txt
1977index 17e0614..54a77b6 100644
1978--- a/lib/lp/soyuz/stories/soyuz/xx-build-record.txt
1979+++ b/lib/lp/soyuz/stories/soyuz/xx-build-record.txt
1980@@ -115,10 +115,10 @@ to all the relevant entities involved in this build.
1981 http://launchpad.test/ubuntutest
1982
1983 >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
1984- http://launchpad.test/ubuntutest/breezy-autotest
1985+ http://launchpad.test/ubuntutest/+series/breezy-autotest
1986
1987 >>> print(anon_browser.getLink('i386').url)
1988- http://launchpad.test/ubuntutest/breezy-autotest/i386
1989+ http://launchpad.test/ubuntutest/+series/breezy-autotest/i386
1990
1991 Pending build records can be 'rescored', which will directly affect
1992 the time they will get started. A link to the corresponding help text
1993@@ -429,7 +429,7 @@ binary reference finally becomes a link to its corresponding page.
1994 testing-bin 1.0
1995
1996 >>> print(anon_browser.getLink('testing-bin 1.0').url)
1997- http://launchpad.test/ubuntutest/breezy-autotest/i386/testing-bin/1.0
1998+ http://launchpad.test/ubuntutest/+series/breezy-autotest/i386/testing-bin/1.0
1999
2000
2001 PPA builds
2002@@ -501,10 +501,10 @@ packages, since they do not exist.
2003 http://launchpad.test/~cprov/+archive/ubuntu/ppa
2004
2005 >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
2006- http://launchpad.test/ubuntutest/breezy-autotest
2007+ http://launchpad.test/ubuntutest/+series/breezy-autotest
2008
2009 >>> print(anon_browser.getLink('i386', index=1).url)
2010- http://launchpad.test/ubuntutest/breezy-autotest/i386
2011+ http://launchpad.test/ubuntutest/+series/breezy-autotest/i386
2012
2013 Similarly, binary packages are not linkified in 'Binary packages'
2014 section for PPA builds.
2015@@ -591,4 +591,4 @@ record always link to its binaries.
2016 imported-bin 666
2017
2018 >>> print(anon_browser.getLink('imported-bin 666').url)
2019- http://launchpad.test/ubuntutest/breezy-autotest/i386/imported-bin/666
2020+ http://launchpad.test/ubuntutest/+series/breezy-autotest/i386/imported-bin/666
2021diff --git a/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt b/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
2022index 4f062a9..fa773cf 100644
2023--- a/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
2024+++ b/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
2025@@ -49,7 +49,7 @@ For Distribution, it's possible to filter build results by state and name:
2026
2027 For DistroSeries, an architecture filter is also presented:
2028
2029- >>> anon_browser.open("http://launchpad.test/ubuntu/hoary")
2030+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/hoary")
2031 >>> anon_browser.getLink("Show builds").click()
2032 >>> print(anon_browser.title)
2033 Builds : Hoary (5.04) : Ubuntu
2034@@ -59,7 +59,7 @@ For DistroSeries, an architecture filter is also presented:
2035
2036 For DistroArchSeries, same as Distribution:
2037
2038- >>> anon_browser.open("http://launchpad.test/ubuntu/hoary/i386")
2039+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/hoary/i386")
2040 >>> anon_browser.getLink("Show builds").click()
2041 >>> print(anon_browser.title)
2042 Builds : i386 : Hoary (5.04) : Ubuntu
2043@@ -91,7 +91,7 @@ For Archive (PPA), same as Distribution:
2044 For SourcePackage, it's only possible to filter by state.
2045
2046 >>> anon_browser.open(
2047- ... "http://launchpad.test/ubuntu/hoary/+source/pmount")
2048+ ... "http://launchpad.test/ubuntu/+series/hoary/+source/pmount")
2049 >>> anon_browser.getLink("Show builds").click()
2050 >>> print(anon_browser.title)
2051 Builds : Hoary (5.04) : pmount package : Ubuntu
2052@@ -430,7 +430,7 @@ repeat the same set of accesses done for Distribution Builds page.
2053
2054 >>> anon_browser.open(
2055 ... "http://launchpad.test/"
2056- ... "ubuntu/hoary/+source/mozilla-firefox/+builds")
2057+ ... "ubuntu/+series/hoary/+source/mozilla-firefox/+builds")
2058
2059 When anonymous user first load only 'No packages currently building'
2060 message is presented.
2061@@ -480,7 +480,7 @@ page as any other packages from the primary archive.
2062 And also on the distro series builds page:
2063
2064 >>> anon_browser.open(
2065- ... "http://launchpad.test/ubuntu/breezy-autotest/+builds"
2066+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/+builds"
2067 ... "?build_text=commercialpackage&build_state=built")
2068
2069 >>> print(extract_text(
2070diff --git a/lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt b/lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt
2071index c944439..b681204 100644
2072--- a/lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt
2073+++ b/lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt
2074@@ -116,7 +116,7 @@ uploaded to.
2075 http://launchpad.test/~name16
2076
2077 >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
2078- http://launchpad.test/ubuntutest/breezy-autotest
2079+ http://launchpad.test/ubuntutest/+series/breezy-autotest
2080
2081 >>> print(anon_browser.getLink('Maintainer').url)
2082 http://launchpad.test/~maintainer
2083@@ -242,7 +242,7 @@ produced by this source. Each binary links to its specific
2084 No description available for foo-bin in ubuntutest breezy-autotest.
2085
2086 >>> print(anon_browser.getLink('foo-bin').url)
2087- http://launchpad.test/ubuntutest/breezy-autotest/+package/foo-bin
2088+ http://launchpad.test/ubuntutest/+series/breezy-autotest/+package/foo-bin
2089
2090 The binary package summary and description are retrieved from the
2091 package caches (see doc/package-cache.txt).
2092diff --git a/lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt b/lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt
2093index 39bd697..baba563 100644
2094--- a/lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt
2095+++ b/lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt
2096@@ -19,7 +19,7 @@ system, so it's impossible to get there except by typing the entire
2097 URL:
2098
2099 >>> browser.open(
2100- ... 'http://launchpad.test/ubuntu/warty/i386/mozilla-firefox')
2101+ ... 'http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox')
2102
2103 This page provides the publishing history of this BinaryPackage within
2104 this architecture:
2105@@ -43,7 +43,7 @@ It also provides a link to the currently published version:
2106 As well as a link to the related distribution source package:
2107
2108 >>> browser.open(
2109- ... 'http://launchpad.test/ubuntu/warty/i386/mozilla-firefox')
2110+ ... 'http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox')
2111 >>> browser.getLink(id="source_package").click()
2112 >>> print(browser.title.decode('ascii', 'ignore'))
2113 iceweasel package : Ubuntu
2114@@ -52,7 +52,7 @@ If the binary distribution does not have a current release, then the
2115 link to the source package will not be present:
2116
2117 >>> browser.open(
2118- ... 'http://launchpad.test/debian/woody/i386/pmount')
2119+ ... 'http://launchpad.test/debian/+series/woody/i386/pmount')
2120 >>> print(browser.getLink(id="source_package"))
2121 Traceback (most recent call last):
2122 ...
2123@@ -71,11 +71,11 @@ It's also reachable in a more natural way, starting from distribution page:
2124
2125 Then we get to the DistroSeries page:
2126
2127- >>> browser.getLink(url='/ubuntu/warty').click()
2128+ >>> browser.getLink(url='/ubuntu/+series/warty').click()
2129
2130 Then the DistroArchSeries page:
2131
2132- >>> browser.getLink(url='/ubuntu/warty/i386').click()
2133+ >>> browser.getLink(url='/ubuntu/+series/warty/i386').click()
2134
2135 Now we are able to use the search box in this page:
2136
2137@@ -157,7 +157,7 @@ Binary Packages with no files to present results in a clear statement
2138 intead of a empty section.
2139
2140 >>> browser.open(
2141- ... 'http://launchpad.test/ubuntu/hoary/i386/pmount/0.1-1')
2142+ ... 'http://launchpad.test/ubuntu/+series/hoary/i386/pmount/0.1-1')
2143 >>> print(extract_text(find_tag_by_id(browser.contents, 'files')))
2144 i386 build of pmount 0.1-1 in ubuntu hoary RELEASE
2145 produced no files for this binary package.
2146@@ -175,7 +175,8 @@ Their page functionality is identical to regular packages, which is described
2147 in the previous section of this page.
2148
2149 >>> browser.open(
2150- ... 'http://launchpad.test/ubuntu/breezy-autotest/i386/commercialpackage')
2151+ ... 'http://launchpad.test/ubuntu/+series/breezy-autotest/i386/'
2152+ ... 'commercialpackage')
2153
2154 This page provides the publishing history of this BinaryPackage within
2155 this architecture:
2156@@ -251,7 +252,7 @@ If the publishing is a copy, the original location, distribution,
2157 distroseries and archive are shown.
2158
2159 >>> anon_browser.open(
2160- ... 'http://launchpad.test/ubuntu/warty/i386/pmount')
2161+ ... 'http://launchpad.test/ubuntu/+series/warty/i386/pmount')
2162
2163 >>> print(extract_text(
2164 ... find_tag_by_id(anon_browser.contents, 'publishing-summary')))
2165diff --git a/lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt b/lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt
2166index 8752daa..1017e78 100644
2167--- a/lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt
2168+++ b/lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt
2169@@ -44,7 +44,7 @@ considering partial string matches as well (fti).
2170 >>> anon_browser.getControl(name="text").value = "firefox"
2171 >>> anon_browser.getControl("Search Packages").click()
2172 >>> anon_browser.url
2173- 'http://launchpad.test/ubuntu/warty/i386/+index?text=firefox'
2174+ 'http://launchpad.test/ubuntu/+series/warty/i386/+index?text=firefox'
2175
2176 Searching for "firefox" finds several binary packages. Each search
2177 result is displayed as the binary package name followed by the binary
2178@@ -84,7 +84,7 @@ page. The builds page is described in 23-builds-page.txt.
2179 Only administrators can edit ('administer', in fact) the
2180 distroarchseries details.
2181
2182- >>> admin_browser.open("http://launchpad.test/ubuntu/warty/i386/")
2183+ >>> admin_browser.open("http://launchpad.test/ubuntu/+series/warty/i386/")
2184 >>> print(extract_text(
2185 ... find_tag_by_id(admin_browser.contents, 'global-actions')))
2186 Administer
2187@@ -93,7 +93,8 @@ distroarchseries details.
2188 Non privileged access to the admin page results in a 'Unauthorized'
2189 error.
2190
2191- >>> user_browser.open("http://launchpad.test/ubuntu/warty/i386/+admin")
2192+ >>> user_browser.open(
2193+ ... "http://launchpad.test/ubuntu/+series/warty/i386/+admin")
2194 Traceback (most recent call last):
2195 ...
2196 Unauthorized...
2197@@ -111,7 +112,7 @@ The page presents a cancellation link that returns the user back to the
2198 DistroArchSeries page if clicked:
2199
2200 >>> print(admin_browser.getLink("Cancel").url)
2201- http://launchpad.test/ubuntu/warty/i386
2202+ http://launchpad.test/ubuntu/+series/warty/i386
2203
2204 Removing the official support for this DistroArchSeries.
2205
2206@@ -122,7 +123,7 @@ DistroArchSeries page.
2207
2208 >>> admin_browser.getControl("Change").click()
2209 >>> print(admin_browser.url)
2210- http://launchpad.test/ubuntu/warty/i386
2211+ http://launchpad.test/ubuntu/+series/warty/i386
2212
2213 There's also a notification message announcing the success of the change:
2214
2215@@ -174,7 +175,7 @@ current distroseries architecture list.
2216 Users with administrative privileges on a DistroSeries can open new
2217 architectures in this DistroSeries.
2218
2219- >>> admin_browser.open("http://launchpad.test/ubuntu/hoary")
2220+ >>> admin_browser.open("http://launchpad.test/ubuntu/+series/hoary")
2221 >>> admin_browser.getLink("Add architecture").click()
2222 >>> print(admin_browser.title)
2223 Add a port of The Hoary Hedgehog...
2224@@ -215,7 +216,7 @@ I will address it one 1.1.12 is gone.
2225 An administrator can open new distinct architecture, for instance,
2226 'amd64'.
2227
2228- >>> admin_browser.open("http://launchpad.test/ubuntu/hoary")
2229+ >>> admin_browser.open("http://launchpad.test/ubuntu/+series/hoary")
2230 >>> admin_browser.getLink("Add architecture").click()
2231
2232 >>> admin_browser.getControl("Architecture Tag").value = 'amd64'
2233diff --git a/lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt b/lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt
2234index c7cf5eb..859c944 100644
2235--- a/lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt
2236+++ b/lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt
2237@@ -4,7 +4,8 @@ A binary package for a distro-series displays the package's name,
2238 summary and description:
2239
2240 >>> browser.open(
2241- ... 'http://launchpad.test/ubuntu/warty/+package/mozilla-firefox')
2242+ ... 'http://launchpad.test/ubuntu/+series/warty/+package/'
2243+ ... 'mozilla-firefox')
2244 >>> print(browser.title)
2245 mozilla-firefox : Warty (4.10) : Ubuntu
2246
2247@@ -25,11 +26,11 @@ And each publishing history item is a link to the relevant binary
2248 release:
2249
2250 >>> print(browser.getLink('mozilla-firefox 0.9 in hppa (Release)').url)
2251- http://launchpad.test/ubuntu/warty/hppa/mozilla-firefox/0.9
2252+ http://launchpad.test/ubuntu/+series/warty/hppa/mozilla-firefox/0.9
2253 >>> print(browser.getLink('mozilla-firefox 0.9 in i386 (Release)').url)
2254- http://launchpad.test/ubuntu/warty/i386/mozilla-firefox/0.9
2255+ http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox/0.9
2256 >>> print(browser.getLink('mozilla-firefox 1.0 in i386 (Release)').url)
2257- http://launchpad.test/ubuntu/warty/i386/mozilla-firefox/1.0
2258+ http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox/1.0
2259
2260 The page also displays a link to the distro series source package
2261 release:
2262@@ -42,7 +43,8 @@ Some DistroSeriesBinaryPackages are unpublished, in this case there is
2263 no link to any source package:
2264
2265 >>> browser.open(
2266- ... 'http://launchpad.test/ubuntu/hoary/+package/mozilla-firefox')
2267+ ... 'http://launchpad.test/ubuntu/+series/hoary/+package/'
2268+ ... 'mozilla-firefox')
2269 >>> print(browser.title)
2270 mozilla-firefox : Hoary (5.04) : Ubuntu
2271
2272diff --git a/lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt b/lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt
2273index 2e9f391..490ea2f 100644
2274--- a/lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt
2275+++ b/lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt
2276@@ -4,11 +4,11 @@ a given name.
2277 As it's main functionality this page allow users to search for binary
2278 packages within the distroseries in context:
2279
2280- >>> anon_browser.open("http://launchpad.test/ubuntu/warty")
2281+ >>> anon_browser.open("http://launchpad.test/ubuntu/+series/warty")
2282 >>> anon_browser.getControl(name="text").value = "a"
2283 >>> anon_browser.getControl("Find a Package").click()
2284 >>> anon_browser.url
2285- 'http://launchpad.test/ubuntu/warty/+search?text=a'
2286+ 'http://launchpad.test/ubuntu/+series/warty/+search?text=a'
2287 >>> print(extract_text(
2288 ... find_tag_by_id(anon_browser.contents, 'search-results'),
2289 ... formatter='html'))
2290@@ -34,7 +34,7 @@ Each entry contains:
2291 the upload was done.
2292
2293 >>> anon_browser.open(
2294- ... "http://launchpad.test/ubuntu/warty/+portlet-latestuploads")
2295+ ... "http://launchpad.test/ubuntu/+series/warty/+portlet-latestuploads")
2296 >>> latest_uploads = str(find_tag_by_id(anon_browser.contents,
2297 ... "latest-uploads"))
2298 >>> 'mozilla-firefox 0.9' in latest_uploads
2299@@ -54,7 +54,7 @@ Empty results are also presented properly (even if they are quite rare
2300 in production environment):
2301
2302 >>> anon_browser.open(
2303- ... "http://launchpad.test/ubuntutest/breezy-autotest/"
2304+ ... "http://launchpad.test/ubuntutest/+series/breezy-autotest/"
2305 ... "+portlet-latestuploads")
2306 >>> find_tag_by_id(anon_browser.contents, 'no-latest-uploads') is not None
2307 True
2308diff --git a/lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt b/lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt
2309index 0769ed2..7ed2cc6 100644
2310--- a/lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt
2311+++ b/lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt
2312@@ -61,7 +61,7 @@ SourcePackage page:
2313
2314 >>> browser.getLink("The Warty Warthog Release").click()
2315 >>> browser.url
2316- 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox'
2317+ 'http://launchpad.test/ubuntu/+series/warty/+source/mozilla-firefox'
2318
2319 Any user can see the package summary.
2320
2321@@ -107,7 +107,7 @@ Let's check the link to the binary package built on i386 architecture,
2322 a DistroArchSeriesBinaryPackage:
2323
2324 >>> print(browser.getLink("i386").url)
2325- http://launchpad.test/ubuntu/warty/i386/mozilla-firefox
2326+ http://launchpad.test/ubuntu/+series/warty/i386/mozilla-firefox
2327
2328 More information about this page can be found at
2329 17-distroarchseries-binpackages.txt.
2330@@ -115,7 +115,7 @@ More information about this page can be found at
2331 Move back to the SourcePackage page to continue the tests:
2332
2333 >>> browser.open(
2334- ... 'http://launchpad.test/ubuntu/breezy-autotest/+source/'
2335+ ... 'http://launchpad.test/ubuntu/+series/breezy-autotest/+source/'
2336 ... 'commercialpackage')
2337
2338 PackageRelationships, 'builddepends', 'builddependsindep', 'builddependsarch',
2339@@ -154,7 +154,7 @@ keeping the page format constant.
2340 Let's inspect a page with non-empty relationships.
2341
2342 >>> browser.open(
2343- ... 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox')
2344+ ... 'http://launchpad.test/ubuntu/+series/warty/+source/mozilla-firefox')
2345
2346 >>> depends_section = find_tag_by_id(browser.contents, 'depends')
2347 >>> parse_relationship_section(str(depends_section))
2348@@ -163,13 +163,13 @@ Let's inspect a page with non-empty relationships.
2349 TEXT: "gcc-3.4-base"
2350 TEXT: "libc6 (>= 2.3.2.ds1-4)"
2351 TEXT: "libstdc++6-dev (>= 3.4.1-4sarge1)"
2352- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/+package/pmount
2353+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/+package/pmount
2354
2355 >>> dependsindep_section = find_tag_by_id(browser.contents, 'dependsindep')
2356 >>> parse_relationship_section(str(dependsindep_section))
2357 TEXT: "bacula-common (= 1.34.6-2)"
2358 TEXT: "bacula-director-common (= 1.34.6-2)"
2359- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/+package/pmount
2360+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/+package/pmount
2361 TEXT: "postgresql-client (>= 7.4)"
2362
2363 >>> dependsarch_section = find_tag_by_id(browser.contents, 'dependsarch')
2364@@ -179,13 +179,13 @@ Let's inspect a page with non-empty relationships.
2365 >>> conflicts_section = find_tag_by_id(browser.contents, 'conflicts')
2366 >>> parse_relationship_section(str(conflicts_section))
2367 TEXT: "gcc-4.0"
2368- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/+package/pmount
2369+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/+package/pmount
2370
2371 >>> conflictsindep_section = find_tag_by_id(
2372 ... browser.contents, 'conflictsindep')
2373 >>> parse_relationship_section(str(conflictsindep_section))
2374 TEXT: "gcc-4.0-base"
2375- LINK: "pmount" -> http://launchpad.test/ubuntu/warty/+package/pmount
2376+ LINK: "pmount" -> http://launchpad.test/ubuntu/+series/warty/+package/pmount
2377
2378 >>> conflictsarch_section = find_tag_by_id(
2379 ... browser.contents, 'conflictsarch')
2380@@ -210,7 +210,7 @@ for each published version.
2381
2382 >>> browser.getLink("View changelog").click()
2383 >>> browser.url
2384- 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox/+changelog'
2385+ 'http://launchpad.test/ubuntu/+series/warty/+source/mozilla-firefox/+changelog'
2386
2387 >>> tag = find_tag_by_id(browser.contents, 'mozilla-firefox_0.9')
2388 >>> print(extract_text(tag))
2389@@ -220,7 +220,7 @@ for each published version.
2390 Back to the SourcePackage page:
2391
2392 >>> browser.open(
2393- ... 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox')
2394+ ... 'http://launchpad.test/ubuntu/+series/warty/+source/mozilla-firefox')
2395
2396 Any user can see the copyright for the most recent source package release.
2397
2398@@ -247,7 +247,8 @@ Any user can see the copyright for the most recent source package release.
2399 Copyright 2010 Ford Prefect.
2400
2401 >>> browser.open(
2402- ... 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox')
2403+ ... 'http://launchpad.test/ubuntu/+series/warty/+source/'
2404+ ... 'mozilla-firefox')
2405
2406
2407 We can visit a specific published release of "mozilla-firefox", this
2408@@ -261,7 +262,8 @@ The deprecated DistroSeriesSourcePackageRelease page redirects to the
2409 same place.
2410
2411 >>> browser.open(
2412- ... 'http://launchpad.test/ubuntu/warty/+source/mozilla-firefox/0.9')
2413+ ... 'http://launchpad.test/ubuntu/+series/warty/+source/'
2414+ ... 'mozilla-firefox/0.9')
2415 >>> browser.url
2416 'http://launchpad.test/ubuntu/+source/mozilla-firefox/0.9'
2417
2418@@ -332,7 +334,7 @@ Their page functionality is identical to regular packages, which is described
2419 in the previous section of this page.
2420
2421 >>> browser.open(
2422- ... "http://launchpad.test/ubuntu/breezy-autotest/"
2423+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/"
2424 ... "+source/commercialpackage")
2425
2426 This page provides its versions publications organised by pocket.
2427@@ -369,7 +371,7 @@ Let's check the link to the binary package built on i386 architecture,
2428 a DistroArchSeriesBinaryPackage:
2429
2430 >>> print(browser.getLink("i386").url)
2431- http://launchpad.test/ubuntu/breezy-autotest/i386/commercialpackage
2432+ http://launchpad.test/ubuntu/+series/breezy-autotest/i386/commercialpackage
2433
2434 More information about this page can be found at
2435 17-distroarchseries-binpackages.txt.
2436@@ -391,7 +393,7 @@ for each published version.
2437
2438 >>> browser.getLink("View changelog").click()
2439 >>> browser.url
2440- 'http://launchpad.test/ubuntu/breezy-autotest/+source/commercialpackage/+changelog'
2441+ 'http://launchpad.test/ubuntu/+series/breezy-autotest/+source/commercialpackage/+changelog'
2442
2443 >>> tag = find_tag_by_id(browser.contents, 'commercialpackage_1.0-1')
2444 >>> print(extract_text(tag))
2445@@ -401,7 +403,7 @@ for each published version.
2446 Back to the SourcePackage page:
2447
2448 >>> browser.open(
2449- ... "http://launchpad.test/ubuntu/breezy-autotest/+source/"
2450+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/+source/"
2451 ... "commercialpackage")
2452
2453 We can visit a specific published release of "commercialpackage", this
2454diff --git a/lib/lp/soyuz/stories/soyuz/xx-person-packages.txt b/lib/lp/soyuz/stories/soyuz/xx-person-packages.txt
2455index 95ff407..06b0c79 100644
2456--- a/lib/lp/soyuz/stories/soyuz/xx-person-packages.txt
2457+++ b/lib/lp/soyuz/stories/soyuz/xx-person-packages.txt
2458@@ -66,7 +66,7 @@ The second column links to the distribution series source package page. The
2459 user follows the "Ubuntu Hoary" link next to cnews:
2460
2461 >>> browser.open("http://launchpad.test/~name16/+related-packages")
2462- >>> link = browser.getLink(url="/ubuntu/hoary/+source/cnews")
2463+ >>> link = browser.getLink(url="/ubuntu/+series/hoary/+source/cnews")
2464 >>> print(link)
2465 <Link text='Ubuntu Hoary' ...>
2466 >>> link.click()
2467diff --git a/lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt b/lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt
2468index 93e2bb0..3105e97 100644
2469--- a/lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt
2470+++ b/lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt
2471@@ -21,7 +21,7 @@ into account any overrides applied since the package was uploaded.
2472 Series and versions are linkified.
2473
2474 >>> print(browser.getLink('Hoary').url)
2475- http://bugs.launchpad.test/ubuntu/hoary/+source/alsa-utils
2476+ http://bugs.launchpad.test/ubuntu/+series/hoary/+source/alsa-utils
2477 >>> print(browser.getLink('1.0.9a-4').url)
2478 http://launchpad.test/ubuntu/+source/alsa-utils/1.0.9a-4
2479
2480diff --git a/lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt b/lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt
2481index 6824527..275a2c6 100644
2482--- a/lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt
2483+++ b/lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt
2484@@ -37,7 +37,7 @@ component:
2485 ... print(extract_text(row))
2486
2487 >>> motu_browser.open(
2488- ... "http://launchpad.test/ubuntu/breezy-autotest/")
2489+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/")
2490 >>> motu_browser.getLink("All uploads").click()
2491 >>> print_queue(motu_browser.contents)
2492 Package Version Component Section Priority Sets Pocket When
2493diff --git a/lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt b/lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt
2494index 4e2ec21..d5c7f49 100644
2495--- a/lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt
2496+++ b/lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt
2497@@ -54,7 +54,7 @@ The link "View Uploads" is presented in Distrorelease page.
2498 Viewing the current queue, by default the NEW queue.
2499
2500 >>> anon_browser.open(
2501- ... "http://launchpad.test/ubuntu/breezy-autotest/")
2502+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/")
2503 >>> anon_browser.getLink("All uploads").click()
2504
2505 >>> anon_browser.getControl(
2506@@ -330,12 +330,12 @@ the form twice.
2507 >>> duplicate_submission_browser = setupBrowser(
2508 ... auth="Basic test@canonical.com:test")
2509 >>> duplicate_submission_browser.open(
2510- ... "http://localhost/ubuntu/breezy-autotest/+queue")
2511+ ... "http://localhost/ubuntu/+series/breezy-autotest/+queue")
2512
2513 Go back to the "new" queue and accept "bar":
2514
2515 >>> upload_manager_browser.open(
2516- ... "http://localhost/ubuntu/breezy-autotest/+queue")
2517+ ... "http://localhost/ubuntu/+series/breezy-autotest/+queue")
2518 >>> print_queue(upload_manager_browser.contents)
2519 Package Version Component Section Priority Sets Pocket When
2520 bar (source) 1.0-1 universe devel low Release ...
2521@@ -393,7 +393,7 @@ accepted queue since it skips that state and goes straight to being published.
2522 Let's accept mozilla-firefox so we can see it in the accepted queue:
2523
2524 >>> upload_manager_browser.open(
2525- ... "http://localhost/ubuntu/breezy-autotest/+queue")
2526+ ... "http://localhost/ubuntu/+series/breezy-autotest/+queue")
2527 >>> upload_manager_browser.getControl(name="QUEUE_ID").value = ['1']
2528 >>> upload_manager_browser.getControl(name="Accept").click()
2529 >>> print_feedback_messages(upload_manager_browser.contents)
2530diff --git a/lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt b/lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt
2531index c4ff5b4..9dc34f1 100644
2532--- a/lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt
2533+++ b/lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt
2534@@ -4,7 +4,8 @@ Source package changelog
2535 Browse the changelog of a sourcepackage..
2536
2537 >>> user_browser.open(
2538- ... "http://launchpad.test/ubuntu/hoary/+source/pmount/+changelog")
2539+ ... "http://launchpad.test/ubuntu/+series/hoary/+source/pmount/"
2540+ ... "+changelog")
2541 >>> print_location(user_browser.contents)
2542 Hierarchy: Ubuntu > ...pmount... package > Hoary (5.04) > Change log
2543 Tabs:
2544@@ -28,7 +29,8 @@ Browse the changelog of a sourcepackage..
2545 .. and another one:
2546
2547 >>> user_browser.open(
2548- ... "http://launchpad.test/ubuntu/hoary/+source/alsa-utils/+changelog")
2549+ ... "http://launchpad.test/ubuntu/+series/hoary/+source/alsa-utils/"
2550+ ... "+changelog")
2551 >>> print(extract_text(
2552 ... find_tag_by_id(user_browser.contents, 'changelogs')))
2553 alsa-utils (1.0.9a-4ubuntu1) hoary; urgency=low
2554@@ -70,7 +72,8 @@ the changelog are obfuscated when the user is not logged in (this stops
2555 bots from picking them up):
2556
2557 >>> anon_browser.open(
2558- ... "http://launchpad.test/ubuntu/hoary/+source/alsa-utils/+changelog")
2559+ ... "http://launchpad.test/ubuntu/+series/hoary/+source/alsa-utils/"
2560+ ... "+changelog")
2561 >>> print(extract_text(find_main_content(anon_browser.contents)))
2562 Change logs for ...alsa-utils... in Hoary
2563 ...
2564@@ -84,8 +87,8 @@ address is linkified to point to the person's profile page. Here,
2565 'commercialpackage' has a known email address in its changelog:
2566
2567 >>> user_browser.open(
2568- ... "http://launchpad.test/ubuntu/breezy-autotest/+source/"
2569- ... "commercialpackage/+changelog")
2570+ ... "http://launchpad.test/ubuntu/+series/breezy-autotest/+source/"
2571+ ... "commercialpackage/+changelog")
2572 >>> changelog = find_tag_by_id(
2573 ... user_browser.contents, 'commercialpackage_1.0-1')
2574 >>> print(extract_text(changelog.find('a')))
2575diff --git a/lib/lp/soyuz/stories/webservice/xx-archive.txt b/lib/lp/soyuz/stories/webservice/xx-archive.txt
2576index 8e2ddc6..290cf20 100644
2577--- a/lib/lp/soyuz/stories/webservice/xx-archive.txt
2578+++ b/lib/lp/soyuz/stories/webservice/xx-archive.txt
2579@@ -478,7 +478,7 @@ component.
2580 We can use ``checkUpload`` to verify that a person can upload a
2581 sourcepackage.
2582
2583- >>> grumpy = user_webservice.get("/ubuntu/grumpy").jsonBody()
2584+ >>> grumpy = user_webservice.get("/ubuntu/+series/grumpy").jsonBody()
2585 >>> response = user_webservice.named_get(
2586 ... ubuntu['main_archive_link'], 'checkUpload',
2587 ... distroseries=grumpy['self_link'],
2588@@ -506,7 +506,7 @@ And we can see that it's gone:
2589
2590 And ``checkUpload`` now also no longer passes:
2591
2592- >>> grumpy = user_webservice.get("/ubuntu/grumpy").jsonBody()
2593+ >>> grumpy = user_webservice.get("/ubuntu/+series/grumpy").jsonBody()
2594 >>> response = user_webservice.named_get(
2595 ... ubuntu['main_archive_link'], 'checkUpload',
2596 ... distroseries=grumpy['self_link'],
2597@@ -664,7 +664,7 @@ newPocketUploader adds a new permission for a person to upload to a pocket.
2598
2599 The person named in the permission can upload a package to this pocket.
2600
2601- >>> grumpy = user_webservice.get("/ubuntu/grumpy").jsonBody()
2602+ >>> grumpy = user_webservice.get("/ubuntu/+series/grumpy").jsonBody()
2603 >>> response = user_webservice.named_get(
2604 ... ubuntu['main_archive_link'], 'checkUpload',
2605 ... distroseries=grumpy['self_link'],
2606@@ -745,7 +745,7 @@ It can also grant series-specific pocket queue admin permissions.
2607
2608 >>> ubuntu_owner_ws = ubuntu_owner_webservice.get(
2609 ... "/~ubuntu-owner").jsonBody()
2610- >>> hoary = user_webservice.get("/ubuntu/hoary").jsonBody()
2611+ >>> hoary = user_webservice.get("/ubuntu/+series/hoary").jsonBody()
2612 >>> new_permissions = []
2613 >>> for series in hoary, grumpy:
2614 ... response = ubuntu_owner_webservice.named_post(
2615diff --git a/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt b/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
2616index 4c35b61..013d583 100644
2617--- a/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
2618+++ b/lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt
2619@@ -79,7 +79,7 @@ Each binary publication exposes a number of properties:
2620 date_removed: None
2621 date_superseded: None
2622 display_name: 'mozilla-firefox 1.0 in warty hppa'
2623- distro_arch_series_link: 'http://.../ubuntu/warty/hppa'
2624+ distro_arch_series_link: 'http://.../ubuntu/+series/warty/hppa'
2625 phased_update_percentage: None
2626 pocket: 'Release'
2627 priority_name: 'IMPORTANT'
2628diff --git a/lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt b/lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt
2629index a2a320b..8d8ec24 100644
2630--- a/lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt
2631+++ b/lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt
2632@@ -12,7 +12,7 @@ operation:
2633 >>> current_series = webservice.get(
2634 ... ubuntu['current_series_link']).jsonBody()
2635 >>> print(current_series['self_link'])
2636- http://.../ubuntu/hoary
2637+ http://.../ubuntu/+series/hoary
2638
2639 We'll first set up a buildd chroot, so we can check that its URL is
2640 exposed.
2641@@ -37,7 +37,7 @@ For a distroarchseries we publish a subset of its attributes.
2642 architecture_tag: 'i386'
2643 chroot_url: 'http://.../.../filename...'
2644 display_name: 'Ubuntu Hoary i386'
2645- distroseries_link: 'http://.../ubuntu/hoary'
2646+ distroseries_link: 'http://.../ubuntu/+series/hoary'
2647 is_nominated_arch_indep: True
2648 main_archive_link: 'http://.../ubuntu/+archive/primary'
2649 official: True
2650@@ -45,21 +45,21 @@ For a distroarchseries we publish a subset of its attributes.
2651 package_count: 1
2652 processor_link: 'http://.../+processors/386'
2653 resource_type_link: 'http://.../#distro_arch_series'
2654- self_link: 'http://.../ubuntu/hoary/i386'
2655+ self_link: 'http://.../ubuntu/+series/hoary/i386'
2656 supports_virtualized: True
2657 title: 'The Hoary Hedgehog Release for i386 (386)'
2658- web_link: 'http://launchpad.../ubuntu/hoary/i386'
2659+ web_link: 'http://launchpad.../ubuntu/+series/hoary/i386'
2660
2661 DistroArchSeries.enabled is published in the API devel version.
2662
2663 >>> distroarchseries = webservice.get(
2664- ... "/ubuntu/hoary/i386", api_version='devel').jsonBody()
2665+ ... "/ubuntu/+series/hoary/i386", api_version='devel').jsonBody()
2666
2667 >>> pprint_entry(distroarchseries)
2668 architecture_tag: 'i386'
2669 chroot_url: 'http://.../.../filename...'
2670 display_name: 'Ubuntu Hoary i386'
2671- distroseries_link: 'http://.../ubuntu/hoary'
2672+ distroseries_link: 'http://.../ubuntu/+series/hoary'
2673 enabled: True
2674 is_nominated_arch_indep: True
2675 main_archive_link: 'http://.../ubuntu/+archive/primary'
2676@@ -68,7 +68,7 @@ DistroArchSeries.enabled is published in the API devel version.
2677 package_count: 1
2678 processor_link: 'http://.../+processors/386'
2679 resource_type_link: 'http://.../#distro_arch_series'
2680- self_link: 'http://.../ubuntu/hoary/i386'
2681+ self_link: 'http://.../ubuntu/+series/hoary/i386'
2682 supports_virtualized: True
2683 title: 'The Hoary Hedgehog Release for i386 (386)'
2684- web_link: 'http://launchpad.../ubuntu/hoary/i386'
2685+ web_link: 'http://launchpad.../ubuntu/+series/hoary/i386'
2686diff --git a/lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt b/lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt
2687index 3246cca..314011e 100644
2688--- a/lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt
2689+++ b/lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt
2690@@ -107,7 +107,7 @@ DistroSeries builds
2691
2692 DistroSeries, like ubuntu/hoary, allow users to call browse builds.
2693
2694- >>> hoary = webservice.get("/ubuntu/hoary").jsonBody()
2695+ >>> hoary = webservice.get("/ubuntu/+series/hoary").jsonBody()
2696 >>> hoary_builds = webservice.named_get(
2697 ... hoary['self_link'], 'getBuildRecords').jsonBody()
2698
2699diff --git a/lib/lp/soyuz/stories/webservice/xx-packageset.txt b/lib/lp/soyuz/stories/webservice/xx-packageset.txt
2700index 5622b17..e757ee3 100644
2701--- a/lib/lp/soyuz/stories/webservice/xx-packageset.txt
2702+++ b/lib/lp/soyuz/stories/webservice/xx-packageset.txt
2703@@ -256,7 +256,7 @@ Every package set is associated with a distro series.
2704 ... '/package-sets', 'getByName', {}, distroseries='/ubuntu/hoary',
2705 ... name=u'mozilla').jsonBody()
2706 >>> print(mozilla['distroseries_link'])
2707- http://api.launchpad.test/beta/ubuntu/hoary
2708+ http://api.launchpad.test/beta/ubuntu/+series/hoary
2709
2710 >>> print(mozilla['self_link'])
2711 http://api.launchpad.test/beta/package-sets/ubuntu/hoary/mozilla
2712@@ -280,9 +280,9 @@ Related package sets
2713 When adding a package set we can specify that is to be related to another set
2714 that exists already.
2715
2716- >>> grumpy = webservice.get("/ubuntu/grumpy").jsonBody()
2717+ >>> grumpy = webservice.get("/ubuntu/+series/grumpy").jsonBody()
2718 >>> print(grumpy['self_link'])
2719- http://api.launchpad.test/beta/ubuntu/grumpy
2720+ http://api.launchpad.test/beta/ubuntu/+series/grumpy
2721
2722 We are adding a new 'mozilla' package set to the 'grumpy' distro series and
2723 it is related to 'mozilla' in 'hoary'.
2724@@ -300,7 +300,7 @@ it is related to 'mozilla' in 'hoary'.
2725 ... '/package-sets', 'getByName', {}, name=u'mozilla',
2726 ... distroseries=grumpy['self_link']).jsonBody()
2727 >>> print(grumpy_mozilla['distroseries_link'])
2728- http://api.launchpad.test/beta/ubuntu/grumpy
2729+ http://api.launchpad.test/beta/ubuntu/+series/grumpy
2730
2731 >>> print(grumpy_mozilla['self_link'])
2732 http://api.launchpad.test/beta/package-sets/ubuntu/grumpy/mozilla
2733@@ -729,10 +729,10 @@ The following query (note the additional 'distroseries' parameter) is
2734 thus equivalent:
2735
2736 >>> print(ubuntu['current_series_link'])
2737- http://api.launchpad.test/beta/ubuntu/hoary
2738- >>> hoary = webservice.get("/ubuntu/hoary").jsonBody()
2739+ http://api.launchpad.test/beta/ubuntu/+series/hoary
2740+ >>> hoary = webservice.get("/ubuntu/+series/hoary").jsonBody()
2741 >>> print(hoary['self_link'])
2742- http://api.launchpad.test/beta/ubuntu/hoary
2743+ http://api.launchpad.test/beta/ubuntu/+series/hoary
2744
2745 >>> response = webservice.named_get(
2746 ... ubuntu['main_archive_link'], 'isSourceUploadAllowed',
2747diff --git a/lib/lp/soyuz/stories/webservice/xx-packageupload.txt b/lib/lp/soyuz/stories/webservice/xx-packageupload.txt
2748index e88c342..5e3dc61 100644
2749--- a/lib/lp/soyuz/stories/webservice/xx-packageupload.txt
2750+++ b/lib/lp/soyuz/stories/webservice/xx-packageupload.txt
2751@@ -25,11 +25,11 @@ Each record exposes a number of properties.
2752 display_arches: 'source'
2753 display_name: 'mozilla-firefox'
2754 display_version: '0.9'
2755- distroseries_link: 'http://.../ubuntu/warty'
2756+ distroseries_link: 'http://.../ubuntu/+series/warty'
2757 id: 11
2758 pocket: 'Release'
2759 resource_type_link: 'http://.../#package_upload'
2760- self_link: 'http://.../ubuntu/warty/+upload/11'
2761+ self_link: 'http://.../ubuntu/+series/warty/+upload/11'
2762 status: 'Done'
2763
2764 getPackageUploads can filter on package names.
2765diff --git a/lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt b/lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt
2766index 4a8e1eb..971614c 100644
2767--- a/lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt
2768+++ b/lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt
2769@@ -129,7 +129,7 @@ publication to play with first.
2770 date_removed: None
2771 date_superseded: None
2772 display_name: 'testwebservice 666 in breezy-autotest'
2773- distro_series_link: 'http://.../ubuntutest/breezy-autotest'
2774+ distro_series_link: 'http://.../ubuntutest/+series/breezy-autotest'
2775 package_creator_link: 'http://.../beta/~name16'
2776 package_maintainer_link: 'http://.../beta/~name16'
2777 package_signer_link: 'http://.../beta/~name16'
2778diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
2779index 71045f0..47a9021 100644
2780--- a/lib/lp/testing/pages.py
2781+++ b/lib/lp/testing/pages.py
2782@@ -509,7 +509,7 @@ def print_portlet_links(content, name, base=None):
2783 >>> print_portlet_links(admin_browser.contents,'Milestone milestone3 for
2784 Ubuntu details')
2785 Ubuntu: /ubuntu
2786- Warty: /ubuntu/warty
2787+ Warty: /ubuntu/+series/warty
2788 --------------
2789 """
2790
2791diff --git a/lib/lp/translations/browser/tests/pofile-views.txt b/lib/lp/translations/browser/tests/pofile-views.txt
2792index 2221e92..36c68b9 100644
2793--- a/lib/lp/translations/browser/tests/pofile-views.txt
2794+++ b/lib/lp/translations/browser/tests/pofile-views.txt
2795@@ -501,4 +501,4 @@ index page.
2796 And we are redirected to the index page, as expected:
2797
2798 >>> print(pofile_view.request.response.getHeader('Location'))
2799- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es
2800+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es
2801diff --git a/lib/lp/translations/browser/tests/test_breadcrumbs.py b/lib/lp/translations/browser/tests/test_breadcrumbs.py
2802index 0032e78..84ea513 100644
2803--- a/lib/lp/translations/browser/tests/test_breadcrumbs.py
2804+++ b/lib/lp/translations/browser/tests/test_breadcrumbs.py
2805@@ -61,7 +61,7 @@ class TestTranslationsFacetBreadcrumb(BaseBreadcrumbTestCase):
2806 ("Translations",
2807 'http://translations.launchpad.test/crumb-tester'),
2808 ("Test (1.0)",
2809- 'http://translations.launchpad.test/crumb-tester/test')],
2810+ 'http://translations.launchpad.test/crumb-tester/+series/test')],
2811 series, rootsite='translations')
2812
2813 def test_project(self):
2814@@ -124,10 +124,10 @@ class TestSeriesLanguageBreadcrumbs(BaseBreadcrumbTestCase):
2815 ("Translations",
2816 "http://translations.launchpad.test/crumb-tester"),
2817 ("Test (1.0)",
2818- "http://translations.launchpad.test/crumb-tester/test"),
2819+ "http://translations.launchpad.test/crumb-tester/+series/test"),
2820 ("Serbian (sr)",
2821 "http://translations.launchpad.test/"
2822- "crumb-tester/test/+lang/sr")],
2823+ "crumb-tester/+series/test/+lang/sr")],
2824 serieslanguage)
2825
2826 def test_productserieslanguage(self):
2827diff --git a/lib/lp/translations/doc/canonical_url_examples.txt b/lib/lp/translations/doc/canonical_url_examples.txt
2828index 3b1acb8..743ed4b 100644
2829--- a/lib/lp/translations/doc/canonical_url_examples.txt
2830+++ b/lib/lp/translations/doc/canonical_url_examples.txt
2831@@ -44,7 +44,7 @@ And here's our subset.
2832 ... distroseries=hoary, sourcepackagename=sourcepackagename)
2833
2834 >>> canonical_url(potemplatesubset)
2835- u'http://launchpad.test/ubuntu/hoary/+source/evolution/+pots'
2836+ u'http://launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots'
2837
2838 We can get a particular PO template for this source package by its PO template
2839 name.
2840@@ -140,7 +140,7 @@ Distribution and distribution series default to the main vhost.
2841 >>> distroseries = factory.makeDistroSeries(
2842 ... name='bah', distribution=distribution)
2843 >>> canonical_url(distroseries)
2844- u'http://launchpad.test/boo/bah'
2845+ u'http://launchpad.test/boo/+series/bah'
2846
2847 DistroSeriesLanguage objects have their URLs on translations vhost.
2848
2849@@ -152,7 +152,7 @@ DistroSeriesLanguage objects have their URLs on translations vhost.
2850 >>> boo_bah_serbian = getUtility(IDistroSeriesLanguageSet).getDummy(
2851 ... distroseries, serbian)
2852 >>> canonical_url(boo_bah_serbian)
2853- u'http://translations.launchpad.test/boo/bah/+lang/sr'
2854+ u'http://translations.launchpad.test/boo/+series/bah/+lang/sr'
2855
2856 Product, ProductSeries and ProductSeriesLanguage
2857 ---------------------------------------------------
2858diff --git a/lib/lp/translations/doc/poexport-request.txt b/lib/lp/translations/doc/poexport-request.txt
2859index cf6d13f..ef06aad 100644
2860--- a/lib/lp/translations/doc/poexport-request.txt
2861+++ b/lib/lp/translations/doc/poexport-request.txt
2862@@ -52,7 +52,7 @@ The user receives a confirmation email.
2863 >>> emails = pop_notifications()
2864 >>> len(emails)
2865 1
2866- >>> print_emails(notifications=emails)
2867+ >>> print_emails(notifications=emails, decode=True)
2868 From: ...
2869 To: downloader@example.com
2870 Subject: Launchpad translation download: Ubuntu Hoary pmount -
2871@@ -70,8 +70,9 @@ The user receives a confirmation email.
2872 <BLANKLINE>
2873 http://translations.launchpad.../hoary/+source/pmount/+pots/p...
2874 <BLANKLINE>
2875- -- =
2876+ --
2877 Automatic message from Launchpad.net.
2878+ <BLANKLINE>
2879 ----------------------------------------
2880
2881 The email contains a URL linking to where the exported file can be downloaded.
2882diff --git a/lib/lp/translations/stories/distribution/xx-distribution-translations.txt b/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
2883index d5e30aa..791505f 100644
2884--- a/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
2885+++ b/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
2886@@ -49,11 +49,11 @@ Now, we are going to check that the language list we got is pointing
2887 to the right translation focus.
2888
2889 >>> print(browser.getLink('Spanish').url)
2890- http://translations.launchpad.test/ubuntu/hoary/+lang/es
2891+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es
2892 >>> print(browser.getLink('Italian').url)
2893- http://translations.launchpad.test/ubuntu/hoary/+lang/it
2894+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/it
2895 >>> print(browser.getLink('Portuguese (Brazil)').url)
2896- http://translations.launchpad.test/ubuntu/hoary/+lang/pt_BR
2897+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/pt_BR
2898
2899 And the other Ubuntu distributions should be there too.
2900
2901@@ -70,11 +70,11 @@ We are not showing its translation status here, so we should have
2902 links to their particular translation status.
2903
2904 >>> print(browser.getLink('Breezy Badger Autotest (6.6.6)').url)
2905- http://translations.launchpad.test/ubuntu/breezy-autotest
2906+ http://translations.launchpad.test/ubuntu/+series/breezy-autotest
2907 >>> print(browser.getLink('Grumpy (5.10)').url)
2908- http://translations.launchpad.test/ubuntu/grumpy
2909+ http://translations.launchpad.test/ubuntu/+series/grumpy
2910 >>> print(browser.getLink('Warty (4.10)').url)
2911- http://translations.launchpad.test/ubuntu/warty
2912+ http://translations.launchpad.test/ubuntu/+series/warty
2913
2914 But we are already showing the status for the translation focus one,
2915 we should not have a link to it.
2916@@ -106,7 +106,7 @@ It doesn't have any translation, so we will get the default GeoIP
2917 languages pointing to the latest release, Hoary.
2918
2919 >>> print(browser.getLink('Zulu').url)
2920- http://translations.launchpad.test/debian/sarge/+lang/zu
2921+ http://translations.launchpad.test/debian/+series/sarge/+lang/zu
2922
2923 And the other Ubuntu distributions should be there too.
2924
2925@@ -122,9 +122,9 @@ We are not showing its translation status here, so we should have
2926 links to their particular translation status.
2927
2928 >>> print(browser.getLink('Sid (3.2)').url)
2929- http://translations.launchpad.test/debian/sid
2930+ http://translations.launchpad.test/debian/+series/sid
2931 >>> print(browser.getLink('Woody (3.0)').url)
2932- http://translations.launchpad.test/debian/woody
2933+ http://translations.launchpad.test/debian/+series/woody
2934
2935 But we are already showing the status for the translation focus one,
2936 we should not have a link to it.
2937diff --git a/lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt b/lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt
2938index 167459c..9ea06c6 100644
2939--- a/lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt
2940+++ b/lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt
2941@@ -16,7 +16,7 @@ To reach that page we go to the distro series page and follow the link to
2942 the language pack admin page.
2943
2944 >>> admin_browser.open(
2945- ... 'http://translations.launchpad.test/ubuntu/hoary')
2946+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
2947 >>> admin_browser.getLink('See all language packs').click()
2948
2949 Initially, there is no package selected and there are options for choosing a
2950@@ -39,7 +39,7 @@ Let's select a base one:
2951 >>> base.displayValue = ['2007-09-10 19:14:26 UTC']
2952 >>> admin_browser.getControl('Change').click()
2953 >>> print(admin_browser.url)
2954- http://translations.launchpad.test/ubuntu/hoary/+language-packs
2955+ http://translations.launchpad.test/ubuntu/+series/hoary/+language-packs
2956
2957 Now the admin page will show us that language pack selected and a list of
2958 available update packages.
2959@@ -56,7 +56,7 @@ available update packages.
2960
2961 Those changes are shown in the public language pack listing web page:
2962
2963- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary')
2964+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary')
2965 >>> browser.getLink('See all language packs').click()
2966 >>> print(extract_text(
2967 ... find_tag_by_id(browser.contents, "language_packs")))
2968@@ -90,7 +90,7 @@ URL uses '+latest-full-language-pack'.
2969 >>> print(browser.getLink('2007-09-10 19:14:26 UTC').url)
2970 http.../71/ubuntu-hoary-translations.tar.gz
2971 >>> print(browser.getLink('2007-09-10 19:16:01 UTC').url)
2972- http://translations.launchpad.test/ubuntu/hoary/+latest-full-language-pack
2973+ http://translations.launchpad.test/ubuntu/+series/hoary/+latest-full-language-pack
2974
2975 An administrator can choose the current update pack and there is also an
2976 option to set/unset whether next language pack generation is a full export:
2977@@ -104,7 +104,7 @@ option to set/unset whether next language pack generation is a full export:
2978 ... 'Request a full language pack export').selected = True
2979 >>> admin_browser.getControl('Change').click()
2980 >>> print(admin_browser.url)
2981- http://translations.launchpad.test/ubuntu/hoary/+language-packs
2982+ http://translations.launchpad.test/ubuntu/+series/hoary/+language-packs
2983 >>> print_feedback_messages(admin_browser.contents)
2984 Your request has been noted. Next language pack export will include
2985 all available translations...
2986@@ -122,7 +122,7 @@ cron, it will do a full export of translations for this distro series.
2987 The language pack changes are visible on the public language pack page:
2988
2989 >>> browser.open(
2990- ... 'http://translations.launchpad.test/ubuntu/hoary/+language-packs')
2991+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+language-packs')
2992 >>> print(extract_text(
2993 ... find_tag_by_id(browser.contents, "language_packs")))
2994 A language pack...
2995@@ -154,4 +154,4 @@ URL uses '+latest-full-language-pack'.
2996 >>> print(browser.getLink('2007-09-10 19:15:01 UTC').url)
2997 http.../72/ubuntu-hoary-translations-update.tar.gz
2998 >>> print(browser.getLink('2007-09-10 19:15:19 UTC').url)
2999- http://translations.launchpad.test/ubuntu/hoary/+latest-delta-language-pack
3000+ http://translations.launchpad.test/ubuntu/+series/hoary/+latest-delta-language-pack
3001diff --git a/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt b/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
3002index fa97ec4..c7118fa 100644
3003--- a/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
3004+++ b/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
3005@@ -6,7 +6,8 @@ packages in a particular distibution series.
3006
3007 In this case, we're asking for the translation overview for Hoary.
3008
3009- >>> anon_browser.open('http://translations.launchpad.test/ubuntu/hoary')
3010+ >>> anon_browser.open(
3011+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3012
3013 The system is not showing non visible languages:
3014
3015@@ -25,7 +26,7 @@ The system will not show English because it is not translatable:
3016 But it shows the ones not hidden:
3017
3018 >>> print(anon_browser.getLink('Spanish').url)
3019- http://translations.launchpad.test/ubuntu/hoary/+lang/es
3020+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es
3021
3022 Launchpad has an option to hide all of the translations for a distribution
3023 series. The link to hide translations is not available to anonymous users:
3024@@ -38,7 +39,7 @@ series. The link to hide translations is not available to anonymous users:
3025 And the page is not available either:
3026
3027 >>> anon_browser.open(
3028- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3029+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3030 ... '+translations-admin')
3031 Traceback (most recent call last):
3032 ...
3033@@ -47,7 +48,8 @@ And the page is not available either:
3034 ... but the link is available to administrators:
3035
3036 >>> dtc_browser = setupDTCBrowser()
3037- >>> dtc_browser.open('http://translations.launchpad.test/ubuntu/hoary')
3038+ >>> dtc_browser.open(
3039+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3040 >>> dtc_browser.getLink('Change settings').click()
3041
3042 Once the administrator hides all translations...
3043@@ -56,7 +58,7 @@ Once the administrator hides all translations...
3044 ... 'Hide translations for this release').selected = True
3045 >>> dtc_browser.getControl('Change').click()
3046 >>> print(dtc_browser.url)
3047- http://translations.launchpad.test/ubuntu/hoary
3048+ http://translations.launchpad.test/ubuntu/+series/hoary
3049
3050 ...a notice about the fact shows up on the overview page.
3051
3052@@ -69,7 +71,8 @@ Once the administrator hides all translations...
3053 Now, the translation status page will no longer display any languages to
3054 regular users.
3055
3056- >>> user_browser.open('http://translations.launchpad.test/ubuntu/hoary')
3057+ >>> user_browser.open(
3058+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3059 Traceback (most recent call last):
3060 ...
3061 TranslationUnavailable: ...
3062@@ -79,7 +82,7 @@ the system tells them that they're not allowed to see those pages.
3063
3064 >>> user_browser.handleErrors = True
3065 >>> user_browser.open(
3066- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/es')
3067+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es')
3068 Traceback (most recent call last):
3069 ...
3070 HTTPError: HTTP Error 503: Service Unavailable
3071@@ -92,20 +95,21 @@ the system tells them that they're not allowed to see those pages.
3072 Translations administrator have access series with hidden translations.
3073
3074 >>> dtc_browser.open(
3075- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/es')
3076+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es')
3077
3078 Non existing languages are not viewable. English is a special case
3079 in that we store the translatable messages as English, so it cannot
3080 should not viewed
3081
3082 >>> user_browser.open(
3083- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/notexists')
3084+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/'
3085+ ... 'notexists')
3086 Traceback (most recent call last):
3087 ...
3088 NotFound: ...
3089
3090 >>> user_browser.open(
3091- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/en')
3092+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/en')
3093 Traceback (most recent call last):
3094 ...
3095 NotFound: ...
3096@@ -114,7 +118,7 @@ Translation pages for source packages are also unavailable to
3097 non-administrative users.
3098
3099 >>> user_browser.open(
3100- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3101+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3102 ... '+sources/evolution/+pots/evolution-2.2')
3103 Traceback (most recent call last):
3104 ...
3105@@ -124,14 +128,15 @@ However, source package translations are still available to the
3106 administrators.
3107
3108 >>> dtc_browser.open(
3109- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3110+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3111 ... '+sources/evolution/+pots/evolution-2.2')
3112
3113 There is also an option to set/unset whether translation imports for a
3114 distribution should be deferred. That option is set also from the same
3115 form where we hide all translations and an admin is able to change it:
3116
3117- >>> dtc_browser.open('http://translations.launchpad.test/ubuntu/hoary')
3118+ >>> dtc_browser.open(
3119+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3120 >>> dtc_browser.getLink('Change settings').click()
3121 >>> dtc_browser.getControl(
3122 ... 'Defer translation imports').selected
3123@@ -140,7 +145,7 @@ form where we hide all translations and an admin is able to change it:
3124 ... 'Defer translation imports').selected = True
3125 >>> dtc_browser.getControl('Change').click()
3126 >>> print(dtc_browser.url)
3127- http://translations.launchpad.test/ubuntu/hoary
3128+ http://translations.launchpad.test/ubuntu/+series/hoary
3129
3130 Once the system accepts the submission, we can see such change applied.
3131
3132diff --git a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
3133index 7910e05..1535c1e 100644
3134--- a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
3135+++ b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
3136@@ -279,7 +279,7 @@ Carlos uploads files for Evolution in Ubuntu Hoary.
3137 >>> import transaction
3138 >>> from io import BytesIO
3139 >>> admin_browser.open(
3140- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3141+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3142 ... 'evolution/+pots/evolution-2.2/+upload')
3143 >>> file_ctrl = admin_browser.getControl('File:')
3144 >>> file_ctrl.add_file(
3145diff --git a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-targets.txt b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-targets.txt
3146index 6de7820..235db51 100644
3147--- a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-targets.txt
3148+++ b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-targets.txt
3149@@ -29,7 +29,8 @@ There is no content for Ubuntu.
3150
3151 The import queue is linked from the translations page for distribution series.
3152
3153- >>> user_browser.open('http://translations.launchpad.test/ubuntu/hoary')
3154+ >>> user_browser.open(
3155+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3156 >>> user_browser.getLink('Hoary import queue').click()
3157
3158 And obviously, given that the ubuntu distribution had no content, Hoary, an
3159diff --git a/lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt b/lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt
3160index 8e9f427..8b93af3 100644
3161--- a/lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt
3162+++ b/lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt
3163@@ -107,8 +107,8 @@ to where we can edit imports.
3164 Now, we attach a new file to an already existing translation resource.
3165
3166 >>> browser.open(
3167- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3168- ... '+pots/evolution-2.2/+upload')
3169+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3170+ ... 'evolution/+pots/evolution-2.2/+upload')
3171 >>> upload = browser.getControl('File')
3172 >>> upload
3173 <Control name='file' type='file'>
3174@@ -117,7 +117,7 @@ Now, we attach a new file to an already existing translation resource.
3175 ... 'text/x-gettext-translation-template', 'evolution.pot')
3176 >>> browser.getControl('Upload').click()
3177 >>> print(browser.url)
3178- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+upload
3179+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+upload
3180 >>> for tag in find_tags_by_class(browser.contents, 'message'):
3181 ... print(tag.renderContents())
3182 Thank you for your upload. It will be automatically reviewed...
3183@@ -362,7 +362,7 @@ Let's try breaking the form by not supplying a file object. It give us a
3184 decent error message:
3185
3186 >>> browser.open(
3187- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3188+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3189 ... '+source/evolution/+pots/evolution-2.2/+upload')
3190 >>> browser.getControl('Upload').click()
3191 >>> for tag in find_tags_by_class(browser.contents, 'message'):
3192diff --git a/lib/lp/translations/stories/navigation-links/pofile.txt b/lib/lp/translations/stories/navigation-links/pofile.txt
3193index 4f2bbc6..f72afd1 100644
3194--- a/lib/lp/translations/stories/navigation-links/pofile.txt
3195+++ b/lib/lp/translations/stories/navigation-links/pofile.txt
3196@@ -24,10 +24,10 @@ The Application tabs should point to IProduct URLs.
3197 Taking an IPOFile for ISourcePackage context:
3198
3199 >>> admin_browser.open(
3200- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3201- ... '+pots/evolution-2.2/es')
3202+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3203+ ... 'evolution/+pots/evolution-2.2/es')
3204 >>> print(admin_browser.url)
3205- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es
3206+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es
3207
3208 The Application tabs should point to IDistributionSourcePackage URLs.
3209
3210diff --git a/lib/lp/translations/stories/navigation-links/pomsgset.txt b/lib/lp/translations/stories/navigation-links/pomsgset.txt
3211index be40c03..5340696 100644
3212--- a/lib/lp/translations/stories/navigation-links/pomsgset.txt
3213+++ b/lib/lp/translations/stories/navigation-links/pomsgset.txt
3214@@ -26,14 +26,14 @@ The Application tabs should point to IProductSeries URLs.
3215 Taking an IPOMsgSet for ISourcePackage context:
3216
3217 >>> browser.open(
3218- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3219- ... '+pots/evolution-2.2/es/1')
3220+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3221+ ... 'evolution/+pots/evolution-2.2/es/1')
3222
3223 We get a +translate page because that's the only page for an IPOMsgSet and the
3224 system forwards automatically there.
3225
3226 >>> print(browser.url)
3227- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/1/+translate
3228+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/1/+translate
3229
3230 The Application tabs should point to ISourcePackage URLs.
3231
3232diff --git a/lib/lp/translations/stories/navigation-links/potemplate.txt b/lib/lp/translations/stories/navigation-links/potemplate.txt
3233index e138b54..43bba91 100644
3234--- a/lib/lp/translations/stories/navigation-links/potemplate.txt
3235+++ b/lib/lp/translations/stories/navigation-links/potemplate.txt
3236@@ -22,10 +22,10 @@ The Application tabs should point to IProduct URLs.
3237 Taking an IPOTemplate for ISourcePackage context:
3238
3239 >>> admin_browser.open(
3240- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3241- ... '+pots/evolution-2.2')
3242+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3243+ ... 'evolution/+pots/evolution-2.2')
3244 >>> print(admin_browser.url)
3245- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2
3246+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2
3247
3248 The Application tabs should point to IDistributionSourcePackage URLs.
3249
3250diff --git a/lib/lp/translations/stories/standalone/xx-pofile-details.txt b/lib/lp/translations/stories/standalone/xx-pofile-details.txt
3251index 888e2fb..53d7736 100644
3252--- a/lib/lp/translations/stories/standalone/xx-pofile-details.txt
3253+++ b/lib/lp/translations/stories/standalone/xx-pofile-details.txt
3254@@ -161,7 +161,7 @@ We'll create two new accounts to demonstrate this.
3255
3256 >>> browser.open(
3257 ... ("http://translations.launchpad.test/"
3258- ... "ubuntu/hoary/+source/%s/+pots/%s/%s/+details") % (
3259+ ... "ubuntu/+series/hoary/+source/%s/+pots/%s/%s/+details") % (
3260 ... package.name, template.name, language_code))
3261 >>> main_text = extract_text(find_main_content(browser.contents))
3262 >>> print(main_text)
3263diff --git a/lib/lp/translations/stories/standalone/xx-pofile-export.txt b/lib/lp/translations/stories/standalone/xx-pofile-export.txt
3264index fef4485..25e7bf8 100644
3265--- a/lib/lp/translations/stories/standalone/xx-pofile-export.txt
3266+++ b/lib/lp/translations/stories/standalone/xx-pofile-export.txt
3267@@ -4,7 +4,7 @@ Exporting Single PO Files through the Web
3268 Not logged in users can't access the +export page.
3269
3270 >>> anon_browser.open(
3271- ... 'http://translations.launchpad.test/ubuntu/hoary'
3272+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary'
3273 ... '/+source/evolution/+pots/evolution-2.2/es/')
3274 >>> anon_browser.getLink('Download').click()
3275 Traceback (most recent call last):
3276@@ -14,7 +14,7 @@ Not logged in users can't access the +export page.
3277 Logged in as a regular user, the +export page is accessible.
3278
3279 >>> user_browser.open(
3280- ... 'http://translations.launchpad.test/ubuntu/hoary'
3281+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary'
3282 ... '/+source/evolution/+pots/evolution-2.2/es')
3283 >>> user_browser.getLink('Download').click()
3284
3285@@ -34,7 +34,7 @@ If we POST the page, it should add the request to the queue.
3286 >>> user_browser.getControl(name='format').value = ['PO']
3287 >>> user_browser.getControl('Request Download').click()
3288 >>> print(user_browser.url)
3289- http://translatio.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es
3290+ http://translatio.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es
3291
3292 >>> for tag in find_tags_by_class(user_browser.contents, 'informational'):
3293 ... tag.renderContents()
3294diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
3295index 070d946..c2a171c 100644
3296--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
3297+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
3298@@ -42,7 +42,7 @@ alternative suggestions. We do not offer suggestions from standard English
3299 since that is the one language we always translate from, never to.
3300
3301 >>> translate_page = (
3302- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3303+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3304 ... 'evolution/+pots/evolution-2.2/es/+translate')
3305 >>> anon_browser.open(translate_page)
3306 >>> get_alternative_languages_widget(anon_browser).displayOptions[:4]
3307@@ -239,7 +239,7 @@ If a user specifies more than one alternative language in the URL, they
3308 get an UnexpectedFormData exception:
3309
3310 >>> browser.open(
3311- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3312+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3313 ... 'evolution/+pots/evolution-2.2/es/+translate'
3314 ... '?field.alternative_language=ja&field.alternative_language=aj')
3315 Traceback (most recent call last):
3316diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-empty-strings-without-validation.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-empty-strings-without-validation.txt
3317index 767035d..452c159 100644
3318--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-empty-strings-without-validation.txt
3319+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-empty-strings-without-validation.txt
3320@@ -2,8 +2,8 @@ Checks that an empty translation is not checked with pygettextpo
3321
3322 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3323 >>> browser.open(
3324- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3325- ... '+pots/evolution-2.2/es/+translate?start=12&batch=1')
3326+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3327+ ... 'evolution/+pots/evolution-2.2/es/+translate?start=12&batch=1')
3328
3329 The msgid for msgset_142 uses a format string ('%s') and that means that the
3330 translation should use it too. If the translation is empty, our validation
3331@@ -27,4 +27,4 @@ We should be redirected to the next page because the validation didn't get
3332 it as an error.
3333
3334 >>> print(browser.url)
3335- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?batch=1&memo=13&start=13
3336+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?batch=1&memo=13&start=13
3337diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-gettext-error-middle-page.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-gettext-error-middle-page.txt
3338index 574be01..e2a34f0 100644
3339--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-gettext-error-middle-page.txt
3340+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-gettext-error-middle-page.txt
3341@@ -4,8 +4,8 @@ messages for this POFile, we still detect the error and notify to our users.
3342
3343 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3344 >>> browser.open(
3345- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3346- ... '+pots/evolution-2.2/es/+translate?start=10&batch=5')
3347+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3348+ ... 'evolution/+pots/evolution-2.2/es/+translate?start=10&batch=5')
3349
3350 Submit the form using a wrong format string. The msgid is using '%s' which
3351 means it will be an string, but we are going to use '%i' which means an
3352@@ -32,7 +32,7 @@ And submit the form.
3353 We remain at the same page:
3354
3355 >>> print(browser.url)
3356- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=10&batch=5
3357+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=10&batch=5
3358
3359 The valid translation is stored:
3360
3361diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt
3362index e528f7f..66e1425 100644
3363--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt
3364+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-html-tags-escape.txt
3365@@ -2,8 +2,8 @@ This check will be sure that we are escaping correctly html tags
3366 inside textareas.
3367
3368 >>> user_browser.open(
3369- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/pmount/'
3370- ... '+pots/pmount/hr/+translate')
3371+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3372+ ... 'pmount/+pots/pmount/hr/+translate')
3373
3374 We are going to assign a value with html tags to see that we escape it when
3375 rendered as part of a textarea. As we want to see what we get back inside a
3376@@ -20,7 +20,7 @@ textarea, and
3377 We are in next form page.
3378
3379 >>> print(user_browser.url)
3380- http://translations.launchpad.test/ubuntu/hoary/+source/pmount/+pots/pmount/hr/+translate?memo=10&start=10
3381+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/pmount/+pots/pmount/hr/+translate?memo=10&start=10
3382
3383 Let's go back to the modified message.
3384
3385diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-lang-direction.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-lang-direction.txt
3386index 0461004..a842cfd 100644
3387--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-lang-direction.txt
3388+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-lang-direction.txt
3389@@ -13,8 +13,8 @@ the separator in language codes rather than an underscore.
3390
3391 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3392 >>> browser.open(
3393- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3394- ... '+pots/evolution-2.2/en_AU/+translate')
3395+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3396+ ... 'evolution/+pots/evolution-2.2/en_AU/+translate')
3397 >>> control = browser.getControl(name="msgset_130_en_AU_translation_0_new")
3398 >>> print(control._control.attrs.get('dir'))
3399 ltr
3400@@ -25,8 +25,8 @@ the separator in language codes rather than an underscore.
3401 When entering Hebrew translations, the form controls are set to right to left:
3402
3403 >>> browser.open(
3404- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3405- ... '+pots/evolution-2.2/he/+translate')
3406+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3407+ ... 'evolution/+pots/evolution-2.2/he/+translate')
3408 >>> control = browser.getControl(name="msgset_130_he_translation_0_new")
3409 >>> print(control._control.attrs.get('dir'))
3410 rtl
3411@@ -37,8 +37,9 @@ When entering Hebrew translations, the form controls are set to right to left:
3412 If we post the form with suggestions, the form controls are still set to rtl:
3413
3414 >>> browser.open(
3415- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3416- ... '+pots/evolution-2.2/he/+translate?field.alternative_language=es')
3417+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3418+ ... 'evolution/+pots/evolution-2.2/he/+translate'
3419+ ... '?field.alternative_language=es')
3420 >>> control = browser.getControl(name="msgset_130_he_translation_0_new")
3421 >>> print(control._control.attrs.get('dir'))
3422 rtl
3423diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-legal-warning.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-legal-warning.txt
3424index 2e8cbf0..7c3644d 100644
3425--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-legal-warning.txt
3426+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-legal-warning.txt
3427@@ -19,7 +19,7 @@ from the upstream project.
3428 >>> logout()
3429
3430 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3431- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/3/+translate')
3432+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/3/+translate')
3433 >>> print(extract_text(find_tag_by_id(
3434 ... browser.contents, 'msgset_132_es_suggestion_3_0')))
3435 tiene
3436diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt
3437index e2de6c3..e909218 100644
3438--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt
3439+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt
3440@@ -45,7 +45,7 @@ No Privileges Person visits the evolution-2.2 package in Ubuntu Hoary to
3441 review the state of the translation.
3442
3443 >>> user_browser.open(
3444- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3445+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3446 ... '+source/evolution/+pots/evolution-2.2/es/+translate')
3447 >>> print(user_browser.title)
3448 Spanish (es) : Template ...evolution-2.2... :
3449@@ -120,7 +120,7 @@ decides to use the 'Untranslated' filter to locate messages that need
3450 translations into Australian English.
3451
3452 >>> user_browser.open(
3453- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3454+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3455 ... '+source/evolution/+pots/evolution-2.2/en_AU/+translate')
3456 >>> user_browser.getControl(name='show', index=1).value = ['untranslated']
3457 >>> user_browser.getControl('Change').click()
3458@@ -229,7 +229,7 @@ No Privileges Person can see entries which have changed in Ubuntu.
3459 There is only one message in the batch.
3460
3461 >>> user_browser.open(
3462- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3463+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3464 ... '+source/evolution/+pots/evolution-2.2/es/+translate')
3465 >>> user_browser.getControl(name='show', index=1).displayValue = [
3466 ... 'changed in Ubuntu']
3467@@ -270,7 +270,7 @@ submitted after they were last reviewed. There is only one message in
3468 the batch.
3469
3470 >>> user_browser.open(
3471- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3472+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3473 ... '+source/evolution/+pots/evolution-2.2/es/+translate')
3474 >>> user_browser.getControl(name='show', index=1).displayValue = [
3475 ... 'with new suggestions']
3476@@ -310,7 +310,7 @@ There was once a filter option called need_review. It no longer exists,
3477 but is quietly accepted.
3478
3479 >>> user_browser.open(
3480- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3481+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3482 ... '+source/evolution/+pots/evolution-2.2/es/+translate'
3483 ... '?show=need_review')
3484
3485@@ -329,7 +329,7 @@ the batch header when they switch the filter to show 'untranslated'
3486 message; they are seeing the first batch.
3487
3488 >>> user_browser.open(
3489- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3490+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3491 ... '+source/evolution/+pots/evolution-2.2/es/+translate')
3492 >>> user_browser.getLink('Last').click()
3493 >>> contents = find_main_content(user_browser.contents)
3494@@ -407,7 +407,7 @@ message filters with alternative suggestion languages. No Privileges
3495 Person submits Chinese translations using Spanish suggestions.
3496
3497 >>> user_browser.open(
3498- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3499+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3500 ... '+source/evolution/+pots/evolution-2.2/zh_CN/+translate')
3501 >>> user_browser.getControl(name='show', index=1).value = ['untranslated']
3502 >>> user_browser.getControl('Change').click()
3503diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-needs-review-flags-preserved.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-needs-review-flags-preserved.txt
3504index 17152d7..7e78530 100644
3505--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-needs-review-flags-preserved.txt
3506+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-needs-review-flags-preserved.txt
3507@@ -24,7 +24,7 @@ If the same user tries translating for another, unrestricted project,
3508 they get to see the checkbox:
3509
3510 >>> user_browser.open(
3511- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3512+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3513 ... 'evolution/+pots/evolution-2.2/es/1/+translate')
3514 >>> needs_review_set = user_browser.getControl(
3515 ... 'Someone should review this translation')
3516@@ -42,7 +42,7 @@ a translator needs to mark the needs review checkbox.
3517 ... name='msgset_130_es_translation_0_new').value = "New suggestion"
3518 >>> user_browser.getControl('Save & Continue').click()
3519 >>> print(user_browser.url)
3520- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/2/+translate
3521+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/2/+translate
3522
3523 The needs review flag is unset when we go back to the previous message.
3524
3525@@ -73,7 +73,7 @@ A new translation is entered and checked that it was saved as the current
3526 translation, while no suggestions are displayed.
3527
3528 >>> admin_browser.open(
3529- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3530+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3531 ... 'evolution/+pots/man/es/1/+translate')
3532 >>> inputradio = admin_browser.getControl(
3533 ... name='msgset_166_es_translation_0_radiobutton')
3534diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt
3535index d0f8190..55a1516 100644
3536--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt
3537+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt
3538@@ -16,7 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=299009
3539
3540 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3541 >>> browser.open(
3542- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3543+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3544 ... 'evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1')
3545
3546 We can see that the message we are interested in is not translated.
3547@@ -41,7 +41,7 @@ answer should have exactly those strings.
3548 ... name='msgset_149_es_translation_0_new').value = '\r\nfoo\r\n\r\n'
3549 >>> browser.getControl(name='submit_translations').click()
3550 >>> print(browser.url)
3551- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1
3552+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1
3553 >>> print(find_tag_by_id(
3554 ... browser.contents, 'msgset_149_es_translation_0_new'))
3555 <textarea ... name="msgset_149_es_translation_0_new"...>
3556@@ -60,7 +60,7 @@ change the test, to be 100% sure that the textarea content is the right one.
3557 >>> browser.getControl(name='msgset_149_es_translation_0_new').value = 'foo'
3558 >>> browser.getControl(name='submit_translations').click()
3559 >>> print(browser.url)
3560- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1
3561+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1
3562 >>> print(find_tag_by_id(
3563 ... browser.contents,
3564 ... 'msgset_149_es_translation_0_new')) #doctest: -NORMALIZE_WHITESPACE
3565diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt
3566index b4a1ada..5ec8ef3 100644
3567--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt
3568+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-performance.txt
3569@@ -16,10 +16,10 @@ number of queries issued is relatively low. Mileage may vary, but consider
3570 this test a tripwire for the number potentially getting out of hand.
3571
3572 >>> anon_browser.open(
3573- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3574- ... '+pots/evolution-2.2/es/+translate')
3575+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3576+ ... 'evolution/+pots/evolution-2.2/es/+translate')
3577 >>> anon_browser.url
3578- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate'
3579+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate'
3580 >>> print(anon_browser.contents)
3581 <...
3582 >>> statement_count = query_counter.count
3583@@ -33,10 +33,10 @@ option to make suggestions, but existing suggestions as well. It takes more
3584 queries, but it still shouldn't run in the hundreds.
3585
3586 >>> admin_browser.open(
3587- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
3588- ... '+pots/evolution-2.2/es/+translate')
3589+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3590+ ... 'evolution/+pots/evolution-2.2/es/+translate')
3591 >>> admin_browser.url
3592- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate'
3593+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate'
3594
3595 XXX: JeroenVermeulen 2008-06-20 bug=241394: This has just started
3596 failing in PQM. May be something to do with the introduction of Storm.
3597diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-search.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-search.txt
3598index 0994bed..f6c9f40 100644
3599--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-search.txt
3600+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-search.txt
3601@@ -8,7 +8,7 @@ No Privileges Person visits the evolution-2.2 package in Ubuntu Hoary
3602 can see the search box on the translate page:
3603
3604 >>> user_browser.open(
3605- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3606+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3607 ... '+source/evolution/+pots/evolution-2.2/es/+translate')
3608 >>> user_browser.getControl('Search', index=0).value = "contact"
3609 >>> user_browser.getForm(id="search_form").submit()
3610diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
3611index ccb3921..e90e6f3 100644
3612--- a/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
3613+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
3614@@ -31,7 +31,7 @@ Anonymous users are able to browse translations, but not to change them
3615 through the translation form.
3616
3617 >>> browser.open(
3618- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3619+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3620 ... 'evolution/+pots/evolution-2.2/es/+translate')
3621
3622 The page is rendered in read-only mode, without any textareas for input.
3623@@ -75,7 +75,7 @@ Translation Admin Access
3624 Let's log in.
3625
3626 >>> admin_browser.open(
3627- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3628+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3629 ... 'evolution/+pots/evolution-2.2/es/+translate')
3630
3631 As a translation admin you will have access to the download, upload
3632@@ -113,7 +113,7 @@ links from off-site; Launchpad did make links for English translations
3633 in the past.
3634
3635 >>> browser.open(
3636- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3637+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3638 ... 'evolution/+pots/evolution-2.2/en/+translate')
3639 Traceback (most recent call last):
3640 ...
3641@@ -144,8 +144,9 @@ they must adhere religiously to an agreed-to format.
3642
3643 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3644 >>> browser.open("http://translations.launchpad.test/"
3645- ... "ubuntu/hoary/+source/evolution/+pots/evolution-2.2"
3646- ... "/en_AU/+translate?field.alternative_language=es")
3647+ ... "ubuntu/+series/hoary/+source/evolution/+pots"
3648+ ... "/evolution-2.2/en_AU/+translate"
3649+ ... "?field.alternative_language=es")
3650
3651 Elements related 1:1 to a translatable message on this form have names and
3652 identifiers constructed as "msgset_<id>," where <id> is the unpadded decimal
3653@@ -210,7 +211,7 @@ There are many variants of this id structure, generated in several places and
3654 for several objects, all generated by the same methods.
3655
3656 >>> browser.open(
3657- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
3658+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
3659 ... 'evolution/+pots/evolution-2.2/es/5/+translate')
3660 >>> print(extract_text(find_tag_by_id(
3661 ... browser.contents, 'msgset_134_es_suggestion_694_0')))
3662@@ -227,12 +228,12 @@ the plural form informations.
3663 This notice is display when doing batch translations or translating a
3664 single message.
3665
3666- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
3667+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary/'
3668 ... '+source/evolution/+pots/evolution-2.2/ab/+translate')
3669 >>> print_feedback_messages(browser.contents)
3670 Launchpad canā€™t handle the plural items ...
3671
3672- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
3673+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary/'
3674 ... '+source/evolution/+pots/evolution-2.2/ab/5/+translate')
3675 >>> print_feedback_messages(browser.contents)
3676 Launchpad canā€™t handle the plural items ...
3677diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt b/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
3678index bf9852a..05256c9 100644
3679--- a/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
3680+++ b/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
3681@@ -208,7 +208,7 @@ autonomously.
3682 >>> translation_group = factory.makeTranslationGroup(group_owner)
3683 >>> ubuntu.translationgroup = translation_group
3684 >>> template_admin_url = str(
3685- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3686+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3687 ... '+source/%s/+pots/%s/+admin' % (
3688 ... dsp.sourcepackagename.name, template.name))
3689 >>> logout()
3690diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-export.txt b/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
3691index 9f42e51..5bfcd5e 100644
3692--- a/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
3693+++ b/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
3694@@ -4,7 +4,7 @@ Exporting Files from PO Templates Through the Web
3695 Not logged in users can't access the +export page.
3696
3697 >>> anon_browser.open(
3698- ... 'http://translations.launchpad.test/ubuntu/hoary'
3699+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary'
3700 ... '/+source/evolution/+pots/evolution-2.2/')
3701 >>> anon_browser.getLink('download').click()
3702 Traceback (most recent call last):
3703@@ -15,7 +15,7 @@ Logged in as a regular user, the +export page is accessible.
3704
3705 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
3706 >>> browser.open(
3707- ... 'http://translations.launchpad.test/ubuntu/hoary'
3708+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary'
3709 ... '/+source/evolution/+pots/evolution-2.2')
3710 >>> browser.getLink('download').click()
3711 >>> browser.title
3712@@ -48,7 +48,7 @@ added to the export queue and individual requests are added for the PO files.
3713 >>> browser.getControl('Format:').value = ['PO']
3714 >>> browser.getControl('Request Download').click()
3715 >>> print(browser.url)
3716- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2
3717+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2
3718
3719 >>> print_feedback_messages(browser.contents)
3720 Your request has been received. Expect to receive an email shortly.
3721@@ -63,7 +63,7 @@ This is a no-op: (See bug https://launchpad.net/rosetta/+bug/1558)
3722 >>> browser.getControl('Format:').value = ['PO']
3723 >>> browser.getControl('Request Download').click()
3724 >>> print(browser.url)
3725- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2
3726+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2
3727
3728 >>> print_feedback_messages(browser.contents)
3729 Your request has been received. Expect to receive an email shortly.
3730diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-index.txt b/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
3731index 8bf48de..614d0ef 100644
3732--- a/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
3733+++ b/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
3734@@ -15,7 +15,7 @@ for a source package. No Privileges Person visits the
3735 evolution-2.2 POTemplate page.
3736
3737 >>> anon_browser.open("http://translations.launchpad.test/"
3738- ... "ubuntu/hoary/+source/evolution/+pots/evolution-2.2/")
3739+ ... "ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/")
3740 >>> print(anon_browser.title)
3741 Template ...evolution-2.2... : Hoary (5.04) :
3742 Translations : evolution package : Ubuntu
3743@@ -55,7 +55,8 @@ languages when the user speaks English or even when English
3744 translations exist. The Mozilla sourcepackage pkgconf-mozilla has
3745 English translations, but they are not displayed to the user.
3746
3747- >>> anon_browser.open('http://translations.launchpad.test/ubuntu/hoary/'
3748+ >>> anon_browser.open(
3749+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3750 ... '+source/mozilla/+pots/pkgconf-mozilla')
3751 >>> table = find_tag_by_id(anon_browser.contents, 'language-chart')
3752 >>> for row in table.findAll('tr')[0:6]:
3753@@ -84,7 +85,7 @@ the Ubuntu source package. This information is displayed on the page.
3754 evolution in Ubuntu Hoary template evolution-2.2.
3755 View sharing details
3756 >>> print(sharing_info)
3757- <div...<a href="/ubuntu/hoary/+source/evolution/+pots/evolution-2.2"...
3758+ <div...<a href="/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2"...
3759
3760 Likewise, the Ubuntu template gives information about how it is sharing
3761 translations with the upstream project.
3762@@ -111,7 +112,7 @@ information.
3763 >>> print(extract_text(sharing_details))
3764 Edit sharing details
3765 >>> print(sharing_details['href'])
3766- http://.../ubuntu/hoary/+source/evolution/+sharing-details
3767+ http://.../ubuntu/+series/hoary/+source/evolution/+sharing-details
3768
3769
3770
3771@@ -171,7 +172,7 @@ templates.
3772
3773 >>> browser.open(
3774 ... ("http://translations.launchpad.test/"
3775- ... "ubuntu/hoary/+source/%s/+pots/%s") % (
3776+ ... "ubuntu/+series/hoary/+source/%s/+pots/%s") % (
3777 ... package.name, template.name))
3778 >>> relatives = find_tag_by_id(
3779 ... browser.contents, 'potemplate-relatives')
3780@@ -191,7 +192,7 @@ Another template is added to the same source package.
3781
3782 >>> browser.open(
3783 ... ("http://translations.launchpad.test/"
3784- ... "ubuntu/hoary/+source/%s/+pots/%s") % (
3785+ ... "ubuntu/+series/hoary/+source/%s/+pots/%s") % (
3786 ... package.name, template.name))
3787 >>> relatives = find_tag_by_id(
3788 ... browser.contents, 'potemplate-relatives')
3789@@ -215,7 +216,7 @@ Another template is added to the same source package.
3790
3791 >>> browser.open((
3792 ... "http://translations.launchpad.test/"
3793- ... "ubuntu/hoary/+source/%s/+pots/%s") % (
3794+ ... "ubuntu/+series/hoary/+source/%s/+pots/%s") % (
3795 ... package.name, template.name))
3796 >>> relatives = find_tag_by_id(
3797 ... browser.contents, 'potemplate-relatives')
3798@@ -226,7 +227,7 @@ Another template is added to the same source package.
3799 >>> browser.getLink('2 other templates').click()
3800 >>> browser.url == ((
3801 ... 'http://translations.launchpad.test/'
3802- ... 'ubuntu/hoary/+source/%s/+translations') % (
3803+ ... 'ubuntu/+series/hoary/+source/%s/+translations') % (
3804 ... package.name))
3805 True
3806
3807@@ -280,7 +281,7 @@ administration or download/upload links.
3808
3809 >>> anon_browser.open(
3810 ... 'http://translations.launchpad.test/'
3811- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3812+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3813 >>> anon_browser.getLink('upload')
3814 Traceback (most recent call last):
3815 ...
3816@@ -296,7 +297,7 @@ but not the one for uploading file to this potemplate.
3817
3818 >>> user_browser.open(
3819 ... 'http://translations.launchpad.test/'
3820- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3821+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3822 >>> user_browser.getLink('upload')
3823 Traceback (most recent call last):
3824 ...
3825@@ -304,7 +305,7 @@ but not the one for uploading file to this potemplate.
3826
3827 >>> user_browser.getLink('download').click()
3828 >>> print(user_browser.url)
3829- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+export
3830+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+export
3831
3832 Translation administrators will see both download and upload links.
3833 Beside administering this template, "Change permissions"
3834@@ -312,28 +313,28 @@ and "Change details" should be also accessible.
3835
3836 >>> admin_browser.open(
3837 ... 'http://translations.launchpad.test/'
3838- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3839+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3840 >>> admin_browser.getLink('upload').click()
3841 >>> print(admin_browser.url)
3842- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+upload
3843+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+upload
3844
3845 >>> admin_browser.open(
3846 ... 'http://translations.launchpad.test/'
3847- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3848+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3849 >>> admin_browser.getLink('download').click()
3850 >>> print(admin_browser.url)
3851- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+export
3852+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+export
3853
3854 >>> admin_browser.open(
3855 ... 'http://translations.launchpad.test/'
3856- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3857+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3858 >>> admin_browser.getLink('Administer this template').click()
3859 >>> print(admin_browser.url)
3860- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+admin
3861+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+admin
3862
3863 >>> admin_browser.open(
3864 ... 'http://translations.launchpad.test/'
3865- ... 'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
3866+ ... 'ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2')
3867 >>> admin_browser.getLink('Change details').click()
3868 >>> print(admin_browser.url)
3869- http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+edit
3870+ http://trans.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+edit
3871diff --git a/lib/lp/translations/stories/standalone/xx-rosetta-source-package-redirects.txt b/lib/lp/translations/stories/standalone/xx-rosetta-source-package-redirects.txt
3872index 949cc61..ddd263e 100644
3873--- a/lib/lp/translations/stories/standalone/xx-rosetta-source-package-redirects.txt
3874+++ b/lib/lp/translations/stories/standalone/xx-rosetta-source-package-redirects.txt
3875@@ -1,7 +1,7 @@
3876 Checks that the '+pots/' page redirects always to the '+translations' one.
3877
3878 >>> print(http(r"""
3879- ... GET /ubuntu/hoary/+source/evolution/+pots/ HTTP/1.1
3880+ ... GET /ubuntu/+series/hoary/+source/evolution/+pots/ HTTP/1.1
3881 ... Accept-Language: en-gb,en;q=0.5
3882 ... Host: translations.launchpad.test
3883 ... """))
3884@@ -15,13 +15,13 @@ Checks that the '+pots/' page redirects always to the '+translations' one.
3885 Checks that the '+pots' page redirects always to the '+translations' one.
3886
3887 >>> print(http(r"""
3888- ... GET /ubuntu/hoary/+source/evolution/+pots HTTP/1.1
3889+ ... GET /ubuntu/+series/hoary/+source/evolution/+pots HTTP/1.1
3890 ... Accept-Language: en-gb,en;q=0.5
3891 ... Host: translations.launchpad.test
3892 ... """))
3893 HTTP/1.1 303 See Other
3894 ...
3895- Location: .../ubuntu/hoary/+source/evolution/+pots...
3896+ Location: .../ubuntu/+series/hoary/+source/evolution/+pots...
3897 ...
3898
3899 Checks that the '+sources/.../+translate' page redirects always to the
3900@@ -32,7 +32,7 @@ Hardy, which is 2013-04. Please consult with the Ubuntu Desktop team before
3901 removing.
3902
3903 >>> print(http(r"""
3904- ... GET /ubuntu/hoary/+sources/evolution/+translate HTTP/1.1
3905+ ... GET /ubuntu/+series/hoary/+sources/evolution/+translate HTTP/1.1
3906 ... Accept-Language: en-gb,en;q=0.5
3907 ... Host: translations.launchpad.test
3908 ... """))
3909diff --git a/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt b/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
3910index e4f442c..31eabd4 100644
3911--- a/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
3912+++ b/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
3913@@ -10,7 +10,7 @@ IP address, since we'll use that later.
3914 >>> anon_browser.addHeader('X_FORWARDED_FOR', '196.36.161.227')
3915
3916 >>> anon_browser.open(
3917- ... 'http://translations.launchpad.test/ubuntu/hoary/'
3918+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3919 ... '+source/evolution')
3920 >>> anon_browser.title
3921 'Hoary (5.04) : Translations : ...evolution...package : Ubuntu'
3922diff --git a/lib/lp/translations/stories/standalone/xx-series-templates.txt b/lib/lp/translations/stories/standalone/xx-series-templates.txt
3923index 882ecff..04b461a 100644
3924--- a/lib/lp/translations/stories/standalone/xx-series-templates.txt
3925+++ b/lib/lp/translations/stories/standalone/xx-series-templates.txt
3926@@ -17,10 +17,10 @@ To get to the listing of all templates, one needs to use the link
3927 from the distribution series translations page.
3928
3929 >>> user_browser.open(
3930- ... 'http://translations.launchpad.test/ubuntu/hoary')
3931+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3932 >>> user_browser.getLink('full list of templates').click()
3933 >>> print(user_browser.url)
3934- http://translations.launchpad.test/ubuntu/hoary/+templates
3935+ http://translations.launchpad.test/ubuntu/+series/hoary/+templates
3936
3937
3938 Templates view for DistroSeries
3939@@ -30,7 +30,7 @@ Full template listing for a distribution series is reached by following
3940 a link from the distribution series translations page.
3941
3942 >>> anon_browser.open(
3943- ... 'http://translations.launchpad.test/ubuntu/hoary')
3944+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3945 >>> anon_browser.getLink('full list of templates').click()
3946
3947 Full listing of templates shows priority, source package name, template name
3948@@ -46,7 +46,7 @@ length, languages and the date of last update for this distribution series.
3949 Logged-in users see a link to all the active translation templates
3950 on a distribution series translation page.
3951 >>> user_browser.open(
3952- ... 'http://translations.launchpad.test/ubuntu/hoary')
3953+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3954 >>> user_browser.getLink('full list of templates').click()
3955
3956 Regular users only see the option to download translations for each of
3957@@ -61,7 +61,7 @@ the active templates.
3958 Administrator can see all editing options.
3959
3960 >>> admin_browser.open(
3961- ... 'http://translations.launchpad.test/ubuntu/hoary')
3962+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary')
3963 >>> admin_browser.getLink('full list of templates').click()
3964
3965 The page shows a table of all templates and links to their subpages.
3966@@ -84,16 +84,18 @@ appropriate page.
3967
3968 >>> utc_browser = setupDTCBrowser()
3969 >>> utc_browser.open(
3970- ... 'http://translations.launchpad.test/ubuntu/hoary/+templates')
3971+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3972+ ... '+templates')
3973 >>> utc_browser.getLink(
3974 ... url='+source/evolution/+pots/evolution-2.2/+edit').click()
3975 >>> print(utc_browser.url)
3976- http://.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/+edit
3977+ http://.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/+edit
3978
3979 Administration page is inaccessible.
3980
3981 >>> utc_browser.open(
3982- ... 'http://translations.launchpad.test/ubuntu/hoary/+templates')
3983+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3984+ ... '+templates')
3985 >>> utc_browser.getLink(
3986 ... url='+source/evolution/+pots/evolution-2.2/+admin')
3987 Traceback (most recent call last):
3988@@ -103,16 +105,18 @@ Administration page is inaccessible.
3989 Trying to edit disabled templates brings them to the appropriate page.
3990
3991 >>> utc_browser.open(
3992- ... 'http://translations.launchpad.test/ubuntu/hoary/+templates')
3993+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
3994+ ... '+templates')
3995 >>> utc_browser.getLink(
3996 ... url='+source/evolution/+pots/disabled-template/+edit').click()
3997 >>> print(utc_browser.url)
3998- http://.../ubuntu/hoary/+source/evolution/+pots/disabled-template/+edit
3999+ http://.../ubuntu/+series/hoary/+source/evolution/+pots/disabled-template/+edit
4000
4001 Administration page is inaccessible.
4002
4003 >>> utc_browser.open(
4004- ... 'http://translations.launchpad.test/ubuntu/hoary/+templates')
4005+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4006+ ... '+templates')
4007 >>> utc_browser.getLink(
4008 ... url='+source/evolution/+pots/disabled-template/+admin')
4009 Traceback (most recent call last):
4010@@ -128,13 +132,14 @@ page.
4011
4012 >>> admin_browser.getLink('pmount').click()
4013 >>> print(admin_browser.url)
4014- http://translations.launchpad.test/ubuntu/hoary/+source/pmount/+pots/pmount
4015+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/pmount/+pots/pmount
4016
4017 Clicking on 'Edit' will take the user to the page to edit the template
4018 details. Likewise for the other links for each template.
4019
4020 >>> admin_browser.open(
4021- ... 'http://translations.launchpad.test/ubuntu/hoary/+templates')
4022+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4023+ ... '+templates')
4024 >>> admin_browser.getLink('Edit', index=1).click()
4025 >>> print(admin_browser.url)
4026 http://translations.../evolution/+pots/disabled-template/+edit
4027diff --git a/lib/lp/translations/stories/standalone/xx-serieslanguage-index.txt b/lib/lp/translations/stories/standalone/xx-serieslanguage-index.txt
4028index 93b3b67..ab1280f 100644
4029--- a/lib/lp/translations/stories/standalone/xx-serieslanguage-index.txt
4030+++ b/lib/lp/translations/stories/standalone/xx-serieslanguage-index.txt
4031@@ -41,7 +41,7 @@ informed about this fact and will be able to add translations without
4032 requiring a review.
4033
4034 >>> user_browser.open(
4035- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/es')
4036+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es')
4037 >>> print(extract_text(
4038 ... find_tag_by_id(user_browser.contents, 'group-team-info')))
4039 There is no translation group to manage Ubuntu translations.
4040@@ -79,10 +79,10 @@ Evolution Spanish templates can be accessed from the distribution series
4041 translation page.
4042
4043 >>> user_browser.open(
4044- ... 'http://translations.launchpad.test/ubuntu/hoary/')
4045+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/')
4046 >>> user_browser.getLink('Spanish').click()
4047 >>> print(user_browser.url)
4048- http://translations.launchpad.test/ubuntu/hoary/+lang/es
4049+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es
4050
4051 >>> print(extract_text(
4052 ... find_tag_by_id(user_browser.contents, 'group-team-info')))
4053@@ -108,10 +108,10 @@ there is no one to review the work, authenticated users can not add
4054 suggestions.
4055
4056 >>> user_browser.open(
4057- ... 'http://translations.launchpad.test/ubuntu/hoary/')
4058+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/')
4059 >>> user_browser.getLink('Catalan').click()
4060 >>> print(user_browser.url)
4061- http://translations.launchpad.test/ubuntu/hoary/+lang/ca
4062+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ca
4063
4064 >>> print(extract_text(
4065 ... find_tag_by_id(user_browser.contents, 'group-team-info')))
4066@@ -131,7 +131,7 @@ Members of translation team and translations admins have full access to
4067 translations. They can add and review translations.
4068
4069 >>> admin_browser.open(
4070- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/ro')
4071+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ro')
4072 >>> print(extract_text(find_tag_by_id(
4073 ... admin_browser.contents, 'translation-access-level')))
4074 You can add and review translations...
4075@@ -145,7 +145,7 @@ be allowed to make any changes.
4076 >>> logout()
4077
4078 >>> user_browser.open(
4079- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/ro')
4080+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ro')
4081 >>> print(extract_text(find_tag_by_id(
4082 ... user_browser.contents, 'translation-access-level')))
4083 These templates can be translated only by their managers...
4084@@ -162,7 +162,7 @@ translations, and will see a link to the licence page.
4085 >>> no_license_browser = setupBrowser(
4086 ... auth='Basic dude@ex.com:test')
4087 >>> no_license_browser.open(
4088- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/ro')
4089+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ro')
4090 >>> print(extract_text(find_tag_by_id(
4091 ... no_license_browser.contents, 'translation-access-level')))
4092 To make translations in Launchpad you need to agree with
4093@@ -179,7 +179,7 @@ this fact. No access level information is displayed.
4094 >>> logout()
4095
4096 >>> user_browser.open(
4097- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/ro')
4098+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ro')
4099 >>> print(extract_text(
4100 ... find_tag_by_id(user_browser.contents, 'group-team-info')))
4101 There is no translation group to manage Ubuntu translations.
4102diff --git a/lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt b/lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt
4103index 04994c8..5591ab6 100644
4104--- a/lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt
4105+++ b/lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt
4106@@ -16,12 +16,13 @@ Mark is a qualified user.
4107
4108 >>> browser = setupBrowser(auth='Basic mark@example.com:test')
4109 >>> browser.open(
4110- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/mozilla/')
4111+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4112+ ... 'mozilla/')
4113 >>> download = browser.getLink('download a full tarball')
4114 >>> download_url = download.url
4115 >>> download.click()
4116 >>> print(browser.url)
4117- http://translations.launchpad.test/ubuntu/hoary/+source/mozilla/+export
4118+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/mozilla/+export
4119
4120
4121 Authorization
4122@@ -43,7 +44,7 @@ reasonable level.
4123 ... """
4124 ... browser.open(
4125 ... 'http://translations.launchpad.test/'
4126- ... 'ubuntu/hoary/+source/mozilla/')
4127+ ... 'ubuntu/+series/hoary/+source/mozilla/')
4128 ... try:
4129 ... browser.getLink('download a full tarball').click()
4130 ... except LinkNotFoundError:
4131@@ -164,14 +165,14 @@ format, and request the download.
4132
4133 >>> browser.open(
4134 ... 'http://translations.launchpad.test/'
4135- ... 'ubuntu/hoary/+source/mozilla/+export')
4136+ ... 'ubuntu/+series/hoary/+source/mozilla/+export')
4137 >>> browser.title
4138 'Download : Hoary (5.04) : Translations : mozilla package : Ubuntu'
4139
4140 >>> browser.getControl('Request Download').click()
4141
4142 >>> print(browser.url)
4143- http://translations.launchpad.test/ubuntu/hoary/+source/mozilla
4144+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/mozilla
4145
4146 >>> print_feedback_messages(browser.contents)
4147 Your request has been received. Expect to receive an email shortly.
4148@@ -202,7 +203,7 @@ there before.
4149
4150 >>> browser.open(
4151 ... 'http://translations.launchpad.test/'
4152- ... 'ubuntu/hoary/+source/evolution/+export')
4153+ ... 'ubuntu/+series/hoary/+source/evolution/+export')
4154 >>> print_feedback_messages(browser.contents)
4155
4156 >>> an_evolution_template.source_file_format = TranslationFileFormat.MO
4157@@ -210,7 +211,7 @@ there before.
4158
4159 >>> browser.open(
4160 ... 'http://translations.launchpad.test/'
4161- ... 'ubuntu/hoary/+source/evolution/+export')
4162+ ... 'ubuntu/+series/hoary/+source/evolution/+export')
4163 >>> print_feedback_messages(browser.contents)
4164 This package has templates with different native file formats. If you
4165 proceed, all translations will be exported in the single format you
4166diff --git a/lib/lp/translations/stories/standalone/xx-test-potlists.txt b/lib/lp/translations/stories/standalone/xx-test-potlists.txt
4167index 67ea105..00a338f 100644
4168--- a/lib/lp/translations/stories/standalone/xx-test-potlists.txt
4169+++ b/lib/lp/translations/stories/standalone/xx-test-potlists.txt
4170@@ -1,7 +1,7 @@
4171 Check that we can get a potlist for a source pacakge that has potemplates:
4172
4173 >>> print(http(br"""
4174- ... GET /ubuntu/hoary/+source/evolution/+potlist HTTP/1.1
4175+ ... GET /ubuntu/+series/hoary/+source/evolution/+potlist HTTP/1.1
4176 ... Host: translations.launchpad.test
4177 ... """))
4178 HTTP/1.1 200 Ok
4179diff --git a/lib/lp/translations/stories/standalone/xx-translation-help.txt b/lib/lp/translations/stories/standalone/xx-translation-help.txt
4180index 4deb2bf..93aaae4 100644
4181--- a/lib/lp/translations/stories/standalone/xx-translation-help.txt
4182+++ b/lib/lp/translations/stories/standalone/xx-translation-help.txt
4183@@ -16,7 +16,7 @@ pages. Namely, on a Distribution and DistroSeries pages:
4184 >>> browser.getLink(id='link-to-translations-help').url
4185 'https://help.launchpad.net/Translations'
4186
4187- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary')
4188+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary')
4189 >>> browser.getLink(id='link-to-translations-help').url
4190 'https://help.launchpad.net/Translations'
4191
4192diff --git a/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt b/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
4193index e7fb942..80c11cf 100644
4194--- a/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
4195+++ b/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
4196@@ -22,7 +22,7 @@ First, we need to be sure that anonymous users are able to browse
4197 translations but are unable to actually change them.
4198
4199 >>> browser.open(
4200- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4201+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4202 ... 'evolution/+pots/evolution-2.2/es/5')
4203
4204 We are in read only mode, so there shouldn't be any textareas:
4205@@ -64,7 +64,7 @@ The main page for a pomsgset object should redirect us to the
4206 translation form.
4207
4208 >>> browser.open(
4209- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4210+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4211 ... 'evolution/+pots/evolution-2.2/es/1')
4212
4213 When we are on the first message, we should be 100% sure that the
4214@@ -72,7 +72,7 @@ When we are on the first message, we should be 100% sure that the
4215 right ones.
4216
4217 >>> browser.open(
4218- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4219+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4220 ... 'evolution/+pots/evolution-2.2/es/1/+translate')
4221
4222 >>> browser.getLink('First')
4223@@ -183,7 +183,7 @@ All those links should linked the proper pages
4224 Now, we are going to check a message submission.
4225
4226 >>> browser.open(
4227- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4228+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4229 ... 'evolution/+pots/evolution-2.2/es/13/+translate')
4230
4231 Check that the message #13 is without translation.
4232@@ -273,7 +273,7 @@ We moved to the next message, that means this submission worked.
4233 Now, it has the submitted value.
4234
4235 >>> browser.open(
4236- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4237+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4238 ... 'evolution/+pots/evolution-2.2/es/13/+translate')
4239
4240 Check that the message #13 has the new value we submitted.
4241@@ -306,7 +306,7 @@ In some other cases where translator and reviewer are different, they
4242 are both shown separately:
4243
4244 >>> browser.open(
4245- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4246+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4247 ... 'evolution/+pots/man/es/1/+translate')
4248 >>> find_tag_by_id(browser.contents, 'translated_and_reviewed_by') is None
4249 True
4250@@ -320,7 +320,7 @@ are both shown separately:
4251 Now, we will check suggestions in this form.
4252
4253 >>> browser.open(
4254- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4255+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4256 ... 'evolution/+pots/evolution-2.2/es/14/+translate')
4257
4258 Check that suggestions come in from other contexts:
4259@@ -358,9 +358,9 @@ If we specify more than one alternative language in the URL, we get an
4260 UnexpectedFormData exception:
4261
4262 >>> browser.open(
4263- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
4264- ... '+pots/evolution-2.2/es/14/+translate?field.alternative_language=ca&'
4265- ... 'field.alternative_language=es')
4266+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4267+ ... 'evolution/+pots/evolution-2.2/es/14/+translate'
4268+ ... '?field.alternative_language=ca&field.alternative_language=es')
4269 Traceback (most recent call last):
4270 ...
4271 UnexpectedFormData: You specified...
4272@@ -373,7 +373,7 @@ the changes.
4273
4274 >>> slow_submission = setupBrowser(auth='Basic carlos@canonical.com:test')
4275 >>> slow_submission.open(
4276- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4277+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4278 ... 'evolution/+pots/evolution-2.2/es/14/+translate')
4279 >>> import transaction
4280 >>> transaction.commit()
4281@@ -383,7 +383,7 @@ Now, we get another instance that will be submitted before
4282
4283 >>> fast_submission = setupBrowser(auth='Basic carlos@canonical.com:test')
4284 >>> fast_submission.open(
4285- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4286+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4287 ... 'evolution/+pots/evolution-2.2/es/14/+translate')
4288
4289 Let's change the translation.
4290@@ -475,8 +475,9 @@ If there is a message which has a translation, but no reviewer (eg.
4291 uploaded from a package), it only shows the translator, and not
4292 reviewer.
4293
4294- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/+source/'
4295- ... 'mozilla/+pots/pkgconf-mozilla/de/1/+translate')
4296+ >>> browser.open(
4297+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4298+ ... 'mozilla/+pots/pkgconf-mozilla/de/1/+translate')
4299 >>> print(extract_text(
4300 ... find_tag_by_id(browser.contents, "translated_by").parent))
4301 Translated by Helge Kreutzmann on 2005-05-06
4302@@ -530,8 +531,9 @@ suggestions, even if we keep them to know when were they deactivated.
4303
4304 Initially, a message has a non-empty packaged translation.
4305
4306- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
4307- ... '+source/evolution/+pots/evolution-2.2/es/5/+translate')
4308+ >>> browser.open(
4309+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4310+ ... '+source/evolution/+pots/evolution-2.2/es/5/+translate')
4311 >>> packaged = find_tag_by_id(browser.contents, 'msgset_134_other')
4312 >>> print(extract_text(packaged))
4313 In upstream: tarjetas
4314@@ -583,8 +585,9 @@ We replace it with an empty, imported translation:
4315 If we browse to the page for this message, we won't be able to see a
4316 packaged translation anymore.
4317
4318- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
4319- ... '+source/evolution/+pots/evolution-2.2/es/5/+translate')
4320+ >>> browser.open(
4321+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4322+ ... '+source/evolution/+pots/evolution-2.2/es/5/+translate')
4323 >>> packaged = find_tag_by_id(browser.contents, 'msgset_134_other')
4324
4325 Also, the page now displays a "(not translated yet)" message.
4326diff --git a/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt b/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
4327index f4b2b55..c2dbcbd 100644
4328--- a/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
4329+++ b/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
4330@@ -750,7 +750,7 @@ Southern Sotho. We expect them to see a readonly form:
4331 ... 'Authorization', 'Basic no-priv@canonical.com:test')
4332 >>> browser.open(
4333 ... 'http://translations.launchpad.test/'
4334- ... 'ubuntu/hoary/+source/evolution/'
4335+ ... 'ubuntu/+series/hoary/+source/evolution/'
4336 ... '+pots/evolution-2.2/st/+translate')
4337 >>> print(browser.url)
4338 http://.../ubuntu/.../evolution/+pots/evolution-2.2/st/+translate
4339@@ -772,7 +772,7 @@ directly.
4340
4341 >>> browser.open(
4342 ... 'http://translations.launchpad.test/'
4343- ... 'ubuntu/hoary/+source/evolution/'
4344+ ... 'ubuntu/+series/hoary/+source/evolution/'
4345 ... '+pots/evolution-2.2/cy/19/+translate')
4346 >>> print(browser.url)
4347 http://.../ubuntu/.../evolution/+pots/evolution-2.2/cy/19/+translate
4348@@ -863,7 +863,7 @@ suggestions.
4349
4350 >>> browser.open(
4351 ... 'http://translations.launchpad.test/'
4352- ... 'ubuntu/hoary/+source/evolution/'
4353+ ... 'ubuntu/+series/hoary/+source/evolution/'
4354 ... '+pots/evolution-2.2/')
4355
4356 >>> print_menu_option(browser.contents, 'edit')
4357@@ -874,7 +874,7 @@ suggestions.
4358
4359 >>> browser.open(
4360 ... 'http://translations.launchpad.test/'
4361- ... 'ubuntu/hoary/+source/evolution/'
4362+ ... 'ubuntu/+series/hoary/+source/evolution/'
4363 ... '+pots/evolution-2.2/st/+translate')
4364
4365 >>> print(find_translation_input_label(browser.contents))
4366@@ -906,7 +906,7 @@ will only accept suggestions.
4367
4368 >>> browser.open(
4369 ... 'http://translations.launchpad.test/'
4370- ... 'ubuntu/hoary/+source/evolution/'
4371+ ... 'ubuntu/+series/hoary/+source/evolution/'
4372 ... '+pots/evolution-2.2/st/+translate')
4373
4374 >>> print(find_translation_input_label(browser.contents))
4375@@ -946,7 +946,7 @@ as well as to upload files.
4376
4377 >>> browser.open(
4378 ... 'http://translations.launchpad.test/'
4379- ... 'ubuntu/hoary/+source/evolution/'
4380+ ... 'ubuntu/+series/hoary/+source/evolution/'
4381 ... '+pots/evolution-2.2/cy/+translate')
4382
4383 >>> print_menu_option(browser.contents, 'upload')
4384@@ -957,7 +957,7 @@ not yet translated.
4385
4386 >>> browser.open(
4387 ... 'http://translations.launchpad.test/'
4388- ... 'ubuntu/hoary/+source/evolution/'
4389+ ... 'ubuntu/+series/hoary/+source/evolution/'
4390 ... '+pots/evolution-2.2/cy/8/+translate')
4391
4392 >>> print(get_detail_tag(browser, 'translation-managers'))
4393@@ -1079,8 +1079,8 @@ First, make sure we can see the page.
4394 Try to get the page when unauthenticated.
4395
4396 >>> browser.open(
4397- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/' +
4398- ... 'evolution/+pots/evolution-2.2/af/+upload')
4399+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4400+ ... 'evolution/+pots/evolution-2.2/af/+upload')
4401 Traceback (most recent call last):
4402 ...
4403 Unauthorized:...
4404@@ -1088,17 +1088,17 @@ Try to get the page when unauthenticated.
4405 And now with valid credentials.
4406
4407 >>> admin_browser.open(
4408- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/' +
4409- ... 'evolution/+pots/evolution-2.2/af/+upload')
4410+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4411+ ... 'evolution/+pots/evolution-2.2/af/+upload')
4412 >>> print(admin_browser.url)
4413- http://.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4414+ http://.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4415
4416 Now hit the upload button, but without giving a file for upload. We get
4417 an error message back.
4418
4419 >>> admin_browser.getControl('Upload').click()
4420 >>> print(admin_browser.url)
4421- http://.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4422+ http://.../ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4423
4424 >>> for tag in find_tags_by_class(admin_browser.contents, 'error'):
4425 ... print(tag.renderContents())
4426@@ -1138,7 +1138,7 @@ cannot be handled.
4427 >>> upload.add_file(BytesIO(af_file), 'application/msword', 'af.doc')
4428 >>> admin_browser.getControl('Upload').click()
4429 >>> print(admin_browser.url)
4430- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4431+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4432
4433 >>> for tag in find_tags_by_class(admin_browser.contents, 'error'):
4434 ... print(tag.renderContents())
4435@@ -1151,7 +1151,7 @@ With all the correct information, a file can be uploaded.
4436 >>> upload.add_file(BytesIO(af_file), 'application/x-po', 'af.po')
4437 >>> admin_browser.getControl('Upload').click()
4438 >>> print(admin_browser.url)
4439- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4440+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/af/+upload
4441
4442 >>> for tag in find_tags_by_class(admin_browser.contents, 'message'):
4443 ... print(tag.renderContents())
4444@@ -1197,7 +1197,7 @@ Let's add a new suggestion as a person without privileges.
4445 >>> browser.addHeader("Authorization", "Basic no-priv@canonical.com:test")
4446 >>> browser.open(
4447 ... 'http://translations.launchpad.test/'
4448- ... 'ubuntu/hoary/+source/evolution/'
4449+ ... 'ubuntu/+series/hoary/+source/evolution/'
4450 ... '+pots/evolution-2.2/es/+translate')
4451 >>> browser.getControl(
4452 ... name='msgset_134_es_translation_0_new_checkbox').value = True
4453diff --git a/lib/lp/translations/stories/translations/xx-translations.txt b/lib/lp/translations/stories/translations/xx-translations.txt
4454index f2cd87d..292b51a 100644
4455--- a/lib/lp/translations/stories/translations/xx-translations.txt
4456+++ b/lib/lp/translations/stories/translations/xx-translations.txt
4457@@ -5,7 +5,7 @@ First, we need to define a function to see that the message we are
4458 interested on doesn't have any translation.
4459
4460 >>> user_browser.open(
4461- ... "http://translations.launchpad.test/ubuntu/hoary/+source/"
4462+ ... "http://translations.launchpad.test/ubuntu/+series/hoary/+source/"
4463 ... "evolution/+pots/evolution-2.2/es/+translate?start=20")
4464
4465 We are going to change message #21, but first, we see that this messages
4466@@ -91,14 +91,14 @@ First, we need to ensure that we can see the distroseries translations
4467 page, and that it has all the data we are expecting, in terms of languages.
4468
4469 >>> from lp.testing.pages import extract_url_parameter
4470- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
4471+ >>> browser.open('http://translations.launchpad.test/ubuntu/+series/hoary/'
4472 ... '+translations')
4473 >>> b'Translation status by language' in browser.contents
4474 True
4475 >>> print(browser.getLink('Catalan').url)
4476- http://translations.launchpad.test/ubuntu/hoary/+lang/ca
4477+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ca
4478 >>> print(browser.getLink('Xhosa').url)
4479- http://translations.launchpad.test/ubuntu/hoary/+lang/xh
4480+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/xh
4481 >>> browser.getLink('Afrihili')
4482 Traceback (most recent call last):
4483 ...
4484@@ -110,16 +110,17 @@ our browser speaks Afrihili, and since the user is anonymous the system will
4485 put Afrihili into the list of "preferred languages".
4486
4487 >>> browser.addHeader('Accept-Language', 'en-us,en;q=0.7,afh;q=0.3')
4488- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'
4489+ >>> browser.open(
4490+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4491 ... '+translations')
4492 >>> b'Translation status by language' in browser.contents
4493 True
4494 >>> print(browser.getLink('Catalan').url)
4495- http://translations.launchpad.test/ubuntu/hoary/+lang/ca
4496+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/ca
4497 >>> print(browser.getLink('Xhosa').url)
4498- http://translations.launchpad.test/ubuntu/hoary/+lang/xh
4499+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/xh
4500 >>> print(browser.getLink('Afrihili').url)
4501- http://translations.launchpad.test/ubuntu/hoary/+lang/afh
4502+ http://translations.launchpad.test/ubuntu/+series/hoary/+lang/afh
4503
4504 If we select Croatian, we would expect to see the list of source package
4505 templates, and in the sample data we should have a croatian pofile for
4506@@ -127,15 +128,16 @@ pmount. Note that we should also have an empty pofile (really a dummy
4507 pofile) for evolution-2.2
4508
4509 >>> browser.open(
4510- ... 'http://translations.launchpad.test/ubuntu/hoary/+lang/hr?batch=2')
4511+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/hr'
4512+ ... '?batch=2')
4513 >>> b'Croatian' in browser.contents
4514 True
4515 >>> b'Translatable templates' in browser.contents
4516 True
4517 >>> print(browser.getLink('evolution-2.2').url)
4518- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/hr/+translate
4519+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/hr/+translate
4520 >>> print(browser.getLink('man').url)
4521- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/man/hr/+translate
4522+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/man/hr/+translate
4523
4524 pmount and pkgconf-mozilla are not in this page, because it belongs to the next batch.
4525
4526@@ -155,9 +157,9 @@ Let's go to next page.
4527 Now, we have the other man and pkgconf-mozilla:
4528
4529 >>> print(browser.getLink('man').url)
4530- http://translations.launchpad.test/ubuntu/hoary/+source/pmount/+pots/man/hr/+translate
4531+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/pmount/+pots/man/hr/+translate
4532 >>> print(browser.getLink('pkgconf-mozilla').url)
4533- http://translations.launchpad.test/ubuntu/hoary/+source/mozilla/+pots/pkgconf-mozilla/hr/+translate
4534+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/mozilla/+pots/pkgconf-mozilla/hr/+translate
4535
4536 Let's go to next page.
4537
4538@@ -166,7 +168,7 @@ Let's go to next page.
4539 And finally, we will get pmount.
4540
4541 >>> print(browser.getLink('pmount').url)
4542- http://translations.launchpad.test/ubuntu/hoary/+source/pmount/+pots/pmount/hr/+translate
4543+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/pmount/+pots/pmount/hr/+translate
4544
4545 With its latest translator.
4546
4547@@ -179,7 +181,8 @@ When there's no reviewer and date of review on a last touched PO message
4548 inside a PO file (for example, when it was uploaded from the package), the
4549 last translator is displayed.
4550
4551- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/+lang/de')
4552+ >>> browser.open(
4553+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/de')
4554 >>> print(extract_text(
4555 ... find_tag_by_id(browser.contents, "pkgconf-mozilla-time")))
4556 2005-05-06
4557@@ -194,8 +197,8 @@ decided to start contributing translations to that package.
4558
4559 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
4560 >>> browser.open(
4561- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/pmount/'
4562- ... '+pots/pmount/pt_BR/+translate')
4563+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4564+ ... 'pmount/+pots/pmount/pt_BR/+translate')
4565
4566 Then he decides that he only wants to filter on untranslated entries (he's
4567 not aware that this translation is actually empty, i.e. there is no such PO
4568@@ -233,7 +236,8 @@ Looking at the Spanish language overview page, we can see that there are
4569 15 untranslated, 1 unreviewed and 1 changed in Ubuntu Evolution translations
4570 (all numbers repeated as hidden 'sortkey' values).
4571
4572- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/+lang/es')
4573+ >>> browser.open(
4574+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+lang/es')
4575 >>> evolution_line = find_tag_by_id(browser.contents, 'evolution-2.2')
4576 >>> print(extract_text(evolution_line))
4577 evolution-2.2
4578@@ -252,7 +256,7 @@ The template title points to the general translate page:
4579 >>> print(extract_text(unfiltered))
4580 evolution-2.2
4581 >>> print(extract_link_from_tag(unfiltered, base_href))
4582- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate
4583+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate
4584
4585 The number of untranslated entries points to the same page, which now show
4586 only untranslated items. When we follow this link, the filtering combo box
4587@@ -264,7 +268,7 @@ has the right filter preselected.
4588 >>> untranslated_link = extract_link_from_tag(untranslated, base_href)
4589 >>> browser.open(untranslated_link.encode('UTF-8'))
4590 >>> browser.url
4591- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=untranslated'
4592+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=untranslated'
4593 >>> print(browser.getControl(name='show', index=1).value)
4594 ['untranslated']
4595
4596@@ -277,7 +281,7 @@ with the 'with new suggestions' filter selected.
4597 >>> unreviewed_link = extract_link_from_tag(unreviewed, base_href)
4598 >>> browser.open(unreviewed_link.encode('UTF-8'))
4599 >>> browser.url
4600- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=new_suggestions'
4601+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=new_suggestions'
4602 >>> print(browser.getControl(name='show', index=1).value)
4603 ['new_suggestions']
4604
4605@@ -290,7 +294,7 @@ The number of updated entries points to the translation page with the
4606 >>> updated_link = extract_link_from_tag(updated, base_href)
4607 >>> browser.open(updated_link.encode('UTF-8'))
4608 >>> browser.url
4609- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=changed_in_ubuntu'
4610+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=changed_in_ubuntu'
4611 >>> print(browser.getControl(name='show', index=1).value)
4612 ['changed_in_ubuntu']
4613
4614@@ -327,16 +331,16 @@ will again see the legend.
4615 The same happens for template overview page for packages.
4616
4617 >>> anon_browser.open(
4618- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
4619- ... '+translations')
4620+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4621+ ... 'evolution/+translations')
4622 >>> find_tag_by_id(anon_browser.contents, 'legend') is None
4623 True
4624
4625 And with at least one translation, legend is shown.
4626
4627 >>> anon_browser.open(
4628- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
4629- ... '+pots/man')
4630+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4631+ ... 'evolution/+pots/man')
4632 >>> find_tag_by_id(anon_browser.contents, 'legend') is None
4633 False
4634
4635@@ -359,8 +363,8 @@ And likewise for PO template pages for templates without translations
4636 in packages:
4637
4638 >>> anon_browser.open(
4639- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/pmount/'
4640- ... '+pots/man')
4641+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4642+ ... 'pmount/+pots/man')
4643 >>> find_tag_by_id(anon_browser.contents, 'legend') is None
4644 True
4645
4646@@ -374,7 +378,7 @@ should see Catalan in the list.
4647
4648 >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test')
4649 >>> browser.open(
4650- ... 'http://translations.launchpad.test/ubuntu/hoary/+source/'
4651+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/'
4652 ... 'evolution/+translations')
4653 >>> b'Catalan' in browser.contents
4654 True
4655@@ -406,8 +410,9 @@ Looking at the POTemplate overview page, we can see that there are
4656 15 untranslated, 1 unreviewed and 1 changed in Ubuntu Spanish translations
4657 (all numbers repeated as hidden 'sortkey' values).
4658
4659- >>> browser.open('http://translations.launchpad.test/ubuntu/hoary/'+
4660- ... '+source/evolution/+pots/evolution-2.2')
4661+ >>> browser.open(
4662+ ... 'http://translations.launchpad.test/ubuntu/+series/hoary/'
4663+ ... '+source/evolution/+pots/evolution-2.2')
4664 >>> spanish_line = find_tag_by_id(browser.contents, 'evolution-2.2_es')
4665 >>> print(extract_text(spanish_line))
4666 Spanish
4667@@ -425,7 +430,7 @@ Language title points to the general translate page:
4668 >>> print(extract_text(unfiltered))
4669 Spanish
4670 >>> print(extract_link_from_tag(unfiltered, base_href))
4671- http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate
4672+ http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate
4673
4674 The number of untranslated entries points to the same page, which now shows
4675 only untranslated items. When we follow this link, the filtering combo box has
4676@@ -437,7 +442,7 @@ the right filter preselected.
4677 >>> untranslated_link = extract_link_from_tag(untranslated, base_href)
4678 >>> browser.open(untranslated_link.encode('UTF-8'))
4679 >>> browser.url
4680- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=untranslated'
4681+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=untranslated'
4682 >>> print(browser.getControl(name='show', index=2).value)
4683 untranslated
4684
4685@@ -450,7 +455,7 @@ with the 'with new suggestions' filter selected.
4686 >>> unreviewed_link = extract_link_from_tag(unreviewed, base_href)
4687 >>> browser.open(unreviewed_link.encode('UTF-8'))
4688 >>> browser.url
4689- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=new_suggestions'
4690+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=new_suggestions'
4691 >>> print(browser.getControl(name='show', index=2).value)
4692 new_suggestions
4693
4694@@ -463,6 +468,6 @@ The number of updated entries points to the translation page with the
4695 >>> updated_link = extract_link_from_tag(updated, base_href)
4696 >>> browser.open(updated_link.encode('UTF-8'))
4697 >>> browser.url
4698- 'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=changed_in_ubuntu'
4699+ 'http://translations.launchpad.test/ubuntu/+series/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?show=changed_in_ubuntu'
4700 >>> print(browser.getControl(name='show', index=2).value)
4701 changed_in_ubuntu
4702diff --git a/lib/lp/translations/stories/webservice/xx-potemplate.txt b/lib/lp/translations/stories/webservice/xx-potemplate.txt
4703index 032ab5b..2cd4681 100644
4704--- a/lib/lp/translations/stories/webservice/xx-potemplate.txt
4705+++ b/lib/lp/translations/stories/webservice/xx-potemplate.txt
4706@@ -9,7 +9,7 @@ Anonymous users have read access to PO templates attributes.
4707
4708 >>> from lazr.restful.testing.webservice import pprint_entry
4709 >>> potemplate = anon_webservice.get(
4710- ... '/ubuntu/hoary/+source/pmount/+pots/pmount').jsonBody()
4711+ ... '/ubuntu/+series/hoary/+source/pmount/+pots/pmount').jsonBody()
4712 >>> pprint_entry(potemplate)
4713 active: True
4714 date_last_updated: '2005-05-06T20:09:23.775993+00:00'
4715@@ -24,11 +24,12 @@ Anonymous users have read access to PO templates attributes.
4716 path: 'po/template.pot'
4717 priority: 0
4718 resource_type_link: 'http://.../#translation_template'
4719- self_link: 'http://.../ubuntu/hoary/+source/pmount/+pots/pmount'
4720+ self_link: 'http://.../ubuntu/+series/hoary/+source/pmount/+pots/pmount'
4721 translation_domain: 'pmount'
4722 translation_files_collection_link:
4723 'http://.../pmount/+pots/pmount/translation_files'
4724- web_link: 'http://translati.../ubuntu/hoary/+source/pmount/+pots/pmount'
4725+ web_link:
4726+ 'http://translati.../ubuntu/+series/hoary/+source/pmount/+pots/pmount'
4727
4728 "translation_files" will list all POFiles associated with this template.
4729
4730@@ -55,7 +56,7 @@ All templates associated to a distribution series are available from the
4731 >>> db_count = len(list(templates))
4732 >>> logout()
4733 >>> all_translation_templates = anon_webservice.named_get(
4734- ... '/ubuntu/hoary/', 'getTranslationTemplates').jsonBody()
4735+ ... '/ubuntu/+series/hoary/', 'getTranslationTemplates').jsonBody()
4736 >>> api_count = all_translation_templates['total_size']
4737 >>> api_count == db_count
4738 True
4739@@ -111,7 +112,7 @@ All translation templates for a source package are available using the
4740 >>> db_count = len(list(templates))
4741 >>> logout()
4742 >>> all_translation_templates = anon_webservice.named_get(
4743- ... '/ubuntu/hoary/+source/evolution',
4744+ ... '/ubuntu/+series/hoary/+source/evolution',
4745 ... 'getTranslationTemplates').jsonBody()
4746 >>> api_count = all_translation_templates['total_size']
4747 >>> api_count == db_count

Subscribers

People subscribed via source and target branches

to status/vote changes: