Merge lp:~thumper/wikkid/script-name into lp:wikkid
- script-name
- Merge into trunk
Proposed by
Tim Penhey
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Tim Penhey | ||||
Approved revision: | 85 | ||||
Merged at revision: | 70 | ||||
Proposed branch: | lp:~thumper/wikkid/script-name | ||||
Merge into: | lp:wikkid | ||||
Diff against target: |
622 lines (+193/-80) 15 files modified
bin/wikkid-serve (+5/-1) setup.py (+2/-1) wikkid/app.py (+35/-19) wikkid/context.py (+13/-4) wikkid/skin/default/base.html (+4/-4) wikkid/tests/factory.py (+2/-2) wikkid/tests/test_app.py (+40/-12) wikkid/tests/views/test_breadcrumbs.py (+13/-0) wikkid/tests/views/test_root.py (+33/-2) wikkid/tests/views/test_urls.py (+22/-17) wikkid/view/base.py (+13/-9) wikkid/view/directory.py (+5/-5) wikkid/view/edit.py (+1/-1) wikkid/view/root.py (+2/-0) wikkid/view/urls.py (+3/-3) |
||||
To merge this branch: | bzr merge lp:~thumper/wikkid/script-name | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Penhey | Approve | ||
Review via email: mp+106141@code.launchpad.net |
Commit message
Description of the change
Allow wikkid-serve to specify a script-name. This is the assumed to be the start of any request for a wikkid served page. If not, a 404 is returned.
Styling and links are updated to use the script-name.
To post a comment you must log in.
lp:~thumper/wikkid/script-name
updated
- 86. By Tim Penhey
-
Have the views use their own canonical_url function that uses the request object.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/wikkid-serve' |
2 | --- bin/wikkid-serve 2011-03-09 21:34:46 +0000 |
3 | +++ bin/wikkid-serve 2012-05-17 11:13:18 +0000 |
4 | @@ -58,12 +58,16 @@ |
5 | '--default-format', type='string', default=DEFAULT_FORMAT, |
6 | help=("Specify the default wiki format to use. Defaults to %r" |
7 | % DEFAULT_FORMAT)) |
8 | + parser.add_option( |
9 | + '--script-name', |
10 | + help=('The SCRIPT_NAME for the environment. This is the prefix for the URLs')) |
11 | options, args = parser.parse_args(sys.argv[1:]) |
12 | |
13 | execution_context = ExecutionContext( |
14 | host=options.host, |
15 | port=options.port, |
16 | - default_format=options.default_format) |
17 | + default_format=options.default_format, |
18 | + script_name=options.script_name) |
19 | |
20 | if len(args) == 0: |
21 | print "No branch location specified.\n" |
22 | |
23 | === modified file 'setup.py' |
24 | --- setup.py 2011-06-29 09:21:55 +0000 |
25 | +++ setup.py 2012-05-17 11:13:18 +0000 |
26 | @@ -31,7 +31,8 @@ |
27 | 'zope.interface', |
28 | ], |
29 | test_requires=[ |
30 | - 'beautifulsoup', |
31 | + 'bs4', |
32 | + 'bzrlib.tests', |
33 | 'testtools', |
34 | ], |
35 | test_suite='wikkid.tests', |
36 | |
37 | === modified file 'wikkid/app.py' |
38 | --- wikkid/app.py 2010-11-22 00:00:31 +0000 |
39 | +++ wikkid/app.py 2012-05-17 11:13:18 +0000 |
40 | @@ -11,6 +11,7 @@ |
41 | import mimetypes |
42 | import os.path |
43 | import urllib |
44 | +from wsgiref.util import shift_path_info |
45 | |
46 | from bzrlib import urlutils |
47 | from webob import Request, Response |
48 | @@ -59,35 +60,50 @@ |
49 | self.skin = Skin(skin_name) |
50 | self.logger = logging.getLogger('wikkid') |
51 | |
52 | - def __call__(self, environ, start_response): |
53 | - """The WSGI bit.""" |
54 | + def process_call(self, environ): |
55 | + """The actual implementation of dealing with the call.""" |
56 | + # TODO: reject requests that aren't GET or POST |
57 | request = Request(environ) |
58 | - |
59 | - # TODO: reject requests that aren't GET or POST |
60 | path = urllib.unquote(request.path) |
61 | + script_name = self.execution_context.script_name |
62 | + if not path.startswith(script_name + '/'): |
63 | + return HTTPNotFound() |
64 | + |
65 | + shifted_prefix = '' |
66 | + while shifted_prefix != script_name: |
67 | + shifted = shift_path_info(environ) |
68 | + shifted_prefix = '{0}/{1}'.format(shifted_prefix, shifted) |
69 | + # Now we are just interested in the path_info having ignored the |
70 | + # script name. |
71 | + path = urllib.unquote(request.path_info) |
72 | + |
73 | if path == '/favicon.ico': |
74 | if self.skin.favicon is not None: |
75 | - response = serve_file(self.skin.favicon) |
76 | + return serve_file(self.skin.favicon) |
77 | else: |
78 | - response = HTTPNotFound() |
79 | - elif path.startswith('/static/'): |
80 | + return HTTPNotFound() |
81 | + |
82 | + if path.startswith('/static/'): |
83 | if self.skin.static_dir is not None: |
84 | static_dir = self.skin.static_dir.rstrip(os.sep) + os.sep |
85 | static_file = os.path.abspath( |
86 | urlutils.joinpath(static_dir, path[8:])) |
87 | if static_file.startswith(static_dir): |
88 | - response = serve_file(static_file) |
89 | + return serve_file(static_file) |
90 | else: |
91 | - response = HTTPNotFound() |
92 | + return HTTPNotFound() |
93 | else: |
94 | - response = HTTPNotFound() |
95 | - else: |
96 | - resource_path, action = parse_url(path) |
97 | - model = self.resource_factory.get_resource_at_path(resource_path) |
98 | - try: |
99 | - view = get_view(model, action, request, self.execution_context) |
100 | - response = view.render(self.skin) |
101 | - except HTTPException, e: |
102 | - response = e |
103 | - |
104 | + return HTTPNotFound() |
105 | + |
106 | + resource_path, action = parse_url(path) |
107 | + model = self.resource_factory.get_resource_at_path(resource_path) |
108 | + try: |
109 | + view = get_view(model, action, request, self.execution_context) |
110 | + return view.render(self.skin) |
111 | + except HTTPException, e: |
112 | + return e |
113 | + |
114 | + def __call__(self, environ, start_response): |
115 | + """The WSGI bit.""" |
116 | + response = self.process_call(environ) |
117 | return response(environ, start_response) |
118 | |
119 | === modified file 'wikkid/context.py' |
120 | --- wikkid/context.py 2010-11-22 00:00:31 +0000 |
121 | +++ wikkid/context.py 2012-05-17 11:13:18 +0000 |
122 | @@ -1,6 +1,6 @@ |
123 | # -*- coding: utf-8 -*- |
124 | # |
125 | -# Copyright (C) 2010 Wikkid Developers. |
126 | +# Copyright (C) 2010-2012 Wikkid Developers. |
127 | # |
128 | # This software is licensed under the GNU Affero General Public License |
129 | # version 3 (see the file LICENSE). |
130 | @@ -13,9 +13,13 @@ |
131 | |
132 | |
133 | class ExecutionContext(object): |
134 | - """Store run-time execution context data.""" |
135 | - |
136 | - def __init__(self, host=None, port=None, default_format=None): |
137 | + """Store run-time execution context data. |
138 | + |
139 | + This is the Encapsulate Context pattern. |
140 | + """ |
141 | + |
142 | + def __init__(self, host=None, port=None, default_format=None, |
143 | + script_name=None): |
144 | """Create an execution context for the application. |
145 | |
146 | :param host: The hostname that content is being served from. |
147 | @@ -32,3 +36,8 @@ |
148 | self.host = host |
149 | self.port = port |
150 | self.default_format = default_format |
151 | + # TODO: make sure the script_name if set starts with a slash and |
152 | + # doesn't finish with one. |
153 | + if script_name is None: |
154 | + script_name = '' |
155 | + self.script_name = script_name |
156 | |
157 | === modified file 'wikkid/skin/default/base.html' |
158 | --- wikkid/skin/default/base.html 2010-07-13 12:49:52 +0000 |
159 | +++ wikkid/skin/default/base.html 2012-05-17 11:13:18 +0000 |
160 | @@ -5,14 +5,14 @@ |
161 | <title>{% block title %}{% endblock %} - Wikkid</title> |
162 | |
163 | <link type="text/css" rel="stylesheet" media="screen, print" |
164 | - href="/static/default.css" /> |
165 | - <link rel="shortcut icon" href="/favicon.ico" /> |
166 | + href="{{ request.script_name }}/static/default.css" /> |
167 | + <link rel="shortcut icon" href="{{ request.script_name }}/favicon.ico" /> |
168 | {% endblock %} |
169 | </head> |
170 | <body> |
171 | <div id="container"> |
172 | <div id="header"> |
173 | - <div class="wikkidlogo"><a href="/"><strong>Wikkid Wiki</strong></a></div> |
174 | + <div class="wikkidlogo"><a href="{{ request.script_name }}/"><strong>Wikkid Wiki</strong></a></div> |
175 | <div id="logged"> |
176 | {% if view.user %} |
177 | |
178 | @@ -44,7 +44,7 @@ |
179 | </div> |
180 | {% block footer %}{% endblock %} |
181 | <div id="footer"> |
182 | - © 2010 |
183 | + © 2010-2012 |
184 | <a href="https://launchpad.net/~wikkid">Wikkid Hackers</a>, |
185 | All rights reserved. |
186 | </div> |
187 | |
188 | === modified file 'wikkid/tests/factory.py' |
189 | --- wikkid/tests/factory.py 2010-06-15 09:05:29 +0000 |
190 | +++ wikkid/tests/factory.py 2012-05-17 11:13:18 +0000 |
191 | @@ -26,7 +26,7 @@ |
192 | class ViewTestCase(FactoryTestCase): |
193 | """A factory test case that can create views.""" |
194 | |
195 | - def get_view(self, factory, path, name=None): |
196 | + def get_view(self, factory, path, name=None, base_url=None): |
197 | info = factory.get_resource_at_path(path) |
198 | - request = Request.blank(path) |
199 | + request = Request.blank(path, base_url=base_url) |
200 | return get_view(info, name, request) |
201 | |
202 | === modified file 'wikkid/tests/test_app.py' |
203 | --- wikkid/tests/test_app.py 2011-04-23 08:18:58 +0000 |
204 | +++ wikkid/tests/test_app.py 2012-05-17 11:13:18 +0000 |
205 | @@ -11,25 +11,28 @@ |
206 | from webob.request import environ_from_url |
207 | |
208 | from wikkid.app import WikkidApp |
209 | +from wikkid.context import ExecutionContext |
210 | from wikkid.filestore.volatile import FileStore |
211 | from wikkid.tests import TestCase |
212 | |
213 | |
214 | class TestApp(TestCase): |
215 | |
216 | + def assert_not_found(self, status, headers): |
217 | + self.assertEqual("404 Not Found", status) |
218 | + |
219 | + def assert_ok(self, status, headers): |
220 | + self.assertEqual("200 OK", status) |
221 | + |
222 | def test_traverse_above_static_not_possible_with_relative_path(self): |
223 | """ |
224 | Traversal above the static folder, by forging a malicious request with |
225 | a relative path for example, is not possible. |
226 | """ |
227 | environ = environ_from_url("/static/../page.html") |
228 | - |
229 | - def start_response(status, headers): |
230 | - self.assertEqual("404 Not Found", status) |
231 | - |
232 | filestore = FileStore() |
233 | app = WikkidApp(filestore) |
234 | - app(environ, start_response) |
235 | + app(environ, self.assert_not_found) |
236 | |
237 | def test_traverse_above_static_not_possible_with_absolute_path(self): |
238 | """ |
239 | @@ -38,10 +41,35 @@ |
240 | """ |
241 | this_file = os.path.abspath(__file__) |
242 | environ = environ_from_url("/static/" + this_file) |
243 | - |
244 | - def start_response(status, headers): |
245 | - self.assertEqual("404 Not Found", status) |
246 | - |
247 | - filestore = FileStore() |
248 | - app = WikkidApp(filestore) |
249 | - app(environ, start_response) |
250 | + filestore = FileStore() |
251 | + app = WikkidApp(filestore) |
252 | + app(environ, self.assert_not_found) |
253 | + |
254 | + def test_getting_static_style_css_works(self): |
255 | + |
256 | + environ = environ_from_url("/static/default.css") |
257 | + filestore = FileStore() |
258 | + app = WikkidApp(filestore) |
259 | + app(environ, self.assert_ok) |
260 | + |
261 | + def test_getting_static_style_css_works_with_script_name(self): |
262 | + |
263 | + environ = environ_from_url("/test/static/default.css") |
264 | + filestore = FileStore() |
265 | + context = ExecutionContext(script_name="/test") |
266 | + app = WikkidApp(filestore, execution_context=context) |
267 | + app(environ, self.assert_ok) |
268 | + |
269 | + def test_getting_static_style_css_works_with_script_name_multiple_segments(self): |
270 | + environ = environ_from_url("/p/project-name/wiki/static/default.css") |
271 | + filestore = FileStore() |
272 | + context = ExecutionContext(script_name="/p/project-name/wiki") |
273 | + app = WikkidApp(filestore, execution_context=context) |
274 | + app(environ, self.assert_ok) |
275 | + |
276 | + def test_getting_anything_outside_script_name_fails(self): |
277 | + environ = environ_from_url("/foo/bar") |
278 | + filestore = FileStore() |
279 | + context = ExecutionContext(script_name="/test") |
280 | + app = WikkidApp(filestore, execution_context=context) |
281 | + app(environ, self.assert_not_found) |
282 | |
283 | === modified file 'wikkid/tests/views/test_breadcrumbs.py' |
284 | --- wikkid/tests/views/test_breadcrumbs.py 2010-06-15 09:05:29 +0000 |
285 | +++ wikkid/tests/views/test_breadcrumbs.py 2012-05-17 11:13:18 +0000 |
286 | @@ -107,3 +107,16 @@ |
287 | ('wiki root', '/+listing'), |
288 | ('SomePage', '/SomePage/+listing'), |
289 | ('SubPage', '/SomePage/SubPage')]) |
290 | + |
291 | + def test_directory_breadcrumbs_nested_with_script_name(self): |
292 | + # For each directory after the root, a listing crumb is added. |
293 | + # Names are not wiki expanded. |
294 | + factory = self.make_factory([ |
295 | + ('SomePage/SubPage/Nested.txt', 'some text')]) |
296 | + view = self.get_view(factory, '/SomePage/SubPage', 'listing', '/p/wiki') |
297 | + self.assertBreadcrumbs( |
298 | + view, |
299 | + [('Home', '/p/wiki/Home'), |
300 | + ('wiki root', '/p/wiki/+listing'), |
301 | + ('SomePage', '/p/wiki/SomePage/+listing'), |
302 | + ('SubPage', '/p/wiki/SomePage/SubPage')]) |
303 | |
304 | === modified file 'wikkid/tests/views/test_root.py' |
305 | --- wikkid/tests/views/test_root.py 2010-06-17 10:45:52 +0000 |
306 | +++ wikkid/tests/views/test_root.py 2012-05-17 11:13:18 +0000 |
307 | @@ -6,16 +6,19 @@ |
308 | |
309 | """Test views for the root object.""" |
310 | |
311 | +from bs4 import BeautifulSoup |
312 | +from testtools.matchers import Equals |
313 | from webob.exc import HTTPSeeOther |
314 | |
315 | +from wikkid.skin.loader import Skin |
316 | from wikkid.tests.factory import ViewTestCase |
317 | |
318 | |
319 | class TestRootViews(ViewTestCase): |
320 | """Test the views on the root object.""" |
321 | |
322 | - def test_last_modified_by(self): |
323 | - """Test that the last committer is displayed properly""" |
324 | + def test_root_redirects(self): |
325 | + """Going to / redirects to the Home page.""" |
326 | factory = self.make_factory() |
327 | view = self.get_view(factory, '/') |
328 | error = self.assertRaises( |
329 | @@ -23,3 +26,31 @@ |
330 | view.render, |
331 | None) |
332 | self.assertEqual('/Home', error.headers['Location']) |
333 | + |
334 | + def test_root_redirects_with_script_name(self): |
335 | + """Redirection works and respects the script name""" |
336 | + factory = self.make_factory() |
337 | + view = self.get_view(factory, '/', base_url='/p/test') |
338 | + error = self.assertRaises( |
339 | + HTTPSeeOther, |
340 | + view.render, |
341 | + None) |
342 | + self.assertEqual('/p/test/Home', error.headers['Location']) |
343 | + |
344 | + def test_home_rendering(self): |
345 | + """Render the home page and test the elements.""" |
346 | + factory = self.make_factory() |
347 | + view = self.get_view(factory, '/Home') |
348 | + content = view.render(Skin('default')) |
349 | + soup = BeautifulSoup(content.text) |
350 | + [style] = soup.find_all('link', {'rel':'stylesheet'}) |
351 | + self.assertThat(style['href'], Equals('/static/default.css')) |
352 | + |
353 | + def test_home_rendering_with_script_name(self): |
354 | + """Render the home page and test the elements.""" |
355 | + factory = self.make_factory() |
356 | + view = self.get_view(factory, '/Home', base_url='/p/test') |
357 | + content = view.render(Skin('default')) |
358 | + soup = BeautifulSoup(content.text) |
359 | + [style] = soup.find_all('link', {'rel':'stylesheet'}) |
360 | + self.assertThat(style['href'], Equals('/p/test/static/default.css')) |
361 | |
362 | === modified file 'wikkid/tests/views/test_urls.py' |
363 | --- wikkid/tests/views/test_urls.py 2010-06-16 10:29:35 +0000 |
364 | +++ wikkid/tests/views/test_urls.py 2012-05-17 11:13:18 +0000 |
365 | @@ -6,6 +6,9 @@ |
366 | |
367 | """Tests the edit views.""" |
368 | |
369 | +from testtools.matchers import Equals |
370 | +from webob import Request |
371 | + |
372 | from wikkid.tests import TestCase |
373 | from wikkid.tests.factory import FactoryTestCase |
374 | from wikkid.view.urls import canonical_url, parse_url |
375 | @@ -14,84 +17,86 @@ |
376 | class TestCanonicalUrl(FactoryTestCase): |
377 | """Test the wikkid.view.base.canonical_url.""" |
378 | |
379 | + def assertUrl(self, resource, url, view_name=None, base_url=None): |
380 | + request = Request.blank('/', base_url=base_url) |
381 | + self.assertThat(canonical_url(resource, request, view_name), |
382 | + Equals(url)) |
383 | + |
384 | def test_root(self): |
385 | factory = self.make_factory() |
386 | root = factory.get_resource_at_path('/') |
387 | - self.assertEqual('/', canonical_url(root)) |
388 | + self.assertUrl(root, '/') |
389 | |
390 | def test_root_listing(self): |
391 | factory = self.make_factory() |
392 | root = factory.get_resource_at_path('/') |
393 | - self.assertEqual('/+listing', canonical_url(root, 'listing')) |
394 | + self.assertUrl(root, '/+listing', view_name='listing') |
395 | |
396 | def test_default(self): |
397 | factory = self.make_factory([ |
398 | ('Home.txt', 'Some content'), |
399 | ]) |
400 | root = factory.get_resource_at_path('/') |
401 | - self.assertEqual('/Home', canonical_url(root.default_resource)) |
402 | + self.assertUrl(root.default_resource, '/Home') |
403 | |
404 | def test_default_view(self): |
405 | factory = self.make_factory([ |
406 | ('Home.txt', 'Some content'), |
407 | ]) |
408 | root = factory.get_resource_at_path('/') |
409 | - self.assertEqual( |
410 | - '/Home/+edit', |
411 | - canonical_url(root.default_resource, 'edit')) |
412 | + self.assertUrl(root.default_resource, '/Home/+edit', view_name='edit') |
413 | |
414 | def test_wiki_page(self): |
415 | factory = self.make_factory([ |
416 | ('SomeDir/SomePage.txt', 'Some content'), |
417 | ]) |
418 | page = factory.get_resource_at_path('/SomeDir/SomePage') |
419 | - self.assertEqual('/SomeDir/SomePage', canonical_url(page)) |
420 | + self.assertUrl(page, '/SomeDir/SomePage') |
421 | |
422 | def test_wiki_page_view(self): |
423 | factory = self.make_factory([ |
424 | ('SomeDir/SomePage.txt', 'Some content'), |
425 | ]) |
426 | page = factory.get_resource_at_path('/SomeDir/SomePage') |
427 | - self.assertEqual( |
428 | - '/SomeDir/SomePage/+edit', canonical_url(page, 'edit')) |
429 | + self.assertUrl(page, '/SomeDir/SomePage/+edit', view_name='edit') |
430 | |
431 | def test_wiki_page_full_url(self): |
432 | factory = self.make_factory([ |
433 | ('SomeDir.txt', 'Some content'), |
434 | ]) |
435 | page = factory.get_resource_at_path('/SomeDir.txt') |
436 | - self.assertEqual('/SomeDir', canonical_url(page)) |
437 | + self.assertUrl(page, '/SomeDir') |
438 | |
439 | def test_wiki_page_full_url_with_view(self): |
440 | factory = self.make_factory([ |
441 | ('SomeDir.txt', 'Some content'), |
442 | ]) |
443 | page = factory.get_resource_at_path('/SomeDir.txt') |
444 | - self.assertEqual('/SomeDir/+edit', canonical_url(page, 'edit')) |
445 | + self.assertUrl(page, '/SomeDir/+edit', view_name='edit') |
446 | |
447 | def test_other_file(self): |
448 | factory = self.make_factory([ |
449 | ('simple.py', '#!/usr/bin/python'), |
450 | ]) |
451 | page = factory.get_resource_at_path('/simple.py') |
452 | - self.assertEqual('/simple.py', canonical_url(page)) |
453 | + self.assertUrl(page, '/simple.py') |
454 | |
455 | def test_other_file_view(self): |
456 | factory = self.make_factory([ |
457 | ('simple.py', '#!/usr/bin/python'), |
458 | ]) |
459 | page = factory.get_resource_at_path('/simple.py') |
460 | - self.assertEqual('/simple.py/+edit', canonical_url(page, 'edit')) |
461 | + self.assertUrl(page, '/simple.py/+edit', view_name='edit') |
462 | |
463 | def test_missing(self): |
464 | factory = self.make_factory() |
465 | - root = factory.get_resource_at_path('/MissingPage') |
466 | - self.assertEqual('/MissingPage', canonical_url(root)) |
467 | + missing = factory.get_resource_at_path('/MissingPage') |
468 | + self.assertUrl(missing, '/MissingPage') |
469 | |
470 | def test_missing_view(self): |
471 | factory = self.make_factory() |
472 | - root = factory.get_resource_at_path('/MissingPage') |
473 | - self.assertEqual('/MissingPage/+edit', canonical_url(root, 'edit')) |
474 | + missing = factory.get_resource_at_path('/MissingPage') |
475 | + self.assertUrl(missing, '/MissingPage/+edit', view_name='edit') |
476 | |
477 | |
478 | class TestParseUrl(TestCase): |
479 | |
480 | === modified file 'wikkid/view/base.py' |
481 | --- wikkid/view/base.py 2010-11-22 09:23:40 +0000 |
482 | +++ wikkid/view/base.py 2012-05-17 11:13:18 +0000 |
483 | @@ -29,8 +29,8 @@ |
484 | class Breadcrumb(object): |
485 | """Breadcrumbs exist to give the user quick links up the path chain.""" |
486 | |
487 | - def __init__(self, context, view=None, title=None): |
488 | - self.path = canonical_url(context, view) |
489 | + def __init__(self, context, request, view=None, title=None): |
490 | + self.path = canonical_url(context, request, view) |
491 | if title is None: |
492 | self.title = title_for_filename(context.base_name) |
493 | else: |
494 | @@ -54,14 +54,14 @@ |
495 | self.logger = logging.getLogger('wikkid') |
496 | |
497 | def _create_breadcrumbs(self): |
498 | - crumbs = [Breadcrumb(self.context)] |
499 | + crumbs = [Breadcrumb(self.context, self.request)] |
500 | current = self.context.parent |
501 | while not IRootResource.providedBy(current): |
502 | - crumbs.append(Breadcrumb(current)) |
503 | + crumbs.append(Breadcrumb(current, self.request)) |
504 | current = current.parent |
505 | # And add in the default page if the context isn't the default. |
506 | if not IDefaultPage.providedBy(self.context): |
507 | - crumbs.append(Breadcrumb(current.default_resource)) |
508 | + crumbs.append(Breadcrumb(current.default_resource, self.request)) |
509 | return reversed(crumbs) |
510 | |
511 | def initialize(self): |
512 | @@ -89,6 +89,9 @@ |
513 | def before_render(self): |
514 | """A hook to do things before rendering.""" |
515 | |
516 | + def canonical_url(self, context, view=None): |
517 | + return canonical_url(context, self.request, view) |
518 | + |
519 | def template_args(self): |
520 | """Needs to be implemented in the derived classes. |
521 | |
522 | @@ -99,7 +102,7 @@ |
523 | 'user': self.user, |
524 | 'context': self.context, |
525 | 'request': self.request, |
526 | - 'canonical_url': canonical_url, |
527 | + 'canonical_url': self.canonical_url, |
528 | } |
529 | |
530 | def _render(self, skin): |
531 | @@ -139,12 +142,13 @@ |
532 | view = None |
533 | while not IRootResource.providedBy(current): |
534 | crumbs.append(Breadcrumb( |
535 | - current, view, title=current.base_name)) |
536 | + current, self.request, view, title=current.base_name)) |
537 | current = current.parent |
538 | # Add listings to subsequent urls. |
539 | view = 'listing' |
540 | # Add in the root dir. |
541 | - crumbs.append(Breadcrumb(current, 'listing', title='wiki root')) |
542 | + crumbs.append(Breadcrumb(current, self.request, 'listing', |
543 | + title='wiki root')) |
544 | # And add in the default page. |
545 | - crumbs.append(Breadcrumb(current.default_resource)) |
546 | + crumbs.append(Breadcrumb(current.default_resource, self.request)) |
547 | return reversed(crumbs) |
548 | |
549 | === modified file 'wikkid/view/directory.py' |
550 | --- wikkid/view/directory.py 2010-06-23 10:57:14 +0000 |
551 | +++ wikkid/view/directory.py 2012-05-17 11:13:18 +0000 |
552 | @@ -14,9 +14,9 @@ |
553 | class ListingItem(object): |
554 | """An item to be shown in the directory listing.""" |
555 | |
556 | - def __init__(self, context, view, css_class, name=None): |
557 | + def __init__(self, context, request, view, css_class, name=None): |
558 | self.context = context |
559 | - self.url = canonical_url(self.context, view) |
560 | + self.url = canonical_url(self.context, request, view) |
561 | if name is None: |
562 | name = context.base_name |
563 | self.name = name |
564 | @@ -53,11 +53,11 @@ |
565 | if self.context.path != '/': |
566 | parent = self.context.parent |
567 | items.append( |
568 | - ListingItem(parent, 'listing', 'up', name='..')) |
569 | + ListingItem(parent, self.request, 'listing', 'up', name='..')) |
570 | for item in sorted(directories, key=sort_key): |
571 | - items.append(ListingItem(item, 'listing', 'directory')) |
572 | + items.append(ListingItem(item, self.request, 'listing', 'directory')) |
573 | for item in sorted(files, key=sort_key): |
574 | - items.append(ListingItem(item, None, 'file')) |
575 | + items.append(ListingItem(item, self.request, None, 'file')) |
576 | self.items = items |
577 | |
578 | @property |
579 | |
580 | === modified file 'wikkid/view/edit.py' |
581 | --- wikkid/view/edit.py 2010-06-17 10:45:52 +0000 |
582 | +++ wikkid/view/edit.py 2012-05-17 11:13:18 +0000 |
583 | @@ -24,7 +24,7 @@ |
584 | @property |
585 | def save_url(self): |
586 | """The link for the cancel button.""" |
587 | - return canonical_url(self.context, 'save') |
588 | + return canonical_url(self.context, self.request, 'save') |
589 | |
590 | @property |
591 | def cancel_url(self): |
592 | |
593 | === modified file 'wikkid/view/root.py' |
594 | --- wikkid/view/root.py 2010-06-17 10:45:52 +0000 |
595 | +++ wikkid/view/root.py 2012-05-17 11:13:18 +0000 |
596 | @@ -23,4 +23,6 @@ |
597 | """Redirect to Home (or the default page).""" |
598 | default_resource = self.context.default_resource |
599 | preferred = default_resource.preferred_path |
600 | + if self.request.script_name: |
601 | + preferred = self.request.script_name + preferred |
602 | raise HTTPSeeOther(location=preferred) |
603 | |
604 | === modified file 'wikkid/view/urls.py' |
605 | --- wikkid/view/urls.py 2010-06-16 10:29:35 +0000 |
606 | +++ wikkid/view/urls.py 2012-05-17 11:13:18 +0000 |
607 | @@ -24,12 +24,12 @@ |
608 | return (path, None) |
609 | |
610 | |
611 | -def canonical_url(context, view=None): |
612 | +def canonical_url(context, request, view=None): |
613 | """The one true URL for the context object.""" |
614 | path = context.preferred_path |
615 | if view is None: |
616 | - return path |
617 | + return '{0}{1}'.format(request.script_name, path) |
618 | else: |
619 | if path == '/': |
620 | path = '' |
621 | - return '{0}/+{1}'.format(path, view) |
622 | + return '{0}{1}/+{2}'.format(request.script_name, path, view) |
I approve :-)