changeset 78:3779ad0ba9c7

Merge with 9c66c0ab395c1e729d548a57389888f8b279b2ad
author casties
date Thu, 25 Nov 2010 14:08:44 +0100
parents 825a92d0ab10 (diff) 9c66c0ab395c (current diff)
children a0a715552e3a
files RestDbGisApi.py RestDbInterface.py
diffstat 20 files changed, 242 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Thu Nov 25 14:08:44 2010 +0100
@@ -0,0 +1,7 @@
+
+syntax: regexp
+^\.settings$
+syntax: regexp
+^\.project$
+syntax: regexp
+^\.pydevproject$
\ No newline at end of file
--- a/RestDbGisApi.py	Thu Nov 25 13:37:39 2010 +0100
+++ b/RestDbGisApi.py	Thu Nov 25 14:08:44 2010 +0100
@@ -16,14 +16,6 @@
 from RestDbInterface import *
 
 
-gisToSqlTypeMap = {
-                   "text": "text",
-                   "number": "numeric",
-                   "id": "text",
-                   "gis_id": "text",
-                   "coord_lat": "numeric",
-                   "coord_lon": "numeric"
-                   }
 
 def kmlEncode(s):
     """returns string encoded for displaying in KML attribute"""
@@ -59,6 +51,15 @@
     #
     # database methods
     #
+    toSqlTypeMap = {
+                    "text": "text",
+                    "number": "numeric",
+                    "id": "text",
+                    "gis_id": "text",
+                    "coord_lat": "numeric",
+                    "coord_lon": "numeric"
+                   }
+    
     def getTableOwner(self,schema,table):
         """returns the owner of the table"""
         # what do we do with the schema?
--- a/RestDbInterface.py	Thu Nov 25 13:37:39 2010 +0100
+++ b/RestDbInterface.py	Thu Nov 25 14:08:44 2010 +0100
@@ -10,19 +10,40 @@
 from Products.ZSQLExtend import ZSQLExtend
 import logging
 import re
-import psycopg2
 import json
 import time
+import psycopg2
+# make psycopg use unicode objects
+import psycopg2.extensions
+psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
+psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
 
 from zope.interface import implements
 from zope.publisher.interfaces import IPublishTraverse
 from ZPublisher.BaseRequest import DefaultPublishTraverse
-#from zope.publisher.interfaces import NotFound 
-#from zope.app import zapi 
-#from zope.component import queryMultiAdapter
-import Shared.DC.ZRDB.DA
-from Products.ZSQLMethods.SQL import SQLConnectionIDs
+
 
+def unicodify(s,alternate='latin-1'):
+    """decode str (utf-8 or latin-1 representation) into unicode object"""
+    if not s:
+        return u""
+    if isinstance(s, str):
+        try:
+            return s.decode('utf-8')
+        except:
+            return s.decode(alternate)
+    else:
+        return s
+
+def utf8ify(s):
+    """encode unicode object or string into byte string in utf-8 representation.
+       assumes string objects to be utf-8"""
+    if not s:
+        return ""
+    if isinstance(s, str):
+        return s
+    else:
+        return s.encode('utf-8')
 
 def getTextFromNode(node):
     """get the cdata content of a XML node"""
@@ -52,14 +73,6 @@
     
     return s
 
-gisToSqlTypeMap = {
-                   "text": "text",
-                   "number": "numeric",
-                   "id": "text",
-                   "gis_id": "text",
-                   "coord_lat": "numeric",
-                   "coord_lon": "numeric"
-                   }
 
 class RestDbInterface(Folder):
     """Object for RESTful database queries
@@ -445,7 +458,10 @@
                 # {name: XX, type: YY}
                 name = sqlName(f['name'])
                 type = f['type']
-                sqltype = gisToSqlTypeMap[type]
+                if hasattr(self, 'toSqlTypeMap'):
+                    sqltype = self.toSqlTypeMap[type]
+                else:
+                    sqltype = 'text'
             
             else:
                 # name only
--- a/RestDbJsonStore.py	Thu Nov 25 13:37:39 2010 +0100
+++ b/RestDbJsonStore.py	Thu Nov 25 14:08:44 2010 +0100
@@ -199,12 +199,14 @@
             items = self.getListOfItemsAndValues(schema, table, tag, type)
             # items contain JSON-strings
             its = ",".join(['{"key":"%s","val":%s}'%(i[0],i[1]) for i in items])
-            its = "["+its+"]"
+            its = "[%s]"%its
             if resultFormat == 'JSON':
                 self.REQUEST.RESPONSE.setHeader("Content-Type", "application/json")
-                self.REQUEST.RESPONSE.write(its)       
+                # we assume utf-8
+                self.REQUEST.RESPONSE.write(utf8ify(its))       
             else:
-                self.REQUEST.RESPONSE.write(its)       
+                # we assume utf-8
+                self.REQUEST.RESPONSE.write(utf8ify(its))       
 
         else:
             items = self.getListOfItems(schema, table, tag, type)
--- a/gis_gui/blocks/home.html.pt	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/home.html.pt	Thu Nov 25 14:08:44 2010 +0100
@@ -169,6 +169,15 @@
       <input type="button" value="Open" class="open_open_layer"/> 
     </div>
 
+    <div class="subscreen open_map" style="display:none">
+      <select size="8">
+        <option> loading... </option>
+      </select> <br/>
+      <!-- <input type="text" value="Filter" style="color: grey;"/>
+      <br/><br/> -->
+      <input type="button" value="Open" class="open_open_map"/> 
+    </div>
+
  </tal:block>
   
 </tal:block>
\ No newline at end of file
--- a/gis_gui/blocks/home.js	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/home.js	Thu Nov 25 14:08:44 2010 +0100
@@ -58,9 +58,31 @@
     });
     
     homeblock.find(".open_map_button").live("click", function(){
-    	clearAllTasks();
-    	foldBlock($(this).parents().filter(".block"));
-    	addBlock("blocks/map?title=map","map");
+        homeblock.find(".open_map_button").addClass("selected");
+        // load list of maps 
+        var el = homeblock.find(".subscreen.open_map select").get(0);
+        blocks.loadListOfItems("test1","map",function(data) {
+            console.debug("open map list of items=",data);
+            $(el).empty();
+            for (var i=0; i < data.length; i++) {
+                var key = data[i].key;
+                var val = data[i].val;
+                var name = val.name;
+                console.debug("map",i," key=",key," value=",name);
+                $(el).append('<option value="'+key+'">'+name+'</option>');
+            }
+            // arm load map button
+            homeblock.find(".open_open_map").unbind("click");
+            homeblock.find(".open_open_map").click(function() {
+                clearAllTasks();
+                foldBlock($(this).parents().filter(".block"));
+                var mapname = $(this).parent().find("select").val();
+                var params = jQuery.grep(data, function(x){return (x.key == mapname);})[0].val;
+                console.debug("open map=",mapname," params=",params);
+                var newblock = blocks.addMapBlock("blocks/map.html?table="+escape(params.table),"map", params);
+            });
+        });
+        homeblock.find(".subscreen.open_map").fadeIn();
     });
 
     // open subtasks
--- a/gis_gui/blocks/layer.js	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/layer.js	Thu Nov 25 14:08:44 2010 +0100
@@ -1,7 +1,7 @@
 
 guiBlocks.prototype.addLayerBlock = function(url, baseId, params) {
     if (typeof(params) == "string") {
-        // then its table name
+        // then its the table name
         params = {"type": "layer", "table": params};
     }
     var block = this.addBlock(url, baseId, params, function() {
@@ -82,7 +82,7 @@
         // create map button
         $(be).find(".map_create_button").click(function() {
             console.debug("layer create map. this=",this," block=",block);
-            var mapblock = blocks.addMapBlock("blocks/map.html?layer="+escape(params.name),"map",params);
+            var mapblock = blocks.addMapBlock("blocks/map.html?layer="+escape(params.name),"map",null,params);
         });
         
     });
@@ -90,10 +90,10 @@
 
 guiBlocks.Block.prototype.readLayerParams = function(elem,params) {
     // read layer parameters from html element and store in param
-    if (typeof(elem) == 'undefined') {
+    if (elem == null) {
         elem = this.element;
     }
-    if (typeof(param) == 'undefined') {
+    if (param == null) {
         params = this.params;
     }
     params.name = $(elem).find(".layer_name").val();
@@ -126,7 +126,7 @@
             // what to do?
             return;
         }
-        if (typeof(google) != "undefined") {
+        if (google != null) {
             var latlng = new google.maps.LatLng(29, 116);
             var mapOpts = {
                     zoom: 5,
--- a/gis_gui/blocks/map.html.pt	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/map.html.pt	Thu Nov 25 14:08:44 2010 +0100
@@ -54,12 +54,13 @@
       </table>
     <!--  Dialog for create table -->
     <div class="dialog add_layer_select" title="Select Layer" style="display:none">
-      <p>Select layer to add to map:<br>
-      <form action="javascript:null">
+      <form action="javascript:false;">
+        <p>Select layer to add to map:<br/>
         <select size="5" name="add_layer_select" class="add_layer_select">
           <option>[loading...]</option>
         </select>
-      </p>
+        </p>
+      </form>
     </div>
 
     </tal:block>
--- a/gis_gui/blocks/map.js	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/map.js	Thu Nov 25 14:08:44 2010 +0100
@@ -1,23 +1,27 @@
 
-guiBlocks.prototype.addMapBlock = function(url, baseId, firstLayer) {
-    var block = this.addBlock(url, baseId, function() {
+guiBlocks.prototype.addMapBlock = function(url, baseId, params, firstLayer) {
+    var block = this.addBlock(url, baseId, params, function() {
         // function after add block finished
+        console.debug("in mapblock after block load!");
         var be = block.element;
-        console.debug("in mapblock after block load!");
-        block.params.map_layers = [];
-        if (typeof(firstLayer) != "undefined") {
+        params = block.params;
+        block.type = "map";
+        params.type = "map";
+        if (params.map_layers == null) {
+            params.map_layers = [];
+        }
+        if (firstLayer != null) {
             block.appendMapLayer(firstLayer);
         }
-        
+        // show block
         block.showMapParams();
-        // show block
         $(be).fadeIn();
 
         // save map params in block.param -- too early
         //params = block.readLayerParams();
         
         // load map
-        block.updateMap(be,block.params);
+        block.updateMap(be,params);
         
         // add layer button
         $(be).find(".layer_add_button").click(function() {
@@ -26,17 +30,26 @@
             block.addMapLayer(be,params);
         });
         // update button
-        $(be).find(".map_update_button").click(function() {
+        $(be).find(".map_update_button").click(function () {
             console.debug("map update");
             params = block.readMapParams();
             block.updateMap(be,params);
         });
+        // save button
+        $(be).find(".map_save_button").click(function () {
+            console.debug("map save. this=",this," block=",block);
+            params = block.readMapParams(be,params);
+            block.storeBlock("test1");
+        });
     });
 };
 
-guiBlocks.Block.prototype.appendMapLayer = function(layerparams) {
+guiBlocks.Block.prototype.appendMapLayer = function (layerparams) {
     // add layer to this map
-    var layerinfo = {display: true, layer:layerparams};
+    var layerinfo = {
+        display: true, 
+        layer: layerparams
+        };
     var layers = this.params.map_layers;
     layers.push(layerinfo);
     // update indexes
@@ -46,30 +59,63 @@
     return this.params;
 };
 
-guiBlocks.Block.prototype.showMapParams = function(elem,params) {
+guiBlocks.Block.prototype.deleteMapLayer = function (idx) {
+    // remove layer from this map
+    var layers = this.params.map_layers;
+    layers.splice(idx,1);
+    // update indexes
+    for (var i = 0; i < layers.length; i++) {
+        layers[i].idx = i;
+    }
+    return this.params;
+};
+
+guiBlocks.Block.prototype.showMapParams = function (elem, params) {
     // create html with layer parameters from params
-    if (typeof(elem) == 'undefined') {
+    var i, layers, $tmpl, lp, idx;
+    var block = this;
+    if (elem == null) {
         elem = this.element;
     }
-    if (typeof(params) == 'undefined') {
+    if (params == null) {
         params = this.params;
     }
-    var layers = params.map_layers;
+    var $elem = $(elem);
+    $elem.find(".map_name").val(params.name);
+    layers = params.map_layers;
     // remove old info
     $(elem).find(".map_layer_table .layer_info").remove();
     // write new info
     if (layers.length > 0) {
-        var tmpl = $(elem).find(".layer_info_tmpl").tmpl(layers);
-        $(elem).find(".map_layer_header").after(tmpl);
+        $tmpl = $elem.find(".layer_info_tmpl").tmpl(layers);
+        $elem.find(".map_layer_header").after($tmpl);
+        // arm edit and delete buttons
+        for (i = 0; i < layers.length; ++i) {
+            lp = layers[i].layer;
+            idx = layers[i].idx;
+            console.debug("showmapparams lp=", lp, " idx=", idx);
+            $tmpl.find(".layer_edit_"+idx).click(function () {
+                var param = lp;
+                console.debug("open layer ",idx);
+                blocks.addLayerBlock("blocks/layer.html?table="+escape(lp.table),"layer", param);
+            });
+            $tmpl.find(".layer_delete_"+idx).click(function () {
+                var layerIdx = idx;
+                console.debug("del layer ",layerIdx);
+                params = block.deleteMapLayer(layerIdx);
+                block.showMapParams(elem, params);
+                block.updateMap(elem, params);
+            });
+        }
     }
 };
 
 guiBlocks.Block.prototype.readMapParams = function(elem,params) {
     // read layer parameters from html element and store in param
-    if (typeof(elem) == 'undefined') {
+    if (elem == null) {
         elem = this.element;
     }
-    if (typeof(params) == 'undefined') {
+    if (params == null) {
         params = this.params;
     }
     params.name = $(elem).find(".map_name").val();
@@ -83,16 +129,16 @@
 };
 
 guiBlocks.Block.prototype.updateMap = function(elem,map_params) {
-    if (! elem != null) {
+    if (elem == null) {
         elem = this.element;
     }
-    if (! map_params != null) {
+    if (map_params == null) {
         map_params = this.params;
     }
     // update and draw the google map
     var le = $(elem).find(".map_box").get(0);
     // create empty map
-    if (typeof(google) != "undefined") {
+    if (google != null) {
         var latlng = new google.maps.LatLng(29, 116);
         var mapOpts = {
                 zoom: 5,
@@ -117,7 +163,7 @@
             paramstr += "&connect_line="+params.connect_line;
         }
         var layerurl = "../db/RESTdb/db/public/" + escape(params.table) + "?" + paramstr;
-        jQuery.get(layerurl, function(data, textStatus, XMLHttpRequest) {
+        jQuery.get(layerurl, function (data, textStatus, XMLHttpRequest) {
             // function after load kml url finished
             console.debug("kml url loaded! this=", this, " data=", data, " ("+typeof(data)+")");
             if (typeof(data) == "string") {
@@ -127,7 +173,7 @@
                 return;
             }
             // render layer
-            if (typeof(google) != "undefined") {
+            if (google != null) {
                 var newMarker=new google.maps.KmlLayer(kmlURL);
                 console.debug("new marker layer=",newMarker," map=",map);
                 newMarker.setMap(map);
--- a/gis_gui/blocks/table.js	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/blocks/table.js	Thu Nov 25 14:08:44 2010 +0100
@@ -1,22 +1,27 @@
 
-guiBlocks.prototype.addTableBlock = function(url, baseId, tablename) {
-    var block = this.addBlock(url, baseId, function() {
+guiBlocks.prototype.addTableBlock = function (url, baseId, params) {
+    if (typeof(params) === "string") {
+        // then its the table name
+        params = {"type": "table", "table": params};
+    }
+    var block = this.addBlock(url, baseId, params, function () {
         // function after add block finished
         var id = block.id;
-        var tbl = $(block.element).find(".datatable");
+        var tablename = params.table;
+        var $tbl = $(block.element).find(".datatable");
         console.debug("in tableblock after block load!");
         console.debug("this=", this);
         console.debug("block=",block);
-        console.debug(tbl);
-        $(tbl).load(
+        console.debug($tbl);
+        $tbl.load(
             "../db/RESTdb/db/public/" + tablename + "?format=HTML&layout=table&element_id=newtable #newtable>*",
-            function() {
+            function () {
                 // function after load table finished
                 console.debug("In table load! this=");
                 console.debug(this);
-                $(tbl).find("td").addClass('compacted');
+                $tbl.find("td").addClass('compacted');
                 // Add DataTables functionality
-                $(tbl).dataTable( {
+                $tbl.dataTable( {
                     'bPaginate' : true,
                     'sPaginationType' : 'full_numbers'
                 });
@@ -27,9 +32,9 @@
 };
 
     
-guiBlocks.prototype.checkTableSubmitFn = function(oldsegment) {
+guiBlocks.prototype.checkTableSubmitFn = function (oldsegment) {
     // we need oldsegment in the returned function
-    return function() {
+    return function () {
         var tablename = $(this).find(".create_table_name").val();
         console.debug("check table="+tablename," this=",this);
         var uploadUrl = "../db/RESTdb/db/public/" + tablename;
@@ -38,13 +43,13 @@
         $(this).find("input[type=hidden][name=format]").remove();
         $(this).prepend('<input type="hidden" name="format" value="JSONHTML"/>');
         //foldBlock(oldsegment);
-    }
-}
+    };
+};
 
-guiBlocks.prototype.checkTableCompleteFn = function(oldsegment,uploadForm) {
+guiBlocks.prototype.checkTableCompleteFn = function (oldsegment,uploadForm) {
     // we need the blocks object in the returned function
     var gui_blocks = this;
-    return function(result) {
+    return function (result) {
         console.debug("check complete. this=", this, " result=",result);
         // json inside pre tag
         var txtres = $(result).text(); 
@@ -74,8 +79,8 @@
             foldBlock(oldsegment);
             gui_blocks.addTableStructureBlock("blocks/tablestructure.html?table="+escape(tablename),"tablestruct", tablename, res.fields, uploadForm);
         }
-    }
-}
+    };
+};
 
 guiBlocks.prototype.uploadTableSubmitFn = function(oldsegment,fields) {
     // we need oldsegment in the returned function
@@ -92,13 +97,13 @@
         $(this).find("input[type=hidden][name=create_table_fields]").remove();
         var fieldsStr = "";
         for (var i = 0; i < fields.length; i++) {
-            if (i > 0) { fieldsStr += ","}
+            if (i > 0) { fieldsStr += ",";}
             fieldsStr += fields[i].name + ":" + fields[i].type;
         }
         $(this).prepend('<input type="hidden" name="create_table_fields" value="'+fieldsStr+'"/>');
         foldBlock(oldsegment);
-    }
-}
+    };
+};
 
 /* function that returns an upload complete function.
  */
@@ -114,8 +119,8 @@
         var tablename = res.tablename;
         // open new table block
         var newblock = gui_blocks.addTableBlock("blocks/table.html?table="+escape(tablename),"table", tablename);
-    }
-}
+    };
+};
 
 // Show Tooltip
 $("td.compacted").livequery(function() {
--- a/gis_gui/head.pt	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/head.pt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,3 +1,4 @@
 <div class="head">
+<span style="float:right" tal:content="python:'logged in as %s'%request.get('AUTHENTICATED_USER','')"/>
 <h1> ChinaGIS </h1>
 </div>
\ No newline at end of file
--- a/gis_gui/lib/blocks.js	Thu Nov 25 13:37:39 2010 +0100
+++ b/gis_gui/lib/blocks.js	Thu Nov 25 14:08:44 2010 +0100
@@ -11,9 +11,21 @@
 
 function defined(x) {
     // returns if x is defined
-    return (typeof arguments[0] != "undefined");
+    return (typeof arguments[0] !== "undefined");
 }
 
+function randomString() {
+    var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
+    var string_length = 8;
+    var randomstring = '';
+    for (var i=0; i<string_length; i++) {
+        var rnum = Math.floor(Math.random() * chars.length);
+        randomstring += chars.substring(rnum,rnum+1);
+    }
+    return randomstring;
+}
+
+
 /* 
  *  guiBlocks base
  */
@@ -38,13 +50,9 @@
 
 // create a new block and add it to the current workspaces stack
 guiBlocks.prototype.addBlock = function(url, baseId, params, whenDone) {
-    // old version compatibility
-    if (typeof(params) == "function") {
-        whenDone = params;
-    }
 	// add title to url
     var newblock = $("<div>Block loading...</div>");
-	var newid=baseId+"_"+randomString();
+	var newid = baseId+"_"+randomString();
 	var container = this.containerElement;
 	console.debug("addblock newid="+newid);
     newblock.hide();
@@ -56,8 +64,8 @@
     	$(this).appendTo(container);
     	// chain done function
         if (typeof(whenDone) == "function") {
-        	// chain whenDone
-        	whenDone();
+        	// chain whenDone (should we add parameters?)
+        	whenDone.apply(this);
         } else {
         	// default after load function
         	$(this).fadeIn();
@@ -67,12 +75,9 @@
     // create new Block object
     var block = new guiBlocks.Block(newid, newblock);
     // add parameters
-    if (typeof(params) != "object") {
+    if (params == null) {
         // create new params
         params = {"id": newid};
-    } else if (typeof(params.id) == "undefined") {
-        // must be new block (with params)
-        params.id = newid;
     }
     block.params = params;
     // add to list of blocks
@@ -123,7 +128,7 @@
 
 // collapse the block so that only its titlebar is visible
 function foldBlock(segment){
-    if (! segment) {
+    if (segment == null) {
         return;
     }
 	if(segment.hasClass("folded")){
@@ -164,16 +169,3 @@
       });
 
 }
-
-
-function randomString() {
-	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
-	var string_length = 8;
-	var randomstring = '';
-	for (var i=0; i<string_length; i++) {
-		var rnum = Math.floor(Math.random() * chars.length);
-		randomstring += chars.substring(rnum,rnum+1);
-	}
-	return randomstring;
-}
-
--- a/zpt/GIS_schema_table.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/GIS_schema_table.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,5 +1,7 @@
-<!DOCTYPE html>
-<html tal:define="root here/getRestDbUrl;
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="root here/getRestDbUrl;
         schema options/schema; table options/table;
         kmlUrl python:here.getLiveKmlUrl(schema=schema,table=table);
 ">
--- a/zpt/HTML_index.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/HTML_index.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
   data here/getListOfSchemas">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">
--- a/zpt/HTML_schema.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/HTML_schema.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
   schema options/schema; data python:here.getListOfTables(schema)">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">
--- a/zpt/HTML_schema_table.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/HTML_schema_table.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None); 
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None); 
     schema options/schema; table options/table; data python:here.getTable(schema=schema,table=table);
     fields data/fields; rows data/rows;">
   <head>
--- a/zpt/HTML_schema_usertables.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/HTML_schema_usertables.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','table'); element_id python:request.get('element_id',None);
   schema python:options.get('schema','public'); data python:here.getListOfUserTables(schema)">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">
--- a/zpt/JSONHTML_index.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/JSONHTML_index.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None);
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None);
   data here/getListOfSchemas">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">
--- a/zpt/JSONHTML_schema.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/JSONHTML_schema.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None);
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None);
   schema options/schema; data python:here.getListOfTables(schema)">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">
--- a/zpt/JSONHTML_schema_table.zpt	Thu Nov 25 13:37:39 2010 +0100
+++ b/zpt/JSONHTML_schema_table.zpt	Thu Nov 25 14:08:44 2010 +0100
@@ -1,4 +1,7 @@
-<html tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None)
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+  tal:define="layout python:request.get('layout','pre'); element_id python:request.get('element_id',None)
     schema options/schema; table options/table; data python:here.getTable(schema=schema,table=table);">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=utf-8">