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='/';">&nbsp;&nbsp;&nbsp;</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

Reply via email to