Add the legacy frontend themes, scripts, and plugin assets required by the main SPOTA interfaces.
271 lines
10 KiB
Python
271 lines
10 KiB
Python
"""
|
|
This sample web server demonstrates how to implement a web service for
|
|
Dynatree requests.
|
|
|
|
As data source, we choose a folder in the local file system, simply
|
|
because it is hierarchical and has a concept of documents (files) and
|
|
folders (directories) and because it was easy to implement.
|
|
A typical web service would of course read the data from a 'real' source
|
|
like an SQL database or XML file, but I hope you get the idea ;-)
|
|
|
|
See: http://dynatree.googlecode.com
|
|
|
|
Martin Wendt, 2009-2011
|
|
|
|
Usage:
|
|
1. Python 2.5 or later is required to run this server.
|
|
For Python 2.5 the simplejson module must also be installed;
|
|
Python 2.6 has built in json support.
|
|
2. Configure the rootPath variable in the main() function at the bottom
|
|
of this module.
|
|
3. Run this module:
|
|
> python dynatree_server.py
|
|
|
|
Optionally pass a root folder:
|
|
> python dynatree_server.py c:\temp
|
|
|
|
This module
|
|
- Is a standalone web server that answers URLs beginning with
|
|
http://127.0.0.1:8001/?...
|
|
with JSON responses that conform to the Dynatree spec.
|
|
However, we don't do error checking or anything else that would be
|
|
required for production environments.
|
|
|
|
- Answers requests to initialize a tree using the 'initAjax: {}' option:
|
|
http://127.0.0.1:8001/?mode=baseFolders
|
|
with a list of files/directories in the configured root directory.
|
|
|
|
- Answers requests to lazy-load node children using 'appendAjax({...})':
|
|
http://127.0.0.1:8001/?key=_25c2b6d6
|
|
with a list of files/directories in the directory that matches this key.
|
|
|
|
- Supports Dynatree's 'lazy persistence':
|
|
http://127.0.0.1:8001/?mode=baseFolder&expandedKeyList=_41771df2%2C_4230fb68%2C...
|
|
will return not only the base entries, but also all children inside
|
|
parents that are listed as expanded.
|
|
|
|
- Supports &sleep=SECONDS argument for simulating slow responses.
|
|
|
|
- Supports the JSONP protocol:
|
|
http://127.0.0.1:8001/?mode=baseFolder&callback=jsonp1241293219729
|
|
will wrap the result like this "jsonp1241293219729(<res>)".
|
|
This is only required, if this web service is not on the same host as
|
|
the web page that contains the Dynatree widget.
|
|
JSONP can be enabled for jQuery.ajax() by passing dataType: 'jsonp'
|
|
instead of 'json'.
|
|
|
|
- Dumps the POST body, if the request URL is '/submit_data'
|
|
|
|
|
|
Sample Dynatree options to use this service:
|
|
$("#tree").dynatree({
|
|
...
|
|
persist: true,
|
|
initAjax: {url: "http://127.0.0.1:8001",
|
|
dataType: "jsonp", // Enable JSONP, so this sample can be run from the local file system against a localhost server
|
|
data: {key: "",
|
|
// sleep: 3,
|
|
// depth: 2,
|
|
mode: "baseFolders"
|
|
},
|
|
addExpandedKeyList: true // Send list of expanded keys, so the webservice can deliver these children also
|
|
},
|
|
onLazyRead: function(dtnode){
|
|
dtnode.appendAjax(
|
|
{url: "http://127.0.0.1:8001",
|
|
dataType: "jsonp",
|
|
data: {key: dtnode.data.key,
|
|
mode: "branch"
|
|
}
|
|
});
|
|
}
|
|
});
|
|
"""
|
|
|
|
import cgi
|
|
import os
|
|
import sys
|
|
import time
|
|
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler
|
|
from tempfile import gettempdir
|
|
|
|
try:
|
|
import json # Available since Python 2.6
|
|
except ImportError:
|
|
import simplejson as json
|
|
|
|
#===============================================================================
|
|
# Helper functions
|
|
#===============================================================================
|
|
|
|
def _keyFromString(s):
|
|
"""Calculate a unique key for an arbitrary string.
|
|
|
|
Example: _keyFromString("c:\temp\wsgidav1\src\DAV") = "_25c2b6d6"
|
|
"""
|
|
return "_" + hex(hash(s)) [3:]
|
|
|
|
|
|
def _findFolderByKey(rootPath, key):
|
|
"""Search rootPath and all sub folders for a directory that matches the key."""
|
|
for root, dirs, files in os.walk(rootPath):
|
|
for name in dirs:
|
|
fullPath = os.path.join(root, name)
|
|
fileKey = _keyFromString(fullPath)
|
|
if key == fileKey:
|
|
return fullPath
|
|
return None
|
|
|
|
|
|
#===============================================================================
|
|
# DynaTreeWsgiApp
|
|
#===============================================================================
|
|
|
|
class DynaTreeWsgiApp(object):
|
|
"""This WSGI application serves a file system hierarchy for dynatree."""
|
|
def __init__(self, optionDict):
|
|
self.optionDict = optionDict
|
|
|
|
def __call__(self, environ, start_response):
|
|
"""Handle one HTTP request."""
|
|
|
|
# Parse URL query into a list of 2-tuples (name, value)
|
|
argList = cgi.parse_qsl(environ.get("QUERY_STRING", ""))
|
|
# Convert to dictionary {"name": "value", ... }
|
|
argDict = dict(argList)
|
|
print "Query args: %s" % argDict
|
|
|
|
# Support &sleep=SECONDS argument to simulate slow connections for debugging
|
|
if argDict.get("sleep"):
|
|
print "Sleeping %s seconds..." % argDict.get("sleep")
|
|
time.sleep(int(argDict.get("sleep")))
|
|
|
|
# Dump POST request data, if http://HOST:PORT/submit_data was requested
|
|
# print "PI", environ["PATH_INFO"]
|
|
if environ["PATH_INFO"] == "/submit_data":
|
|
print "Got /submit_data request, CONTENT_LENGTH=%r" % environ.get("CONTENT_LENGTH")
|
|
try:
|
|
length = int(environ["CONTENT_LENGTH"])
|
|
data = environ["wsgi.input"].read(length)
|
|
except:
|
|
print >>sys.stderr, "Couldn't read from wsgi.input! This can happen when using Firefox locally"
|
|
try:
|
|
data = environ["wsgi.input"].read()
|
|
except:
|
|
print
|
|
print "Data: ", data
|
|
start_response("200 OK", [("Content-Type", "text/html")])
|
|
return [ "Thanks for sending<br><pre><code>%s</code></pre>" % data ]
|
|
|
|
# Support &depth=LEVEL argument to read more than one level (1: direct children)
|
|
depth = int(argDict.get("depth", 0))
|
|
if depth > 1:
|
|
print "'depth' mode: loading %s levels" % depth
|
|
|
|
# Return empty list when '&returnEmpty' is passed
|
|
returnEmpty = "returnEmpty" in argDict
|
|
|
|
# Eval 'mode' and 'key' arguments
|
|
rootPath = self.optionDict["rootPath"]
|
|
if argDict.get("mode") == "baseFolders":
|
|
folderPath = rootPath
|
|
elif argDict.get("key"):
|
|
key = argDict.get("key")
|
|
folderPath = _findFolderByKey(rootPath, key)
|
|
if not folderPath:
|
|
raise RuntimeError("Could not find folder for key '%s'." % key)
|
|
else:
|
|
raise RuntimeError("Missing required argument '&mode=baseFolder' or '&key=...'")
|
|
|
|
# Get list of child nodes (may be recursive)
|
|
childList = [ ]
|
|
if not returnEmpty:
|
|
self.makeChildList(argDict, folderPath, childList, depth)
|
|
|
|
# Convert result list to a JSON string
|
|
res = json.dumps(childList, encoding="Latin-1")
|
|
|
|
# Support for the JSONP protocol.
|
|
if "callback" in argDict:
|
|
res = argDict["callback"] + "(" + res + ")"
|
|
|
|
# Return HTTP response
|
|
start_response("200 OK", [("Content-Type", "application/json")])
|
|
return [ res ]
|
|
|
|
|
|
def makeChildList(self, argDict, folderPath, childList, depth):
|
|
print "makeChildList(%s, depth=%s) " % (folderPath, depth)
|
|
expandedKeyList = argDict.get("expandedKeyList", "").split(",")
|
|
filenameList = os.listdir(folderPath)
|
|
for fn in filenameList:
|
|
fullPath = os.path.join(folderPath, fn)
|
|
isFolder = os.path.isdir(fullPath)
|
|
key = _keyFromString(fullPath)
|
|
try:
|
|
size = os.path.getsize(fullPath)
|
|
date = time.ctime(os.path.getmtime(fullPath))
|
|
except:
|
|
# May fail when path contains funny chars (don't care in this sample)
|
|
size = 0
|
|
date = time.ctime()
|
|
# Create a node dictionary and append it to the child list
|
|
node = {"title": fn,
|
|
"key": key,
|
|
"isFolder": isFolder,
|
|
"isLazy": isFolder,
|
|
"tooltip": "%s, %s bytes, modified: %s" % (fullPath, size, date),
|
|
}
|
|
childList.append(node)
|
|
# Support lazy persistence:
|
|
# If the current node was requested as 'expanded', load the children too
|
|
if isFolder and (key in expandedKeyList or depth > 1):
|
|
subNodes = []
|
|
self.makeChildList(argDict, fullPath, subNodes, depth-1)
|
|
node["children"] = subNodes
|
|
# node["isLazy"] = False
|
|
# node["expand"] = True
|
|
|
|
|
|
#===============================================================================
|
|
# Server
|
|
#===============================================================================
|
|
|
|
# Requires Python >= 2.5
|
|
|
|
def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):
|
|
"""Create a new WSGI server listening on 'host' and 'port' for 'app'."""
|
|
server = server_class((host, port), handler_class)
|
|
server.set_app(app)
|
|
return server
|
|
|
|
|
|
def main():
|
|
# Configure root directory that will be exported:
|
|
rootPath = gettempdir()
|
|
if len(sys.argv) > 1:
|
|
rootPath =sys.argv[1]
|
|
|
|
# rootPath = "/temp"
|
|
|
|
# Configure hostname and port on which the server will listen
|
|
# hostname = "127.0.0.1" # Use empty string for localhost (local access only)
|
|
hostname = "" # Use empty string for 0.0.0.0 (allows remote access)
|
|
port = 8001
|
|
|
|
wsgi_app = DynaTreeWsgiApp({"rootPath": rootPath})
|
|
|
|
httpd = make_server(hostname, port, wsgi_app)
|
|
|
|
sa = httpd.socket.getsockname()
|
|
|
|
print "Exporting file system at ", rootPath, " for Dynatree."
|
|
print "Serving HTTP on", sa[0], "port", sa[1], "..."
|
|
assert os.path.isdir(rootPath), "Invalid root path: '%s'" % rootPath
|
|
|
|
httpd.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|