[MERGE] reformatting of some python code, fix of JS warnings (trailing commas and missing semicolons), fix extraction of 'full' class name, removal of the Hello controller and addition of a doc skeleton for differences between the old and the new web client

bzr revid: xmo@openerp.com-20110321122922-t48omtc7ebahjhzg
This commit is contained in:
Xavier Morel 2011-03-21 13:29:22 +01:00
commit 7b92ab4328
8 changed files with 121 additions and 86 deletions

View File

@ -25,14 +25,14 @@ class Xml2Json:
@staticmethod
def convert_element(el, skip_whitespaces=True):
res = {}
if el.tag[0]=="{":
ns, name = el.tag.rsplit("}",1)
if el.tag[0] == "{":
ns, name = el.tag.rsplit("}", 1)
res["tag"] = name
res["namespace"] = ns[1:]
else:
res["tag"] = el.tag
res["attrs"] = {}
for k,v in el.items():
for k, v in el.items():
res["attrs"][k] = v
kids = []
if el.text and (not skip_whitespaces or el.text.strip() != ''):
@ -48,20 +48,6 @@ class Xml2Json:
# OpenERP Web base Controllers
#----------------------------------------------------------
class Hello(openerpweb.Controller):
_cp_path = "/base/hello"
def index(self):
return "hello world"
@openerpweb.jsonrequest
def ajax_hello_world(self,req):
return {"welcome":"hello world"}
@openerpweb.jsonrequest
def ajax_hello_error(self,req):
raise Exception("You suck")
class Session(openerpweb.Controller):
_cp_path = "/base/session"
@ -84,7 +70,7 @@ class Session(openerpweb.Controller):
files_content = []
files_timestamp = 0
for i in file_list:
fname = os.path.join(root,i)
fname = os.path.join(root, i)
ftime = os.path.getmtime(fname)
if ftime > files_timestamp:
files_timestamp = ftime
@ -96,7 +82,7 @@ class Session(openerpweb.Controller):
def login(self, req, db, login, password):
req.session.login(db, login, password)
return {
"session_id" : req.session_id,
"session_id": req.session_id,
"uid": req.session._uid,
}
@ -117,31 +103,32 @@ class Session(openerpweb.Controller):
concat = self.concat_files(files)[0]
# TODO request set the Date of last modif and Etag
return concat
css.exposed=1
css.exposed = True
def js(self, req, mods='base,base_hello'):
files = self.manifest_glob(mods.split(','), 'js')
concat = self.concat_files(files)[0]
# TODO request set the Date of last modif and Etag
return concat
js.exposed=1
js.exposed = True
class Menu(openerpweb.Controller):
_cp_path = "/base/menu"
@openerpweb.jsonrequest
def load(self,req):
def load(self, req):
m = req.session.model('ir.ui.menu')
# menus are loaded fully unlike a regular tree view, cause there are
# less than 512 items
menu_ids = m.search([])
menu_items = m.read(menu_ids,['name','sequence','parent_id'])
menu_root = {'id':False, 'name':'root', 'parent_id':[-1,'']}
menu_items = m.read(menu_ids, ['name', 'sequence', 'parent_id'])
menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']}
menu_items.append(menu_root)
# make a tree using parent_id
for i in menu_items:
i['children'] = []
d = dict([(i["id"],i) for i in menu_items])
d = dict([(i["id"], i) for i in menu_items])
for i in menu_items:
if not i['parent_id']:
pid = False
@ -151,35 +138,38 @@ class Menu(openerpweb.Controller):
d[pid]['children'].append(i)
# sort by sequence a tree using parent_id
for i in menu_items:
i['children'].sort(key = lambda x:x["sequence"])
i['children'].sort(key=lambda x:x["sequence"])
return {'data': menu_root}
@openerpweb.jsonrequest
def action(self,req,menu_id):
def action(self, req, menu_id):
m = req.session.model('ir.values')
r = m.get('action', 'tree_but_open', [('ir.ui.menu', menu_id)], False, {})
res={"action":r}
res = {"action": r}
return res
class DataSet(openerpweb.Controller):
_cp_path = "/base/dataset"
@openerpweb.jsonrequest
def fields(self,req,model):
def fields(self, req, model):
return {'fields': req.session.model(model).fields_get(False)}
@openerpweb.jsonrequest
def load(self,req,model,domain=[],fields=['id']):
def load(self, req, model, domain=[], fields=['id']):
m = req.session.model(model)
ids = m.search(domain)
values = m.read(ids, fields)
return {'ids': ids, 'values': values}
class DataRecord(openerpweb.Controller):
_cp_path = "/base/datarecord"
@openerpweb.jsonrequest
def load(self,req,model,id,fields):
def load(self, req, model, id, fields):
m = req.session.model(model)
value = {}
r = m.read([id])
@ -187,36 +177,43 @@ class DataRecord(openerpweb.Controller):
value = r[0]
return {'value': value}
class FormView(openerpweb.Controller):
_cp_path = "/base/formview"
@openerpweb.jsonrequest
def load(self,req,model,view_id):
def load(self, req, model, view_id):
m = req.session.model(model)
r = m.fields_view_get(view_id,'form')
r["arch"]=Xml2Json.convert_to_structure(r["arch"])
return {'fields_view':r}
r = m.fields_view_get(view_id, 'form')
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
return {'fields_view': r}
class ListView(openerpweb.Controller):
_cp_path = "/base/listview"
@openerpweb.jsonrequest
def load(self,req,model,view_id):
def load(self, req, model, view_id):
m = req.session.model(model)
r = m.fields_view_get(view_id,'tree')
r["arch"]=Xml2Json.convert_to_structure(r["arch"])
return {'fields_view':r}
r = m.fields_view_get(view_id, 'tree')
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
return {'fields_view': r}
class SearchView(openerpweb.Controller):
_cp_path = "/base/searchview"
@openerpweb.jsonrequest
def load(self,req,model,view_id):
def load(self, req, model, view_id):
m = req.session.model(model)
r = m.fields_view_get(view_id,'search')
r["arch"]=Xml2Json.convert_to_structure(r["arch"])
return {'fields_view':r}
r = m.fields_view_get(view_id, 'search')
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
return {'fields_view': r}
class Action(openerpweb.Controller):
_cp_path = "/base/action"
@openerpweb.jsonrequest
def load(self,req,action_id):
def load(self, req, action_id):
return {}

View File

@ -114,7 +114,7 @@ openerp.base.BasicController = Class.extend({
openerp.base.Console = openerp.base.BasicController.extend({
init: function(element_id, server, port) {
this._super(element_id);
},
}
});
openerp.base.Session = openerp.base.BasicController.extend({

View File

@ -273,9 +273,9 @@ openerp.base.FormView = openerp.base.Controller.extend({
},
on_record_loaded: function() {
for (var f in this.fields) {
this.fields[f].set_value()
this.fields[f].set_value();
}
},
}
});
openerp.base.ListView = openerp.base.Controller.extend({
@ -536,7 +536,7 @@ openerp.base.FieldChar = openerp.base.Field.extend({
},
on_change: function() {
//this.view.update_field(this.name,value);
},
}
});
openerp.base.FieldEmail = openerp.base.Field.extend({
@ -637,7 +637,7 @@ openerp.base.widgets = {
'boolean' : openerp.base.FieldBoolean,
'float' : openerp.base.FieldFloat,
'button' : openerp.base.WidgetButton
}
};
openerp.base.CalendarView = openerp.base.Controller.extend({
// Dhtmlx scheduler ?

View File

@ -7,12 +7,12 @@ openerp.base_hello = function(openerp) {
openerp.base.SearchView = openerp.base.SearchView.extend({
init:function() {
this._super.apply(this,arguments);
this.on_search.add(function(){alert('hello')});
},
this.on_search.add(function(){console.log('hello');});
}
});
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
}
};
// vim:et fdc=0 fdl=0:

View File

@ -17,6 +17,7 @@ Contents:
addons
development
project
old-version
Indices and tables
==================

View File

@ -0,0 +1,11 @@
Main differences with the 6.0 client
====================================
.. No more populate.sh, use virtualenvs
.. Logic is mainly in Javascript (had to make a choice between JS and
.. Python logic)
.. Templating language changes
.. How to port addons and modules?

View File

@ -3,6 +3,12 @@ Deploying OpenERP Web
.. After release one, add upgrade instructions if any
.. How about running the web client on alternative Python
.. implementations e.g. pypy or Jython? Since the only lib with C
.. accelerators we're using right now is SimpleJSON and it has a pure
.. Python base component, we should be able to test and deploy on
.. non-cpython no?
In-depth configuration
----------------------

View File

@ -1,7 +1,12 @@
#!/usr/bin/python
import functools
import optparse, os, re, sys, tempfile, traceback, uuid, xmlrpclib
import optparse
import os
import sys
import tempfile
import traceback
import uuid
import xmlrpclib
import cherrypy
import cherrypy.lib.static
@ -15,18 +20,23 @@ import xmlrpctimeout
class OpenERPUnboundException(Exception):
pass
class OpenERPConnector(object):
pass
class OpenERPAuth(object):
pass
class OpenERPModel(object):
def __init__(self,session,model):
def __init__(self, session, model):
self._session = session
self._model = model
def __getattr__(self,name):
return lambda *l:self._session.execute(self._model,name,*l)
def __getattr__(self, name):
return lambda *l:self._session.execute(self._model, name, *l)
class OpenERPSession(object):
def __init__(self, server='127.0.0.1', port=8069):
@ -38,7 +48,7 @@ class OpenERPSession(object):
self._password = False
def proxy(self, service):
s = xmlrpctimeout.TimeoutServerProxy('http://%s:%s/xmlrpc/%s'%(self._server, self._port, service), timeout=5)
s = xmlrpctimeout.TimeoutServerProxy('http://%s:%s/xmlrpc/%s' % (self._server, self._port, service), timeout=5)
return s
def bind(self, db, uid, password):
@ -52,14 +62,14 @@ class OpenERPSession(object):
self._login = login
return uid
def execute(self,model,func,*l,**d):
def execute(self, model, func, *l, **d):
if not (self._db and self._uid and self._password):
raise OpenERPUnboundException()
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
return r
def model(self,model):
return OpenERPModel(self,model)
def model(self, model):
return OpenERPModel(self, model)
#----------------------------------------------------------
# OpenERP Web RequestHandler
@ -82,7 +92,7 @@ class JsonRequest(object):
"""
def parse(self, request):
self.params = request.get("params",{})
self.params = request.get("params", {})
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
self.session = cherrypy.session.setdefault(self.session_id, OpenERPSession())
self.context = self.params.pop('context', None)
@ -108,7 +118,7 @@ class JsonRequest(object):
else:
request = simplejson.loads(request)
try:
print "--> %s.%s %s"%(controller.__class__.__name__,method.__name__,request)
print "--> %s.%s %s" % (controller.__class__.__name__, method.__name__, request)
error = None
result = method(controller, self, **self.parse(request))
except OpenERPUnboundException:
@ -127,7 +137,8 @@ class JsonRequest(object):
'data': {
'type': 'server_exception',
'fault_code': e.faultCode,
'debug': "Client %s\nServer %s" % ("".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
'debug': "Client %s\nServer %s" % (
"".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
}
}
except Exception:
@ -139,13 +150,13 @@ class JsonRequest(object):
'debug': "Client %s" % traceback.format_exc()
}
}
response = {"jsonrpc": "2.0", "id": request.get('id')}
response = {"jsonrpc": "2.0", "id": request.get('id')}
if error:
response["error"] = error
else:
response["result"] = result
print "<--", response
print "<--", response
print
content = simplejson.dumps(response)
@ -153,31 +164,36 @@ class JsonRequest(object):
cherrypy.response.headers['Content-Length'] = len(content)
return content
def jsonrequest(f):
@cherrypy.expose
@functools.wraps(f)
def json_handler(self):
return JsonRequest().dispatch(self, f, requestf=cherrypy.request.body)
return json_handler
class HttpRequest(object):
""" Regular GET/POST request
"""
def __init__(self):
# result may be filled, it's content will be updated by the return
# value of the dispatched function if it's a dict
self.result = ""
def dispatch(self, controller, f, request):
print "GET/POST --> %s.%s %s"%(controller.__class__.__name__,f.__name__,request)
r=f(controller, self, request)
print "GET/POST --> %s.%s %s" % (controller.__class__.__name__, f.__name__, request)
r = f(controller, self, request)
return r
def httprequest(f):
# check cleaner wrapping:
# functools.wraps(f)(lambda x: JsonRequest().dispatch(x, f))
l=lambda self, request: HttpRequest().dispatch(self, f, request)
l.exposed=1
l = lambda self, request: HttpRequest().dispatch(self, f, request)
l.exposed = 1
return l
#-----------------------------------------------------------
@ -185,7 +201,7 @@ def httprequest(f):
#-----------------------------------------------------------
path_root = os.path.dirname(os.path.dirname(os.path.normpath(__file__)))
path_addons = os.path.join(path_root,'addons')
path_addons = os.path.join(path_root, 'addons')
cherrypy_root = None
# globals might move into a pool if needed
@ -198,64 +214,68 @@ controllers_path = {}
class ControllerType(type):
def __init__(cls, name, bases, attrs):
super(ControllerType, cls).__init__(name, bases, attrs)
# TODO forgive me this hack and find me a clean way to get the absolute name of a class
cls.fullname = re.search("'(.+)'",repr(cls)).group(1)
controllers_class[cls.fullname] = cls
controllers_class["%s.%s" % (cls.__module__, cls.__name__)] = cls
class Controller(object):
__metaclass__ = ControllerType
class Root(object):
def __init__(self):
self.addons = {}
self._load_addons()
def _load_addons(self):
if path_addons not in sys.path:
sys.path.insert(0,path_addons)
sys.path.insert(0, path_addons)
for i in os.listdir(path_addons):
if i not in sys.modules:
manifest_path = os.path.join(path_addons,i,'__openerp__.py')
manifest_path = os.path.join(path_addons, i, '__openerp__.py')
if os.path.isfile(manifest_path):
manifest = eval(open(manifest_path).read())
print "Loading",i
print "Loading", i
m = __import__(i)
addons_module[i] = m
addons_manifest[i] = manifest
for k,v in controllers_class.items():
for k, v in controllers_class.items():
if k not in controllers_object:
o = v()
controllers_object[k] = o
if hasattr(o,'_cp_path'):
if hasattr(o, '_cp_path'):
controllers_path[o._cp_path] = o
def default(self, *l, **kw):
#print "default",l,kw
# handle static files
if len(l) > 2 and l[1]=='static':
if len(l) > 2 and l[1] == 'static':
# sanitize path
p = os.path.normpath(os.path.join(*l))
return cherrypy.lib.static.serve_file(os.path.join(path_addons,p))
return cherrypy.lib.static.serve_file(os.path.join(path_addons, p))
elif len(l) > 1:
for i in range(1,len(l)+1):
for i in range(1, len(l) + 1):
ps = "/" + "/".join(l[0:i])
if ps in controllers_path:
c = controllers_path[ps]
rest = l[i:] or ['index']
meth = rest[0]
m = getattr(c,meth)
if getattr(m,'exposed',0):
print "Calling",ps,c,meth,m
m = getattr(c, meth)
if getattr(m, 'exposed', 0):
print "Calling", ps, c, meth, m
return m(**kw)
else:
raise cherrypy.HTTPRedirect('/base/static/openerp/base.html', 301)
default.exposed = True
def main(argv):
# Parse config
op = optparse.OptionParser()
op.add_option("-p", "--port", dest="socket_port", help="listening port", metavar="NUMBER", default=8002)
op.add_option("-s", "--session-path", dest="storage_path", help="directory used for session storage", metavar="DIR", default=os.path.join(tempfile.gettempdir(),"cpsessions"))
op.add_option("-s", "--session-path", dest="storage_path",
help="directory used for session storage", metavar="DIR",
default=os.path.join(tempfile.gettempdir(), "cpsessions"))
(o, args) = op.parse_args(argv[1:])
# Prepare cherrypy config from options