Merge lp:~jtv/launchpad/bug-427263 into lp:launchpad/db-devel

Proposed by Jeroen T. Vermeulen
Status: Rejected
Rejected by: Jeroen T. Vermeulen
Proposed branch: lp:~jtv/launchpad/bug-427263
Merge into: lp:launchpad/db-devel
Diff against target: 348 lines (+111/-48)
9 files modified
lib/canonical/launchpad/doc/lazr-js-widgets.txt (+1/-1)
lib/canonical/launchpad/emailtemplates/person-merged.txt (+15/-0)
lib/canonical/widgets/lazrjs.py (+1/-1)
lib/lp/archiveuploader/poppyinterface.py (+0/-4)
lib/lp/registry/doc/person-merge.txt (+26/-2)
lib/lp/registry/model/person.py (+14/-15)
lib/lp/registry/tests/test_personset.py (+52/-11)
lib/lp/testing/menu.py (+2/-4)
lib/lp/translations/scripts/message_sharing_migration.py (+0/-10)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-427263
Reviewer Review Type Date Requested Status
Michael Nelson (community) code Approve
Review via email: mp+14851@code.launchpad.net

Commit message

Make the "edit" icons for multi-line editor widgets show up in Konqueror as well.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

= Bug 427263 =

In Konqueror, the little "edit" buttons for bug descriptions and other multi-line edit fields do not show up. The reason, as Michael Nelson found, was that this browser does not render <a> tags with no text in them.

This branch works around that by inserting a zero-width non-joiner (as well as the sprite) in the anchor tag for the button. There are no visible changes in other browsers that I can see; as the name says, it's a zero-width character.

Tests:
{{{
./bin/test -t lazr-js-widgets.txt
}}}

Unsurprisingly, there's not a whole lot of test impact. This is purely a visual thing.

Demo and Q/A: Log in. Visit anything with an editable description that would normally use the Ajax inline editor—such a bug or a blueprint that you have edit rights on. (On a blueprint the "edit" icon will appear for the Whiteboard). Visit the same page in Konqueror, while logged into the same account. Without this fix, the edit icon is not visible. With this fix, it is. The inline editor doesn't work in konqueror, so it'll still take you to the pre-ajax edit form. But at least you've got a way to reach it!

Jeroen

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Thanks Jeroen. Sorry it took so long to do (hotel internet :/ ).

So I'm approving your changes `bzr diff -r 9878..9880`, which is exactly what I see locally doing `bzr diff -r ancestor:../devel`, not sure why the MP has other changes displayed.

Thanks!

review: Approve (code)
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> Thanks Jeroen. Sorry it took so long to do (hotel internet :/ ).
>
> So I'm approving your changes `bzr diff -r 9878..9880`, which is exactly what
> I see locally doing `bzr diff -r ancestor:../devel`, not sure why the MP has
> other changes displayed.

Thanks for the review—feels better to finish the sprint without WIP branches. The diff was just messed up from branching off devel and then proposing for merge in db-devel (which is the default).

Jeroen

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Stumbled across a better way to fix this at the last moment. Uses CSS only. Abandoning this MP.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/doc/lazr-js-widgets.txt'
--- lib/canonical/launchpad/doc/lazr-js-widgets.txt 2009-07-14 13:16:50 +0000
+++ lib/canonical/launchpad/doc/lazr-js-widgets.txt 2009-11-13 22:40:28 +0000
@@ -39,7 +39,7 @@
39 important</span>39 important</span>
40 <a href="http://bugs.launchpad.dev/.../+edit"40 <a href="http://bugs.launchpad.dev/.../+edit"
41 class="yui-editable_text-trigger sprite edit"41 class="yui-editable_text-trigger sprite edit"
42 ></a>42 >...</a>
43 </h1>43 </h1>
44 <script>44 <script>
45 ...45 ...
4646
=== added file 'lib/canonical/launchpad/emailtemplates/person-merged.txt'
--- lib/canonical/launchpad/emailtemplates/person-merged.txt 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/emailtemplates/person-merged.txt 2009-11-13 22:40:28 +0000
@@ -0,0 +1,15 @@
1
2The Launchpad account named '%(dupename)s' was merged into the account
3named '%(person)s'. You can confirm the merged email addresses to
4associate them with your account. Merged email addresses are unsubscribed
5from all Launchpad mailing lists during the merge. All team memberships
6for the merged account were also transferred to your account, and
7those team may have mailing lists.
8
9You can review and update your email and subscription settings at:
10
11 https://launchpad.net/~%(person)s/+editemails
12
13Thank you,
14
15The Launchpad team
016
=== modified file 'lib/canonical/widgets/lazrjs.py'
--- lib/canonical/widgets/lazrjs.py 2009-10-28 08:03:36 +0000
+++ lib/canonical/widgets/lazrjs.py 2009-11-13 22:40:28 +0000
@@ -55,7 +55,7 @@
55 # Template for the trigger button.55 # Template for the trigger button.
56 TRIGGER_TEMPLATE = dedent(u"""\56 TRIGGER_TEMPLATE = dedent(u"""\
57 <a href="%(edit_url)s" class="yui-editable_text-trigger sprite edit"57 <a href="%(edit_url)s" class="yui-editable_text-trigger sprite edit"
58 ></a>58 >&zwnj;</a>
59 """)59 """)
6060
61 # Template for the activation script.61 # Template for the activation script.
6262
=== modified file 'lib/lp/archiveuploader/poppyinterface.py'
--- lib/lp/archiveuploader/poppyinterface.py 2009-06-24 23:33:29 +0000
+++ lib/lp/archiveuploader/poppyinterface.py 2009-11-13 22:40:28 +0000
@@ -9,8 +9,6 @@
9import stat9import stat
10import time10import time
1111
12from canonical.launchpad.scripts import execute_zcml_for_scripts
13from canonical.lp import initZopeless
14from contrib.glock import GlobalLock12from contrib.glock import GlobalLock
1513
16class PoppyInterfaceFailure(Exception):14class PoppyInterfaceFailure(Exception):
@@ -22,8 +20,6 @@
2220
23 def __init__(self, targetpath, logger, allow_user, cmd=None,21 def __init__(self, targetpath, logger, allow_user, cmd=None,
24 targetstart=0, perms=None):22 targetstart=0, perms=None):
25 execute_zcml_for_scripts()
26 self.tm = initZopeless(dbuser='ro')
27 self.targetpath = targetpath23 self.targetpath = targetpath
28 self.logger = logging.getLogger("%s.PoppyInterface" % logger.name)24 self.logger = logging.getLogger("%s.PoppyInterface" % logger.name)
29 self.cmd = cmd25 self.cmd = cmd
3026
=== renamed file 'lib/lp/registry/doc/xx-coc-views.txt' => 'lib/lp/registry/browser/tests/coc-views.txt'
=== renamed file 'lib/lp/registry/doc/person-edit-views.txt' => 'lib/lp/registry/browser/tests/person-edit-views.txt'
=== renamed file 'lib/lp/registry/doc/person-karma-views.txt' => 'lib/lp/registry/browser/tests/person-karma-views.txt'
=== renamed file 'lib/lp/registry/doc/team-hierarchy-views.txt' => 'lib/lp/registry/browser/tests/team-hierarchy-views.txt'
=== renamed file 'lib/lp/registry/doc/teammembership-views.txt' => 'lib/lp/registry/browser/tests/teammembership-views.txt'
=== modified file 'lib/lp/registry/doc/person-merge.txt'
--- lib/lp/registry/doc/person-merge.txt 2009-08-13 19:03:36 +0000
+++ lib/lp/registry/doc/person-merge.txt 2009-11-13 22:40:28 +0000
@@ -199,8 +199,32 @@
199 >>> results.get_one()[0] is None199 >>> results.get_one()[0] is None
200 True200 True
201201
202202An email is sent to the user informing him that he should review his email
203== Person decoration ==203and mailing list subscription settings.
204
205 >>> from canonical.launchpad.interfaces.personnotification import (
206 ... IPersonNotificationSet)
207
208 >>> notification_set = getUtility(IPersonNotificationSet)
209 >>> notifications = notification_set.getNotificationsToSend()
210 >>> notifications.count()
211 1
212 >>> notification = notifications[0]
213 >>> print notification.person.name
214 name12
215 >>> print notification.subject
216 Launchpad accounts merged
217 >>> print notification.body
218 The Launchpad account named 'marilize-merged' was merged into the account
219 named 'name12'. ...
220
221 You can review and update your email and subscription settings at:
222
223 https://launchpad.net/name12/+editemails ...
224
225
226Person decoration
227-----------------
204228
205Several tables "extend" the Person table by having additional information229Several tables "extend" the Person table by having additional information
206that is UNIQUEly keyed to Person.id. We have a utility function that merges230that is UNIQUEly keyed to Person.id. We have a utility function that merges
207231
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2009-10-16 09:25:45 +0000
+++ lib/lp/registry/model/person.py 2009-11-13 22:40:28 +0000
@@ -2861,21 +2861,9 @@
2861 name=name, new_name=new_name, product=product))2861 name=name, new_name=new_name, product=product))
28622862
2863 def _mergeMailingListSubscriptions(self, cur, from_id, to_id):2863 def _mergeMailingListSubscriptions(self, cur, from_id, to_id):
2864 # Update MailingListSubscription. Note that no remaining records2864 # Update MailingListSubscription. Note that since all the from_id
2865 # will have email_address set, as we assert earlier that the2865 # email addresses are set to NEW, all the subscriptions must be
2866 # from_person has no email addresses.2866 # removed because the user must confirm them.
2867 # Update records that don't conflict.
2868 cur.execute('''
2869 UPDATE MailingListSubscription
2870 SET person=%(to_id)d
2871 WHERE person=%(from_id)d
2872 AND mailing_list NOT IN (
2873 SELECT mailing_list
2874 FROM MailingListSubscription
2875 WHERE person=%(to_id)d
2876 )
2877 ''' % vars())
2878 # Then trash the remainders.
2879 cur.execute('''2867 cur.execute('''
2880 DELETE FROM MailingListSubscription WHERE person=%(from_id)d2868 DELETE FROM MailingListSubscription WHERE person=%(from_id)d
2881 ''' % vars())2869 ''' % vars())
@@ -3472,6 +3460,17 @@
3472 # flush its caches.3460 # flush its caches.
3473 store.invalidate()3461 store.invalidate()
34743462
3463 # Inform the user of the merge changes.
3464 if not to_person.isTeam():
3465 mail_text = get_email_template('person-merged.txt')
3466 mail_text = mail_text % {
3467 'dupename': from_person.name,
3468 'person': to_person.name,
3469 }
3470 subject = 'Launchpad accounts merged'
3471 getUtility(IPersonNotificationSet).addNotification(
3472 to_person, subject, mail_text)
3473
3475 def getValidPersons(self, persons):3474 def getValidPersons(self, persons):
3476 """See `IPersonSet.`"""3475 """See `IPersonSet.`"""
3477 person_ids = [person.id for person in persons]3476 person_ids = [person.id for person in persons]
34783477
=== modified file 'lib/lp/registry/tests/test_personset.py'
--- lib/lp/registry/tests/test_personset.py 2009-09-29 19:00:28 +0000
+++ lib/lp/registry/tests/test_personset.py 2009-11-13 22:40:28 +0000
@@ -7,46 +7,56 @@
77
8from unittest import TestLoader8from unittest import TestLoader
99
10import transaction
10from zope.component import getUtility11from zope.component import getUtility
11from zope.security.proxy import removeSecurityProxy12from zope.security.proxy import removeSecurityProxy
1213
13from lp.registry.model.person import PersonSet14from lp.registry.model.person import PersonSet
15from lp.registry.interfaces.mailinglistsubscription import (
16 MailingListAutoSubscribePolicy)
14from lp.registry.interfaces.person import (17from lp.registry.interfaces.person import (
15 PersonCreationRationale, IPersonSet)18 PersonCreationRationale, IPersonSet)
16from lp.testing import TestCaseWithFactory19from lp.testing import TestCaseWithFactory, login_person, logout
20
21from canonical.database.sqlbase import cursor
17from canonical.launchpad.testing.databasehelpers import (22from canonical.launchpad.testing.databasehelpers import (
18 remove_all_sample_data_branches)23 remove_all_sample_data_branches)
19from canonical.testing import LaunchpadFunctionalLayer24from canonical.testing import DatabaseFunctionalLayer
2025
2126
22class TestPersonSetBranchCounts(TestCaseWithFactory):27class TestPersonSetBranchCounts(TestCaseWithFactory):
2328
24 layer = LaunchpadFunctionalLayer29 layer = DatabaseFunctionalLayer
2530
26 def setUp(self):31 def setUp(self):
27 TestCaseWithFactory.setUp(self)32 TestCaseWithFactory.setUp(self)
28 remove_all_sample_data_branches()33 remove_all_sample_data_branches()
34 self.person_set = getUtility(IPersonSet)
2935
30 def test_no_branches(self):36 def test_no_branches(self):
31 """Initially there should be no branches."""37 """Initially there should be no branches."""
32 self.assertEqual(0, PersonSet().getPeopleWithBranches().count())38 self.assertEqual(0, self.person_set.getPeopleWithBranches().count())
3339
34 def test_five_branches(self):40 def test_five_branches(self):
35 branches = [self.factory.makeAnyBranch() for x in range(5)]41 branches = [self.factory.makeAnyBranch() for x in range(5)]
36 # Each branch has a different product, so any individual product42 # Each branch has a different product, so any individual product
37 # will return one branch.43 # will return one branch.
38 self.assertEqual(5, PersonSet().getPeopleWithBranches().count())44 self.assertEqual(5, self.person_set.getPeopleWithBranches().count())
39 self.assertEqual(1, PersonSet().getPeopleWithBranches(45 self.assertEqual(1, self.person_set.getPeopleWithBranches(
40 branches[0].product).count())46 branches[0].product).count())
4147
4248
43class TestPersonSetEnsurePerson(TestCaseWithFactory):49class TestPersonSetEnsurePerson(TestCaseWithFactory):
4450
45 layer = LaunchpadFunctionalLayer51 layer = DatabaseFunctionalLayer
46 email_address = 'testing.ensure.person@example.com'52 email_address = 'testing.ensure.person@example.com'
47 displayname = 'Testing ensurePerson'53 displayname = 'Testing ensurePerson'
48 rationale = PersonCreationRationale.SOURCEPACKAGEUPLOAD54 rationale = PersonCreationRationale.SOURCEPACKAGEUPLOAD
4955
56 def setUp(self):
57 TestCaseWithFactory.setUp(self)
58 self.person_set = getUtility(IPersonSet)
59
50 def test_ensurePerson_returns_existing_person(self):60 def test_ensurePerson_returns_existing_person(self):
51 # IPerson.ensurePerson returns existing person and does not61 # IPerson.ensurePerson returns existing person and does not
52 # override its details.62 # override its details.
@@ -54,7 +64,7 @@
54 testing_person = self.factory.makePerson(64 testing_person = self.factory.makePerson(
55 email=self.email_address, displayname=testing_displayname)65 email=self.email_address, displayname=testing_displayname)
5666
57 ensured_person = getUtility(IPersonSet).ensurePerson(67 ensured_person = self.person_set.ensurePerson(
58 self.email_address, self.displayname, self.rationale)68 self.email_address, self.displayname, self.rationale)
59 self.assertEquals(testing_person.id, ensured_person.id)69 self.assertEquals(testing_person.id, ensured_person.id)
60 self.assertIsNot(70 self.assertIsNot(
@@ -67,7 +77,7 @@
67 def test_ensurePerson_hides_new_person_email(self):77 def test_ensurePerson_hides_new_person_email(self):
68 # IPersonSet.ensurePerson creates new person with78 # IPersonSet.ensurePerson creates new person with
69 # 'hide_email_addresses' set.79 # 'hide_email_addresses' set.
70 ensured_person = getUtility(IPersonSet).ensurePerson(80 ensured_person = self.person_set.ensurePerson(
71 self.email_address, self.displayname, self.rationale)81 self.email_address, self.displayname, self.rationale)
72 self.assertTrue(ensured_person.hide_email_addresses)82 self.assertTrue(ensured_person.hide_email_addresses)
7383
@@ -78,7 +88,7 @@
78 self.displayname, email=self.email_address)88 self.displayname, email=self.email_address)
79 self.assertIs(None, test_account.preferredemail.person)89 self.assertIs(None, test_account.preferredemail.person)
8090
81 ensured_person = getUtility(IPersonSet).ensurePerson(91 ensured_person = self.person_set.ensurePerson(
82 self.email_address, self.displayname, self.rationale)92 self.email_address, self.displayname, self.rationale)
83 self.assertEquals(test_account.id, ensured_person.account.id)93 self.assertEquals(test_account.id, ensured_person.account.id)
84 self.assertTrue(ensured_person.hide_email_addresses)94 self.assertTrue(ensured_person.hide_email_addresses)
@@ -98,7 +108,7 @@
98 self.assertIs(None, testing_account.preferredemail.person)108 self.assertIs(None, testing_account.preferredemail.person)
99 self.assertIs(None, testing_person.preferredemail)109 self.assertIs(None, testing_person.preferredemail)
100110
101 ensured_person = getUtility(IPersonSet).ensurePerson(111 ensured_person = self.person_set.ensurePerson(
102 self.email_address, self.displayname, self.rationale)112 self.email_address, self.displayname, self.rationale)
103113
104 # The existing Person was retrieved and the Account114 # The existing Person was retrieved and the Account
@@ -108,5 +118,36 @@
108 ensured_person.preferredemail.id)118 ensured_person.preferredemail.id)
109119
110120
121class TestPersonSetMerge(TestCaseWithFactory):
122
123 layer = DatabaseFunctionalLayer
124
125 def setUp(self):
126 TestCaseWithFactory.setUp(self)
127 # Use the unsecured PersonSet so that private methods can be tested.
128 self.person_set = PersonSet()
129 self.from_person = self.factory.makePerson()
130 self.to_person = self.factory.makePerson()
131 self.cur = cursor()
132
133 def test__mergeMailingListSubscriptions_no_subscriptions(self):
134 self.person_set._mergeMailingListSubscriptions(
135 self.cur, self.from_person.id, self.to_person.id)
136 self.assertEqual(0, self.cur.rowcount)
137
138 def test__mergeMailingListSubscriptions_with_subscriptions(self):
139 self.from_person.mailing_list_auto_subscribe_policy = (
140 MailingListAutoSubscribePolicy.ALWAYS)
141 self.team, self.mailing_list = self.factory.makeTeamAndMailingList(
142 'test-mailinglist', 'team-owner')
143 login_person(self.team.teamowner)
144 self.team.addMember(self.from_person, reviewer=self.team.teamowner)
145 logout()
146 transaction.commit()
147 self.person_set._mergeMailingListSubscriptions(
148 self.cur, self.from_person.id, self.to_person.id)
149 self.assertEqual(1, self.cur.rowcount)
150
151
111def test_suite():152def test_suite():
112 return TestLoader().loadTestsFromName(__name__)153 return TestLoader().loadTestsFromName(__name__)
113154
=== modified file 'lib/lp/testing/menu.py'
--- lib/lp/testing/menu.py 2009-09-10 20:35:24 +0000
+++ lib/lp/testing/menu.py 2009-11-13 22:40:28 +0000
@@ -13,7 +13,6 @@
1313
14def check_menu_links(menu):14def check_menu_links(menu):
15 context = menu.context15 context = menu.context
16 is_sane_menu = True
17 for link in menu.iterlinks():16 for link in menu.iterlinks():
18 if '?' in link.target:17 if '?' in link.target:
19 view_name, _args = link.target.split('?')18 view_name, _args = link.target.split('?')
@@ -24,6 +23,5 @@
24 try:23 try:
25 view = getMultiAdapter((context, request), name=view_name)24 view = getMultiAdapter((context, request), name=view_name)
26 except:25 except:
27 is_sane_menu = False26 return 'Bad link %s: %s' % (link.name, url)
28 print 'Bad link %s: %s' % (link.name, url)27 return True
29 print is_sane_menu
3028
=== modified file 'lib/lp/translations/scripts/message_sharing_migration.py'
--- lib/lp/translations/scripts/message_sharing_migration.py 2009-10-30 11:18:59 +0000
+++ lib/lp/translations/scripts/message_sharing_migration.py 2009-11-13 22:40:28 +0000
@@ -7,7 +7,6 @@
7 ]7 ]
88
99
10import gc
11import os10import os
1211
13from zope.component import getUtility12from zope.component import getUtility
@@ -291,15 +290,6 @@
291 if self.txn is None:290 if self.txn is None:
292 return291 return
293292
294 if self.commit_count % 100 == 0 or not intermediate:
295 freed = gc.collect()
296 objcount = len(gc.get_objects())
297 garbage = len(gc.garbage)
298 memsize = open("/proc/%s/statm" % os.getpid()).read().split()[5]
299 log.debug(
300 "Freed: %d. Object count: %d. Garbage: %d. Memory size: %s"
301 % (freed, objcount, garbage, memsize))
302
303 self.commit_count += 1293 self.commit_count += 1
304294
305 if intermediate and self.commit_count % 10 != 0:295 if intermediate and self.commit_count % 10 != 0:

Subscribers

People subscribed via source and target branches

to status/vote changes: