Merge lp:~salgado/launchpad/remove-logintoken-unused-code into lp:launchpad
- remove-logintoken-unused-code
- Merge into devel
Proposed by
Guilherme Salgado
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~salgado/launchpad/remove-logintoken-unused-code |
Merge into: | lp:launchpad |
Prerequisite: | lp:~salgado/launchpad/remove-auth-store |
Diff against target: |
1474 lines (+43/-1077) 23 files modified
lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt (+10/-11) lib/canonical/launchpad/browser/logintoken.py (+7/-228) lib/canonical/launchpad/browser/tests/test_logintoken.py (+1/-7) lib/canonical/launchpad/browser/tests/test_password_reset.py (+0/-94) lib/canonical/launchpad/database/logintoken.py (+0/-17) lib/canonical/launchpad/doc/logintoken-pages.txt (+13/-295) lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt (+0/-14) lib/canonical/launchpad/emailtemplates/forgottenpassword.txt (+0/-11) lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt (+0/-15) lib/canonical/launchpad/emailtemplates/newuser-email.txt (+0/-19) lib/canonical/launchpad/interfaces/authtoken.py (+3/-53) lib/canonical/launchpad/templates/logintoken-newaccount.pt (+0/-19) lib/canonical/launchpad/templates/logintoken-resetpassword.pt (+0/-18) lib/canonical/launchpad/tests/test_login.py (+1/-20) lib/canonical/launchpad/tests/test_token_creation.py (+2/-2) lib/canonical/launchpad/webapp/login.py (+2/-20) lib/canonical/launchpad/webapp/publication.py (+1/-1) lib/canonical/launchpad/zcml/logintoken.zcml (+0/-16) lib/lp/registry/browser/configure.zcml (+0/-6) lib/lp/registry/browser/person.py (+2/-32) lib/lp/registry/interfaces/person.py (+1/-40) lib/lp/registry/stories/person/xx-new-profile.txt (+0/-110) lib/lp/registry/templates/people-newperson.pt (+0/-29) |
To merge this branch: | bzr merge lp:~salgado/launchpad/remove-logintoken-unused-code |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Lange (community) | Approve | ||
Review via email: mp+22719@code.launchpad.net |
Commit message
Description of the change
Remove views (and logintoken code) for registering new accounts and resetting passwords as this is now handled by the OpenID provider.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt' |
2 | --- lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt 2009-07-17 15:26:05 +0000 |
3 | +++ lib/canonical/launchpad/browser/ftests/logintoken-corner-cases.txt 2010-04-05 17:13:29 +0000 |
4 | @@ -7,25 +7,24 @@ |
5 | |
6 | === Double Post on the NewAccountView === |
7 | |
8 | -Using the +newaccount view on a token that was already confirmed should |
9 | +Using the +validateemail view on a token that was already consumed should |
10 | redirect to the default token view. This would happen if for example the |
11 | -user tried to re-post the form after registering his or her account. |
12 | +user tried to re-post the form after validating one of their email addresses. |
13 | |
14 | + >>> from lp.registry.interfaces.person import IPersonSet |
15 | >>> from canonical.launchpad.interfaces import ( |
16 | ... ILoginTokenSet, LoginTokenType) |
17 | - >>> from canonical.launchpad.browser import NewUserAccountView |
18 | + >>> from canonical.launchpad.browser import ValidateEmailView |
19 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
20 | |
21 | + >>> foo_bar = getUtility(IPersonSet).getByName('name16') |
22 | >>> token = getUtility(ILoginTokenSet).new( |
23 | - ... requester=None, requesteremail=None, |
24 | - ... email='foo@barino.com', tokentype=LoginTokenType.NEWACCOUNT) |
25 | + ... requester=foo_bar, requesteremail='foo.bar@canonical.com', |
26 | + ... email='foo@barino.com', tokentype=LoginTokenType.VALIDATEEMAIL) |
27 | >>> token.consume() |
28 | - >>> form = {'field.actions.continue': 'Continue', |
29 | - ... 'field.displayname': 'Alex', |
30 | - ... 'field.hide_email_addresses': 'off', |
31 | - ... 'field.password': '23-is-the-answer', |
32 | - ... 'field.password_dupe': '23-is-the-answer'} |
33 | - >>> view = NewUserAccountView(token, LaunchpadTestRequest(form=form)) |
34 | + >>> form = {'field.actions.continue': 'Continue'} |
35 | + >>> view = ValidateEmailView( |
36 | + ... token, LaunchpadTestRequest(form=form, method='POST')) |
37 | >>> view.initialize() |
38 | |
39 | >>> response = view.request.response |
40 | |
41 | === modified file 'lib/canonical/launchpad/browser/logintoken.py' |
42 | --- lib/canonical/launchpad/browser/logintoken.py 2010-03-12 19:56:06 +0000 |
43 | +++ lib/canonical/launchpad/browser/logintoken.py 2010-04-05 17:13:29 +0000 |
44 | @@ -11,8 +11,6 @@ |
45 | 'LoginTokenSetNavigation', |
46 | 'LoginTokenView', |
47 | 'MergePeopleView', |
48 | - 'NewUserAccountView', |
49 | - 'ResetPasswordView', |
50 | 'ValidateEmailView', |
51 | 'ValidateTeamEmailView', |
52 | 'ValidateGPGKeyView', |
53 | @@ -24,16 +22,13 @@ |
54 | |
55 | from zope.app.form.browser import TextAreaWidget |
56 | from zope.component import getUtility |
57 | -from zope.event import notify |
58 | from zope.interface import alsoProvides, directlyProvides, Interface |
59 | -from zope.lifecycleevent import ObjectCreatedEvent |
60 | from zope.security.proxy import removeSecurityProxy |
61 | |
62 | from canonical.database.sqlbase import flush_database_updates |
63 | from canonical.launchpad import _ |
64 | -from canonical.launchpad.interfaces.account import AccountStatus, IAccountSet |
65 | -from canonical.launchpad.interfaces.authtoken import ( |
66 | - IAuthToken, LoginTokenType) |
67 | +from canonical.launchpad.interfaces.account import AccountStatus |
68 | +from canonical.launchpad.interfaces.authtoken import LoginTokenType |
69 | from canonical.launchpad.interfaces.emailaddress import ( |
70 | EmailAddressStatus, IEmailAddressSet) |
71 | from canonical.launchpad.interfaces.gpghandler import ( |
72 | @@ -53,8 +48,7 @@ |
73 | from canonical.widgets import LaunchpadRadioWidget, PasswordChangeWidget |
74 | |
75 | from lp.registry.browser.team import HasRenewalPolicyMixin |
76 | -from lp.registry.interfaces.person import ( |
77 | - INewPersonForm, IPerson, IPersonSet, ITeam, PersonCreationRationale) |
78 | +from lp.registry.interfaces.person import IPerson, IPersonSet, ITeam |
79 | |
80 | |
81 | UTC = pytz.UTC |
82 | @@ -77,15 +71,9 @@ |
83 | they got this token because they tried to do something that required email |
84 | address confirmation, but that confirmation is already concluded. |
85 | """ |
86 | - auth_token_pages = { |
87 | - LoginTokenType.NEWPERSONLESSACCOUNT: '+newaccount', |
88 | - LoginTokenType.PASSWORDRECOVERY: '+resetpassword', |
89 | + PAGES = { |
90 | + LoginTokenType.ACCOUNTMERGE: '+accountmerge', |
91 | LoginTokenType.VALIDATEEMAIL: '+validateemail', |
92 | - } |
93 | - login_token_pages = { |
94 | - LoginTokenType.NEWPROFILE: '+newaccount', |
95 | - LoginTokenType.NEWACCOUNT: '+newaccount', |
96 | - LoginTokenType.ACCOUNTMERGE: '+accountmerge', |
97 | LoginTokenType.VALIDATETEAMEMAIL: '+validateteamemail', |
98 | LoginTokenType.VALIDATEGPG: '+validategpg', |
99 | LoginTokenType.VALIDATESIGNONLYGPG: '+validatesignonlygpg', |
100 | @@ -93,8 +81,6 @@ |
101 | LoginTokenType.TEAMCLAIM: '+claimteam', |
102 | LoginTokenType.BUGTRACKER: '+bugtracker-handshake', |
103 | } |
104 | - login_token_pages.update(auth_token_pages) |
105 | - PAGES = login_token_pages |
106 | page_title = 'You have already done this' |
107 | label = 'Confirmation already concluded' |
108 | |
109 | @@ -198,81 +184,6 @@ |
110 | return True |
111 | |
112 | |
113 | -class ResetPasswordView(BaseTokenView, LaunchpadFormView): |
114 | - |
115 | - schema = IAuthToken |
116 | - field_names = ['email', 'password'] |
117 | - custom_widget('password', PasswordChangeWidget) |
118 | - label = 'Reset password' |
119 | - expected_token_types = (LoginTokenType.PASSWORDRECOVERY,) |
120 | - |
121 | - def initialize(self): |
122 | - self.redirectIfInvalidOrConsumedToken() |
123 | - super(ResetPasswordView, self).initialize() |
124 | - |
125 | - def validate(self, form_values): |
126 | - """Validate the email address.""" |
127 | - email = form_values.get("email", "").strip() |
128 | - # All operations with email addresses must be case-insensitive. We |
129 | - # enforce that in EmailAddressSet, but here we only do a comparison, |
130 | - # so we have to .lower() them first. |
131 | - if email.lower() != self.context.email.lower(): |
132 | - self.addError(_( |
133 | - "The email address you provided didn't match the address " |
134 | - "you provided when requesting the password reset.")) |
135 | - |
136 | - @property |
137 | - def default_next_url(self): |
138 | - if self.context.redirection_url is not None: |
139 | - return self.context.redirection_url |
140 | - else: |
141 | - return self.request.getApplicationURL() |
142 | - |
143 | - @action(_('Continue'), name='continue') |
144 | - def continue_action(self, action, data): |
145 | - """Reset the user's password. When password is successfully changed, |
146 | - the AuthToken (self.context) used is consumed, so nobody can use |
147 | - it again. |
148 | - """ |
149 | - account = self.context.requester_account |
150 | - # Suspended accounts cannot reset their password. |
151 | - reason = ('Your password cannot be reset because your account ' |
152 | - 'is suspended.') |
153 | - if self.accountWasSuspended(account, reason): |
154 | - return |
155 | - |
156 | - naked_account = removeSecurityProxy(account) |
157 | - # Reset password can be used to reactivate a deactivated account. |
158 | - inactive_states = [AccountStatus.DEACTIVATED, AccountStatus.NOACCOUNT] |
159 | - if account.status in inactive_states: |
160 | - self.reactivate(data) |
161 | - self.request.response.addInfoNotification( |
162 | - _('Welcome back to Launchpad.')) |
163 | - else: |
164 | - naked_account.password = data.get('password') |
165 | - |
166 | - self.context.consume() |
167 | - self.logInPrincipalByEmail(self.context.email) |
168 | - |
169 | - self.request.response.addInfoNotification( |
170 | - _('Your password has been reset successfully.')) |
171 | - |
172 | - @action(_('Cancel'), name='cancel', validator='validate_cancel') |
173 | - def cancel_action(self, action, data): |
174 | - self._cancel() |
175 | - |
176 | - def reactivate(self, data): |
177 | - """Reactivate the person (and account) of this token.""" |
178 | - emailaddress = getUtility(IEmailAddressSet).getByEmail( |
179 | - self.context.email) |
180 | - # Need to remove the security proxy of the account because at this |
181 | - # point the user is not logged in. |
182 | - removeSecurityProxy(self.context.requester).reactivate( |
183 | - comment="User reactivated the account using reset password.", |
184 | - password=data['password'], |
185 | - preferred_email=emailaddress) |
186 | - |
187 | - |
188 | class ClaimProfileView(BaseTokenView, LaunchpadFormView): |
189 | schema = IPerson |
190 | field_names = ['displayname', 'hide_email_addresses', 'password'] |
191 | @@ -545,7 +456,8 @@ |
192 | label = 'Confirm e-mail address' |
193 | |
194 | def initialize(self): |
195 | - self.redirectIfInvalidOrConsumedToken() |
196 | + if self.redirectIfInvalidOrConsumedToken(): |
197 | + return |
198 | super(ValidateEmailView, self).initialize() |
199 | |
200 | def validate(self, data): |
201 | @@ -746,136 +658,3 @@ |
202 | self.request.response.setStatus(200) |
203 | self.request.response.setHeader('Content-type', 'text/plain') |
204 | return "Handshake token validated." |
205 | - |
206 | - |
207 | -class NewUserAccountView(BaseTokenView, LaunchpadFormView): |
208 | - """Page to create a new Launchpad user account.""" |
209 | - |
210 | - created_person = None |
211 | - |
212 | - schema = INewPersonForm |
213 | - field_names = ['displayname', 'hide_email_addresses', 'password'] |
214 | - custom_widget('password', PasswordChangeWidget) |
215 | - label = 'Complete your registration' |
216 | - expected_token_types = ( |
217 | - LoginTokenType.NEWACCOUNT, LoginTokenType.NEWPROFILE) |
218 | - |
219 | - def initialize(self): |
220 | - if self.redirectIfInvalidOrConsumedToken(): |
221 | - return |
222 | - else: |
223 | - self.email = getUtility(IEmailAddressSet).getByEmail( |
224 | - self.context.email) |
225 | - super(NewUserAccountView, self).initialize() |
226 | - |
227 | - @property |
228 | - def default_next_url(self): |
229 | - if self.context.redirection_url: |
230 | - return self.context.redirection_url |
231 | - elif self.created_person is not None: |
232 | - return canonical_url(self.created_person) |
233 | - else: |
234 | - return None |
235 | - |
236 | - def validate(self, form_values): |
237 | - """Verify if the email address is not used by an existing account.""" |
238 | - if self.email is not None: |
239 | - if self.email.person is not None: |
240 | - person = IMasterObject(self.email.person) |
241 | - if person.is_valid_person: |
242 | - self.addError(_( |
243 | - 'The email address ${email} is already registered.', |
244 | - mapping=dict(email=self.context.email))) |
245 | - else: |
246 | - self.addError(_( |
247 | - 'The email address ${email} is already registered in ' |
248 | - 'the Launchpad Login Service (used by the Ubuntu shop ' |
249 | - 'and other OpenID sites). Please use the same email and ' |
250 | - 'password to log into Launchpad.', |
251 | - mapping=dict(email=self.context.email))) |
252 | - |
253 | - |
254 | - @action(_('Continue'), name='continue') |
255 | - def continue_action(self, action, data): |
256 | - """Create a new Person with the context's email address and set a |
257 | - preferred email and password to it, or use an existing Person |
258 | - associated with the context's email address, setting it as the |
259 | - preferred address and also setting the password. |
260 | - |
261 | - If everything went ok, we consume the LoginToken (self.context), so |
262 | - nobody can use it again. |
263 | - """ |
264 | - if self.email is not None: |
265 | - assert self.email.person is not None, ( |
266 | - "People trying to register using emails associated with " |
267 | - "personless accounts should be told to just use their Login " |
268 | - "Service credentials to log into LP") |
269 | - # This is a placeholder profile automatically created by one of |
270 | - # our scripts, let's just confirm its email address and set a |
271 | - # password. |
272 | - person = getUtility(IPersonSet).get( |
273 | - removeSecurityProxy(self.email).personID) |
274 | - assert not person.is_valid_person, ( |
275 | - 'Account %s has already been claimed and this should ' |
276 | - 'have been caught by the validate() method.' % person.name) |
277 | - email = self.email |
278 | - # The user is not yet logged in, but we need to set some |
279 | - # things on his new account, so we need to remove the security |
280 | - # proxy from it. |
281 | - # XXX: Guilherme Salgado 2006-09-27 bug=62674: |
282 | - # We should be able to login with this person and set the |
283 | - # password, to avoid removing the security proxy, but it didn't |
284 | - # work, so I'm leaving this hack for now. |
285 | - naked_person = removeSecurityProxy(person) |
286 | - # Suspended accounts cannot reactivate their profile. |
287 | - reason = ('This profile cannot be claimed because the account ' |
288 | - 'is suspended.') |
289 | - if self.accountWasSuspended(person.account, reason): |
290 | - return |
291 | - naked_person.displayname = data['displayname'] |
292 | - naked_person.hide_email_addresses = data['hide_email_addresses'] |
293 | - # Need to remove the security proxy of the account because at this |
294 | - # point the user is not logged in. |
295 | - account = removeSecurityProxy(IMasterObject(person.account)) |
296 | - account.activate( |
297 | - "Activated by new account.", |
298 | - password=data['password'], |
299 | - preferred_email=self.email) |
300 | - naked_person.creation_rationale = self._getCreationRationale() |
301 | - naked_person.creation_comment = None |
302 | - else: |
303 | - account, person, email = self._createAccountEmailAndMaybePerson( |
304 | - data['displayname'], data['hide_email_addresses'], |
305 | - data['password']) |
306 | - |
307 | - self.context.consume() |
308 | - self.logInPrincipalByEmail(removeSecurityProxy(email).email) |
309 | - self.created_person = person |
310 | - self.request.response.addInfoNotification(_( |
311 | - "Registration completed successfully")) |
312 | - |
313 | - def _getCreationRationale(self): |
314 | - """The creation rationale that should be used for this account.""" |
315 | - return PersonCreationRationale.OWNER_CREATED_LAUNCHPAD |
316 | - |
317 | - def _createAccountEmailAndMaybePerson( |
318 | - self, displayname, hide_email_addresses, password): |
319 | - """Create and return a new Account, Person and EmailAddress. |
320 | - |
321 | - This method will always create an Account (in the ACTIVE state), |
322 | - an EmailAddress as the account's preferred one and a Person. |
323 | - |
324 | - Also fire ObjectCreatedEvents for both the newly created Account, |
325 | - EmailAddress, and Person. |
326 | - """ |
327 | - person, email = getUtility(IPersonSet).createPersonAndEmail( |
328 | - self.context.email, rationale=self._getCreationRationale(), |
329 | - displayname=displayname, password=password, |
330 | - passwordEncrypted=True, hide_email_addresses=hide_email_addresses) |
331 | - person.validateAndEnsurePreferredEmail(email) |
332 | - account = getUtility(IAccountSet).get(person.accountID) |
333 | - removeSecurityProxy(account).status = AccountStatus.ACTIVE |
334 | - notify(ObjectCreatedEvent(person)) |
335 | - notify(ObjectCreatedEvent(account)) |
336 | - notify(ObjectCreatedEvent(email)) |
337 | - return account, person, email |
338 | |
339 | === modified file 'lib/canonical/launchpad/browser/tests/test_logintoken.py' |
340 | --- lib/canonical/launchpad/browser/tests/test_logintoken.py 2009-11-03 17:13:21 +0000 |
341 | +++ lib/canonical/launchpad/browser/tests/test_logintoken.py 2010-04-05 17:13:29 +0000 |
342 | @@ -6,7 +6,7 @@ |
343 | from zope.component import getUtility |
344 | |
345 | from canonical.launchpad.browser.logintoken import ( |
346 | - ClaimTeamView, ResetPasswordView, ValidateEmailView, ValidateGPGKeyView) |
347 | + ClaimTeamView, ValidateEmailView, ValidateGPGKeyView) |
348 | from canonical.launchpad.ftests import LaunchpadFormHarness |
349 | from canonical.launchpad.interfaces.authtoken import LoginTokenType |
350 | from canonical.launchpad.interfaces.logintoken import ILoginTokenSet |
351 | @@ -29,12 +29,6 @@ |
352 | self.email = self.person.preferredemail.email |
353 | self.expected_next_url = 'http://127.0.0.1/~test-user' |
354 | |
355 | - def test_ResetPasswordView(self): |
356 | - token = getUtility(ILoginTokenSet).new( |
357 | - self.person, self.email, self.email, |
358 | - LoginTokenType.PASSWORDRECOVERY) |
359 | - self._testCancelAction(ResetPasswordView, token) |
360 | - |
361 | def test_ClaimTeamView(self): |
362 | token = getUtility(ILoginTokenSet).new( |
363 | self.person, self.email, self.email, LoginTokenType.TEAMCLAIM) |
364 | |
365 | === removed file 'lib/canonical/launchpad/browser/tests/test_password_reset.py' |
366 | --- lib/canonical/launchpad/browser/tests/test_password_reset.py 2010-03-11 20:54:36 +0000 |
367 | +++ lib/canonical/launchpad/browser/tests/test_password_reset.py 1970-01-01 00:00:00 +0000 |
368 | @@ -1,94 +0,0 @@ |
369 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
370 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
371 | - |
372 | -import unittest |
373 | - |
374 | -from zope.component import getUtility |
375 | -from zope.security.proxy import removeSecurityProxy |
376 | - |
377 | -import transaction |
378 | -from canonical.launchpad.browser.logintoken import ResetPasswordView |
379 | -from canonical.launchpad.ftests import LaunchpadFormHarness |
380 | -from canonical.launchpad.interfaces.account import AccountStatus |
381 | -from canonical.launchpad.interfaces.authtoken import LoginTokenType |
382 | -from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus |
383 | -from canonical.launchpad.interfaces.logintoken import ILoginTokenSet |
384 | -from canonical.launchpad.interfaces.lpstorm import IMasterObject |
385 | -from lp.testing import TestCaseWithFactory |
386 | -from canonical.testing import DatabaseFunctionalLayer |
387 | - |
388 | - |
389 | -class TestPasswordReset(TestCaseWithFactory): |
390 | - layer = DatabaseFunctionalLayer |
391 | - email = 'foo@example.com' |
392 | - |
393 | - def _create_inactive_person(self): |
394 | - self.person = self.factory.makePerson( |
395 | - email=self.email, email_address_status=EmailAddressStatus.NEW) |
396 | - self.account = self.person.account |
397 | - self.assertEquals(self.account.status, AccountStatus.NOACCOUNT) |
398 | - |
399 | - def test_inactive_accounts_are_activated(self): |
400 | - # Resetting the password of an account in the NOACCOUNT state will |
401 | - # activate it. |
402 | - self._create_inactive_person() |
403 | - self._resetPassword(ensure_no_errors=True) |
404 | - self.assertEquals(self.account.status, AccountStatus.ACTIVE) |
405 | - self.assertEquals( |
406 | - self.account.preferredemail.email, 'foo@example.com') |
407 | - |
408 | - def _create_deactivated_person(self): |
409 | - self.person = self.factory.makePerson(email=self.email) |
410 | - removeSecurityProxy(self.person).deactivateAccount('Testing') |
411 | - # Get the account from the master DB to make sure it has the changes |
412 | - # we did above. |
413 | - self.account = IMasterObject(self.person.account) |
414 | - self.assertEquals(self.account.status, AccountStatus.DEACTIVATED) |
415 | - |
416 | - def test_deactivated_accounts_are_reactivated(self): |
417 | - # Resetting the password of an account in the DEACTIVATED state will |
418 | - # reactivate it. |
419 | - self._create_deactivated_person() |
420 | - self._resetPassword(ensure_no_errors=True) |
421 | - self.assertEquals(self.account.status, AccountStatus.ACTIVE) |
422 | - self.assertEquals( |
423 | - self.account.preferredemail.email, 'foo@example.com') |
424 | - |
425 | - def _create_suspended_person(self): |
426 | - self.person = self.factory.makePerson(email=self.email) |
427 | - # Get the account from the master DB as we're going to change it. |
428 | - self.account = IMasterObject(self.person.account) |
429 | - removeSecurityProxy(self.account).status = AccountStatus.SUSPENDED |
430 | - self.assertEquals(self.account.status, AccountStatus.SUSPENDED) |
431 | - |
432 | - def test_suspended_accounts_cannot_reset_password(self): |
433 | - # It's not possible to reset the password of a SUSPENDED account. |
434 | - self._create_suspended_person() |
435 | - harness = self._resetPassword() |
436 | - notifications = [notification.message |
437 | - for notification in harness.request.notifications] |
438 | - self.assertIn( |
439 | - 'Your password cannot be reset because your account is suspended', |
440 | - '\n'.join(notifications)) |
441 | - self.assertEquals(self.account.status, AccountStatus.SUSPENDED) |
442 | - |
443 | - def _resetPassword(self, ensure_no_errors=False): |
444 | - token = getUtility(ILoginTokenSet).new( |
445 | - self.person, self.email, self.email, |
446 | - LoginTokenType.PASSWORDRECOVERY) |
447 | - harness = LaunchpadFormHarness(token, ResetPasswordView) |
448 | - form = {'field.email': self.email, |
449 | - 'field.password': 'test', |
450 | - 'field.password_dupe': 'test'} |
451 | - harness.submit('continue', form) |
452 | - if ensure_no_errors: |
453 | - self.assertFalse(harness.hasErrors()) |
454 | - # Need to manually commit because we're interested in testing the |
455 | - # changes done by the view on the token's requester (i.e. |
456 | - # self.person). |
457 | - transaction.commit() |
458 | - return harness |
459 | - |
460 | - |
461 | -def test_suite(): |
462 | - return unittest.TestLoader().loadTestsFromName(__name__) |
463 | |
464 | === modified file 'lib/canonical/launchpad/database/logintoken.py' |
465 | --- lib/canonical/launchpad/database/logintoken.py 2010-03-05 13:04:06 +0000 |
466 | +++ lib/canonical/launchpad/database/logintoken.py 2010-04-05 17:13:29 +0000 |
467 | @@ -152,23 +152,6 @@ |
468 | subject = 'Launchpad: Confirm your OpenPGP Key' |
469 | self._send_email(from_name, subject, text) |
470 | |
471 | - def sendPasswordResetEmail(self): |
472 | - """See ILoginToken.""" |
473 | - template = get_email_template('forgottenpassword.txt') |
474 | - from_name = "Launchpad" |
475 | - message = template % dict(token_url=canonical_url(self)) |
476 | - subject = "Launchpad: Forgotten Password" |
477 | - self._send_email(from_name, subject, message) |
478 | - |
479 | - def sendNewUserEmail(self): |
480 | - """See ILoginToken.""" |
481 | - template = get_email_template('newuser-email.txt') |
482 | - message = template % dict(token_url=canonical_url(self)) |
483 | - |
484 | - from_name = "Launchpad" |
485 | - subject = "Launchpad: complete your registration" |
486 | - self._send_email(from_name, subject, message) |
487 | - |
488 | def sendProfileCreatedEmail(self, profile, comment): |
489 | """See ILoginToken.""" |
490 | template = get_email_template('profile-created.txt') |
491 | |
492 | === modified file 'lib/canonical/launchpad/doc/logintoken-pages.txt' |
493 | --- lib/canonical/launchpad/doc/logintoken-pages.txt 2009-04-17 10:32:16 +0000 |
494 | +++ lib/canonical/launchpad/doc/logintoken-pages.txt 2010-04-05 17:13:29 +0000 |
495 | @@ -1,19 +1,18 @@ |
496 | = LoginToken pages = |
497 | |
498 | -Users interact with login tokens for operations that require the user to |
499 | -prove he has access to a resource that is external to Launchpad. For |
500 | -example, claiming an email address or resetting a password require the |
501 | -user to use the login token sent to him in an email. |
502 | - |
503 | - |
504 | -== Reset password view == |
505 | - |
506 | -A user can reset his password if he forgets it. A link for a LoginToken |
507 | -is sent to the email address that the user claims is his. The view |
508 | -displays a form asking the user to confirm his email address and provide |
509 | -a new password. |
510 | - |
511 | - >>> from zope.component import getMultiAdapter, getUtility |
512 | +Users interact with login tokens for operations that require the user to prove |
513 | +he has access to a resource that is external to Launchpad. For example, |
514 | +claiming an email address or a OpenPGP key require the user to use the login |
515 | +token sent to him in an email. |
516 | + |
517 | + |
518 | +== Claiming a profile == |
519 | + |
520 | +The ClaimProfileView allows users to claim accounts created by Launchpad |
521 | +processes. The user profile for Matsubara was created during an import. |
522 | +The user profile does not have a preferred email address or a password. |
523 | + |
524 | + >>> from zope.component import getMultiAdapter |
525 | >>> from canonical.launchpad.interfaces.authtoken import LoginTokenType |
526 | >>> from canonical.launchpad.interfaces.logintoken import ( |
527 | ... ILoginTokenSet) |
528 | @@ -24,159 +23,7 @@ |
529 | # of gettint it as a zope secured utility. If we don't do that we'd have |
530 | # to remove the security proxy of the person objects all the time. |
531 | >>> person_set = PersonSet() |
532 | - >>> no_priv = person_set.getByEmail('no-priv@canonical.com') |
533 | - >>> old_password = no_priv.account.password |
534 | - |
535 | >>> login_token_set = getUtility(ILoginTokenSet) |
536 | - >>> login_token = login_token_set.new( |
537 | - ... no_priv, 'no-priv@canonical.com', |
538 | - ... 'no-priv@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
539 | - >>> request = LaunchpadTestRequest( |
540 | - ... SERVER_URL='http://launchpad.dev', |
541 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
542 | - ... method='POST', |
543 | - ... form={ |
544 | - ... 'field.email': 'no-priv@canonical.com', |
545 | - ... 'field.password': 'test1', |
546 | - ... 'field.password_dupe': 'test1', |
547 | - ... 'field.actions.continue': 'Continue', |
548 | - ... }) |
549 | - >>> login(ANONYMOUS, request) |
550 | - >>> resetpassword_view = getMultiAdapter( |
551 | - ... (login_token, request), name="+resetpassword") |
552 | - >>> resetpassword_view.initialize() |
553 | - >>> old_password == no_priv.account.password |
554 | - False |
555 | - |
556 | -If the user submits the wrong email address for the token, an error is |
557 | -stored. The password is not reset. |
558 | - |
559 | - >>> login_token = login_token_set.new( |
560 | - ... no_priv, 'no-priv@canonical.com', |
561 | - ... 'no-priv@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
562 | - >>> request = LaunchpadTestRequest( |
563 | - ... SERVER_URL='http://launchpad.dev', |
564 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
565 | - ... method='POST', |
566 | - ... form={ |
567 | - ... 'field.email': 'wrong@canonical.com', |
568 | - ... 'field.password': 'test2', |
569 | - ... 'field.password_dupe': 'test2', |
570 | - ... 'field.actions.continue': 'Continue', |
571 | - ... }) |
572 | - >>> login(ANONYMOUS, request) |
573 | - >>> resetpassword_view = getMultiAdapter( |
574 | - ... (login_token, request), name="+resetpassword") |
575 | - >>> resetpassword_view.initialize() |
576 | - >>> resetpassword_view.errors |
577 | - [u"The email address ... didn't match ... the password reset."] |
578 | - |
579 | - >>> old_password == no_priv.account.password |
580 | - True |
581 | - |
582 | - |
583 | -=== Reactivating an account using reset password === |
584 | - |
585 | -A user with a DEACTIVATED account does not have a password--he must use |
586 | -the +resetpassword view to set his account to ACTIVE and add a password. |
587 | - |
588 | - >>> from canonical.launchpad.interfaces import IMasterObject |
589 | - >>> former_user = IMasterObject( |
590 | - ... person_set.getByEmail('former-user@canonical.com')) |
591 | - >>> former_user.name |
592 | - u'former-user-deactivatedaccount' |
593 | - |
594 | - >>> former_user.account.status |
595 | - <DBItem AccountStatus.DEACTIVATED, ...> |
596 | - >>> print former_user.account.password |
597 | - None |
598 | - |
599 | - >>> login_token = login_token_set.new( |
600 | - ... former_user, 'former-user@canonical.com', |
601 | - ... 'former-user@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
602 | - >>> transaction.commit() |
603 | - |
604 | -If the user provides the correct email address for the token, the |
605 | -password is reset, and the account status is set ACTIVE. |
606 | - |
607 | - >>> request = LaunchpadTestRequest( |
608 | - ... SERVER_URL='http://launchpad.dev', |
609 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
610 | - ... method='POST', |
611 | - ... form={ |
612 | - ... 'field.email': 'former-user@canonical.com', |
613 | - ... 'field.password': 'test3', |
614 | - ... 'field.password_dupe': 'test3', |
615 | - ... 'field.actions.continue': 'Continue', |
616 | - ... }) |
617 | - >>> login(ANONYMOUS, request) |
618 | - >>> resetpassword_view = getMultiAdapter( |
619 | - ... (login_token, request), name="+resetpassword") |
620 | - >>> resetpassword_view.initialize() |
621 | - >>> transaction.commit() |
622 | - |
623 | - >>> former_user.name |
624 | - u'former-user' |
625 | - >>> former_user.account.status |
626 | - <DBItem AccountStatus.ACTIVE, ...> |
627 | - >>> former_user.is_valid_person |
628 | - True |
629 | - |
630 | - |
631 | -== Reset password cannot reactivate a Suspended account == |
632 | - |
633 | -A user with a SUSPENDED account cannot use +resetpassword view to set his |
634 | -account to ACTIVE, nor will it reset his password. |
635 | - |
636 | - >>> from canonical.launchpad.interfaces.account import AccountStatus |
637 | - |
638 | - # Admins can suspend an account and access the user's password. |
639 | - >>> login('foo.bar@canonical.com') |
640 | - >>> suspended_user = factory.makePerson( |
641 | - ... email='suspended-user@canonical.com', |
642 | - ... name='suspended-user', |
643 | - ... password='invalid') |
644 | - >>> suspended_account = IMasterObject(suspended_user.account) |
645 | - >>> suspended_account.status = AccountStatus.SUSPENDED |
646 | - >>> original_password = suspended_account.password |
647 | - >>> transaction.commit() |
648 | - >>> suspended_user.is_valid_person |
649 | - False |
650 | - >>> suspended_account.status |
651 | - <DBItem AccountStatus.SUSPENDED, ...> |
652 | - |
653 | - >>> login_token = login_token_set.new( |
654 | - ... suspended_user, 'suspended-user@canonical.com', |
655 | - ... 'suspended-user@canonical.com', LoginTokenType.PASSWORDRECOVERY) |
656 | - >>> request = LaunchpadTestRequest( |
657 | - ... SERVER_URL='http://launchpad.dev', |
658 | - ... PATH_INFO='/token/%s/+resetpassword' % login_token.token, |
659 | - ... method='POST', |
660 | - ... form={ |
661 | - ... 'field.email': 'suspended-user@canonical.com', |
662 | - ... 'field.password': 'bad-intentions', |
663 | - ... 'field.password_dupe': 'bad-intentions', |
664 | - ... 'field.actions.continue': 'Continue', |
665 | - ... }) |
666 | - >>> login(ANONYMOUS, request) |
667 | - >>> resetpassword_view = getMultiAdapter( |
668 | - ... (login_token, request), name="+resetpassword") |
669 | - >>> resetpassword_view.initialize() |
670 | - |
671 | - >>> login('foo.bar@canonical.com') |
672 | - >>> suspended_user.is_valid_person |
673 | - False |
674 | - >>> suspended_user.account.password == original_password |
675 | - True |
676 | - >>> suspended_user.account.status |
677 | - <DBItem AccountStatus.SUSPENDED, ...> |
678 | - |
679 | - |
680 | -== Claiming a profile == |
681 | - |
682 | -The ClaimProfileView allows users to claim accounts created by Launchpad |
683 | -processes. The user profile for Matsubara was created during an import. |
684 | -The user profile does not have a preferred email address or a password. |
685 | |
686 | >>> matsubara = person_set.getByEmail('matsubara@async.com.br') |
687 | >>> matsubara.name |
688 | @@ -240,135 +87,6 @@ |
689 | True |
690 | |
691 | |
692 | -== Creating a new ordinary account == |
693 | - |
694 | -Unlike accounts created using OpenID, which don't have an associated Person |
695 | -(as shown in sso-workflow-register.txt), ordinary accounts will always have |
696 | -a Person entry associated with them. |
697 | - |
698 | - >>> login_token = login_token_set.new( |
699 | - ... None, 'foo@debian.org', 'foo@debian.org', |
700 | - ... LoginTokenType.NEWACCOUNT) |
701 | - >>> request = LaunchpadTestRequest( |
702 | - ... SERVER_URL='http://launchpad.dev', |
703 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
704 | - ... method='POST', |
705 | - ... form={ |
706 | - ... 'field.displayname': 'Andre Lopes', |
707 | - ... 'field.hide_email_addresses.used': '', |
708 | - ... 'field.password': 'test', |
709 | - ... 'field.password_dupe': 'test', |
710 | - ... 'field.actions.continue': 'Continue', |
711 | - ... }) |
712 | - >>> login(ANONYMOUS, request) |
713 | - >>> newaccount_view = getMultiAdapter( |
714 | - ... (login_token, request), name="+newaccount") |
715 | - >>> newaccount_view.initialize() |
716 | - >>> newaccount_view.errors |
717 | - [] |
718 | - >>> person_set.getByEmail('foo@debian.org') |
719 | - <Person... |
720 | - |
721 | - |
722 | -== Creating a new account from an existing profile == |
723 | - |
724 | -If a user's email address matches the email address of an unclaimed |
725 | -account, the existing profile is activated and given to the user. |
726 | -Andre Lopes (andrelop) has an account created via an automated process. |
727 | - |
728 | - >>> andrelop = person_set.getByEmail('andrelop@debian.org') |
729 | - >>> andrelop.name |
730 | - u'andrelop' |
731 | - >>> print andrelop.preferredemail |
732 | - None |
733 | - |
734 | - >>> andrelop.account.status |
735 | - <DBItem AccountStatus.NOACCOUNT, ...> |
736 | - >>> andrelop.is_valid_person |
737 | - False |
738 | - |
739 | -Andre would normally create a new account by claiming a LoginToken |
740 | -that verifies his access to the email address his provided. |
741 | - |
742 | - >>> login_token = login_token_set.new( |
743 | - ... andrelop, 'andrelop@debian.org', |
744 | - ... 'andrelop@debian.org', LoginTokenType.NEWACCOUNT) |
745 | - >>> transaction.commit() |
746 | - |
747 | -The NewAccountView will activate the existing account and update |
748 | -the Person object. |
749 | - |
750 | - >>> request = LaunchpadTestRequest( |
751 | - ... SERVER_URL='http://launchpad.dev', |
752 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
753 | - ... method='POST', |
754 | - ... form={ |
755 | - ... 'field.displayname': 'Andre Lopes', |
756 | - ... 'field.hide_email_addresses.used': '', |
757 | - ... 'field.password': 'test5', |
758 | - ... 'field.password_dupe': 'test5', |
759 | - ... 'field.actions.continue': 'Continue', |
760 | - ... }) |
761 | - >>> login(ANONYMOUS, request) |
762 | - >>> claimprofile_view = getMultiAdapter( |
763 | - ... (login_token, request), name="+newaccount") |
764 | - >>> claimprofile_view.initialize() |
765 | - >>> claimprofile_view.errors |
766 | - [] |
767 | - >>> transaction.commit() |
768 | - |
769 | -The existing Account, Person and EmailAddress are updated. |
770 | - |
771 | - >>> andrelop.displayname |
772 | - u'Andre Lopes' |
773 | - >>> andrelop.hide_email_addresses |
774 | - False |
775 | - >>> andrelop.account.preferredemail.email |
776 | - u'andrelop@debian.org' |
777 | - |
778 | - >>> andrelop.account.status |
779 | - <DBItem AccountStatus.ACTIVE, ...> |
780 | - >>> andrelop.account.status_comment |
781 | - u'Activated by new account.' |
782 | - >>> andrelop.is_valid_person |
783 | - True |
784 | - |
785 | - |
786 | -== Creating a new account cannot reactivate a suspended profile == |
787 | - |
788 | -An existing account that is SUSPENDED cannot be reactivated by claiming |
789 | -the email address during account creation. |
790 | - |
791 | - >>> login_token = login_token_set.new( |
792 | - ... suspended_user, 'suspended-user@canonical.com', |
793 | - ... 'suspended-user@canonical.com', LoginTokenType.NEWACCOUNT) |
794 | - >>> request = LaunchpadTestRequest( |
795 | - ... SERVER_URL='http://launchpad.dev', |
796 | - ... PATH_INFO='/token/%s/+newaccount' % login_token.token, |
797 | - ... method='POST', |
798 | - ... form={ |
799 | - ... 'field.displayname': 'Bad User', |
800 | - ... 'field.hide_email_addresses.used': '', |
801 | - ... 'field.password': 'bad-intentions', |
802 | - ... 'field.password_dupe': 'bad-intentions', |
803 | - ... 'field.actions.continue': 'Continue', |
804 | - ... }) |
805 | - >>> login(ANONYMOUS, request) |
806 | - >>> claimprofile_view = getMultiAdapter( |
807 | - ... (login_token, request), name="+newaccount") |
808 | - >>> claimprofile_view.initialize() |
809 | - >>> claimprofile_view.errors |
810 | - [] |
811 | - |
812 | - >>> login('foo.bar@canonical.com') |
813 | - >>> suspended_user.is_valid_person |
814 | - False |
815 | - >>> suspended_user.account.password == original_password |
816 | - True |
817 | - >>> suspended_user.account.status |
818 | - <DBItem AccountStatus.SUSPENDED, ...> |
819 | - |
820 | - |
821 | == Validating GPG keys == |
822 | |
823 | In order to add a GPG key to Launchpad we require that the person |
824 | |
825 | === removed file 'lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt' |
826 | --- lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt 2007-06-19 15:54:26 +0000 |
827 | +++ lib/canonical/launchpad/emailtemplates/forgottenpassword-neutral.txt 1970-01-01 00:00:00 +0000 |
828 | @@ -1,14 +0,0 @@ |
829 | -Hello |
830 | - |
831 | -You have requested a new password for your Launchpad Login Service account. |
832 | - |
833 | -To change your password: |
834 | - |
835 | - %(token_url)s |
836 | - |
837 | -If you don't know what this is about, then someone else has entered |
838 | -your e-mail address at the Launchpad Login Service. Sorry about |
839 | -that. You don't need to do anything further, just delete this message. |
840 | - |
841 | -Regards, |
842 | -The Launchpad Login Service team |
843 | |
844 | === removed file 'lib/canonical/launchpad/emailtemplates/forgottenpassword.txt' |
845 | --- lib/canonical/launchpad/emailtemplates/forgottenpassword.txt 2006-08-31 17:52:22 +0000 |
846 | +++ lib/canonical/launchpad/emailtemplates/forgottenpassword.txt 1970-01-01 00:00:00 +0000 |
847 | @@ -1,11 +0,0 @@ |
848 | -Hi |
849 | - |
850 | -You, or someone posing as you, has requested a new password for |
851 | -your Launchpad account. |
852 | - |
853 | -To reset your password, please click on the link below. |
854 | -You will need to enter this email address, enter and confirm your new password. |
855 | - |
856 | - %(token_url)s |
857 | - |
858 | -The Launchpad Team |
859 | |
860 | === removed file 'lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt' |
861 | --- lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt 2007-06-19 15:54:26 +0000 |
862 | +++ lib/canonical/launchpad/emailtemplates/newuser-email-neutral.txt 1970-01-01 00:00:00 +0000 |
863 | @@ -1,15 +0,0 @@ |
864 | -Hello |
865 | - |
866 | -Thank you for registering with the Launchpad Login Service. |
867 | - |
868 | -To complete your registration: |
869 | - |
870 | - %(token_url)s |
871 | - |
872 | -If you don't know what this is about, then someone has probably |
873 | -entered your e-mail address by mistake at the Launchpad Login Service |
874 | -Web site. Sorry about that. You don't need to do anything further, |
875 | -just delete this message. |
876 | - |
877 | -Regards, |
878 | -The Launchpad Login Service team |
879 | |
880 | === removed file 'lib/canonical/launchpad/emailtemplates/newuser-email.txt' |
881 | --- lib/canonical/launchpad/emailtemplates/newuser-email.txt 2008-11-17 17:17:33 +0000 |
882 | +++ lib/canonical/launchpad/emailtemplates/newuser-email.txt 1970-01-01 00:00:00 +0000 |
883 | @@ -1,19 +0,0 @@ |
884 | -Hello! |
885 | - |
886 | -Thank you for registering at Launchpad. To complete your |
887 | -registration, please follow the instructions here: |
888 | - |
889 | - %(token_url)s |
890 | - |
891 | -Launchpad is a suite of applications for people who work with |
892 | -free software projects. Using Launchpad you can report bugs, help |
893 | -with translation, find the answer to a question, find and share |
894 | -code and host your own free software project. |
895 | - |
896 | -Once you've completed the registration, take a look at our |
897 | -"getting started" guide here: |
898 | - |
899 | - https://help.launchpad.net/NewToLaunchpad |
900 | - |
901 | -Thanks, |
902 | -The Launchpad team. |
903 | |
904 | === modified file 'lib/canonical/launchpad/interfaces/authtoken.py' |
905 | --- lib/canonical/launchpad/interfaces/authtoken.py 2009-10-13 16:59:43 +0000 |
906 | +++ lib/canonical/launchpad/interfaces/authtoken.py 2010-04-05 17:13:29 +0000 |
907 | @@ -10,7 +10,6 @@ |
908 | __all__ = [ |
909 | 'LoginTokenType', |
910 | 'IAuthToken', |
911 | - 'IAuthTokenSet', |
912 | ] |
913 | |
914 | from zope.schema import Choice, Datetime, Int, Text, TextLine |
915 | @@ -118,6 +117,9 @@ |
916 | """) |
917 | |
918 | |
919 | +# XXX: Guilherme Salgado, 2010-03-30: This interface was created to be used by |
920 | +# our old OpenID provider, but that doesn't exist anymore, so we should merge |
921 | +# it with ILoginToken. |
922 | class IAuthToken(Interface): |
923 | """The object that stores one time tokens used for validating email |
924 | addresses and other tasks that require verifying if an email address is |
925 | @@ -193,55 +195,3 @@ |
926 | """Send an email message to the requester with a magic URL that allows |
927 | him to finish the Launchpad registration process. |
928 | """ |
929 | - |
930 | - |
931 | -class IAuthTokenSet(Interface): |
932 | - """The set of AuthTokens.""" |
933 | - |
934 | - title = Attribute('Title') |
935 | - |
936 | - def get(id, default=None): |
937 | - """Return the AuthToken object with the given id. |
938 | - |
939 | - Return the default value if there's no such AuthToken. |
940 | - """ |
941 | - |
942 | - def searchByEmailAccountAndType(email, account, type, consumed=None): |
943 | - """Return all AuthTokens for the given email, account and type. |
944 | - |
945 | - :param email: The email address to search for. |
946 | - :param account: The Account object representing the requester |
947 | - to search for. |
948 | - :param type: The AuthTokenType to search for. |
949 | - :param consumed: A flag indicating whether to return consumed tokens. |
950 | - If False, only unconsumed tokens will be returned. |
951 | - If True, only consumed tokens will be returned. |
952 | - If None, this parameter will be ignored and all tokens will be |
953 | - returned. |
954 | - """ |
955 | - |
956 | - def deleteByEmailAccountAndType(email, account, type): |
957 | - """Delete all AuthToken entries with the given email, |
958 | - requester account and type.""" |
959 | - |
960 | - def new(requester, requesteremail, email, tokentype, redirection_url): |
961 | - """Create a new AuthToken object. |
962 | - |
963 | - :param requester: a Person object or None (in case of a new |
964 | - account) |
965 | - :param requesteremail: the email address used to login on the |
966 | - system. Can also be None in case of a new account |
967 | - :param email: the email address that this request will be sent |
968 | - to. It should be previously validated by valid_email() |
969 | - :param tokentype: the type of the request, according to |
970 | - LoginTokenType. |
971 | - :param redirection_url: the URL the user will be forwarded to |
972 | - after consuming the token. May be None. |
973 | - """ |
974 | - |
975 | - def __getitem__(id): |
976 | - """Returns the AuthToken with the given id. |
977 | - |
978 | - Raises KeyError if there is no such AuthToken. |
979 | - """ |
980 | - |
981 | |
982 | === removed file 'lib/canonical/launchpad/templates/logintoken-newaccount.pt' |
983 | --- lib/canonical/launchpad/templates/logintoken-newaccount.pt 2009-09-16 20:51:57 +0000 |
984 | +++ lib/canonical/launchpad/templates/logintoken-newaccount.pt 1970-01-01 00:00:00 +0000 |
985 | @@ -1,19 +0,0 @@ |
986 | -<html |
987 | - xmlns="http://www.w3.org/1999/xhtml" |
988 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
989 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
990 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
991 | - metal:use-macro="view/macro:page/locationless" |
992 | - i18n:domain="launchpad" |
993 | -> |
994 | - <body> |
995 | - |
996 | - <div metal:fill-slot="main"> |
997 | - |
998 | - <div metal:use-macro="context/@@launchpad_form/form" /> |
999 | - |
1000 | - </div> |
1001 | - </body> |
1002 | - |
1003 | -</html> |
1004 | - |
1005 | |
1006 | === removed file 'lib/canonical/launchpad/templates/logintoken-resetpassword.pt' |
1007 | --- lib/canonical/launchpad/templates/logintoken-resetpassword.pt 2009-09-16 20:51:57 +0000 |
1008 | +++ lib/canonical/launchpad/templates/logintoken-resetpassword.pt 1970-01-01 00:00:00 +0000 |
1009 | @@ -1,18 +0,0 @@ |
1010 | -<html |
1011 | - xmlns="http://www.w3.org/1999/xhtml" |
1012 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
1013 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
1014 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
1015 | - metal:use-macro="view/macro:page/locationless" |
1016 | - i18n:domain="launchpad" |
1017 | -> |
1018 | - <body> |
1019 | - |
1020 | -<div metal:fill-slot="main"> |
1021 | - |
1022 | - <div metal:use-macro="context/@@launchpad_form/form" /> |
1023 | - |
1024 | -</div> |
1025 | - |
1026 | -</body> |
1027 | -</html> |
1028 | |
1029 | === modified file 'lib/canonical/launchpad/tests/test_login.py' |
1030 | --- lib/canonical/launchpad/tests/test_login.py 2009-07-17 00:26:05 +0000 |
1031 | +++ lib/canonical/launchpad/tests/test_login.py 2010-04-05 17:13:29 +0000 |
1032 | @@ -13,13 +13,11 @@ |
1033 | from canonical.launchpad.ftests import ANONYMOUS, login |
1034 | from canonical.launchpad.interfaces.account import ( |
1035 | AccountCreationRationale, IAccountSet) |
1036 | -from lp.registry.interfaces.person import IPerson |
1037 | from lp.testing import TestCaseWithFactory |
1038 | from canonical.launchpad.webapp.authentication import LaunchpadPrincipal |
1039 | from canonical.launchpad.webapp.interfaces import ( |
1040 | CookieAuthLoggedInEvent, ILaunchpadPrincipal, IPlacelessAuthUtility) |
1041 | -from canonical.launchpad.webapp.login import ( |
1042 | - logInPrincipal, logInPrincipalAndMaybeCreatePerson, logoutPerson) |
1043 | +from canonical.launchpad.webapp.login import logInPrincipal, logoutPerson |
1044 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
1045 | from canonical.testing import DatabaseFunctionalLayer |
1046 | |
1047 | @@ -137,23 +135,6 @@ |
1048 | self.failUnless(ILaunchpadPrincipal.providedBy(principal)) |
1049 | self.failUnless(principal.person is None) |
1050 | |
1051 | - def test_logInPrincipalAndMaybeCreatePerson(self): |
1052 | - # logInPrincipalAndMaybeCreatePerson() will log the given principal in |
1053 | - # and create a Person entry associated with it if one doesn't exist |
1054 | - # already. |
1055 | - logInPrincipalAndMaybeCreatePerson( |
1056 | - self.request, self.principal, 'foo@example.com') |
1057 | - |
1058 | - # This is so that the authenticate() call below uses cookie auth. |
1059 | - self.request.response.setCookie( |
1060 | - config.launchpad_session.cookie, 'xxx') |
1061 | - |
1062 | - principal = getUtility(IPlacelessAuthUtility).authenticate( |
1063 | - self.request) |
1064 | - self.failUnless(ILaunchpadPrincipal.providedBy(principal)) |
1065 | - person = IPerson(principal.account) |
1066 | - self.failUnless(IPerson.providedBy(person)) |
1067 | - |
1068 | |
1069 | def test_suite(): |
1070 | return unittest.TestLoader().loadTestsFromName(__name__) |
1071 | |
1072 | === modified file 'lib/canonical/launchpad/tests/test_token_creation.py' |
1073 | --- lib/canonical/launchpad/tests/test_token_creation.py 2009-06-25 05:30:52 +0000 |
1074 | +++ lib/canonical/launchpad/tests/test_token_creation.py 2010-04-05 17:13:29 +0000 |
1075 | @@ -36,9 +36,9 @@ |
1076 | |
1077 | # Now insert the token in the table so that the next time we call |
1078 | # create_unique_token_for_table() we get a different token. |
1079 | - login_token = LoginToken( |
1080 | + LoginToken( |
1081 | requester=None, token=token2, email='email@example.com', |
1082 | - tokentype=LoginTokenType.NEWACCOUNT, created=UTC_NOW) |
1083 | + tokentype=LoginTokenType.ACCOUNTMERGE, created=UTC_NOW) |
1084 | random.seed(0) |
1085 | token3 = create_unique_token_for_table(99, LoginToken.token) |
1086 | self.assertNotEquals(token1, token3) |
1087 | |
1088 | === modified file 'lib/canonical/launchpad/webapp/login.py' |
1089 | --- lib/canonical/launchpad/webapp/login.py 2010-03-30 14:57:02 +0000 |
1090 | +++ lib/canonical/launchpad/webapp/login.py 2010-04-05 17:13:29 +0000 |
1091 | @@ -38,8 +38,8 @@ |
1092 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy |
1093 | from canonical.launchpad.webapp.error import SystemErrorView |
1094 | from canonical.launchpad.webapp.interfaces import ( |
1095 | - CookieAuthLoggedInEvent, ILaunchpadApplication, ILaunchpadPrincipal, |
1096 | - IPlacelessAuthUtility, IPlacelessLoginSource, LoggedOutEvent) |
1097 | + CookieAuthLoggedInEvent, ILaunchpadApplication, IPlacelessAuthUtility, |
1098 | + IPlacelessLoginSource, LoggedOutEvent) |
1099 | from canonical.launchpad.webapp.metazcml import ILaunchpadPermission |
1100 | from canonical.launchpad.webapp.publisher import LaunchpadView |
1101 | from canonical.launchpad.webapp.url import urlappend |
1102 | @@ -378,24 +378,6 @@ |
1103 | notify(CookieAuthLoggedInEvent(request, email)) |
1104 | |
1105 | |
1106 | -def logInPrincipalAndMaybeCreatePerson(request, principal, email): |
1107 | - """Log the principal in, creating a Person if necessary. |
1108 | - |
1109 | - If the given principal has no associated person, we create a new |
1110 | - person, fetch a new principal and set it in the request. |
1111 | - |
1112 | - Password validation must be done in callsites. |
1113 | - """ |
1114 | - logInPrincipal(request, principal, email) |
1115 | - if ILaunchpadPrincipal.providedBy(principal) and principal.person is None: |
1116 | - person = principal.account.createPerson( |
1117 | - PersonCreationRationale.OWNER_CREATED_LAUNCHPAD) |
1118 | - new_principal = getUtility(IPlacelessLoginSource).getPrincipal( |
1119 | - principal.id) |
1120 | - assert ILaunchpadPrincipal.providedBy(new_principal) |
1121 | - request.setPrincipal(new_principal) |
1122 | - |
1123 | - |
1124 | def expireSessionCookie(request, client_id_manager=None, |
1125 | delta=timedelta(minutes=10)): |
1126 | if client_id_manager is None: |
1127 | |
1128 | === modified file 'lib/canonical/launchpad/webapp/publication.py' |
1129 | --- lib/canonical/launchpad/webapp/publication.py 2010-03-30 14:33:26 +0000 |
1130 | +++ lib/canonical/launchpad/webapp/publication.py 2010-04-05 17:13:29 +0000 |
1131 | @@ -317,7 +317,7 @@ |
1132 | if request.method != 'POST': |
1133 | return |
1134 | # XXX: jamesh 2007-11-23 bug=124421: |
1135 | - # Allow offsite posts to our OpenID endpoint. Ideally we'd |
1136 | + # Allow offsite posts to our TestOpenID endpoint. Ideally we'd |
1137 | # have a better way of marking this URL as allowing offsite |
1138 | # form posts. |
1139 | if request['PATH_INFO'] == '/+openid': |
1140 | |
1141 | === modified file 'lib/canonical/launchpad/zcml/logintoken.zcml' |
1142 | --- lib/canonical/launchpad/zcml/logintoken.zcml 2009-07-18 00:05:49 +0000 |
1143 | +++ lib/canonical/launchpad/zcml/logintoken.zcml 2010-04-05 17:13:29 +0000 |
1144 | @@ -36,22 +36,6 @@ |
1145 | /> |
1146 | |
1147 | <browser:page |
1148 | - name="+newaccount" |
1149 | - for="canonical.launchpad.interfaces.ILoginToken" |
1150 | - class="canonical.launchpad.browser.logintoken.NewUserAccountView" |
1151 | - permission="zope.Public" |
1152 | - template="../../launchpad/templates/logintoken-newaccount.pt" |
1153 | - /> |
1154 | - |
1155 | - <browser:page |
1156 | - for="canonical.launchpad.interfaces.ILoginToken" |
1157 | - name="+resetpassword" |
1158 | - permission="zope.Public" |
1159 | - class="canonical.launchpad.browser.logintoken.ResetPasswordView" |
1160 | - template="../templates/logintoken-resetpassword.pt" |
1161 | - /> |
1162 | - |
1163 | - <browser:page |
1164 | for="canonical.launchpad.interfaces.ILoginToken" |
1165 | name="+validateemail" |
1166 | permission="zope.Public" |
1167 | |
1168 | === modified file 'lib/lp/registry/browser/configure.zcml' |
1169 | --- lib/lp/registry/browser/configure.zcml 2010-03-24 22:06:30 +0000 |
1170 | +++ lib/lp/registry/browser/configure.zcml 2010-04-05 17:13:29 +0000 |
1171 | @@ -1192,12 +1192,6 @@ |
1172 | classes=" |
1173 | PersonSetNavigation"/> |
1174 | <browser:page |
1175 | - name="+newperson" |
1176 | - for="lp.registry.interfaces.person.IPersonSet" |
1177 | - class="lp.registry.browser.person.PersonAddView" |
1178 | - permission="launchpad.Admin" |
1179 | - template="../templates/people-newperson.pt"/> |
1180 | - <browser:page |
1181 | name="+newteam" |
1182 | for="lp.registry.interfaces.person.IPersonSet" |
1183 | class="lp.registry.browser.team.TeamAddView" |
1184 | |
1185 | === modified file 'lib/lp/registry/browser/person.py' |
1186 | --- lib/lp/registry/browser/person.py 2010-03-25 14:50:31 +0000 |
1187 | +++ lib/lp/registry/browser/person.py 2010-04-05 17:13:29 +0000 |
1188 | @@ -10,10 +10,8 @@ |
1189 | 'BeginTeamClaimView', |
1190 | 'BugSubscriberPackageBugsSearchListingView', |
1191 | 'EmailToPersonView', |
1192 | - 'PeopleSearchMenu' |
1193 | 'PeopleSearchView', |
1194 | 'PersonAccountAdministerView', |
1195 | - 'PersonAddView', |
1196 | 'PersonAdministerView', |
1197 | 'PersonAnswerContactForView', |
1198 | 'PersonAnswersMenu', |
1199 | @@ -163,9 +161,8 @@ |
1200 | from lp.registry.interfaces.mailinglistsubscription import ( |
1201 | MailingListAutoSubscribePolicy) |
1202 | from lp.registry.interfaces.person import ( |
1203 | - INewPerson, IPerson, IPersonClaim, IPersonSet, ITeam, ITeamReassignment, |
1204 | - PersonCreationRationale, PersonVisibility, TeamMembershipRenewalPolicy, |
1205 | - TeamSubscriptionPolicy) |
1206 | + IPerson, IPersonClaim, IPersonSet, ITeam, ITeamReassignment, |
1207 | + PersonVisibility, TeamMembershipRenewalPolicy, TeamSubscriptionPolicy) |
1208 | from lp.registry.interfaces.poll import IPollSet, IPollSubset |
1209 | from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyType |
1210 | from lp.registry.interfaces.teammembership import ( |
1211 | @@ -1375,33 +1372,6 @@ |
1212 | return BatchNavigator(results, self.request) |
1213 | |
1214 | |
1215 | -class PersonAddView(LaunchpadFormView): |
1216 | - """The page where users can create new Launchpad profiles.""" |
1217 | - |
1218 | - page_title = "Create a new Launchpad profile" |
1219 | - label = page_title |
1220 | - schema = INewPerson |
1221 | - |
1222 | - custom_widget('creation_comment', TextAreaWidget, height=5, width=60) |
1223 | - |
1224 | - @action(_("Create Profile"), name="create") |
1225 | - def create_action(self, action, data): |
1226 | - emailaddress = data['emailaddress'] |
1227 | - displayname = data['displayname'] |
1228 | - creation_comment = data['creation_comment'] |
1229 | - person, ignored = getUtility(IPersonSet).createPersonAndEmail( |
1230 | - emailaddress, PersonCreationRationale.USER_CREATED, |
1231 | - displayname=displayname, comment=creation_comment, |
1232 | - registrant=self.user) |
1233 | - self.next_url = canonical_url(person) |
1234 | - logintokenset = getUtility(ILoginTokenSet) |
1235 | - token = logintokenset.new( |
1236 | - requester=self.user, |
1237 | - requesteremail=self.user.preferredemail.email, |
1238 | - email=emailaddress, tokentype=LoginTokenType.NEWPROFILE) |
1239 | - token.sendProfileCreatedEmail(person, creation_comment) |
1240 | - |
1241 | - |
1242 | class DeactivateAccountSchema(Interface): |
1243 | comment = copy_field( |
1244 | IPerson['account_status_comment'], readonly=False, __name__='comment') |
1245 | |
1246 | === modified file 'lib/lp/registry/interfaces/person.py' |
1247 | --- lib/lp/registry/interfaces/person.py 2010-03-25 14:17:56 +0000 |
1248 | +++ lib/lp/registry/interfaces/person.py 2010-04-05 17:13:29 +0000 |
1249 | @@ -11,11 +11,8 @@ |
1250 | 'IAdminPeopleMergeSchema', |
1251 | 'IAdminTeamMergeSchema', |
1252 | 'IHasStanding', |
1253 | - 'INewPerson', |
1254 | - 'INewPersonForm', |
1255 | 'IObjectReassignment', |
1256 | 'IPerson', |
1257 | - 'IPersonChangePassword', |
1258 | 'IPersonClaim', |
1259 | 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py |
1260 | 'IPersonSet', |
1261 | @@ -72,8 +69,7 @@ |
1262 | from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
1263 | from canonical.launchpad.interfaces.launchpad import ( |
1264 | IHasIcon, IHasLogo, IHasMugshot, IPrivacy) |
1265 | -from canonical.launchpad.interfaces.validation import ( |
1266 | - validate_new_person_email, validate_new_team_email) |
1267 | +from canonical.launchpad.interfaces.validation import validate_new_team_email |
1268 | from canonical.launchpad.validators import LaunchpadValidationError |
1269 | from canonical.launchpad.validators.email import email_validator |
1270 | from canonical.launchpad.validators.name import name_validator |
1271 | @@ -435,37 +431,12 @@ |
1272 | super(PersonNameField, self)._validate(input) |
1273 | |
1274 | |
1275 | -# XXX: salgado, 2010/03/05, bug=532688: This is currently used by c-i-p, so it |
1276 | -# can't be removed yet. As soon as we stop using c-i-p, though, we'll be able |
1277 | -# to remove this. |
1278 | -class IPersonChangePassword(Interface): |
1279 | - """The schema used by Person +changepassword form.""" |
1280 | - |
1281 | - currentpassword = PasswordField( |
1282 | - title=_('Current password'), required=True, readonly=False) |
1283 | - |
1284 | - password = PasswordField( |
1285 | - title=_('New password'), required=True, readonly=False) |
1286 | - |
1287 | - |
1288 | class IPersonClaim(Interface): |
1289 | """The schema used by IPerson's +claim form.""" |
1290 | |
1291 | emailaddress = TextLine(title=_('Email address'), required=True) |
1292 | |
1293 | |
1294 | -class INewPerson(Interface): |
1295 | - """The schema used by IPersonSet's +newperson form.""" |
1296 | - |
1297 | - emailaddress = StrippedTextLine( |
1298 | - title=_('Email address'), required=True, |
1299 | - constraint=validate_new_person_email) |
1300 | - displayname = StrippedTextLine(title=_('Display name'), required=True) |
1301 | - creation_comment = Text( |
1302 | - title=_('Creation reason'), required=True, |
1303 | - description=_("The reason why you're creating this profile.")) |
1304 | - |
1305 | - |
1306 | # This has to be defined here to avoid circular import problems. |
1307 | class IHasStanding(Interface): |
1308 | """An object that can have personal standing.""" |
1309 | @@ -1544,16 +1515,6 @@ |
1310 | PersonChoice.schema = IPerson |
1311 | |
1312 | |
1313 | -class INewPersonForm(IPerson): |
1314 | - """Interface used to create new Launchpad accounts. |
1315 | - |
1316 | - The only change with `IPerson` is a customised Password field. |
1317 | - """ |
1318 | - |
1319 | - password = PasswordField( |
1320 | - title=_('Create password'), required=True, readonly=False) |
1321 | - |
1322 | - |
1323 | class ITeamPublic(Interface): |
1324 | """Public attributes of a Team.""" |
1325 | |
1326 | |
1327 | === removed file 'lib/lp/registry/stories/person/xx-new-profile.txt' |
1328 | --- lib/lp/registry/stories/person/xx-new-profile.txt 2009-11-22 15:43:16 +0000 |
1329 | +++ lib/lp/registry/stories/person/xx-new-profile.txt 1970-01-01 00:00:00 +0000 |
1330 | @@ -1,110 +0,0 @@ |
1331 | - |
1332 | -We allow Launchpad admins to create random profiles that can latter be used |
1333 | -throughout Launchpad. These profiles are not considered validated and because |
1334 | -of that we won't send any email notification to them, except for an initial |
1335 | -email explaining that a given user has created a Launchpad profile for that |
1336 | -email address' owner. |
1337 | - |
1338 | - >>> admin_browser.open('http://launchpad.dev/people/+newperson') |
1339 | - >>> admin_browser.url |
1340 | - 'http://launchpad.dev/people/+newperson' |
1341 | - |
1342 | -All fields (email address, displayname and creation reason) in that form |
1343 | -are required. |
1344 | - |
1345 | - >>> admin_browser.getControl('Create Profile').click() |
1346 | - >>> print admin_browser.contents |
1347 | - <... |
1348 | - ...class="error message">There are 3 errors... |
1349 | - |
1350 | -Trying to create a profile with an invalid or already used email address |
1351 | -is not allowed. |
1352 | - |
1353 | - >>> admin_browser.getControl('Email address').value = 'foo@' |
1354 | - >>> admin_browser.getControl('Display name').value = 'Foo' |
1355 | - >>> admin_browser.getControl('Creation reason').value = ( |
1356 | - ... 'I was just playing around and found this nice form, so I ' |
1357 | - ... 'decided to create a new profile to test.') |
1358 | - >>> admin_browser.getControl('Create Profile').click() |
1359 | - >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
1360 | - ... print tag.renderContents() |
1361 | - There is 1 error. |
1362 | - foo@ isn't a valid email address. |
1363 | - |
1364 | - >>> admin_browser.getControl('Email address').value = 'test@canonical.com' |
1365 | - >>> admin_browser.getControl('Create Profile').click() |
1366 | - >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
1367 | - ... print tag.renderContents() |
1368 | - There is 1 error. |
1369 | - The profile you're trying to create already exists:... |
1370 | - |
1371 | -When all fields are filled correctly, the new profile is created. |
1372 | - |
1373 | - >>> admin_browser.getControl('Email address').value = 'test2@canonical.com' |
1374 | - >>> admin_browser.getControl('Create Profile').click() |
1375 | - >>> admin_browser.url |
1376 | - 'http://launchpad.dev/~test2' |
1377 | - |
1378 | - >>> print admin_browser.contents |
1379 | - <... |
1380 | - ...This page was created... |
1381 | - ...by ... |
1382 | - ...and the reason given by that user for its creation is:... |
1383 | - ...I was just playing around and found this nice form,... |
1384 | - |
1385 | -The creation of this new profile will trigger an email notification to the |
1386 | -profile's email address. This email will contain the name of the person who |
1387 | -created the profile as well as the reason left by that person. It'll also |
1388 | -contain a link to a page where it's possible to finish the registration |
1389 | -process in order to validate that profile. |
1390 | - |
1391 | - >>> import email |
1392 | - >>> from lp.services.mail import stub |
1393 | - >>> from canonical.launchpad.ftests.logintoken import ( |
1394 | - ... get_token_url_from_email) |
1395 | - >>> len(stub.test_emails) |
1396 | - 1 |
1397 | - >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop() |
1398 | - >>> token_url = get_token_url_from_email(raw_msg) |
1399 | - |
1400 | - >>> print email.message_from_string(raw_msg).get_payload() |
1401 | - Hello! |
1402 | - ... has created a new Launchpad profile [1] for |
1403 | - you, and left the following comment as the reason for its creation: |
1404 | - ... |
1405 | - If you wish to use this profile, please follow the instructions here: |
1406 | - ... |
1407 | - |
1408 | - >>> browser.open(token_url) |
1409 | - >>> browser.url |
1410 | - 'http://launchpad.dev/token/.../+newaccount' |
1411 | - |
1412 | -We don't prefill the new account's display name because the one we have was |
1413 | -not entered by the users themselves. |
1414 | - |
1415 | - >>> browser.getControl('Display Name').value |
1416 | - '' |
1417 | - |
1418 | -We also explain the users what to write in each form: |
1419 | - |
1420 | - >>> for tag in find_tags_by_class(browser.contents, 'formHelp'): |
1421 | - ... print extract_text(tag) |
1422 | - Your name as you would like it displayed throughout Launchpad... |
1423 | - |
1424 | -Now we fill the form and save it to create the new account. |
1425 | - |
1426 | - >>> browser.getControl('Display Name').value = 'My name' |
1427 | - >>> browser.getControl('Create password').value = 'aaa' |
1428 | - >>> browser.getControl(name='field.password_dupe').value = 'aaa' |
1429 | - >>> browser.getControl('Continue').click() |
1430 | - |
1431 | - >>> browser.url |
1432 | - 'http://launchpad.dev/~test2' |
1433 | - |
1434 | - |
1435 | -Non-launchpad admins can't acces the page to ceate a new profile. |
1436 | - |
1437 | - >>> user_browser.open('http://launchpad.dev/people/+newperson') |
1438 | - Traceback (most recent call last): |
1439 | - ... |
1440 | - Unauthorized: ... |
1441 | |
1442 | === removed file 'lib/lp/registry/templates/people-newperson.pt' |
1443 | --- lib/lp/registry/templates/people-newperson.pt 2009-08-31 17:46:33 +0000 |
1444 | +++ lib/lp/registry/templates/people-newperson.pt 1970-01-01 00:00:00 +0000 |
1445 | @@ -1,29 +0,0 @@ |
1446 | -<html |
1447 | - xmlns="http://www.w3.org/1999/xhtml" |
1448 | - xmlns:tal="http://xml.zope.org/namespaces/tal" |
1449 | - xmlns:metal="http://xml.zope.org/namespaces/metal" |
1450 | - xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
1451 | - metal:use-macro="view/macro:page/main_only" |
1452 | - i18n:domain="launchpad" |
1453 | - > |
1454 | - <body> |
1455 | - <div metal:fill-slot="main"> |
1456 | - <div metal:use-macro="context/@@launchpad_form/form"> |
1457 | - <div metal:fill-slot="extra_info" class="application-summary"> |
1458 | - <p> |
1459 | - Launchpad will send a message to the email address you provide |
1460 | - below. This message explains that you created a Launchpad profile |
1461 | - for the email address's owner, and it will include your name and |
1462 | - email address so that the user can contact you if they want to. |
1463 | - </p> |
1464 | - <p> |
1465 | - It will not be possible to log in with this new profile, and |
1466 | - Launchpad will not send any other messages to it before its email |
1467 | - address is confirmed. The registration process must be finished by |
1468 | - the person represented by the new profile. |
1469 | - </p> |
1470 | - </div> |
1471 | - </div> |
1472 | - </div> |
1473 | - </body> |
1474 | -</html> |
Rubber-stamp. Haven't actually examined the diff.