diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index da2b345d217..2c2d990364d 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -3,4 +3,36 @@ "version" : "2.0", "depends" : [], 'active': True, + 'js' : [ + "static/lib/datejs/date-en-US.js", + "static/lib/jquery/jquery-1.6.2.js", + "static/lib/jquery.ba-bbq/jquery.ba-bbq.js", + "static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js", + "static/lib/jquery.superfish/js/hoverIntent.js", + "static/lib/jquery.superfish/js/superfish.js", + "static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js", + "static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js", + "static/lib/jquery.ui.notify/js/jquery.notify.js", + "static/lib/json/json2.js", + "static/lib/qweb/qweb2.js", + "static/lib/underscore/underscore.js", + "static/lib/underscore/underscore.string.js", + "static/src/js/base.js", + "static/src/js/chrome.js", + "static/src/js/controller.js", + "static/src/js/views.js", + "static/src/js/data.js", + "static/src/js/dates.js", + "static/src/js/form.js", + "static/src/js/list.js", + "static/src/js/list-editable.js", + "static/src/js/search.js", + "static/src/js/view_tree.js", + ], + 'css' : [ + "static/lib/jquery.superfish/css/superfish.css", + "static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css", + "static/lib/jquery.ui.notify/css/ui.notify.css", + "static/src/css/base.css", + ], } diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index e42784443a2..e90f3178377 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -54,6 +54,99 @@ class Xml2Json: # OpenERP Web base Controllers #---------------------------------------------------------- +def manifest_glob(addons, key): + files = [] + for addon in addons: + globlist = openerpweb.addons_manifest.get(addon, {}).get(key, []) + print globlist + for pattern in globlist: + for path in glob.glob(os.path.join(openerpweb.path_addons, addon, pattern)): + files.append(path[len(openerpweb.path_addons):]) + return files + +def concat_files(file_list): + """ Concatenate file content + return (concat,timestamp) + concat: concatenation of file content + timestamp: max(os.path.getmtime of file_list) + """ + root = openerpweb.path_root + files_content = [] + files_timestamp = 0 + for i in file_list: + fname = os.path.join(root, i) + ftime = os.path.getmtime(fname) + if ftime > files_timestamp: + files_timestamp = ftime + files_content = open(fname).read() + files_concat = "".join(files_content) + return files_concat + +class WebClient(openerpweb.Controller): + _cp_path = "/base/webclient" + + @openerpweb.jsonrequest + def csslist(self, req, mods='base'): + return manifest_glob(mods.split(','), 'css') + + @openerpweb.jsonrequest + def jslist(self, req, mods='base'): + return manifest_glob(mods.split(','), 'js') + + @openerpweb.httprequest + def css(self, req, mods='base'): + cherrypy.response.headers['Content-Type'] = 'text/css' + files = manifest_glob(mods.split(','), 'css') + concat = concat_files(files)[0] + # TODO request set the Date of last modif and Etag + return concat + + @openerpweb.httprequest + def js(self, req, mods='base'): + cherrypy.response.headers['Content-Type'] = 'application/javascript' + files = manifest_glob(mods.split(','), 'js') + concat = concat_files(files)[0] + # TODO request set the Date of last modif and Etag + return concat + + @openerpweb.httprequest + def home(self, req): + template =""" + + + + OpenERP + %s + + + %s + + + + + """.replace('\n'+' '*8,'\n') + + # script tags + jslist = ['/base/webclient/js'] + if 1: # debug == 1 + jslist = manifest_glob(['base'], 'js') + js = "\n ".join([''%i for i in jslist]) + + # css tags + csslist = ['/base/webclient/css'] + if 1: # debug == 1 + csslist = manifest_glob(['base'], 'css') + css = "\n ".join([''%i for i in csslist]) + r = template % (js, css) + return r + class Database(openerpweb.Controller): _cp_path = "/base/database" @@ -71,37 +164,6 @@ class Database(openerpweb.Controller): class Session(openerpweb.Controller): _cp_path = "/base/session" - def manifest_glob(self, addons, key): - files = [] - for addon in addons: - globlist = openerpweb.addons_manifest.get(addon, {}).get(key, []) - - files.extend([ - resource_path[len(openerpweb.path_addons):] - for pattern in globlist - for resource_path in glob.glob(os.path.join( - openerpweb.path_addons, addon, pattern)) - ]) - return files - - def concat_files(self, file_list): - """ Concatenate file content - return (concat,timestamp) - concat: concatenation of file content - timestamp: max(os.path.getmtime of file_list) - """ - root = openerpweb.path_root - files_content = [] - files_timestamp = 0 - for i in file_list: - fname = os.path.join(root, i) - ftime = os.path.getmtime(fname) - if ftime > files_timestamp: - files_timestamp = ftime - files_content = open(fname).read() - files_concat = "".join(files_content) - return files_concat - @openerpweb.jsonrequest def login(self, req, db, login, password): req.session.login(db, login, password) @@ -118,31 +180,12 @@ class Session(openerpweb.Controller): @openerpweb.jsonrequest def modules(self, req): - return {"modules": [name - for name, manifest in openerpweb.addons_manifest.iteritems() - if manifest.get('active', True)]} - - @openerpweb.jsonrequest - def csslist(self, req, mods='base'): - return {'files': self.manifest_glob(mods.split(','), 'css')} - - @openerpweb.jsonrequest - def jslist(self, req, mods='base'): - return {'files': self.manifest_glob(mods.split(','), 'js')} - - def css(self, req, mods='base'): - files = self.manifest_glob(mods.split(','), 'css') - concat = self.concat_files(files)[0] - # TODO request set the Date of last modif and Etag - return concat - css.exposed = True - - def js(self, req, mods='base'): - 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 = True + # TODO query server for installed web modules + mods = [] + for name, manifest in openerpweb.addons_manifest.items(): + if name != 'base' and manifest.get('active', True): + mods.append(name) + return mods @openerpweb.jsonrequest def eval_domain_and_context(self, req, contexts, domains, @@ -269,7 +312,7 @@ def clean_action(action, session): action['domain'] = eval( action['domain'], session.evaluation_context( - action['context'])) or [] + action.get('context', {}))) or [] if 'flags' not in action: # Set empty flags dictionary for web client. action['flags'] = dict() @@ -574,10 +617,15 @@ class View(openerpweb.Controller): else: xml = ElementTree.fromstring(arch) fvg['arch'] = Xml2Json.convert_element(xml) - for field in fvg['fields'].values(): - if field.has_key('views') and field['views']: - for view in field["views"].values(): + + for field in fvg['fields'].itervalues(): + if field.get('views'): + for view in field["views"].itervalues(): self.process_view(session, view, None, transform) + if field.get('domain'): + field["domain"] = self.parse_domain(field["domain"], session) + if field.get('context'): + field["context"] = self.parse_context(field["context"], session) @openerpweb.jsonrequest def add_custom(self, request, view_id, arch): @@ -615,27 +663,38 @@ class View(openerpweb.Controller): self.parse_domains_and_contexts(elem, session) return root - def parse_domain(self, elem, attr_name, session): - """ Parses an attribute of the provided name as a domain, transforms it + def parse_domain(self, domain, session): + """ Parses an arbitrary string containing a domain, transforms it to either a literal domain or a :class:`openerpweb.nonliterals.Domain` - :param elem: the node being parsed - :type param: xml.etree.ElementTree.Element - :param str attr_name: the name of the attribute which should be parsed + :param domain: the domain to parse, if the domain is not a string it is assumed to + be a literal domain and is returned as-is :param session: Current OpenERP session :type session: openerpweb.openerpweb.OpenERPSession """ - domain = elem.get(attr_name, '').strip() - if domain: - try: - elem.set( - attr_name, - openerpweb.ast.literal_eval( - domain)) - except ValueError: - # not a literal - elem.set(attr_name, - openerpweb.nonliterals.Domain(session, domain)) + if not isinstance(domain, (str, unicode)): + return domain + try: + return openerpweb.ast.literal_eval(domain) + except ValueError: + # not a literal + return openerpweb.nonliterals.Domain(session, domain) + + def parse_context(self, context, session): + """ Parses an arbitrary string containing a context, transforms it + to either a literal context or a :class:`openerpweb.nonliterals.Context` + + :param context: the context to parse, if the context is not a string it is assumed to + be a literal domain and is returned as-is + :param session: Current OpenERP session + :type session: openerpweb.openerpweb.OpenERPSession + """ + if not isinstance(context, (str, unicode)): + return context + try: + return openerpweb.ast.literal_eval(context) + except ValueError: + return openerpweb.nonliterals.Context(session, context) def parse_domains_and_contexts(self, elem, session): """ Converts domains and contexts from the view into Python objects, @@ -648,18 +707,14 @@ class View(openerpweb.Controller): non-literal objects :type session: openerpweb.openerpweb.OpenERPSession """ - self.parse_domain(elem, 'domain', session) - self.parse_domain(elem, 'filter_domain', session) + for el in ['domain', 'filter_domain']: + domain = elem.get(el, '').strip() + if domain: + elem.set(el, self.parse_domain(domain, session)) for el in ['context', 'default_get']: context_string = elem.get(el, '').strip() if context_string: - try: - elem.set(el, - openerpweb.ast.literal_eval(context_string)) - except ValueError: - elem.set(el, - openerpweb.nonliterals.Context( - session, context_string)) + elem.set(el, self.parse_context(context_string, session)) class FormView(View): _cp_path = "/base/formview" @@ -707,7 +762,41 @@ class SearchView(View): def fields_get(self, req, model): Model = req.session.model(model) fields = Model.fields_get(False, req.session.eval_context(req.context)) + for field in fields.values(): + # shouldn't convert the views too? + if field.get('domain'): + field["domain"] = self.parse_domain(field["domain"], req.session) + if field.get('context'): + field["context"] = self.parse_domain(field["context"], req.session) return {'fields': fields} + + @openerpweb.jsonrequest + def get_filters(self, req, model): + Model = req.session.model("ir.filters") + filters = Model.get_filters(model) + for filter in filters: + filter["context"] = req.session.eval_context(self.parse_context(filter["context"], req.session)) + filter["domain"] = req.session.eval_domain(self.parse_domain(filter["domain"], req.session)) + return filters + + @openerpweb.jsonrequest + def save_filter(self, req, model, name, context_to_save, domain): + Model = req.session.model("ir.filters") + ctx = openerpweb.nonliterals.CompoundContext(context_to_save) + ctx.session = req.session + ctx = ctx.evaluate() + domain = openerpweb.nonliterals.CompoundDomain(domain) + domain.session = req.session + domain = domain.evaluate() + uid = req.session._uid + context = req.session.eval_context(req.context) + to_return = Model.create_or_replace({"context": ctx, + "domain": domain, + "model_id": model, + "name": name, + "user_id": uid + }, context) + return to_return class Binary(openerpweb.Controller): _cp_path = "/base/binary" diff --git a/addons/base/static/lib/LABjs/LAB.js b/addons/base/static/lib/LABjs/LAB.js deleted file mode 100644 index 1c3c85bd7a3..00000000000 --- a/addons/base/static/lib/LABjs/LAB.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! LAB.js (LABjs :: Loading And Blocking JavaScript) - v1.2.0 (c) Kyle Simpson - MIT License -*/ -(function(p){var q="string",w="head",L="body",M="script",u="readyState",j="preloaddone",x="loadtrigger",N="srcuri",E="preload",Z="complete",y="done",z="which",O="preserve",F="onreadystatechange",ba="onload",P="hasOwnProperty",bb="script/cache",Q="[object ",bw=Q+"Function]",bx=Q+"Array]",e=null,h=true,i=false,k=p.document,bc=p.location,bd=p.ActiveXObject,A=p.setTimeout,be=p.clearTimeout,R=function(a){return k.getElementsByTagName(a)},S=Object.prototype.toString,G=function(){},r={},T={},bf=/^[^?#]*\//.exec(bc.href)[0],bg=/^\w+\:\/\/\/?[^\/]+/.exec(bf)[0],by=R(M),bh=p.opera&&S.call(p.opera)==Q+"Opera]",bi=("MozAppearance"in k.documentElement.style),bj=(k.createElement(M).async===true),v={cache:!(bi||bh),order:bi||bh||bj,xhr:h,dupe:h,base:"",which:w};v[O]=i;v[E]=h;r[w]=k.head||R(w);r[L]=R(L);function B(a){return S.call(a)===bw}function U(a,b){var c=/^\w+\:\/\//,d;if(typeof a!=q)a="";if(typeof b!=q)b="";d=((/^\/\//.test(a))?bc.protocol:"")+a;d=(c.test(d)?"":b)+d;return((c.test(d)?"":(d.charAt(0)==="/"?bg:bf))+d)}function bz(a){return(U(a).indexOf(bg)===0)}function bA(a){var b,c=-1;while(b=by[++c]){if(typeof b.src==q&&a===U(b.src)&&b.type!==bb)return h}return i}function H(t,l){t=!(!t);if(l==e)l=v;var bk=i,C=t&&l[E],bl=C&&l.cache,I=C&&l.order,bm=C&&l.xhr,bB=l[O],bC=l.which,bD=l.base,bn=G,J=i,D,s=h,m={},K=[],V=e;C=bl||bm||I;function bo(a,b){if((a[u]&&a[u]!==Z&&a[u]!=="loaded")||b[y]){return i}a[ba]=a[F]=e;return h}function W(a,b,c){c=!(!c);if(!c&&!(bo(a,b)))return;b[y]=h;for(var d in m){if(m[P](d)&&!(m[d][y]))return}bk=h;bn()}function bp(a){if(B(a[x])){a[x]();a[x]=e}}function bE(a,b){if(!bo(a,b))return;b[j]=h;A(function(){r[b[z]].removeChild(a);bp(b)},0)}function bF(a,b){if(a[u]===4){a[F]=G;b[j]=h;A(function(){bp(b)},0)}}function X(b,c,d,g,f,n){var o=b[z];A(function(){if("item"in r[o]){if(!r[o][0]){A(arguments.callee,25);return}r[o]=r[o][0]}var a=k.createElement(M);if(typeof d==q)a.type=d;if(typeof g==q)a.charset=g;if(B(f)){a[ba]=a[F]=function(){f(a,b)};a.src=c;if(bj){a.async=i}}r[o].insertBefore(a,(o===w?r[o].firstChild:e));if(typeof n==q){a.text=n;W(a,b,h)}},0)}function bq(a,b,c,d){T[a[N]]=h;X(a,b,c,d,W)}function br(a,b,c,d){var g=arguments;if(s&&a[j]==e){a[j]=i;X(a,b,bb,d,bE)}else if(!s&&a[j]!=e&&!a[j]){a[x]=function(){br.apply(e,g)}}else if(!s){bq.apply(e,g)}}function bs(a,b,c,d){var g=arguments,f;if(s&&a[j]==e){a[j]=i;f=a.xhr=(bd?new bd("Microsoft.XMLHTTP"):new p.XMLHttpRequest());f[F]=function(){bF(f,a)};f.open("GET",b);f.send("")}else if(!s&&a[j]!=e&&!a[j]){a[x]=function(){bs.apply(e,g)}}else if(!s){T[a[N]]=h;X(a,b,c,d,e,a.xhr.responseText);a.xhr=e}}function bt(a){if(typeof a=="undefined"||!a)return;if(a.allowDup==e)a.allowDup=l.dupe;var b=a.src,c=a.type,d=a.charset,g=a.allowDup,f=U(b,bD),n,o=bz(f);if(typeof d!=q)d=e;g=!(!g);if(!g&&((T[f]!=e)||(s&&m[f])||bA(f))){if(m[f]!=e&&m[f][j]&&!m[f][y]&&o){W(e,m[f],h)}return}if(m[f]==e)m[f]={};n=m[f];if(n[z]==e)n[z]=bC;n[y]=i;n[N]=f;J=h;if(!I&&bm&&o)bs(n,f,c,d);else if(!I&&bl)br(n,f,c,d);else bq(n,f,c,d)}function Y(a){if(t&&!I)K.push(a);if(!t||C)a()}function bu(a){var b=[],c;for(c=-1;++c - - - - OpenERP - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index f1af4b88ee1..d19140a8a49 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -578,6 +578,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, display: none; } +.openerp .searchview_extended_delete_prop { + text-decoration: none; +} + .openerp .searchview_extended_delete_group span, .openerp .searchview_extended_delete_prop span { font-size: 0.9em; @@ -592,6 +596,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, border: 1px solid silver; } +.openerp .oe-listview thead table { + width: 100%; + border: none; +} .openerp .oe-listview tr.odd { background-color: #f3f3f3; } @@ -601,6 +609,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, .openerp .oe-listview tbody tr:hover { background-color: #eae9f0; } +.openerp .oe-listview thead table tr, +.openerp .oe-listview thead table tr:hover { + background: none; +} .openerp .oe-listview td, .openerp .oe-listview th { @@ -640,9 +652,11 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, .openerp .oe-listview th.oe-actions { text-align: left; + white-space: nowrap; } .openerp .oe-listview th.oe-list-pager { text-align: right; + white-space: nowrap; } .openerp .oe-list-pager .oe-pager-state { cursor: pointer; @@ -672,29 +686,29 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%, rounded corners are a pain on tables: need to round not only table, but also on the first and last children of the first and last row */ -.openerp .oe-listview table { +.openerp .oe-listview table.oe-listview-content { -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; } -.openerp .oe-listview table thead tr:first-child th:first-child { +.openerp .oe-listview table.oe-listview-content thead tr:first-child th:first-child { -webkit-border-top-left-radius: 7px; -moz-border-radius-topleft: 7px; border-top-left-radius: 7px; } -.openerp .oe-listview table thead tr:first-child th:last-child { +.openerp .oe-listview table.oe-listview-content thead tr:first-child th:last-child { -webkit-border-top-right-radius: 7px; -moz-border-radius-topright: 7px; border-top-right-radius: 7px; } -.openerp .oe-listview table tfoot td:first-child, -.openerp .oe-listview table tbody:last-child tr:last-child th:first-child { +.openerp .oe-listview table.oe-listview-content tfoot td:first-child, +.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child th:first-child { -webkit-border-bottom-left-radius: 7px; -moz-border-radius-bottomleft: 7px; border-bottom-left-radius: 7px; } -.openerp .oe-listview table tfoot td:last-child, -.openerp .oe-listview table tbody:last-child tr:last-child td:last-child { +.openerp .oe-listview table.oe-listview-content tfoot td:last-child, +.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child td:last-child { -webkit-border-bottom-right-radius: 7px; -moz-border-radius-bottomright: 7px; border-bottom-right-radius: 7px; diff --git a/addons/base/static/src/js/base.js b/addons/base/static/src/js/base.js index 55e4929e4c0..28022f8b764 100644 --- a/addons/base/static/src/js/base.js +++ b/addons/base/static/src/js/base.js @@ -1,69 +1,3 @@ -/*--------------------------------------------------------- - * John Resig Class, to be moved to openerp.base.Class - *---------------------------------------------------------*/ - -(function() { - var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; - // The base Class implementation (does nothing) - this.Class = function(){}; - - // Create a new Class that inherits from this class - Class.extend = function(prop) { - var _super = this.prototype; - - // Instantiate a base class (but only create the instance, - // don't run the init constructor) - initializing = true; - var prototype = new this(); - initializing = false; - - // Copy the properties over onto the new prototype - for (var name in prop) { - // Check if we're overwriting an existing function - prototype[name] = typeof prop[name] == "function" && - typeof _super[name] == "function" && fnTest.test(prop[name]) ? - (function(name, fn){ - return function() { - var tmp = this._super; - - // Add a new ._super() method that is the same method - // but on the super-class - this._super = _super[name]; - - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; - - return ret; - }; - })(name, prop[name]) : - prop[name]; - } - - // The dummy class constructor - function Class() { - // All construction is actually done in the init method - if ( !initializing && this.init ) { - var ret = this.init.apply(this, arguments); - if (ret) { return ret; } - } - return this; - } - - // Populate our constructed prototype object - Class.prototype = prototype; - - // Enforce the constructor to be what we expect - Class.constructor = Class; - - // And make this class extendable - Class.extend = arguments.callee; - - return Class; - }; -})(); - //--------------------------------------------------------- // OpenERP initialisation and black magic about the pool //--------------------------------------------------------- @@ -107,11 +41,13 @@ _session_id: "session" + session_counter++, screen: openerp.screen, sessions: openerp.sessions, - base: {} + base: {}, + web_mobile: {} }; openerp.sessions[new_instance._session_id] = new_instance; - if (!skip_init) + if (!skip_init){ openerp.base(new_instance); + } return new_instance; } }; @@ -123,6 +59,7 @@ /** @namespace */ openerp.base = function(instance) { + openerp.base.controller(instance); openerp.base.dates(instance); openerp.base.chrome(instance); openerp.base.data(instance); @@ -135,9 +72,6 @@ openerp.base = function(instance) { if (openerp.base.list) { openerp.base.list(instance); } - if (openerp.base.tree) { - openerp.base.tree(instance); - } if (openerp.base. m2o) { openerp.base.m2o(instance); } @@ -147,6 +81,12 @@ openerp.base = function(instance) { if (openerp.base.list && openerp.base.list.editable) { openerp.base.list.editable(instance); } + if (openerp.web_mobile) { + openerp.web_mobile(instance); + } + if (openerp.base.view_tree) { + openerp.base.view_tree(instance); + } }; // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 5a049bf5dd8..dbf2f61abde 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -4,61 +4,12 @@ openerp.base.chrome = function(openerp) { -openerp.base.callback = function(obj, method) { - var callback = function() { - var args = Array.prototype.slice.call(arguments); - var r; - for(var i = 0; i < callback.callback_chain.length; i++) { - var c = callback.callback_chain[i]; - if(c.unique) { - callback.callback_chain.splice(i, 1); - i -= 1; - } - r = c.callback.apply(c.self, c.args.concat(args)); - // TODO special value to stop the chain - // openerp.base.callback_stop - } - return r; - }; - callback.callback_chain = []; - callback.add = function(f) { - if(typeof(f) == 'function') { - f = { callback: f, args: Array.prototype.slice.call(arguments, 1) }; - } - f.self = f.self || null; - f.args = f.args || []; - f.unique = !!f.unique; - if(f.position == 'last') { - callback.callback_chain.push(f); - } else { - callback.callback_chain.unshift(f); - } - return callback; - }; - callback.add_first = function(f) { - return callback.add.apply(null,arguments); - }; - callback.add_last = function(f) { - return callback.add({ - callback: f, - args: Array.prototype.slice.call(arguments, 1), - position: "last" - }); - }; - - return callback.add({ - callback: method, - self:obj, - args:Array.prototype.slice.call(arguments, 2) - }); -}; - /** * Base error for lookup failure * * @class */ -openerp.base.NotFound = Class.extend( /** @lends openerp.base.NotFound# */ { +openerp.base.NotFound = openerp.base.Class.extend( /** @lends openerp.base.NotFound# */ { }); openerp.base.KeyNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.KeyNotFound# */ { /** @@ -91,7 +42,7 @@ openerp.base.ObjectNotFound = openerp.base.NotFound.extend( /** @lends openerp.b return "Could not find any object of path " + this.path; } }); -openerp.base.Registry = Class.extend( /** @lends openerp.base.Registry# */ { +openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Registry# */ { /** * Stores a mapping of arbitrary key (strings) to object paths (as strings * as well). @@ -185,144 +136,117 @@ openerp.base.Registry = Class.extend( /** @lends openerp.base.Registry# */ { } }); -openerp.base.BasicController = Class.extend( /** @lends openerp.base.BasicController# */{ - /** - * rpc operations, event binding and callback calling should be done in - * start() instead of init so that event can be hooked in between. - * - * @constructs - */ - init: function(element_id) { - this.element_id = element_id; - this.$element = $('#' + element_id); - if (element_id) { - openerp.screen[element_id] = this; - } - - // Transform on_* method into openerp.base.callbacks - for (var name in this) { - if(typeof(this[name]) == "function") { - this[name].debug_name = name; - // bind ALL function to this not only on_and _do ? - if((/^on_|^do_/).test(name)) { - this[name] = openerp.base.callback(this, this[name]); - } - } +/** + * OpenERP session aware controller + * a controller takes an already existing dom element and manage it + */ +openerp.base.Controller = openerp.base.Controller.extend( /** @lends openerp.base.Controller# */{ + init: function(parent, element_id) { + this._super(parent, element_id); + if(this.controller_parent && this.controller_parent.session) { + this.session = this.controller_parent.session; } }, /** - * Controller start - * event binding, rpc and callback calling required to initialize the - * object can happen here + * Performs a JSON-RPC call * - * Returns a promise object letting callers (subclasses and direct callers) - * know when this component is done starting - * - * @returns {jQuery.Deferred} + * @param {String} url endpoint url + * @param {Object} data RPC parameters + * @param {Function} success RPC call success callback + * @param {Function} error RPC call error callback + * @returns {jQuery.Deferred} deferred object for the RPC call */ - start: function() { - // returns an already fulfilled promise. Maybe we could return nothing? - // $.when can take non-deferred and in that case it simply considers - // them all as fulfilled promises. - // But in thise case we *have* to ensure callers use $.when and don't - // try to call deferred methods on this return value. - return $.Deferred().done().promise(); + rpc: function(url, data, success, error) { + return this.session.rpc(url, data, success, error); }, - stop: function() { - }, - log: function() { - var args = Array.prototype.slice.call(arguments); - var caller = arguments.callee.caller; - // TODO add support for line number using - // https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js - // args.unshift("" + caller.debug_name); - this.on_log.apply(this,args); - }, - on_log: function() { - if(window.openerp.debug || (window.location.search.indexOf('?debug') !== -1)) { - var notify = false; - var body = false; - if(window.console) { - console.log(arguments); - } else { - body = true; - } - var a = Array.prototype.slice.call(arguments, 0); - for(var i = 0; i < a.length; i++) { - var v = a[i]==null ? "null" : a[i].toString(); - if(i==0) { - notify = v.match(/^not/); - body = v.match(/^bod/); - } - if(body) { - $('
').text(v).appendTo($('body'));
-                }
-                if(notify && this.notification) {
-                    this.notification.notify("Logging:",v);
-                }
-            }
-        }
-
+    do_action: function(action, on_finished) {
+        return this.parent.do_action(action, on_finished);
     }
 });
 
 /**
- * Generates an inherited class that replaces all the methods by null methods (methods
- * that does nothing and always return undefined).
- * 
- * @param {Class} claz
- * @param {Object} add Additional functions to override.
- * @return {Class}
+ * OpenERP session aware widget
+ * A widget is a controller that doesnt take an element_id
+ * it render its own html render() that you should insert into the dom
+ * and bind it at start()
  */
-openerp.base.generate_null_object_class = function(claz, add) {
-    var newer = {};
-    var copy_proto = function(prototype) {
-        for (var name in prototype) {
-            if(typeof prototype[name] == "function") {
-                newer[name] = function() {};
-            }
+openerp.base.BaseWidget = openerp.base.Controller.extend({
+    /**
+     * The name of the QWeb template that will be used for rendering. Must be
+     * redefined in subclasses or the render() method can not be used.
+     * 
+     * @type string
+     */
+    template: null,
+    /**
+     * The prefix used to generate an id automatically. Should be redefined in
+     * subclasses. If it is not defined, a default identifier will be used.
+     * 
+     * @type string
+     */
+    identifier_prefix: 'generic-identifier',
+    /**
+     * Base class for widgets. Handle rendering (based on a QWeb template),
+     * identifier generation, parenting and destruction of the widget.
+     * Also initialize the identifier.
+     *
+     * @constructs
+     * @params {openerp.base.search.BaseWidget} parent The parent widget.
+     */
+    init: function (parent) {
+        this._super(parent);
+        this.make_id(this.identifier_prefix);
+    },
+    /**
+     * Sets and returns a globally unique identifier for the widget.
+     *
+     * If a prefix is appended, the identifier will be appended to it.
+     *
+     * @params sections prefix sections, empty/falsy sections will be removed
+     */
+    make_id: function () {
+        this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
+        return this.element_id;
+    },
+    /**
+     * Render the widget. This.template must be defined.
+     * The content of the current object is passed as context to the template.
+     * 
+     * @param {object} additional Additional context arguments to pass to the template.
+     */
+    render: function (additional) {
+        return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
+    },
+    /**
+     * "Starts" the widgets. Called at the end of the rendering, this allows
+     * to get a jQuery object referring to the DOM ($element attribute).
+     */
+    start: function () {
+        this._super();
+        var tmp = document.getElementById(this.element_id);
+        this.$element = tmp ? $(tmp) : null;
+    },
+    /**
+     * "Stops" the widgets. Called when the view destroys itself, this
+     * lets the widgets clean up after themselves.
+     */
+    stop: function () {
+        if(this.$element != null) {
+            this.$element.remove();
         }
-        if (prototype.prototype)
-            copy_proto(prototype.prototype);
-    };
-    copy_proto(claz.prototype);
-    newer.init = openerp.base.BasicController.prototype.init;
-    var tmpclass = claz.extend(newer);
-    return tmpclass.extend(add || {});
-};
-
-openerp.base.Notification =  openerp.base.BasicController.extend({
-    init: function(element_id) {
-        this._super(element_id);
-        this.$element.notify({
-            speed: 500,
-            expires: 1500
-        });
-    },
-    notify: function(title, text) {
-        this.$element.notify('create', {
-            title: title,
-            text: text
-        });
-    },
-    warn: function(title, text) {
-        this.$element.notify('create', 'oe_notification_alert', {
-            title: title,
-            text: text
-        });
+        this._super();
     }
 });
 
-openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.base.Session# */{
+openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.Session# */{
     /**
      * @constructs
-     * @extends openerp.base.BasicController
      * @param element_id to use for exception reporting
      * @param server
      * @param port
      */
-    init: function(element_id, server, port) {
-        this._super(element_id);
+    init: function(parent, element_id, server, port) {
+        this._super(parent, element_id);
         this.server = (server == undefined) ? location.hostname : server;
         this.port = (port == undefined) ? location.port : port;
         this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
@@ -337,12 +261,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
         this.context = {};
     },
     start: function() {
-        var self = this;
-        return this.session_restore().then(function () {
-            self.on_session_valid();
-        }, function () {
-            self.on_session_invalid();
-        });
+        this.session_restore();
     },
     /**
      * Executes an RPC call, registering the provided callbacks.
@@ -478,7 +397,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
         this.session_id = this.get_cookie('session_id');
         // we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
         // then call on_session_valid
-        return this.rpc('/base/session/check', {}, function () {}, function () {});
+        this.on_session_valid();
     },
     /**
      * Saves the session id and uid locally
@@ -506,7 +425,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
         for(var i=0; i').text(v).appendTo($('body'));
+                }
+                if(notify && this.notification) {
+                    this.notification.notify("Logging:",v);
+                }
+            }
+        }
+
+    }
+});
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js
index e8a3415fbb3..080f4409fa1 100644
--- a/addons/base/static/src/js/data.js
+++ b/addons/base/static/src/js/data.js
@@ -39,18 +39,16 @@ openerp.base.DataGroup =  openerp.base.Controller.extend( /** @lends openerp.bas
      * @param {Array} group_by sequence of fields by which to group
      * @param {Number} [level=0] nesting level of the group
      */
-    init: function(session, model, domain, context, group_by, level) {
+    init: function(parent, model, domain, context, group_by, level) {
+        this._super(parent, null);
         if (group_by) {
             if (group_by.length || context['group_by_no_leaf']) {
-                return new openerp.base.ContainerDataGroup(
-                        session, model, domain, context, group_by, level);
+                return new openerp.base.ContainerDataGroup( this, model, domain, context, group_by, level);
             } else {
-                return new openerp.base.GrouplessDataGroup(
-                        session, model, domain, context, level);
+                return new openerp.base.GrouplessDataGroup( this, model, domain, context, level);
             }
         }
 
-        this._super(session, null);
         this.model = model;
         this.context = context;
         this.domain = domain;
@@ -59,8 +57,7 @@ openerp.base.DataGroup =  openerp.base.Controller.extend( /** @lends openerp.bas
     },
     cls: 'DataGroup'
 });
-openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
-    /** @lends openerp.base.ContainerDataGroup# */ {
+openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend( /** @lends openerp.base.ContainerDataGroup# */ {
     /**
      *
      * @constructs
@@ -73,8 +70,8 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
      * @param group_by
      * @param level
      */
-    init: function (session, model, domain, context, group_by, level) {
-        this._super(session, model, domain, context, null, level);
+    init: function (parent, model, domain, context, group_by, level) {
+        this._super(parent, model, domain, context, null, level);
 
         this.group_by = group_by;
     },
@@ -189,7 +186,7 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
                 var child_context = _.extend({}, self.context, group.__context);
                 return _.extend(
                     new openerp.base.DataGroup(
-                        self.session, self.model, group.__domain,
+                        self, self.model, group.__domain,
                         child_context, child_context.group_by,
                         self.level + 1),
                     group, {sort: self.sort});
@@ -197,8 +194,7 @@ openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
         });
     }
 });
-openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend(
-    /** @lends openerp.base.GrouplessDataGroup# */ {
+openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend( /** @lends openerp.base.GrouplessDataGroup# */ {
     /**
      *
      * @constructs
@@ -210,18 +206,16 @@ openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend(
      * @param context
      * @param level
      */
-    init: function (session, model, domain, context, level) {
-        this._super(session, model, domain, context, null, level);
+    init: function (parent, model, domain, context, level) {
+        this._super(parent, model, domain, context, null, level);
     },
     list: function (fields, ifGroups, ifRecords) {
         ifRecords(_.extend(
-            new openerp.base.DataSetSearch(this.session, this.model),
+            new openerp.base.DataSetSearch(this, this.model),
             {domain: this.domain, context: this.context, _sort: this.sort}));
     }
 });
-
-openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend(
-    /** @lends openerp.base.StaticDataGroup# */ {
+openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend( /** @lends openerp.base.StaticDataGroup# */ {
     /**
      * A specialization of groupless data groups, relying on a single static
      * dataset as its records provider.
@@ -246,11 +240,12 @@ openerp.base.DataSet =  openerp.base.Controller.extend( /** @lends openerp.base.
      * @constructs
      * @extends openerp.base.Controller
      *
-     * @param {openerp.base.Session} session current OpenERP session
      * @param {String} model the OpenERP model this dataset will manage
      */
-    init: function(session, model, context) {
-        this._super(session);
+    init: function(source_controller, model, context) {
+        // we don't want the dataset to be a child of anything!
+        this._super(null);
+        this.session = source_controller ? source_controller.session : undefined;
         this.model = model;
         this.context = context || {};
         this.index = null;
@@ -382,15 +377,11 @@ openerp.base.DataSet =  openerp.base.Controller.extend( /** @lends openerp.base.
         return this.context;
     }
 });
-
 openerp.base.DataSetStatic =  openerp.base.DataSet.extend({
-    init: function(session, model, context, ids) {
-        this._super(session, model, context);
+    init: function(parent, model, context, ids) {
+        this._super(parent, model, context);
         // all local records
         this.ids = ids || [];
-        if (this.ids.length) {
-            this.index = 0;
-        }
     },
     read_slice: function (fields, offset, limit, callback) {
         var self = this;
@@ -400,8 +391,10 @@ openerp.base.DataSetStatic =  openerp.base.DataSet.extend({
     },
     set_ids: function (ids) {
         this.ids = ids;
-        this.index = this.index <= this.ids.length - 1 ?
-            this.index : (this.ids.length > 0 ? this.length - 1 : 0);
+        if (this.index !== null) {
+            this.index = this.index <= this.ids.length - 1 ?
+                this.index : (this.ids.length > 0 ? this.length - 1 : 0);
+        }
     },
     unlink: function(ids) {
         this.on_unlink(ids);
@@ -411,18 +404,17 @@ openerp.base.DataSetStatic =  openerp.base.DataSet.extend({
         this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
     }
 });
-
 openerp.base.DataSetSearch =  openerp.base.DataSet.extend({
     /**
      * @constructs
      *
-     * @param {openerp.base.Session} session
+     * @param {Object} parent
      * @param {String} model
      * @param {Object} context
      * @param {Array} domain
      */
-    init: function(session, model, context, domain) {
-        this._super(session, model, context);
+    init: function(parent, model, context, domain) {
+        this._super(parent, model, context);
         this.domain = domain || [];
         this._sort = [];
         this.offset = 0;
@@ -486,14 +478,15 @@ openerp.base.DataSetSearch =  openerp.base.DataSet.extend({
         var self = this;
         return this._super(ids, function(result) {
             self.ids = _.without.apply(_, [self.ids].concat(ids));
-            self.index = self.index <= self.ids.length - 1 ?
-                self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
+            if (this.index !== null) {
+                self.index = self.index <= self.ids.length - 1 ?
+                    self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
+            }
             if (callback)
                 callback(result);
         }, error_callback);
     }
 });
-
 openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({
     virtual_id_prefix: "one2many_v_id_",
     virtual_id_regex: /one2many_v_id_.*/,
@@ -598,7 +591,6 @@ openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({
         return completion.promise();
     }
 });
-
 openerp.base.ReadOnlyDataSetSearch = openerp.base.DataSetSearch.extend({
     create: function(data, callback, error_callback) {
         this.on_create(data);
diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js
index fe981f9fdac..7b86ce9f372 100644
--- a/addons/base/static/src/js/form.js
+++ b/addons/base/static/src/js/form.js
@@ -17,9 +17,9 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
      *
      * @property {openerp.base.Registry} registry=openerp.base.form.widgets widgets registry for this form view instance
      */
-    init: function(view_manager, session, element_id, dataset, view_id) {
-        this._super(session, element_id);
-        this.view_manager = view_manager || new openerp.base.NullViewManager();
+    init: function(parent, element_id, dataset, view_id, options) {
+        this._super(parent, element_id);
+        this.view_manager = parent || new openerp.base.NullViewManager();
         this.dataset = dataset;
         this.model = dataset.model;
         this.view_id = view_id;
@@ -37,6 +37,8 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
         this.registry = openerp.base.form.widgets;
         this.has_been_loaded = $.Deferred();
         this.$form_header = null;
+        this.options = options || {};
+        _.defaults(this.options, {"always_show_new_button": true});
     },
     start: function() {
         //this.log('Starting FormView '+this.model+this.view_id)
@@ -79,19 +81,27 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
         this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
         this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
 
-        this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
+        this.$form_header.find('button.oe_get_xml_view').click(function() {
+            $('' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '').dialog({ width: '95%', height: 600});
+        });
+
+        if(this.view_manager.sidebar)
+            this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
         this.has_been_loaded.resolve();
     },
     do_show: function () {
         var self = this;
+        var promise;
         if (this.dataset.index === null) {
             // null index means we should start a new record
-            this.on_button_new();
+            promise = this.on_button_new();
         } else {
-            this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
+            promise = this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
         }
         self.$element.show();
-        this.view_manager.sidebar.do_refresh(true);
+        if(this.view_manager.sidebar)
+            this.view_manager.sidebar.do_refresh(true);
+        return promise;
     },
     do_hide: function () {
         this.$element.hide();
@@ -103,7 +113,9 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
         if (!record.id) {
             this.$form_header.find('.oe_form_on_create').show();
             this.$form_header.find('.oe_form_on_update').hide();
-            this.$form_header.find('button.oe_form_button_new').hide();
+            if (!this.options["always_show_new_button"]) {
+                this.$form_header.find('button.oe_form_button_new').hide();
+            }
         } else {
             this.$form_header.find('.oe_form_on_create').hide();
             this.$form_header.find('.oe_form_on_update').show();
@@ -231,10 +243,11 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
         }
     },
     on_processed_onchange: function(response, processed) {
-        var result = response.result;
+        var result = response;
         if (result.value) {
             for (var f in result.value) {
                 var field = this.fields[f];
+                // If field is not defined in the view, just ignore it
                 if (field) {
                     var value = result.value[f];
                     processed.push(field.name);
@@ -245,8 +258,6 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
                             this.do_onchange(field, processed);
                         }
                     }
-                } else {
-                    // this is a common case, the normal behavior should be to ignore it
                 }
             }
             this.on_form_changed();
@@ -262,16 +273,20 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
             });
         }
         if (result.domain) {
-            // Will be removed ?
+            // TODO: 
         }
         this.ready = true;
     },
     on_button_new: function() {
         var self = this;
+        var def = $.Deferred();
         $.when(this.has_been_loaded).then(function() {
             self.dataset.default_get(
-                _.keys(self.fields_view.fields), self.on_record_loaded);
+                _.keys(self.fields_view.fields)).then(self.on_record_loaded).then(function() {
+                    def.resolve();
+                    });
         });
+        return def.promise();
     },
     /**
      * Triggers saving the form's record. Chooses between creating a new
@@ -392,20 +407,19 @@ openerp.base.FormView =  openerp.base.View.extend( /** @lends openerp.base.FormV
         this.notification.notify("Cancelling form");
     },
     do_update_sidebar: function() {
-        if (this.flags.sidebar === false) {
+        if (this.flags.sidebar === false || this.view_manager.sidebar === undefined) {
             return;
         }
         if (!this.datarecord.id) {
             this.on_attachments_loaded([]);
         } else {
-            // TODO fme: modify this so it doesn't try to load attachments when there is not sidebar
-            /*(new openerp.base.DataSetSearch(
-                    this.session, 'ir.attachment', this.dataset.get_context(),
+            (new openerp.base.DataSetSearch(
+                    this, 'ir.attachment', this.dataset.get_context(),
                     [['res_model', '=', this.dataset.model],
                      ['res_id', '=', this.datarecord.id],
                      ['type', 'in', ['binary', 'url']]])).read_slice(
                 ['name', 'url', 'type'], false, false,
-                this.on_attachments_loaded);*/
+                this.on_attachments_loaded);
         }
     },
     on_attachments_loaded: function(attachments) {
@@ -526,7 +540,7 @@ openerp.base.form.Widget = openerp.base.Controller.extend({
         this.element_name = this.element_name || this.type;
         this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
 
-        this._super(this.view.session, this.element_id);
+        this._super(view, this.element_id);
 
         this.view.widgets[this.element_id] = this;
         this.children = node.children;
@@ -825,8 +839,10 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
      * the fields'context with the action's context.
      */
     build_context: function() {
-        var a_context = this.view.dataset.get_context() || {};
-        var f_context = this.field.context || {};
+        // I previously belevied contexts should be herrited, but now I doubt it
+        //var a_context = this.view.dataset.get_context() || {};
+        var f_context = this.field.context || null;
+        // maybe the default_get should only be used when we do a default_get?
         var v_context1 = this.node.attrs.default_get || {};
         var v_context2 = this.node.attrs.context || {};
         var v_context = new openerp.base.CompoundContext(v_context1, v_context2);
@@ -834,17 +850,19 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
             var fields_values = this._build_view_fields_values();
             v_context.set_eval_context(fields_values);
         }
-        var ctx = new openerp.base.CompoundContext(a_context, f_context, v_context);
+        // if there is a context on the node, overrides the model's context
+        var ctx = f_context || v_context;
         return ctx;
     },
     build_domain: function() {
-        var f_domain = this.field.domain || [];
+        var f_domain = this.field.domain || null;
         var v_domain = this.node.attrs.domain || [];
         if (!(v_domain instanceof Array) || true) { //TODO niv: remove || true
             var fields_values = this._build_view_fields_values();
             v_domain = new openerp.base.CompoundDomain(v_domain).set_eval_context(fields_values);
         }
-        return new openerp.base.CompoundDomain(f_domain, v_domain);
+        // if there is a domain on the node, overrides the model's domain
+        return f_domain || v_domain;
     }
 });
 
@@ -1238,6 +1256,21 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({
     });
 })();
 
+openerp.base.form.dialog = function(content, options) {
+    options = _.extend({
+        autoOpen: true,
+        width: '90%',
+        height: '90%',
+        min_width: '800px',
+        min_height: '600px'
+    }, options || {});
+    options.autoOpen = true;
+    var dialog = new openerp.base.Dialog(null, options);
+    dialog.$dialog = $(content).dialog(dialog.options);
+    console.log(dialog.options)
+    return dialog.$dialog;
+}
+
 openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
     init: function(view, node) {
         this._super(view, node);
@@ -1267,7 +1300,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
             if (!self.value) {
                 return;
             }
-            var pop = new openerp.base.form.FormOpenPopup(null, self.view.session);
+            var pop = new openerp.base.form.FormOpenPopup(self.view);
             pop.show_element(self.field.relation, self.value[0],self.build_context(), {});
             pop.on_write_completed.add_last(function() {
                 self.set_value(self.value[0]);
@@ -1355,13 +1388,13 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
         var search_val = request.term;
         var self = this;
 
-        var dataset = new openerp.base.DataSetStatic(this.session, this.field.relation, self.build_context());
+        var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
 
         dataset.name_search(search_val, self.build_domain(), 'ilike',
                 this.limit + 1, function(data) {
-            self.last_search = data.result;
+            self.last_search = data;
             // possible selections for the m2o
-            var values = _.map(data.result, function(x) {
+            var values = _.map(data, function(x) {
                 return {label: $('').text(x[1]).html(), name:x[1], id:x[0]};
             });
 
@@ -1372,7 +1405,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
                     dataset.name_search(search_val, self.build_domain(), 'ilike'
                     , false, function(data) {
                         self._change_int_value(null);
-                        self._search_create_popup("search", data.result);
+                        self._search_create_popup("search", data);
                     });
                 }});
             }
@@ -1397,9 +1430,9 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
     },
     _quick_create: function(name) {
         var self = this;
-        var dataset = new openerp.base.DataSetStatic(this.session, this.field.relation, self.build_context());
+        var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
         dataset.name_create(name, function(data) {
-            self._change_int_ext_value(data.result);
+            self._change_int_ext_value(data);
         }).fail(function(error, event) {
             event.preventDefault();
             self._change_int_value(null);
@@ -1409,7 +1442,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
     // all search/create popup handling
     _search_create_popup: function(view, ids, context) {
         var self = this;
-        var pop = new openerp.base.form.SelectCreatePopup(null, self.view.session);
+        var pop = new openerp.base.form.SelectCreatePopup(this);
         pop.select_element(self.field.relation,{
                 initial_ids: ids ? _.map(ids, function(x) {return x[0]}) : undefined,
                 initial_view: view,
@@ -1417,9 +1450,9 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
                 }, self.build_domain(),
                 new openerp.base.CompoundContext(self.build_context(), context || {}));
         pop.on_select_elements.add(function(element_ids) {
-            var dataset = new openerp.base.DataSetStatic(this.session, this.field.relation, self.build_context());
+            var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
             dataset.name_get([element_ids[0]], function(data) {
-                self._change_int_ext_value(data.result[0]);
+                self._change_int_ext_value(data[0]);
                 pop.stop();
             });
         });
@@ -1455,9 +1488,9 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
             self._change_int_ext_value(rval);
         };
         if(typeof(value) === "number") {
-            var dataset = new openerp.base.DataSetStatic(this.session, this.field.relation, self.build_context());
+            var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
             dataset.name_get([value], function(data) {
-                real_set_value(data.result[0]);
+                real_set_value(data[0]);
             }).fail(function() {self.tmp_value = undefined;});
         } else {
             setTimeout(function() {real_set_value(value);}, 0);
@@ -1533,13 +1566,14 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
         this._super(view, node);
         this.template = "FieldOne2Many";
         this.is_started = $.Deferred();
+        this.form_last_update = $.Deferred();
     },
     start: function() {
         this._super.apply(this, arguments);
 
         var self = this;
 
-        this.dataset = new openerp.base.form.One2ManyDataSet(this.session, this.field.relation);
+        this.dataset = new openerp.base.form.One2ManyDataSet(this, this.field.relation);
         this.dataset.o2m = this;
         this.dataset.parent_view = this.view;
         this.dataset.on_change.add_last(function() {
@@ -1563,17 +1597,21 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
         });
         this.views = views;
 
-        this.viewmanager = new openerp.base.ViewManager(this.view.session,
-            this.element_id, this.dataset, views);
+        this.viewmanager = new openerp.base.ViewManager(this, this.element_id, this.dataset, views);
         this.viewmanager.registry = openerp.base.views.clone({
-            list: 'openerp.base.form.One2ManyListView'
+            list: 'openerp.base.form.One2ManyListView',
+            form: 'openerp.base.form.One2ManyFormView'
+        });
+        var once = $.Deferred().then(function() {
+            self.form_last_update.resolve();
         });
-
         this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
             if (view_type == "list") {
                 controller.o2m = self;
             } else if (view_type == "form") {
-                // TODO niv
+                controller.on_record_loaded.add_last(function() {
+                    once.resolve();
+                });
             }
             self.is_started.resolve();
         });
@@ -1587,7 +1625,12 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
         if(self.viewmanager.active_view === "list") {
             view.reload_content();
         } else if (self.viewmanager.active_view === "form") {
-            // TODO niv: implement
+            if (this.dataset.index === null && this.dataset.ids.length >= 1) {
+                this.dataset.index = 0;
+            }
+            this.form_last_update.then(function() {
+                this.form_last_update = view.do_show();
+            });
         }
     },
     set_value_from_ui: function() {},
@@ -1678,13 +1721,17 @@ openerp.base.form.One2ManyDataSet = openerp.base.BufferedDataSet.extend({
     }
 });
 
+openerp.base.form.One2ManyFormView = openerp.base.FormView.extend({
+    
+});
+
 openerp.base.form.One2ManyListView = openerp.base.ListView.extend({
     do_add_record: function () {
         if (this.options.editable) {
             this._super.apply(this, arguments);
         } else {
             var self = this;
-            var pop = new openerp.base.form.SelectCreatePopup(null, self.o2m.view.session);
+            var pop = new openerp.base.form.SelectCreatePopup(this);
             pop.select_element(self.o2m.field.relation,{
                 initial_view: "form",
                 alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
@@ -1703,7 +1750,7 @@ openerp.base.form.One2ManyListView = openerp.base.ListView.extend({
     },
     do_activate_record: function(index, id) {
         var self = this;
-        var pop = new openerp.base.form.FormOpenPopup(null, self.o2m.view.session);
+        var pop = new openerp.base.form.FormOpenPopup(self.o2m.view);
         pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(),{
             auto_write: false,
             alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
@@ -1730,15 +1777,13 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
 
         var self = this;
 
-        this.dataset = new openerp.base.form.Many2ManyDataSet(
-                this.session, this.field.relation);
+        this.dataset = new openerp.base.form.Many2ManyDataSet( this, this.field.relation);
         this.dataset.m2m = this;
         this.dataset.on_unlink.add_last(function(ids) {
             self.on_ui_change();
         });
 
-        this.list_view = new openerp.base.form.Many2ManyListView(
-                null, this.view.session, this.list_id, this.dataset, false, {
+        this.list_view = new openerp.base.form.Many2ManyListView(new openerp.base.NullViewManager(this), this.list_id, this.dataset, false, {
                     'addable': 'Add',
                     'selectable': self.multi_selection
             });
@@ -1781,8 +1826,7 @@ openerp.base.form.Many2ManyDataSet = openerp.base.DataSetStatic.extend({
 
 openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
     do_add_record: function () {
-        var pop = new openerp.base.form.SelectCreatePopup(
-                null, this.m2m_field.view.session);
+        var pop = new openerp.base.form.SelectCreatePopup(this);
         pop.select_element(this.model, {},
             new openerp.base.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
             this.m2m_field.build_context());
@@ -1800,7 +1844,7 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
     },
     do_activate_record: function(index, id) {
         var self = this;
-        var pop = new openerp.base.form.FormOpenPopup(null, this.m2m_field.view.session);
+        var pop = new openerp.base.form.FormOpenPopup(this);
         pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {});
         pop.on_write_completed.add_last(function() {
             self.reload_content();
@@ -1826,15 +1870,12 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
         this.context = context || {};
         this.options = _.defaults(options || {}, {"initial_view": "search", "auto_create": true});
         this.initial_ids = this.options.initial_ids;
-        jQuery(this.render()).dialog({title: '',
-                    modal: true,
-                    width: 960,
-                    height: 600});
+        openerp.base.form.dialog(this.render());
         this.start();
     },
     start: function() {
         this._super();
-        this.dataset = new openerp.base.ReadOnlyDataSetSearch(this.session, this.model,
+        this.dataset = new openerp.base.ReadOnlyDataSetSearch(this, this.model,
             this.context);
         this.dataset.parent_view = this.options.parent_view;
         if (this.options.initial_view == "search") {
@@ -1848,7 +1889,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
         if (this.searchview) {
             this.searchview.stop();
         }
-        this.searchview = new openerp.base.SearchView(null, this.session,
+        this.searchview = new openerp.base.SearchView(this,
                 this.element_id + "_search", this.dataset, false, {
                     "selectable": !this.options.disable_multiple_selection,
                     "deletable": false
@@ -1876,7 +1917,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
             $sbutton.click(function() {
                 self.on_select_elements(self.selected_ids);
             });
-            self.view_list = new openerp.base.form.SelectCreateListView( null, self.session,
+            self.view_list = new openerp.base.form.SelectCreateListView(self,
                     self.element_id + "_view_list", self.dataset, false,
                     {'deletable': false});
             self.view_list.popup = self;
@@ -1891,7 +1932,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
         if (!this.options.auto_create)
             return;
         var self = this;
-        var wdataset = new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain);
+        var wdataset = new openerp.base.DataSetSearch(this, this.model, this.context, this.domain);
         wdataset.parent_view = this.options.parent_view;
         wdataset.create(data, function(r) {
             self.on_select_elements([r.result]);
@@ -1916,8 +1957,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
             this.view_list.$element.hide();
         }
         this.dataset.index = null;
-        this.view_form = new openerp.base.FormView(null, this.session,
-                this.element_id + "_view_form", this.dataset, false);
+        this.view_form = new openerp.base.FormView(this, this.element_id + "_view_form", this.dataset, false);
         if (this.options.alternative_form_view) {
             this.view_form.set_embedded_view(this.options.alternative_form_view);
         }
@@ -1974,8 +2014,7 @@ openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
     },
     start: function() {
         this._super();
-        this.dataset = new openerp.base.ReadOnlyDataSetSearch(this.session, this.model,
-            this.context);
+        this.dataset = new openerp.base.ReadOnlyDataSetSearch(this, this.model, this.context);
         this.dataset.ids = [this.row_id];
         this.dataset.index = 0;
         this.dataset.parent_view = this.options.parent_view;
@@ -1986,7 +2025,7 @@ openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
         if (!this.options.auto_write)
             return;
         var self = this;
-        var wdataset = new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain);
+        var wdataset = new openerp.base.DataSetSearch(this, this.model, this.context, this.domain);
         wdataset.parent_view = this.options.parent_view;
         wdataset.write(id, data, function(r) {
             self.on_write_completed();
@@ -1995,8 +2034,7 @@ openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
     on_write_completed: function() {},
     setup_form_view: function() {
         var self = this;
-        this.view_form = new openerp.base.FormView(null, this.session,
-                this.element_id + "_view_form", this.dataset, false);
+        this.view_form = new openerp.base.FormView(this, this.element_id + "_view_form", this.dataset, false);
         if (this.options.alternative_form_view) {
             this.view_form.set_embedded_view(this.options.alternative_form_view);
         }
@@ -2018,10 +2056,10 @@ openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
     }
 });
 
-openerp.base.form.FieldReference = openerp.base.form.Field.extend({
+openerp.base.form.FieldReference = openerp.base.form.FieldChar.extend({
     init: function(view, node) {
         this._super(view, node);
-        this.template = "FieldReference";
+        //this.template = "FieldReference";
     }
 });
 
@@ -2039,6 +2077,10 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({
     },
     set_value_from_ui: function() {
     },
+    update_dom: function() {
+        this._super.apply(this, arguments);
+        this.$element.find('.oe-binary').toggle(!this.readonly);
+    },
     human_filesize : function(size) {
         var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
         var i = 0;
@@ -2136,6 +2178,13 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({
         this._super.apply(this, arguments);
         this.$image = this.$element.find('img.oe-binary-image');
     },
+    set_value: function(value) {
+        this._super.apply(this, arguments);
+        this.set_image_maxwidth();
+        var url = '/base/binary/image?session_id=' + this.session.session_id + '&model=' +
+            this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime())
+        this.$image.attr('src', url);
+    },
     set_image_maxwidth: function() {
         this.$image.css('max-width', this.$element.width());
     },
@@ -2151,13 +2200,6 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({
     on_clear: function() {
         this._super.apply(this, arguments);
         this.$image.attr('src', '/base/static/src/img/placeholder.png');
-    },
-    set_value: function(value) {
-        this._super.apply(this, arguments);
-        this.set_image_maxwidth();
-        var url = '/base/binary/image?session_id=' + this.session.session_id + '&model=' +
-            this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime())
-        this.$image.attr('src', url);
     }
 });
 
@@ -2176,8 +2218,10 @@ openerp.base.form.widgets = new openerp.base.Registry({
     'url' : 'openerp.base.form.FieldUrl',
     'text' : 'openerp.base.form.FieldText',
     'text_wiki' : 'openerp.base.form.FieldText',
-    'date' : 'openerp.base.form.FieldDate',
-    'datetime' : 'openerp.base.form.FieldDatetime',
+//    'date' : 'openerp.base.form.FieldDate',
+//    'datetime' : 'openerp.base.form.FieldDatetime',
+    'date' : 'openerp.base.form.FieldChar',
+    'datetime' : 'openerp.base.form.FieldChar',
     'selection' : 'openerp.base.form.FieldSelection',
     'many2one' : 'openerp.base.form.FieldMany2One',
     'many2many' : 'openerp.base.form.FieldMany2Many',
diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js
index 4aa06cc8152..9a48889d801 100644
--- a/addons/base/static/src/js/list-editable.js
+++ b/addons/base/static/src/js/list-editable.js
@@ -103,18 +103,27 @@ openerp.base.list.editable = function (openerp) {
          * Checks if a record is being edited, and if so cancels it
          */
         cancel_pending_edition: function () {
+            var self = this, cancelled = $.Deferred();
             if (!this.edition) {
-                return;
+                cancelled.resolve();
+                return cancelled.promise();
             }
 
             if (this.edition_index !== null) {
-                this.reload_record(this.edition_index);
+                this.reload_record(this.edition_index, true).then(function () {
+                    cancelled.resolve();
+                });
+            } else {
+                cancelled.resolve();
             }
-            this.edition_form.stop();
-            this.edition_form.$element.remove();
-            delete this.edition_form;
-            delete this.edition_index;
-            delete this.edition;
+            cancelled.then(function () {
+                self.edition_form.stop();
+                self.edition_form.$element.remove();
+                delete self.edition_form;
+                delete self.edition_index;
+                delete self.edition;
+            });
+            return cancelled.promise();
         },
         /**
          * Adapts this list's view description to be suitable to the inner form view of a row being edited.
@@ -134,66 +143,66 @@ openerp.base.list.editable = function (openerp) {
             return view;
         },
         render_row_as_form: function (row) {
-            this.cancel_pending_edition();
-
             var self = this;
-            var $new_row = $('', {
-                    id: _.uniqueId('oe-editable-row-'),
-                    'class': $(row).attr('class'),
-                    click: function (e) {e.stopPropagation();}
-                })
-                .delegate('button.oe-edit-row-save', 'click', function () {
-                    self.save_row();
-                })
-                .delegate('button.oe-edit-row-cancel', 'click', function () {
-                    self.cancel_edition();
-                })
-                .delegate('button', 'keyup', function (e) {
-                    e.stopImmediatePropagation();
-                })
-                .keyup(function (e) {
-                    switch (e.which) {
-                        case KEY_RETURN:
-                            self.save_row(true);
-                            break;
-                        case KEY_ESCAPE:
-                            self.cancel_edition();
-                            break;
-                        default:
-                            return;
-                    }
-                });
-            if (row) {
-                $new_row.replaceAll(row);
-            } else if (this.options.editable === 'top') {
-                this.$current.prepend($new_row);
-            } else if (this.options.editable) {
-                this.$current.append($new_row);
-            }
-            this.edition = true;
-            this.edition_index = this.dataset.index;
-            this.edition_form = _.extend(new openerp.base.FormView(
-                    null, this.group.view.session, $new_row.attr('id'),
-                    this.dataset, false), {
-                template: 'ListView.row.form',
-                registry: openerp.base.list.form.widgets
-            });
-            $.when(this.edition_form.on_loaded({fields_view: this.get_form_fields_view()})).then(function () {
-                // put in $.when just in case  FormView.on_loaded becomes asynchronous
-                $new_row.find('td')
-                      .addClass('oe-field-cell')
-                      .removeAttr('width')
-                  .end()
-                  .find('td:first').removeClass('oe-field-cell').end()
-                  .find('td:last').removeClass('oe-field-cell').end();
-                // pad in case of groupby
-                _(self.columns).each(function (column) {
-                    if (column.meta) {
-                        $new_row.prepend('');
-                    }
+            this.cancel_pending_edition().then(function () {
+                var $new_row = $('', {
+                        id: _.uniqueId('oe-editable-row-'),
+                        'class': $(row).attr('class'),
+                        click: function (e) {e.stopPropagation();}
+                    })
+                    .delegate('button.oe-edit-row-save', 'click', function () {
+                        self.save_row();
+                    })
+                    .delegate('button.oe-edit-row-cancel', 'click', function () {
+                        self.cancel_edition();
+                    })
+                    .delegate('button', 'keyup', function (e) {
+                        e.stopImmediatePropagation();
+                    })
+                    .keyup(function (e) {
+                        switch (e.which) {
+                            case KEY_RETURN:
+                                self.save_row(true);
+                                break;
+                            case KEY_ESCAPE:
+                                self.cancel_edition();
+                                break;
+                            default:
+                                return;
+                        }
+                    });
+                if (row) {
+                    $new_row.replaceAll(row);
+                } else if (self.options.editable === 'top') {
+                    self.$current.prepend($new_row);
+                } else if (self.options.editable) {
+                    self.$current.append($new_row);
+                }
+                self.edition = true;
+                self.edition_index = self.dataset.index;
+                self.edition_form = _.extend(new openerp.base.FormView(
+                        null, self.group.view.session, $new_row.attr('id'),
+                        self.dataset, false), {
+                    template: 'ListView.row.form',
+                    registry: openerp.base.list.form.widgets
                 });
+                $.when(self.edition_form.on_loaded({fields_view: self.get_form_fields_view()})).then(function () {
+                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
+                    $new_row.find('td')
+                          .addClass('oe-field-cell')
+                          .removeAttr('width')
+                      .end()
+                      .find('td:first').removeClass('oe-field-cell').end()
+                      .find('td:last').removeClass('oe-field-cell').end();
+                    // pad in case of groupby
+                    _(self.columns).each(function (column) {
+                        if (column.meta) {
+                            $new_row.prepend('');
+                        }
+                    });
 
-                self.edition_form.do_show();
+                    self.edition_form.do_show();
+                });
             });
         },
         /**
@@ -205,12 +214,7 @@ openerp.base.list.editable = function (openerp) {
         save_row: function (edit_next) {
             var self = this;
             this.edition_form.do_save(function (result) {
-                self.reload_record(self.dataset.index, true).then(function () {
-                    self.edition_form.stop();
-                    delete self.edition_form;
-                    delete self.edition_index;
-                    delete self.edition;
-
+                self.cancel_pending_edition().then(function () {
                     $(self).trigger('saved', [self.dataset]);
                     if (!edit_next) {
                         return;
diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js
index 49cd10c10b6..a2fca3aa89e 100644
--- a/addons/base/static/src/js/list.js
+++ b/addons/base/static/src/js/list.js
@@ -24,24 +24,37 @@ openerp.base.list = {
             ].join('')
         }
 
-        var record = row_data[column.id];
-        if (record.value === false) {
-            return value_if_empty === undefined ?  '' : value_if_empty;
+        var value = row_data[column.id].value;
+
+        // If NaN value, display as with a `false` (empty cell)
+        if (typeof value === 'number' && isNaN(value)) {
+            value = false;
+        }
+        switch (value) {
+            case false:
+            case Infinity:
+            case -Infinity:
+                return value_if_empty === undefined ?  '' : value_if_empty;
         }
         switch (column.widget || column.type) {
-            case 'many2one':
-                // name_get value format
-                return record.value[1];
+            case 'integer':
+                return _.sprintf('%d', value);
+            case 'float':
+                var precision = column.digits ? column.digits[1] : 2;
+                return _.sprintf('%.' + precision + 'f', value);
             case 'float_time':
                 return _.sprintf("%02d:%02d",
-                        Math.floor(record.value),
-                        Math.round((record.value % 1) * 60));
+                        Math.floor(value),
+                        Math.round((value % 1) * 60));
             case 'progressbar':
                 return _.sprintf(
                     '%.2f%%',
-                        record.value, record.value);
+                        value, value);
+            case 'many2one':
+                // name_get value format
+                return value[1];
             default:
-                return record.value;
+                return value;
         }
     }
 };
@@ -87,9 +100,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
      *
      * @borrows openerp.base.ActionExecutor#execute_action as #execute_action
      */
-    init: function(view_manager, session, element_id, dataset, view_id, options) {
-        this._super(session, element_id);
-        this.view_manager = view_manager || new openerp.base.NullViewManager();
+    init: function(parent, element_id, dataset, view_id, options) {
+        this._super(parent, element_id);
+        this.view_manager = parent || new openerp.base.NullViewManager();
         this.dataset = dataset;
         this.model = dataset.model;
         this.view_id = view_id;
@@ -100,7 +113,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
         this.flags =  this.view_manager.flags || {};
 
         this.set_groups(new openerp.base.ListView.Groups(this));
-        
+
         if (this.dataset instanceof openerp.base.DataSetStatic) {
             this.groups.datagroup = new openerp.base.StaticDataGroup(this.dataset);
         }
@@ -254,8 +267,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
                         })
                         .val(self._limit || 'NaN');
                 });
+        if(this.view_manager.sidebar)
+            this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
 
-        this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
     },
     /**
      * Configures the ListView pager based on the provided dataset's information
@@ -347,16 +361,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
                 }
                 var aggregation_func = column['group_operator'] || 'sum';
 
-                if (!column[aggregation_func]) {
-                    return {};
-                }
-
-                return {
-                    field: column.id,
-                    type: column.type,
+                return _.extend({}, column, {
                     'function': aggregation_func,
                     label: column[aggregation_func]
-                };
+                });
             });
     },
     /**
@@ -386,11 +394,12 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
     do_show: function () {
         this.$element.show();
         if (this.hidden) {
-            this.$element.find('table').append(
+            this.$element.find('.oe-listview-content').append(
                 this.groups.apoptosis().render());
             this.hidden = false;
         }
-        this.view_manager.sidebar.do_refresh(true);
+        if(this.view_manager.sidebar)
+            this.view_manager.sidebar.do_refresh(true);
     },
     do_hide: function () {
         this.$element.hide();
@@ -421,7 +430,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
      * re-renders the content of the list view
      */
     reload_content: function () {
-        this.$element.find('table').append(
+        this.$element.find('.oe-listview-content').append(
             this.groups.apoptosis().render(
                 $.proxy(this, 'compute_aggregates')));
     },
@@ -451,7 +460,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
         this.dataset.context = results.context;
         this.dataset.domain = results.domain;
         this.groups.datagroup = new openerp.base.DataGroup(
-            this.session, this.model,
+            this, this.model,
             results.domain, results.context,
             results.group_by);
         this.groups.datagroup.sort = this.dataset._sort;
@@ -475,6 +484,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
         var self = this;
         return $.when(this.dataset.unlink(ids)).then(function () {
             self.groups.drop_records(ids);
+            self.compute_aggregates();
         });
     },
     /**
@@ -503,11 +513,17 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
      * @param {Function} callback should be called after the action is executed, if non-null
      */
     do_action: function (name, id, callback) {
-        var action = _.detect(this.columns, function (field) {
+        var   self = this,
+            action = _.detect(this.columns, function (field) {
             return field.name === name;
         });
         if (!action) { return; }
-        this.execute_action(action, this.dataset, this.session.action_manager, id, callback);
+        this.execute_action(
+            action, this.dataset, this.session.action_manager, id, function () {
+                $.when(callback.apply(this, arguments).then(function () {
+                    self.compute_aggregates();
+                }));
+        });
     },
     /**
      * Handles the activation of a record (clicking on it)
@@ -554,7 +570,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
     compute_aggregates: function (records) {
         var columns = _(this.aggregate_columns).filter(function (column) {
             return column['function']; });
-
         if (_.isEmpty(columns)) { return; }
 
         if (_.isEmpty(records)) {
@@ -562,17 +577,39 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
         }
 
         var count = 0, sums = {};
-        _(columns).each(function (column) { sums[column.field] = 0; });
+        _(columns).each(function (column) {
+            switch (column['function']) {
+                case 'max':
+                    sums[column.id] = -Infinity;
+                    break;
+                case 'min':
+                    sums[column.id] = Infinity;
+                    break;
+                default:
+                    sums[column.id] = 0;
+            }
+        });
         _(records).each(function (record) {
             count += record.count || 1;
             _(columns).each(function (column) {
-                var field = column.field;
+                var field = column.id,
+                    value = record.values[field];
                 switch (column['function']) {
                     case 'sum':
-                        sums[field] += record.values[field];
+                        sums[field] += value;
                         break;
                     case 'avg':
-                        sums[field] += record.count * record.values[field];
+                        sums[field] += record.count * value;
+                        break;
+                    case 'min':
+                        if (sums[field] > value) {
+                            sums[field] = value;
+                        }
+                        break;
+                    case 'max':
+                        if (sums[field] < value) {
+                            sums[field] = value;
+                        }
                         break;
                 }
             });
@@ -580,14 +617,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
 
         var aggregates = {};
         _(columns).each(function (column) {
-            var field = column.field;
+            var field = column.id;
             switch (column['function']) {
-                case 'sum':
-                    aggregates[field] = sums[field];
-                    break;
                 case 'avg':
-                    aggregates[field] = sums[field] / count;
+                    aggregates[field] = {value: sums[field] / count};
                     break;
+                default:
+                    aggregates[field] = {value: sums[field]};
             }
         });
 
@@ -599,14 +635,14 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
             if (!column['function']) {
                 return;
             }
-            var pattern = (column.type == 'integer') ? '%d' : '%.2f';
-            $footer_cells.filter(_.sprintf('[data-field=%s]', column.field))
-                .text(_.sprintf(pattern, aggregation[column.field]));
+
+            $footer_cells.filter(_.sprintf('[data-field=%s]', column.id))
+                .html(openerp.base.list.render_cell(aggregation, column));
         });
     }
     // TODO: implement reorder (drag and drop rows)
 });
-openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List# */{
+openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.ListView.List# */{
     /**
      * List display for the ListView, handles basic DOM events and transforms
      * them in the relevant higher-level events, to which the list view (or
@@ -666,7 +702,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
                       index = self.row_position($row);
 
                 $(self).trigger('action', [field, record_id, function () {
-                    self.reload_record(index, true);
+                    return self.reload_record(index, true);
                 }]);
             })
             .delegate('tr', 'click', function (e) {
@@ -804,7 +840,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
 
         return $.when(read_p).then(function () {
             self.$current.children().eq(record_index)
-                .replaceWith(self.render_record(record_index)); })
+                .replaceWith(self.render_record(record_index)); });
     },
     /**
      * Renders a list record to HTML
@@ -842,7 +878,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
     }
     // drag and drop
 });
-openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Groups# */{
+openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.base.ListView.Groups# */{
     passtrough_events: 'action deleted row_link',
     /**
      * Grouped display for the ListView. Handles basic DOM events and interacts
@@ -979,8 +1015,8 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
                 row_data[group.grouped_on] = group;
                 var group_column = _(self.columns).detect(function (column) {
                     return column.id === group.grouped_on; });
-                $group_column.text(openerp.base.list.render_cell(
-                        row_data, group_column, "Undefined"
+                $group_column.html(openerp.base.list.render_cell(
+                    row_data, group_column, "Undefined"
                 ));
                 if (group.openable) {
                     // Make openable if not terminal group & group_by_no_leaf
@@ -1084,6 +1120,45 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
             });
         return d.promise();
     },
+    setup_resequence_rows: function (list, dataset) {
+        // drag and drop enabled if list is not sorted and there is a
+        // "sequence" column in the view.
+        if ((dataset.sort && dataset.sort())
+            || !_(this.columns).any(function (column) {
+                    return column.name === 'sequence'; })) {
+            return;
+        }
+        // ondrop, move relevant record & fix sequences
+        list.$current.sortable({
+            stop: function (event, ui) {
+                var from = ui.item.data('index'),
+                      to = ui.item.prev().data('index') || 0;
+                if (from === to) { return; }
+                list.rows.splice(to, 0, list.rows.splice(from, 1)[0]);
+
+                ui.item.parent().children().each(function (i, e) {
+                    // reset record-index accelerators on rows and even/odd
+                    var even = i%2 === 0;
+                    $(e).data('index', i)
+                        .toggleClass('even', even)
+                        .toggleClass('odd', !even);
+                });
+
+                // resequencing time!
+                var data, index = to,
+                    // if drag to 1st row (to = 0), start sequencing from 0
+                    // (exclusive lower bound)
+                    seq = to ? list.rows[to - 1].data.sequence.value : 0;
+                while (++seq, data = list.rows[index++].data) {
+                    data.sequence.value = seq;
+                    // write are independent from one another, so we can just
+                    // launch them all at the same time and we don't really
+                    // give a fig about when they're done
+                    dataset.write(data.id.value, {sequence: seq});
+                }
+            }
+        });
+    },
     render: function (post_render) {
         var self = this;
         var $element = $('');
@@ -1102,6 +1177,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
                     self.children[null] = list;
                     self.elements =
                         [list.$current.replaceAll($element)[0]];
+                    self.setup_resequence_rows(list, dataset);
                     if (post_render) { post_render(); }
                 });
             });
diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js
index d8b33e3d9a6..8a51abe1335 100644
--- a/addons/base/static/src/js/search.js
+++ b/addons/base/static/src/js/search.js
@@ -1,9 +1,9 @@
 openerp.base.search = function(openerp) {
 
 openerp.base.SearchView = openerp.base.Controller.extend({
-    init: function(view_manager, session, element_id, dataset, view_id, defaults) {
-        this._super(session, element_id);
-        this.view_manager = view_manager || new openerp.base.NullViewManager();
+    init: function(parent, element_id, dataset, view_id, defaults) {
+        this._super(parent, element_id);
+        this.view_manager = parent || new openerp.base.NullViewManager();
         this.dataset = dataset;
         this.model = dataset.model;
         this.view_id = view_id;
@@ -14,10 +14,13 @@ openerp.base.SearchView = openerp.base.Controller.extend({
         this.enabled_filters = [];
 
         this.has_focus = false;
+
+        this.ready = $.Deferred();
     },
     start: function() {
         //this.log('Starting SearchView '+this.model+this.view_id)
-        return this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
+        this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
+        return this.ready.promise();
     },
     show: function () {
         this.$element.show();
@@ -107,12 +110,13 @@ openerp.base.SearchView = openerp.base.Controller.extend({
         }
     },
     on_loaded: function(data) {
-        var lines = this.make_widgets(
-            data.fields_view['arch'].children,
-            data.fields_view.fields);
+        var self = this,
+           lines = this.make_widgets(
+                data.fields_view['arch'].children,
+                data.fields_view.fields);
 
         // for extended search view
-        var ext = new openerp.base.search.ExtendedSearch(null, this.session, this.model);
+        var ext = new openerp.base.search.ExtendedSearch(this, this.model);
         lines.push([ext]);
         this.inputs.push(ext);
         
@@ -121,6 +125,7 @@ openerp.base.SearchView = openerp.base.Controller.extend({
             'lines': lines,
             'defaults': this.defaults
         });
+
         // We don't understand why the following commented line does not work in Chrome but
         // the non-commented line does. As far as we investigated, only God knows.
         //this.$element.html(render);
@@ -132,30 +137,90 @@ openerp.base.SearchView = openerp.base.Controller.extend({
                 .submit(this.do_search)
                 .bind('reset', this.do_clear);
         // start() all the widgets
-        _(lines).chain().flatten().each(function (widget) {
-            widget.start();
+        var widget_starts = _(lines).chain().flatten().map(function (widget) {
+            return widget.start();
+        }).value();
+        
+        $.when.apply(null, widget_starts).then(function () {
+            self.ready.resolve();
         });
         
-        // filters management
-        this.$element.find(".oe_search-view-filters-management").change(this.on_filters_management);
+        this.reload_managed_filters();
+    },
+    reload_managed_filters: function() {
+        var self = this;
+        return this.rpc('/base/searchview/get_filters', {
+            model: this.dataset.model
+        }).then(function(result) {
+            self.managed_filters = result;
+            var filters = self.$element.find(".oe_search-view-filters-management");
+            filters.html(QWeb.render("SearchView.managed-filters", {filters: result}));
+            filters.change(self.on_filters_management);
+        });
     },
     /**
      * Handle event when the user make a selection in the filters management select box.
      */
     on_filters_management: function(e) {
+        var self = this;
         var select = this.$element.find(".oe_search-view-filters-management");
         var val = select.val();
-        select.val("_filters");
         
-        if (val.slice(0,1) == "_") // useless action
+        if (val.slice(0,1) == "_") { // useless action
+            select.val("_filters");
             return;
+        }
         if (val.slice(0, "get:".length) == "get:") {
             val = val.slice("get:".length);
-            //TODO niv
+            val = parseInt(val);
+            var filter = this.managed_filters[val];
+            this.on_search([filter.domain], [filter.context], []);
         } else if (val == "save_filter") {
-            //TODO niv
+            select.val("_filters");
+            var data = this.build_search_data();
+            var context = new openerp.base.CompoundContext();
+            _.each(data.contexts, function(x) {
+                context.add(x);
+            });
+            var domain = new openerp.base.CompoundDomain();
+            _.each(data.domains, function(x) {
+                domain.add(x);
+            });
+            var dial_html = QWeb.render("SearchView.managed-filters.add");
+            var $dial = $(dial_html);
+            $dial.dialog({
+                modal: true,
+                title: "Filter Entry",
+                buttons: {
+                    Cancel: function() {
+                        $(this).dialog("close");
+                    },
+                    OK: function() {
+                        $(this).dialog("close");
+                        var name = $(this).find("input").val();
+                        self.rpc('/base/searchview/save_filter', {
+                            model: self.dataset.model,
+                            context_to_save: context,
+                            domain: domain,
+                            name: name
+                        }).then(function(result) {
+                            self.reload_managed_filters();
+                        });
+                    },
+                }
+            });
         } else { // manage_filters
-            //TODO niv
+            select.val("_filters");
+            this.do_action({
+                res_model: 'ir.filters',
+                views: [[false, 'list'], [false, 'form']],
+                type: 'ir.actions.act_window',
+                context: {"search_default_user_id": this.session.uid,
+                "search_default_model_id": this.dataset.model},
+                target: "current",
+                limit : 80,
+                auto_search : true
+            });
         }
     },
     /**
@@ -170,11 +235,25 @@ openerp.base.SearchView = openerp.base.Controller.extend({
      * @param e jQuery event object coming from the "Search" button
      */
     do_search: function (e) {
+        // reset filters management
+        var select = this.$element.find(".oe_search-view-filters-management");
+        select.val("_filters");
+        
         if (e && e.preventDefault) { e.preventDefault(); }
 
-        var domains = [], contexts = [];
+        var data = this.build_search_data();
 
-        var errors = [];
+        if (data.errors.length) {
+            this.on_invalid(data.errors);
+            return;
+        }
+
+        this.on_search(data.domains, data.contexts, data.groupbys);
+    },
+    build_search_data: function() {
+        var domains = [],
+           contexts = [],
+             errors = [];
 
         _.each(this.inputs, function (input) {
             try {
@@ -196,19 +275,13 @@ openerp.base.SearchView = openerp.base.Controller.extend({
             }
         });
 
-        if (errors.length) {
-            this.on_invalid(errors);
-            return;
-        }
-
         // TODO: do we need to handle *fields* with group_by in their context?
         var groupbys = _(this.enabled_filters)
                 .chain()
                 .map(function (filter) { return filter.get_context();})
                 .compact()
                 .value();
-
-        this.on_search(domains, contexts, groupbys);
+        return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
     },
     /**
      * Triggered after the SearchView has collected all relevant domains and
@@ -293,8 +366,7 @@ openerp.base.search.fields = new openerp.base.Registry({
     'many2one': 'openerp.base.search.ManyToOneField',
     'many2many': 'openerp.base.search.ManyToManyField'
 });
-openerp.base.search.Invalid = Class.extend(
-    /** @lends openerp.base.search.Invalid# */{
+openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base.search.Invalid# */{
     /**
      * Exception thrown by search widgets when they hold invalid values,
      * which they can not return when asked.
@@ -314,8 +386,7 @@ openerp.base.search.Invalid = Class.extend(
                 ': [' + this.value + '] is ' + this.message);
     }
 });
-openerp.base.search.Widget = openerp.base.Controller.extend(
-    /** @lends openerp.base.search.Widget# */{
+openerp.base.search.Widget = openerp.base.Controller.extend( /** @lends openerp.base.search.Widget# */{
     template: null,
     /**
      * Root class of all search widgets
@@ -395,8 +466,6 @@ openerp.base.search.add_expand_listener = function($root) {
 };
 openerp.base.search.Group = openerp.base.search.Widget.extend({
     template: 'SearchView.group',
-    // TODO: contain stuff
-    // TODO: @expand
     init: function (view_section, view, fields) {
         this._super(view);
         this.attrs = view_section.attrs;
@@ -414,8 +483,7 @@ openerp.base.search.Group = openerp.base.search.Widget.extend({
     }
 });
 
-openerp.base.search.Input = openerp.base.search.Widget.extend(
-    /** @lends openerp.base.search.Input# */{
+openerp.base.search.Input = openerp.base.search.Widget.extend( /** @lends openerp.base.search.Input# */{
     /**
      * @constructs
      * @extends openerp.base.search.Widget
@@ -437,7 +505,6 @@ openerp.base.search.Input = openerp.base.search.Widget.extend(
 });
 openerp.base.search.Filter = openerp.base.search.Input.extend({
     template: 'SearchView.filter',
-    // TODO: force rendering
     init: function (node, view) {
         this._super(view);
         this.attrs = node.attrs;
@@ -486,13 +553,9 @@ openerp.base.search.Filter = openerp.base.search.Input.extend({
         return this.attrs.domain;
     }
 });
-openerp.base.search.Field = openerp.base.search.Input.extend(
-    /** @lends openerp.base.search.Field# */ {
+openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp.base.search.Field# */ {
     template: 'SearchView.field',
     default_operator: '=',
-    // TODO: set default values
-    // TODO: get context, domain
-    // TODO: holds Filters
     /**
      * @constructs
      * @extends openerp.base.search.Input
@@ -530,9 +593,7 @@ openerp.base.search.Field = openerp.base.search.Input.extend(
     },
     get_domain: function () {
         var val = this.get_value();
-
-        var has_value = (val !== null && val !== '');
-        if(!has_value) {
+        if (val === null || val === '') {
             return;
         }
 
@@ -544,9 +605,7 @@ openerp.base.search.Field = openerp.base.search.Input.extend(
                 this.get_value()
             ]];
         }
-        return _.extend(
-            {}, domain,
-            {own_values: {self: val}});
+        return _.extend({}, domain, {own_values: {self: val}});
     }
 });
 /**
@@ -559,8 +618,7 @@ openerp.base.search.Field = openerp.base.search.Input.extend(
  * @class
  * @extends openerp.base.search.Field
  */
-openerp.base.search.CharField = openerp.base.search.Field.extend(
-    /** @lends openerp.base.search.CharField# */ {
+openerp.base.search.CharField = openerp.base.search.Field.extend( /** @lends openerp.base.search.CharField# */ {
     default_operator: 'ilike',
     get_value: function () {
         return this.$element.val();
@@ -599,32 +657,40 @@ openerp.base.search.BooleanField = openerp.base.search.Field.extend({
         }
     }
 });
-openerp.base.search.IntegerField = openerp.base.search.Field.extend({
+openerp.base.search.NumberField = openerp.base.search.Field.extend(/** @lends openerp.base.search.NumberField# */{
     get_value: function () {
         if (!this.$element.val()) {
             return null;
         }
-        var val = parseInt(this.$element.val());
-        var check = Number(this.$element.val());
-        if (isNaN(check) || val !== check) {
+        var val = this.parse(this.$element.val()),
+          check = Number(this.$element.val());
+        if (isNaN(val) || val !== check) {
             this.$element.addClass('error');
             throw new openerp.base.search.Invalid(
-                this.attrs.name, this.$element.val(), "not a valid integer");
+                this.attrs.name, this.$element.val(), this.error_message);
         }
         this.$element.removeClass('error');
         return val;
     }
 });
-openerp.base.search.FloatField = openerp.base.search.Field.extend({
-    get_value: function () {
-        var val = Number(this.$element.val());
-        if (isNaN(val)) {
-            this.$element.addClass('error');
-            throw new openerp.base.search.Invalid(
-                this.attrs.name, this.$element.val(), "not a valid number");
-        }
-        this.$element.removeClass('error');
-        return val;
+/**
+ * @class
+ * @extends openerp.base.search.NumberField
+ */
+openerp.base.search.IntegerField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.IntegerField# */{
+    error_message: "not a valid integer",
+    parse: function (value) {
+        return parseInt(value, 10);
+    }
+});
+/**
+ * @class
+ * @extends openerp.base.search.NumberField
+ */
+openerp.base.search.FloatField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.FloatField# */{
+    error_message: "not a valid number",
+    parse: function (value) {
+        return parseFloat(value);
     }
 });
 openerp.base.search.SelectionField = openerp.base.search.Field.extend({
@@ -633,12 +699,7 @@ openerp.base.search.SelectionField = openerp.base.search.Field.extend({
         return this.$element.val();
     }
 });
-/**
- * @class
- * @extends openerp.base.search.Field
- */
-openerp.base.search.DateField = openerp.base.search.Field.extend(
-    /** @lends openerp.base.search.DateField# */{
+openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends openerp.base.search.DateField# */{
     template: 'SearchView.fields.date',
     /**
      * enables date picker on the HTML widgets
@@ -663,8 +724,12 @@ openerp.base.search.DateField = openerp.base.search.Field.extend(
     get_values: function () {
         var values_array = this.$element.find('input').serializeArray();
 
-        var from = values_array[0].value;
-        var to = values_array[1].value;
+        if (!values_array || !values_array[0]) {
+            throw new openerp.base.search.Invalid(
+                this.attrs.name, null, "widget not ready");
+        }
+        var from = values_array[0].value,
+              to = values_array[1].value;
 
         var field_values = {};
         if (from) {
@@ -709,22 +774,88 @@ openerp.base.search.DateField = openerp.base.search.Field.extend(
 openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({
     // TODO: time?
 });
-openerp.base.search.OneToManyField = openerp.base.search.IntegerField.extend({
+openerp.base.search.OneToManyField = openerp.base.search.CharField.extend({
     // TODO: .relation, .context, .domain
 });
-openerp.base.search.ManyToOneField = openerp.base.search.IntegerField.extend({
+openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
     // TODO: @widget
     // TODO: .relation, .selection, .context, .domain
+    init: function (view_section, field, view) {
+        this._super(view_section, field, view);
+        var self = this;
+        this.got_name = $.Deferred().then(function () {
+            self.$element.val(self.name);
+        });
+        this.dataset = new openerp.base.DataSet(
+                this.view, this.attrs['relation']);
+    },
+    start: function () {
+        this._super();
+        this.setup_autocomplete();
+        var started = $.Deferred();
+        this.got_name.then(function () { started.resolve();},
+                           function () { started.resolve(); });
+        return started.promise();
+    },
+    setup_autocomplete: function () {
+        var self = this;
+        this.$element.autocomplete({
+            source: function (req, resp) {
+                self.dataset.name_search(
+                    req.term, self.attrs.domain, 'ilike', 8, function (data) {
+                        resp(_.map(data, function (result) {
+                            return {id: result[0], label: result[1]}
+                        }));
+                });
+            },
+            select: function (event, ui) {
+                self.id = ui.item.id;
+                self.name = ui.item.label;
+            },
+            delay: 0
+        })
+    },
+    on_name_get: function (name_get) {
+        if (!name_get.length) {
+            delete this.id;
+            this.got_name.reject();
+            return;
+        }
+        this.name = name_get[0][1];
+        this.got_name.resolve();
+    },
+    render: function (defaults) {
+        if (defaults[this.attrs.name]) {
+            this.id = defaults[this.attrs.name];
+            // TODO: maybe this should not be completely removed
+            delete defaults[this.attrs.name];
+            this.dataset.name_get([this.id], $.proxy(this, 'on_name_get'));
+        } else {
+            this.got_name.reject();
+        }
+        return this._super(defaults);
+    },
+    get_domain: function () {
+        if (this.id && this.name) {
+            if (this.$element.val() === this.name) {
+                return [[this.attrs.name, '=', this.id]];
+            } else {
+                delete this.id;
+                delete this.name;
+            }
+        }
+        return this._super();
+    }
 });
-openerp.base.search.ManyToManyField = openerp.base.search.IntegerField.extend({
+openerp.base.search.ManyToManyField = openerp.base.search.CharField.extend({
     // TODO: .related_columns (Array), .context, .domain
 });
 
 openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
     template: 'SearchView.extended_search',
     identifier_prefix: 'extended-search',
-    init: function (parent, session, model) {
-        this._super(parent, session);
+    init: function (parent, model) {
+        this._super(parent);
         this.model = model;
     },
     add_group: function() {
@@ -780,7 +911,9 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
     },
     check_last_element: function() {
         _.each(this.children, function(x) {x.set_last_group(false);});
-        this.children[this.children.length - 1].set_last_group(true);
+        if (this.children.length >= 1) {
+            this.children[this.children.length - 1].set_last_group(true);
+        }
     }
 });
 
@@ -893,7 +1026,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
             if (! e instanceof openerp.base.KeyNotFound) {
                 throw e;
             }
-            var type = "char";
+            type = "char";
             this.log('Unknow field type ' + e.key);
         }
         this.value = new (openerp.base.search.custom_filters.get_object(type))
@@ -994,12 +1127,11 @@ openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.BaseWidget.
         {value: "<=", text: "less or equal than"}
     ],
     get_value: function() {
-        val = this.$element.val();
-        val2 = parseFloat(val);
-        if(val2 != 0 && !val2) {
+        var value = parseFloat(this.$element.val());
+        if(value != 0 && !value) {
             return "";
         }
-        return Math.round(val2);
+        return Math.round(value);
     }
 });
 openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.extend({
@@ -1014,12 +1146,11 @@ openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.ex
         {value: "<=", text: "less or equal than"}
     ],
     get_value: function() {
-        val = this.$element.val();
-        val2 = parseFloat(val);
-        if(val2 != 0 && !val2) {
+        var value = parseFloat(this.$element.val());
+        if(value != 0 && !value) {
             return "";
         }
-        return val2;
+        return value;
     }
 });
 openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.BaseWidget.extend({
diff --git a/addons/base/static/src/js/view_help.js b/addons/base/static/src/js/view_help.js
new file mode 100644
index 00000000000..704c982eb3a
--- /dev/null
+++ b/addons/base/static/src/js/view_help.js
@@ -0,0 +1,15 @@
+/*---------------------------------------------------------
+ * OpenERP base library
+ *---------------------------------------------------------*/
+
+openerp.base.view_help = function(openerp) {
+
+openerp.base.ProcessView = openerp.base.Controller.extend({
+});
+
+openerp.base.HelpView = openerp.base.Controller.extend({
+});
+
+};
+
+// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/base/static/src/js/tree.js b/addons/base/static/src/js/view_tree.js
similarity index 93%
rename from addons/base/static/src/js/tree.js
rename to addons/base/static/src/js/view_tree.js
index e333c295473..ceb5478a4a7 100644
--- a/addons/base/static/src/js/tree.js
+++ b/addons/base/static/src/js/view_tree.js
@@ -2,7 +2,7 @@
  * OpenERP base library
  *---------------------------------------------------------*/
 
-openerp.base.tree = function(openerp) {
+openerp.base.view_tree = function(openerp) {
 
 openerp.base.views.add('tree', 'openerp.base.TreeView');
 openerp.base.TreeView = openerp.base.Controller.extend({
diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js
index 0e0fb4a40b2..94ae1325b83 100644
--- a/addons/base/static/src/js/views.js
+++ b/addons/base/static/src/js/views.js
@@ -7,18 +7,30 @@ openerp.base.views = function(openerp) {
 openerp.base.client_actions = new openerp.base.Registry();
 openerp.base.ActionManager = openerp.base.Controller.extend({
 // process all kind of actions
-    init: function(session, element_id) {
-        this._super(session, element_id);
+    init: function(parent, element_id) {
+        this._super(parent, element_id);
         this.viewmanager = null;
         this.current_dialog = null;
         // Temporary linking view_manager to session.
-        // Will use controller_parent to find it when implementation will be done.
-        session.action_manager = this;
+        // Will use parent to find it when implementation will be done.
+        this.session.action_manager = this;
     },
     /**
      * Process an action
      * Supported actions: act_window
      */
+    action_window: function() {
+    },
+    action_window_close: function() {
+    },
+    action_server: function() {
+    },
+    action_url: function() {
+    },
+    action_report: function() {
+    },
+    action_client: function() {
+    },
     do_action: function(action, on_closed) {
         action.flags = _.extend({
             sidebar : action.target != 'new',
@@ -41,7 +53,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
             action.flags.new_window = true;
         }
         if (action.target == 'new') {
-            var dialog = this.current_dialog = new openerp.base.ActionDialog(this.session, {
+            var dialog = this.current_dialog = new openerp.base.ActionDialog(this, {
                 title: action.name,
                 width: '50%'
             });
@@ -49,7 +61,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
                 dialog.close_callback = on_closed;
             }
             dialog.start(false);
-            var viewmanager = dialog.viewmanager = new openerp.base.ViewManagerAction(this.session, dialog.element_id, action);
+            var viewmanager = dialog.viewmanager = new openerp.base.ViewManagerAction(this, dialog.element_id, action);
             viewmanager.start();
             dialog.open();
         } else if (action.flags.new_window) {
@@ -58,12 +70,15 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
                 var url = window.location.protocol + "//" + window.location.host +
                         window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
                 window.open(url);
+                if (on_closed) {
+                    on_closed();
+                }
             });
         } else {
             if (this.viewmanager) {
                 this.viewmanager.stop();
             }
-            this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action);
+            this.viewmanager = new openerp.base.ViewManagerAction(this, this.element_id, action);
             this.viewmanager.start();
         }
     },
@@ -81,7 +96,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
     },
     'ir.actions.client': function (action) {
         var Handler = openerp.base.client_actions.get_object(action.tag);
-        new Handler(this.session, this.element_id, action['kwargs']).start();
+        new Handler(this, this.element_id, action['kwargs']).start();
     },
     close_dialog: function () {
         if (this.current_dialog) {
@@ -91,9 +106,22 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
     }
 });
 
+openerp.base.ActionDialog = openerp.base.Dialog.extend({
+    identifier_prefix: 'action_dialog',
+    stop: function() {
+        this._super(this, arguments);
+        if (this.close_callback) {
+            this.close_callback();
+        }
+        if (this.viewmanager) {
+            this.viewmanager.stop();
+        }
+    }
+});
+
 openerp.base.ViewManager =  openerp.base.Controller.extend({
-    init: function(session, element_id, dataset, views) {
-        this._super(session, element_id);
+    init: function(parent, element_id, dataset, views) {
+        this._super(parent, element_id);
         this.model = dataset.model;
         this.dataset = dataset;
         this.searchview = null;
@@ -140,7 +168,7 @@ openerp.base.ViewManager =  openerp.base.Controller.extend({
         if (!view.controller) {
             // Lazy loading of views
             var controllerclass = this.registry.get_object(view_type);
-            var controller = new controllerclass( this, this.session, this.element_id + "_view_" + view_type,
+            var controller = new controllerclass( this, this.element_id + "_view_" + view_type,
                 this.dataset, view.view_id, view.options);
             if (view.embedded_view) {
                 controller.set_embedded_view(view.embedded_view);
@@ -210,7 +238,7 @@ openerp.base.ViewManager =  openerp.base.Controller.extend({
         if (this.searchview) {
             this.searchview.stop();
         }
-        this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search", this.dataset, view_id, search_defaults);
+        this.searchview = new openerp.base.SearchView(this, this.element_id + "_search", this.dataset, view_id, search_defaults);
         if (this.flags.search_view === false) {
             this.searchview.hide();
         }
@@ -252,26 +280,30 @@ openerp.base.ViewManager =  openerp.base.Controller.extend({
 });
 
 openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.base.ViewManager, {
-    init: function() {
-        this._super();
+    init: function(parent) {
+        this._super(parent);
+        if(parent)
+            this.session = parent.session;
         this.action = {flags: {}};
         this.sidebar = new openerp.base.NullSidebar();
     }
 });
 
+// TODO Will move to action Manager
 openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
-    init: function(session, element_id, action) {
+    init: function(parent, element_id, action) {
+        this.session = parent.session;
         var dataset;
         if (!action.res_id) {
-            dataset = new openerp.base.DataSetSearch(session, action.res_model, action.context || null);
+            dataset = new openerp.base.DataSetSearch(this, action.res_model, action.context || null);
         } else {
-            dataset = new openerp.base.DataSetStatic(session, action.res_model, {}, [action.res_id]);
+            dataset = new openerp.base.DataSetStatic(this, action.res_model, {}, [action.res_id]);
             if (action.context) {
                 // TODO fme: should normalize all DataSets constructors to (session, model, context, ...)
                 dataset.context = action.context;
             }
         }
-        this._super(session, element_id, dataset, action.views);
+        this._super(parent, element_id, dataset, action.views);
         this.action = action;
         this.flags = this.action.flags || {};
         if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
@@ -342,19 +374,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
     }
 });
 
-openerp.base.ActionDialog = openerp.base.Dialog.extend({
-    identifier_prefix: 'action_dialog',
-    stop: function() {
-        this._super(this, arguments);
-        if (this.close_callback) {
-            this.close_callback();
-        }
-        if (this.viewmanager) {
-            this.viewmanager.stop();
-        }
-    }
-});
-
 openerp.base.Sidebar = openerp.base.BaseWidget.extend({
     template: "ViewManager.sidebar",
     init: function(parent, view_manager) {
@@ -409,7 +428,6 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
     }
 });
 
-
 openerp.base.NullSidebar = openerp.base.generate_null_object_class(openerp.base.Sidebar);
 
 openerp.base.Export = openerp.base.Dialog.extend({
@@ -500,12 +518,6 @@ openerp.base.View = openerp.base.Controller.extend({
  */
 openerp.base.views = new openerp.base.Registry();
 
-openerp.base.ProcessView = openerp.base.Controller.extend({
-});
-
-openerp.base.HelpView = openerp.base.Controller.extend({
-});
-
 openerp.base.json_node_to_xml = function(node, single_quote, indent) {
     // For debugging purpose, this function will convert a json node back to xml
     // Maybe usefull for xml view editor
diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml
index 3ff42864c5e..06e2b1b8e5c 100644
--- a/addons/base/static/src/xml/base.xml
+++ b/addons/base/static/src/xml/base.xml
@@ -62,7 +62,7 @@
                         
                         
                             
-                                
+                                
                             
                             
                                 
             
             
             
-        
+        
         
diff --git a/addons/base_default_home/static/src/js/home.js b/addons/base_default_home/static/src/js/home.js index ac319e21bc5..f90561fca76 100644 --- a/addons/base_default_home/static/src/js/home.js +++ b/addons/base_default_home/static/src/js/home.js @@ -26,7 +26,7 @@ openerp.base_default_home = function (openerp) { openerp.base.WebClient.prototype.default_home = function () { var self = this; var Installer = new openerp.base.DataSet( - this.session, 'base.setup.installer'); + this, 'base.setup.installer'); Installer.call('already_installed', [], function (installed_modules) { if (!_(installed_modules).isEmpty()) { return old_home.call(self); diff --git a/addons/base_gantt/static/lib/dhtmlxGantt/sources/dhtmlxgantt.js b/addons/base_gantt/static/lib/dhtmlxGantt/sources/dhtmlxgantt.js index 75b33fefc45..0d838fdd510 100644 --- a/addons/base_gantt/static/lib/dhtmlxGantt/sources/dhtmlxgantt.js +++ b/addons/base_gantt/static/lib/dhtmlxGantt/sources/dhtmlxgantt.js @@ -1162,7 +1162,7 @@ GanttProject.prototype.deleteChildTask = function(task) * @type: public * @topic: 1 */ -GanttProject.prototype.insertTask = function(id, name, EST, Duration, PercentCompleted, predecessorTaskId, parentTaskId) +GanttProject.prototype.insertTask = function(id, name, EST, Duration, PercentCompleted, predecessorTaskId, color, parentTaskId) { var task = null; var _task = null; @@ -1205,7 +1205,7 @@ GanttProject.prototype.insertTask = function(id, name, EST, Duration, PercentCom return false; } - task = new GanttTaskInfo(id, name, EST, Duration, PercentCompleted, predecessorTaskId); + task = new GanttTaskInfo(id, name, EST, Duration, PercentCompleted, predecessorTaskId, color); if (!this.Chart.checkPosParentTask(parentTask, task)) { this.Chart.Error.throwError("DATA_INSERT_ERROR", 19, [parentTaskId,id]); diff --git a/addons/base_gantt/static/src/js/gantt.js b/addons/base_gantt/static/src/js/gantt.js index 336454f0bd1..cf0ce449d60 100644 --- a/addons/base_gantt/static/src/js/gantt.js +++ b/addons/base_gantt/static/src/js/gantt.js @@ -24,7 +24,6 @@ init: function(view_manager, session, element_id, dataset, view_id) { this.date_delay = ""; this.date_stop = ""; this.color_field = ""; - this.day_lenth = 8; this.colors = []; this.color_values = []; this.calendar_fields = {}; @@ -58,6 +57,7 @@ init: function(view_manager, session, element_id, dataset, view_id) { this.parent = this.fields_view.arch.children[0].attrs.link; this.format = "yyyy-MM-dd"; + this.grp = []; self.create_gantt(); self.get_events(); @@ -81,7 +81,7 @@ init: function(view_manager, session, element_id, dataset, view_id) { get_events: function() { var self = this; - this.dataset.read_ids(this.dataset.ids, {}, function(result) { + this.dataset.read_slice(false, false, false, function(result) { self.load_event(result); }); @@ -91,7 +91,6 @@ init: function(view_manager, session, element_id, dataset, view_id) { var self = this; var result = events; - var project = {}; var smalldate = ""; COLOR_PALETTE = ['#ccccff', '#cc99ff', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400', @@ -118,13 +117,21 @@ init: function(view_manager, session, element_id, dataset, view_id) { } } } - project = new GanttProjectInfo(1, self.name, smalldate); - ganttChartControl.addProject(project); + if (smalldate == ""){ + smalldate = Date.today(); + } + project = new GanttProjectInfo("_1", "", smalldate); + ganttChartControl.addProject(project); } //create child var k = 0; var color_box = {}; + var parents = {}; + var all_events = {}; + var child_event = {}; + var temp_id = ""; + var final_events = []; for (i in show_event) { var res = show_event[i]; @@ -153,8 +160,129 @@ init: function(view_manager, session, element_id, dataset, view_id) { } if (duration == false) duration = 0 - task = new GanttTaskInfo(id, text, start_date, duration, 100, "", color_box[color]); - project.addTask(task); + + if (self.grp.length == 0){ + self.grp.push({'group_by' : this.parent}) + } + if (self.grp != undefined){ + for (j in self.grp){ + var grp_key = res[self.grp[j]['group_by']]; + if (typeof(grp_key) == "object"){ + grp_key = res[self.grp[j]['group_by']][1]; + } + else{ + grp_key = res[self.grp[j]['group_by']]; + } + + if (grp_key == false){ + grp_key = "False"; + } + + if (j == 0){ + if (parents[grp_key] == undefined){ + var mod_id = i+ "_" +j; + parents[grp_key] = mod_id; + child_event[mod_id] = {}; + all_events[mod_id] = {'parent': "", 'evt':[mod_id , grp_key, start_date, start_date, 100, "", "white"]}; + } + else{ + mod_id = parents[grp_key]; + } + temp_id = mod_id; + }else{ + if (child_event[mod_id][grp_key] == undefined){ + var ch_mod_id = i+ "_" +j; + child_event[mod_id][grp_key] = ch_mod_id; + child_event[ch_mod_id] = {}; + temp_id = ch_mod_id; + all_events[ch_mod_id] = {'parent': mod_id, 'evt':[ch_mod_id , grp_key, start_date, start_date, 100, "","white"]}; + mod_id = ch_mod_id; + } + else{ + mod_id = child_event[mod_id][grp_key]; + temp_id = mod_id; + } + } + } + all_events[id] = {'parent': temp_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]}; + final_events.push(id); + } + } + + for (i in final_events){ + var evt_id = final_events[i]; + var evt_date = all_events[evt_id]['evt'][2]; + while (all_events[evt_id]['parent'] != "") { + var parent_id =all_events[evt_id]['parent']; + if (all_events[parent_id]['evt'][2] > evt_date){ + all_events[parent_id]['evt'][2] = evt_date; + } + evt_id = parent_id; + } + } + var evt_id = []; + var evt_date = ""; + var evt_duration = ""; + var evt_end_date = ""; + + for (i in final_events){ + evt_id = final_events[i]; + evt_date = all_events[evt_id]['evt'][2]; + evt_duration = all_events[evt_id]['evt'][3]; + + evt_str_date = this.convert_date_str(evt_date); + evt_end_date = this.end_date(evt_str_date, evt_duration); + + while (all_events[evt_id]['parent'] != "") { + var parent_id =all_events[evt_id]['parent']; + if (all_events[parent_id]['evt'][3] < evt_end_date){ + all_events[parent_id]['evt'][3] = evt_end_date; + } + evt_id = parent_id; + } + } + + for (j in self.grp){ + for (i in all_events){ + res = all_events[i]; + if ((typeof(res['evt'][3])) == "object"){ + res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3]); + } + + k = res['evt'][0].toString().indexOf('_'); + if (k != -1){ + if (res['evt'][0].substring(k) == "_"+j){ + if (j == 0){ + task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]); + project.addTask(task); + } else { + task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]); + prt = project.getTaskById(res['parent']); + prt.addChildTask(task); + } + } + } + } + } + for (i in final_events){ + evt_id = final_events[i]; + res = all_events[evt_id]; + + task=new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]); + prt = project.getTaskById(res['parent']); + prt.addChildTask(task); + } + + oth_hgt = 264; + min_hgt = 150; + name_min_wdt = 150; + gantt_hgt = jQuery(window).height() - oth_hgt; + search_wdt = jQuery("#oe_app_search").width(); + + if (gantt_hgt > min_hgt){ + jQuery('#GanttDiv').height(gantt_hgt).width(search_wdt); + } else{ + jQuery('#GanttDiv').height(min_hgt).width(search_wdt); } ganttChartControl.create("GanttDiv"); @@ -163,6 +291,69 @@ init: function(view_manager, session, element_id, dataset, view_id) { ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");}); ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);}); + taskdiv = jQuery("div.taskPanel").parent(); + taskdiv.addClass('ganttTaskPanel'); + taskdiv.prev().addClass('ganttDayPanel'); + $gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel"); + + ganttrow = jQuery('.taskPanel').closest('tr'); + gtd = ganttrow.children(':first-child'); + gtd.children().addClass('task-name'); + + jQuery(".toggle-sidebar").click(function(e) { + self.set_width(); + }); + + jQuery(window).bind('resize',function(){ + window.clearTimeout(ganttChartControl._resize_timer); + ganttChartControl._resize_timer = window.setTimeout(function(){ + self.reload_gantt(); + }, 200); + }); + + jQuery("div #_1, div #_1 + div").hide(); + }, + + set_width: function() { + + $gantt_panel.width(1); + jQuery(".ganttTaskPanel").parent().width(1); + + search_wdt = jQuery("#oe_app_search").width(); + day_wdt = jQuery(".ganttDayPanel").children().children().width(); + name_wdt = jQuery('.task-name').width(); + jQuery('#GanttDiv').css('width','100%'); + + if (search_wdt - day_wdt <= name_min_wdt){ + jQuery(".ganttTaskPanel").parent().width(search_wdt - name_min_wdt); + jQuery(".ganttTaskPanel").width(search_wdt - name_min_wdt); + jQuery(".ganttDayPanel").width(search_wdt - name_min_wdt - 14); + jQuery('.task-name').width(name_min_wdt); + jQuery('.task-name').children().width(name_min_wdt); + }else{ + jQuery(".ganttTaskPanel").parent().width(day_wdt); + jQuery(".ganttTaskPanel").width(day_wdt); + jQuery(".taskPanel").width(day_wdt - 16); + jQuery(".ganttDayPanel").width(day_wdt -16); + jQuery('.task-name').width(search_wdt - day_wdt); + jQuery('.task-name').children().width(search_wdt - day_wdt); + } + + }, + + end_date: function(dat, duration) { + + var self = this; + + dat = this.convert_str_date(dat); + + var day = Math.floor(duration/self.day_length); + var hrs = duration % self.day_length; + + dat.add(day).days(); + dat.add(hrs).hour(); + + return dat; }, hours_between: function(date1, date2) { @@ -172,15 +363,18 @@ init: function(view_manager, session, element_id, dataset, view_id) { var date2_ms = date2.getTime(); var difference_ms = Math.abs(date1_ms - date2_ms); - d = Math.round(difference_ms / ONE_DAY); - h = Math.round((difference_ms % ONE_DAY)/(1000 * 60 * 60)); - - return (d * this.day_length) + h; + d = Math.floor(difference_ms / ONE_DAY); + h = (difference_ms % ONE_DAY)/(1000 * 60 * 60); + num = (d * this.day_length) + h; + return parseFloat(num.toFixed(2)); }, open_popup : function(task) { var event_id = task.getId(); + + if(event_id.toString().search("_") != -1) + return; if (event_id) { event_id = parseInt(event_id, 10); var dataset_event_index = jQuery.inArray(event_id, this.ids); @@ -214,6 +408,8 @@ init: function(view_manager, session, element_id, dataset, view_id) { var event_id = task.getId(); var data = {}; + if(event_id.toString().search("_") != -1) + return; if (evt == "drag"){ full_date = task.getEST().set({hour: self.hh, minute : self.mm, second:0}); data[this.date_start] = this.convert_date_str(full_date); @@ -272,9 +468,8 @@ init: function(view_manager, session, element_id, dataset, view_id) { }, do_search: function (domains, contexts, groupbys) { - var self = this; - + this.grp = groupbys; return this.rpc('/base/session/eval_domain_and_context', { domains: domains, contexts: contexts, diff --git a/addons/base_gantt/static/src/xml/base_gantt.xml b/addons/base_gantt/static/src/xml/base_gantt.xml index c85abf0cad8..d064d6e9ef2 100644 --- a/addons/base_gantt/static/src/xml/base_gantt.xml +++ b/addons/base_gantt/static/src/xml/base_gantt.xml @@ -1,12 +1,5 @@ diff --git a/addons/base_graph/static/src/js/graph.js b/addons/base_graph/static/src/js/graph.js index fdf3585449c..25ce024638b 100644 --- a/addons/base_graph/static/src/js/graph.js +++ b/addons/base_graph/static/src/js/graph.js @@ -16,9 +16,9 @@ QWeb.add_template('/base_graph/static/src/xml/base_graph.xml'); openerp.base.views.add('graph', 'openerp.base_graph.GraphView'); openerp.base_graph.GraphView = openerp.base.View.extend({ - init: function(view_manager, session, element_id, dataset, view_id) { - this._super(session, element_id); - this.view_manager = view_manager; + init: function(parent, element_id, dataset, view_id) { + this._super(parent, element_id); + this.view_manager = parent; this.dataset = dataset; this.dataset_index = 0; this.model = this.dataset.model; @@ -76,12 +76,20 @@ openerp.base_graph.GraphView = openerp.base.View.extend({ load_chart: function(data) { var self = this; + var domain = false; if(data){ this.x_title = this.all_fields[this.chart_info_fields]['string']; this.y_title = this.all_fields[this.operator_field]['string']; self.schedule_chart(data); }else{ - this.dataset.read_slice(this.fields, 0, false, function(res) { + if(! _.isEmpty(this.view_manager.dataset.domain)){ + domain = this.view_manager.dataset.domain; + }else if(! _.isEmpty(this.view_manager.action.domain)){ + domain = this.view_manager.action.domain; + } + this.dataset.domain = domain; + this.dataset.context = this.view_manager.dataset.context; + this.dataset.read_slice(_(this.fields).keys(), 0, false, function(res) { self.schedule_chart(res); }); } diff --git a/addons/base_graph/static/src/xml/base_graph.xml b/addons/base_graph/static/src/xml/base_graph.xml index f8dee0a91c6..4320b9f156e 100644 --- a/addons/base_graph/static/src/xml/base_graph.xml +++ b/addons/base_graph/static/src/xml/base_graph.xml @@ -1,6 +1,4 @@ \ No newline at end of file diff --git a/addons/web_mobile/static/src/css/web_mobile.css b/addons/web_mobile/static/src/css/web_mobile.css index e69de29bb2d..18cf93f4d66 100644 --- a/addons/web_mobile/static/src/css/web_mobile.css +++ b/addons/web_mobile/static/src/css/web_mobile.css @@ -0,0 +1,4 @@ + +.ui-collapsible-content-expanded { + display: block; +} diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js new file mode 100644 index 00000000000..138d37136a9 --- /dev/null +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -0,0 +1,217 @@ +openerp.web_mobile.chrome_mobile = function(openerp) { +openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({ + init: function(session, element_id) { + this._super(session, element_id); + }, + start: function() { + var self = this; + this.rpc('/base/session/sc_list',{} ,function(res){ + self.$element.html(QWeb.render("Shortcuts", {'sc' : res})) + self.$element.find("a").click(self.on_clicked); + }) + }, + on_clicked: function(ev) { + $shortcut = $(ev.currentTarget); + id = $shortcut.data('menu'); + res_id = $shortcut.data('res'); + jQuery("#oe_header").find("h1").html($shortcut.data('name')); + this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", res_id); + this.listview.start(); + } +}); +openerp.web_mobile.Header = openerp.base.Controller.extend({ + init: function(session, element_id) { + this._super(session, element_id); + }, + start: function() { + this.$element.html(QWeb.render("Header", this)); + this.$element.find("a").click(this.on_clicked); + }, + on_clicked: function(ev) { + $opt = $(ev.currentTarget); + current_id = $opt.attr('id'); + if (current_id == 'home') { + this.homepage = new openerp.web_mobile.Login(this.session, "oe_app"); + this.homepage.on_login_valid(); + } + } +}); +openerp.web_mobile.Secondary = openerp.base.Controller.extend({ + init: function(session, element_id, secondary_menu_id) { + this._super(session, element_id); + this.data = secondary_menu_id; + }, + start: function(ev, id) { + var v = { menu : this.data }; + this.$element.html(QWeb.render("Menu.secondary", v)); + this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click); + }, + on_menu_click: function(ev, id) { + $menu = $(ev.currentTarget); + id = $menu.data('menu'); + for (var i = 0; i < this.data.children.length; i++) { + if (this.data.children[i].id == id) { + this.children = this.data.children[i]; + } + } + jQuery("#oe_header").find("h1").html($menu.data('name')); + + var child_len = this.children.children.length; + if (child_len > 0) { + this.$element + .removeClass("secondary_menu") + .addClass("content_menu"); + //.hide(); + this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children); + this.secondary.start(); + } + else { + if (id) { + this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", id); + this.listview.start(); + } + } + } +}); + +openerp.web_mobile.Menu = openerp.base.Controller.extend({ + init: function(session, element_id, secondary_menu_id) { + this._super(session, element_id); + this.secondary_menu_id = secondary_menu_id; + this.$secondary_menu = $("#" + secondary_menu_id); + this.menu = false; + }, + start: function() { + this.rpc("/base/menu/load", {}, this.on_loaded); + }, + on_loaded: function(data) { + this.data = data; + this.$element.html(QWeb.render("Menu", this.data)); + this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click); + }, + on_menu_click: function(ev, id) { + $menu = $(ev.currentTarget); + id = $menu.data('menu'); + for (var i = 0; i < this.data.data.children.length; i++) { + if (this.data.data.children[i].id == id) { + this.children = this.data.data.children[i]; + } + } + jQuery("#oe_header").find("h1").html($menu.data('name')); + this.$element + .removeClass("login_valid") + .addClass("secondary_menu"); + //.hide(); + this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children); + this.secondary.start(); + } +}); +openerp.web_mobile.Options = openerp.base.Controller.extend({ + init: function(session, element_id) { + this._super(session, element_id); + }, + start: function() { + var self = this; + this.$element.html(QWeb.render("Options", this)); + self.$element.find("#logout").click(self.on_logout); + }, + on_logout: function(ev) { + this.session.logout(); + this.login = new openerp.web_mobile.Login(this.session, "oe_app"); + this.login.start(); + } +}); +openerp.web_mobile.Login = openerp.base.Controller.extend({ + init: function(session, element_id) { + this._super(session, element_id); + }, + start: function() { + var self = this; + + jQuery("#oe_header").children().remove(); + this.rpc("/base/session/get_databases_list", {}, function(result) { + self.db_list = result.db_list; + self.$element.html(QWeb.render("Login", self)); + self.$element.find('#database').click(self.on_db_select); + self.$element.find("#login").click(self.on_login); + $.mobile.initializePage(); + }) + }, + on_db_select: function(ev) { + var db = this.$element.find("#database option:selected").val(); + jQuery("#db_text").html(db); + }, + on_login: function(ev) { + ev.preventDefault(); + var self = this; + var $e = this.$element; + var db = $e.find("div select[name=database]").val(); + var login = $e.find("div input[name=login]").val(); + var password = $e.find("div input[name=password]").val(); + //$e.hide(); + // Should hide then call callback + this.session.session_login(db, login, password, function() { + if(self.session.session_is_valid()) { + self.on_login_valid(); + } else { + self.on_login_invalid(); + } + }); + }, + on_login_invalid: function() { + this.$element + .removeClass("login_valid") + .addClass("login_invalid") + .show(); + }, + on_login_valid: function() { + this.$element + .removeClass("login_invalid") + .addClass("login_valid"); + //.hide(); + this.$element.html(QWeb.render("HomePage", {})); + this.header = new openerp.web_mobile.Header(this.session, "oe_header"); + this.shortcuts = new openerp.web_mobile.Shortcuts(this.session, "oe_shortcuts"); + this.menu = new openerp.web_mobile.Menu(this.session, "oe_menu", "oe_secondary_menu"); + this.options = new openerp.web_mobile.Options(this.session, "oe_options"); + this.header.start(); + this.shortcuts.start(); + this.menu.start(); + this.options.start(); + jQuery("#oe_header").find("h1").html('Home'); + + }, + do_ask_login: function(continuation) { + this.on_login_invalid(); + this.on_login_valid.add({ + position: "last", + unique: true, + callback: continuation + }); + } +}); +openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({ + init: function(element_id) { + var self = this; + this._super(null, element_id); + QWeb.add_template("xml/web_mobile.xml"); + var params = {}; + + this.$element.html(QWeb.render("WebClient", {})); + this.session = new openerp.base.Session("oe_errors"); + this.crashmanager = new openerp.base.CrashManager(this.session); + this.login = new openerp.web_mobile.Login(this.session, "oe_app"); + this.session.on_session_invalid.add(this.login.do_ask_login); + }, + start: function() { + this.session.start(); + this.login.start(); + } +}); +openerp.web_mobile.mobilewebclient = function(element_id) { + // TODO Helper to start mobile webclient rename it openerp.base.webclient + var client = new openerp.web_mobile.MobileWebClient(element_id); + client.start(); + return client; +}; +} \ No newline at end of file diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js new file mode 100644 index 00000000000..9fa219ee86e --- /dev/null +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -0,0 +1,104 @@ +openerp.web_mobile.form_mobile = function (openerp) { +openerp.web_mobile.FormView = openerp.base.Controller.extend({ + init: function(session, element_id, list_id, action) { + this._super(session, element_id); + this.list_id = list_id; + this.action = action; + }, + start: function() { + var self = this; + id = this.list_id; + model = this.action.res_model; + view_id = this.action.views[1][0]; + + this.dataset = new openerp.base.DataSetSearch(this.session, this.action.res_model, null, null); + this.dataset.read_slice(false, false, false, function (result) { + + for (var i = 0; i < result.length; i++) { + if (result[i].id == id) { + var data = result[i]; + } + } + self.rpc("/base/formview/load", {"model": model, "view_id": view_id }, function (result) { + var view_fields = result.fields_view.arch.children; + var get_fields = self.get_fields(view_fields); + + for (var j = 0; j < view_fields.length; j++) { + if (view_fields[j].tag == 'notebook') { + var notebooks = view_fields[j]; + } + } + $("#oe_header").find("h1").html(result.fields_view.arch.attrs.string); + self.$element.html(QWeb.render("FormView", {'get_fields': get_fields, 'notebooks': notebooks || false, 'fields' : result.fields_view.fields, 'values' : data })); + + self.$element.find('a').click(function(){ + + for (var k = 0; k < notebooks.children.length; k++) { + + var text = $(this).find('.ui-btn-text').text(); + var next = $(this).next(); + var span = $(this).find('span .ui-icon'); + var content_expanded_class = "ui-collapsible-content ui-collapsible-content-expanded"; + var content_collapsed_class = "ui-collapsible-content ui-collapsible-content-collapsed"; + var expanded_class = "ui-collapsible-content-expanded"; + var collapsed_class = "ui-collapsible-content-collapsed"; + var plus_class = "ui-icon-plus"; + var minus_class = "ui-icon-minus"; + + if (notebooks.children[k].attrs.string == $.trim(text)) { + get_fields = self.get_fields(notebooks.children[k].children); + + $(this).parents().find('form').find('div').each( function() { + var prev = $(this).prev(); + var trim_class = $.trim($(this).attr('class')); + if (trim_class == content_expanded_class || trim_class == content_collapsed_class) { + prev.removeClass('ui-btn-active'); + + if ($.trim(prev.find('.ui-btn-text').text()) != notebooks.children[k].attrs.string) { + prev.find('span .ui-icon').removeClass(minus_class); + prev.find('span .ui-icon').addClass(plus_class); + $(this).removeClass(expanded_class); + $(this).addClass(collapsed_class); + } + } + }); + + if (next.hasClass(content_collapsed_class)) { + span.removeClass(plus_class); + span.addClass(minus_class); + next.removeClass(collapsed_class); + next.addClass(expanded_class); + } + else if (next.hasClass(content_expanded_class)) { + span.removeClass(minus_class); + span.addClass(plus_class); + next.removeClass(expanded_class); + next.addClass(collapsed_class); + } + if (!next.find('.detail').html().length) { + next.find('.detail').append(QWeb.render("FormView", {'get_fields': get_fields,'fields' : result.fields_view.fields, 'values' : data })); + } + } + } + }); + }); + }); + }, + get_fields: function(view_fields, fields) { + this.fields = fields || []; + for (var i=0; i < view_fields.length; i++){ + if (view_fields[i].tag == 'field') { + this.fields.push(view_fields[i]); + } + if (view_fields[i].tag == 'group') { + this.get_fields(view_fields[i].children, this.fields); + } + if (view_fields[i].tag == 'separator') { + this.get_fields(view_fields[i].children, this.fields); + } + } + return this.fields; + }, +}); + +} \ No newline at end of file diff --git a/addons/web_mobile/static/src/js/list_mobile.js b/addons/web_mobile/static/src/js/list_mobile.js new file mode 100644 index 00000000000..f7a88019e3d --- /dev/null +++ b/addons/web_mobile/static/src/js/list_mobile.js @@ -0,0 +1,67 @@ +openerp.web_mobile.list_mobile = function (openerp) { +openerp.web_mobile.ListView = openerp.base.Controller.extend({ + init: function(session, element_id, list_id) { + this._super(session, element_id); + this.list_id = list_id; + }, + start: function() { + this.rpc('/base/menu/action', {'menu_id': this.list_id}, + this.on_menu_action_loaded); + }, + on_menu_action_loaded: function(data) { + var self = this; + if (data.action.length) { + this.action = data.action[0][2]; + this.on_search_data(''); + } + }, + on_search_data: function(request){ + if(request){ + if(request.term){ + var search_val = request.term; + }else{ + if(request.which==27 || request.which==13 || request.which==9){ + var search_val = ''; + }else if(request.which==38 || request.which==40 || request.which==39 || request.which==37){ + return; + }else if($("#searchid").val()==""){ + var search_val = ''; + }else{ + return; + } + } + } + else{ + var search_val = ''; + } + var self = this; + var dataset = new openerp.base.DataSetStatic(this.session, this.action.res_model, this.action.context); + dataset.domain=[['name','ilike',search_val]]; + dataset.name_search(search_val, dataset.domain, 'ilike',false ,function(result){ + self.$element.html(QWeb.render("ListView", {'records' : result.result})); + self.$element.find("#searchid").focus(); + if(request.term){ + self.$element.find("#searchid").val(request.term); + } + self.$element.find("#searchid").autocomplete({ + source: function(req) { self.on_search_data(req); }, + focus: function(e, ui) { + e.preventDefault(); + }, + html: true, + minLength: 0, + delay: 0 + }); + self.$element.find("#searchid").keyup(self.on_search_data); + self.$element.find("a#list-id").click(self.on_list_click); + }); + }, + on_list_click: function(ev) { + $record = $(ev.currentTarget); + var self = this; + id = $record.data('id'); + this.formview = new openerp.web_mobile.FormView(this.session, "oe_app", id, this.action); + this.formview.start(); + } + }); +} \ No newline at end of file diff --git a/addons/web_mobile/static/src/js/web_mobile.js b/addons/web_mobile/static/src/js/web_mobile.js index 059c374d7d4..cdf25b22b38 100644 --- a/addons/web_mobile/static/src/js/web_mobile.js +++ b/addons/web_mobile/static/src/js/web_mobile.js @@ -1,257 +1,5 @@ - -openerp.web_mobile = function(openerp) { - -openerp.web_mobile = {}; - -openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({ - init: function(element_id) { - var self = this; - this._super(null, element_id); - QWeb.add_template("xml/web_mobile.xml"); - var params = {}; - - this.$element.html(QWeb.render("WebClient", {})); - this.session = new openerp.base.Session("oe_errors"); - this.crashmanager = new openerp.base.CrashManager(this.session); - this.login = new openerp.web_mobile.Login(this.session, "oe_app"); - - this.session.on_session_invalid.add(this.login.do_ask_login); - - }, - start: function() { - this.session.start(); - this.login.start(); - } -}); - -openerp.web_mobile.mobilewebclient = function(element_id) { - // TODO Helper to start mobile webclient rename it openerp.base.webclient - var client = new openerp.web_mobile.MobileWebClient(element_id); - client.start(); - return client; -}; - -openerp.web_mobile.Header = openerp.base.Controller.extend({ - init: function(session, element_id) { - this._super(session, element_id); - }, - start: function() { - this.$element.html(QWeb.render("Header", this)); - this.$element.find("a").click(this.on_clicked); - }, - on_clicked: function(ev) { - $opt = $(ev.currentTarget); - current_id = $opt.attr('id'); - if (current_id == 'home') { - this.homepage = new openerp.web_mobile.Login(this.session, "oe_app"); - this.homepage.on_login_valid(); - } - } - -}); - -openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({ - init: function(session, element_id) { - this._super(session, element_id); - }, - start: function() { - var self = this; - this.rpc('/base/session/sc_list',{} ,function(res){ - self.$element.html(QWeb.render("Shortcuts", {'sc' : res})) - self.$element.find("a").click(self.on_clicked); - }) - }, - on_clicked: function(ev) { - $shortcut = $(ev.currentTarget); - id = $shortcut.data('menu'); - res_id = $shortcut.data('res'); - jQuery("#oe_header").find("h1").html($shortcut.data('name')); - this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", res_id); - this.listview.start(); - } -}); - -openerp.web_mobile.ListView = openerp.base.Controller.extend({ - init: function(session, element_id, list_id) { - this._super(session, element_id); - this.list_id = list_id; - }, - start: function() { - this.rpc('/base/menu/action', {'menu_id': this.list_id}, - this.on_menu_action_loaded); - }, - on_menu_action_loaded: function(data) { - var self = this; - if (data.action.length) { - var action = data.action[0][2]; - self.on_action(action); - } - }, - on_action: function(action) { - var self = this; - var view_id = action.views[0][0]; - (new openerp.base.DataSetSearch(this.session, action.res_model, null, null)) - .read_slice(false, false, false, function(result){ - this.listview = new openerp.web_mobile.ListView(this.session, "oe_app"); - self.$element.html(QWeb.render("ListView", {'records' : result})); - }); - } - }); - -openerp.web_mobile.Secondary = openerp.base.Controller.extend({ - init: function(session, element_id, secondary_menu_id) { - this._super(session, element_id); - this.data = secondary_menu_id; - }, - start: function(ev, id) { - var v = { menu : this.data }; - this.$element.html(QWeb.render("Menu.secondary", v)); - this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click); - }, - on_menu_click: function(ev, id) { - $menu = $(ev.currentTarget); - id = $menu.data('menu'); - for (var i = 0; i < this.data.children.length; i++) { - if (this.data.children[i].id == id) { - this.children = this.data.children[i]; - } - } - jQuery("#oe_header").find("h1").html($menu.data('name')); - - var child_len = this.children.children.length; - if (child_len > 0) { - this.$element - .removeClass("secondary_menu") - .addClass("content_menu"); - //.hide(); - this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children); - this.secondary.start(); - } - else { - if (id) { - this.listview = new openerp.web_mobile.ListView(this.session, "oe_app", id); - this.listview.start(); - } - } - } -}); - -openerp.web_mobile.Menu = openerp.base.Controller.extend({ - init: function(session, element_id, secondary_menu_id) { - this._super(session, element_id); - this.secondary_menu_id = secondary_menu_id; - this.$secondary_menu = $("#" + secondary_menu_id); - this.menu = false; - }, - start: function() { - this.rpc("/base/menu/load", {}, this.on_loaded); - }, - on_loaded: function(data) { - this.data = data; - this.$element.html(QWeb.render("Menu", this.data)); - this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click); - }, - on_menu_click: function(ev, id) { - $menu = $(ev.currentTarget); - id = $menu.data('menu'); - for (var i = 0; i < this.data.data.children.length; i++) { - if (this.data.data.children[i].id == id) { - this.children = this.data.data.children[i]; - } - } - jQuery("#oe_header").find("h1").html($menu.data('name')); - this.$element - .removeClass("login_valid") - .addClass("secondary_menu"); - //.hide(); - this.secondary = new openerp.web_mobile.Secondary(this.session, "oe_app", this.children); - this.secondary.start(); - } -}); - -openerp.web_mobile.Options = openerp.base.Controller.extend({ - init: function(session, element_id) { - this._super(session, element_id); - }, - start: function() { - var self = this; - this.$element.html(QWeb.render("Options", this)); - self.$element.find("#logout").click(self.on_logout); - }, - on_logout: function(ev) { - this.session.logout(); - this.login = new openerp.web_mobile.Login(this.session, "oe_app"); - this.login.start(); - } -}); -openerp.web_mobile.Login = openerp.base.Controller.extend({ - init: function(session, element_id) { - this._super(session, element_id); - }, - start: function() { - var self = this; - - jQuery("#oe_header").children().remove(); - this.rpc("/base/session/get_databases_list", {}, function(result) { - self.db_list = result.db_list; - self.$element.html(QWeb.render("Login", self)); - self.$element.find('#database').click(self.on_db_select); - self.$element.find("#login").click(self.on_login); - $.mobile.initializePage(); - }) - }, - on_db_select: function(ev) { - var db = this.$element.find("#database option:selected").val(); - jQuery("#db_text").html(db); - }, - on_login: function(ev) { - ev.preventDefault(); - var self = this; - var $e = this.$element; - var db = $e.find("div select[name=database]").val(); - var login = $e.find("div input[name=login]").val(); - var password = $e.find("div input[name=password]").val(); - //$e.hide(); - // Should hide then call callback - this.session.session_login(db, login, password, function() { - if(self.session.session_is_valid()) { - self.on_login_valid(); - } else { - self.on_login_invalid(); - } - }); - }, - on_login_invalid: function() { - this.$element - .removeClass("login_valid") - .addClass("login_invalid") - .show(); - }, - on_login_valid: function() { - this.$element - .removeClass("login_invalid") - .addClass("login_valid"); - //.hide(); - this.$element.html(QWeb.render("HomePage", {})); - this.header = new openerp.web_mobile.Header(this.session, "oe_header"); - this.shortcuts = new openerp.web_mobile.Shortcuts(this.session, "oe_shortcuts"); - this.menu = new openerp.web_mobile.Menu(this.session, "oe_menu", "oe_secondary_menu"); - this.options = new openerp.web_mobile.Options(this.session, "oe_options"); - this.header.start(); - this.shortcuts.start(); - this.menu.start(); - this.options.start(); - jQuery("#oe_header").find("h1").html('Home'); - - }, - do_ask_login: function(continuation) { - this.on_login_invalid(); - this.on_login_valid.add({ - position: "last", - unique: true, - callback: continuation - }); - } -}); - +openerp.web_mobile = function(instance) { + openerp.web_mobile.chrome_mobile(instance); + openerp.web_mobile.list_mobile(instance); + openerp.web_mobile.form_mobile(instance); }; diff --git a/addons/web_mobile/static/src/web_mobile.html b/addons/web_mobile/static/src/web_mobile.html index 893e15b0468..f30ead90e63 100755 --- a/addons/web_mobile/static/src/web_mobile.html +++ b/addons/web_mobile/static/src/web_mobile.html @@ -2,11 +2,13 @@ - Login Page OpenERP mobile + OpenERP Web Mobile - + + + @@ -17,21 +19,23 @@ - + - + + + + + - +
diff --git a/addons/web_mobile/static/src/xml/web_mobile.xml b/addons/web_mobile/static/src/xml/web_mobile.xml index 2f8a464ab72..fa5fe0c6b27 100644 --- a/addons/web_mobile/static/src/xml/web_mobile.xml +++ b/addons/web_mobile/static/src/xml/web_mobile.xml @@ -36,7 +36,7 @@ Choose Database...
- @@ -67,12 +67,17 @@
-
    + +
    • @@ -82,6 +87,151 @@
      + +
      +
      + +
      + + + + +
      +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      + +
      +
      + +
      +
      + + + + + + + + + + + + + +
      +
      +
      + +
      +
      + + + + + + + +
      +
      +
      + + +
      +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      +
      + + + + + +
      +
      +
      + +
      • diff --git a/openerpweb/nonliterals.py b/openerpweb/nonliterals.py index 8daf30c7423..5b61f39fc83 100644 --- a/openerpweb/nonliterals.py +++ b/openerpweb/nonliterals.py @@ -172,9 +172,8 @@ class Context(BaseContext): ctx = self.session.evaluation_context(context) if self.own: ctx.update(self.own) - return eval(self.get_context_string(), - SuperDict(ctx)) - + return eval(self.get_context_string(), SuperDict(ctx)) + class SuperDict(dict): def __getattr__(self, name): try: @@ -182,7 +181,7 @@ class SuperDict(dict): except KeyError: raise AttributeError(name) def __getitem__(self, key): - tmp = super(type(self), self).__getitem__(key) + tmp = super(SuperDict, self).__getitem__(key) if isinstance(tmp, dict): return SuperDict(tmp) return tmp @@ -208,8 +207,7 @@ class CompoundDomain(BaseDomain): ctx = dict(context or {}) ctx.update(self.get_eval_context() or {}) - ctx['context'] = ctx - + domain.session = self.session final_domain.extend(domain.evaluate(ctx)) return final_domain @@ -247,8 +245,7 @@ class CompoundContext(BaseContext): continue ctx.update(final_context) - ctx["context"] = ctx - + context_to_eval.session = self.session final_context.update(context_to_eval.evaluate(ctx)) return final_context diff --git a/openerpweb/openerpweb.py b/openerpweb/openerpweb.py index 49a2cbf8786..f3cd72fade0 100644 --- a/openerpweb/openerpweb.py +++ b/openerpweb/openerpweb.py @@ -179,10 +179,10 @@ class OpenERPSession(object): :returns: the augmented context :rtype: dict """ - d = {} - d.update(self.base_eval_context) + d = dict(self.base_eval_context) if context: d.update(context) + d['context'] = d return d def eval_context(self, context_to_eval, context=None): @@ -202,7 +202,6 @@ class OpenERPSession(object): ctx = dict( self.base_eval_context, **(context or {})) - ctx['context'] = ctx # adding the context of the session to send to the openerp server ccontext = nonliterals.CompoundContext(self.context, context_to_eval or {}) @@ -227,12 +226,9 @@ class OpenERPSession(object): if isinstance(domain, list): return domain - ctx = dict(context or {}) - ctx['context'] = ctx - cdomain = nonliterals.CompoundDomain(domain) cdomain.session = self - return cdomain.evaluate(ctx) + return cdomain.evaluate(context or {}) #---------------------------------------------------------- # OpenERP Web RequestHandler @@ -416,7 +412,7 @@ class Root(object): if path_addons not in sys.path: sys.path.insert(0, path_addons) for i in os.listdir(path_addons): - if i not in sys.modules: + if i not in addons_module: manifest_path = os.path.join(path_addons, i, '__openerp__.py') if os.path.isfile(manifest_path): manifest = eval(open(manifest_path).read()) @@ -454,7 +450,7 @@ class Root(object): #for the mobile web client we are supposed to use a different url to just add '/mobile' raise cherrypy.HTTPRedirect('/web_mobile/static/src/web_mobile.html', 301) else: - raise cherrypy.HTTPRedirect('/base/static/src/base.html', 301) + raise cherrypy.HTTPRedirect('/base/webclient/home', 301) default.exposed = True def main(argv):