Merge lp:~sinzui/launchpad/launchpad-header-0 into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Paul Hummer
Approved revision: no longer in the source branch.
Merged at revision: 11229
Proposed branch: lp:~sinzui/launchpad/launchpad-header-0
Merge into: lp:launchpad
Diff against target: 1273 lines (+397/-542)
13 files modified
lib/canonical/launchpad/doc/hierarchical-menu.txt (+20/-14)
lib/canonical/launchpad/icing/style-3-0.css.in (+28/-13)
lib/lp/app/browser/tests/base-layout.txt (+1/-280)
lib/lp/app/browser/tests/test_base_layout.py (+163/-0)
lib/lp/app/templates/base-layout-macros.pt (+1/-1)
lib/lp/app/templates/base-layout.pt (+18/-13)
lib/lp/app/templates/launchpad-hierarchy.pt (+4/-4)
lib/lp/blueprints/stories/sprints/05-sprint-creation.txt (+99/-117)
lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt (+4/-4)
lib/lp/bugs/stories/bugs/xx-malone-homepage.txt (+10/-18)
lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt (+4/-11)
lib/lp/bugs/stories/cve/cve-pages.txt (+40/-65)
lib/lp/bugs/templates/bugtask-index.pt (+5/-2)
To merge this branch: bzr merge lp:~sinzui/launchpad/launchpad-header-0
Reviewer Review Type Date Requested Status
Paul Hummer (community) ui Approve
Brad Crittenden (community) code Approve
Robert Collins (community) Abstain
Review via email: mp+30570@code.launchpad.net

Description of the change

This is my branch to fix the launchpad header so for the title edit widget.
This branch fixes a number of visual issues in the header, but in doing so,
it call attention to the registered slot. Michael and I decided that the
registration slot is out of scope for this branch and the probable solution
is to move it, which requires a reexamination of all its uses.

    lp:~sinzui/launchpad/owner-driver-supervisor
    Diff size: 643
    Launchpad bug: https://bugs.launchpad.net/bugs/538028
    Test command: ./bin/test -vv -t test_base_layout -t base-layout
    Pre-implementation: noodles
    Target release: 10.08

Fix the launchpad header so for the title edit widget
-----------------------------------------------------

The floated left and right instructions in the launchpad header break
the layout of the title edit widget; The widget disassembles itself trying
to avoid the floated content.

Rules
-----

    * Consider using inline-block CSS and relative positioning to fix the
      issue.

    Michael suggested:
    * ADDENDUM: Fix the leading white space before the watermark.
    * ADDENDUM: Align the apps with the bottom of the icon so that there is
      no extra white space between the context, apps, heading and bead crumbs.

    Curtis:
    * Michael's suggestions are very good. They make other problems in the
      header more obvious. The registration slot is taking advantage of the
      unwanted white space. It will be obvious that it was an after thought
      when it creates an extra line. If it is clear that the apps are the
      second part of the header, it may be clear to some users they appear
      in the wrong place in the bread crumbs.

    * ADDENDUM: Move the check for bread crumbs to the list element so that
      no part of the block is rendered when there is no bread crumbs.

QA
--

    * Visit https://edge.launchpad.net/launchpad
    * Choose the edit the title
    * Verify the widget stays together and remains adjacent to the logo.
    * Visit https://bugs.edge.launchpad.net/launchpad-web/+bug/538028
    * Verify the context, apps, title, and bread crumbs are together...
      the reported by line does not interrupt.
    * Visit https://bugs.edge.launchpad.net/launchpad-web
    * Verify the context, apps, title, and bread crumbs are together

Lint
----

Linting changed files:
  lib/canonical/launchpad/icing/style-3-0.css.in
  lib/lp/app/browser/tests/base-layout.txt
  lib/lp/app/browser/tests/test_base_layout.py
  lib/lp/app/templates/base-layout-macros.pt
  lib/lp/app/templates/base-layout.pt
  lib/lp/app/templates/launchpad-hierarchy.pt

Test
----

    * lib/lp/app/browser/tests/base-layout.txt
      * Removed big chunks of a very brittle test that broke from the minor
        markup changes. The diff shows the tests for common parts were
        different, but they where/should be the same. I decided to move
        the testing to a unittest. Note that the diff /before/ my decision to
        to replace the tests was long and hard to read.
    * lib/lp/app/browser/tests/test_base_layout.py
      * Moved the tests for the parts that base-layout provides to unittests.
        I should now be obvious when parts each layout provides.

Implementation
--------------

    * lib/canonical/launchpad/icing/style-3-0.css.in
      * Added .flowed-block which allows us to layout horizontal chunks
        of block content like we image it in our heads.
      * Replaced #locationbar with.login-logout which accidentally solved
        Michaels concern about the leading space :)
      * Updated watermark-apps-portlet rules to not wrap and clear any floated
        content so that the title edit widget does not need to avoid other
        parts of the header.
      * Removed the excess whitespace from .registering and #logincontrol
    * lib/lp/app/templates/base-layout-macros.pt
      * Added an id to make the content easy to test.
    * lib/lp/app/templates/base-layout.pt
      * Wrapped the problem code in flowed-block divs.
      * Moved the registration slot below the logo so that there was no
        floated content near the heading that the title edit widget wraps.
      * Added ids to make content testable.
    * lib/lp/app/templates/launchpad-hierarchy.pt
      * Moved the bread crumb test condition to the block element...the page
        was always rendering space for the bread crumbs.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This sounds nice, but I'm not comfortable enough with this part of the system yet to be your reviewer - sorry!

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

These are the three headers Michael and I looked at. They represent the best and worst of how the header works. In all cases the space before the logo and the space without bread crumbs is fixed \o/. The title edit widget hold together since it does not need to dodge the left and right content. By aligning the primary context title and apps with the bottom of the logo, view.png shows what we wanted users to see. But the two context images have a registration line that inserts a blank line. Registration always did this, it was not as obvious.

http://people.canonical.com/~curtis/primary-context.png
http://people.canonical.com/~curtis/title-edit-widget.png
http://people.canonical.com/~curtis/view.png
http://people.canonical.com/~curtis/secondary-context.png

I ponder moving the registation before or after the bread crumbs. We want the registration to be adjacent to the title of the context. This was/is not possible for primary contexts. We rely on the sparceness of the header to imply that the context is the only thing the registration text can pertain to. Placing the registration before the breadcrumbs, a, returns to the orginal design for 3.0, and b, puts the information closer to the primary context logo, and the secondary context title when it exists.

/me hacks to make screen caps.

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

I moved the registration slot into context-location area, it it between the context title and bread crumbs. I made the text the same size as the bread crumbs.

http://people.canonical.com/~curtis/better-primary-context.png
http://people.canonical.com/~curtis/better-title-edit.png
http://people.canonical.com/~curtis/better-view.png
http://people.canonical.com/~curtis/better-secondary-context.png

These changes did not inflate the diff. I updated a test to verify the content moved.

The bug page did look bad, and that is because it violates the registration slot rules...the
data is immutable; it is not a status. I moved the heat inline and updated the one affected
test. Another was robust. I removed the redundant bug number too (no tests broke).
./bin/test -vvc -t bug-heat-view -t xx-bug-heat-on-bug-page

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

Robert, if you claim a review from the team and then abstain the review disappears from the view of other reviewers. To remedy, select 'Request another review' and assign it to the Launchpad Reviewers team. (Sorry for cluttering the MP with these instructions but it makes sense since it provides context.)

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

Curtis I think these changes really clean up the top of the page. The code looks good. Another round of UI review will be beneficial.

This docstring needs a final period:
"""Test for the ILaunchpadRoot permission"""

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

Hi Paul.

In summary. The start of the review says I did not want to fix the registration slot issue, and you can see pictures of how fixing the white space made the problem very obvious. The more recent comments with the better-* set of images shows I decided to move the registration slot anyway. I think this is an improvement, though I would not ever claim it is perfect.

Revision history for this message
Paul Hummer (rockstar) wrote :

This looks REALLY good. Thanks for dealing with it. The header can sometimes be an eyesore, so anything we can do to make it better is something I'm onboard with.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/doc/hierarchical-menu.txt'
--- lib/canonical/launchpad/doc/hierarchical-menu.txt 2010-01-22 17:28:32 +0000
+++ lib/canonical/launchpad/doc/hierarchical-menu.txt 2010-07-26 13:34:58 +0000
@@ -1,15 +1,16 @@
1= Hierarchical menus =1Hierarchical menus
2==================
23
3The location bar aids users in navigating the depths of Launchpad. It4The location bar aids users in navigating the depths of Launchpad. It
4is built from a list of Breadcrumb objects collected during Zope's5is built from a list of Breadcrumb objects collected during Zope's
5object-traversal step.6object-traversal step.
67
7== A simple object hierarchy ==8A simple object hierarchy
9-------------------------
810
9First, we need a hierarchy of objects to build upon:11First, we need a hierarchy of objects to build upon:
1012
11 >>> from zope.component import (getMultiAdapter, provideAdapter,13 >>> from zope.component import getMultiAdapter, provideAdapter
12 ... queryAdapter)
13 >>> from zope.interface import Interface, implements14 >>> from zope.interface import Interface, implements
1415
15 >>> class ICookbook(Interface):16 >>> class ICookbook(Interface):
@@ -56,7 +57,8 @@
56 >>> recipe = Recipe('spam', cookbook)57 >>> recipe = Recipe('spam', cookbook)
5758
5859
59== Discovering breadcrumbs ==60Discovering breadcrumbs
61-----------------------
6062
61The Hierarchy class builds the breadcrumbs by looking at each object in63The Hierarchy class builds the breadcrumbs by looking at each object in
62the request.traversed_objects attribute. If a traversed object can be64the request.traversed_objects attribute. If a traversed object can be
@@ -124,10 +126,11 @@
124 >>> cooker_hierarchy = getMultiAdapter(126 >>> cooker_hierarchy = getMultiAdapter(
125 ... (cooker, cooker_request), name='+hierarchy')127 ... (cooker, cooker_request), name='+hierarchy')
126 >>> cooker_hierarchy.items128 >>> cooker_hierarchy.items
127 [<TextualBreadcrumb url='http://launchpad.dev/+cooker/jamie' text='Jamie'>]129 [<TextualBreadcrumb url='.../+cooker/jamie' text='Jamie'>]
128130
129131
130== Displaying breadcrumbs ==132Displaying breadcrumbs
133----------------------
131134
132Breadcrumbs are only displayed if there is more than one breadcrumb, as135Breadcrumbs are only displayed if there is more than one breadcrumb, as
133otherwise the breadcrumb will simply replicate the context.title heading136otherwise the breadcrumb will simply replicate the context.title heading
@@ -163,7 +166,8 @@
163 False166 False
164167
165168
166== Building IBreadcrumb objects ==169Building IBreadcrumb objects
170----------------------------
167171
168The construction of breadcrumb objects is handled by an IBreadcrumb adapter,172The construction of breadcrumb objects is handled by an IBreadcrumb adapter,
169which adapts a context object and produces an IBreadcrumb object for that173which adapts a context object and produces an IBreadcrumb object for that
@@ -195,7 +199,8 @@
195 text='Joy of cooking'>199 text='Joy of cooking'>
196200
197201
198== Customizing the hierarchy ==202Customizing the hierarchy
203-------------------------
199204
200We can customize the hierarchy itself by changing the list of objects205We can customize the hierarchy itself by changing the list of objects
201and URLs that it uses to construct the breadcrumbs list.206and URLs that it uses to construct the breadcrumbs list.
@@ -216,7 +221,8 @@
216 text='Spam'>]221 text='Spam'>]
217222
218223
219== Rendering the list ==224Rendering the list
225------------------
220226
221The Hierarchy object is responsible for rendering the HTML for the227The Hierarchy object is responsible for rendering the HTML for the
222location bar.228location bar.
@@ -261,5 +267,5 @@
261 >>> homepage_hierarchy.items267 >>> homepage_hierarchy.items
262 []268 []
263269
264 >>> print_hierarchy(homepage_hierarchy.render())270 >>> homepage_hierarchy.render().strip()
265 Location:271 ''
266272
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css.in'
--- lib/canonical/launchpad/icing/style-3-0.css.in 2010-07-22 16:27:12 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-07-26 13:34:58 +0000
@@ -709,6 +709,13 @@
709.batch-navigation-links .next {709.batch-navigation-links .next {
710 margin-right: 0.25em;710 margin-right: 0.25em;
711 }711 }
712.flowed-block {
713 display: table-cell;
714 display: inline-table;
715 display: inline-block;
716 text-align: left;
717 vertical-align: top;
718 }
712719
713720
714/* =========================721/* =========================
@@ -1111,18 +1118,22 @@
1111.demo {1118.demo {
1112 background-color: #fee;1119 background-color: #fee;
1113 }1120 }
1114#locationbar {1121.login-logout {
1115 /* The following style works around the fact that the locationbar, which1122 position: absolute;
1116 is only used of the Login/Register links in 3.0 UI, is accessible on1123 top: .5em;
1117 "locationless" pages. It adds extra space at the top of every page in1124 right: 1.5em;
1118 Launchpad but that does actually look quite good ... ;-).*/
1119 height: 1em;
1120 }1125 }
1121div.watermark-apps-portlet {1126div.watermark-apps-portlet {
1122 padding-bottom:1em;1127 clear: both;
1128 margin-bottom: .5em;
1129 white-space: no-wrap;
1130 }
1131div.watermark-apps-portlet .wide {
1132 width: 75%;
1133 vertical-align: bottom;
1134 margin-bottom: 0.3em;
1123 }1135 }
1124div.watermark-apps-portlet img {1136div.watermark-apps-portlet img {
1125 float: left;
1126 margin: 0 1.5em 0 0;1137 margin: 0 1.5em 0 0;
1127 }1138 }
1128div.watermark-apps-portlet h1, div.watermark-apps-portlet h2 {1139div.watermark-apps-portlet h1, div.watermark-apps-portlet h2 {
@@ -1176,19 +1187,20 @@
1176 width: 100%;1187 width: 100%;
1177 white-space: nowrap;1188 white-space: nowrap;
1178 }1189 }
1190.context-publication {
1191 margin-bottom: 1em;
1192 }
1179.registering {1193.registering {
1180 /* Registered slot */1194 /* Registered slot */
1181 float: right;1195 margin: 0 0 0.2em 0;
1182 font-size: 85%;1196 font-style: italic;
1197 font-size: 77%;
1183 color: #666;1198 color: #666;
1184 position: relative;
1185 top: 2em;
1186 }1199 }
1187.breadcrumbs {1200.breadcrumbs {
1188 margin-left: 0;1201 margin-left: 0;
1189 list-style-type: none;1202 list-style-type: none;
1190 clear: both;1203 clear: both;
1191 margin-bottom: 2em;
1192 font-size: 77%;1204 font-size: 77%;
1193 }1205 }
1194.breadcrumbs li {1206.breadcrumbs li {
@@ -1217,6 +1229,9 @@
1217 /* align the image with the text */1229 /* align the image with the text */
1218 margin-bottom: -2px;1230 margin-bottom: -2px;
1219 }1231 }
1232#logincontrol form {
1233 margin: 0;
1234 }
1220#logincontrol input[type='submit'] {1235#logincontrol input[type='submit'] {
1221 /* The button lacks the right margin that buttons usually have: */1236 /* The button lacks the right margin that buttons usually have: */
1222 font-size: 77%;1237 font-size: 77%;
12231238
=== modified file 'lib/lp/app/browser/tests/base-layout.txt'
--- lib/lp/app/browser/tests/base-layout.txt 2010-07-08 15:26:09 +0000
+++ lib/lp/app/browser/tests/base-layout.txt 2010-07-26 13:34:58 +0000
@@ -35,101 +35,12 @@
35main and side content are positioned using the "yui-t4", "yui-main",35main and side content are positioned using the "yui-t4", "yui-main",
36"yui-b", and "yui-b side" classes.36"yui-b", and "yui-b side" classes.
3737
38Header.38 >>> from canonical.launchpad.testing.pages import find_tag_by_id
3939
40 >>> view = MainSideView(user, request)40 >>> view = MainSideView(user, request)
41 >>> html = view.render()41 >>> html = view.render()
42 >>> print html42 >>> print html
43 <!DOCTYPE html ...43 <!DOCTYPE html ...
44 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
45 lang="en" dir="ltr">
46 <head>
47 <title>Test base-layout: main_side</title>
48 <link rel="shortcut icon" href="/@@/launchpad.png" />
49 ...
50
51Standard Javascript functions.
52
53 >>> print html
54 <!DOCTYPE html ...
55 ...
56 sortables_init();
57 initInlineHelp();
58 Y.lp.activate_collapsibles();
59 activateFoldables();
60 activateConstrainBugExpiration();
61 ...
62
63Body preamble.
64
65 >>> from canonical.launchpad.testing.pages import find_tag_by_id
66
67 >>> body_tag = find_tag_by_id(html, 'document')
68 >>> body = str(body_tag)
69 >>> print body
70 <body id="document"
71 class="tab-overview
72 main_side
73 public
74 yui-skin-sam">
75 <div class="yui-d0">
76 <BLANKLINE>
77 <div id="locationbar">
78 ...
79
80Watermark and breadcrumbs.
81
82 >>> print body
83 <body ...
84 ...
85 <div class="watermark-apps-portlet top-portlet">
86 <img alt="" width="64" height="64" src="/@@/person-logo" />
87 <h2>Waffles</h2>
88 ...
89 <!-- Application Menu -->
90 <ul class="facetmenu">
91 <BLANKLINE>
92 <li class="overview active"
93 title="General information about Waffles"><a
94 href="http://launchpad.dev/~waffles">Overview</a></li>
95 ...
96 <div class="yui-b" dir="ltr">
97 <div>
98 <h2>Heading</h2>
99 <ol class="breadcrumbs">
100 ...
101
102Top portlet.
103
104 >>> print body
105 <body ...
106 <div class="top-portlet">
107 <p class="registered">
108 Registered on 2005-09-16
109 by <a class="sprite team" href="#">Illuminati team</a>
110 </p>
111 <p>
112 Main content of the page.
113 </p>
114 </div>
115 ...
116
117Help pane.
118
119 >>> print str(find_tag_by_id(body_tag, 'help-pane'))
120 <div id="help-pane" class="invisible">
121 <div id="help-body">
122 <iframe id="help-pane-content" class="invisible" ...
123 </div>
124 <div id="help-footer">
125 <span id="help-close"></span>
126 </div>
127 </div>
128
129Footer.
130
131 >>> print html
132 <!DOCTYPE html ...
133 <!--44 <!--
134 Facet name: overview45 Facet name: overview
135 Page type: main_side46 Page type: main_side
@@ -151,72 +62,6 @@
15162
152 >>> view = MainOnlyView(user, request)63 >>> view = MainOnlyView(user, request)
153 >>> html = view.render()64 >>> html = view.render()
154
155Heading.
156
157 >>> print html
158 <!DOCTYPE html ...
159 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
160 lang="en" dir="ltr">
161 <head>
162 <title>Test base-layout: main_only</title>
163 <link rel="shortcut icon" href="/@@/launchpad.png" />
164 ...
165
166Watermark.
167
168 >>> body_tag = find_tag_by_id(html, 'document')
169 >>> body = str(body_tag)
170 >>> print body
171 <body ...
172 <div class="watermark-apps-portlet top-portlet">
173 <img alt="" width="64" height="64" src="/@@/person-logo" />
174 <h2>Waffles</h2>
175 ...
176
177Main content.
178
179 >>> print str(find_tag_by_id(body_tag, 'maincontent'))
180 <div id="maincontent" class="yui-main">
181 <div class="yui-b" dir="ltr">
182 <div>
183 <BLANKLINE>
184 <ol class="breadcrumbs">
185 <BLANKLINE>
186 </ol>
187 <BLANKLINE>
188 </div>
189 <BLANKLINE>
190 <BLANKLINE>
191 <BLANKLINE>
192 <div class="top-portlet">
193 <h1>Heading</h1>
194 <p class="registered">
195 Registered on 2005-09-16
196 by <a class="sprite team" href="#">Illuminati team</a>
197 </p>
198 <p>
199 Main content of the page.
200 </p>
201 </div>
202 </div><!-- yui-b -->
203 </div>
204
205Global search.
206
207 >>> print body
208 <body ...
209 <form id="globalsearch" method="get"
210 accept-charset="UTF-8"
211 action="http://launchpad.dev/+search">
212 <input type="search" id="search-text" name="field.text" />
213 <input type="submit" value="" class="icon-only sprite search-icon" />
214 </form>
215 </div>
216 ...
217
218Footer.
219
220 >>> print html65 >>> print html
221 <!DOCTYPE html ...66 <!DOCTYPE html ...
222 ...67 ...
@@ -240,83 +85,6 @@
24085
241 >>> view = SearchlessView(user, request)86 >>> view = SearchlessView(user, request)
242 >>> html = view.render()87 >>> html = view.render()
243
244Heading.
245
246 >>> print html
247 <!DOCTYPE html ...
248 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
249 lang="en" dir="ltr">
250 <head>
251 <title>Test base-layout: searchless</title>
252 <link rel="shortcut icon" href="/@@/launchpad.png" />
253 ...
254
255Body class.
256
257 >>> body_tag = find_tag_by_id(html, 'document')
258 >>> body = str(body_tag)
259 >>> print body
260 <body id="document"
261 class="tab-overview
262 searchless
263 public
264 yui-skin-sam">
265 <div class="yui-d0">
266 ...
267
268Watermarks.
269
270 >>> print body
271 <body ...
272 <div class="watermark-apps-portlet top-portlet">
273 <img alt="" width="64" height="64" src="/@@/person-logo" />
274 <h2>Waffles</h2>
275 <div id="registration" class="registering">
276 </div>
277 <BLANKLINE>
278 <!-- Application Menu -->
279 <ul class="facetmenu">
280 <BLANKLINE>
281 <li class="overview active"
282 title="General information about Waffles"><a
283 href="http://launchpad.dev/~waffles">Overview</a></li>
284 ...
285
286Main content.
287
288 >>> print str(find_tag_by_id(body_tag, 'maincontent'))
289 <div id="maincontent" class="yui-main">
290 <div class="yui-b" dir="ltr">
291 <div>
292 <BLANKLINE>
293 <ol class="breadcrumbs">
294 <BLANKLINE>
295 </ol>
296 ...
297
298Top portlet.
299
300 >>> print body
301 <body ...
302 <div class="top-portlet">
303 <h1>Heading</h1>
304 <p class="registered">
305 Registered on 2005-09-16
306 by <a class="sprite team" href="#">Illuminati team</a>
307 </p>
308 <p>
309 Main content of the page.
310 </p>
311 </div>
312 </div><!-- yui-b -->
313 </div><!-- yui-main -->
314 <!-- yui-b side -->
315 <!-- yui-t4 -->
316 ...
317
318Footer.
319
320 >>> print html88 >>> print html
321 <!DOCTYPE html ...89 <!DOCTYPE html ...
322 ...90 ...
@@ -342,51 +110,6 @@
342110
343 >>> view = LocationlessView(user, request)111 >>> view = LocationlessView(user, request)
344 >>> html = view.render()112 >>> html = view.render()
345
346Heading.
347
348 >>> print html
349 <!DOCTYPE html ...
350 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
351 lang="en" dir="ltr">
352 <head>
353 <title>Test base-layout: locationless</title>
354 <link rel="shortcut icon" href="/@@/launchpad.png" />
355 ...
356
357Body.
358
359 >>> body_tag = find_tag_by_id(html, 'document')
360 >>> body = str(body_tag)
361 >>> print body
362 <body id="document"
363 class="tab-overview
364 locationless
365 public
366 yui-skin-sam">
367 <div class="yui-d0">
368 ...
369
370Main content.
371
372 >>> print str(find_tag_by_id(body_tag, 'maincontent'))
373 <div id="maincontent" class="yui-main">
374 <div class="yui-b" dir="ltr">
375 <div class="top-portlet">
376 <h1>Heading</h1>
377 <p class="registered">
378 Registered on 2005-09-16
379 by <a class="sprite team" href="#">Illuminati team</a>
380 </p>
381 <p>
382 Main content of the page.
383 </p>
384 </div>
385 </div><!-- yui-b -->
386 </div>
387
388Footer.
389
390 >>> print html113 >>> print html
391 <!DOCTYPE html ...114 <!DOCTYPE html ...
392 ...115 ...
@@ -517,8 +240,6 @@
517 >>> body_tag = find_tag_by_id(view.render(), 'maincontent')240 >>> body_tag = find_tag_by_id(view.render(), 'maincontent')
518 >>> print str(body_tag)241 >>> print str(body_tag)
519 <div id="maincontent" ...242 <div id="maincontent" ...
520 <ol class="breadcrumbs">
521 </ol>
522 ...243 ...
523 <div class="informational message">I cannot do that Dave.</div>244 <div class="informational message">I cannot do that Dave.</div>
524 <div class="top-portlet">245 <div class="top-portlet">
525246
=== added file 'lib/lp/app/browser/tests/test_base_layout.py'
--- lib/lp/app/browser/tests/test_base_layout.py 1970-01-01 00:00:00 +0000
+++ lib/lp/app/browser/tests/test_base_layout.py 2010-07-26 13:34:58 +0000
@@ -0,0 +1,163 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for base-layout.pt and its macros.
5
6The base-layout master template defines macros that control the layout
7of the page. Any page can use these layout options by including
8
9 metal:use-macro='view/macro:page/<layout>"
10
11in the root element. The template provides common layout to Launchpad.
12"""
13
14__metaclass__ = type
15
16from BeautifulSoup import BeautifulSoup
17
18from z3c.ptcompat import ViewPageTemplateFile
19
20from canonical.launchpad.webapp.publisher import LaunchpadView
21from canonical.launchpad.webapp.servers import LaunchpadTestRequest
22from canonical.testing.layers import DatabaseFunctionalLayer
23from canonical.launchpad.testing.pages import find_tag_by_id
24
25from lp.testing import TestCaseWithFactory
26
27
28class TestBaseLayout(TestCaseWithFactory):
29 """Test the page parts provided by the base-layout.pt."""
30 layer = DatabaseFunctionalLayer
31
32 def setUp(self):
33 super(TestBaseLayout, self).setUp()
34 self.user = self.factory.makePerson(name='waffles')
35 self.request = LaunchpadTestRequest(
36 SERVER_URL='http://launchpad.dev',
37 PATH_INFO='/~waffles/+layout')
38 self.request.setPrincipal(self.user)
39
40 def makeTemplateView(self, layout):
41 """Return a view that uses the specified layout."""
42
43 class TemplateView(LaunchpadView):
44 """A simple view to test base-layout."""
45 __launchpad_facetname__ = 'overview'
46 template = ViewPageTemplateFile(
47 'testfiles/%s.pt' % layout.replace('_', '-'))
48 page_title = 'Test base-layout: %s' % layout
49
50 return TemplateView(self.user, self.request)
51
52 def test_base_layout_doctype(self):
53 # Verify that the document is a html DOCTYPE.
54 view = self.makeTemplateView('main_side')
55 markup = view()
56 self.assertTrue(markup.startswith('<!DOCTYPE html'))
57
58 def verify_base_layout_html_element(self, content):
59 # The html element states the namespace and language information.
60 self.assertEqual(
61 'http://www.w3.org/1999/xhtml', content.html['xmlns'])
62 html_tag = content.html
63 self.assertEqual('en', html_tag['xml:lang'])
64 self.assertEqual('en', html_tag['lang'])
65 self.assertEqual('ltr', html_tag['dir'])
66
67 def verify_base_layout_head_parts(self, view, content):
68 # Verify the common head parts of every layout.
69 head = content.head
70 # The page's title starts with the view's page_title.
71 self.assertTrue(head.title.string.startswith(view.page_title))
72 # The shortcut icon for the browser chrome is provided.
73 link_tag = head.link
74 self.assertEqual('shortcut icon', link_tag['rel'])
75 self.assertEqual('/@@/launchpad.png', link_tag['href'])
76 # The template loads the common scripts.
77 load_script = find_tag_by_id(head, 'base-layout-load-scripts').name
78 self.assertEqual('script', load_script)
79
80 def verify_base_layout_body_parts(self, document):
81 # Verify the common body parts of every layout.
82 self.assertEqual('body', document.name)
83 yui_layout = document.find('div', 'yui-d0')
84 self.assertTrue(yui_layout is not None)
85 self.assertEqual(
86 'login-logout', yui_layout.find(True, id='locationbar')['class'])
87 self.assertEqual(
88 'yui-main', yui_layout.find(True, id='maincontent')['class'])
89 self.assertEqual(
90 'invisible', document.find(True, id='help-pane')['class'])
91 self.assertEqual(
92 'footer', yui_layout.find(True, id='footer')['class'])
93
94 def verify_watermark(self, document):
95 # Verify the parts of a watermark.
96 yui_layout = document.find('div', 'yui-d0')
97 watermark = yui_layout.find(True, id='watermark')
98 self.assertEqual('watermark-apps-portlet', watermark['class'])
99 self.assertEqual('/@@/person-logo', watermark.img['src'])
100 self.assertEqual('Waffles', watermark.h2.string)
101 self.assertEqual('facetmenu', watermark.ul['class'])
102
103 def test_main_side(self):
104 # The main_side layout has everything.
105 view = self.makeTemplateView('main_side')
106 content = BeautifulSoup(view())
107 self.verify_base_layout_html_element(content)
108 self.verify_base_layout_head_parts(view, content)
109 document = find_tag_by_id(content, 'document')
110 self.verify_base_layout_body_parts(document)
111 classes = 'tab-overview main_side public yui-skin-sam'.split()
112 self.assertEqual(classes, document['class'].split())
113 self.verify_watermark(document)
114 self.assertEqual(
115 'yui-b side', document.find(True, id='side-portlets')['class'])
116 self.assertEqual('form', document.find(True, id='globalsearch').name)
117
118 def test_main_only(self):
119 # The main_only layout has everything except side portlets.
120 view = self.makeTemplateView('main_only')
121 content = BeautifulSoup(view())
122 self.verify_base_layout_html_element(content)
123 self.verify_base_layout_head_parts(view, content)
124 document = find_tag_by_id(content, 'document')
125 self.verify_base_layout_body_parts(document)
126 classes = 'tab-overview main_only public yui-skin-sam'.split()
127 self.assertEqual(classes, document['class'].split())
128 self.verify_watermark(document)
129 self.assertEqual(
130 'registering', document.find(True, id='registration')['class'])
131 self.assertEqual(None, document.find(True, id='side-portlets'))
132 self.assertEqual('form', document.find(True, id='globalsearch').name)
133
134 def test_searchless(self):
135 # The searchless layout is missing side portlets and search.
136 view = self.makeTemplateView('searchless')
137 content = BeautifulSoup(view())
138 self.verify_base_layout_html_element(content)
139 self.verify_base_layout_head_parts(view, content)
140 document = find_tag_by_id(content, 'document')
141 self.verify_base_layout_body_parts(document)
142 self.verify_watermark(document)
143 classes = 'tab-overview searchless public yui-skin-sam'.split()
144 self.assertEqual(classes, document['class'].split())
145 self.assertEqual(
146 'registering', document.find(True, id='registration')['class'])
147 self.assertEqual(None, document.find(True, id='side-portlets'))
148 self.assertEqual(None, document.find(True, id='globalsearch'))
149
150 def test_locationless(self):
151 # The locationless layout has no optional content.
152 view = self.makeTemplateView('locationless')
153 content = BeautifulSoup(view())
154 self.verify_base_layout_html_element(content)
155 self.verify_base_layout_head_parts(view, content)
156 document = find_tag_by_id(content, 'document')
157 self.verify_base_layout_body_parts(document)
158 classes = 'tab-overview locationless public yui-skin-sam'.split()
159 self.assertEqual(classes, document['class'].split())
160 self.assertEqual(None, document.find(True, id='registration'))
161 self.assertEqual(None, document.find(True, id='watermark'))
162 self.assertEqual(None, document.find(True, id='side-portlets'))
163 self.assertEqual(None, document.find(True, id='globalsearch'))
0164
=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt 2010-07-14 14:38:07 +0000
+++ lib/lp/app/templates/base-layout-macros.pt 2010-07-26 13:34:58 +0000
@@ -288,7 +288,7 @@
288288
289 <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" />289 <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" />
290290
291 <script type="text/javascript">291 <script id="base-layout-load-scripts" type="text/javascript">
292 LPS.use('node', 'lp', function(Y) {292 LPS.use('node', 'lp', function(Y) {
293 Y.on('load', function(e) {293 Y.on('load', function(e) {
294 sortables_init();294 sortables_init();
295295
=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt 2010-07-02 18:35:03 +0000
+++ lib/lp/app/templates/base-layout.pt 2010-07-26 13:34:58 +0000
@@ -83,21 +83,22 @@
83 })();83 })();
84 </script>84 </script>
85 <div class="yui-d0">85 <div class="yui-d0">
86 <div id="locationbar">86 <div id="locationbar" class="login-logout">
87 <tal:login replace="structure context/@@login_status" />87 <tal:login replace="structure context/@@login_status" />
88 </div><!--id="locationbar"-->88 </div><!--id="locationbar"-->
8989
90 <div class="watermark-apps-portlet top-portlet"90 <div id="watermark" class="watermark-apps-portlet"
91 tal:condition="view/macro:pagehas/applicationtabs">91 tal:condition="view/macro:pagehas/applicationtabs">
92 <span tal:replace="structure view/watermark:logo"></span>92 <div class="flowed-block">
93 <h2 tal:replace="structure view/watermark:heading">93 <span tal:replace="structure view/watermark:logo"></span>
94 Celso Providelo94 </div>
95 </h2>95 <div class="flowed-block wide">
96 <div id="registration" class="registering">96 <h2 tal:replace="structure view/watermark:heading">
97 <metal:registering define-slot="registering" />97 Celso Providelo
98 </div>98 </h2>
99 <metal:heading_nav99 <metal:heading_nav
100 use-macro="context/@@+base-layout-macros/application-buttons"/>100 use-macro="context/@@+base-layout-macros/application-buttons"/>
101 </div>
101 </div>102 </div>
102103
103 <div class="yui-t4"104 <div class="yui-t4"
@@ -108,13 +109,17 @@
108 lang view/lang|default_language|default;109 lang view/lang|default_language|default;
109 xml:lang view/lang|default_language|default;110 xml:lang view/lang|default_language|default;
110 dir view/dir|string:ltr">111 dir view/dir|string:ltr">
111 <div tal:condition="view/macro:pagehas/applicationtabs">112 <div class="context-publication"
113 tal:condition="view/macro:pagehas/applicationtabs">
112 <h1114 <h1
113 tal:condition="view/label|nothing"115 tal:condition="view/label|nothing"
114 tal:content="view/label"116 tal:content="view/label"
115 metal:define-slot="heading"117 metal:define-slot="heading"
116 >Page Label118 >Page Label
117 </h1>119 </h1>
120 <div id="registration" class="registering">
121 <metal:registering define-slot="registering" />
122 </div>
118 <tal:breadcrumbs replace="structure context/@@+hierarchy">123 <tal:breadcrumbs replace="structure context/@@+hierarchy">
119 ProjectName > Branches > Merge Proposals > fix-for-navigation124 ProjectName > Branches > Merge Proposals > fix-for-navigation
120 </tal:breadcrumbs>125 </tal:breadcrumbs>
@@ -132,7 +137,7 @@
132 </div><!-- yui-b -->137 </div><!-- yui-b -->
133 </div><!-- yui-main -->138 </div><!-- yui-main -->
134139
135 <div class="yui-b side"140 <div id="side-portlets" class="yui-b side"
136 tal:condition="view/macro:pagehas/portlets">141 tal:condition="view/macro:pagehas/portlets">
137 <metal:portlets define-slot="side" />142 <metal:portlets define-slot="side" />
138 </div><!-- yui-b side -->143 </div><!-- yui-b side -->
139144
=== modified file 'lib/lp/app/templates/launchpad-hierarchy.pt'
--- lib/lp/app/templates/launchpad-hierarchy.pt 2010-01-22 17:28:32 +0000
+++ lib/lp/app/templates/launchpad-hierarchy.pt 2010-07-26 13:34:58 +0000
@@ -3,13 +3,13 @@
3 xmlns:tal="http://xml.zope.org/namespaces/tal"3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 i18n:domain="launchpad">6 i18n:domain="launchpad"
7 tal:condition="view/display_breadcrumbs">
78
8<tal:breadcrumbs repeat="breadcrumb view/items"9<tal:breadcrumbs repeat="breadcrumb view/items">
9 condition="view/display_breadcrumbs">
10 <li>10 <li>
11 <a tal:attributes="href breadcrumb/url"11 <a tal:attributes="href breadcrumb/url"
12 tal:omit-tag="repeat/breadcrumb/end"><tal:text 12 tal:omit-tag="repeat/breadcrumb/end"><tal:text
13 condition="not: repeat/breadcrumb/end"13 condition="not: repeat/breadcrumb/end"
14 replace="breadcrumb/text">14 replace="breadcrumb/text">
15 Bugs on redfish</tal:text><tal:text15 Bugs on redfish</tal:text><tal:text
1616
=== modified file 'lib/lp/blueprints/stories/sprints/05-sprint-creation.txt'
--- lib/lp/blueprints/stories/sprints/05-sprint-creation.txt 2009-11-08 20:09:26 +0000
+++ lib/lp/blueprints/stories/sprints/05-sprint-creation.txt 2010-07-26 13:34:58 +0000
@@ -1,137 +1,119 @@
1= Creating new sprints =1Creating new sprints
22====================
3We should also be able to create a new sprint. We do this off the Sprints3
4+new page.4We should also be able to create a new sprint. We do this off the
55Sprints +new page.
6 >>> user_browser.open('http://launchpad.dev/sprints')6
7 >>> user_browser.getLink('Register a meeting').click()7 >>> user_browser.open('http://launchpad.dev/sprints')
88 >>> user_browser.getLink('Register a meeting').click()
9 >>> print user_browser.title9
10 Register a meeting...10 >>> print user_browser.title
11 Register a meeting...
1112
12First we'll test the name field validator.13First we'll test the name field validator.
1314
14 >>> user_browser.getControl('Name').value = 'ltsp_on_steroids!'15 >>> user_browser.getControl('Name').value = 'ltsp_on_steroids!'
15 >>> user_browser.getControl('Title').value = 'LTSP On Steroids'16 >>> user_browser.getControl('Title').value = 'LTSP On Steroids'
16 >>> summary = 'This is a sprint summary. Some words about the sprint'17 >>> summary = 'This is a sprint summary. Some words about the sprint'
17 >>> user_browser.getControl('Summary').value = summary18 >>> user_browser.getControl('Summary').value = summary
18 >>> user_browser.getControl('Driver').value = 'kamion'19 >>> user_browser.getControl('Driver').value = 'kamion'
19 >>> user_browser.getControl('Home Page').value = 'http://www.willy.net'20 >>> user_browser.getControl('Home Page').value = 'http://www.willy.net'
20 >>> user_browser.getControl('Timezone').value = ['UTC']21 >>> user_browser.getControl('Timezone').value = ['UTC']
21 >>> user_browser.getControl(22 >>> user_browser.getControl(
22 ... 'Starting Date and Time').value = '10 Oct 2006 09:15'23 ... 'Starting Date and Time').value = '10 Oct 2006 09:15'
23 >>> user_browser.getControl(24 >>> user_browser.getControl(
24 ... 'Finishing Date and Time').value = '13 Oct 2006 16:00'25 ... 'Finishing Date and Time').value = '13 Oct 2006 16:00'
25 >>> user_browser.getControl('Add Sprint').click()26 >>> user_browser.getControl('Add Sprint').click()
2627
27 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):28 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
28 ... print tag.renderContents()29 ... print tag.renderContents()
29 There is 1 error.30 There is 1 error.
30 <BLANKLINE>31 <BLANKLINE>
31 Invalid name 'ltsp_on_steroids!'. Names must be at least two characters ...32 Invalid name 'ltsp_on_steroids!'. Names must be at least two characte...
3233
33Register a sprint with the same name of a existing one also returns a nice34Register a sprint with the same name of a existing one also returns a
34error message35nice error message
3536
36 >>> user_browser.getControl('Name').value = 'ubz'37 >>> user_browser.getControl('Name').value = 'ubz'
37 >>> user_browser.getControl('Add Sprint').click()38 >>> user_browser.getControl('Add Sprint').click()
3839
39 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):40 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
40 ... print tag.renderContents()41 ... print tag.renderContents()
41 There is 1 error.42 There is 1 error.
42 <BLANKLINE>43 <BLANKLINE>
43 ubz is already in use by another sprint.44 ubz is already in use by another sprint.
4445
45Create a new sprint with a finish date before the starting date returns46Create a new sprint with a finish date before the starting date returns
46a error message.47a error message.
4748
48 >>> user_browser.getControl('Name').value = 'ltsponsteroids'49 >>> user_browser.getControl('Name').value = 'ltsponsteroids'
49 >>> user_browser.getControl(50 >>> user_browser.getControl(
50 ... 'Starting Date and Time').value = '13 Oct 2006 09:15 '51 ... 'Starting Date and Time').value = '13 Oct 2006 09:15 '
51 >>> user_browser.getControl(52 >>> user_browser.getControl(
52 ... 'Finishing Date and Time').value = '10 Oct 2006 16:00'53 ... 'Finishing Date and Time').value = '10 Oct 2006 16:00'
53 >>> user_browser.getControl('Add Sprint').click()54 >>> user_browser.getControl('Add Sprint').click()
5455
55 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):56 >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
56 ... print tag.renderContents()57 ... print tag.renderContents()
57 There is 1 error.58 There is 1 error.
58 <BLANKLINE>59 <BLANKLINE>
59 This event can't start after it ends60 This event can't start after it ends
6061
61Also, the date is now presented in a canonicalised format, with time in62Also, the date is now presented in a canonicalised format, with time in
62minutes rather than second-level accuracy:63minutes rather than second-level accuracy:
6364
64 >>> user_browser.getControl('Starting Date and Time').value65 >>> user_browser.getControl('Starting Date and Time').value
65 '2006-10-13 09:15'66 '2006-10-13 09:15'
66 >>> user_browser.getControl('Finishing Date and Time').value67
67 '2006-10-10 16:00'68 >>> user_browser.getControl('Finishing Date and Time').value
6869 '2006-10-10 16:00'
69Fix the date and try again. We're redirected to the sprint home page70
70for the new sprint.71Fix the date and try again. We're redirected to the sprint home page for
7172the new sprint.
72 >>> user_browser.getControl(73
73 ... 'Starting Date and Time').value = '10 Oct 2006 09:15 '74 >>> user_browser.getControl(
74 >>> user_browser.getControl(75 ... 'Starting Date and Time').value = '10 Oct 2006 09:15 '
75 ... 'Finishing Date and Time').value = '13 Oct 2006 16:00'76 >>> user_browser.getControl(
76 >>> user_browser.getControl('Add Sprint').click()77 ... 'Finishing Date and Time').value = '13 Oct 2006 16:00'
7778 >>> user_browser.getControl('Add Sprint').click()
78 >>> user_browser.url79
79 'http://launchpad.dev/sprints/ltsponsteroids'80 >>> user_browser.url
8081 'http://launchpad.dev/sprints/ltsponsteroids'
81Because sprints are root contexts, an h1 heading is used to display
82the main heading above the application buttons) and the breadcrumbs
83are not displayed.
84
85 >>> print_location(user_browser.contents)
86 Hierarchy: None displayed
87 Tabs:
88 * Overview (selected) - not linked
89 ...
90 * Bugs - not linked
91 * Blueprints - http://blueprints.launchpad.dev/sprints/ltsponsteroids
92 * Translations - not linked
93 * Answers - not linked
94 Main heading: LTSP On Steroids
95
96There is no h1 heading in the main content.
97
98 >>> print find_main_content(user_browser.contents).find('h1')
99 None
10082
101Since the sprint's time zone was set to UTC, the dates are displayed in83Since the sprint's time zone was set to UTC, the dates are displayed in
102that time zone:84that time zone:
10385
104 >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))86 >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))
105 Starts: 09:15 UTC on Tuesday, 2006-10-1087 Starts: 09:15 UTC on Tuesday, 2006-10-10
106 Ends: 16:00 UTC on Friday, 2006-10-1388 Ends: 16:00 UTC on Friday, 2006-10-13
10789
108Because this is a brand new sprint, it will have no specs, and we should see90Because this is a brand new sprint, it will have no specs, and we should
109a warning to that effect on the page.91see a warning to that effect on the page.
11092
111 >>> message = 'Nobody has yet proposed any blueprints for discussion'93 >>> message = 'Nobody has yet proposed any blueprints for discussion'
112 >>> message in user_browser.contents94 >>> message in user_browser.contents
113 True95 True
114
11596
116Add a new sprint with a different time zone is also handled correctly.97Add a new sprint with a different time zone is also handled correctly.
11798
118 >>> user_browser.open('http://launchpad.dev/sprints/+new')99 >>> user_browser.open('http://launchpad.dev/sprints/+new')
119 >>> user_browser.getControl('Name').value = 'africa-sprint'100 >>> user_browser.getControl('Name').value = 'africa-sprint'
120 >>> user_browser.getControl('Title').value = 'Africa Sprint'101 >>> user_browser.getControl('Title').value = 'Africa Sprint'
121 >>> summary = 'This is a sprint summary. Some words about the sprint'102 >>> summary = 'This is a sprint summary. Some words about the sprint'
122 >>> user_browser.getControl('Summary').value = summary103 >>> user_browser.getControl('Summary').value = summary
123 >>> user_browser.getControl('Home Page').value = 'http://www.ubuntu.com'104 >>> user_browser.getControl('Home Page').value = 'http://www.ubuntu.com'
124 >>> user_browser.getControl('Timezone').value = ['Africa/Johannesburg']105 >>> user_browser.getControl('Timezone').value = ['Africa/Johannesburg']
125 >>> user_browser.getControl(106 >>> user_browser.getControl(
126 ... 'Starting Date and Time').value = '10 Jul 2006 09:15'107 ... 'Starting Date and Time').value = '10 Jul 2006 09:15'
127 >>> user_browser.getControl(108 >>> user_browser.getControl(
128 ... 'Finishing Date and Time').value = '13 Jul 2006 16:00'109 ... 'Finishing Date and Time').value = '13 Jul 2006 16:00'
129 >>> user_browser.getControl('Add Sprint').click()110 >>> user_browser.getControl('Add Sprint').click()
130111
131 >>> user_browser.url112 >>> user_browser.url
132 'http://launchpad.dev/sprints/africa-sprint'113 'http://launchpad.dev/sprints/africa-sprint'
133114
134 >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))115 >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))
135 Starts: 09:15 SAST on Monday, 2006-07-10116 Starts: 09:15 SAST on Monday, 2006-07-10
136 Ends: 16:00 SAST on Thursday, 2006-07-13117 Ends: 16:00 SAST on Thursday, 2006-07-13
118
137119
138120
=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-03-15 13:22:02 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-07-26 13:34:58 +0000
@@ -3,8 +3,8 @@
3The bug heat appears on the bug index page as four flames.3The bug heat appears on the bug index page as four flames.
44
5 >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')5 >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
6 >>> print find_tag_by_id(6 >>> print find_main_content(
7 ... anon_browser.contents, 'registration').fetch('img')[0]7 ... anon_browser.contents).find('img', src='/@@/bug-heat-0.png')
8 <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 0" />8 <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 0" />
99
10Packages use their distribution as context when generating flame icons10Packages use their distribution as context when generating flame icons
@@ -30,6 +30,6 @@
30 >>> logout()30 >>> logout()
3131
32 >>> anon_browser.open(canonical_url(dsp_bug))32 >>> anon_browser.open(canonical_url(dsp_bug))
33 >>> print find_tag_by_id(33 >>> print find_main_content(
34 ... anon_browser.contents, 'registration').fetch('img')[0]34 ... anon_browser.contents).find('img', src='/@@/bug-heat-0.png')
35 <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 1250" />35 <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 1250" />
3636
=== modified file 'lib/lp/bugs/stories/bugs/xx-malone-homepage.txt'
--- lib/lp/bugs/stories/bugs/xx-malone-homepage.txt 2010-05-19 05:47:50 +0000
+++ lib/lp/bugs/stories/bugs/xx-malone-homepage.txt 2010-07-26 13:34:58 +0000
@@ -1,26 +1,18 @@
1Say hello to Bugs. :)1Say hello to Bugs. :)
22
3 >>> browser.open('http://bugs.launchpad.dev/')3 >>> browser.open('http://bugs.launchpad.dev/')
4 >>> browser.url4 >>> browser.url
5 'http://bugs.launchpad.dev/'5 'http://bugs.launchpad.dev/'
66
7 >>> print_location(browser.contents)7 >>> print browser.title
8 Hierarchy: None displayed8 Bug tracking
9 Tabs:
10 * Launchpad Home - http://launchpad.dev/
11 * Code - http://code.launchpad.dev/
12 * Bugs (selected) - http://bugs.launchpad.dev/
13 * Blueprints - http://blueprints.launchpad.dev/
14 * Translations - http://translations.launchpad.dev/
15 * Answers - http://answers.launchpad.dev/
16 Main heading: Bug tracking
179
18There are a few related pages linked in a portlet:10There are a few related pages linked in a portlet:
1911
20 >>> related_pages = find_portlet(browser.contents, 'Related pages')12 >>> related_pages = find_portlet(browser.contents, 'Related pages')
21 >>> for link in related_pages.findAll('a'):13 >>> for link in related_pages.findAll('a'):
22 ... print "%s\n --> %s" % (extract_text(link), link.get('href'))14 ... print "%s\n --> %s" % (extract_text(link), link.get('href'))
23 Bug trackers15 Bug trackers
24 --> http://bugs.launchpad.dev/bugs/bugtrackers16 --> http://bugs.launchpad.dev/bugs/bugtrackers
25 CVE tracker17 CVE tracker
26 --> http://bugs.launchpad.dev/bugs/cve18 --> http://bugs.launchpad.dev/bugs/cve
2719
=== modified file 'lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt'
--- lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt 2010-06-16 15:28:35 +0000
+++ lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt 2010-07-26 13:34:58 +0000
@@ -1,19 +1,12 @@
1= Bug trackers in Launchpad =1Bug trackers in Launchpad
2=========================
23
3The bug trackers index page has the same navigation as the main Bugs4The bug trackers index page has the same navigation as the main Bugs
4page, with the addition of a breadcrumb itself.5page, with the addition of a breadcrumb itself.
56
6 >>> user_browser.open('http://launchpad.dev/bugs/bugtrackers')7 >>> user_browser.open('http://launchpad.dev/bugs/bugtrackers')
7 >>> print_location(user_browser.contents)8 >>> print user_browser.title
8 Hierarchy: None displayed9 Bug trackers registered in Launchpad
9 Tabs:
10 * Launchpad Home - http://launchpad.dev/
11 * Code - http://code.launchpad.dev/
12 * Bugs (selected) - http://bugs.launchpad.dev/
13 * Blueprints - http://blueprints.launchpad.dev/
14 * Translations - http://translations.launchpad.dev/
15 * Answers - http://answers.launchpad.dev/
16 Main heading: Bug trackers registered in Launchpad
1710
18The page presents a table with all bugtrackers currently registered:11The page presents a table with all bugtrackers currently registered:
1912
2013
=== modified file 'lib/lp/bugs/stories/cve/cve-pages.txt'
--- lib/lp/bugs/stories/cve/cve-pages.txt 2010-05-21 13:49:36 +0000
+++ lib/lp/bugs/stories/cve/cve-pages.txt 2010-07-26 13:34:58 +0000
@@ -1,4 +1,5 @@
1= CVE tracking =1CVE tracking
2============
23
3Launchpad supports the CVE database system.4Launchpad supports the CVE database system.
45
@@ -17,16 +18,8 @@
17 >>> browser.getLink('CVE entries').click()18 >>> browser.getLink('CVE entries').click()
18 >>> print browser.url19 >>> print browser.url
19 http://bugs.launchpad.dev/bugs/cve20 http://bugs.launchpad.dev/bugs/cve
20 >>> print_location(browser.contents)21 >>> print browser.title
21 Hierarchy: None displayed22 Launchpad CVE tracker
22 Tabs:
23 * Launchpad Home - http://launchpad.dev/
24 * Code - http://code.launchpad.dev/
25 * Bugs (selected) - http://bugs.launchpad.dev/
26 * Blueprints - http://blueprints.launchpad.dev/
27 * Translations - http://translations.launchpad.dev/
28 * Answers - http://answers.launchpad.dev/
29 Main heading: Launchpad CVE tracker
30 >>> print browser.contents23 >>> print browser.contents
31 <...24 <...
32 ...CVE-2005-2737 (Candidate)...25 ...CVE-2005-2737 (Candidate)...
@@ -37,76 +30,58 @@
37 >>> browser.getLink('Show all registered CVEs').click()30 >>> browser.getLink('Show all registered CVEs').click()
38 >>> print browser.url31 >>> print browser.url
39 http://bugs.launchpad.dev/bugs/cve/+all32 http://bugs.launchpad.dev/bugs/cve/+all
40 >>> print_location(browser.contents)33 >>> print browser.title
41 Hierarchy: None displayed34 Launchpad CVE tracker
42 Tabs:
43 * Launchpad Home - http://launchpad.dev/
44 * Code - http://code.launchpad.dev/
45 * Bugs (selected) - http://bugs.launchpad.dev/
46 * Blueprints - http://blueprints.launchpad.dev/
47 * Translations - http://translations.launchpad.dev/
48 * Answers - http://answers.launchpad.dev/
49 Main heading: Launchpad CVE tracker
5035
51Now, we will test the search functionality of the CVE tracker. First we36Now, we will test the search functionality of the CVE tracker. First we
52will search for the word "loss" in the database and see how many show37will search for the word "loss" in the database and see how many show
53up. We expect to see CVE-1999-2345.38up. We expect to see CVE-1999-2345.
5439
55 >>> print http(r"""40 >>> print http(r"""
56 ... POST /bugs/cve/ HTTP/1.141 ... POST /bugs/cve/ HTTP/1.1
57 ... Content-Length: 942 ... Content-Length: 9
58 ... Content-Type: application/x-www-form-urlencoded43 ... Content-Type: application/x-www-form-urlencoded
59 ...44 ...
60 ... text=loss""")45 ... text=loss""")
61 HTTP/1.1 200 Ok46 HTTP/1.1 200 Ok
62 ...Matches: 1...47 ...Matches: 1...
63 ...CVE-1999-2345 (Candidate)...48 ...CVE-1999-2345 (Candidate)...
6449
6550
66Finally, let's put a CVE number in there. We expect to jump straight to that51Finally, let's put a CVE number in there. We expect to jump straight to that
67CVE.52CVE.
6853
69 >>> print http(r"""54 >>> print http(r"""
70 ... POST /bugs/cve/ HTTP/1.155 ... POST /bugs/cve/ HTTP/1.1
71 ... Content-Length: 1456 ... Content-Length: 14
72 ... Content-Type: application/x-www-form-urlencoded57 ... Content-Type: application/x-www-form-urlencoded
73 ...58 ...
74 ... text=2005-2737""")59 ... text=2005-2737""")
75 HTTP/1.1 303 See Other60 HTTP/1.1 303 See Other
76 ...61 ...
77 Location: http://.../bugs/cve/2005-273762 Location: http://.../bugs/cve/2005-2737
78 ...63 ...
7964
80A CVE page has the same navigation as the Bugs front page (because CVEs65A CVE page includes a link back to the main CVE page.
81don't have their own blueprints, translations, etc), and also includes
82a link back to the main CVE page.
8366
84 >>> anon_browser.open('http://launchpad.dev/bugs/cve/2005-2737')67 >>> anon_browser.open('http://launchpad.dev/bugs/cve/2005-2737')
85 >>> print_location(anon_browser.contents)68 >>> print anon_browser.title
86 Hierarchy: None displayed69 CVE 2005-2737 : ...
87 Tabs:
88 * Launchpad Home - http://launchpad.dev/
89 * Code - http://code.launchpad.dev/
90 * Bugs (selected) - http://bugs.launchpad.dev/
91 * Blueprints - http://blueprints.launchpad.dev/
92 * Translations - http://translations.launchpad.dev/
93 * Answers - http://answers.launchpad.dev/
94 Main heading: CVE 2005-2737
95 >>> back_link = anon_browser.getLink('Launchpad CVE tracker')70 >>> back_link = anon_browser.getLink('Launchpad CVE tracker')
96 >>> print back_link.url71 >>> print back_link.url
97 http://launchpad.dev/bugs/cve72 http://launchpad.dev/bugs/cve
9873
99The CVE page links to the related bugs.74The CVE page links to the related bugs.
10075
101 >>> "Cross-site scripting (XSS) vulnerability" in anon_browser.contents76 >>> "Cross-site scripting (XSS) vulnerability" in anon_browser.contents
102 True77 True
103 >>> 'No related bugs' in anon_browser.contents78 >>> 'No related bugs' in anon_browser.contents
104 True79 True
105 >>> for tag in find_tags_by_class(80 >>> for tag in find_tags_by_class(
106 ... anon_browser.contents, 'menu-link-linkbug'):81 ... anon_browser.contents, 'menu-link-linkbug'):
107 ... print tag82 ... print tag
108 <a href="+linkbug" class="menu-link-linkbug sprite add">Link to bug</a>83 <a href="+linkbug" class="menu-link-linkbug sprite add">Link to bug</a>
109 >>> 'Candidate' in anon_browser.contents84 >>> 'Candidate' in anon_browser.contents
110 True85 True
111 >>> 'http://marc.theaimsgroup.com/?l=bugtraq&amp;m=112511025414488&amp;w=2">20050826 Multiple PHP' in anon_browser.contents86 >>> '20050826 Multiple PHP' in anon_browser.contents
112 True87 True
11388
=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
--- lib/lp/bugs/templates/bugtask-index.pt 2010-07-16 16:14:39 +0000
+++ lib/lp/bugs/templates/bugtask-index.pt 2010-07-26 13:34:58 +0000
@@ -43,12 +43,11 @@
43 </metal:side>43 </metal:side>
4444
45 <tal:registering metal:fill-slot="registering">45 <tal:registering metal:fill-slot="registering">
46 Bug #<tal:block content="context/bug/id"/> reported by46 Reported by
47 <tal:reporter replace="structure context/bug/owner/fmt:link" />47 <tal:reporter replace="structure context/bug/owner/fmt:link" />
48 <span48 <span
49 tal:attributes="title context/bug/datecreated/fmt:datetime"49 tal:attributes="title context/bug/datecreated/fmt:datetime"
50 tal:content="context/bug/datecreated/fmt:displaydate" />50 tal:content="context/bug/datecreated/fmt:displaydate" />
51 <tal:heat replace="structure view/bug_heat_html" />
52 </tal:registering>51 </tal:registering>
5352
54 <metal:heading fill-slot="heading" tal:define="context_menu context/menu:context">53 <metal:heading fill-slot="heading" tal:define="context_menu context/menu:context">
@@ -92,6 +91,10 @@
92 </tal:XXX>91 </tal:XXX>
93 </p>92 </p>
9493
94 <div style="float: right;">
95 <tal:heat replace="structure view/bug_heat_html" />
96 </div>
97
95 <div tal:replace="structure context/bug/@@+bugtasks-and-nominations-table" />98 <div tal:replace="structure context/bug/@@+bugtasks-and-nominations-table" />
9699
97 <div id="maincontentsub">100 <div id="maincontentsub">