Merge lp:~sinzui/launchpad/spam-eggs-bug-495250 into lp:launchpad
- spam-eggs-bug-495250
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~sinzui/launchpad/spam-eggs-bug-495250 |
Merge into: | lp:launchpad |
Diff against target: |
358 lines (+183/-15) 9 files modified
lib/lp/registry/browser/person.py (+42/-3) lib/lp/registry/browser/tests/karmaaction-views.txt (+1/-1) lib/lp/registry/browser/tests/person-views.txt (+121/-4) lib/lp/registry/browser/tests/poll-views.txt (+1/-1) lib/lp/registry/stories/person/xx-admin-person-review.txt (+4/-0) lib/lp/registry/stories/person/xx-login.txt (+2/-0) lib/lp/registry/stories/person/xx-reg-with-existing-email.txt (+5/-0) lib/lp/registry/templates/person-index.pt (+3/-3) lib/lp/testing/views.py (+4/-3) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/spam-eggs-bug-495250 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Данило Шеган (community) | release-critical | Abstain | |
Henning Eggers (community) | Approve | ||
Review via email: mp+16174@code.launchpad.net |
Commit message
Description of the change
Curtis Hovey (sinzui) wrote : | # |
Henning Eggers (henninge) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Am 15.12.2009 00:00, Curtis Hovey schrieb:
> Curtis Hovey has proposed merging lp:~sinzui/launchpad/spam-eggs-bug-495250 into lp:launchpad/devel.
> This is my branch to discourage spammers.
Yeah! Cool stuff! Thanks for the quick and smart fix.
review approve
All I found was a little glitch in a comment...
> === modified file 'lib/lp/
Straightforward implementation, clear comments, good code. ;)
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -4,7 +4,127 @@
> person's information.
>
>
> -== Email address disclosure ==
> +Probationary and invalid users
> +------
> +
> +The person +index view provides the is_probationary
> +page features can be disabled because the user may abuse them. Active
> +users with karma are not on probation. The user's homepage_content formatted
> +as HTML
Add an "is" and a ".", please. ;)
[...]
> === modified file 'lib/lp/
As expected.
> === modified file 'lib/lp/
Thanks for the drive-by fix.
Cheers,
Henning
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAks
x7IAn2Pb+
=qBEM
-----END PGP SIGNATURE-----
Данило Шеган (danilo) wrote : | # |
As agreed on IRC, let's have this QAd a bit first (perhaps by cowboying, or landing right after the rollout as an RC=danilo) and then include it in the re-roll or CP it if there's no re-roll.
Preview Diff
1 | === modified file 'lib/lp/registry/browser/person.py' | |||
2 | --- lib/lp/registry/browser/person.py 2009-12-08 10:20:37 +0000 | |||
3 | +++ lib/lp/registry/browser/person.py 2009-12-15 20:35:30 +0000 | |||
4 | @@ -226,7 +226,8 @@ | |||
5 | 226 | logoutPerson, allowUnauthenticatedSession) | 226 | logoutPerson, allowUnauthenticatedSession) |
6 | 227 | from canonical.launchpad.webapp.menu import get_current_view | 227 | from canonical.launchpad.webapp.menu import get_current_view |
7 | 228 | from canonical.launchpad.webapp.publisher import LaunchpadView | 228 | from canonical.launchpad.webapp.publisher import LaunchpadView |
9 | 229 | from canonical.launchpad.webapp.tales import DateTimeFormatterAPI | 229 | from canonical.launchpad.webapp.tales import ( |
10 | 230 | DateTimeFormatterAPI, FormattersAPI) | ||
11 | 230 | from lazr.uri import URI, InvalidURIError | 231 | from lazr.uri import URI, InvalidURIError |
12 | 231 | 232 | ||
13 | 232 | from canonical.launchpad import _ | 233 | from canonical.launchpad import _ |
14 | @@ -2460,7 +2461,7 @@ | |||
15 | 2460 | 2461 | ||
16 | 2461 | @property | 2462 | @property |
17 | 2462 | def next_url(self): | 2463 | def next_url(self): |
19 | 2463 | """Redirect back to the +languages page if request originated there.""" | 2464 | """Redirect to the +languages page if request originated there.""" |
20 | 2464 | redirection_url = self.request.get('redirection_url') | 2465 | redirection_url = self.request.get('redirection_url') |
21 | 2465 | if redirection_url: | 2466 | if redirection_url: |
22 | 2466 | return redirection_url | 2467 | return redirection_url |
23 | @@ -2468,7 +2469,7 @@ | |||
24 | 2468 | 2469 | ||
25 | 2469 | @property | 2470 | @property |
26 | 2470 | def cancel_url(self): | 2471 | def cancel_url(self): |
28 | 2471 | """Redirect back to the +languages page if request originated there.""" | 2472 | """Redirect to the +languages page if request originated there.""" |
29 | 2472 | redirection_url = self.getRedirectionURL() | 2473 | redirection_url = self.getRedirectionURL() |
30 | 2473 | if redirection_url: | 2474 | if redirection_url: |
31 | 2474 | return redirection_url | 2475 | return redirection_url |
32 | @@ -2663,6 +2664,38 @@ | |||
33 | 2663 | check_permission('launchpad.Edit', self.context)) | 2664 | check_permission('launchpad.Edit', self.context)) |
34 | 2664 | 2665 | ||
35 | 2665 | @cachedproperty | 2666 | @cachedproperty |
36 | 2667 | def is_probationary_or_invalid_user(self): | ||
37 | 2668 | """True when the user is not active or does not have karma. | ||
38 | 2669 | |||
39 | 2670 | Some content should not be rendered when the context is not a an | ||
40 | 2671 | established user. For example, probationary and invalid user pages | ||
41 | 2672 | must not be indexed by search engines and their narrative linkified. | ||
42 | 2673 | """ | ||
43 | 2674 | user = self.context | ||
44 | 2675 | if user.isTeam(): | ||
45 | 2676 | # Teams are always valid and do not have probationary rules. | ||
46 | 2677 | return False | ||
47 | 2678 | else: | ||
48 | 2679 | return user.karma == 0 or not user.is_valid_person | ||
49 | 2680 | |||
50 | 2681 | @cachedproperty | ||
51 | 2682 | def homepage_content(self): | ||
52 | 2683 | """The user's HTML formatted homepage content. | ||
53 | 2684 | |||
54 | 2685 | The markup is simply escaped for probationary or invalid users. | ||
55 | 2686 | The homepage content is reformatted as HTML and linkified if the user | ||
56 | 2687 | is active. | ||
57 | 2688 | """ | ||
58 | 2689 | content = self.context.homepage_content | ||
59 | 2690 | if content is None: | ||
60 | 2691 | return None | ||
61 | 2692 | elif self.is_probationary_or_invalid_user: | ||
62 | 2693 | return cgi.escape(content) | ||
63 | 2694 | else: | ||
64 | 2695 | formatter = FormattersAPI | ||
65 | 2696 | return formatter(content).text_to_html() | ||
66 | 2697 | |||
67 | 2698 | @cachedproperty | ||
68 | 2666 | def recently_approved_members(self): | 2699 | def recently_approved_members(self): |
69 | 2667 | members = self.context.getMembersByStatus( | 2700 | members = self.context.getMembersByStatus( |
70 | 2668 | TeamMembershipStatus.APPROVED, | 2701 | TeamMembershipStatus.APPROVED, |
71 | @@ -3137,6 +3170,12 @@ | |||
72 | 3137 | if self.request.method == "POST": | 3170 | if self.request.method == "POST": |
73 | 3138 | self.processForm() | 3171 | self.processForm() |
74 | 3139 | 3172 | ||
75 | 3173 | def render(self): | ||
76 | 3174 | """See `LaunchpadView`.""" | ||
77 | 3175 | if self.context.account_status == AccountStatus.SUSPENDED: | ||
78 | 3176 | self.request.response.setStatus(410) | ||
79 | 3177 | return super(PersonIndexView, self).render() | ||
80 | 3178 | |||
81 | 3140 | @property | 3179 | @property |
82 | 3141 | def page_title(self): | 3180 | def page_title(self): |
83 | 3142 | context = self.context | 3181 | context = self.context |
84 | 3143 | 3182 | ||
85 | === modified file 'lib/lp/registry/browser/tests/karmaaction-views.txt' | |||
86 | --- lib/lp/registry/browser/tests/karmaaction-views.txt 2009-08-17 16:18:51 +0000 | |||
87 | +++ lib/lp/registry/browser/tests/karmaaction-views.txt 2009-12-15 20:35:30 +0000 | |||
88 | @@ -17,7 +17,7 @@ | |||
89 | 17 | 17 | ||
90 | 18 | >>> user = getUtility(ILaunchBag).user | 18 | >>> user = getUtility(ILaunchBag).user |
91 | 19 | >>> view = create_view(karmaactionset, '+index', principal=user, | 19 | >>> view = create_view(karmaactionset, '+index', principal=user, |
93 | 20 | ... PATH_INFO='/karmaaction') | 20 | ... path_info='/karmaaction') |
94 | 21 | >>> print extract_text(find_tag_by_id(view(), 'karmas')) | 21 | >>> print extract_text(find_tag_by_id(view(), 'karmas')) |
95 | 22 | Category Action Points | 22 | Category Action Points |
96 | 23 | bugs Bug Accepted 5 | 23 | bugs Bug Accepted 5 |
97 | 24 | 24 | ||
98 | === modified file 'lib/lp/registry/browser/tests/person-views.txt' | |||
99 | --- lib/lp/registry/browser/tests/person-views.txt 2009-11-20 18:44:57 +0000 | |||
100 | +++ lib/lp/registry/browser/tests/person-views.txt 2009-12-15 20:35:30 +0000 | |||
101 | @@ -4,7 +4,127 @@ | |||
102 | 4 | person's information. | 4 | person's information. |
103 | 5 | 5 | ||
104 | 6 | 6 | ||
106 | 7 | == Email address disclosure == | 7 | Probationary and invalid users |
107 | 8 | ------------------------------ | ||
108 | 9 | |||
109 | 10 | The person +index view provides the is_probationary_or_invalid_user so that | ||
110 | 11 | page features can be disabled because the user may abuse them. Active | ||
111 | 12 | users with karma are not on probation; the user's homepage_content is | ||
112 | 13 | formatted as HTML. | ||
113 | 14 | |||
114 | 15 | >>> from lp.registry.interfaces.person import IPersonSet | ||
115 | 16 | |||
116 | 17 | >>> homepage_content = "line one <script>\n\nhttp://aa.aa/" | ||
117 | 18 | >>> person_set = getUtility(IPersonSet) | ||
118 | 19 | >>> active_user = person_set.getByName('name12') | ||
119 | 20 | >>> login_person(active_user) | ||
120 | 21 | >>> active_user.homepage_content = homepage_content | ||
121 | 22 | >>> login(ANONYMOUS) | ||
122 | 23 | >>> view = create_initialized_view(active_user, '+index') | ||
123 | 24 | >>> view.is_probationary_or_invalid_user | ||
124 | 25 | False | ||
125 | 26 | |||
126 | 27 | >>> print view.homepage_content | ||
127 | 28 | <p>line one <script></p> | ||
128 | 29 | <BLANKLINE> | ||
129 | 30 | <p><a rel="nofollow" href="http://aa.aa/">http://<wbr></wbr>aa.aa/</a></p> | ||
130 | 31 | |||
131 | 32 | Teams are always valid and do not have probation rules; the homepage content | ||
132 | 33 | is formatted HTML. | ||
133 | 34 | |||
134 | 35 | >>> team = factory.makeTeam() | ||
135 | 36 | >>> login_person(team.teamowner) | ||
136 | 37 | >>> team.homepage_content = homepage_content | ||
137 | 38 | >>> login(ANONYMOUS) | ||
138 | 39 | >>> view = create_initialized_view(team, '+index') | ||
139 | 40 | >>> view.is_probationary_or_invalid_user | ||
140 | 41 | False | ||
141 | 42 | |||
142 | 43 | >>> print view.homepage_content | ||
143 | 44 | <p>line one <script></p> | ||
144 | 45 | <BLANKLINE> | ||
145 | 46 | <p><a rel="nofollow" href="http://aa.aa/">http://<wbr></wbr>aa.aa/</a></p> | ||
146 | 47 | |||
147 | 48 | New users (those without karma) are on probation; the homepage content is | ||
148 | 49 | escaped HTML. | ||
149 | 50 | |||
150 | 51 | >>> new_user = factory.makePerson() | ||
151 | 52 | >>> new_user.homepage_content = homepage_content | ||
152 | 53 | >>> new_user.karma | ||
153 | 54 | 0 | ||
154 | 55 | |||
155 | 56 | >>> view = create_initialized_view(new_user, '+index') | ||
156 | 57 | >>> view.is_probationary_or_invalid_user | ||
157 | 58 | True | ||
158 | 59 | |||
159 | 60 | >>> print view.homepage_content | ||
160 | 61 | line one <script> | ||
161 | 62 | <BLANKLINE> | ||
162 | 63 | http://aa.aa/ | ||
163 | 64 | |||
164 | 65 | Inactive and suspended users are invalid; the homepage content is escaped | ||
165 | 66 | HTML. | ||
166 | 67 | |||
167 | 68 | >>> from canonical.launchpad.interfaces.account import AccountStatus | ||
168 | 69 | >>> from canonical.launchpad.interfaces import IMasterObject | ||
169 | 70 | |||
170 | 71 | # Only admins can change an account. | ||
171 | 72 | >>> admin_user = person_set.getByName('name16') | ||
172 | 73 | >>> login_person(admin_user) | ||
173 | 74 | >>> invalid_user = factory.makePerson(name="ugh") | ||
174 | 75 | >>> invalid_user.homepage_content = homepage_content | ||
175 | 76 | >>> IMasterObject(invalid_user.account).status = AccountStatus.NOACCOUNT | ||
176 | 77 | >>> view = create_initialized_view(invalid_user, '+index') | ||
177 | 78 | >>> view.is_probationary_or_invalid_user | ||
178 | 79 | True | ||
179 | 80 | |||
180 | 81 | >>> print view.homepage_content | ||
181 | 82 | line one <script> | ||
182 | 83 | <BLANKLINE> | ||
183 | 84 | http://aa.aa/ | ||
184 | 85 | |||
185 | 86 | >>> login(ANONYMOUS) | ||
186 | 87 | |||
187 | 88 | If the user has no homepage content, the view's value is None. | ||
188 | 89 | |||
189 | 90 | >>> new_user.homepage_content = None | ||
190 | 91 | >>> view = create_initialized_view(new_user, '+index') | ||
191 | 92 | >>> print view.homepage_content | ||
192 | 93 | None | ||
193 | 94 | |||
194 | 95 | |||
195 | 96 | Account status and profile page status codes | ||
196 | 97 | -------------------------------------------- | ||
197 | 98 | |||
198 | 99 | The PersonIndexView sets the HTTP status code is 410 when the profile is for | ||
199 | 100 | a suspended account. | ||
200 | 101 | |||
201 | 102 | # Only admins can change an account. | ||
202 | 103 | >>> login_person(active_user) | ||
203 | 104 | >>> IMasterObject(invalid_user.account).status = AccountStatus.SUSPENDED | ||
204 | 105 | >>> login(ANONYMOUS) | ||
205 | 106 | >>> view = create_initialized_view( | ||
206 | 107 | ... invalid_user, '+index', principal=ANONYMOUS, | ||
207 | 108 | ... server_url='http://launchpad.dev', | ||
208 | 109 | ... path_info="/~%s" % invalid_user.name) | ||
209 | 110 | |||
210 | 111 | >>> content = view() | ||
211 | 112 | >>> view.request.response.getStatus() | ||
212 | 113 | 410 | ||
213 | 114 | |||
214 | 115 | Otherwise it does nothing (which in Zope's case the response is 599). | ||
215 | 116 | |||
216 | 117 | >>> view = create_initialized_view( | ||
217 | 118 | ... active_user, '+index', principal=ANONYMOUS, | ||
218 | 119 | ... server_url='http://launchpad.dev', | ||
219 | 120 | ... path_info="/~%s" % active_user.name) | ||
220 | 121 | >>> content = view() | ||
221 | 122 | >>> view.request.response.getStatus() | ||
222 | 123 | 599 | ||
223 | 124 | |||
224 | 125 | |||
225 | 126 | Email address disclosure | ||
226 | 127 | ------------------------ | ||
227 | 8 | 128 | ||
228 | 9 | PersonView is the base for many views for Person objects, including the | 129 | PersonView is the base for many views for Person objects, including the |
229 | 10 | default view. It provides several properties to help display email | 130 | default view. It provides several properties to help display email |
230 | @@ -19,9 +139,6 @@ | |||
231 | 19 | Mark has a registered email address, and he has chosen to disclose it to | 139 | Mark has a registered email address, and he has chosen to disclose it to |
232 | 20 | the world. | 140 | the world. |
233 | 21 | 141 | ||
234 | 22 | >>> from lp.registry.interfaces.person import IPersonSet | ||
235 | 23 | |||
236 | 24 | >>> person_set = getUtility(IPersonSet) | ||
237 | 25 | >>> mark = person_set.getByEmail('mark@example.com') | 142 | >>> mark = person_set.getByEmail('mark@example.com') |
238 | 26 | >>> mark.preferredemail.email | 143 | >>> mark.preferredemail.email |
239 | 27 | u'mark@example.com' | 144 | u'mark@example.com' |
240 | 28 | 145 | ||
241 | === modified file 'lib/lp/registry/browser/tests/poll-views.txt' | |||
242 | --- lib/lp/registry/browser/tests/poll-views.txt 2009-11-20 14:06:04 +0000 | |||
243 | +++ lib/lp/registry/browser/tests/poll-views.txt 2009-12-15 20:35:30 +0000 | |||
244 | @@ -16,7 +16,7 @@ | |||
245 | 16 | ... server_url = 'http://launchpad.dev' | 16 | ... server_url = 'http://launchpad.dev' |
246 | 17 | ... view = create_view( | 17 | ... view = create_view( |
247 | 18 | ... team, name=name, principal=principal, | 18 | ... team, name=name, principal=principal, |
249 | 19 | ... server_url=server_url, PATH_INFO=path_info) | 19 | ... server_url=server_url, path_info=path_info) |
250 | 20 | ... view.initialize() | 20 | ... view.initialize() |
251 | 21 | ... return view | 21 | ... return view |
252 | 22 | 22 | ||
253 | 23 | 23 | ||
254 | === modified file 'lib/lp/registry/stories/person/xx-admin-person-review.txt' | |||
255 | --- lib/lp/registry/stories/person/xx-admin-person-review.txt 2009-09-18 15:24:30 +0000 | |||
256 | +++ lib/lp/registry/stories/person/xx-admin-person-review.txt 2009-12-15 20:35:30 +0000 | |||
257 | @@ -58,6 +58,10 @@ | |||
258 | 58 | >>> admin_browser.getControl( | 58 | >>> admin_browser.getControl( |
259 | 59 | ... name='field.status_comment').value = 'Bad boy.' | 59 | ... name='field.status_comment').value = 'Bad boy.' |
260 | 60 | >>> admin_browser.getControl('Change').click() | 60 | >>> admin_browser.getControl('Change').click() |
261 | 61 | Traceback (most recent call last): | ||
262 | 62 | ... | ||
263 | 63 | HTTPError: HTTP Error 410: Gone | ||
264 | 64 | |||
265 | 61 | >>> print admin_browser.title | 65 | >>> print admin_browser.title |
266 | 62 | The one and only Salgado does not use Launchpad | 66 | The one and only Salgado does not use Launchpad |
267 | 63 | 67 | ||
268 | 64 | 68 | ||
269 | === modified file 'lib/lp/registry/stories/person/xx-login.txt' | |||
270 | --- lib/lp/registry/stories/person/xx-login.txt 2009-09-11 20:12:19 +0000 | |||
271 | +++ lib/lp/registry/stories/person/xx-login.txt 2009-12-15 20:35:30 +0000 | |||
272 | @@ -57,6 +57,8 @@ | |||
273 | 57 | when he visits his profile page. | 57 | when he visits his profile page. |
274 | 58 | 58 | ||
275 | 59 | >>> browser = setupBrowser() | 59 | >>> browser = setupBrowser() |
276 | 60 | >>> browser.handleErrors = True | ||
277 | 61 | >>> browser.raiseHttpErrors = False | ||
278 | 60 | >>> browser.open('http://launchpad.dev/~bad-user') | 62 | >>> browser.open('http://launchpad.dev/~bad-user') |
279 | 61 | >>> print browser.title | 63 | >>> print browser.title |
280 | 62 | Bad-user does not use Launchpad | 64 | Bad-user does not use Launchpad |
281 | 63 | 65 | ||
282 | === modified file 'lib/lp/registry/stories/person/xx-reg-with-existing-email.txt' | |||
283 | --- lib/lp/registry/stories/person/xx-reg-with-existing-email.txt 2009-10-16 16:13:00 +0000 | |||
284 | +++ lib/lp/registry/stories/person/xx-reg-with-existing-email.txt 2009-12-15 20:35:30 +0000 | |||
285 | @@ -134,6 +134,8 @@ | |||
286 | 134 | when he visits his profile page. | 134 | when he visits his profile page. |
287 | 135 | 135 | ||
288 | 136 | >>> browser = setupBrowser() | 136 | >>> browser = setupBrowser() |
289 | 137 | >>> browser.handleErrors = True | ||
290 | 138 | >>> browser.raiseHttpErrors = False | ||
291 | 137 | >>> browser.open('http://launchpad.dev/~bad-user') | 139 | >>> browser.open('http://launchpad.dev/~bad-user') |
292 | 138 | >>> browser.title | 140 | >>> browser.title |
293 | 139 | 'Bad-user does not use Launchpad' | 141 | 'Bad-user does not use Launchpad' |
294 | @@ -177,6 +179,9 @@ | |||
295 | 177 | >>> browser.getControl(name='field.password').value = 'test' | 179 | >>> browser.getControl(name='field.password').value = 'test' |
296 | 178 | >>> browser.getControl(name='field.password_dupe').value = 'test' | 180 | >>> browser.getControl(name='field.password_dupe').value = 'test' |
297 | 179 | >>> browser.getControl('Continue').click() | 181 | >>> browser.getControl('Continue').click() |
298 | 182 | Traceback (most recent call last): | ||
299 | 183 | ... | ||
300 | 184 | HTTPError: HTTP Error 410: Gone | ||
301 | 180 | 185 | ||
302 | 181 | >>> for tag in find_tags_by_class( | 186 | >>> for tag in find_tags_by_class( |
303 | 182 | ... browser.contents, 'warning'): | 187 | ... browser.contents, 'warning'): |
304 | 183 | 188 | ||
305 | === modified file 'lib/lp/registry/templates/person-index.pt' | |||
306 | --- lib/lp/registry/templates/person-index.pt 2009-11-20 16:37:51 +0000 | |||
307 | +++ lib/lp/registry/templates/person-index.pt 2009-12-15 20:35:30 +0000 | |||
308 | @@ -16,7 +16,7 @@ | |||
309 | 16 | title="FOAF" href="+rdf" | 16 | title="FOAF" href="+rdf" |
310 | 17 | /> | 17 | /> |
311 | 18 | </tal:valid_person_or_team> | 18 | </tal:valid_person_or_team> |
313 | 19 | <meta tal:condition="not: context/is_valid_person_or_team" | 19 | <meta tal:condition="view/is_probationary_or_invalid_user" |
314 | 20 | name="robots" content="noindex,nofollow" /> | 20 | name="robots" content="noindex,nofollow" /> |
315 | 21 | <tal:openid_delegation condition="view/is_delegated_identity"> | 21 | <tal:openid_delegation condition="view/is_delegated_identity"> |
316 | 22 | <link rel="openid.server" | 22 | <link rel="openid.server" |
317 | @@ -84,8 +84,8 @@ | |||
318 | 84 | 84 | ||
319 | 85 | <div | 85 | <div |
320 | 86 | class="description" | 86 | class="description" |
323 | 87 | tal:condition="context/homepage_content" | 87 | tal:condition="view/homepage_content" |
324 | 88 | tal:content="structure context/homepage_content/fmt:text-to-html" | 88 | tal:content="structure view/homepage_content" |
325 | 89 | /> | 89 | /> |
326 | 90 | <ul class="horizontal"> | 90 | <ul class="horizontal"> |
327 | 91 | <tal:comment condition="nothing"> | 91 | <tal:comment condition="nothing"> |
328 | 92 | 92 | ||
329 | === modified file 'lib/lp/testing/views.py' | |||
330 | --- lib/lp/testing/views.py 2009-10-05 20:31:28 +0000 | |||
331 | +++ lib/lp/testing/views.py 2009-12-15 20:35:30 +0000 | |||
332 | @@ -43,7 +43,7 @@ | |||
333 | 43 | if request is None: | 43 | if request is None: |
334 | 44 | request = LaunchpadTestRequest( | 44 | request = LaunchpadTestRequest( |
335 | 45 | form=form, SERVER_URL=server_url, QUERY_STRING=query_string, | 45 | form=form, SERVER_URL=server_url, QUERY_STRING=query_string, |
337 | 46 | HTTP_COOKIE=cookie, method=method, **kwargs) | 46 | HTTP_COOKIE=cookie, method=method, PATH_INFO=path_info, **kwargs) |
338 | 47 | if principal is not None: | 47 | if principal is not None: |
339 | 48 | request.setPrincipal(principal) | 48 | request.setPrincipal(principal) |
340 | 49 | else: | 49 | else: |
341 | @@ -59,7 +59,8 @@ | |||
342 | 59 | 59 | ||
343 | 60 | def create_initialized_view(context, name, form=None, layer=None, | 60 | def create_initialized_view(context, name, form=None, layer=None, |
344 | 61 | server_url=None, method=None, principal=None, | 61 | server_url=None, method=None, principal=None, |
346 | 62 | query_string=None, cookie=None, request=None): | 62 | query_string=None, cookie=None, request=None, |
347 | 63 | path_info='/'): | ||
348 | 63 | """Return a view that has already been initialized.""" | 64 | """Return a view that has already been initialized.""" |
349 | 64 | if method is None: | 65 | if method is None: |
350 | 65 | if form is None: | 66 | if form is None: |
351 | @@ -68,6 +69,6 @@ | |||
352 | 68 | method = 'POST' | 69 | method = 'POST' |
353 | 69 | view = create_view( | 70 | view = create_view( |
354 | 70 | context, name, form, layer, server_url, method, principal, | 71 | context, name, form, layer, server_url, method, principal, |
356 | 71 | query_string, cookie, request) | 72 | query_string, cookie, request, path_info) |
357 | 72 | view.initialize() | 73 | view.initialize() |
358 | 73 | return view | 74 | return view |
This is my branch to discourage spammers.
lp:~sinzui/launchpad/spam-eggs-bug-495250 /bugs.launchpad .net/bugs/ 495250 implementation: flacoste, gary
Diff size: 282
Launchpad bug: https:/
Test command: ./bin/test -vvt "person-views"
Pre-
Target release: 3.1.12
= Discourage spammers =
Do not link the homepage_content of probationary users. Do not let search
engine index probationary user' pages.
== Rules ==
* All users with karma == 0 are probationary.
* Set the robots directive to noindex,nofollow
* Do not linkify the homepage_content.
* Set the HTTP status code to 410 on profile pages for suspended accounts.
== QA ==
* Locate a suspended user (search answers)
* Verify the request response headers are set the status code to 410.
* Locate an account with zero karma.
* Verify that the profile page meta robots tag is index,nofollow.
* Locate an account with zero karma and a description (probably a spammer)
* Verify the homepage text is not formated as HTML.
== Lint ==
Linting changed files: registry/ browser/ person. py registry/ browser/ tests/person- views.txt registry/ templates/ person- index.pt
lib/lp/
lib/lp/
lib/lp/
== Test ==
* lib/lp/ registry/ browser/ tests/person- views.txt _or_invalid_ user and
homepage_ content properties on the PersonView.
* Added tests to verify is_probationary
* Add a test to verify that the HTTP status code is set to 410
for suspended accounts.
== Implementation ==
* lib/lp/ registry/ browser/ person. py _or_invalid_ user and homepage_content properties registry/ templates/ person- index.pt testing/ views.py
* Added is_probationary
to control how to display the profile page for probationary users.
* Redefined the render() method to set the status code to 410 when the
account is suspended.
* lib/lp/
* Updated the template to use the view instead of the context object
to show content and set the meta data.
* lib/lp/
* Discovered that path_info was not used. I updated the helper to
use it.