From 94bee1ee2c1e56ceb6a94e889a62dff2b8159e36 Mon Sep 17 00:00:00 2001 From: ianb Date: Sun, 3 Jul 2005 19:59:13 +0000 Subject: 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. --- examples/filebrowser/js-lib/common.js | 2 +- examples/filebrowser/pathobj.py | 175 ++++++++++++++++++++++++++++ examples/filebrowser/server.conf | 12 +- examples/filebrowser/sitepage.py | 38 +++--- examples/filebrowser/templates/directory.pt | 2 +- examples/filebrowser/templates/edit_text.pt | 15 +++ examples/filebrowser/templates/view_file.pt | 5 +- examples/filebrowser/web/__init__.py | 18 +-- examples/filebrowser/web/download.py | 20 ---- examples/filebrowser/web/index.py | 40 ------- 10 files changed, 225 insertions(+), 102 deletions(-) create mode 100644 examples/filebrowser/pathobj.py create mode 100644 examples/filebrowser/templates/edit_text.pt delete mode 100644 examples/filebrowser/web/download.py delete mode 100644 examples/filebrowser/web/index.py (limited to 'examples') 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'', 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 '%s' % ( 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">
- + + +
+ +Edit:
+ +
+ + +
+ +
+ \ 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: name
-Mimetype:
+Mimetype:
download + | + edit +

', 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'
-- 
cgit v1.2.1