Merge lp:~sinzui/launchpad/wax-and-wane into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Merged at revision: not available
Proposed branch: lp:~sinzui/launchpad/wax-and-wane
Merge into: lp:launchpad
Diff against target: 296 lines (+110/-63)
8 files modified
lib/canonical/launchpad/icing/style-3-0.css (+3/-0)
lib/lp/registry/browser/mailinglists.py (+13/-4)
lib/lp/registry/browser/milestone.py (+1/-1)
lib/lp/registry/browser/tests/mailinglist-message-views.txt (+74/-0)
lib/lp/registry/browser/tests/milestone-views.txt (+15/-2)
lib/lp/registry/browser/tests/test_views.py (+1/-0)
lib/lp/registry/doc/message-holds.txt (+0/-52)
lib/lp/registry/templates/milestone-index.pt (+3/-4)
To merge this branch: bzr merge lp:~sinzui/launchpad/wax-and-wane
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+17141@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (5.1 KiB)

This is my branch to fix from simple nuisances in launchpad.

    lp:~sinzui/launchpad/wax-and-wane
    Diff size: 206
    Launchpad bug: https://bugs.launchpad.net/bugs/wax-and-wane
    Test command: ./bin/test -vv \
        -t mailinglist-message-views
        -t milestone-views
    Pre-implementation: no one
    Target release: 10.01

Fix from simple nuisances in launchpad
--------------------------------------------------------------------

Bug #504024 [invalid list moderation markup breaks webkit]
    The HeldMessageView and message-moderation.pt create ill-formed markup
    that makes the message moderation page unusable in webkit browsers. I
    have observed two issues:

    1) The template uses `structure view/author`, but the author field is not
    HTML. The angle brackets are not escaped, creating an unclosed tag.

    2) The view's initialize() method is using `paragraphs.append('\n<p>\n')`
    which creates an unclosed paragraph. (this markup was deprecated in 1994
    and it invalid XHTML which the page claims to be).

    Both of these errors can cause form submittal errors.

Bug #505919 [milestones assignees wrap poorly]
    The assignee name wraps to the next line and it is difficult to see
    understand that the number belongs to the next name.

Bug #505917 [Sort milestone bugs by status by default]
    People working the bugs need to see status, then importance.

Rules
-----

Bug #504024 [invalid list moderation markup breaks webkit]
    * Add a <p> before each added paragraph and change the markup added
      afterwards to </p>
    * Remove the `structure` command

Bug #505919 [milestones assignees wrap poorly]
    * Add a CSS class to prevent the count and name from wrapping.
    * ADDENDUM: white-space: nowrap does not work because the comma is
      allowed to wrap. The pre value works if the markup is has no line breaks

Bug #505917 [Sort milestone bugs by status by default]
    * Change the sort order in the view.

QA
--

Bug #504024 [invalid list moderation markup breaks webkit]
    * Visit https://edge.launchpad.net/~launchpad-users/+mailinglist-moderate
    * Verify the From address shows the real address in the VIAGRA messages.
    * Expand a real message (not an empty spam)
    * Verify the paragraphs are shown as paragraphs.

Bug #505919 [milestones assignees wrap poorly]
    * Visit https://edge.launchpad.net/launchpad-registry/+milestone/10.01
    * Verify assignee names do not wrap in activities portlet.

Bug #505917 [Sort milestone bugs by status by default]
    * Visit https://edge.launchpad.net/launchpad-registry/+milestone/10.01
    * Verify that the bugs are listed from triaged, in progress, fix committed
      to fix released.

Lint
----

Linting changed files:
  lib/canonical/launchpad/icing/style-3-0.css
  lib/lp/registry/browser/mailinglists.py
  lib/lp/registry/browser/milestone.py
  lib/lp/registry/browser/tests/mailinglist-message-views.txt
  lib/lp/registry/browser/tests/milestone-views.txt
  lib/lp/registry/browser/tests/test_views.py
  lib/lp/registry/templates/milestone-index.pt

Test
----

    * lib/lp/registry/browser/tests/test_views.py
      * Registered mailinglist-message-views.tx...

Read more...

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

Hi Curtis,

Thanks for these changes. Everything looks really good. I would suggest you put a test into _append_paragraph to ensure current_paragraph is not empty. This could happen if you closed the paragraph right before you hit line 20. It's not big deal but we shouldn't generate an empty <p></p> if we can easily help it.

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

Hi Brad.

On Tue, 2010-01-12 at 14:18 +0000, Brad Crittenden wrote:
> Review: Approve code
> Hi Curtis,
>
> Thanks for these changes. Everything looks really good. I would
> suggest you put a test into _append_paragraph to ensure
> current_paragraph is not empty. This could happen if you closed the
> paragraph right before you hit line 20. It's not big deal but we
> shouldn't generate an empty <p></p> if we can easily help it.

Thanks for your insight. I discounted the end case because leading and
trailing white space are stripped before the paragraphs are formatted.
But putting the rule in the helper method makes the rule clear and
universal. So I moved it.

=== modified file 'lib/lp/registry/browser/mailinglists.py'
--- lib/lp/registry/browser/mailinglists.py 2010-01-09 16:48:38 +0000
+++ lib/lp/registry/browser/mailinglists.py 2010-01-12 15:56:02 +0000
@@ -158,7 +158,7 @@
         for lineno, line in enumerate(details.splitlines()):
             if lineno > 20:
                 break
- if len(line.strip()) == 0 and len(current_paragraph) > 0:
+ if len(line.strip()) == 0:
                 self._append_paragraph(paragraphs, current_paragraph)
                 current_paragraph = []
             else:
@@ -167,6 +167,10 @@
         self.body_details = u''.join(paragraphs)

     def _append_paragraph(self, paragraphs, current_paragraph):
+ if len(current_paragraph) == 0:
+ # There is nothing to append. The message has multiple
+ # blank lines.
+ return
         paragraphs.append(u'\n<p>\n')
         paragraphs.append(u'\n'.join(current_paragraph))
         paragraphs.append(u'\n</p>\n')

--
__Curtis C. Hovey_________
http://launchpad.net/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
2--- lib/canonical/launchpad/icing/style-3-0.css 2010-01-06 02:02:18 +0000
3+++ lib/canonical/launchpad/icing/style-3-0.css 2010-01-13 14:18:16 +0000
4@@ -276,6 +276,9 @@
5 display: inline;
6 padding: 0 1.5em 0 0;
7 }
8+.pre {
9+ white-space: pre;
10+ }
11 .table-actions:nth-child(2) {
12 /* text-align only works here because the <li> also has the
13 style display:inline.
14
15=== modified file 'lib/lp/registry/browser/mailinglists.py'
16--- lib/lp/registry/browser/mailinglists.py 2009-08-16 17:21:54 +0000
17+++ lib/lp/registry/browser/mailinglists.py 2010-01-13 14:18:16 +0000
18@@ -130,7 +130,8 @@
19 # The author field is very close to what the details has, except that
20 # the view wants to include a link to the person's overview page.
21 self.author = '<a href="%s">%s</a>' % (
22- canonical_url(self.details.author), self.details.sender)
23+ canonical_url(self.details.author),
24+ escape(self.details.sender))
25
26 def initialize(self):
27 """See `LaunchpadView`."""
28@@ -158,13 +159,21 @@
29 if lineno > 20:
30 break
31 if len(line.strip()) == 0:
32- paragraphs.append(u'\n'.join(current_paragraph))
33- paragraphs.append('\n<p>\n')
34+ self._append_paragraph(paragraphs, current_paragraph)
35 current_paragraph = []
36 else:
37 current_paragraph.append(line)
38+ self._append_paragraph(paragraphs, current_paragraph)
39+ self.body_details = u''.join(paragraphs)
40+
41+ def _append_paragraph(self, paragraphs, current_paragraph):
42+ if len(current_paragraph) == 0:
43+ # There is nothing to append. The message has multiple
44+ # blank lines.
45+ return
46+ paragraphs.append(u'\n<p>\n')
47 paragraphs.append(u'\n'.join(current_paragraph))
48- self.body_details = u''.join(paragraphs)
49+ paragraphs.append(u'\n</p>\n')
50
51 def _remove_leading_blank_lines(self):
52 """Strip off any leading blank lines.
53
54=== modified file 'lib/lp/registry/browser/milestone.py'
55--- lib/lp/registry/browser/milestone.py 2009-12-05 18:37:28 +0000
56+++ lib/lp/registry/browser/milestone.py 2010-01-13 14:18:16 +0000
57@@ -214,7 +214,7 @@
58 """The list of non-conjoined bugtasks targeted to this milestone."""
59 user = getUtility(ILaunchBag).user
60 params = BugTaskSearchParams(user, milestone=self.context,
61- orderby=['-importance', 'datecreated', 'id'],
62+ orderby=['status', '-importance', 'id'],
63 omit_dupes=True)
64 tasks = getUtility(IBugTaskSet).search(params)
65 # We could replace all the code below with a simple
66
67=== added file 'lib/lp/registry/browser/tests/mailinglist-message-views.txt'
68--- lib/lp/registry/browser/tests/mailinglist-message-views.txt 1970-01-01 00:00:00 +0000
69+++ lib/lp/registry/browser/tests/mailinglist-message-views.txt 2010-01-13 14:18:16 +0000
70@@ -0,0 +1,74 @@
71+Held message view
72+-----------------
73+
74+The HeldMessageView formats a held message so that it can be moderated by
75+the mailing list administrator.
76+
77+ >>> from canonical.launchpad.interfaces import IMessageSet
78+
79+ >>> team, mlist = factory.makeTeamAndMailingList('parrot', 'owner')
80+
81+ >>> login('admin@canonical.com')
82+ >>> message_set = getUtility(IMessageSet)
83+ >>> message = message_set.fromEmail("""\
84+ ... From: Carlos <carlos@canonical.com>
85+ ... To: parrot@lists.launchpad.dev
86+ ... Subject: monkey
87+ ... Message-ID: <monkey>
88+ ... Date: Fri, 01 Aug 2000 01:09:00 -0000
89+ ...
90+ ... First paragraph.
91+ ...
92+ ... Second paragraph.
93+ ...
94+ ... Third paragraph.
95+ ... """)
96+ >>> held_message = mlist.holdMessage(message)
97+ >>> transaction.commit()
98+
99+ >>> login_person(team.teamowner)
100+ >>> view = create_initialized_view(
101+ ... held_message, name='+moderation', principal=team.teamowner)
102+ >>> print view.widget_name
103+ field.%3Cmonkey%3E
104+ >>> print view.message_id
105+ <monkey>
106+ >>> print view.subject
107+ monkey
108+ >>> print view.date
109+ 2000-08-01 01:09:00+00:00
110+ >>> print view.author
111+ <a href="http://launchpad.dev/~carlos">Carlos &lt;carlos@canonical.com&gt;</a>
112+ >>> print view.body_summary
113+ First paragraph.
114+ >>> print view.body_details
115+ <p>
116+ Second paragraph.
117+ </p>
118+ <p>
119+ Third paragraph.
120+ </p>
121+
122+
123+View helpers
124+------------
125+
126+The view uses a helper method to format the paragraphs.
127+
128+ >>> paragraphs = []
129+ >>> current_paragraph = ['line 1', 'line 2']
130+ >>> view._append_paragraph(paragraphs, current_paragraph)
131+ >>> for line in paragraphs:
132+ ... print line
133+ <p>
134+ line 1
135+ line 2
136+ </p>
137+
138+The _append_paragraph method ignores empty paragraphs.
139+
140+ >>> paragraphs = []
141+ >>> current_paragraph = []
142+ >>> view._append_paragraph(paragraphs, current_paragraph)
143+ >>> len(paragraphs)
144+ 0
145
146=== modified file 'lib/lp/registry/browser/tests/milestone-views.txt'
147--- lib/lp/registry/browser/tests/milestone-views.txt 2009-12-02 15:18:08 +0000
148+++ lib/lp/registry/browser/tests/milestone-views.txt 2010-01-13 14:18:16 +0000
149@@ -72,11 +72,14 @@
150 The has_bugs_or_specs boolean property can be used to verify if the
151 milestone has any bugs or specifications.
152
153+ >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
154+
155 >>> view.has_bugs_or_specs
156 False
157
158 >>> bug = factory.makeBug(title="kiwi")
159 >>> bugtask = factory.makeBugTask(bug=bug, target=milestone.product)
160+ >>> bugtask.transitionToStatus(BugTaskStatus.FIXCOMMITTED, person)
161 >>> bugtask.transitionToAssignee(person)
162 >>> bugtask.milestone = milestone
163 >>> spec = factory.makeSpecification(
164@@ -142,6 +145,7 @@
165
166 >>> bug = factory.makeBug(title="emo")
167 >>> bugtask = factory.makeBugTask(bug=bug, target=milestone.product)
168+ >>> bugtask.transitionToAssignee(person)
169 >>> bugtask.milestone = milestone
170 >>> spec = factory.makeSpecification(
171 ... product=milestone.product, title='ostrich')
172@@ -154,6 +158,15 @@
173 >>> print view.specification_count_text
174 <strong>2 blueprints</strong>
175
176+Bugtasks are ordered by status (fix released last), and importance
177+(critical first).
178+
179+ >>> for bugtask in view.bugtasks:
180+ ... assignee = bugtask.assignee
181+ ... print bugtask.bug.title, assignee.name, bugtask.status.title
182+ emo puffin-owner New
183+ kiwi puffin-owner Fix Committed
184+
185 The view provides a list of StatusCounts that summarise the targeted
186 specifications and bugtasks.
187
188@@ -174,7 +187,8 @@
189
190 >>> for status_count in view.bugtask_status_counts:
191 ... print '%s: %s' % (status_count.status.title, status_count.count)
192- New: 2
193+ New: 1
194+ Fix Committed: 1
195
196 The assignment_counts property returns all the users and count of bugs and
197 specifications assigned to them.
198@@ -711,4 +725,3 @@
199 >>> view = create_initialized_view(milestone, '+delete')
200 >>> check_permission('launchpad.Edit', view)
201 False
202-
203
204=== modified file 'lib/lp/registry/browser/tests/test_views.py'
205--- lib/lp/registry/browser/tests/test_views.py 2009-08-18 02:00:17 +0000
206+++ lib/lp/registry/browser/tests/test_views.py 2010-01-13 14:18:16 +0000
207@@ -21,6 +21,7 @@
208 # that require something special like the librarian or mailman must run
209 # on a layer that sets those services up.
210 special_test_layer = {
211+ 'mailinglist-message-views.txt': LaunchpadFunctionalLayer,
212 'milestone-views.txt': LaunchpadFunctionalLayer,
213 'person-views.txt': LaunchpadFunctionalLayer,
214 'user-to-user-views.txt': LaunchpadFunctionalLayer,
215
216=== modified file 'lib/lp/registry/doc/message-holds.txt'
217--- lib/lp/registry/doc/message-holds.txt 2009-05-12 21:39:35 +0000
218+++ lib/lp/registry/doc/message-holds.txt 2010-01-13 14:18:16 +0000
219@@ -377,58 +377,6 @@
220 ----------------------------------------
221
222
223-== Held message formatting ==
224-
225-When held messages are displayed in the u/i, they will be formatted in such a
226-way that we can show the body summary with an ellipsis to review more body
227-details. Because formatting email messages for the web can get tricky, we
228-take a fairly conservative 80/20 approach.
229-
230- >>> from lp.registry.browser.mailinglists import HeldMessageView
231- >>> from canonical.launchpad.interfaces import (
232- ... IHeldMessageDetails, IPersonSet)
233- >>> from zope.component import getUtility
234- >>> from zope.interface import implements
235-
236-First, we craft an object like what the view expects.
237-
238- >>> person = getUtility(IPersonSet).getByName('name12')
239- >>> class FakeHeldMessageDetails:
240- ... implements(IHeldMessageDetails)
241- ... message_id = '<zebra>'
242- ... subject = 'A subject'
243- ... date = 'Today'
244- ... author = person
245- ... sender = 'test@example.com'
246- ... body = """\
247- ... What are the Guadamen? I think I might want to be come one
248- ... but I'm not sure. Are they men who like guacamole? Or are
249- ... they men who live on Gauadalcanal? I used to live on Guadalcanal
250- ... and boy, do I sure like avocados!
251- ...
252- ... Please consider me for Guadamanhood.
253- ...
254- ... -Guy
255- ... """
256-
257- >>> view = HeldMessageView(FakeHeldMessageDetails(), None)
258- >>> view.initialize()
259- >>> view.widget_name
260- 'field.%3Czebra%3E'
261- >>> view.author
262- u'<a href="http://launchpad.dev/~name12">test@example.com</a>'
263- >>> view.body_summary
264- 'What are the Guadamen? I think I might want to be come one'
265- >>> print view.body_details
266- but I'm not sure. Are they men who like guacamole? Or are
267- they men who live on Gauadalcanal? I used to live on Guadalcanal
268- and boy, do I sure like avocados!
269- <p>
270- Please consider me for Guadamanhood.
271- <p>
272- -Guy
273-
274-
275 == Posting privileges ==
276
277 Message approvals are also used to derived posting privileges for non-team
278
279=== modified file 'lib/lp/registry/templates/milestone-index.pt'
280--- lib/lp/registry/templates/milestone-index.pt 2009-12-11 19:54:04 +0000
281+++ lib/lp/registry/templates/milestone-index.pt 2010-01-13 14:18:16 +0000
282@@ -147,11 +147,10 @@
283 <dl>
284 <dt>Assignees:</dt>
285 <dd tal:define="count_statuses view/assignment_counts">
286- <tal:statuses repeat="count_status count_statuses">
287- <strong tal:content="count_status/count">2</strong>&nbsp;<a
288+ <span class="pre"
289+ tal:repeat="count_status count_statuses"><strong tal:content="count_status/count">2</strong> <a
290 tal:replace="structure count_status/status/fmt:link" /><tal:comma
291- condition="not: repeat/count_status/end">,</tal:comma>
292- </tal:statuses>
293+ condition="not: repeat/count_status/end">,</tal:comma></span>
294 <tal:no-statuses condition="not: count_statuses">
295 No users assigned to blueprints and bugs.
296 </tal:no-statuses>