Here is a patch that uses ajax to display a vserver's config fields and
submit the information as if to update it.
Currently, the only action is to send back what was posted as a json
object, which is then displayed in a pop-up message box.
Index: admin.js
===================================================================
--- admin.js (revision 932)
+++ admin.js (working copy)
@@ -7,17 +7,18 @@
*/
// main call for turning configNodes into form objects
-function formDisplayResponse(container, response) {
+function formDisplayResponse(container, response, fieldPrefix) {
container.innerHTML = "";
var configObject = Ext.util.JSON.decode(response.responseText);
- var configObjectDom = configObjectToDom(configObject);
+ var configObjectDom = configObjectToDom(configObject, null, fieldPrefix);
container.appendChild(configObjectDom);
}
// top call, recursable call for turning configObjects
// into form fields
-function configObjectToDom(configObject, depth) {
-
+function configObjectToDom(configObject, depth, fieldPrefix) {
+
+ fieldPrefix = fieldPrefix ? fieldPrefix : "";
// contain fields in a section
var div = document.createElement("div");
div.className = "section";
@@ -38,7 +39,7 @@
simpleKeys.sort();
for(var i = 0; i < simpleKeys.length; i++) {
var key = simpleKeys[i];
- var node = toFormField(key, configObject[key]);
+ var node = toFormField(key, configObject[key], fieldPrefix);
div.appendChild(node);
}
@@ -47,9 +48,9 @@
complexKeys.sort();
for(var i = 0; i < complexKeys.length; i++) {
var key = complexKeys[i];
-
+
// toSection will recurse back this function when child information is complex
- var node = toSection(key, configObject[key], depth);
+ var node = toSection(key, configObject[key], depth, fieldPrefix);
div.appendChild(node);
}
@@ -57,13 +58,13 @@
}
// given key and value, return a div with a label and an imput box
-// TODO: add a name to assocate with it
-function toFormField(key, value) {
+function toFormField(key, value, fieldPrefix) {
var div = document.createElement("div");
div.className = "entry";
var label = document.createElement("label");
label.appendChild(document.createTextNode(key + ": "));
- var input = toInput(value);
+ var name = fieldPrefix + '!' + key;
+ var input = toInput(value, name);
label.appendChild(input);
div.appendChild(label);
return div;
@@ -71,32 +72,34 @@
// given a value, return an imput object
// length adjusted based on current value
-// TODO: add a name to assocate with it
-function toInput(value) {
+function toInput(value, name) {
var input = document.createElement("input");
input.value = value;
input.size = value.length;
+ input.name = name;
return input;
}
// give a key, complexConfigObject, and depth
// return a header
// followed (ala configObjectToDom) by key/value inputs, then more complex sections
-function toSection(key, complexConfigObject, depth) {
+function toSection(key, complexConfigObject, depth, fieldPrefix) {
var div = document.createElement("div");
var hNumber = Math.min(depth, 6); // limit h tags to h6
var h = document.createElement("h" + hNumber);
h.appendChild(document.createTextNode(key));
var kids = complexConfigObject;
+ var sectionName = fieldPrefix + "!" + key;
if(complexConfigObject.length) {
// this is an array... [value, kids]
value = complexConfigObject[0];
- var input = toInput(value);
+ var name = sectionName;
+ var input = toInput(value, name);
h.appendChild(document.createTextNode(" "));
h.appendChild(input);
kids = complexConfigObject[1];
}
- var innerSection = configObjectToDom(kids, depth + 1);
+ var innerSection = configObjectToDom(kids, depth + 1, sectionName);
div.appendChild(h);
div.appendChild(innerSection);
@@ -108,6 +111,20 @@
container.innerHTML = response.responseText;
}
+// take a request like /json/vserver/default
+// return vserver!default
+function prefixFromPath(path) {
+ var prefix = "";
+ paths = path.split("/");
+ for(var i = 2; i < paths.length; i++) {
+ if(prefix) {
+ prefix += "!"
+ }
+ prefix += paths[i];
+ }
+ return prefix;
+}
+
// swith between JSON display and form field display
//var processResponse = simpleDisplayResponse;
var processResponse = formDisplayResponse;
@@ -115,11 +132,14 @@
Ext.onReady(function () {
function render_configuration_objects (path) {
+ var fieldPrefix = prefixFromPath(path)
function onSuccess (response) {
var targetElement = document.getElementById("vserver-options");
// display config options for selected vserver
- processResponse(targetElement, response);
+ processResponse(targetElement, response, fieldPrefix);
+ enableSubmit();
}
+ disableSubmit();
var connection = new Ext.data.Connection();
connection.request({
url: path,
@@ -127,11 +147,11 @@
});
}
- function change_vserver (grid, rowIndex, columnIndex) {
+ function change_vserver(grid, rowIndex, columnIndex) {
var record = grid.getDataSource().getAt(rowIndex);
var vserver = record.data.vserver.toString();
- render_configuration_objects("/config/vserver/" + vserver);
+ render_configuration_objects("/json/vserver/" + vserver);
}
var recordDefinition = Ext.data.Record.create ([{name: 'vserver'}]);
@@ -139,23 +159,101 @@
var vserverReader = new Ext.data.JsonReader ({root: "vservers"}, recordDefinition);
var ds = new Ext.data.Store({
- proxy: new Ext.data.HttpProxy ({url: "/config/vserver/?widget=grid"}),
- reader: vserverReader,
+ proxy: new Ext.data.HttpProxy ({url: "/json/vserver/?widget=grid"}),
+ reader: vserverReader
});
ds.load();
- var colModel = new Ext.grid.ColumnModel ([{id: 'name', header: "Virtual Server", width: 160, sortable: false, locked:false, dataIndex: 'vserver'}]);
+ var colModel = new Ext.grid.ColumnModel([{id: 'name', header: "Virtual Server", width: 160, sortable: false, locked:false, dataIndex: 'vserver'}]);
- // create the Grid
- var grid = new Ext.grid.Grid ('vserver-list', {
+ // create the Grid
+ var grid = new Ext.grid.Grid('vserver-list', {
ds: ds,
cm: colModel
});
+ grid.on('cellclick', change_vserver);
+ grid.render();
+ grid.getSelectionModel().selectFirstRow();
+
+});
- grid.on ('cellclick', change_vserver);
+function disableSubmit() {
+ document.getElementById("commit").setAttribute("disabled", "disabled", true);
+}
- grid.render ();
- grid.getSelectionModel ().selectFirstRow ();
-});
+function enableSubmit() {
+ document.getElementById("commit").removeAttribute("disabled");
+}
+// gathers all the key value pairs for the selected vserver
+// then (for now, at least) displays results
+function postChanges() {
+ function onPostSuccess (response) {
+ var receivedKeyValues = Ext.util.JSON.decode(response.responseText);
+ showDialog (receivedKeyValues);
+ enableSubmit();
+ }
+ disableSubmit();
+ var keyValues = {}
+ var inputs = document.getElementById("vserver-options").getElementsByTagName("input");
+ for(var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if(input.name) {
+ keyValues[input.name] = input.value;
+ }
+ }
+
+ var connection = new Ext.data.Connection();
+ connection.request({
+ url: "/json/vserver",
+ params: keyValues,
+ success: onPostSuccess
+ });
+}
+
+var dialog;
+// shows a Ext dialog, displaying the keyValue pairs
+function showDialog (keyValues) {
+ if(!dialog){ // lazy initialize the dialog and only create it once
+ dialog = new Ext.LayoutDialog("response-confirm", {
+ modal:true,
+ width:600,
+ height:400,
+ shadow:true,
+ minWidth:300,
+ minHeight:300,
+ proxyDrag: true,
+ center: {
+ autoScroll:true,
+ tabPosition: 'top',
+ closeOnTab: true,
+ alwaysShowTabs: true
+ }
+ });
+ dialog.addKeyListener(27, dialog.hide, dialog);
+ dialog.addButton('Close', dialog.hide, dialog);
+
+ var layout = dialog.getLayout();
+ layout.beginUpdate();
+ layout.add('center', new Ext.ContentPanel('response-confirm-panel', {title: 'Received Change Request'}));
+ layout.endUpdate();
+ }
+ var content = document.getElementById('response-confirm-panel');
+ content.innerHTML = "";
+ var ul = document.createElement("ul");
+ ul.style.textAlign = "left";
+ var keys = [];
+ for(var key in keyValues) {
+ keys[keys.length] = key;
+ }
+ keys.sort();
+ for(var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var li = document.createElement("li");
+ li.appendChild(document.createTextNode(key + " = " + keyValues[key]));
+ ul.appendChild(li);
+ }
+ content.appendChild(ul);
+ dialog.show();
+}
Index: admin.html
===================================================================
--- admin.html (revision 932)
+++ admin.html (working copy)
@@ -1,16 +1,55 @@
<html>
<head>
+<link href="http://cherokee-project.com/themes/cherokee/cherokee.css" rel="stylesheet" type="text/css" media="all" />
<link rel="stylesheet" type="text/css" href="js/resources/css/ext-all.css" />
<script text="text/javascript" src="js/adapter/ext/ext-base.js"></script>
<script text="text/javascript" src="js/ext-all.js"></script>
<script type="text/javascript" src="admin.js"></script>
<style>
.section { padding: 0 0 0 1em; }
+ #vserver-list-wrapper { width: 180px; float: left; }
+ #vserver-list { margin-bottom: 2em; }
+ #vserver-options { float: left; }
</style>
</head>
-<body>
+<body id="cherokee_body">
+ <div id="container">
+ <table id="container-table" >
+ <tr>
+ <td id="container-logo" onclick="window.location='/';"> </td>
+ <td id="container-nav">
+ <ul id="nav">
+ <li><a href="/">Configuration Home</a></li>
+ <li><a href="http://cherokee-project.com/">Project</a></li>
+ <li><a href="http://cherokee-project.com/doc">Documentation</a></li>
+ <li><a href="http://cherokee-project.com/branding">Logos</a></li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <br />
+
+ <div id="main">
+
+<form>
+<div id="vserver-list-wrapper">
+<div id="vserver-list"></div>
+<button onclick="postChanges();" id="commit" disabled="disabled">Commit Changes</button>
+</div>
+<div id="vserver-options"></div>
+</form>
-<div id="vserver-list" style="width: 180px;"></div>
-<div id="vserver-options"></div>
+ </div> <!-- #main -->
+</div> <!-- #container -->
+
+<div id="response-confirm" style="visibility:hidden;">
+ <div class="x-dlg-hd">Changes Requert Successful</div>
+
+ <div class="x-dlg-bd">
+ <div id="response-confirm-panel" class="x-layout-inactive-content" style="padding:10px;">
+ </div>
+ </div>
+</div>
+
</body>
</html>
Index: server.py
===================================================================
--- server.py (revision 932)
+++ server.py (working copy)
@@ -2,15 +2,17 @@
from pyscgi.pyscgi import ServerFactory, SCGIHandler
from cherokeeconf import Config
+from urllib import unquote
DEFAULT_PORT = 4000
CONFIG_FILE = "admin_test.conf"
ADMIN_TEMPLATE = "admin.html"
+CONTENT_TYPES = {'js': 'text/javascript', 'css': 'text/css', 'png': 'image/png'}
def strip_slashes (uri):
if uri[0] == '/':
uri = uri[1:]
- if uri[-1] == '/':
+ if uri and uri[-1] == '/':
uri = uri[:-1]
return uri
@@ -18,22 +20,62 @@
def __init__ (self, request, client_address, server):
SCGIHandler.__init__ (self, request, client_address, server)
+ def _content_type(self, request_uri):
+ """Return context type based on file extension, defaulting to text/html"""
+ ext = request_uri.split('.')[-1]
+ try:
+ return CONTENT_TYPES[ext]
+ except:
+ return "text/html"
+
def handle_request (self):
- self.wfile.write('Content-Type: text/html\r\n\r\n')
+ """Main Request/Post Handler Method"""
+ request_uri = self.env['REQUEST_URI']
+ self.wfile.write('Content-Type: %s\r\n\r\n' % (self._content_type(request_uri)))
self.config = Config(CONFIG_FILE)
+
+ # process any posted changes
+ self._process_post()
+
+ if request_uri.startswith("/json"):
+ if self.post:
+ self._json_response()
+ else:
+ self._json_request(request_uri)
+ return
- request_uri = self.env['REQUEST_URI']
+ # By default, it's an admin interface request
+ self._admin_request(request_uri)
- if request_uri.startswith("/config"):
- self._config_request(request_uri)
- return
+ def _process_post(self):
+ """If values are sent via a post, process them
- # By deafault, it's an admin interface request
- self._admin_request(request_uri)
+ Currently, they are simply displayed above the main content
+ """
+ self.handle_post()
- def _config_request(self, request_uri):
+ if self.post:
+ post_info = {}
+ keys = []
+ for keyvalue in self.post.split("&"):
+ keyvalue = keyvalue.split("=")
+ key, value = unquote(keyvalue[0]), unquote(keyvalue[1].replace("+", " "))
+ keys.append(key)
+ post_info[key] = value
+
+ keys.sort()
+ view_results = []
+ last_key = keys and keys[0]
+ for key in keys:
+ view_results.append("%s = %s" % (key, post_info[key]))
+
+ self.post_info = post_info
+ self.post_info_display = "\n".join(view_results)
+
+
+ def _json_request(self, request_uri):
"""Configuration request handler."""
if "?" in request_uri:
@@ -47,7 +89,7 @@
request_options = self._parse_options(request_options)
request_uri_elements = request_path.split("/")
- config_path = request_uri_elements[1:] # Override the /config/ element
+ config_path = request_uri_elements[1:] # Override the /json/ element
config_node = self.config.root
@@ -72,10 +114,17 @@
if config_node:
self._node_as_json(config_node)
+
+ def _json_response(self):
+ """Response to a json post"""
+
+ self.wfile.write(str(self.post_info))
+
def _admin_request(self, request_uri):
"""Javascript or CSS content request handler."""
- if request_uri.endswith(".js") or request_uri.endswith(".css"):
+ ext = request_uri.split('.')[-1]
+ if CONTENT_TYPES.has_key(ext):
content = open(request_uri[1:])
self.wfile.write(content.read())
content.close()
_______________________________________________
Cherokee mailing list
[email protected]
http://cherokee-project.com/cgi-bin/mailman/listinfo/cherokee