Merge lp:~yeliabmas/wikkid/fixes-for-sloecode into lp:wikkid

Proposed by Brendan Cooper on 2012-10-29
Status: Needs review
Proposed branch: lp:~yeliabmas/wikkid/fixes-for-sloecode
Merge into: lp:wikkid
Diff against target: 373 lines (+92/-26)
15 files modified
wikkid/app.py (+24/-2)
wikkid/context.py (+9/-1)
wikkid/filestore/bzr.py (+2/-2)
wikkid/filestore/volatile.py (+7/-3)
wikkid/formatter/creoleformatter.py (+1/-1)
wikkid/formatter/markdownformatter.py (+5/-3)
wikkid/formatter/pygmentsformatter.py (+1/-1)
wikkid/formatter/restformatter.py (+1/-1)
wikkid/formatter/textileformatter.py (+1/-1)
wikkid/interface/filestore.py (+1/-1)
wikkid/interface/formatter.py (+1/-1)
wikkid/tests/filestore.py (+13/-0)
wikkid/tests/test_app.py (+16/-0)
wikkid/view/textfile.py (+4/-3)
wikkid/view/wiki.py (+6/-6)
To merge this branch: bzr merge lp:~yeliabmas/wikkid/fixes-for-sloecode
Reviewer Review Type Date Requested Status
Tim Penhey 2012-10-29 Pending
Review via email: mp+131809@code.launchpad.net

Description of the change

Small fixes and docstrings in preprocess_environ for when using Wikkid as a module.

Added a method to filestore for getting a recursive listing on a directory.

Added a config setting for telling formatters to strip html. Doesn't work in most formatters as that functionality doesn't exist/is hard to find, but the option is there to implement it.

Small fix for TextFile to UTF encode content before rendering.

To post a comment you must log in.
82. By Sam Bailey on 2012-11-06

Fixed bug where Markdown would not display unicode content in files.

Unmerged revisions

82. By Sam Bailey on 2012-11-06

Fixed bug where Markdown would not display unicode content in files.

81. By Sam Bailey on 2012-10-28

Removed some outdated docstrings.

80. By Sam Bailey on 2012-10-28

Forgot to add strip_html to preview.

79. By Sam Bailey on 2012-10-28

Added a strip_html parameter to ExecutionContext. Currently only works in Markdown; no other formatters seem to support this.

78. By Sam Bailey on 2012-10-28

Changed the public-facing implementation of preprocess_environ to create a copy of the environ before use. This prevents the path_info in environ being modified if Wikkid is being used as a module.

77. By Sam Bailey on 2012-10-28

TextFile content now UTF encoded.

76. By Sam Bailey on 2012-10-28

Added parameter to the list_directory method to allow recursive directory lookups.

75. By Sam Bailey on 2012-09-13

Changed preprocess_environ in some of the views that need to be redirected. Now can call _preprocess_environ() for redirection without actually requiring a skin.

74. By Sam Bailey on 2012-09-03

Small fixes to ensure that it works with Sloecode. Moved redirection to before_render. Copying the environ before using it so that it can be reused

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'wikkid/app.py'
2--- wikkid/app.py 2012-06-14 08:49:55 +0000
3+++ wikkid/app.py 2012-11-06 03:23:19 +0000
4@@ -7,15 +7,16 @@
5
6 """A WSGI application for Wikkid."""
7
8+import copy
9 import logging
10 import mimetypes
11 import os.path
12 import urllib
13-from wsgiref.util import shift_path_info
14
15 from bzrlib import urlutils
16 from webob import Request, Response
17 from webob.exc import HTTPException, HTTPNotFound
18+from wsgiref.util import shift_path_info
19
20 from wikkid.context import ExecutionContext
21 from wikkid.dispatcher import get_view
22@@ -61,6 +62,22 @@
23 self.logger = logging.getLogger('wikkid')
24
25 def preprocess_environ(self, environ):
26+ """External entry point which creates a copy of the environ
27+ then uses that.
28+
29+ If environ is being used by any other sites then things can
30+ get messy (mostly because script_name gets used where it
31+ shouldn't, etc).
32+
33+ """
34+ new_environ = copy.copy(environ)
35+ return self._preprocess_environ(new_environ)
36+
37+ def _preprocess_environ(self, environ):
38+ """Strips the script_name from the start of the environ's
39+ PATH_INFO.
40+
41+ """
42 request = Request(environ)
43 path = urllib.unquote(request.path)
44 script_name = self.execution_context.script_name
45@@ -69,6 +86,11 @@
46 not path.startswith(script_name + '/')):
47 raise HTTPNotFound()
48
49+ # Strip script_name from the start of environ['PATH_INFO'].
50+ # PATH_INFO should always start with script_name here. If
51+ # the assertion fails then user shold probably be using
52+ # preprocess_environ
53+ assert environ['PATH_INFO'].startswith(script_name)
54 shifted_prefix = ''
55 while shifted_prefix != script_name:
56 shifted = shift_path_info(environ)
57@@ -90,7 +112,7 @@
58 """The actual implementation of dealing with the call."""
59 # TODO: reject requests that aren't GET or POST
60 try:
61- request, path = self.preprocess_environ(environ)
62+ request, path = self._preprocess_environ(environ)
63 except HTTPException, e:
64 return e
65
66
67=== modified file 'wikkid/context.py'
68--- wikkid/context.py 2012-06-14 08:40:25 +0000
69+++ wikkid/context.py 2012-11-06 03:23:19 +0000
70@@ -10,6 +10,9 @@
71 DEFAULT_FORMAT = 'rest'
72 DEFAULT_HOST = 'localhost'
73 DEFAULT_PORT = 8080
74+# STRIP_HTML only does anything in Markdown currently. The other formatters
75+# do not support this.
76+DEFAULT_STRIP_HTML = True
77
78
79 class ExecutionContext(object):
80@@ -19,13 +22,15 @@
81 """
82
83 def __init__(self, host=None, port=None, default_format=None,
84- script_name=None):
85+ script_name=None, strip_html=None):
86 """Create an execution context for the application.
87
88 :param host: The hostname that content is being served from.
89 :param port: The port that is being listened on.
90 :param default_format: The default wiki format for pages that
91 don't specify any.
92+ :param strip_html: Toggles whether or not raw html gets stripped
93+ from page content. Currently only supported in Markdown.
94 """
95 if host is None:
96 host = DEFAULT_HOST
97@@ -41,3 +46,6 @@
98 if script_name is None:
99 script_name = ''
100 self.script_name = script_name.rstrip('/')
101+ if strip_html is None:
102+ strip_html = DEFAULT_STRIP_HTML
103+ self.strip_html = strip_html
104
105=== modified file 'wikkid/filestore/bzr.py'
106--- wikkid/filestore/bzr.py 2012-06-14 01:14:00 +0000
107+++ wikkid/filestore/bzr.py 2012-11-06 03:23:19 +0000
108@@ -207,7 +207,7 @@
109 finally:
110 wt.unlock()
111
112- def list_directory(self, directory_path):
113+ def list_directory(self, directory_path, recursive=False):
114 """Return a list of File objects for in the directory path.
115
116 If the path doesn't exist, returns None. If the path exists but is
117@@ -223,7 +223,7 @@
118 wt.lock_read()
119 try:
120 for fp, fc, fkind, fid, entry in wt.list_files(
121- from_dir=directory_path, recursive=False):
122+ from_dir=directory_path, recursive=recursive):
123 if fc != 'V':
124 # If the file isn't versioned, skip it.
125 continue
126
127=== modified file 'wikkid/filestore/volatile.py'
128--- wikkid/filestore/volatile.py 2010-06-07 08:56:13 +0000
129+++ wikkid/filestore/volatile.py 2012-11-06 03:23:19 +0000
130@@ -86,7 +86,7 @@
131 existing_file.last_modified_by = user
132 existing_file.last_modified_date = datetime.utcnow()
133
134- def list_directory(self, directory_path):
135+ def list_directory(self, directory_path, recursive=False):
136 """Return a list of File objects for in the directory path.
137
138 If the path doesn't exist, returns None. If the path exists but is
139@@ -102,8 +102,12 @@
140 listing = []
141 for path, value in self.path_map.iteritems():
142 path_dir = dirname(path)
143- if path_dir == directory_path:
144- listing.append(value)
145+ if not recursive:
146+ if path_dir == directory_path:
147+ listing.append(value)
148+ else:
149+ if path_dir.startswith(directory_path):
150+ listing.append(value)
151 return listing
152
153
154
155=== modified file 'wikkid/formatter/creoleformatter.py'
156--- wikkid/formatter/creoleformatter.py 2010-05-12 10:39:13 +0000
157+++ wikkid/formatter/creoleformatter.py 2012-11-06 03:23:19 +0000
158@@ -28,6 +28,6 @@
159 def __init__(self):
160 self.rules = Rules(wiki_words=True)
161
162- def format(self, filename, text):
163+ def format(self, filename, text, strip_html=True):
164 """Format the text."""
165 return HtmlEmitter(Parser(text, self.rules).parse()).emit()
166
167=== modified file 'wikkid/formatter/markdownformatter.py'
168--- wikkid/formatter/markdownformatter.py 2010-11-05 05:32:47 +0000
169+++ wikkid/formatter/markdownformatter.py 2012-11-06 03:23:19 +0000
170@@ -19,9 +19,11 @@
171
172 implements(ITextFormatter)
173
174- def format(self, filename, text):
175+ def format(self, filename, text, strip_html=True):
176 """Format the text.
177 """
178- md = markdown.Markdown(safe_mode='replace')
179+ if strip_html is False:
180+ md = markdown.Markdown()
181+ else:
182+ md = markdown.Markdown(safe_mode='replace')
183 return md.convert(text)
184-
185
186=== modified file 'wikkid/formatter/pygmentsformatter.py'
187--- wikkid/formatter/pygmentsformatter.py 2010-11-05 05:32:47 +0000
188+++ wikkid/formatter/pygmentsformatter.py 2012-11-06 03:23:19 +0000
189@@ -22,7 +22,7 @@
190
191 implements(ITextFormatter)
192
193- def format(self, filename, text):
194+ def format(self, filename, text, strip_html=True):
195 """Format the text.
196
197 We can at a later time try to guess the lexer based on the file
198
199=== modified file 'wikkid/formatter/restformatter.py'
200--- wikkid/formatter/restformatter.py 2010-05-12 10:56:03 +0000
201+++ wikkid/formatter/restformatter.py 2012-11-06 03:23:19 +0000
202@@ -17,7 +17,7 @@
203
204 implements(ITextFormatter)
205
206- def format(self, filename, text):
207+ def format(self, filename, text, strip_html=True):
208 """Format the text.
209
210 I'm almost 100% positive that this method needs more args.
211
212=== modified file 'wikkid/formatter/textileformatter.py'
213--- wikkid/formatter/textileformatter.py 2010-11-12 09:00:42 +0000
214+++ wikkid/formatter/textileformatter.py 2012-11-06 03:23:19 +0000
215@@ -19,7 +19,7 @@
216
217 implements(ITextFormatter)
218
219- def format(self, filename, text):
220+ def format(self, filename, text, strip_html=True):
221 """Format the text.
222 """
223 return textile(text)
224
225=== modified file 'wikkid/interface/filestore.py'
226--- wikkid/interface/filestore.py 2010-06-07 08:24:06 +0000
227+++ wikkid/interface/filestore.py 2012-11-06 03:23:19 +0000
228@@ -36,7 +36,7 @@
229 provided, then some sensible default will be used.
230 """
231
232- def list_directory(path):
233+ def list_directory(path, recursive=False):
234 """Return a list of IFile objects.
235
236 Each of the IFile objects will be directly in the directory specified
237
238=== modified file 'wikkid/interface/formatter.py'
239--- wikkid/interface/formatter.py 2010-05-12 10:39:13 +0000
240+++ wikkid/interface/formatter.py 2012-11-06 03:23:19 +0000
241@@ -13,5 +13,5 @@
242 class ITextFormatter(Interface):
243 """A text formatter takes plain text and makes HTML of some form."""
244
245- def format(filename, text):
246+ def format(filename, text, strip_html=True):
247 """Takes text, and returns HTML."""
248
249=== modified file 'wikkid/tests/filestore.py'
250--- wikkid/tests/filestore.py 2010-11-08 00:00:06 +0000
251+++ wikkid/tests/filestore.py 2012-11-06 03:23:19 +0000
252@@ -154,6 +154,19 @@
253 ['another', 'subfile'],
254 sorted(f.base_name for f in listing))
255
256+ def test_recursive_list_directory(self):
257+ filestore = self.make_filestore(
258+ [('some-file', 'content'),
259+ ('directory/', None),
260+ ('directory/another-file', 'content'),
261+ ('directory/subdirectory/', None),
262+ ('directory/subdirectory/nested-file', 'content'),
263+ ])
264+ listing = filestore.list_directory('directory', recursive=True)
265+ self.assertEqual(
266+ ['another-file', 'nested-file', 'subdirectory'],
267+ sorted(f.base_name for f in listing))
268+
269 def test_last_modified(self):
270 # Make sure that the timestamp and author are recorded.
271 start = datetime.utcnow()
272
273=== modified file 'wikkid/tests/test_app.py'
274--- wikkid/tests/test_app.py 2012-06-14 08:51:43 +0000
275+++ wikkid/tests/test_app.py 2012-11-06 03:23:19 +0000
276@@ -99,3 +99,19 @@
277 def test_get_home_view(self):
278 content = [('Home.txt', 'Welcome Home.')]
279 self.assertUrlIsView("/Home", WikiPage, store_content=content)
280+
281+ def test_environ_can_be_reused_without_script_name(self):
282+ environ = environ_from_url("/baz")
283+ filestore = FileStore([("baz.txt", "Content")])
284+ context = ExecutionContext()
285+ app = WikkidApp(filestore, execution_context=context)
286+ app.preprocess_environ(environ)
287+ app(environ, self.assert_ok)
288+
289+ def test_environ_can_be_reused_with_script_name(self):
290+ environ = environ_from_url("/foo/bar/baz")
291+ filestore = FileStore([("baz.txt", "Content")])
292+ context = ExecutionContext(script_name="/foo/bar")
293+ app = WikkidApp(filestore, execution_context=context)
294+ app.preprocess_environ(environ)
295+ app(environ, self.assert_ok)
296
297=== modified file 'wikkid/view/textfile.py'
298--- wikkid/view/textfile.py 2012-05-17 11:41:05 +0000
299+++ wikkid/view/textfile.py 2012-11-06 03:23:19 +0000
300@@ -45,7 +45,7 @@
301
302 name = 'save'
303
304- def _render(self, skin):
305+ def before_render(self):
306 """Save the text file.
307
308 If it conflicts, render the edit, otherwise render the page (ideally
309@@ -62,11 +62,13 @@
310 self.rev_id = rev_id
311 self.description = description
312 self.content = content
313+ strip_html = self.execution_context.strip_html
314 default_format = self.execution_context.default_format
315 self.preview_content = format_content(
316- content, self.context.base_name, default_format)
317+ content, self.context.base_name, default_format, strip_html)
318 else:
319 try:
320+ content = content.encode('utf-8')
321 self.context.put_bytes(
322 content, self.user.committer_id, rev_id, description)
323
324@@ -81,7 +83,6 @@
325 self.message = "Conflicts detected during merge."
326
327 self.description = description
328- return super(SaveNewTextContent, self)._render(skin)
329
330
331 class UpdateTextFile(SaveNewTextContent):
332
333=== modified file 'wikkid/view/wiki.py'
334--- wikkid/view/wiki.py 2012-05-17 11:41:05 +0000
335+++ wikkid/view/wiki.py 2012-11-06 03:23:19 +0000
336@@ -14,7 +14,7 @@
337 from wikkid.view.base import BaseView
338
339
340-def format_content(bytes, base_name, default_format):
341+def format_content(bytes, base_name, default_format, strip_html=None):
342 """ Format the content with the right formatter.
343
344 Check the first line of the content to see if it specifies a
345@@ -22,7 +22,7 @@
346 configurable shortly.
347 """
348 content, formatter = get_wiki_formatter(bytes, default_format)
349- return formatter.format(base_name, content)
350+ return formatter.format(base_name, content, strip_html=strip_html)
351
352
353 class WikiPage(BaseView):
354@@ -37,9 +37,11 @@
355 def content(self):
356 bytes = self.context.get_bytes()
357 default_format = self.execution_context.default_format
358- return format_content(bytes, self.context.base_name, default_format)
359+ strip_html = self.execution_context.strip_html
360+ bytes = bytes.decode('utf-8')
361+ return format_content(bytes, self.context.base_name, default_format, strip_html=strip_html)
362
363- def _render(self, skin):
364+ def before_render(self):
365 """If the page is not being viewed with the preferred path, redirect.
366
367 For example, /FrontPage.txt will redirect to /FrontPage
368@@ -48,5 +50,3 @@
369 if self.context.path != preferred:
370 location = self.canonical_url(self.context)
371 raise HTTPTemporaryRedirect(location=location)
372- else:
373- return super(WikiPage, self)._render(skin)

Subscribers

People subscribed via source and target branches