diff options
| author | ianb <devnull@localhost> | 2005-07-03 19:59:13 +0000 |
|---|---|---|
| committer | ianb <devnull@localhost> | 2005-07-03 19:59:13 +0000 |
| commit | 94bee1ee2c1e56ceb6a94e889a62dff2b8159e36 (patch) | |
| tree | f12a173621e3f41544595dc0beae92b4d44feee6 /examples | |
| parent | 70f1fa9f5e88aa9083802b7f575bcf9862da06dc (diff) | |
| download | paste-94bee1ee2c1e56ceb6a94e889a62dff2b8159e36.tar.gz | |
Refactored heavily; now each file type (of which there are currently just two) is its own class of servlet, and dispatching is done to the appropriate class.
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/filebrowser/js-lib/common.js | 2 | ||||
| -rw-r--r-- | examples/filebrowser/pathobj.py | 175 | ||||
| -rw-r--r-- | examples/filebrowser/server.conf | 12 | ||||
| -rw-r--r-- | examples/filebrowser/sitepage.py | 38 | ||||
| -rw-r--r-- | examples/filebrowser/templates/directory.pt | 2 | ||||
| -rw-r--r-- | examples/filebrowser/templates/edit_text.pt | 15 | ||||
| -rw-r--r-- | examples/filebrowser/templates/view_file.pt | 5 | ||||
| -rw-r--r-- | examples/filebrowser/web/__init__.py | 18 | ||||
| -rw-r--r-- | examples/filebrowser/web/download.py | 20 | ||||
| -rw-r--r-- | examples/filebrowser/web/index.py | 40 |
10 files changed, 225 insertions, 102 deletions
diff --git a/examples/filebrowser/js-lib/common.js b/examples/filebrowser/js-lib/common.js index a7e52c7..31a9162 100644 --- a/examples/filebrowser/js-lib/common.js +++ b/examples/filebrowser/js-lib/common.js @@ -13,7 +13,7 @@ function lazyToggle(container, url, options) { if (Element.hidden(container)) { Element.show(container); if (! $(container).lazy_loaded) { - new Ajax.Updater(container, url, {method: 'get'}); + new Ajax.Updater(container, url, options); $(container).lazy_loaded = true; } if (options.link) { diff --git a/examples/filebrowser/pathobj.py b/examples/filebrowser/pathobj.py new file mode 100644 index 0000000..015b882 --- /dev/null +++ b/examples/filebrowser/pathobj.py @@ -0,0 +1,175 @@ +import re +import os +import mimetypes +from paste.wareweb import dispatch, public +from paste.httpexceptions import * +import sitepage + +view_servlet_module = None + +class BadFilePath(Exception): + pass + +class PathContext(object): + + path_classes = {} + + def __init__(self, root): + self.root = root + + def path(self, path): + filename = self.root + '/' + path.lstrip('/') + if os.path.isdir(filename): + ptype = 'dir' + else: + ptype = os.path.splitext(filename)[1] + path_class = self.path_classes.get( + ptype, self.path_classes['*']) + return path_class(path, filename, self) + + @classmethod + def register_class(cls, path_class): + assert not isinstance(path_class.extensions, (str, unicode)) + for ptype in path_class.extensions: + assert ptype not in cls.path_classes, ( + "When adding class %r, conflict with class %r for " + "extension %r" % (path_class, cls.path_classes[ptype], + ptype)) + cls.path_classes[ptype] = path_class + +class Path(sitepage.SitePage): + + extensions = ['*'] + + dispatch = dispatch.ActionDispatch( + action_name='action', + default_action='view_raw') + + isdir = False + + def __init__(self, path, filename, context): + super(Path, self).__init__() + self.path = path + self.filename = filename + self.pathcontext = context + self.root = context.root + self.mimetype, self.encoding = mimetypes.guess_type(self.filename) + if not self.mimetype: + self.mimetype = 'application/octet-stream' + self.basename = os.path.basename(filename) + self.exists = os.path.exists(filename) + + def __str__(self): + return self.path + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, self.path) + + def setup(self): + self.title = 'File: %s' % self.basename + self.options.parent = { + 'url': self.pathurl.up(), + 'name': self.pathurl.up().name() or 'root', + } + self.setup_file() + + def setup_file(self): + mime = self.mimetype + if mime and mime.startswith('text/'): + self.options.content = self.read() + else: + self.options.content = None + self.options.is_image = mime and mime.startswith('image/') + self.options.use_iframe = mime == 'text/html' + + def action_view(self): + self.view = 'view_file.pt' + self.options.allow_edit = self.mimetype.startswith('text/') + + def join(self, name): + parts = name.split('/') + for part in parts: + if part.startswith('.'): + raise BadFilePath( + "The path part %r starts with '.', which is illegal" + % part) + if ':' in name: + raise BadFilePath( + "The path %r contains ':', which is illegal" % name) + if '\\' in name: + raise BadFilePath( + "The path %r contains '\\', which is illegal" % name) + name = name.lstrip('/') + name = re.sub(r'//+', '/', name) + new_path = self.path + '/' + name + return self.pathcontext.path(new_path) + + bad_regexes = [ + re.compile(r'<script.*?</script>', re.I+re.S), + re.compile(r'style="[^"]*position:.*?"', re.I+re.S), + ] + + def action_view_raw(self): + self.view = None + if not self.exists: + raise HTTPNotFound + self.set_header('Content-type', self.mimetype) + content = self.read() + if (self.mimetype.startswith('text/html') + and servlet.fields.get('html') == 'clean'): + for bad_regex in bad_regexes: + content = self.bad_regex.sub('', content) + self.write(content) + + def action_edit(self): + self.view = 'edit_text.pt' + self.options.content = self.read() + self.options.action = str(self.pathurl) + + def action_save(self): + content = self.fields.content + f = open(self.filename, 'wb') + f.write(content) + f.close() + self.message.write('%i bytes saved' % len(content)) + self.redirect(str(self.pathurl(action='view'))) + + def read(self): + f = open(self.filename, 'rb') + content = f.read() + f.close() + return content + +PathContext.register_class(Path) + +class Dir(Path): + + extensions = ['dir'] + + isdir = True + + def setup_file(self): + files = [] + for filename in sorted(os.listdir(self.filename)): + try: + path_servlet = self.join(filename) + except BadFilePath: + continue + files.append({ + 'path': path_servlet, + 'name': filename, + 'url': self.pathurl(filename, action='view'), + 'copyid': self.pathid('copy_', str(path_servlet)), + }) + if path_servlet.isdir: + files[-1]['name'] += '/' + self.options.files = files + + def action_view_raw(self): + self.action_view() + + def action_view(self): + self.view = 'directory.pt' + +PathContext.register_class(Dir) + diff --git a/examples/filebrowser/server.conf b/examples/filebrowser/server.conf index 7a0dd1b..da36acb 100644 --- a/examples/filebrowser/server.conf +++ b/examples/filebrowser/server.conf @@ -5,13 +5,8 @@ import os app_template = 'wareweb_zpt' app_name = 'filebrowser' framework = 'wareweb' -root_path = '/home/ianb/svn.w4py.org/Paste/trunk/examples/filebrowser' +root_path = os.path.dirname(__file__) publish_dir = os.path.join(root_path, 'web') -sys_path.extend([ - os.path.dirname(root_path), - '/home/ianb/co/py-lib', - '/home/ianb/co/ZPTKit', - ]) ## Server options: @@ -23,6 +18,9 @@ server = 'wsgiutils' reload = True # If true, tracebacks will be shown in the browser: debug = True +show_exceptions_in_error_log = False browse_path = os.path.join(root_path, 'test-data') -#browse_path = '/home/ianb/media/music/artist' + +if os.path.exists(os.path.join(root_path, 'local.conf')): + include('local.conf') diff --git a/examples/filebrowser/sitepage.py b/examples/filebrowser/sitepage.py index 10ac1a5..62b31ad 100644 --- a/examples/filebrowser/sitepage.py +++ b/examples/filebrowser/sitepage.py @@ -4,8 +4,8 @@ import os import urllib from paste.wareweb import * from paste import wsgilib +from paste import CONFIG from ZPTKit.zptwareweb import ZPTComponent -from py.path import local import handlers class SitePage(Servlet): @@ -16,32 +16,23 @@ class SitePage(Servlet): def awake(self): super(SitePage, self).awake(call_setup=False) - self.root = local(self.config['browse_path']) - if 'browser.filepath' in self.environ: - self.pathinfo = self.environ['browser.filepath'] - self.path = self.root.join(self.pathinfo) - else: - self.pathinfo = '' - self.path = None + self.pathcontext = self.environ['filebrowser.pathcontext'] self.pathurl = URL(self.environ, script_name=self.app_url, - path_info=self.pathinfo) + path_info=self.path_info) + # While this is the URL that points to the non-file-specific + # application (for things like copy and past) self.globalurl = URL(self.environ, script_name=self.app_url + '/_app', path_info='') self.icons = Icon(self.app_url + '/_icons') self.app_static_url = self.app_url + '/_static' + # Prototype adds this special _ variable for Ajax requests: self.fragment = '_' in self.fields self.copybin = self.session.setdefault('copybin', {}) self.update_copybin_display() self.setup() - def rootedpath(self, path): - path = str(path) - assert path.startswith(str(self.root)), ( - "Bad path: %r (doesn't start with %r)" % (path, self.root)) - return path[len(str(self.root)):] - def handler(self, path=None): path = path or self.file handler = handlers.get_handler(path) @@ -49,10 +40,10 @@ class SitePage(Servlet): def update_copybin_display(self): files = self.options.copyfiles = [] for filename, cutcopy in self.copybin.items(): - path = local(self.root).join(filename) + path = self.pathcontext.path(filename) files.append( {'filename': filename, - 'url': self.app_url + filename, + 'url': self.app_url + filename + "?action=view", 'path': path, 'name': path.basename, 'cut': cutcopy=='cut', @@ -62,14 +53,12 @@ class SitePage(Servlet): files.sort(key=lambda x: x['filename']) def pathid(self, prefix, path): - if isinstance(path, local): - path = self.rootedpath(path) - return prefix + path.replace('/', '__') + return prefix + str(path).replace('/', '__') def copybutton(self, path, copytype='copy'): assert copytype in ('copy', 'cut') copyurl = self.globalurl('copybin', **{ - copytype: self.rootedpath(path)}) + copytype: path}) remote = copyurl.remote( id='copybin', onComplete='function (req) {new Effect.Highlight(%r)}' @@ -79,7 +68,7 @@ class SitePage(Servlet): return remote.link(img) def pastebutton(self, path): - copyurl = self.globalurl('copybin', paste=self.rootedpath(path), + copyurl = self.globalurl('copybin', paste=path, back=wsgilib.construct_url(self.environ)) return '<a href="%s">%s</a>' % ( copyurl, self.icons('editpaste', alt="paste", @@ -155,6 +144,9 @@ class URL(object): def name(self): return self.path_info.split('/')[-1] + def js_set_location(self): + return 'location.href = %r' % str(self) + _idgen_count = itertools.count() def idgen(): return 'node%s' % hex(_idgen_count.next() % 0xffffff)[2:] @@ -190,7 +182,7 @@ class JSRemote(object): class JSToggle(JSRemote): def javascript(self): - return "lazyToggle(%r, %r, {link: this, parameters: %r}); return false" % ( + return "lazyToggle(%r, %r, %s); return false" % ( self.id, self.url.url_without_qs, self.option_js(parameters=repr(urllib.urlencode(self.url.vars.items())), link='this')) diff --git a/examples/filebrowser/templates/directory.pt b/examples/filebrowser/templates/directory.pt index 6215086..5f0df9a 100644 --- a/examples/filebrowser/templates/directory.pt +++ b/examples/filebrowser/templates/directory.pt @@ -18,7 +18,7 @@ 'odd', 'even')" tal:define="toggle f/url/toggle"> <div class="controls"> - <span tal:condition="python: f['path'].check(dir=1)" + <span tal:condition="python: f['path'].isdir" tal:replace="structure python: servlet.pastebutton(f['path'])"/> <span tal:replace="structure python: servlet.copybutton(f['path'], 'cut')"/> <span tal:replace="structure python: servlet.copybutton(f['path'], diff --git a/examples/filebrowser/templates/edit_text.pt b/examples/filebrowser/templates/edit_text.pt new file mode 100644 index 0000000..e2d7ba7 --- /dev/null +++ b/examples/filebrowser/templates/edit_text.pt @@ -0,0 +1,15 @@ +<html metal:use-macro="here/standard_template.pt/macros/page"> +<metal:body fill-slot="body"> + +<form tal:attributes="action options/action" method="POST"> +<input type="hidden" name="action" value="save"> +Edit:<br> +<textarea name="content" rows=10 cols=60 style="width: 100%" + tal:content="options/content"></textarea> +<br> +<input type="submit" value="Save"> +<button tal:attributes="onclick servlet/pathurl/js_set_location">Cancel</button> +</form> + +</metal:body> +</html>
\ No newline at end of file diff --git a/examples/filebrowser/templates/view_file.pt b/examples/filebrowser/templates/view_file.pt index f0904df..d6ad93a 100644 --- a/examples/filebrowser/templates/view_file.pt +++ b/examples/filebrowser/templates/view_file.pt @@ -8,8 +8,11 @@ Parent: <a tal:attributes="href options/parent/url" tal:content="options/parent/name">name</a><br> </tal:if> </tal:if> -Mimetype: <span tal:replace="options/mimetype"/><br> +Mimetype: <span tal:replace="servlet/mimetype"/><br> <a tal:attributes="href servlet/pathurl/action=download">download</a> +<tal:if condition="options/allow_edit"> | + <a tal:attributes="href servlet/pathurl/action=edit">edit</a></tal:if> + </p> <pre tal:condition="python: options['content'] and not options['use_iframe']" diff --git a/examples/filebrowser/web/__init__.py b/examples/filebrowser/web/__init__.py index 0ffdb8b..4be1d00 100644 --- a/examples/filebrowser/web/__init__.py +++ b/examples/filebrowser/web/__init__.py @@ -1,9 +1,11 @@ import os import urllib from py.path import local +from paste import CONFIG from paste import wsgilib from paste import httpexceptions from paste import urlparser +from filebrowser import pathobj def urlparser_hook(environ): if not environ.has_key('filebrowser.base_url'): @@ -20,20 +22,18 @@ special_dirs['app'] = urlparser.URLParser( os.path.dirname(__file__), 'filebrowser.web') def application(environ, start_response): - if environ.get('browser.resolved'): + context = environ['filebrowser.pathcontext'] = pathobj.PathContext( + root=environ['paste.config']['browse_path']) + if environ.get('filebrowser.resolved'): return special_dirs['app'](environ, start_response) - conf = environ['paste.config'] path_info = environ.get('PATH_INFO', '') for special_dir, parser in special_dirs.items(): if path_info.startswith('/_%s/' % special_dir): - environ['browser.resolved'] = True + environ['filebrowser.resolved'] = True wsgilib.path_info_pop(environ) return parser(environ, start_response) - environ['browser.filepath'] = urllib.unquote(environ['PATH_INFO']) - vars = dict(wsgilib.parse_querystring(environ)) - action = (vars.get('action') or ['index'])[0] - servlet_fn = os.path.join(os.path.dirname(__file__), action + '.py') - servlet_app = urlparser.make_py(environ, servlet_fn) - return servlet_app(environ, start_response) + path = urllib.unquote(environ['PATH_INFO']) + path_servlet = context.path(path) + return path_servlet(environ, start_response) diff --git a/examples/filebrowser/web/download.py b/examples/filebrowser/web/download.py deleted file mode 100644 index a18ebf6..0000000 --- a/examples/filebrowser/web/download.py +++ /dev/null @@ -1,20 +0,0 @@ -import mimetypes -import re -from filebrowser.sitepage import SitePage - -bad_regexes = [ - re.compile(r'<script.*?</script>', re.I+re.S), - re.compile(r'style="[^"]*position:.*?"', re.I+re.S), - ] - -class download(SitePage): - - def setup(self): - mime, _ = mimetypes.guess_type(str(self.path)) - self.set_header('Content-type', mime) - self.view = None - content = self.path.read() - if self.fields.get('html') == 'clean': - for bad_regex in bad_regexes: - content = bad_regex.sub('', content) - self.write(content) diff --git a/examples/filebrowser/web/index.py b/examples/filebrowser/web/index.py deleted file mode 100644 index 2a80712..0000000 --- a/examples/filebrowser/web/index.py +++ /dev/null @@ -1,40 +0,0 @@ -import mimetypes -from filebrowser.sitepage import SitePage - -class index(SitePage): - - def setup(self): - self.title = 'File: %s' % self.path.basename - self.options.parent = { - 'url': self.pathurl.up(), - 'name': self.pathurl.up().name() or 'root', - } - if self.path.check(dir=1): - self.setup_dir() - else: - self.setup_file() - - def setup_dir(self): - files = [] - for f in self.path.listdir(sort=True): - files.append({ - 'path': f, - 'name': f.basename, - 'url': self.pathurl[f.basename], - 'copyid': self.pathid('copy_', f), - }) - if f.check(dir=1): - files[-1]['name'] += '/' - self.options.files = files - self.view = 'directory.pt' - - def setup_file(self): - self.options.mimetype, _ = mimetypes.guess_type(str(self.path)) - mime = self.options.mimetype - self.view = 'view_file.pt' - if mime and mime.startswith('text/'): - self.options.content = self.path.read() - else: - self.options.content = None - self.options.is_image = mime and mime.startswith('image/') - self.options.use_iframe = mime == 'text/html' |
