Merge lp:~allenap/launchpad/just-comment-on-question-bug-114710 into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Barry Warsaw
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~allenap/launchpad/just-comment-on-question-bug-114710
Merge into: lp:launchpad
Diff against target: 498 lines
8 files modified
lib/lp/answers/browser/question.py (+31/-17)
lib/lp/answers/browser/tests/views.txt (+16/-15)
lib/lp/answers/stories/question-edit.txt (+13/-8)
lib/lp/answers/stories/question-obfuscation.txt (+1/-1)
lib/lp/answers/stories/question-reject-and-change-status.txt (+11/-0)
lib/lp/answers/stories/question-workflow.txt (+41/-21)
lib/lp/answers/stories/this-is-a-faq.txt (+6/-6)
lib/lp/answers/templates/question-index.pt (+1/-1)
To merge this branch: bzr merge lp:~allenap/launchpad/just-comment-on-question-bug-114710
Reviewer Review Type Date Requested Status
Barry Warsaw (community) code ui* Approve
Review via email: mp+12405@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

The first change in this branch is to make the "Add comment" button
always appear. I just followed sinzui's patch in the bug report and
then updated tests. I've done other stuff in this branch, so I've
broken out just the changes that account for this fix here:

  http://pastebin.ubuntu.com/277696/

The other stuff was adding a few cancel links, changing some of the
form action wordings, and fixing a bit of lint:

  http://pastebin.ubuntu.com/277724/

To test:
  bin/test -vvt lp/answers

The only lint reported is bogus.

Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (4.4 KiB)

Wow, I am so psyched you are adding this. It's a huge annoyance every time
I'm CHR. Thanks!

We talked about the order of the buttons and since we want to encourage people
to answer the question, it seems like a better order would be "Add Answer"
"Add Information Request" "Add Comment".

We also discussed possible confusion that users may have as to what the
different actions mean. We agreed that it might be nice to add a (?) help
pop up but also that that's outside the scope of this branch.

I have only minor comments on style, but otherwise the code looks great.
merge-conditional, r=me with their consideration.

=== modified file 'lib/lp/answers/browser/question.py'
--- lib/lp/answers/browser/question.py 2009-09-22 15:43:52 +0000
+++ lib/lp/answers/browser/question.py 2009-09-25 09:01:14 +0000
> @@ -678,11 +683,16 @@
> editable_fields.append(field.__name__)
> self.form_fields = self.form_fields.select(*editable_fields)
>
> - @action(u"Continue", name="change")
> + @action(u"Save Changes", name="change")

Why do you use a u-string here but everywhere else you use the translation
marker _()? I think for consistency you should change this to
_('Save Changes')

> @@ -810,12 +823,10 @@
> def canAddComment(self, action):
> """Return whether the comment action should be displayed.
>
> - Comments (message without a status change) can be added when the
> - question is solved or invalid
> + Comments (message without a status change) can be added at any
> + time by any logged-in user.
> """
> - return (self.user is not None and
> - self.context.status in [
> - QuestionStatus.SOLVED, QuestionStatus.INVALID])
> + return (self.user is not None)

You don't need the parentheses any more.

=== modified file 'lib/lp/answers/stories/question-edit.txt'
--- lib/lp/answers/stories/question-edit.txt 2009-08-25 21:30:02 +0000
+++ lib/lp/answers/stories/question-edit.txt 2009-09-25 09:01:14 +0000
> @@ -14,6 +14,11 @@
> >>> user_browser.url
> '.../firefox/+question/2/+edit'
>
> +There is a cancel link should the user decide otherwise:
> +
> + >>> user_browser.getLink('Cancel').url
> + '.../firefox/+question/2'

You should print the url here and elsewhere to avoid the quote noise.

=== modified file 'lib/lp/answers/stories/question-reject-and-change-status.txt'
--- lib/lp/answers/stories/question-reject-and-change-status.txt 2009-08-25 21:30:02 +0000
+++ lib/lp/answers/stories/question-reject-and-change-status.txt 2009-09-25 09:01:14 +0000
> @@ -35,6 +35,12 @@
> There is 1 error.
> You must provide an explanation message.
>
> +At this point the user might decide this is a bad idea, so there is a
> +cancel link to take him back to the question:
> +
> + >>> admin_browser.getLink('Cancel').url
> + '.../firefox/+question/2'

print

> +
> Entering an explanation message and clicking the 'Reject' button,
> will reject the question.
>
> @@ -108,6 +114,11 @@
> >>> admin_browser.getControl('Status', index=0).displayValue
> ['Invalid']
>
> +There is also a cancel link should the user decid...

Read more...

review: Approve (code ui*)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/answers/browser/question.py'
2--- lib/lp/answers/browser/question.py 2009-09-24 11:10:26 +0000
3+++ lib/lp/answers/browser/question.py 2009-09-29 15:26:13 +0000
4@@ -639,7 +639,12 @@
5 self.context.setStatus(self.user, data['status'], data['message'])
6 self.request.response.addNotification(
7 _('Question status updated.'))
8- self.request.response.redirect(canonical_url(self.context))
9+
10+ @property
11+ def next_url(self):
12+ return canonical_url(self.context)
13+
14+ cancel_url = next_url
15
16
17 class QuestionEditView(QuestionSupportLanguageMixin, LaunchpadEditFormView):
18@@ -678,11 +683,16 @@
19 editable_fields.append(field.__name__)
20 self.form_fields = self.form_fields.select(*editable_fields)
21
22- @action(u"Continue", name="change")
23+ @action(_("Save Changes"), name="change")
24 def change_action(self, action, data):
25 """Update the Question from the request form data."""
26 self.updateContextFromData(data)
27- self.request.response.redirect(canonical_url(self.context))
28+
29+ @property
30+ def next_url(self):
31+ return canonical_url(self.context)
32+
33+ cancel_url = next_url
34
35
36 class QuestionRejectView(LaunchpadFormView):
37@@ -701,15 +711,12 @@
38 self.setFieldError(
39 'message', _('You must provide an explanation message.'))
40
41- @action(_('Reject'))
42+ @action(_('Reject Question'), name="reject")
43 def reject_action(self, action, data):
44 """Reject the Question."""
45 self.context.reject(self.user, data['message'])
46 self.request.response.addNotification(
47 _('You have rejected this question.'))
48- self.request.response.redirect(canonical_url(self.context))
49- return ''
50-
51
52 def initialize(self):
53 """See `LaunchpadFormView`.
54@@ -724,6 +731,12 @@
55
56 LaunchpadFormView.initialize(self)
57
58+ @property
59+ def next_url(self):
60+ return canonical_url(self.context)
61+
62+ cancel_url = next_url
63+
64
65 class LinkFAQMixin:
66 """Mixin that contains common functionality for views linking a FAQ."""
67@@ -814,12 +827,10 @@
68 def canAddComment(self, action):
69 """Return whether the comment action should be displayed.
70
71- Comments (message without a status change) can be added when the
72- question is solved or invalid
73+ Comments (message without a status change) can be added at any
74+ time by any logged-in user.
75 """
76- return (self.user is not None and
77- self.context.status in [
78- QuestionStatus.SOLVED, QuestionStatus.INVALID])
79+ return self.user is not None
80
81 @action(_('Add Comment'), name='comment', condition=canAddComment)
82 def comment_action(self, action, data):
83@@ -1090,9 +1101,7 @@
84 """View to create a new FAQ."""
85
86 schema = IFAQ
87-
88- page_title = _('Create a new FAQ')
89- label = page_title
90+ label = _('Create a new FAQ')
91
92 @property
93 def page_title(self):
94@@ -1303,10 +1312,15 @@
95 if self.context.faq == data.get('faq'):
96 self.setFieldError('faq', _("You didn't modify the linked FAQ."))
97
98- @action(_('Link FAQ'), name="link")
99+ @action(_('Link to FAQ'), name="link")
100 def link_action(self, action, data):
101 """Link the selected FAQ to the question."""
102 if data['faq'] is not None:
103 data['message'] += '\n' + self.getFAQMessageReference(data['faq'])
104 self.context.linkFAQ(self.user, data['faq'], data['message'])
105- self.next_url = canonical_url(self.context)
106+
107+ @property
108+ def next_url(self):
109+ return canonical_url(self.context)
110+
111+ cancel_url = next_url
112
113=== modified file 'lib/lp/answers/browser/tests/views.txt'
114--- lib/lp/answers/browser/tests/views.txt 2009-09-19 14:36:11 +0000
115+++ lib/lp/answers/browser/tests/views.txt 2009-09-29 15:26:13 +0000
116@@ -102,21 +102,21 @@
117 >>> getAvailableActionNames(workflow_harness.view)
118 []
119
120-When question is in the OPEN state, the owner can either answer his own
121-question or provide more information.
122+When question is in the OPEN state, the owner can comment, answer his
123+own question or provide more information.
124
125 >>> login('test@canonical.com')
126 >>> workflow_harness.submit('', {})
127 >>> getAvailableActionNames(workflow_harness.view)
128- ['giveinfo', 'selfanswer']
129+ ['comment', 'giveinfo', 'selfanswer']
130
131-But when another user sees the question, he can provide an answer or
132-request for more information.
133+But when another user sees the question, he can comment, provide an
134+answer or request more information.
135
136 >>> login('no-priv@canonical.com')
137 >>> workflow_harness.submit('', {})
138 >>> getAvailableActionNames(workflow_harness.view)
139- ['answer', 'requestinfo']
140+ ['answer', 'comment', 'requestinfo']
141
142 When the other user requests for more information, a confirmation is
143 displayed, the question status is changed to NEEDSINFO and the user is
144@@ -141,18 +141,18 @@
145 >>> len(pop_notifications())
146 1
147
148-The available actions for that other user are still between giving an
149-answer or requesting for more information:
150+The available actions for that other user are still comment, give an
151+answer or request more information:
152
153 >>> getAvailableActionNames(workflow_harness.view)
154- ['answer', 'requestinfo']
155+ ['answer', 'comment', 'requestinfo']
156
157-And the question owner still has the same possibilities then initially:
158+And the question owner still has the same possibilities as at first:
159
160 >>> login('test@canonical.com')
161 >>> workflow_harness.submit('', {})
162 >>> getAvailableActionNames(workflow_harness.view)
163- ['giveinfo', 'selfanswer']
164+ ['comment', 'giveinfo', 'selfanswer']
165
166 If he replies with the requested information, the question is moved back
167 to the OPEN state.
168@@ -185,14 +185,15 @@
169 >>> workflow_harness.redirectionTarget()
170 '.../+question/2'
171
172-Once the question is answered, the set of possible actions for the question
173-owner changes. He can now either confirm the answer, answer the problem
174-himself, or reopen the request because that answer isn't working.
175+Once the question is answered, the set of possible actions for the
176+question owner changes. He can now either comment, confirm the answer,
177+answer the problem himself, or reopen the request because that answer
178+isn't working.
179
180 >>> login('test@canonical.com')
181 >>> workflow_harness.submit('', {})
182 >>> getAvailableActionNames(workflow_harness.view)
183- ['confirm', 'reopen', 'selfanswer']
184+ ['comment', 'confirm', 'reopen', 'selfanswer']
185
186 Let's say he confirms the previous answer, in this case, the question will
187 move to the 'SOLVED' state. Note that the UI doesn't enable the user to
188
189=== modified file 'lib/lp/answers/stories/question-edit.txt'
190--- lib/lp/answers/stories/question-edit.txt 2009-09-24 13:45:05 +0000
191+++ lib/lp/answers/stories/question-edit.txt 2009-09-29 15:26:13 +0000
192@@ -11,8 +11,13 @@
193
194 >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
195 >>> user_browser.getLink('Edit question').click()
196- >>> user_browser.url
197- '.../firefox/+question/2/+edit'
198+ >>> print user_browser.url
199+ http://answers.launchpad.dev/firefox/+question/2/+edit
200+
201+There is a cancel link should the user decide otherwise:
202+
203+ >>> print user_browser.getLink('Cancel').url
204+ http://answers.launchpad.dev/firefox/+question/2
205
206 When we post the form, we should be redirected back to the question page.
207
208@@ -22,10 +27,10 @@
209 >>> user_browser.getControl('Description').value = description
210 >>> summary = "Problem showing the SVG demo on W3C web site"
211 >>> user_browser.getControl('Summary').value = summary
212- >>> user_browser.getControl('Continue').click()
213+ >>> user_browser.getControl('Save Changes').click()
214
215- >>> user_browser.url
216- '.../firefox/+question/2'
217+ >>> print user_browser.url
218+ http://answers.launchpad.dev/firefox/+question/2
219
220 And viewing that page should show the updated information.
221
222@@ -61,7 +66,7 @@
223 >>> user_browser.getLink('Edit question').click()
224 >>> user_browser.getControl(
225 ... name='field.target.package').value = 'linux-source-2.6.15'
226- >>> user_browser.getControl('Continue').click()
227+ >>> user_browser.getControl('Save Changes').click()
228
229 Product questions ignore sourcepackage information if it is submitted:
230
231@@ -69,7 +74,7 @@
232 >>> user_browser.getLink('Edit question').click()
233 >>> user_browser.getControl(
234 ... name='field.target.package').value = 'linux-source-2.6.15'
235- >>> user_browser.getControl('Continue').click()
236+ >>> user_browser.getControl('Save Changes').click()
237
238
239 == Changing Other Metadata ==
240@@ -85,7 +90,7 @@
241
242 >>> browser.getControl('Assignee').value = 'name16'
243 >>> browser.getControl('Status Whiteboard').value = 'Some note'
244- >>> browser.getControl('Continue').click()
245+ >>> browser.getControl('Save Changes').click()
246
247 >>> soup = find_main_content(browser.contents)
248 >>> print soup.first('b', text='Whiteboard:').findNext('td').renderContents()
249
250=== modified file 'lib/lp/answers/stories/question-obfuscation.txt'
251--- lib/lp/answers/stories/question-obfuscation.txt 2009-09-23 14:40:53 +0000
252+++ lib/lp/answers/stories/question-obfuscation.txt 2009-09-29 15:26:13 +0000
253@@ -51,7 +51,7 @@
254 >>> user_browser.getControl(name='field.faq-query').value = 'voip'
255 >>> user_browser.getControl('Search', index=0).click()
256 >>> user_browser.getControl('4').selected = True
257- >>> user_browser.getControl('Link FAQ').click()
258+ >>> user_browser.getControl('Link to FAQ').click()
259 >>> user_browser.getLink('How can I make VOIP calls?').click()
260 >>> print user_browser.title
261 Questions : Ubuntu
262
263=== modified file 'lib/lp/answers/stories/question-reject-and-change-status.txt'
264--- lib/lp/answers/stories/question-reject-and-change-status.txt 2009-09-24 13:45:05 +0000
265+++ lib/lp/answers/stories/question-reject-and-change-status.txt 2009-09-29 15:26:13 +0000
266@@ -35,6 +35,12 @@
267 There is 1 error.
268 You must provide an explanation message.
269
270+At this point the user might decide this is a bad idea, so there is a
271+cancel link to take him back to the question:
272+
273+ >>> print admin_browser.getLink('Cancel').url
274+ http://answers.launchpad.dev/firefox/+question/2
275+
276 Entering an explanation message and clicking the 'Reject' button,
277 will reject the question.
278
279@@ -108,6 +114,11 @@
280 >>> admin_browser.getControl('Status', index=0).displayValue
281 ['Invalid']
282
283+There is also a cancel link should the user decide otherwise:
284+
285+ >>> print admin_browser.getLink('Cancel').url
286+ http://answers.launchpad.dev/firefox/+question/2
287+
288 The user needs to select a status and enter a message explaining the
289 status change:
290
291
292=== modified file 'lib/lp/answers/stories/question-workflow.txt'
293--- lib/lp/answers/stories/question-workflow.txt 2009-09-24 13:45:05 +0000
294+++ lib/lp/answers/stories/question-workflow.txt 2009-09-29 15:26:13 +0000
295@@ -17,11 +17,13 @@
296 ... print extract_text(
297 ... find_tag_by_id(contents, 'registration'))
298
299-
300 >>> def find_last_comment(contents):
301 ... soup = find_main_content(contents)
302 ... return soup.fetch('div', 'boardCommentBody')[-1]
303
304+ >>> def print_last_comment(contents):
305+ ... print extract_text(find_last_comment(contents))
306+
307
308 == Logging In ==
309
310@@ -69,9 +71,8 @@
311
312 >>> find_request_status(support_browser.contents)
313 Needs information...
314- >>> print find_last_comment(
315- ... support_browser.contents).renderContents()
316- <p>Can you provide an example of an URL displaying the problem?</p>
317+ >>> print_last_comment(support_browser.contents)
318+ Can you provide an example of an URL displaying the problem?
319
320 Of course, if you don't add a message, clicking on the button will give
321 you an error.
322@@ -82,6 +83,25 @@
323 Please enter a message.
324
325
326+== Adding a Comment ==
327+
328+A comment can be added at any point without altering the status. The
329+user simply enters the comment in the 'Message' box and clicks the
330+'Add Comment' button.
331+
332+ >>> support_browser.getControl('Message').value = (
333+ ... "I forgot to mention, in the meantime here is a workaround...")
334+ >>> support_browser.getControl('Add Comment').click()
335+
336+This appends the comment to the question and it doesn't change its
337+status:
338+
339+ >>> print find_request_status(support_browser.contents)
340+ Needs information...
341+ >>> print_last_comment(support_browser.contents)
342+ I forgot to mention, in the meantime here is a workaround...
343+
344+
345 == Answering with More Information ==
346
347 When the question is in the 'Needs information' state, it means that
348@@ -109,9 +129,9 @@
349
350 >>> print find_request_status(owner_browser.contents)
351 Open...
352- >>> print find_last_comment(owner_browser.contents).renderContents()
353- <p>The following SVG doesn't display properly:<br />
354- <a rel="nofollow" href="http://www.w3.org/2001/08/rdfweb/rdfweb-chaals-and-dan.svg">...</a></p>
355+ >>> print_last_comment(owner_browser.contents)
356+ The following SVG doesn't display properly:
357+ http://www.w3.org/2001/08/rdfweb/rdfweb-chaals-and-dan.svg
358
359
360 == Giving an Answer ==
361@@ -132,10 +152,9 @@
362
363 >>> print find_request_status(support_browser.contents)
364 Answered...
365- >>> print find_last_comment(
366- ... support_browser.contents).renderContents()
367- <p>New version of the firefox package are available with SVG support
368- enabled. You can use apt-get or adept to upgrade.</p>
369+ >>> print_last_comment(support_browser.contents)
370+ New version of the firefox package are available with SVG support
371+ enabled. You can use apt-get or adept to upgrade.
372
373
374 == Confirming an Answer ==
375@@ -175,8 +194,8 @@
376 Since no message can be provided when that button is clicked. A default
377 confirmation message was appended to the question discussion:
378
379- >>> print find_last_comment(owner_browser.contents).renderContents()
380- <p>Thanks No Privileges Person, that solved my question.</p>
381+ >>> print_last_comment(owner_browser.contents)
382+ Thanks No Privileges Person, that solved my question.
383
384 The confirmed answer is also highlighted.
385
386@@ -199,7 +218,7 @@
387 Link to a FAQ
388
389
390-== Adding a Comment ==
391+== Adding another Comment ==
392
393 When the question is Solved, it is still possible to add comments to it.
394 The user simply enters the comment in the 'Message' box and clicks the
395@@ -214,8 +233,8 @@
396
397 >>> print find_request_status(owner_browser.contents)
398 Solved...
399- >>> print find_last_comment(owner_browser.contents).renderContents()
400- <p>The example now displays correctly. Thanks.</p>
401+ >>> print_last_comment(owner_browser.contents)
402+ The example now displays correctly. Thanks.
403
404
405 == Reopening ==
406@@ -237,11 +256,11 @@
407
408 >>> print find_request_status(owner_browser.contents)
409 Open...
410- >>> print find_last_comment(owner_browser.contents).renderContents()
411- <p>Actually, there are still SVGs that do not display correctly.
412- For example, the following<br />
413- <a rel="nofollow" href="http://people.w3.org/maxf/ChessGML/immortal.svg">...</a> doesn't
414- display correctly.</p>
415+ >>> print_last_comment(owner_browser.contents)
416+ Actually, there are still SVGs that do not display correctly.
417+ For example, the following
418+ http://people.w3.org/maxf/ChessGML/immortal.svg doesn't
419+ display correctly.
420
421 This also removes the highlighting from the previous answer and sets
422 the answerer back to None.
423@@ -346,6 +365,7 @@
424 ... new_status = cells[3].renderContents()
425 ... print who.lstrip('&nbsp;'), action, new_status
426 No Privileges Person Request for more information Needs information
427+ No Privileges Person Comment Needs information
428 Sample Person Give more information Open
429 No Privileges Person Answer Answered
430 Sample Person Confirm Solved
431
432=== modified file 'lib/lp/answers/stories/this-is-a-faq.txt'
433--- lib/lp/answers/stories/this-is-a-faq.txt 2009-09-24 13:45:05 +0000
434+++ lib/lp/answers/stories/this-is-a-faq.txt 2009-09-29 15:26:13 +0000
435@@ -92,11 +92,11 @@
436 >>> print user_browser.getControl('Message').value
437 No Privileges Person suggests this article as an answer to your question:
438
439-He can then click 'Link FAQ' to answer the question with the selected
440+He can then click 'Link to FAQ' to answer the question with the selected
441 FAQ. After clicking the button, the user is redirected to the question
442 page.
443
444- >>> user_browser.getControl('Link FAQ').click()
445+ >>> user_browser.getControl('Link to FAQ').click()
446 >>> print user_browser.url
447 http://answers.launchpad.dev/firefox/+question/2
448
449@@ -145,11 +145,11 @@
450 ( ) 9: How do I troubleshoot problems with extensions/themes?
451 ( ) 7: How do I install Java?
452
453-He changes the message and click 'Link FAQ'.
454+He changes the message and click 'Link to FAQ'.
455
456 >>> user_browser.getControl('Message').value = (
457 ... "Sorry, this document doesn't really answer your question.")
458- >>> user_browser.getControl('Link FAQ').click()
459+ >>> user_browser.getControl('Link to FAQ').click()
460
461 But since, he forgot to change the link, the form is displayed again
462 with an error message.
463@@ -165,7 +165,7 @@
464 submit the form again.
465
466 >>> user_browser.getControl('No existing FAQs').selected = True
467- >>> user_browser.getControl('Link FAQ').click()
468+ >>> user_browser.getControl('Link to FAQ').click()
469
470 The new message was added to the question:
471
472@@ -359,7 +359,7 @@
473 >>> user_browser.getControl('Search', index=0).click()
474 >>> user_browser.getControl('6').selected = True
475 >>> user_browser.getControl('Message').value = "The FAQ mentions this:"
476- >>> user_browser.getControl('Link FAQ').click()
477+ >>> user_browser.getControl('Link to FAQ').click()
478
479 The question is still solved. No Privileges Person sees the FAQ was
480 added to the question, and his message was added to the question's
481
482=== modified file 'lib/lp/answers/templates/question-index.pt'
483--- lib/lp/answers/templates/question-index.pt 2009-09-24 11:10:26 +0000
484+++ lib/lp/answers/templates/question-index.pt 2009-09-29 15:26:13 +0000
485@@ -143,12 +143,12 @@
486 </tal:comment>
487 <div class="actions" metal:fill-slot="buttons">
488 <div>
489- <input tal:replace="structure view/comment_action/render" />
490 <input tal:replace="structure view/answer_action/render" />
491 <input tal:replace="structure view/selfanswer_action/render" />
492 <input tal:replace="structure view/requestinfo_action/render" />
493 <input tal:replace="structure view/giveinfo_action/render" />
494 <input tal:replace="structure view/reopen_action/render" />
495+ <input tal:replace="structure view/comment_action/render" />
496 </div>
497 <p id="answer-button-hint"
498 tal:condition="view/confirm_action/available">