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

Proposed by Brendan Cooper
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 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

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

Unmerged revisions

82. By Sam Bailey

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

81. By Sam Bailey

Removed some outdated docstrings.

80. By Sam Bailey

Forgot to add strip_html to preview.

79. By Sam Bailey

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

78. By Sam Bailey

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

TextFile content now UTF encoded.

76. By Sam Bailey

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

75. By Sam Bailey

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

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