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
=== modified file 'wikkid/app.py'
--- wikkid/app.py 2012-06-14 08:49:55 +0000
+++ wikkid/app.py 2012-11-06 03:23:19 +0000
@@ -7,15 +7,16 @@
77
8"""A WSGI application for Wikkid."""8"""A WSGI application for Wikkid."""
99
10import copy
10import logging11import logging
11import mimetypes12import mimetypes
12import os.path13import os.path
13import urllib14import urllib
14from wsgiref.util import shift_path_info
1515
16from bzrlib import urlutils16from bzrlib import urlutils
17from webob import Request, Response17from webob import Request, Response
18from webob.exc import HTTPException, HTTPNotFound18from webob.exc import HTTPException, HTTPNotFound
19from wsgiref.util import shift_path_info
1920
20from wikkid.context import ExecutionContext21from wikkid.context import ExecutionContext
21from wikkid.dispatcher import get_view22from wikkid.dispatcher import get_view
@@ -61,6 +62,22 @@
61 self.logger = logging.getLogger('wikkid')62 self.logger = logging.getLogger('wikkid')
6263
63 def preprocess_environ(self, environ):64 def preprocess_environ(self, environ):
65 """External entry point which creates a copy of the environ
66 then uses that.
67
68 If environ is being used by any other sites then things can
69 get messy (mostly because script_name gets used where it
70 shouldn't, etc).
71
72 """
73 new_environ = copy.copy(environ)
74 return self._preprocess_environ(new_environ)
75
76 def _preprocess_environ(self, environ):
77 """Strips the script_name from the start of the environ's
78 PATH_INFO.
79
80 """
64 request = Request(environ)81 request = Request(environ)
65 path = urllib.unquote(request.path)82 path = urllib.unquote(request.path)
66 script_name = self.execution_context.script_name83 script_name = self.execution_context.script_name
@@ -69,6 +86,11 @@
69 not path.startswith(script_name + '/')):86 not path.startswith(script_name + '/')):
70 raise HTTPNotFound()87 raise HTTPNotFound()
7188
89 # Strip script_name from the start of environ['PATH_INFO'].
90 # PATH_INFO should always start with script_name here. If
91 # the assertion fails then user shold probably be using
92 # preprocess_environ
93 assert environ['PATH_INFO'].startswith(script_name)
72 shifted_prefix = ''94 shifted_prefix = ''
73 while shifted_prefix != script_name:95 while shifted_prefix != script_name:
74 shifted = shift_path_info(environ)96 shifted = shift_path_info(environ)
@@ -90,7 +112,7 @@
90 """The actual implementation of dealing with the call."""112 """The actual implementation of dealing with the call."""
91 # TODO: reject requests that aren't GET or POST113 # TODO: reject requests that aren't GET or POST
92 try:114 try:
93 request, path = self.preprocess_environ(environ)115 request, path = self._preprocess_environ(environ)
94 except HTTPException, e:116 except HTTPException, e:
95 return e117 return e
96118
97119
=== modified file 'wikkid/context.py'
--- wikkid/context.py 2012-06-14 08:40:25 +0000
+++ wikkid/context.py 2012-11-06 03:23:19 +0000
@@ -10,6 +10,9 @@
10DEFAULT_FORMAT = 'rest'10DEFAULT_FORMAT = 'rest'
11DEFAULT_HOST = 'localhost'11DEFAULT_HOST = 'localhost'
12DEFAULT_PORT = 808012DEFAULT_PORT = 8080
13# STRIP_HTML only does anything in Markdown currently. The other formatters
14# do not support this.
15DEFAULT_STRIP_HTML = True
1316
1417
15class ExecutionContext(object):18class ExecutionContext(object):
@@ -19,13 +22,15 @@
19 """22 """
2023
21 def __init__(self, host=None, port=None, default_format=None,24 def __init__(self, host=None, port=None, default_format=None,
22 script_name=None):25 script_name=None, strip_html=None):
23 """Create an execution context for the application.26 """Create an execution context for the application.
2427
25 :param host: The hostname that content is being served from.28 :param host: The hostname that content is being served from.
26 :param port: The port that is being listened on.29 :param port: The port that is being listened on.
27 :param default_format: The default wiki format for pages that30 :param default_format: The default wiki format for pages that
28 don't specify any.31 don't specify any.
32 :param strip_html: Toggles whether or not raw html gets stripped
33 from page content. Currently only supported in Markdown.
29 """34 """
30 if host is None:35 if host is None:
31 host = DEFAULT_HOST36 host = DEFAULT_HOST
@@ -41,3 +46,6 @@
41 if script_name is None:46 if script_name is None:
42 script_name = ''47 script_name = ''
43 self.script_name = script_name.rstrip('/')48 self.script_name = script_name.rstrip('/')
49 if strip_html is None:
50 strip_html = DEFAULT_STRIP_HTML
51 self.strip_html = strip_html
4452
=== modified file 'wikkid/filestore/bzr.py'
--- wikkid/filestore/bzr.py 2012-06-14 01:14:00 +0000
+++ wikkid/filestore/bzr.py 2012-11-06 03:23:19 +0000
@@ -207,7 +207,7 @@
207 finally:207 finally:
208 wt.unlock()208 wt.unlock()
209209
210 def list_directory(self, directory_path):210 def list_directory(self, directory_path, recursive=False):
211 """Return a list of File objects for in the directory path.211 """Return a list of File objects for in the directory path.
212212
213 If the path doesn't exist, returns None. If the path exists but is213 If the path doesn't exist, returns None. If the path exists but is
@@ -223,7 +223,7 @@
223 wt.lock_read()223 wt.lock_read()
224 try:224 try:
225 for fp, fc, fkind, fid, entry in wt.list_files(225 for fp, fc, fkind, fid, entry in wt.list_files(
226 from_dir=directory_path, recursive=False):226 from_dir=directory_path, recursive=recursive):
227 if fc != 'V':227 if fc != 'V':
228 # If the file isn't versioned, skip it.228 # If the file isn't versioned, skip it.
229 continue229 continue
230230
=== modified file 'wikkid/filestore/volatile.py'
--- wikkid/filestore/volatile.py 2010-06-07 08:56:13 +0000
+++ wikkid/filestore/volatile.py 2012-11-06 03:23:19 +0000
@@ -86,7 +86,7 @@
86 existing_file.last_modified_by = user86 existing_file.last_modified_by = user
87 existing_file.last_modified_date = datetime.utcnow()87 existing_file.last_modified_date = datetime.utcnow()
8888
89 def list_directory(self, directory_path):89 def list_directory(self, directory_path, recursive=False):
90 """Return a list of File objects for in the directory path.90 """Return a list of File objects for in the directory path.
9191
92 If the path doesn't exist, returns None. If the path exists but is92 If the path doesn't exist, returns None. If the path exists but is
@@ -102,8 +102,12 @@
102 listing = []102 listing = []
103 for path, value in self.path_map.iteritems():103 for path, value in self.path_map.iteritems():
104 path_dir = dirname(path)104 path_dir = dirname(path)
105 if path_dir == directory_path:105 if not recursive:
106 listing.append(value)106 if path_dir == directory_path:
107 listing.append(value)
108 else:
109 if path_dir.startswith(directory_path):
110 listing.append(value)
107 return listing111 return listing
108112
109113
110114
=== modified file 'wikkid/formatter/creoleformatter.py'
--- wikkid/formatter/creoleformatter.py 2010-05-12 10:39:13 +0000
+++ wikkid/formatter/creoleformatter.py 2012-11-06 03:23:19 +0000
@@ -28,6 +28,6 @@
28 def __init__(self):28 def __init__(self):
29 self.rules = Rules(wiki_words=True)29 self.rules = Rules(wiki_words=True)
3030
31 def format(self, filename, text):31 def format(self, filename, text, strip_html=True):
32 """Format the text."""32 """Format the text."""
33 return HtmlEmitter(Parser(text, self.rules).parse()).emit()33 return HtmlEmitter(Parser(text, self.rules).parse()).emit()
3434
=== modified file 'wikkid/formatter/markdownformatter.py'
--- wikkid/formatter/markdownformatter.py 2010-11-05 05:32:47 +0000
+++ wikkid/formatter/markdownformatter.py 2012-11-06 03:23:19 +0000
@@ -19,9 +19,11 @@
1919
20 implements(ITextFormatter)20 implements(ITextFormatter)
2121
22 def format(self, filename, text):22 def format(self, filename, text, strip_html=True):
23 """Format the text.23 """Format the text.
24 """24 """
25 md = markdown.Markdown(safe_mode='replace')25 if strip_html is False:
26 md = markdown.Markdown()
27 else:
28 md = markdown.Markdown(safe_mode='replace')
26 return md.convert(text)29 return md.convert(text)
27
2830
=== modified file 'wikkid/formatter/pygmentsformatter.py'
--- wikkid/formatter/pygmentsformatter.py 2010-11-05 05:32:47 +0000
+++ wikkid/formatter/pygmentsformatter.py 2012-11-06 03:23:19 +0000
@@ -22,7 +22,7 @@
2222
23 implements(ITextFormatter)23 implements(ITextFormatter)
2424
25 def format(self, filename, text):25 def format(self, filename, text, strip_html=True):
26 """Format the text.26 """Format the text.
2727
28 We can at a later time try to guess the lexer based on the file28 We can at a later time try to guess the lexer based on the file
2929
=== modified file 'wikkid/formatter/restformatter.py'
--- wikkid/formatter/restformatter.py 2010-05-12 10:56:03 +0000
+++ wikkid/formatter/restformatter.py 2012-11-06 03:23:19 +0000
@@ -17,7 +17,7 @@
1717
18 implements(ITextFormatter)18 implements(ITextFormatter)
1919
20 def format(self, filename, text):20 def format(self, filename, text, strip_html=True):
21 """Format the text.21 """Format the text.
2222
23 I'm almost 100% positive that this method needs more args.23 I'm almost 100% positive that this method needs more args.
2424
=== modified file 'wikkid/formatter/textileformatter.py'
--- wikkid/formatter/textileformatter.py 2010-11-12 09:00:42 +0000
+++ wikkid/formatter/textileformatter.py 2012-11-06 03:23:19 +0000
@@ -19,7 +19,7 @@
1919
20 implements(ITextFormatter)20 implements(ITextFormatter)
2121
22 def format(self, filename, text):22 def format(self, filename, text, strip_html=True):
23 """Format the text.23 """Format the text.
24 """24 """
25 return textile(text)25 return textile(text)
2626
=== modified file 'wikkid/interface/filestore.py'
--- wikkid/interface/filestore.py 2010-06-07 08:24:06 +0000
+++ wikkid/interface/filestore.py 2012-11-06 03:23:19 +0000
@@ -36,7 +36,7 @@
36 provided, then some sensible default will be used.36 provided, then some sensible default will be used.
37 """37 """
3838
39 def list_directory(path):39 def list_directory(path, recursive=False):
40 """Return a list of IFile objects.40 """Return a list of IFile objects.
4141
42 Each of the IFile objects will be directly in the directory specified42 Each of the IFile objects will be directly in the directory specified
4343
=== modified file 'wikkid/interface/formatter.py'
--- wikkid/interface/formatter.py 2010-05-12 10:39:13 +0000
+++ wikkid/interface/formatter.py 2012-11-06 03:23:19 +0000
@@ -13,5 +13,5 @@
13class ITextFormatter(Interface):13class ITextFormatter(Interface):
14 """A text formatter takes plain text and makes HTML of some form."""14 """A text formatter takes plain text and makes HTML of some form."""
1515
16 def format(filename, text):16 def format(filename, text, strip_html=True):
17 """Takes text, and returns HTML."""17 """Takes text, and returns HTML."""
1818
=== modified file 'wikkid/tests/filestore.py'
--- wikkid/tests/filestore.py 2010-11-08 00:00:06 +0000
+++ wikkid/tests/filestore.py 2012-11-06 03:23:19 +0000
@@ -154,6 +154,19 @@
154 ['another', 'subfile'],154 ['another', 'subfile'],
155 sorted(f.base_name for f in listing))155 sorted(f.base_name for f in listing))
156156
157 def test_recursive_list_directory(self):
158 filestore = self.make_filestore(
159 [('some-file', 'content'),
160 ('directory/', None),
161 ('directory/another-file', 'content'),
162 ('directory/subdirectory/', None),
163 ('directory/subdirectory/nested-file', 'content'),
164 ])
165 listing = filestore.list_directory('directory', recursive=True)
166 self.assertEqual(
167 ['another-file', 'nested-file', 'subdirectory'],
168 sorted(f.base_name for f in listing))
169
157 def test_last_modified(self):170 def test_last_modified(self):
158 # Make sure that the timestamp and author are recorded.171 # Make sure that the timestamp and author are recorded.
159 start = datetime.utcnow()172 start = datetime.utcnow()
160173
=== modified file 'wikkid/tests/test_app.py'
--- wikkid/tests/test_app.py 2012-06-14 08:51:43 +0000
+++ wikkid/tests/test_app.py 2012-11-06 03:23:19 +0000
@@ -99,3 +99,19 @@
99 def test_get_home_view(self):99 def test_get_home_view(self):
100 content = [('Home.txt', 'Welcome Home.')]100 content = [('Home.txt', 'Welcome Home.')]
101 self.assertUrlIsView("/Home", WikiPage, store_content=content)101 self.assertUrlIsView("/Home", WikiPage, store_content=content)
102
103 def test_environ_can_be_reused_without_script_name(self):
104 environ = environ_from_url("/baz")
105 filestore = FileStore([("baz.txt", "Content")])
106 context = ExecutionContext()
107 app = WikkidApp(filestore, execution_context=context)
108 app.preprocess_environ(environ)
109 app(environ, self.assert_ok)
110
111 def test_environ_can_be_reused_with_script_name(self):
112 environ = environ_from_url("/foo/bar/baz")
113 filestore = FileStore([("baz.txt", "Content")])
114 context = ExecutionContext(script_name="/foo/bar")
115 app = WikkidApp(filestore, execution_context=context)
116 app.preprocess_environ(environ)
117 app(environ, self.assert_ok)
102118
=== modified file 'wikkid/view/textfile.py'
--- wikkid/view/textfile.py 2012-05-17 11:41:05 +0000
+++ wikkid/view/textfile.py 2012-11-06 03:23:19 +0000
@@ -45,7 +45,7 @@
4545
46 name = 'save'46 name = 'save'
4747
48 def _render(self, skin):48 def before_render(self):
49 """Save the text file.49 """Save the text file.
5050
51 If it conflicts, render the edit, otherwise render the page (ideally51 If it conflicts, render the edit, otherwise render the page (ideally
@@ -62,11 +62,13 @@
62 self.rev_id = rev_id62 self.rev_id = rev_id
63 self.description = description63 self.description = description
64 self.content = content64 self.content = content
65 strip_html = self.execution_context.strip_html
65 default_format = self.execution_context.default_format66 default_format = self.execution_context.default_format
66 self.preview_content = format_content(67 self.preview_content = format_content(
67 content, self.context.base_name, default_format)68 content, self.context.base_name, default_format, strip_html)
68 else:69 else:
69 try:70 try:
71 content = content.encode('utf-8')
70 self.context.put_bytes(72 self.context.put_bytes(
71 content, self.user.committer_id, rev_id, description)73 content, self.user.committer_id, rev_id, description)
7274
@@ -81,7 +83,6 @@
81 self.message = "Conflicts detected during merge."83 self.message = "Conflicts detected during merge."
8284
83 self.description = description85 self.description = description
84 return super(SaveNewTextContent, self)._render(skin)
8586
8687
87class UpdateTextFile(SaveNewTextContent):88class UpdateTextFile(SaveNewTextContent):
8889
=== modified file 'wikkid/view/wiki.py'
--- wikkid/view/wiki.py 2012-05-17 11:41:05 +0000
+++ wikkid/view/wiki.py 2012-11-06 03:23:19 +0000
@@ -14,7 +14,7 @@
14from wikkid.view.base import BaseView14from wikkid.view.base import BaseView
1515
1616
17def format_content(bytes, base_name, default_format):17def format_content(bytes, base_name, default_format, strip_html=None):
18 """ Format the content with the right formatter.18 """ Format the content with the right formatter.
1919
20 Check the first line of the content to see if it specifies a20 Check the first line of the content to see if it specifies a
@@ -22,7 +22,7 @@
22 configurable shortly.22 configurable shortly.
23 """23 """
24 content, formatter = get_wiki_formatter(bytes, default_format)24 content, formatter = get_wiki_formatter(bytes, default_format)
25 return formatter.format(base_name, content)25 return formatter.format(base_name, content, strip_html=strip_html)
2626
2727
28class WikiPage(BaseView):28class WikiPage(BaseView):
@@ -37,9 +37,11 @@
37 def content(self):37 def content(self):
38 bytes = self.context.get_bytes()38 bytes = self.context.get_bytes()
39 default_format = self.execution_context.default_format39 default_format = self.execution_context.default_format
40 return format_content(bytes, self.context.base_name, default_format)40 strip_html = self.execution_context.strip_html
41 bytes = bytes.decode('utf-8')
42 return format_content(bytes, self.context.base_name, default_format, strip_html=strip_html)
4143
42 def _render(self, skin):44 def before_render(self):
43 """If the page is not being viewed with the preferred path, redirect.45 """If the page is not being viewed with the preferred path, redirect.
4446
45 For example, /FrontPage.txt will redirect to /FrontPage47 For example, /FrontPage.txt will redirect to /FrontPage
@@ -48,5 +50,3 @@
48 if self.context.path != preferred:50 if self.context.path != preferred:
49 location = self.canonical_url(self.context)51 location = self.canonical_url(self.context)
50 raise HTTPTemporaryRedirect(location=location)52 raise HTTPTemporaryRedirect(location=location)
51 else:
52 return super(WikiPage, self)._render(skin)

Subscribers

People subscribed via source and target branches