Merge lp:~jtv/launchpad/bug-427263 into lp:launchpad/db-devel
- bug-427263
- Merge into db-devel
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 |
Related bugs: |
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.
Description of the change
Jeroen T. Vermeulen (jtv) wrote : | # |
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!
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
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
1 | === modified file 'lib/canonical/launchpad/doc/lazr-js-widgets.txt' |
2 | --- lib/canonical/launchpad/doc/lazr-js-widgets.txt 2009-07-14 13:16:50 +0000 |
3 | +++ lib/canonical/launchpad/doc/lazr-js-widgets.txt 2009-11-13 22:40:28 +0000 |
4 | @@ -39,7 +39,7 @@ |
5 | important</span> |
6 | <a href="http://bugs.launchpad.dev/.../+edit" |
7 | class="yui-editable_text-trigger sprite edit" |
8 | - ></a> |
9 | + >...</a> |
10 | </h1> |
11 | <script> |
12 | ... |
13 | |
14 | === added file 'lib/canonical/launchpad/emailtemplates/person-merged.txt' |
15 | --- lib/canonical/launchpad/emailtemplates/person-merged.txt 1970-01-01 00:00:00 +0000 |
16 | +++ lib/canonical/launchpad/emailtemplates/person-merged.txt 2009-11-13 22:40:28 +0000 |
17 | @@ -0,0 +1,15 @@ |
18 | + |
19 | +The Launchpad account named '%(dupename)s' was merged into the account |
20 | +named '%(person)s'. You can confirm the merged email addresses to |
21 | +associate them with your account. Merged email addresses are unsubscribed |
22 | +from all Launchpad mailing lists during the merge. All team memberships |
23 | +for the merged account were also transferred to your account, and |
24 | +those team may have mailing lists. |
25 | + |
26 | +You can review and update your email and subscription settings at: |
27 | + |
28 | + https://launchpad.net/~%(person)s/+editemails |
29 | + |
30 | +Thank you, |
31 | + |
32 | +The Launchpad team |
33 | |
34 | === modified file 'lib/canonical/widgets/lazrjs.py' |
35 | --- lib/canonical/widgets/lazrjs.py 2009-10-28 08:03:36 +0000 |
36 | +++ lib/canonical/widgets/lazrjs.py 2009-11-13 22:40:28 +0000 |
37 | @@ -55,7 +55,7 @@ |
38 | # Template for the trigger button. |
39 | TRIGGER_TEMPLATE = dedent(u"""\ |
40 | <a href="%(edit_url)s" class="yui-editable_text-trigger sprite edit" |
41 | - ></a> |
42 | + >‌</a> |
43 | """) |
44 | |
45 | # Template for the activation script. |
46 | |
47 | === modified file 'lib/lp/archiveuploader/poppyinterface.py' |
48 | --- lib/lp/archiveuploader/poppyinterface.py 2009-06-24 23:33:29 +0000 |
49 | +++ lib/lp/archiveuploader/poppyinterface.py 2009-11-13 22:40:28 +0000 |
50 | @@ -9,8 +9,6 @@ |
51 | import stat |
52 | import time |
53 | |
54 | -from canonical.launchpad.scripts import execute_zcml_for_scripts |
55 | -from canonical.lp import initZopeless |
56 | from contrib.glock import GlobalLock |
57 | |
58 | class PoppyInterfaceFailure(Exception): |
59 | @@ -22,8 +20,6 @@ |
60 | |
61 | def __init__(self, targetpath, logger, allow_user, cmd=None, |
62 | targetstart=0, perms=None): |
63 | - execute_zcml_for_scripts() |
64 | - self.tm = initZopeless(dbuser='ro') |
65 | self.targetpath = targetpath |
66 | self.logger = logging.getLogger("%s.PoppyInterface" % logger.name) |
67 | self.cmd = cmd |
68 | |
69 | === renamed file 'lib/lp/registry/doc/xx-coc-views.txt' => 'lib/lp/registry/browser/tests/coc-views.txt' |
70 | === renamed file 'lib/lp/registry/doc/person-edit-views.txt' => 'lib/lp/registry/browser/tests/person-edit-views.txt' |
71 | === renamed file 'lib/lp/registry/doc/person-karma-views.txt' => 'lib/lp/registry/browser/tests/person-karma-views.txt' |
72 | === renamed file 'lib/lp/registry/doc/team-hierarchy-views.txt' => 'lib/lp/registry/browser/tests/team-hierarchy-views.txt' |
73 | === renamed file 'lib/lp/registry/doc/teammembership-views.txt' => 'lib/lp/registry/browser/tests/teammembership-views.txt' |
74 | === modified file 'lib/lp/registry/doc/person-merge.txt' |
75 | --- lib/lp/registry/doc/person-merge.txt 2009-08-13 19:03:36 +0000 |
76 | +++ lib/lp/registry/doc/person-merge.txt 2009-11-13 22:40:28 +0000 |
77 | @@ -199,8 +199,32 @@ |
78 | >>> results.get_one()[0] is None |
79 | True |
80 | |
81 | - |
82 | -== Person decoration == |
83 | +An email is sent to the user informing him that he should review his email |
84 | +and mailing list subscription settings. |
85 | + |
86 | + >>> from canonical.launchpad.interfaces.personnotification import ( |
87 | + ... IPersonNotificationSet) |
88 | + |
89 | + >>> notification_set = getUtility(IPersonNotificationSet) |
90 | + >>> notifications = notification_set.getNotificationsToSend() |
91 | + >>> notifications.count() |
92 | + 1 |
93 | + >>> notification = notifications[0] |
94 | + >>> print notification.person.name |
95 | + name12 |
96 | + >>> print notification.subject |
97 | + Launchpad accounts merged |
98 | + >>> print notification.body |
99 | + The Launchpad account named 'marilize-merged' was merged into the account |
100 | + named 'name12'. ... |
101 | + |
102 | + You can review and update your email and subscription settings at: |
103 | + |
104 | + https://launchpad.net/name12/+editemails ... |
105 | + |
106 | + |
107 | +Person decoration |
108 | +----------------- |
109 | |
110 | Several tables "extend" the Person table by having additional information |
111 | that is UNIQUEly keyed to Person.id. We have a utility function that merges |
112 | |
113 | === modified file 'lib/lp/registry/model/person.py' |
114 | --- lib/lp/registry/model/person.py 2009-10-16 09:25:45 +0000 |
115 | +++ lib/lp/registry/model/person.py 2009-11-13 22:40:28 +0000 |
116 | @@ -2861,21 +2861,9 @@ |
117 | name=name, new_name=new_name, product=product)) |
118 | |
119 | def _mergeMailingListSubscriptions(self, cur, from_id, to_id): |
120 | - # Update MailingListSubscription. Note that no remaining records |
121 | - # will have email_address set, as we assert earlier that the |
122 | - # from_person has no email addresses. |
123 | - # Update records that don't conflict. |
124 | - cur.execute(''' |
125 | - UPDATE MailingListSubscription |
126 | - SET person=%(to_id)d |
127 | - WHERE person=%(from_id)d |
128 | - AND mailing_list NOT IN ( |
129 | - SELECT mailing_list |
130 | - FROM MailingListSubscription |
131 | - WHERE person=%(to_id)d |
132 | - ) |
133 | - ''' % vars()) |
134 | - # Then trash the remainders. |
135 | + # Update MailingListSubscription. Note that since all the from_id |
136 | + # email addresses are set to NEW, all the subscriptions must be |
137 | + # removed because the user must confirm them. |
138 | cur.execute(''' |
139 | DELETE FROM MailingListSubscription WHERE person=%(from_id)d |
140 | ''' % vars()) |
141 | @@ -3472,6 +3460,17 @@ |
142 | # flush its caches. |
143 | store.invalidate() |
144 | |
145 | + # Inform the user of the merge changes. |
146 | + if not to_person.isTeam(): |
147 | + mail_text = get_email_template('person-merged.txt') |
148 | + mail_text = mail_text % { |
149 | + 'dupename': from_person.name, |
150 | + 'person': to_person.name, |
151 | + } |
152 | + subject = 'Launchpad accounts merged' |
153 | + getUtility(IPersonNotificationSet).addNotification( |
154 | + to_person, subject, mail_text) |
155 | + |
156 | def getValidPersons(self, persons): |
157 | """See `IPersonSet.`""" |
158 | person_ids = [person.id for person in persons] |
159 | |
160 | === modified file 'lib/lp/registry/tests/test_personset.py' |
161 | --- lib/lp/registry/tests/test_personset.py 2009-09-29 19:00:28 +0000 |
162 | +++ lib/lp/registry/tests/test_personset.py 2009-11-13 22:40:28 +0000 |
163 | @@ -7,46 +7,56 @@ |
164 | |
165 | from unittest import TestLoader |
166 | |
167 | +import transaction |
168 | from zope.component import getUtility |
169 | from zope.security.proxy import removeSecurityProxy |
170 | |
171 | from lp.registry.model.person import PersonSet |
172 | +from lp.registry.interfaces.mailinglistsubscription import ( |
173 | + MailingListAutoSubscribePolicy) |
174 | from lp.registry.interfaces.person import ( |
175 | PersonCreationRationale, IPersonSet) |
176 | -from lp.testing import TestCaseWithFactory |
177 | +from lp.testing import TestCaseWithFactory, login_person, logout |
178 | + |
179 | +from canonical.database.sqlbase import cursor |
180 | from canonical.launchpad.testing.databasehelpers import ( |
181 | remove_all_sample_data_branches) |
182 | -from canonical.testing import LaunchpadFunctionalLayer |
183 | +from canonical.testing import DatabaseFunctionalLayer |
184 | |
185 | |
186 | class TestPersonSetBranchCounts(TestCaseWithFactory): |
187 | |
188 | - layer = LaunchpadFunctionalLayer |
189 | + layer = DatabaseFunctionalLayer |
190 | |
191 | def setUp(self): |
192 | TestCaseWithFactory.setUp(self) |
193 | remove_all_sample_data_branches() |
194 | + self.person_set = getUtility(IPersonSet) |
195 | |
196 | def test_no_branches(self): |
197 | """Initially there should be no branches.""" |
198 | - self.assertEqual(0, PersonSet().getPeopleWithBranches().count()) |
199 | + self.assertEqual(0, self.person_set.getPeopleWithBranches().count()) |
200 | |
201 | def test_five_branches(self): |
202 | branches = [self.factory.makeAnyBranch() for x in range(5)] |
203 | # Each branch has a different product, so any individual product |
204 | # will return one branch. |
205 | - self.assertEqual(5, PersonSet().getPeopleWithBranches().count()) |
206 | - self.assertEqual(1, PersonSet().getPeopleWithBranches( |
207 | + self.assertEqual(5, self.person_set.getPeopleWithBranches().count()) |
208 | + self.assertEqual(1, self.person_set.getPeopleWithBranches( |
209 | branches[0].product).count()) |
210 | |
211 | |
212 | class TestPersonSetEnsurePerson(TestCaseWithFactory): |
213 | |
214 | - layer = LaunchpadFunctionalLayer |
215 | + layer = DatabaseFunctionalLayer |
216 | email_address = 'testing.ensure.person@example.com' |
217 | displayname = 'Testing ensurePerson' |
218 | rationale = PersonCreationRationale.SOURCEPACKAGEUPLOAD |
219 | |
220 | + def setUp(self): |
221 | + TestCaseWithFactory.setUp(self) |
222 | + self.person_set = getUtility(IPersonSet) |
223 | + |
224 | def test_ensurePerson_returns_existing_person(self): |
225 | # IPerson.ensurePerson returns existing person and does not |
226 | # override its details. |
227 | @@ -54,7 +64,7 @@ |
228 | testing_person = self.factory.makePerson( |
229 | email=self.email_address, displayname=testing_displayname) |
230 | |
231 | - ensured_person = getUtility(IPersonSet).ensurePerson( |
232 | + ensured_person = self.person_set.ensurePerson( |
233 | self.email_address, self.displayname, self.rationale) |
234 | self.assertEquals(testing_person.id, ensured_person.id) |
235 | self.assertIsNot( |
236 | @@ -67,7 +77,7 @@ |
237 | def test_ensurePerson_hides_new_person_email(self): |
238 | # IPersonSet.ensurePerson creates new person with |
239 | # 'hide_email_addresses' set. |
240 | - ensured_person = getUtility(IPersonSet).ensurePerson( |
241 | + ensured_person = self.person_set.ensurePerson( |
242 | self.email_address, self.displayname, self.rationale) |
243 | self.assertTrue(ensured_person.hide_email_addresses) |
244 | |
245 | @@ -78,7 +88,7 @@ |
246 | self.displayname, email=self.email_address) |
247 | self.assertIs(None, test_account.preferredemail.person) |
248 | |
249 | - ensured_person = getUtility(IPersonSet).ensurePerson( |
250 | + ensured_person = self.person_set.ensurePerson( |
251 | self.email_address, self.displayname, self.rationale) |
252 | self.assertEquals(test_account.id, ensured_person.account.id) |
253 | self.assertTrue(ensured_person.hide_email_addresses) |
254 | @@ -98,7 +108,7 @@ |
255 | self.assertIs(None, testing_account.preferredemail.person) |
256 | self.assertIs(None, testing_person.preferredemail) |
257 | |
258 | - ensured_person = getUtility(IPersonSet).ensurePerson( |
259 | + ensured_person = self.person_set.ensurePerson( |
260 | self.email_address, self.displayname, self.rationale) |
261 | |
262 | # The existing Person was retrieved and the Account |
263 | @@ -108,5 +118,36 @@ |
264 | ensured_person.preferredemail.id) |
265 | |
266 | |
267 | +class TestPersonSetMerge(TestCaseWithFactory): |
268 | + |
269 | + layer = DatabaseFunctionalLayer |
270 | + |
271 | + def setUp(self): |
272 | + TestCaseWithFactory.setUp(self) |
273 | + # Use the unsecured PersonSet so that private methods can be tested. |
274 | + self.person_set = PersonSet() |
275 | + self.from_person = self.factory.makePerson() |
276 | + self.to_person = self.factory.makePerson() |
277 | + self.cur = cursor() |
278 | + |
279 | + def test__mergeMailingListSubscriptions_no_subscriptions(self): |
280 | + self.person_set._mergeMailingListSubscriptions( |
281 | + self.cur, self.from_person.id, self.to_person.id) |
282 | + self.assertEqual(0, self.cur.rowcount) |
283 | + |
284 | + def test__mergeMailingListSubscriptions_with_subscriptions(self): |
285 | + self.from_person.mailing_list_auto_subscribe_policy = ( |
286 | + MailingListAutoSubscribePolicy.ALWAYS) |
287 | + self.team, self.mailing_list = self.factory.makeTeamAndMailingList( |
288 | + 'test-mailinglist', 'team-owner') |
289 | + login_person(self.team.teamowner) |
290 | + self.team.addMember(self.from_person, reviewer=self.team.teamowner) |
291 | + logout() |
292 | + transaction.commit() |
293 | + self.person_set._mergeMailingListSubscriptions( |
294 | + self.cur, self.from_person.id, self.to_person.id) |
295 | + self.assertEqual(1, self.cur.rowcount) |
296 | + |
297 | + |
298 | def test_suite(): |
299 | return TestLoader().loadTestsFromName(__name__) |
300 | |
301 | === modified file 'lib/lp/testing/menu.py' |
302 | --- lib/lp/testing/menu.py 2009-09-10 20:35:24 +0000 |
303 | +++ lib/lp/testing/menu.py 2009-11-13 22:40:28 +0000 |
304 | @@ -13,7 +13,6 @@ |
305 | |
306 | def check_menu_links(menu): |
307 | context = menu.context |
308 | - is_sane_menu = True |
309 | for link in menu.iterlinks(): |
310 | if '?' in link.target: |
311 | view_name, _args = link.target.split('?') |
312 | @@ -24,6 +23,5 @@ |
313 | try: |
314 | view = getMultiAdapter((context, request), name=view_name) |
315 | except: |
316 | - is_sane_menu = False |
317 | - print 'Bad link %s: %s' % (link.name, url) |
318 | - print is_sane_menu |
319 | + return 'Bad link %s: %s' % (link.name, url) |
320 | + return True |
321 | |
322 | === modified file 'lib/lp/translations/scripts/message_sharing_migration.py' |
323 | --- lib/lp/translations/scripts/message_sharing_migration.py 2009-10-30 11:18:59 +0000 |
324 | +++ lib/lp/translations/scripts/message_sharing_migration.py 2009-11-13 22:40:28 +0000 |
325 | @@ -7,7 +7,6 @@ |
326 | ] |
327 | |
328 | |
329 | -import gc |
330 | import os |
331 | |
332 | from zope.component import getUtility |
333 | @@ -291,15 +290,6 @@ |
334 | if self.txn is None: |
335 | return |
336 | |
337 | - if self.commit_count % 100 == 0 or not intermediate: |
338 | - freed = gc.collect() |
339 | - objcount = len(gc.get_objects()) |
340 | - garbage = len(gc.garbage) |
341 | - memsize = open("/proc/%s/statm" % os.getpid()).read().split()[5] |
342 | - log.debug( |
343 | - "Freed: %d. Object count: %d. Garbage: %d. Memory size: %s" |
344 | - % (freed, objcount, garbage, memsize)) |
345 | - |
346 | self.commit_count += 1 |
347 | |
348 | if intermediate and self.commit_count % 10 != 0: |
= 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