=== modified file 'lib/canonical/launchpad/javascript/bugs/bugtask-index.js'
--- lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-12-01 12:08:29 +0000
+++ lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-12-11 14:04:24 +0000
@@ -1495,7 +1495,7 @@
*
* @method setup_me_too
*/
-bugs.setup_me_too = function(user_is_affected) {
+bugs.setup_me_too = function(user_is_affected, others_affected_count) {
// IE (7 & 8 tested) is stupid, stupid, stupid.
if (Y.UA.ie) {
return;
@@ -1504,7 +1504,8 @@
var me_too_edit = new MeTooChoiceSource({
contentBox: me_too_content, value: user_is_affected,
elementToFlash: me_too_content,
- editicon: ".dynamic img[src$=/@@/edit]"
+ editicon: ".dynamic img[src$=/@@/edit]",
+ others_affected_count: others_affected_count
});
me_too_edit.render();
};
@@ -1547,12 +1548,10 @@
*/
items: {
value: [
- { name: 'Yes, it affects me', value: true,
- source_name: 'This bug affects me too',
- disabled: false },
- { name: "No, it doesn't affect me", value: false,
- source_name: "This bug doesn't affect me",
- disabled: false }
+ { name: 'Yes, it affects me',
+ value: true, disabled: false },
+ { name: "No, it doesn't affect me",
+ value: false, disabled: false }
]
},
@@ -1572,6 +1571,16 @@
set: function(v) {
return Y.one(v);
}
+ },
+
+ /**
+ * The number of other users currently affected by this bug.
+ *
+ * @attribute others_affected_count
+ * @type Number
+ */
+ others_affected_count: {
+ value: null
}
};
@@ -1588,6 +1597,50 @@
this.error_handler.showError = function(error_msg) {
widget.showError(error_msg);
};
+ // Set source_names.
+ var others_affected_count = this.get('others_affected_count');
+ var source_names = this._getSourceNames(others_affected_count);
+ Y.each(this.get('items'), function(item) {
+ if (item.value in source_names) {
+ item.source_name = source_names[item.value];
+ }
+ });
+ },
+
+ /*
+ * The results of _getSourceNames() should closely mirror the
+ * results of BugTasksAndNominationsView.affected_statement and
+ * anon_affected_statement.
+ */
+ _getSourceNames: function(others_affected_count) {
+ var source_names = {};
+ // What to say when the user is marked as affected.
+ if (others_affected_count == 1) {
+ source_names[true] = (
+ 'This bug affects you and 1 other person');
+ }
+ else if (others_affected_count > 1) {
+ source_names[true] = (
+ 'This bug affects you and ' +
+ others_affected_count + ' other people');
+ }
+ else {
+ source_names[true] = 'This bug affects you';
+ }
+ // What to say when the user is marked as not affected.
+ if (others_affected_count == 1) {
+ source_names[false] = (
+ 'This bug affects 1 person, but not you');
+ }
+ else if (others_affected_count > 1) {
+ source_names[false] = (
+ 'This bug affects ' + others_affected_count +
+ ' people, but not you');
+ }
+ else {
+ source_names[false] = "This bug doesn't affect you";
+ }
+ return source_names;
},
showError: function(err) {
@@ -1742,7 +1795,8 @@
* @method setup_add_attachment
*/
function setup_add_attachment() {
- var attachment_link = Y.one('.menu-link-addcomment');
+ // Find zero or more links to modify.
+ var attachment_link = Y.all('.menu-link-addcomment');
attachment_link.on('click', function(e) {
var comment_input = Y.one('[id="field.comment"]');
if (comment_input.get('value') !== '') {
=== modified file 'lib/canonical/launchpad/javascript/bugs/tests/test_me_too.js'
--- lib/canonical/launchpad/javascript/bugs/tests/test_me_too.js 2009-12-02 16:23:42 +0000
+++ lib/canonical/launchpad/javascript/bugs/tests/test_me_too.js 2009-12-11 14:04:24 +0000
@@ -72,7 +72,7 @@
var me_too_content = Y.one('#affectsmetoo');
this.config = {
contentBox: me_too_content, value: null,
- elementToFlash: me_too_content
+ elementToFlash: me_too_content, others_affected_count: 5
};
this.choice_edit = new Y.bugs._MeTooChoiceSource(this.config);
this.choice_edit.render();
@@ -205,6 +205,39 @@
Assert.isNull(
edit_icon.get('src').match(/\/spinner$/),
"The edit icon is displaying a spinner once the choice has been made.");
+ },
+
+ test__getSourceNames: function() {
+ var names;
+ // No other users affected.
+ names = this.choice_edit._getSourceNames(0);
+ Assert.areEqual(
+ 'This bug affects you', names[true]);
+ Assert.areEqual(
+ "This bug doesn't affect you", names[false]);
+ // 1 other user affected.
+ names = this.choice_edit._getSourceNames(1);
+ Assert.areEqual(
+ 'This bug affects you and 1 other person', names[true]);
+ Assert.areEqual(
+ 'This bug affects 1 person, but not you', names[false]);
+ // 2 other users affected.
+ names = this.choice_edit._getSourceNames(2);
+ Assert.areEqual(
+ 'This bug affects you and 2 other people', names[true]);
+ Assert.areEqual(
+ 'This bug affects 2 people, but not you', names[false]);
+ },
+
+ test_new_names_are_applied: function() {
+ var names = {};
+ Y.each(this.choice_edit.get('items'), function(item) {
+ names[item.value] = item.source_name;
+ });
+ Assert.areEqual(
+ 'This bug affects you and 5 other people', names[true]);
+ Assert.areEqual(
+ 'This bug affects 5 people, but not you', names[false]);
}
}));
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2009-11-28 08:11:20 +0000
+++ lib/lp/bugs/browser/bugtask.py 2009-12-11 14:04:24 +0000
@@ -3097,6 +3097,62 @@
else:
return 'false'
+ @property
+ def other_users_affected_count(self):
+ """The number of other users affected by this bug."""
+ if self.current_user_affected_status:
+ return self.context.users_affected_count - 1
+ else:
+ return self.context.users_affected_count
+
+ @property
+ def affected_statement(self):
+ """The default "this bug affects" statement to show.
+
+ The outputs of this method should be mirrored in
+ MeTooChoiceSource._getSourceNames() (Javascript).
+ """
+ if self.other_users_affected_count == 1:
+ if self.current_user_affected_status is None:
+ return "This bug affects 1 person. Does this bug affect you?"
+ elif self.current_user_affected_status:
+ return "This bug affects you and 1 other person"
+ else:
+ return "This bug affects 1 person, but not you"
+ elif self.other_users_affected_count > 1:
+ if self.current_user_affected_status is None:
+ return (
+ "This bug affects %d people. Does this bug "
+ "affect you?" % (self.other_users_affected_count))
+ elif self.current_user_affected_status:
+ return "This bug affects you and %d other people" % (
+ self.other_users_affected_count)
+ else:
+ return "This bug affects %d people, but not you" % (
+ self.other_users_affected_count)
+ else:
+ if self.current_user_affected_status is None:
+ return "Does this bug affect you?"
+ elif self.current_user_affected_status:
+ return "This bug affects you"
+ else:
+ return "This bug doesn't affect you"
+
+ @property
+ def anon_affected_statement(self):
+ """The "this bug affects" statement to show to anonymous users.
+
+ The outputs of this method should be mirrored in
+ MeTooChoiceSource._getSourceNames() (Javascript).
+ """
+ if self.context.users_affected_count == 1:
+ return "This bug affects 1 person"
+ elif self.context.users_affected_count > 1:
+ return "This bug affects %d people" % (
+ self.context.users_affected_count)
+ else:
+ return None
+
class BugTaskTableRowView(LaunchpadView):
"""Browser class for rendering a bugtask row on the bug page."""
=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
--- lib/lp/bugs/browser/tests/test_bugtask.py 2009-09-18 20:23:46 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask.py 2009-12-11 14:04:24 +0000
@@ -68,6 +68,142 @@
self.bug.default_bugtask, False, False)
self.failUnless(row_view.many_bugtasks)
+ def test_other_users_affected_count(self):
+ # The number of other users affected does not change when the
+ # logged-in user marked him or herself as affected or not.
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ self.view.context.markUserAffected(self.view.user, False)
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+
+ def test_other_users_affected_count_other_users(self):
+ # The number of other users affected only changes when other
+ # users mark themselves as affected.
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ other_user_1 = self.factory.makePerson()
+ self.view.context.markUserAffected(other_user_1, True)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+ other_user_2 = self.factory.makePerson()
+ self.view.context.markUserAffected(other_user_2, True)
+ self.failUnlessEqual(
+ 3, self.view.other_users_affected_count)
+ self.view.context.markUserAffected(other_user_1, False)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+
+ def test_affected_statement_no_one_affected(self):
+ self.bug.markUserAffected(self.bug.owner, False)
+ self.failUnlessEqual(
+ 0, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "Does this bug affect you?",
+ self.view.affected_statement)
+
+ def test_affected_statement_only_you(self):
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnless(self.bug.isUserAffected(self.view.user))
+ self.view.context.markUserAffected(self.bug.owner, False)
+ self.failUnlessEqual(
+ 0, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects you",
+ self.view.affected_statement)
+
+ def test_affected_statement_only_not_you(self):
+ self.view.context.markUserAffected(self.view.user, False)
+ self.failIf(self.bug.isUserAffected(self.view.user))
+ self.view.context.markUserAffected(self.bug.owner, False)
+ self.failUnlessEqual(
+ 0, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug doesn't affect you",
+ self.view.affected_statement)
+
+ def test_affected_statement_1_person_not_you(self):
+ self.assertIs(None, self.bug.isUserAffected(self.view.user))
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 1 person. Does this bug affect you?",
+ self.view.affected_statement)
+
+ def test_affected_statement_1_person_and_you(self):
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnless(self.bug.isUserAffected(self.view.user))
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects you and 1 other person",
+ self.view.affected_statement)
+
+ def test_affected_statement_1_person_and_not_you(self):
+ self.view.context.markUserAffected(self.view.user, False)
+ self.failIf(self.bug.isUserAffected(self.view.user))
+ self.failUnlessEqual(
+ 1, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 1 person, but not you",
+ self.view.affected_statement)
+
+ def test_affected_statement_more_than_1_person_not_you(self):
+ self.assertIs(None, self.bug.isUserAffected(self.view.user))
+ other_user = self.factory.makePerson()
+ self.view.context.markUserAffected(other_user, True)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 2 people. Does this bug affect you?",
+ self.view.affected_statement)
+
+ def test_affected_statement_more_than_1_person_and_you(self):
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnless(self.bug.isUserAffected(self.view.user))
+ other_user = self.factory.makePerson()
+ self.view.context.markUserAffected(other_user, True)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects you and 2 other people",
+ self.view.affected_statement)
+
+ def test_affected_statement_more_than_1_person_and_not_you(self):
+ self.view.context.markUserAffected(self.view.user, False)
+ self.failIf(self.bug.isUserAffected(self.view.user))
+ other_user = self.factory.makePerson()
+ self.view.context.markUserAffected(other_user, True)
+ self.failUnlessEqual(
+ 2, self.view.other_users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 2 people, but not you",
+ self.view.affected_statement)
+
+ def test_anon_affected_statement_no_one_affected(self):
+ self.bug.markUserAffected(self.bug.owner, False)
+ self.failUnlessEqual(0, self.bug.users_affected_count)
+ self.assertIs(None, self.view.anon_affected_statement)
+
+ def test_anon_affected_statement_1_user_affected(self):
+ self.failUnlessEqual(1, self.bug.users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 1 person",
+ self.view.anon_affected_statement)
+
+ def test_anon_affected_statement_2_users_affected(self):
+ self.view.context.markUserAffected(self.view.user, True)
+ self.failUnlessEqual(2, self.bug.users_affected_count)
+ self.failUnlessEqual(
+ "This bug affects 2 people",
+ self.view.anon_affected_statement)
+
def test_suite():
suite = unittest.TestSuite()
=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-07-31 13:49:53 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-12-11 14:04:24 +0000
@@ -10,13 +10,14 @@
>>> logout()
The user goes to the bug's index page, and finds a statement that the
-bug is not marked as affecting them.
+bug affects one other person (in this instance, the person who filed
+the bug).
>>> user_browser.open(test_bug_url)
>>> print extract_text(find_tag_by_id(
... user_browser.contents, 'affectsmetoo').find(
... None, 'static'))
- This bug doesn't affect me
+ This bug affects 1 person. Does this bug affect you?
Next to the statement is a link containing an edit icon.
@@ -45,7 +46,7 @@
>>> print extract_text(find_tag_by_id(
... user_browser.contents, 'affectsmetoo').find(
... None, 'static'))
- This bug affects me too
+ This bug affects you and 1 other person
Next to it, we also see the 'hot bug' icon, to indicate that the user
has marked the bug as affecting them.
@@ -69,7 +70,28 @@
>>> print extract_text(find_tag_by_id(
... user_browser.contents, 'affectsmetoo').find(
... None, 'static'))
- This bug doesn't affect me
+ This bug affects 1 person, but not you
+
+
+== Anonymous users ==
+
+Anonymous users just see the number of affected users.
+
+ >>> anon_browser.open(test_bug_url)
+ >>> print extract_text(find_tag_by_id(
+ ... anon_browser.contents, 'affectsmetoo'))
+ This bug affects 1 person
+
+If no one is marked as affected by the bug, the message does not
+appear at all to anonymous users.
+
+ >>> login('test@canonical.com')
+ >>> test_bug.markUserAffected(test_bug.owner, False)
+ >>> logout()
+
+ >>> anon_browser.open(test_bug_url)
+ >>> print find_tag_by_id(anon_browser.contents, 'affectsmetoo')
+ None
== Static and dynamic support ==
=== modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-table.pt'
--- lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-03 18:36:37 +0000
+++ lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-12-11 14:04:24 +0000
@@ -2,6 +2,63 @@
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
omit-tag="">
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
=== modified file 'lib/lp/bugs/windmill/tests/test_bug_me_too.py'
--- lib/lp/bugs/windmill/tests/test_bug_me_too.py 2009-11-04 14:06:04 +0000
+++ lib/lp/bugs/windmill/tests/test_bug_me_too.py 2009-12-11 14:04:24 +0000
@@ -111,7 +111,7 @@
def check_for_save_not_affects(client):
client.asserts.assertText(
xpath=VALUE_LOCATION_XPATH,
- validator=u"This bug doesn't affect me")
+ validator=u"This bug doesn't affect you")
# Hah! But this bug does affect the logged-in user! The logged-in
# user made a mistake, oh noes. Better fix that.
@@ -125,7 +125,7 @@
def check_for_save_does_affect(client):
client.asserts.assertText(
xpath=VALUE_LOCATION_XPATH,
- validator=u"This bug affects me too")
+ validator=u"This bug affects you")
# The flame icon is now visible.
client.asserts.assertElemJS(