Merge lp:~thumper/launchpad/descriptions-for-merge-proposals into lp:launchpad/db-devel

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: not available
Merged at revision: 9035
Proposed branch: lp:~thumper/launchpad/descriptions-for-merge-proposals
Merge into: lp:launchpad/db-devel
Diff against target: 1275 lines (+269/-246)
27 files modified
.bzrignore (+1/-0)
database/schema/patch-2207-33-0.sql (+9/-0)
lib/canonical/launchpad/javascript/code/codereview.js (+49/-34)
lib/lp/code/browser/branch.py (+5/-3)
lib/lp/code/browser/branchmergeproposal.py (+40/-1)
lib/lp/code/browser/configure.zcml (+7/-0)
lib/lp/code/browser/tests/test_branchmergeproposal.py (+9/-20)
lib/lp/code/configure.zcml (+6/-2)
lib/lp/code/doc/branch-merge-proposal-notifications.txt (+1/-1)
lib/lp/code/doc/codereviewcomment.txt (+2/-8)
lib/lp/code/interfaces/branch.py (+15/-18)
lib/lp/code/interfaces/branchmergeproposal.py (+7/-3)
lib/lp/code/mail/branchmergeproposal.py (+12/-19)
lib/lp/code/mail/codehandler.py (+4/-7)
lib/lp/code/mail/tests/test_branchmergeproposal.py (+0/-3)
lib/lp/code/mail/tests/test_codehandler.py (+24/-25)
lib/lp/code/model/branch.py (+4/-7)
lib/lp/code/model/branchmergeproposal.py (+3/-12)
lib/lp/code/model/tests/test_branch.py (+1/-3)
lib/lp/code/model/tests/test_branchmergeproposals.py (+1/-31)
lib/lp/code/model/tests/test_codereviewcomment.py (+0/-3)
lib/lp/code/stories/branches/xx-branchmergeproposals.txt (+1/-1)
lib/lp/code/stories/branches/xx-claiming-team-code-reviews.txt (+2/-1)
lib/lp/code/stories/webservice/xx-branchmergeproposal.txt (+3/-15)
lib/lp/code/templates/branchmergeproposal-index.pt (+53/-24)
lib/lp/code/windmill/tests/test_branchmergeproposal_commitmessage.py (+3/-3)
lib/lp/testing/factory.py (+7/-2)
To merge this branch: bzr merge lp:~thumper/launchpad/descriptions-for-merge-proposals
Reviewer Review Type Date Requested Status
Björn Tillenius (community) db Approve
Stuart Bishop (community) db Approve
Paul Hummer (community) code js Approve
Review via email: mp+19564@code.launchpad.net

Commit message

Add a description to merge proposals. Also removes the initial comment and sets the description instead.

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Right now, I just want to check on the size of the diff. Really should be another way to do this :)

Add an editable description to merge proposals, so a little more like bugs.

Revision history for this message
Tim Penhey (thumper) wrote :

Here is a picture with neither the commit message nor the description set:
  http://people.canonical.com/~tim/description-1.png

With a commit message but no description:
  http://people.canonical.com/~tim/description-2.png

With commit message and description set:
http://people.canonical.com/~tim/description-3.png
http://people.canonical.com/~tim/description-4.png

Revision history for this message
Tim Penhey (thumper) wrote :

Damn, hit the wrong key...

Pic 4 contains description but not commit message.

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

206 @property
207 + def description_html(self):
208 + """The description as widget HTML."""
209 + description = self.context.description
210 + if description is None:
211 + description = ''
212 + formatter = FormattersAPI
213 + hide_email = formatter(description).obfuscate_email()
214 + description = formatter(hide_email).text_to_html()
215 + return TextAreaEditorWidget(
216 + self.context,
217 + 'description',
218 + canonical_url(self.context, view_name='+edit-description'),
219 + id="edit-description",
220 + title="Description of the Change",
221 + value=description,
222 + accept_empty=True)
223 +
224 + @property

I don't particularly like this, but it's not an issue that you really need to worry about. I'll bring it up in the next reviewer meeting.

review: Approve (code js)
Revision history for this message
Stuart Bishop (stub) wrote :

Fine if BranchMergeProposal.description really should be NULLable. patch-2207-33-0.sql.

If there will always be a description, I can cook up some data migration in the patch to copy the text from the first message attached to the BranchMergeProposal.

review: Approve (db)
Revision history for this message
Björn Tillenius (bjornt) wrote :

I'm approving this, since I think it's small enough and changes can be done later.

However, I would like to start a discussion on how similar to bugs this should be. If you have comments and description, it would be good to be consistent with other parts of Launchpad. The commenting system in merge proposals are similar enough to bugs, so having them work the same way would be beneficial.

review: Approve (db)
Revision history for this message
Tim Penhey (thumper) wrote :

On Wed, 24 Feb 2010 19:36:27 Björn Tillenius wrote:
> Review: Approve db
> I'm approving this, since I think it's small enough and changes can be done
> later.
>
> However, I would like to start a discussion on how similar to bugs this
> should be. If you have comments and description, it would be good to be
> consistent with other parts of Launchpad. The commenting system in merge
> proposals are similar enough to bugs, so having them work the same way
> would be beneficial.

Ah yes. Francis and I were talking about this earlier. We need to come up
with an architectural solution to remembering history.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2010-01-22 02:30:19 +0000
+++ .bzrignore 2010-02-24 08:55:31 +0000
@@ -58,4 +58,5 @@
58.subversion58.subversion
59lib/canonical/buildd/launchpad-files59lib/canonical/buildd/launchpad-files
60.testrepository60.testrepository
61.memcache.pid
61./pipes62./pipes
6263
=== added file 'database/schema/patch-2207-33-0.sql'
--- database/schema/patch-2207-33-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2207-33-0.sql 2010-02-24 08:55:31 +0000
@@ -0,0 +1,9 @@
1-- Copyright 2010 Canonical Ltd. This software is licensed under the
2-- GNU Affero General Public License version 3 (see the file LICENSE).
3
4SET client_min_messages=ERROR;
5
6ALTER TABLE BranchMergeProposal
7 ADD COLUMN description TEXT;
8
9INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 33, 0);
010
=== modified file 'lib/canonical/launchpad/javascript/code/codereview.js'
--- lib/canonical/launchpad/javascript/code/codereview.js 2010-01-29 16:00:43 +0000
+++ lib/canonical/launchpad/javascript/code/codereview.js 2010-02-24 08:55:31 +0000
@@ -39,22 +39,9 @@
39 */39 */
40 link.on('click', show_request_review_form);40 link.on('click', show_request_review_form);
41 }41 }
42 link = Y.one('.menu-link-set_commit_message');42
43 if (Y.Lang.isValue(link)) {43 link_multiline_editor('commit_message');
44 link.addClass('js-action');44 link_multiline_editor('description');
45 link.on('click', edit_commit_message);
46 }
47 if (Y.Lang.isValue(Y.lp.widgets)) {
48 var widget = Y.lp.widgets['edit-commit-message'];
49 if (Y.Lang.isValue(widget)) {
50 widget.editor.on('save', function() {
51 commit_message_listener(this.get('value'), true);
52 });
53 widget.editor.on('cancel', function() {
54 commit_message_listener(this.get('value'), false);
55 });
56 }
57 }
58 link_scroller('#proposal-summary a.diff-link', '#review-diff');45 link_scroller('#proposal-summary a.diff-link', '#review-diff');
59 link_scroller('.menu-link-add_comment', '#add-comment', function() {46 link_scroller('.menu-link-add_comment', '#add-comment', function() {
60 Y.one('#add-comment-form textarea').focus();47 Y.one('#add-comment-form textarea').focus();
@@ -82,24 +69,54 @@
82 });69 });
83}70}
8471
85/*72
86 * Hide the commit message editor if the value is empty.73/*
74 * Make the edit link a javascript link (green).
75 * Link the listener to the save and cancel events of the multiline editor.
76 */
77function link_multiline_editor(name) {
78 var link = Y.one('.menu-link-set_' + name);
79 if (Y.Lang.isValue(link)) {
80 link.addClass('js-action');
81 link.on('click', function(e) {
82 hide_link_show_multiline_edit(e, name);}
83 );
84 var parent = link.ancestor();
85 if (parent.hasClass('unseen')) {
86 link.addClass('unseen');
87 parent.removeClass('unseen');
88 }
89 }
90 if (Y.Lang.isValue(Y.lp.widgets)) {
91 var widget = Y.lp.widgets['edit-' + name];
92 if (Y.Lang.isValue(widget)) {
93 widget.editor.on('save', function() {
94 multiline_edit_message_listener(name, this.get('value'), true);
95 });
96 widget.editor.on('cancel', function() {
97 multiline_edit_message_listener(name, this.get('value'), false);
98 });
99 }
100 }
101}
102
103
104/*
105 * Hide the editor if the value is empty.
87 *106 *
88 * If the commit message is empty, we want to show the107 * If the value is empty, we want to show the 'Set commit message' link again.
89 * 'Set commit message' link again. For consistency with108 * For consistency with page updates we want to flash this link so the user
90 * page updates we want to flash this link so the user can109 * can see what we are doing. If the commit message was saved and is empty,
91 * see what we are doing. If the commit message was saved110 * then we flash green as all is good. If the user has cancelled the edit,
92 * and is empty, then we flash green as all is good. If the111 * and the commit message is empty, then we flash the link red.
93 * user has cancelled the edit, and the commit message is
94 * empty, then we flash the link red.
95 */112 */
96function commit_message_listener(message, saved)113function multiline_edit_message_listener(name, message, saved)
97{114{
98 if (message === '') {115 if (message === '') {
99 // Hide the multiline editor116 // Hide the multiline editor
100 Y.one('#edit-commit-message').addClass('unseen');117 Y.one('#edit-' + name).addClass('unseen');
101 // Show the link again118 // Show the link again
102 var link = Y.one('.menu-link-set_commit_message');119 var link = Y.one('.menu-link-set_' + name);
103 link.removeClass('unseen');120 link.removeClass('unseen');
104 if (saved) {121 if (saved) {
105 // Flash green.122 // Flash green.
@@ -113,19 +130,17 @@
113}130}
114131
115/*132/*
116 * Edit the commit message.
117 *
118 * Hide the link, show the multi-line editor, and set it to edit.133 * Hide the link, show the multi-line editor, and set it to edit.
119 */134 */
120function edit_commit_message(e) {135function hide_link_show_multiline_edit(e, name) {
121 // We are handling this click event.136 // We are handling this click event.
122 e.halt();137 e.halt();
123 // Make the edit button unseen.138 // Make the edit button unseen.
124 Y.one('#edit-commit-message').removeClass('unseen');139 Y.one('#edit-' + name).removeClass('unseen');
125 // Remove the unseen class from the commit message.140 // Remove the unseen class from the commit message.
126 Y.one('.menu-link-set_commit_message').addClass('unseen');141 Y.one('.menu-link-set_' + name).addClass('unseen');
127 // Trigger the edit on the multiline editor.142 // Trigger the edit on the multiline editor.
128 Y.lp.widgets['edit-commit-message']._triggerEdit(e);143 Y.lp.widgets['edit-' + name]._triggerEdit(e);
129}144}
130145
131/*146/*
132147
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2010-02-23 16:12:55 +0000
+++ lib/lp/code/browser/branch.py 2010-02-24 08:55:31 +0000
@@ -1189,8 +1189,10 @@
1189 ' will not be shown in the diff.)'))1189 ' will not be shown in the diff.)'))
11901190
1191 comment = Text(1191 comment = Text(
1192 title=_('Initial Comment'), required=False,1192 title=_('Description of the Change'), required=False,
1193 description=_('Describe your change.'))1193 description=_('Describe what changes your branch introduces, '
1194 'what bugs it fixes, or what features it implements. '
1195 'Ideally include rationale and how to test.'))
11941196
1195 reviewer = copy_field(1197 reviewer = copy_field(
1196 ICodeReviewVoteReference['reviewer'], required=False)1198 ICodeReviewVoteReference['reviewer'], required=False)
@@ -1260,7 +1262,7 @@
1260 registrant=registrant, target_branch=target_branch,1262 registrant=registrant, target_branch=target_branch,
1261 prerequisite_branch=prerequisite_branch,1263 prerequisite_branch=prerequisite_branch,
1262 needs_review=data['needs_review'],1264 needs_review=data['needs_review'],
1263 initial_comment=data.get('comment'),1265 description=data.get('comment'),
1264 review_requests=review_requests,1266 review_requests=review_requests,
1265 commit_message=data.get('commit_message'))1267 commit_message=data.get('commit_message'))
1266 self.next_url = canonical_url(proposal)1268 self.next_url = canonical_url(proposal)
12671269
=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
--- lib/lp/code/browser/branchmergeproposal.py 2010-02-05 21:59:26 +0000
+++ lib/lp/code/browser/branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -15,6 +15,7 @@
15 'BranchMergeProposalContextMenu',15 'BranchMergeProposalContextMenu',
16 'BranchMergeProposalDeleteView',16 'BranchMergeProposalDeleteView',
17 'BranchMergeProposalDequeueView',17 'BranchMergeProposalDequeueView',
18 'BranchMergeProposalDescriptionEditView',
18 'BranchMergeProposalEditMenu',19 'BranchMergeProposalEditMenu',
19 'BranchMergeProposalEditView',20 'BranchMergeProposalEditView',
20 'BranchMergeProposalEnqueueView',21 'BranchMergeProposalEnqueueView',
@@ -197,6 +198,11 @@
197 return Link('+edit', text, icon='edit', enabled=enabled)198 return Link('+edit', text, icon='edit', enabled=enabled)
198199
199 @enabled_with_permission('launchpad.Edit')200 @enabled_with_permission('launchpad.Edit')
201 def set_description(self):
202 text = 'Set description'
203 return Link('+edit-description', text, icon='add')
204
205 @enabled_with_permission('launchpad.Edit')
200 def set_commit_message(self):206 def set_commit_message(self):
201 text = 'Set commit message'207 text = 'Set commit message'
202 enabled = self.context.isMergable()208 enabled = self.context.isMergable()
@@ -295,6 +301,7 @@
295 'add_comment',301 'add_comment',
296 'dequeue',302 'dequeue',
297 'set_commit_message',303 'set_commit_message',
304 'set_description',
298 'edit_status',305 'edit_status',
299 'enqueue',306 'enqueue',
300 'merge',307 'merge',
@@ -662,6 +669,24 @@
662 for bug in self.context.related_bugs]669 for bug in self.context.related_bugs]
663670
664 @property671 @property
672 def description_html(self):
673 """The description as widget HTML."""
674 description = self.context.description
675 if description is None:
676 description = ''
677 formatter = FormattersAPI
678 hide_email = formatter(description).obfuscate_email()
679 description = formatter(hide_email).text_to_html()
680 return TextAreaEditorWidget(
681 self.context,
682 'description',
683 canonical_url(self.context, view_name='+edit-description'),
684 id="edit-description",
685 title="Description of the Change",
686 value=description,
687 accept_empty=True)
688
689 @property
665 def commit_message_html(self):690 def commit_message_html(self):
666 """The commit message as widget HTML."""691 """The commit message as widget HTML."""
667 commit_message = self.context.commit_message692 commit_message = self.context.commit_message
@@ -674,7 +699,7 @@
674 self.context,699 self.context,
675 'commit_message',700 'commit_message',
676 canonical_url(self.context, view_name='+edit-commit-message'),701 canonical_url(self.context, view_name='+edit-commit-message'),
677 id="edit-commit-message",702 id="edit-commit_message",
678 title="Commit Message",703 title="Commit Message",
679 value=commit_message,704 value=commit_message,
680 accept_empty=True)705 accept_empty=True)
@@ -977,6 +1002,20 @@
977 self.updateContextFromData(data)1002 self.updateContextFromData(data)
9781003
9791004
1005class BranchMergeProposalDescriptionEditView(MergeProposalEditView):
1006 """The view to edit the description of merge proposals."""
1007
1008 schema = IBranchMergeProposal
1009 label = "Edit merge proposal description"
1010 page_title = label
1011 field_names = ['description']
1012
1013 @action('Update', name='update')
1014 def update_action(self, action, data):
1015 """Update the commit message."""
1016 self.updateContextFromData(data)
1017
1018
980class BranchMergeProposalDeleteView(MergeProposalEditView):1019class BranchMergeProposalDeleteView(MergeProposalEditView):
981 """The view to control the deletion of merge proposals."""1020 """The view to control the deletion of merge proposals."""
982 schema = IBranchMergeProposal1021 schema = IBranchMergeProposal
9831022
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2010-02-19 16:33:29 +0000
+++ lib/lp/code/browser/configure.zcml 2010-02-24 08:55:31 +0000
@@ -224,6 +224,13 @@
224 permission="launchpad.Edit"224 permission="launchpad.Edit"
225 template="../../app/templates/generic-edit.pt"/>225 template="../../app/templates/generic-edit.pt"/>
226 <browser:page226 <browser:page
227 name="+edit-description"
228 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
229 class="lp.code.browser.branchmergeproposal.BranchMergeProposalDescriptionEditView"
230 facet="branches"
231 permission="launchpad.Edit"
232 template="../../app/templates/generic-edit.pt"/>
233 <browser:page
227 name="+delete"234 name="+delete"
228 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"235 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
229 class="lp.code.browser.branchmergeproposal.BranchMergeProposalDeleteView"236 class="lp.code.browser.branchmergeproposal.BranchMergeProposalDeleteView"
230237
=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
--- lib/lp/code/browser/tests/test_branchmergeproposal.py 2010-02-05 21:59:26 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -358,17 +358,6 @@
358 self.assertEqual(self.target_branch, proposal.target_branch)358 self.assertEqual(self.target_branch, proposal.target_branch)
359 return proposal359 return proposal
360360
361 def assertNoComments(self, proposal):
362 # There should be no comments.
363 self.assertEqual([], list(proposal.all_comments))
364
365 def assertOneComment(self, proposal, comment_text):
366 # There should be one and only one comment with the text specified.
367 self.assertEqual(
368 [comment_text],
369 [comment.message.text_contents
370 for comment in proposal.all_comments])
371
372 def assertNoPendingReviews(self, proposal):361 def assertNoPendingReviews(self, proposal):
373 # There should be no votes recorded for the proposal.362 # There should be no votes recorded for the proposal.
374 self.assertEqual([], list(proposal.votes))363 self.assertEqual([], list(proposal.votes))
@@ -395,7 +384,7 @@
395 'needs_review': True})384 'needs_review': True})
396 proposal = self._getSourceProposal()385 proposal = self._getSourceProposal()
397 self.assertNoPendingReviews(proposal)386 self.assertNoPendingReviews(proposal)
398 self.assertNoComments(proposal)387 self.assertIs(None, proposal.description)
399388
400 def test_register_work_in_progress(self):389 def test_register_work_in_progress(self):
401 # The needs review checkbox can be unchecked to create a work in390 # The needs review checkbox can be unchecked to create a work in
@@ -420,17 +409,17 @@
420 self.assertEqual('Fixed the bug!', proposal.commit_message)409 self.assertEqual('Fixed the bug!', proposal.commit_message)
421410
422 def test_register_initial_comment(self):411 def test_register_initial_comment(self):
423 # If the user specifies an initial comment, this is added to the412 # If the user specifies a description, this is recorded on the
424 # proposal.413 # proposal.
425 view = self._createView()414 view = self._createView()
426 view.register_action.success(415 view.register_action.success(
427 {'target_branch': self.target_branch,416 {'target_branch': self.target_branch,
428 'comment': "This is the first comment.",417 'comment': "This is the description.",
429 'needs_review': True})418 'needs_review': True})
430419
431 proposal = self._getSourceProposal()420 proposal = self._getSourceProposal()
432 self.assertNoPendingReviews(proposal)421 self.assertNoPendingReviews(proposal)
433 self.assertOneComment(proposal, "This is the first comment.")422 self.assertEqual(proposal.description, "This is the description.")
434423
435 def test_register_request_reviewer(self):424 def test_register_request_reviewer(self):
436 # If the user requests a reviewer, then a pending vote is added to the425 # If the user requests a reviewer, then a pending vote is added to the
@@ -444,7 +433,7 @@
444433
445 proposal = self._getSourceProposal()434 proposal = self._getSourceProposal()
446 self.assertOnePendingReview(proposal, reviewer)435 self.assertOnePendingReview(proposal, reviewer)
447 self.assertNoComments(proposal)436 self.assertIs(None, proposal.description)
448437
449 def test_register_request_review_type(self):438 def test_register_request_review_type(self):
450 # We can request a specific review type of the reviewer. If we do, it439 # We can request a specific review type of the reviewer. If we do, it
@@ -459,10 +448,10 @@
459448
460 proposal = self._getSourceProposal()449 proposal = self._getSourceProposal()
461 self.assertOnePendingReview(proposal, reviewer, 'god-like')450 self.assertOnePendingReview(proposal, reviewer, 'god-like')
462 self.assertNoComments(proposal)451 self.assertIs(None, proposal.description)
463452
464 def test_register_comment_and_review(self):453 def test_register_comment_and_review(self):
465 # The user can give an initial comment and request a review from454 # The user can give a description and request a review from
466 # someone.455 # someone.
467 reviewer = self.factory.makePerson()456 reviewer = self.factory.makePerson()
468 view = self._createView()457 view = self._createView()
@@ -470,12 +459,12 @@
470 {'target_branch': self.target_branch,459 {'target_branch': self.target_branch,
471 'reviewer': reviewer,460 'reviewer': reviewer,
472 'review_type': 'god-like',461 'review_type': 'god-like',
473 'comment': "This is the first comment.",462 'comment': "This is the description.",
474 'needs_review': True})463 'needs_review': True})
475464
476 proposal = self._getSourceProposal()465 proposal = self._getSourceProposal()
477 self.assertOnePendingReview(proposal, reviewer, 'god-like')466 self.assertOnePendingReview(proposal, reviewer, 'god-like')
478 self.assertOneComment(proposal, "This is the first comment.")467 self.assertEqual(proposal.description, "This is the description.")
479468
480469
481class TestBranchMergeProposalView(TestCaseWithFactory):470class TestBranchMergeProposalView(TestCaseWithFactory):
482471
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2010-02-22 12:07:03 +0000
+++ lib/lp/code/configure.zcml 2010-02-24 08:55:31 +0000
@@ -206,6 +206,7 @@
206 source_branch206 source_branch
207 target_branch207 target_branch
208 prerequisite_branch208 prerequisite_branch
209 description
209 whiteboard210 whiteboard
210 queue_status211 queue_status
211 private212 private
@@ -229,7 +230,6 @@
229 next_preview_diff_job230 next_preview_diff_job
230 preview_diff231 preview_diff
231 votes232 votes
232 root_comment
233 all_comments233 all_comments
234 related_bugs234 related_bugs
235 isMergable235 isMergable
@@ -243,7 +243,8 @@
243 <allow interface="lp.code.interfaces.branchtarget.IHasBranchTarget"/>243 <allow interface="lp.code.interfaces.branchtarget.IHasBranchTarget"/>
244 <require244 <require
245 permission="launchpad.Edit"245 permission="launchpad.Edit"
246 set_attributes="whiteboard merged_revno commit_message root_message_id review_diff"246 set_attributes="description whiteboard merged_revno commit_message
247 root_message_id review_diff"
247 attributes="248 attributes="
248 deleteProposal249 deleteProposal
249 setStatus250 setStatus
@@ -267,6 +268,9 @@
267 <adapter268 <adapter
268 factory="lp.code.browser.branchmergeproposal.text_xhtml_representation"269 factory="lp.code.browser.branchmergeproposal.text_xhtml_representation"
269 name="commit_message"/>270 name="commit_message"/>
271 <adapter
272 factory="lp.code.browser.branchmergeproposal.text_xhtml_representation"
273 name="description"/>
270274
271275
272 <class class="lp.code.model.branchmergeproposaljob.CreateMergeProposalJob">276 <class class="lp.code.model.branchmergeproposaljob.CreateMergeProposalJob">
273277
=== modified file 'lib/lp/code/doc/branch-merge-proposal-notifications.txt'
--- lib/lp/code/doc/branch-merge-proposal-notifications.txt 2010-01-05 23:26:44 +0000
+++ lib/lp/code/doc/branch-merge-proposal-notifications.txt 2010-02-24 08:55:31 +0000
@@ -153,7 +153,7 @@
153 >>> bmp.deleteProposal()153 >>> bmp.deleteProposal()
154 >>> bmp = source_branch.addLandingTarget(154 >>> bmp = source_branch.addLandingTarget(
155 ... registrant, target_branch,155 ... registrant, target_branch,
156 ... initial_comment=initial_comment, review_requests=reviewers)156 ... description=initial_comment, review_requests=reviewers)
157 >>> removeSecurityProxy(bmp).preview_diff = preview_diff157 >>> removeSecurityProxy(bmp).preview_diff = preview_diff
158 >>> [job,] = list(getUtility(IMergeProposalCreatedJobSource).iterReady())158 >>> [job,] = list(getUtility(IMergeProposalCreatedJobSource).iterReady())
159 >>> job.run(_create_preview=False)159 >>> job.run(_create_preview=False)
160160
=== modified file 'lib/lp/code/doc/codereviewcomment.txt'
--- lib/lp/code/doc/codereviewcomment.txt 2009-07-08 18:42:10 +0000
+++ lib/lp/code/doc/codereviewcomment.txt 2010-02-24 08:55:31 +0000
@@ -27,14 +27,8 @@
27 >>> comment = merge_proposal.createComment(27 >>> comment = merge_proposal.createComment(
28 ... sender, 'Please merge', 'This patch is very nice.')28 ... sender, 'Please merge', 'This patch is very nice.')
2929
30The first comment to be created is the root of the code review30The initial email that gets sent out has the message_id stored in the merge
31conversation, and is available as BranchMergeProposal.root_comment.31proposal. Subsequent comments are marked as replies to the initial email.
32
33 >>> merge_proposal.root_comment == comment
34 True
35
36Subsequent comments are marked as replies to the root_comment, by
37default.
3832
39 >>> from lp.code.enums import CodeReviewVote33 >>> from lp.code.enums import CodeReviewVote
40 >>> comment2 = merge_proposal.createComment(34 >>> comment2 = merge_proposal.createComment(
4135
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2010-02-18 16:00:24 +0000
+++ lib/lp/code/interfaces/branch.py 2010-02-24 08:55:31 +0000
@@ -754,8 +754,8 @@
754 target_branch=Reference(schema=Interface),754 target_branch=Reference(schema=Interface),
755 prerequisite_branch=Reference(schema=Interface),755 prerequisite_branch=Reference(schema=Interface),
756 needs_review=Bool(title=_('Needs review'),756 needs_review=Bool(title=_('Needs review'),
757 description=_('If True, set queue_status to NEEDS_REVIEW.'757 description=_('If True the proposal needs review.'
758 'Otherwise, it will be WORK_IN_PROGRESS.')),758 'Otherwise, it will be work in progress.')),
759 initial_comment=Text(759 initial_comment=Text(
760 title=_('Initial comment'),760 title=_('Initial comment'),
761 description=_("Registrant's initial description of proposal.")),761 description=_("Registrant's initial description of proposal.")),
@@ -774,38 +774,35 @@
774 registrant, target_branch, prerequisite_branch=None,774 registrant, target_branch, prerequisite_branch=None,
775 needs_review=True, initial_comment=None, commit_message=None,775 needs_review=True, initial_comment=None, commit_message=None,
776 reviewers=None, review_types=None):776 reviewers=None, review_types=None):
777 """API-oriented version of addLandingTarget.777 """Create a new BranchMergeProposal with this branch as the source.
778778
779 The parameters are the same as addLandingTarget, except that779 Both the target_branch and the prerequisite_branch, if it is there,
780 review_requests is split into a list of reviewers and a list of780 must be branches with the same target as the source branch.
781 review types.781
782 Personal branches (a.k.a. junk branches) cannot specify landing targets.
782 """783 """
783784
784 def addLandingTarget(registrant, target_branch, prerequisite_branch=None,785 def addLandingTarget(registrant, target_branch, prerequisite_branch=None,
785 whiteboard=None, date_created=None,786 date_created=None, needs_review=False,
786 needs_review=False, initial_comment=None,787 description=None, review_requests=None,
787 review_requests=None, review_diff=None,788 review_diff=None, commit_message=None):
788 commit_message=None):
789 """Create a new BranchMergeProposal with this branch as the source.789 """Create a new BranchMergeProposal with this branch as the source.
790790
791 Both the target_branch and the prerequisite_branch, if it is there,791 Both the target_branch and the prerequisite_branch, if it is there,
792 must be branches of the same project as the source branch.792 must be branches with the same target as the source branch.
793793
794 Branches without associated projects, junk branches, cannot794 Personal branches (a.k.a. junk branches) cannot specify landing targets.
795 specify landing targets.
796795
797 :param registrant: The person who is adding the landing target.796 :param registrant: The person who is adding the landing target.
798 :param target_branch: Must be another branch, and different to self.797 :param target_branch: Must be another branch, and different to self.
799 :param prerequisite_branch: Optional but if it is not None, it must be798 :param prerequisite_branch: Optional but if it is not None, it must be
800 another branch.799 another branch.
801 :param whiteboard: Optional. Just text, notes or instructions
802 pertinant to the landing such as testing notes.
803 :param date_created: Used to specify the date_created value of the800 :param date_created: Used to specify the date_created value of the
804 merge request.801 merge request.
805 :param needs_review: Used to specify the proposal is ready for802 :param needs_review: Used to specify the proposal is ready for
806 review right now.803 review right now.
807 :param initial_comment: An optional initial comment can be added804 :param description: A description of the bugs fixed, features added,
808 when adding the new target.805 or refactorings.
809 :param review_requests: An optional list of (`Person`, review_type).806 :param review_requests: An optional list of (`Person`, review_type).
810 """807 """
811808
812809
=== modified file 'lib/lp/code/interfaces/branchmergeproposal.py'
--- lib/lp/code/interfaces/branchmergeproposal.py 2010-01-11 20:07:17 +0000
+++ lib/lp/code/interfaces/branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -96,6 +96,13 @@
96 description=_(96 description=_(
97 "If True, this proposal is visible only to subscribers.")))97 "If True, this proposal is visible only to subscribers.")))
9898
99 description = exported(
100 Text(title=_('Description of the Change'), required=False,
101 description=_(
102 "A detailed description of the changes that are being "
103 "addressed by the branch being proposed to be merged."),
104 max_length=50000))
105
99 whiteboard = Whiteboard(106 whiteboard = Whiteboard(
100 title=_('Whiteboard'), required=False,107 title=_('Whiteboard'), required=False,
101 description=_('Notes about the merge.'))108 description=_('Notes about the merge.'))
@@ -211,9 +218,6 @@
211 date_queued = exported(218 date_queued = exported(
212 Datetime(219 Datetime(
213 title=_('Date Queued'), required=False, readonly=True))220 title=_('Date Queued'), required=False, readonly=True))
214 # Cannote use Object as this would cause circular dependencies.
215 root_comment = Attribute(
216 _("The first message in discussion of this merge proposal"))
217 root_message_id = Text(221 root_message_id = Text(
218 title=_('The email message id from the first message'),222 title=_('The email message id from the first message'),
219 required=False)223 required=False)
220224
=== modified file 'lib/lp/code/mail/branchmergeproposal.py'
--- lib/lp/code/mail/branchmergeproposal.py 2010-02-15 10:12:11 +0000
+++ lib/lp/code/mail/branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -67,7 +67,7 @@
6767
68 def __init__(self, subject, template_name, recipients, merge_proposal,68 def __init__(self, subject, template_name, recipients, merge_proposal,
69 from_address, delta=None, message_id=None,69 from_address, delta=None, message_id=None,
70 requested_reviews=None, comment=None, preview_diff=None,70 requested_reviews=None, preview_diff=None,
71 direct_email=False):71 direct_email=False):
72 BranchMailer.__init__(72 BranchMailer.__init__(
73 self, subject, template_name, recipients, from_address, delta,73 self, subject, template_name, recipients, from_address, delta,
@@ -76,7 +76,6 @@
76 if requested_reviews is None:76 if requested_reviews is None:
77 requested_reviews = []77 requested_reviews = []
78 self.requested_reviews = requested_reviews78 self.requested_reviews = requested_reviews
79 self.comment = comment
80 self.preview_diff = preview_diff79 self.preview_diff = preview_diff
81 self.template_params = self._generateTemplateParams()80 self.template_params = self._generateTemplateParams()
82 self.direct_email = direct_email81 self.direct_email = direct_email
@@ -106,7 +105,6 @@
106 'branch-merge-proposal-created.txt', recipients, merge_proposal,105 'branch-merge-proposal-created.txt', recipients, merge_proposal,
107 from_address, message_id=get_msgid(),106 from_address, message_id=get_msgid(),
108 requested_reviews=merge_proposal.votes,107 requested_reviews=merge_proposal.votes,
109 comment=merge_proposal.root_comment,
110 preview_diff=merge_proposal.preview_diff)108 preview_diff=merge_proposal.preview_diff)
111109
112 @classmethod110 @classmethod
@@ -140,17 +138,11 @@
140 """Return a mailer for a request to review a BranchMergeProposal."""138 """Return a mailer for a request to review a BranchMergeProposal."""
141 from_address = cls._format_user_address(from_user)139 from_address = cls._format_user_address(from_user)
142 recipients = {reason.subscriber: reason}140 recipients = {reason.subscriber: reason}
143 comment = None
144 if (merge_proposal.root_comment is not None and
145 (merge_proposal.root_comment.message.owner ==
146 merge_proposal.registrant)):
147 comment = merge_proposal.root_comment
148 return cls(141 return cls(
149 '%(proposal_title)s',142 '%(proposal_title)s',
150 'review-requested.txt', recipients,143 'review-requested.txt', recipients,
151 merge_proposal, from_address, message_id=get_msgid(),144 merge_proposal, from_address, message_id=get_msgid(),
152 comment=comment, preview_diff=merge_proposal.preview_diff,145 preview_diff=merge_proposal.preview_diff, direct_email=True)
153 direct_email=True)
154146
155 def _getReplyToAddress(self):147 def _getReplyToAddress(self):
156 """Return the address to use for the reply-to header."""148 """Return the address to use for the reply-to header."""
@@ -195,13 +187,14 @@
195187
196 def _generateTemplateParams(self):188 def _generateTemplateParams(self):
197 """For template params that don't change, calcualte just once."""189 """For template params that don't change, calcualte just once."""
190 proposal = self.merge_proposal
198 params = {191 params = {
199 'proposal_registrant': self.merge_proposal.registrant.displayname,192 'proposal_registrant': proposal.registrant.displayname,
200 'source_branch': self.merge_proposal.source_branch.bzr_identity,193 'source_branch': proposal.source_branch.bzr_identity,
201 'target_branch': self.merge_proposal.target_branch.bzr_identity,194 'target_branch': proposal.target_branch.bzr_identity,
202 'prerequisite': '',195 'prerequisite': '',
203 'proposal_title': self.merge_proposal.title,196 'proposal_title': proposal.title,
204 'proposal_url': canonical_url(self.merge_proposal),197 'proposal_url': canonical_url(proposal),
205 'edit_subscription': '',198 'edit_subscription': '',
206 'comment': '',199 'comment': '',
207 'gap': '',200 'gap': '',
@@ -210,8 +203,8 @@
210 'diff_cutoff_warning': '',203 'diff_cutoff_warning': '',
211 }204 }
212205
213 if self.merge_proposal.prerequisite_branch is not None:206 if proposal.prerequisite_branch is not None:
214 prereq_url = self.merge_proposal.prerequisite_branch.bzr_identity207 prereq_url = proposal.prerequisite_branch.bzr_identity
215 params['prerequisite'] = ' with %s as a prerequisite' % prereq_url208 params['prerequisite'] = ' with %s as a prerequisite' % prereq_url
216209
217 requested_reviews = []210 requested_reviews = []
@@ -228,8 +221,8 @@
228 params['reviews'] = (''.join(' %s\n' % review221 params['reviews'] = (''.join(' %s\n' % review
229 for review in requested_reviews))222 for review in requested_reviews))
230223
231 if self.comment is not None:224 if proposal.description is not None:
232 params['comment'] = (self.comment.message.text_contents)225 params['comment'] = (proposal.description)
233 if len(requested_reviews) > 0:226 if len(requested_reviews) > 0:
234 params['gap'] = '\n\n'227 params['gap'] = '\n\n'
235228
236229
=== modified file 'lib/lp/code/mail/codehandler.py'
--- lib/lp/code/mail/codehandler.py 2010-02-10 15:04:00 +0000
+++ lib/lp/code/mail/codehandler.py 2010-02-24 08:55:31 +0000
@@ -604,13 +604,10 @@
604 target.code_reviewer, submitter, None,604 target.code_reviewer, submitter, None,
605 _notify_listeners=False)605 _notify_listeners=False)
606606
607 if comment_text.strip() == '':607 comment_text = comment_text.strip()
608 comment = None608 if comment_text != '':
609 else:609 bmp.description = comment_text
610 comment = bmp.createComment(610 return bmp
611 submitter, message['Subject'], comment_text,
612 _notify_listeners=False)
613 return bmp, comment
614611
615 except BranchMergeProposalExists:612 except BranchMergeProposalExists:
616 body = get_error_message(613 body = get_error_message(
617614
=== modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py'
--- lib/lp/code/mail/tests/test_branchmergeproposal.py 2010-02-15 10:12:11 +0000
+++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -383,9 +383,6 @@
383 self.assertEqual(383 self.assertEqual(
384 'Requester <requester@example.com>', mailer.from_address)384 'Requester <requester@example.com>', mailer.from_address)
385 self.assertEqual(385 self.assertEqual(
386 request.merge_proposal.root_comment,
387 mailer.comment)
388 self.assertEqual(
389 request.merge_proposal.preview_diff,386 request.merge_proposal.preview_diff,
390 mailer.preview_diff)387 mailer.preview_diff)
391 self.assertRecipientsMatches([request.recipient], mailer)388 self.assertRecipientsMatches([request.recipient], mailer)
392389
=== modified file 'lib/lp/code/mail/tests/test_codehandler.py'
--- lib/lp/code/mail/tests/test_codehandler.py 2010-02-10 17:05:36 +0000
+++ lib/lp/code/mail/tests/test_codehandler.py 2010-02-24 08:55:31 +0000
@@ -16,6 +16,7 @@
16from bzrlib.transport import get_transport16from bzrlib.transport import get_transport
17from bzrlib.urlutils import join as urljoin17from bzrlib.urlutils import join as urljoin
18from bzrlib.workingtree import WorkingTree18from bzrlib.workingtree import WorkingTree
19from storm.store import Store
19from zope.component import getUtility20from zope.component import getUtility
20from zope.interface import directlyProvides, directlyProvidedBy21from zope.interface import directlyProvides, directlyProvidedBy
21from zope.security.management import setSecurityPolicy22from zope.security.management import setSecurityPolicy
@@ -469,12 +470,11 @@
469 self.switchDbUser(config.create_merge_proposals.dbuser)470 self.switchDbUser(config.create_merge_proposals.dbuser)
470 code_handler = CodeHandler()471 code_handler = CodeHandler()
471 pop_notifications()472 pop_notifications()
472 bmp, comment = code_handler.processMergeProposal(message)473 bmp = code_handler.processMergeProposal(message)
473 self.assertEqual(source, bmp.source_branch)474 self.assertEqual(source, bmp.source_branch)
474 self.assertEqual(target, bmp.target_branch)475 self.assertEqual(target, bmp.target_branch)
475 self.assertIs(None, bmp.review_diff)476 self.assertIs(None, bmp.review_diff)
476 self.assertEqual('Hi!\n', comment.message.text_contents)477 self.assertEqual('Hi!', bmp.description)
477 self.assertEqual('My subject', comment.message.subject)
478 # No emails are sent.478 # No emails are sent.
479 messages = pop_notifications()479 messages = pop_notifications()
480 self.assertEqual(0, len(messages))480 self.assertEqual(0, len(messages))
@@ -493,10 +493,10 @@
493 self.factory.makeMergeDirectiveEmail(body=' '))493 self.factory.makeMergeDirectiveEmail(body=' '))
494 self.switchDbUser(config.create_merge_proposals.dbuser)494 self.switchDbUser(config.create_merge_proposals.dbuser)
495 code_handler = CodeHandler()495 code_handler = CodeHandler()
496 bmp, comment = code_handler.processMergeProposal(message)496 bmp = code_handler.processMergeProposal(message)
497 self.assertEqual(source_branch, bmp.source_branch)497 self.assertEqual(source_branch, bmp.source_branch)
498 self.assertEqual(target_branch, bmp.target_branch)498 self.assertEqual(target_branch, bmp.target_branch)
499 self.assertIs(None, comment)499 self.assertIs(None, bmp.description)
500 self.assertEqual(0, bmp.all_comments.count())500 self.assertEqual(0, bmp.all_comments.count())
501 transaction.commit()501 transaction.commit()
502502
@@ -554,7 +554,7 @@
554 JobRunner.fromReady(CreateMergeProposalJob).runAll()554 JobRunner.fromReady(CreateMergeProposalJob).runAll()
555 self.assertEqual(target, source.landing_targets[0].target_branch)555 self.assertEqual(target, source.landing_targets[0].target_branch)
556 # Ensure the DB operations violate no constraints.556 # Ensure the DB operations violate no constraints.
557 transaction.commit()557 Store.of(source).flush()
558558
559 def test_processWithUnicodeMergeDirectiveEmail(self):559 def test_processWithUnicodeMergeDirectiveEmail(self):
560 """process creates a comment from a unicode message body."""560 """process creates a comment from a unicode message body."""
@@ -568,11 +568,10 @@
568 code_handler.process(message, 'merge@code.launchpad.net', file_alias)568 code_handler.process(message, 'merge@code.launchpad.net', file_alias)
569 self.switchDbUser(config.create_merge_proposals.dbuser)569 self.switchDbUser(config.create_merge_proposals.dbuser)
570 JobRunner.fromReady(CreateMergeProposalJob).runAll()570 JobRunner.fromReady(CreateMergeProposalJob).runAll()
571 comment = source.landing_targets[0].root_comment571 proposal = source.landing_targets[0]
572 self.assertIsNot(None, comment)572 self.assertEqual(u'\u1234', proposal.description)
573 self.assertEqual(u'\u1234', comment.message.text_contents)
574 # Ensure the DB operations violate no constraints.573 # Ensure the DB operations violate no constraints.
575 transaction.commit()574 Store.of(proposal).flush()
576575
577 def test_processMergeProposalReviewerRequested(self):576 def test_processMergeProposalReviewerRequested(self):
578 # The commands in the merge proposal are parsed.577 # The commands in the merge proposal are parsed.
@@ -586,7 +585,7 @@
586 self.switchDbUser(config.create_merge_proposals.dbuser)585 self.switchDbUser(config.create_merge_proposals.dbuser)
587 code_handler = CodeHandler()586 code_handler = CodeHandler()
588 pop_notifications()587 pop_notifications()
589 bmp, comment = code_handler.processMergeProposal(message)588 bmp = code_handler.processMergeProposal(message)
590 pending_reviews = list(bmp.votes)589 pending_reviews = list(bmp.votes)
591 self.assertEqual(1, len(pending_reviews))590 self.assertEqual(1, len(pending_reviews))
592 self.assertEqual(eric, pending_reviews[0].reviewer)591 self.assertEqual(eric, pending_reviews[0].reviewer)
@@ -594,7 +593,7 @@
594 messages = pop_notifications()593 messages = pop_notifications()
595 self.assertEqual(0, len(messages))594 self.assertEqual(0, len(messages))
596 # Ensure the DB operations violate no constraints.595 # Ensure the DB operations violate no constraints.
597 transaction.commit()596 Store.of(bmp).flush()
598597
599 def test_reviewer_with_diff(self):598 def test_reviewer_with_diff(self):
600 """Requesting a review with a diff works."""599 """Requesting a review with a diff works."""
@@ -625,7 +624,7 @@
625 self.switchDbUser(config.create_merge_proposals.dbuser)624 self.switchDbUser(config.create_merge_proposals.dbuser)
626 code_handler = CodeHandler()625 code_handler = CodeHandler()
627 pop_notifications()626 pop_notifications()
628 bmp, comment = code_handler.processMergeProposal(message)627 bmp = code_handler.processMergeProposal(message)
629 # If no reviewer is specified, then the default reviewer of the target628 # If no reviewer is specified, then the default reviewer of the target
630 # branch is requested to review.629 # branch is requested to review.
631 pending_reviews = list(bmp.votes)630 pending_reviews = list(bmp.votes)
@@ -637,7 +636,7 @@
637 messages = pop_notifications()636 messages = pop_notifications()
638 self.assertEqual(0, len(messages))637 self.assertEqual(0, len(messages))
639 # Ensure the DB operations violate no constraints.638 # Ensure the DB operations violate no constraints.
640 transaction.commit()639 Store.of(target_branch).flush()
641640
642 def test_processMergeProposalExists(self):641 def test_processMergeProposalExists(self):
643 """processMergeProposal raises BranchMergeProposalExists642 """processMergeProposal raises BranchMergeProposalExists
@@ -649,7 +648,7 @@
649 self.factory.makeMergeDirectiveEmail())648 self.factory.makeMergeDirectiveEmail())
650 self.switchDbUser(config.create_merge_proposals.dbuser)649 self.switchDbUser(config.create_merge_proposals.dbuser)
651 code_handler = CodeHandler()650 code_handler = CodeHandler()
652 bmp, comment = code_handler.processMergeProposal(message)651 bmp = code_handler.processMergeProposal(message)
653 _unused = pop_notifications()652 _unused = pop_notifications()
654 transaction.commit()653 transaction.commit()
655 _unused = code_handler.processMergeProposal(message)654 _unused = code_handler.processMergeProposal(message)
@@ -879,7 +878,7 @@
879 self.useBzrBranches(real_server=True)878 self.useBzrBranches(real_server=True)
880 branch, source, message = self._createTargetSourceAndBundle(879 branch, source, message = self._createTargetSourceAndBundle(
881 format="pack-0.92")880 format="pack-0.92")
882 bmp, comment = self._processMergeDirective(message)881 bmp = self._processMergeDirective(message)
883 self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)882 self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)
884 self.assertIs(None, bmp.source_branch.next_mirror_time)883 self.assertIs(None, bmp.source_branch.next_mirror_time)
885884
@@ -895,7 +894,7 @@
895 # concerned.894 # concerned.
896 branch.last_mirrored = None895 branch.last_mirrored = None
897 branch.last_mirrored_id = None896 branch.last_mirrored_id = None
898 bmp, comment = self._processMergeDirective(message)897 bmp = self._processMergeDirective(message)
899 self.assertEqual(BranchType.REMOTE, bmp.source_branch.branch_type)898 self.assertEqual(BranchType.REMOTE, bmp.source_branch.branch_type)
900899
901 def test_stackable_target(self):900 def test_stackable_target(self):
@@ -906,7 +905,7 @@
906 self.useBzrBranches(real_server=True)905 self.useBzrBranches(real_server=True)
907 branch, source, message = self._createTargetSourceAndBundle(906 branch, source, message = self._createTargetSourceAndBundle(
908 format="1.9")907 format="1.9")
909 bmp, comment = self._processMergeDirective(message)908 bmp = self._processMergeDirective(message)
910 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)909 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
911 self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)910 self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)
912 self.assertIsNot(None, bmp.source_branch.next_mirror_time)911 self.assertIsNot(None, bmp.source_branch.next_mirror_time)
@@ -919,7 +918,7 @@
919 self.useBzrBranches(real_server=True)918 self.useBzrBranches(real_server=True)
920 branch, source, message = self._createTargetSourceAndBundle(919 branch, source, message = self._createTargetSourceAndBundle(
921 format="1.9")920 format="1.9")
922 bmp, comment = self._processMergeDirective(message)921 bmp = self._processMergeDirective(message)
923 # The hosted location should be populated (open succeeds).922 # The hosted location should be populated (open succeeds).
924 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)923 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
925 # Not the mirror (open raises).924 # Not the mirror (open raises).
@@ -933,7 +932,7 @@
933 self.useBzrBranches(real_server=True)932 self.useBzrBranches(real_server=True)
934 branch, source, message = self._createTargetSourceAndBundle(933 branch, source, message = self._createTargetSourceAndBundle(
935 format="1.9")934 format="1.9")
936 bmp, comment = self._processMergeDirective(message)935 bmp = self._processMergeDirective(message)
937 # The source branch is stacked on the target.936 # The source branch is stacked on the target.
938 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)937 source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
939 self.assertEqual(938 self.assertEqual(
@@ -955,7 +954,7 @@
955 format="1.9")954 format="1.9")
956 target_tree = WorkingTree.open('.')955 target_tree = WorkingTree.open('.')
957 target_tree.commit('rev2b')956 target_tree.commit('rev2b')
958 bmp, comment = self._processMergeDirective(message)957 bmp = self._processMergeDirective(message)
959 lp_branch = self._openBazaarBranchAsClient(bmp.source_branch)958 lp_branch = self._openBazaarBranchAsClient(bmp.source_branch)
960 self.assertEqual(source.last_revision(), lp_branch.last_revision())959 self.assertEqual(source.last_revision(), lp_branch.last_revision())
961960
@@ -999,7 +998,7 @@
999 self.useBzrBranches(real_server=True)998 self.useBzrBranches(real_server=True)
1000 lp_source, message = self._createPreexistingSourceAndMessage(999 lp_source, message = self._createPreexistingSourceAndMessage(
1001 target_format="1.9", source_format="1.9", set_stacked=True)1000 target_format="1.9", source_format="1.9", set_stacked=True)
1002 bmp, comment = self._processMergeDirective(message)1001 bmp = self._processMergeDirective(message)
1003 # The branch merge proposal should use the existing db branch.1002 # The branch merge proposal should use the existing db branch.
1004 self.assertEqual(lp_source, bmp.source_branch)1003 self.assertEqual(lp_source, bmp.source_branch)
1005 # Now the branch is now scheduled to be mirrorred.1004 # Now the branch is now scheduled to be mirrorred.
@@ -1018,7 +1017,7 @@
1018 self.useBzrBranches(real_server=True)1017 self.useBzrBranches(real_server=True)
1019 lp_source, message = self._createPreexistingSourceAndMessage(1018 lp_source, message = self._createPreexistingSourceAndMessage(
1020 target_format="1.9", source_format="1.9")1019 target_format="1.9", source_format="1.9")
1021 bmp, comment = self._processMergeDirective(message)1020 bmp = self._processMergeDirective(message)
1022 # The branch merge proposal should use the existing db branch.1021 # The branch merge proposal should use the existing db branch.
1023 self.assertEqual(lp_source, bmp.source_branch)1022 self.assertEqual(lp_source, bmp.source_branch)
1024 # Now the branch is not scheduled to be mirrorred.1023 # Now the branch is not scheduled to be mirrorred.
@@ -1033,7 +1032,7 @@
1033 self.useBzrBranches(real_server=True)1032 self.useBzrBranches(real_server=True)
1034 lp_source, message = self._createPreexistingSourceAndMessage(1033 lp_source, message = self._createPreexistingSourceAndMessage(
1035 target_format="pack-0.92", source_format="1.9")1034 target_format="pack-0.92", source_format="1.9")
1036 bmp, comment = self._processMergeDirective(message)1035 bmp = self._processMergeDirective(message)
1037 # The branch merge proposal should use the existing db branch.1036 # The branch merge proposal should use the existing db branch.
1038 self.assertEqual(lp_source, bmp.source_branch)1037 self.assertEqual(lp_source, bmp.source_branch)
1039 # Now the branch is not scheduled to be mirrorred.1038 # Now the branch is not scheduled to be mirrorred.
@@ -1048,7 +1047,7 @@
1048 self.useBzrBranches(real_server=True)1047 self.useBzrBranches(real_server=True)
1049 lp_source, message = self._createPreexistingSourceAndMessage(1048 lp_source, message = self._createPreexistingSourceAndMessage(
1050 target_format="1.9", source_format="pack-0.92")1049 target_format="1.9", source_format="pack-0.92")
1051 bmp, comment = self._processMergeDirective(message)1050 bmp = self._processMergeDirective(message)
1052 # The branch merge proposal should use the existing db branch.1051 # The branch merge proposal should use the existing db branch.
1053 self.assertEqual(lp_source, bmp.source_branch)1052 self.assertEqual(lp_source, bmp.source_branch)
1054 # Now the branch is not scheduled to be mirrorred.1053 # Now the branch is not scheduled to be mirrorred.
10551054
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2010-02-22 01:44:55 +0000
+++ lib/lp/code/model/branch.py 2010-02-24 08:55:31 +0000
@@ -315,7 +315,7 @@
315 def addLandingTarget(self, registrant, target_branch,315 def addLandingTarget(self, registrant, target_branch,
316 prerequisite_branch=None, whiteboard=None,316 prerequisite_branch=None, whiteboard=None,
317 date_created=None, needs_review=False,317 date_created=None, needs_review=False,
318 initial_comment=None, review_requests=None,318 description=None, review_requests=None,
319 review_diff=None, commit_message=None):319 review_diff=None, commit_message=None):
320 """See `IBranch`."""320 """See `IBranch`."""
321 if not self.target.supports_merge_proposals:321 if not self.target.supports_merge_proposals:
@@ -369,11 +369,8 @@
369 date_created=date_created,369 date_created=date_created,
370 date_review_requested=date_review_requested,370 date_review_requested=date_review_requested,
371 queue_status=queue_status, review_diff=review_diff,371 queue_status=queue_status, review_diff=review_diff,
372 commit_message=commit_message)372 commit_message=commit_message,
373373 description=description)
374 if initial_comment is not None:
375 bmp.createComment(
376 registrant, None, initial_comment, _notify_listeners=False)
377374
378 for reviewer, review_type in review_requests:375 for reviewer, review_type in review_requests:
379 bmp.nominateReviewer(376 bmp.nominateReviewer(
@@ -397,7 +394,7 @@
397 review_requests = zip(reviewers, review_types)394 review_requests = zip(reviewers, review_types)
398 return self.addLandingTarget(395 return self.addLandingTarget(
399 registrant, target_branch, prerequisite_branch,396 registrant, target_branch, prerequisite_branch,
400 needs_review=needs_review, initial_comment=initial_comment,397 needs_review=needs_review, description=initial_comment,
401 commit_message=commit_message, review_requests=review_requests)398 commit_message=commit_message, review_requests=review_requests)
402399
403 def scheduleDiffUpdates(self):400 def scheduleDiffUpdates(self):
404401
=== modified file 'lib/lp/code/model/branchmergeproposal.py'
--- lib/lp/code/model/branchmergeproposal.py 2009-12-18 20:14:21 +0000
+++ lib/lp/code/model/branchmergeproposal.py 2010-02-24 08:55:31 +0000
@@ -114,6 +114,8 @@
114 prerequisite_branch = ForeignKey(114 prerequisite_branch = ForeignKey(
115 dbName='dependent_branch', foreignKey='Branch', notNull=False)115 dbName='dependent_branch', foreignKey='Branch', notNull=False)
116116
117 description = StringCol(default=None)
118
117 whiteboard = StringCol(default=None)119 whiteboard = StringCol(default=None)
118120
119 queue_status = EnumCol(121 queue_status = EnumCol(
@@ -207,17 +209,6 @@
207 """See `IHasBranchTarget`."""209 """See `IHasBranchTarget`."""
208 return self.source_branch.target210 return self.source_branch.target
209211
210 @property
211 def root_comment(self):
212 return CodeReviewComment.selectOne("""
213 CodeReviewMessage.id in (
214 SELECT CodeReviewMessage.id
215 FROM CodeReviewMessage, Message
216 WHERE CodeReviewMessage.branch_merge_proposal = %d AND
217 CodeReviewMessage.message = Message.id
218 ORDER BY Message.datecreated LIMIT 1)
219 """ % self.id)
220
221 root_message_id = StringCol(default=None)212 root_message_id = StringCol(default=None)
222213
223 @property214 @property
@@ -517,7 +508,7 @@
517 registrant=registrant,508 registrant=registrant,
518 target_branch=self.target_branch,509 target_branch=self.target_branch,
519 prerequisite_branch=self.prerequisite_branch,510 prerequisite_branch=self.prerequisite_branch,
520 whiteboard=self.whiteboard,511 description=self.description,
521 needs_review=True, review_requests=review_requests)512 needs_review=True, review_requests=review_requests)
522 self.superseded_by = proposal513 self.superseded_by = proposal
523 # This sync update is needed to ensure that the transitive514 # This sync update is needed to ensure that the transitive
524515
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2010-02-02 22:26:04 +0000
+++ lib/lp/code/model/tests/test_branch.py 2010-02-24 08:55:31 +0000
@@ -1238,16 +1238,14 @@
12381238
1239 def test_attributeAssignment(self):1239 def test_attributeAssignment(self):
1240 """Smoke test to make sure the assignments are there."""1240 """Smoke test to make sure the assignments are there."""
1241 whiteboard = u"Some whiteboard"
1242 commit_message = u'Some commit message'1241 commit_message = u'Some commit message'
1243 proposal = self.source.addLandingTarget(1242 proposal = self.source.addLandingTarget(
1244 self.user, self.target, self.prerequisite, whiteboard,1243 self.user, self.target, self.prerequisite,
1245 commit_message=commit_message)1244 commit_message=commit_message)
1246 self.assertEqual(proposal.registrant, self.user)1245 self.assertEqual(proposal.registrant, self.user)
1247 self.assertEqual(proposal.source_branch, self.source)1246 self.assertEqual(proposal.source_branch, self.source)
1248 self.assertEqual(proposal.target_branch, self.target)1247 self.assertEqual(proposal.target_branch, self.target)
1249 self.assertEqual(proposal.prerequisite_branch, self.prerequisite)1248 self.assertEqual(proposal.prerequisite_branch, self.prerequisite)
1250 self.assertEqual(proposal.whiteboard, whiteboard)
1251 self.assertEqual(proposal.commit_message, commit_message)1249 self.assertEqual(proposal.commit_message, commit_message)
12521250
1253 def test__createMergeProposal_with_reviewers(self):1251 def test__createMergeProposal_with_reviewers(self):
12541252
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposals.py'
--- lib/lp/code/model/tests/test_branchmergeproposals.py 2010-01-07 21:02:00 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposals.py 2010-02-24 08:55:31 +0000
@@ -531,36 +531,6 @@
531 "Expected %s, got %s" % (new_queue_order, queue_order))531 "Expected %s, got %s" % (new_queue_order, queue_order))
532532
533533
534class TestRootComment(TestCase):
535 """Test the behavior of the root_comment attribute"""
536
537 layer = DatabaseFunctionalLayer
538
539 def setUp(self):
540 TestCase.setUp(self)
541 login('foo.bar@canonical.com')
542 self.factory = LaunchpadObjectFactory()
543 self.merge_proposal = self.factory.makeBranchMergeProposal()
544
545 def test_orderedByDateNotInsertion(self):
546 """Root is determined by create date, not insert order"""
547 counter = time_counter()
548 oldest_date, middle_date, newest_date = [counter.next() for index in
549 (1, 2, 3)]
550 comment1 = self.merge_proposal.createComment(
551 self.merge_proposal.registrant, "Subject",
552 _date_created=middle_date)
553 self.assertEqual(comment1, self.merge_proposal.root_comment)
554 comment2 = self.merge_proposal.createComment(
555 self.merge_proposal.registrant, "Subject",
556 _date_created=newest_date)
557 self.assertEqual(comment1, self.merge_proposal.root_comment)
558 comment3 = self.merge_proposal.createComment(
559 self.merge_proposal.registrant, "Subject",
560 _date_created=oldest_date)
561 self.assertEqual(comment3, self.merge_proposal.root_comment)
562
563
564class TestCreateCommentNotifications(TestCaseWithFactory):534class TestCreateCommentNotifications(TestCaseWithFactory):
565 """Test the notifications are raised at the right times."""535 """Test the notifications are raised at the right times."""
566536
@@ -1755,7 +1725,7 @@
1755 signing_context=signing_context))1725 signing_context=signing_context))
1756 job = CreateMergeProposalJob.create(file_alias)1726 job = CreateMergeProposalJob.create(file_alias)
1757 transaction.commit()1727 transaction.commit()
1758 proposal, comment = job.run()1728 proposal = job.run()
1759 self.assertEqual(proposal.source_branch, source)1729 self.assertEqual(proposal.source_branch, source)
1760 self.assertEqual(proposal.target_branch, target)1730 self.assertEqual(proposal.target_branch, target)
17611731
17621732
=== modified file 'lib/lp/code/model/tests/test_codereviewcomment.py'
--- lib/lp/code/model/tests/test_codereviewcomment.py 2009-10-22 21:17:56 +0000
+++ lib/lp/code/model/tests/test_codereviewcomment.py 2010-02-24 08:55:31 +0000
@@ -35,7 +35,6 @@
35 self.assertEqual(None, comment.vote)35 self.assertEqual(None, comment.vote)
36 self.assertEqual(None, comment.vote_tag)36 self.assertEqual(None, comment.vote_tag)
37 self.assertEqual(self.submitter, comment.message.owner)37 self.assertEqual(self.submitter, comment.message.owner)
38 self.assertEqual(comment, self.bmp.root_comment)
39 self.assertEqual('Message subject', comment.message.subject)38 self.assertEqual('Message subject', comment.message.subject)
40 self.assertEqual('Message content', comment.message.chunks[0].content)39 self.assertEqual('Message content', comment.message.chunks[0].content)
4140
@@ -45,7 +44,6 @@
45 self.assertEqual(None, comment.vote)44 self.assertEqual(None, comment.vote)
46 self.assertEqual(None, comment.vote_tag)45 self.assertEqual(None, comment.vote_tag)
47 self.assertEqual(self.submitter, comment.message.owner)46 self.assertEqual(self.submitter, comment.message.owner)
48 self.assertEqual(comment, self.bmp.root_comment)
49 self.assertEqual(47 self.assertEqual(
50 'Re: [Merge] %s into %s' % (48 'Re: [Merge] %s into %s' % (
51 self.bmp.source_branch.bzr_identity,49 self.bmp.source_branch.bzr_identity,
@@ -58,7 +56,6 @@
58 reply = self.bmp.createComment(56 reply = self.bmp.createComment(
59 self.reviewer, 'Reply subject', 'Reply content',57 self.reviewer, 'Reply subject', 'Reply content',
60 CodeReviewVote.ABSTAIN, 'My tag', comment)58 CodeReviewVote.ABSTAIN, 'My tag', comment)
61 self.assertEqual(comment, self.bmp.root_comment)
62 self.assertEqual(comment.message.id, reply.message.parent.id)59 self.assertEqual(comment.message.id, reply.message.parent.id)
63 self.assertEqual(comment.message, reply.message.parent)60 self.assertEqual(comment.message, reply.message.parent)
64 self.assertEqual('Reply subject', reply.message.subject)61 self.assertEqual('Reply subject', reply.message.subject)
6562
=== modified file 'lib/lp/code/stories/branches/xx-branchmergeproposals.txt'
--- lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2010-02-18 17:31:54 +0000
+++ lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2010-02-24 08:55:31 +0000
@@ -100,7 +100,7 @@
100 ... 'Add more <b>mojo</b>')100 ... 'Add more <b>mojo</b>')
101 >>> nopriv_browser.getControl('Update').click()101 >>> nopriv_browser.getControl('Update').click()
102102
103 >>> print_tag_with_id(nopriv_browser.contents, 'edit-commit-message')103 >>> print_tag_with_id(nopriv_browser.contents, 'edit-commit_message')
104 Commit Message104 Commit Message
105 Add more &lt;b&gt;mojo&lt;/b&gt;105 Add more &lt;b&gt;mojo&lt;/b&gt;
106106
107107
=== modified file 'lib/lp/code/stories/branches/xx-claiming-team-code-reviews.txt'
--- lib/lp/code/stories/branches/xx-claiming-team-code-reviews.txt 2009-08-07 02:56:17 +0000
+++ lib/lp/code/stories/branches/xx-claiming-team-code-reviews.txt 2010-02-24 08:55:31 +0000
@@ -21,7 +21,8 @@
21 >>> eric_browser = setupBrowser(auth="Basic eric@example.com:test")21 >>> eric_browser = setupBrowser(auth="Basic eric@example.com:test")
22 >>> eric_browser.open(branch_url)22 >>> eric_browser.open(branch_url)
23 >>> eric_browser.getLink('Propose for merging').click()23 >>> eric_browser.getLink('Propose for merging').click()
24 >>> eric_browser.getControl('Initial Comment').value = 'Initial comment'24 >>> eric_browser.getControl('Description of the Change').value = (
25 ... 'This fix is awesome!')
25 >>> eric_browser.getControl('Propose Merge').click()26 >>> eric_browser.getControl('Propose Merge').click()
2627
27The reviewer table shows both the Vikings team and Eric himself.28The reviewer table shows both the Vikings team and Eric himself.
2829
=== modified file 'lib/lp/code/stories/webservice/xx-branchmergeproposal.txt'
--- lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2010-02-04 16:52:05 +0000
+++ lib/lp/code/stories/webservice/xx-branchmergeproposal.txt 2010-02-24 08:55:31 +0000
@@ -35,6 +35,7 @@
35 date_queued: None35 date_queued: None
36 date_review_requested: u'...'36 date_review_requested: u'...'
37 date_reviewed: None37 date_reviewed: None
38 description: u'Merge\nit!'
38 merge_reporter_link: None39 merge_reporter_link: None
39 merged_revno: None40 merged_revno: None
40 prerequisite_branch_link: u'http://api.launchpad.dev/beta/~...'41 prerequisite_branch_link: u'http://api.launchpad.dev/beta/~...'
@@ -57,20 +58,6 @@
57 votes_collection_link:58 votes_collection_link:
58 u'http://api.launchpad.dev/beta/~.../+merge/.../votes'59 u'http://api.launchpad.dev/beta/~.../+merge/.../votes'
5960
60 >>> all_comments = webservice.get(
61 ... bmp['all_comments_collection_link']).jsonBody()
62 >>> pprint_entry(all_comments['entries'][0])
63 as_quoted_email: u'> Merge\n> it!'
64 branch_merge_proposal_link:
65 u'http://api.launchpad.dev/beta/~person-name.../product-name.../branch.../+merge/...'
66 id: ...
67 message_body: u'Merge\nit!'
68 resource_type_link: u'http://api.launchpad.dev/beta/#code_review_comment'
69 self_link: u'http://api.launchpad.dev/beta/~person-name.../product-name.../branch.../+merge/.../comments/...'
70 title: u'Comment on proposed merge of lp://dev/~person-name.../product-name.../branch... into lp://dev/~person-name.../product-name.../branch...'
71 vote: None
72 vote_tag: None
73
74Our review request is listed in the votes collection.61Our review request is listed in the votes collection.
7562
76 >>> votes = webservice.get(63 >>> votes = webservice.get(
@@ -129,6 +116,7 @@
129 date_queued: None116 date_queued: None
130 date_review_requested: None117 date_review_requested: None
131 date_reviewed: None118 date_reviewed: None
119 description: None
132 merge_reporter_link: None120 merge_reporter_link: None
133 merged_revno: None121 merged_revno: None
134 prerequisite_branch_link: None122 prerequisite_branch_link: None
@@ -170,7 +158,7 @@
170 vote_tag: u'code'158 vote_tag: u'code'
171159
172 >>> comment_2 = webservice.named_get(160 >>> comment_2 = webservice.named_get(
173 ... merge_proposal['self_link'], 'getComment', id=3).jsonBody()161 ... merge_proposal['self_link'], 'getComment', id=2).jsonBody()
174 >>> pprint_entry(comment_2)162 >>> pprint_entry(comment_2)
175 as_quoted_email: u'> This is mediocre work.'163 as_quoted_email: u'> This is mediocre work.'
176 branch_merge_proposal_link: u'http://.../~source/fooix/fix-it/+merge/...'164 branch_merge_proposal_link: u'http://.../~source/fooix/fix-it/+merge/...'
177165
=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
--- lib/lp/code/templates/branchmergeproposal-index.pt 2010-01-15 04:34:31 +0000
+++ lib/lp/code/templates/branchmergeproposal-index.pt 2010-02-24 08:55:31 +0000
@@ -18,8 +18,8 @@
18 #code-review-votes {18 #code-review-votes {
19 margin: 1em 0;19 margin: 1em 0;
20 }20 }
21 #commit-message, #edit-commit-message {21 #description, #edit-description {
22 margin: 1em 0 0 0;22 margin: 1em 0;
23 }23 }
24 #add-comment-form {24 #add-comment-form {
25 max-width: 60em;25 max-width: 60em;
@@ -39,8 +39,16 @@
39 #add-comment-review-fields div {39 #add-comment-review-fields div {
40 display: inline;40 display: inline;
41 }41 }
42 #proposal-summary th {
43 font-weight: bold;
44 color: #717171;
45 }
46 #proposal-summary td {
47 padding-left: 0.5em;
48 }
42 /* A page-specific fix for inline text are editing to line up box. */49 /* A page-specific fix for inline text are editing to line up box. */
43 #edit-commit-message .yui-ieditor-input { top: 0; }50 #edit-description .yui-ieditor-input { top: 0; }
51 #edit-commit_message .yui-ieditor-input { top: 0; }
44 </style>52 </style>
45</metal:block>53</metal:block>
4654
@@ -66,27 +74,6 @@
66 <tal:summary replace="structure context/@@+pagelet-summary" />74 <tal:summary replace="structure context/@@+pagelet-summary" />
67 </div>75 </div>
6876
69 <div id="commit-message" class="yui-g">
70 <tal:no-commit-message condition="not: context/commit_message">
71 <div tal:define="link menu/set_commit_message"
72 tal:condition="link/enabled"
73 tal:content="structure link/render">
74 Set commit message
75 </div>
76 <div id="edit-commit-message" class="lazr-multiline-edit unseen"
77 tal:content="structure view/commit_message_html"/>
78 </tal:no-commit-message>
79 <tal:has-commit-message condition="context/commit_message">
80 <div tal:define="link menu/set_commit_message"
81 tal:condition="link/enabled"
82 tal:content="structure link/render" class="unseen">
83 Set commit message
84 </div>
85 <div id="edit-commit-message" class="lazr-multiline-edit"
86 tal:content="structure view/commit_message_html"/>
87 </tal:has-commit-message>
88 </div>
89
90 <div class="yui-g">77 <div class="yui-g">
91 <div id="votes-target"78 <div id="votes-target"
92 tal:content="structure context/@@+votes" />79 tal:content="structure context/@@+votes" />
@@ -109,6 +96,48 @@
109 </p>96 </p>
110 </div>97 </div>
11198
99 <div id="commit-message" class="yui-g">
100 <tal:no-commit-message condition="not: context/commit_message">
101 <div tal:define="link menu/set_commit_message"
102 tal:condition="link/enabled"
103 tal:content="structure link/render">
104 Set commit message
105 </div>
106 <div id="edit-commit_message" class="lazr-multiline-edit unseen"
107 tal:content="structure view/commit_message_html"/>
108 </tal:no-commit-message>
109 <tal:has-commit-message condition="context/commit_message">
110 <div tal:define="link menu/set_commit_message"
111 tal:condition="link/enabled"
112 tal:content="structure link/render" class="unseen">
113 Set commit message
114 </div>
115 <div id="edit-commit_message" class="lazr-multiline-edit"
116 tal:content="structure view/commit_message_html"/>
117 </tal:has-commit-message>
118 </div>
119
120 <div id="description" class="yui-g">
121 <tal:no-description condition="not: context/description">
122 <div tal:define="link menu/set_description"
123 tal:condition="link/enabled"
124 tal:content="structure link/render">
125 Set description
126 </div>
127 <div id="edit-description" class="lazr-multiline-edit unseen"
128 tal:content="structure view/description_html"/>
129 </tal:no-description>
130 <tal:has-description condition="context/description">
131 <div tal:define="link menu/set_description"
132 tal:condition="link/enabled"
133 tal:content="structure link/render" class="unseen">
134 Set description
135 </div>
136 <div id="edit-description" class="lazr-multiline-edit"
137 tal:content="structure view/description_html"/>
138 </tal:has-description>
139 </div>
140
112 <div class="yui-g">141 <div class="yui-g">
113 <tal:not-logged-in condition="not: view/user">142 <tal:not-logged-in condition="not: view/user">
114 <div align="center" id="add-comment-login-first">143 <div align="center" id="add-comment-login-first">
115144
=== modified file 'lib/lp/code/windmill/tests/test_branchmergeproposal_commitmessage.py'
--- lib/lp/code/windmill/tests/test_branchmergeproposal_commitmessage.py 2010-02-01 18:37:00 +0000
+++ lib/lp/code/windmill/tests/test_branchmergeproposal_commitmessage.py 2010-02-24 08:55:31 +0000
@@ -21,12 +21,12 @@
21# There seem to be two textareas rendered for the yui-ieditor-input for some21# There seem to be two textareas rendered for the yui-ieditor-input for some
22# reason.22# reason.
23EDIT_COMMENT_TEXTBOX = (23EDIT_COMMENT_TEXTBOX = (
24 u'//div[@id="edit-commit-message"]//textarea[@class="yui-ieditor-input"][1]')24 u'//div[@id="edit-commit_message"]//textarea[@class="yui-ieditor-input"][1]')
25EDIT_COMMENT_SUBMIT = (25EDIT_COMMENT_SUBMIT = (
26 u'//div[@id="edit-commit-message"]//'26 u'//div[@id="edit-commit_message"]//'
27 'button[contains(@class, "yui-ieditor-submit_button")]')27 'button[contains(@class, "yui-ieditor-submit_button")]')
28COMMIT_MESSAGE_TEXT = (28COMMIT_MESSAGE_TEXT = (
29 u'//div[@id="edit-commit-message"]//div[@class="yui-editable_text-text"]')29 u'//div[@id="edit-commit_message"]//div[@class="yui-editable_text-text"]')
3030
3131
32class TestCommitMessage(WindmillTestCase):32class TestCommitMessage(WindmillTestCase):
3333
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-02-23 16:12:55 +0000
+++ lib/lp/testing/factory.py 2010-02-24 08:55:31 +0000
@@ -917,7 +917,8 @@
917 set_state=None, prerequisite_branch=None,917 set_state=None, prerequisite_branch=None,
918 product=None, review_diff=None,918 product=None, review_diff=None,
919 initial_comment=None, source_branch=None,919 initial_comment=None, source_branch=None,
920 preview_diff=None, date_created=None):920 preview_diff=None, date_created=None,
921 description=None):
921 """Create a proposal to merge based on anonymous branches."""922 """Create a proposal to merge based on anonymous branches."""
922 if target_branch is not None:923 if target_branch is not None:
923 target = target_branch.target924 target = target_branch.target
@@ -932,6 +933,10 @@
932 target_branch = self.makeProductBranch(product)933 target_branch = self.makeProductBranch(product)
933 target = target_branch.target934 target = target_branch.target
934935
936 # Fall back to initial_comment for description.
937 if description is None:
938 description = initial_comment
939
935 if target_branch is None:940 if target_branch is None:
936 target_branch = self.makeBranchTargetBranch(target)941 target_branch = self.makeBranchTargetBranch(target)
937 if source_branch is None:942 if source_branch is None:
@@ -941,7 +946,7 @@
941 proposal = source_branch.addLandingTarget(946 proposal = source_branch.addLandingTarget(
942 registrant, target_branch,947 registrant, target_branch,
943 prerequisite_branch=prerequisite_branch, review_diff=review_diff,948 prerequisite_branch=prerequisite_branch, review_diff=review_diff,
944 initial_comment=initial_comment, date_created=date_created)949 description=description, date_created=date_created)
945950
946 unsafe_proposal = removeSecurityProxy(proposal)951 unsafe_proposal = removeSecurityProxy(proposal)
947 if preview_diff is not None:952 if preview_diff is not None:

Subscribers

People subscribed via source and target branches

to status/vote changes: