bzr revid: sma@tinyerp.com-20110801100258-ohrqjm5hzt7mmxxc
This commit is contained in:
sma (Tiny) 2011-08-01 15:32:58 +05:30
commit 97b3779437
36 changed files with 3287 additions and 1598 deletions

View File

@ -3,4 +3,38 @@
"version" : "2.0",
"depends" : [],
'active': True,
'js' : [
"static/lib/datejs/date-en-US.js",
"static/lib/jquery/jquery-1.5.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/boot.js",
"static/src/js/core.js",
"static/src/js/dates.js",
"static/src/js/chrome.js",
"static/src/js/views.js",
"static/src/js/data.js",
"static/src/js/data_export.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",
"static/src/css/data_export.css",
],
}

View File

@ -10,6 +10,8 @@ import openerpweb.ast
import openerpweb.nonliterals
import cherrypy
import csv
import xml.dom.minidom
# Should move to openerpweb.Xml2Json
class Xml2Json:
@ -54,6 +56,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 ="""<!DOCTYPE html>
<html style="height: 100%%">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>OpenERP</title>
%s
<script type="text/javascript">
$(function() {
QWeb = new QWeb2.Engine();
openerp.init().base.webclient("oe");
});
</script>
<link rel="shortcut icon" href="/base/static/src/img/favicon.ico" type="image/x-icon"/>
%s
<!--[if lte IE 7]>
<link rel="stylesheet" href="/base/static/src/css/base-ie7.css" type="text/css"/>
<![endif]-->
</head>
<body id="oe" class="openerp"></body>
</html>
""".replace('\n'+' '*8,'\n')
# script tags
jslist = ['/base/webclient/js']
if 1: # debug == 1
jslist = manifest_glob(['base'], 'js')
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
# css tags
csslist = ['/base/webclient/css']
if 1: # debug == 1
csslist = manifest_glob(['base'], 'css')
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
r = template % (js, css)
return r
class Database(openerpweb.Controller):
_cp_path = "/base/database"
@ -71,37 +166,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 +182,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,
@ -179,7 +224,7 @@ class Session(openerpweb.Controller):
context, domain = eval_context_and_domain(req.session,
openerpweb.nonliterals.CompoundContext(*(contexts or [])),
openerpweb.nonliterals.CompoundDomain(*(domains or [])))
group_by_sequence = []
for candidate in (group_by_seq or []):
ctx = req.session.eval_context(candidate, context)
@ -190,7 +235,7 @@ class Session(openerpweb.Controller):
group_by_sequence.append(group_by)
else:
group_by_sequence.extend(group_by)
return {
'context': context,
'domain': domain,
@ -203,7 +248,7 @@ class Session(openerpweb.Controller):
This method store an action object in the session object and returns an integer
identifying that action. The method get_session_action() can be used to get
back the action.
:param the_action: The action to save in the session.
:type the_action: anything
:return: A key identifying the saved action.
@ -226,7 +271,7 @@ class Session(openerpweb.Controller):
"""
Gets back a previously saved action. This method can return None if the action
was saved since too much time (this case should be handled in a smart way).
:param key: The key given by save_session_action()
:type key: integer
:return: The saved action or None.
@ -264,7 +309,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()
@ -361,7 +406,7 @@ class Menu(openerpweb.Controller):
menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context)
menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']}
menu_items.append(menu_root)
# make a tree using parent_id
menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items)
for menu_item in menu_items:
@ -468,7 +513,7 @@ class DataSet(openerpweb.Controller):
record_map = dict((record['id'], record) for record in records)
return [record_map[id] for id in ids if record_map.get(id)]
@openerpweb.jsonrequest
def load(self, req, model, id, fields):
m = req.session.model(model)
@ -631,7 +676,7 @@ class View(openerpweb.Controller):
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`
@ -721,6 +766,34 @@ class SearchView(View):
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"
@ -832,4 +905,229 @@ class Action(openerpweb.Controller):
return clean_action(req.session.model('ir.actions.server').run(
[action_id], req.session.eval_context(req.context)), req.session)
#
def export_csv(fields, result):
import StringIO
fp = StringIO.StringIO()
writer = csv.writer(fp, quoting=csv.QUOTE_ALL)
writer.writerow(fields)
for data in result:
row = []
for d in data:
if isinstance(d, basestring):
d = d.replace('\n',' ').replace('\t',' ')
try:
d = d.encode('utf-8')
except:
pass
if d is False: d = None
row.append(d)
writer.writerow(row)
fp.seek(0)
data = fp.read()
fp.close()
return data
def export_xls(fieldnames, table):
import StringIO
try:
import xlwt
except ImportError:
common.error(_('Import Error.'), _('Please install xlwt library to export to MS Excel.'))
workbook = xlwt.Workbook()
worksheet = workbook.add_sheet('Sheet 1')
for i, fieldname in enumerate(fieldnames):
worksheet.write(0, i, str(fieldname))
worksheet.col(i).width = 8000 # around 220 pixels
style = xlwt.easyxf('align: wrap yes')
for row_index, row in enumerate(table):
for cell_index, cell_value in enumerate(row):
cell_value = str(cell_value)
cell_value = re.sub("\r", " ", cell_value)
worksheet.write(row_index + 1, cell_index, cell_value, style)
fp = StringIO.StringIO()
workbook.save(fp)
fp.seek(0)
data = fp.read()
fp.close()
#return data.decode('ISO-8859-1')
return unicode(data, 'utf-8', 'replace')
def node_attributes(node):
attrs = node.attributes
if not attrs:
return {}
# localName can be a unicode string, we're using attribute names as
# **kwargs keys and python-level kwargs don't take unicode keys kindly
# (they blow up) so we need to ensure all keys are ``str``
return dict([(str(attrs.item(i).localName), attrs.item(i).nodeValue)
for i in range(attrs.length)])
class Export(View):
_cp_path = "/base/export"
def fields_get(self, req, model):
Model = req.session.model(model)
fields = Model.fields_get(False, req.session.eval_context(req.context))
return fields
@openerpweb.jsonrequest
def get_fields(self, req, model, prefix='', name= '', field_parent=None, params={}):
import_compat = params.get("import_compat", False)
fields = self.fields_get(req, model)
field_parent_type = params.get("parent_field_type",False)
if import_compat and field_parent_type and field_parent_type == "many2one":
fields = {}
fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}})
records = []
fields_order = fields.keys()
fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', '')))
for index, field in enumerate(fields_order):
value = fields[field]
record = {}
if import_compat and value.get('readonly', False):
ok = False
for sl in value.get('states', {}).values():
for s in sl:
ok = ok or (s==['readonly',False])
if not ok: continue
id = prefix + (prefix and '/'or '') + field
nm = name + (name and '/' or '') + value['string']
record.update(id=id, string= nm, action='javascript: void(0)',
target=None, icon=None, children=[], field_type=value.get('type',False), required=value.get('required', False))
records.append(record)
if len(nm.split('/')) < 3 and value.get('relation', False):
if import_compat:
ref = value.pop('relation')
cfields = self.fields_get(req, ref)
if (value['type'] == 'many2many'):
record['children'] = []
record['params'] = {'model': ref, 'prefix': id, 'name': nm}
elif value['type'] == 'many2one':
record['children'] = [id + '/id', id + '/.id']
record['params'] = {'model': ref, 'prefix': id, 'name': nm}
else:
cfields_order = cfields.keys()
cfields_order.sort(lambda x,y: -cmp(cfields[x].get('string', ''), cfields[y].get('string', '')))
children = []
for j, fld in enumerate(cfields_order):
cid = id + '/' + fld
cid = cid.replace(' ', '_')
children.append(cid)
record['children'] = children or []
record['params'] = {'model': ref, 'prefix': id, 'name': nm}
else:
ref = value.pop('relation')
cfields = self.fields_get(req, ref)
cfields_order = cfields.keys()
cfields_order.sort(lambda x,y: -cmp(cfields[x].get('string', ''), cfields[y].get('string', '')))
children = []
for j, fld in enumerate(cfields_order):
cid = id + '/' + fld
cid = cid.replace(' ', '_')
children.append(cid)
record['children'] = children or []
record['params'] = {'model': ref, 'prefix': id, 'name': nm}
records.reverse()
return records
@openerpweb.jsonrequest
def save_export_lists(self, req, name, model, field_list):
result = {'resource':model, 'name':name, 'export_fields': []}
for field in field_list:
result['export_fields'].append((0, 0, {'name': field}))
return req.session.model("ir.exports").create(result, req.session.eval_context(req.context))
@openerpweb.jsonrequest
def exist_export_lists(self, req, model):
export_model = req.session.model("ir.exports")
return export_model.read(export_model.search([('resource', '=', model)]), ['name'])
@openerpweb.jsonrequest
def delete_export(self, req, export_id):
req.session.model("ir.exports").unlink(export_id, req.session.eval_context(req.context))
return True
@openerpweb.jsonrequest
def namelist(self,req, model, export_id):
result = self.get_data(req, model, req.session.eval_context(req.context))
ir_export_obj = req.session.model("ir.exports")
ir_export_line_obj = req.session.model("ir.exports.line")
field = ir_export_obj.read(export_id)
fields = ir_export_line_obj.read(field['export_fields'])
name_list = {}
[name_list.update({field['name']: result.get(field['name'])}) for field in fields]
return name_list
def get_data(self, req, model, context=None):
ids = []
context = context or {}
fields_data = {}
proxy = req.session.model(model)
fields = self.fields_get(req, model)
if not ids:
f1 = proxy.fields_view_get(False, 'tree', context)['fields']
f2 = proxy.fields_view_get(False, 'form', context)['fields']
fields = dict(f1)
fields.update(f2)
fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}})
def rec(fields):
_fields = {'id': 'ID' , '.id': 'Database ID' }
def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2):
fields_order = fields.keys()
fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', '')))
for field in fields_order:
fields_data[prefix_node+field] = fields[field]
if prefix_node:
fields_data[prefix_node + field]['string'] = '%s%s' % (prefix_value, fields_data[prefix_node + field]['string'])
st_name = fields[field]['string'] or field
_fields[prefix_node+field] = st_name
if fields[field].get('relation', False) and level>0:
fields2 = self.fields_get(req, fields[field]['relation'])
fields2.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}})
model_populate(fields2, prefix_node+field+'/', None, st_name+'/', level-1)
model_populate(fields)
return _fields
return rec(fields)
@openerpweb.jsonrequest
def export_data(self, req, model, fields, ids, domain, import_compat=False, export_format="csv", context=None):
context = req.session.eval_context(req.context)
modle_obj = req.session.model(model)
ids = ids or modle_obj.search(domain, context=context)
field = fields.keys()
result = modle_obj.export_data(ids, field , context).get('datas',[])
if not import_compat:
field = [val.strip() for val in fields.values()]
if export_format == 'xls':
return export_xls(field, result)
else:
return export_csv(field, result)

View File

@ -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<a.length;){if(S.call(a[c])===bx)b=b.concat(bu(a[c]));else b[b.length]=a[c]}return b}D={script:function(){be(V);var a=bu(arguments),b=D,c;if(bB){for(c=-1;++c<a.length;){if(B(a[c]))a[c]=a[c]();if(c===0){Y(function(){bt((typeof a[0]==q)?{src:a[0]}:a[0])})}else b=b.script(a[c]);b=b.wait()}}else{for(c=-1;++c<a.length;){if(B(a[c]))a[c]=a[c]()}Y(function(){for(c=-1;++c<a.length;){bt((typeof a[c]==q)?{src:a[c]}:a[c])}})}V=A(function(){s=i},5);return b},wait:function(a){be(V);s=i;if(!B(a))a=G;var b=H(t||J,l),c=b.trigger,d=function(){try{a()}catch(err){}c()};delete b.trigger;var g=function(){if(J&&!bk)bn=d;else d()};if(t&&!J)K.push(g);else Y(g);return b}};if(t){D.trigger=function(){var a,b=-1;while(a=K[++b])a();K=[]}}else D.trigger=G;return D}function bv(a){var b,c={},d={"UseCachePreload":"cache","UseLocalXHR":"xhr","UsePreloading":E,"AlwaysPreserveOrder":O,"AllowDuplicates":"dupe"},g={"AppendTo":z,"BasePath":"base"};for(b in d)g[b]=d[b];c.order=!(!v.order);for(b in g){if(g[P](b)&&v[g[b]]!=e)c[g[b]]=(a[b]!=e)?a[b]:v[g[b]]}for(b in d){if(d[P](b))c[d[b]]=!(!c[d[b]])}if(!c[E])c.cache=c.order=c.xhr=i;c.which=(c.which===w||c.which===L)?c.which:w;return c}p.$LAB={setGlobalDefaults:function(a){v=bv(a)},setOptions:function(a){return H(i,bv(a))},script:function(){return H().script.apply(e,arguments)},wait:function(){return H().wait.apply(e,arguments)}};(function(a,b,c){if(k[u]==e&&k[a]){k[u]="loading";k[a](b,c=function(){k.removeEventListener(b,c,i);k[u]=Z},i)}})("addEventListener","DOMContentLoaded")})(window);

View File

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>OpenERP</title>
<link rel="shortcut icon" href="/base/static/src/img/favicon.ico" type="image/x-icon"/>
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
<script type="text/javascript" src="/base/static/lib/underscore/underscore.js"></script>
<script type="text/javascript" src="/base/static/lib/underscore/underscore.string.js"></script>
<script type="text/javascript" src="/base/static/lib/qweb/qweb2.js"></script>
<!-- Uncomment in order to use legacy QWeb
<script type="text/javascript" src="/base/static/lib/qweb/qweb.js"></script>
-->
<script type="text/javascript" src="/base/static/lib/jquery/jquery-1.5.2.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ui.notify/js/jquery.notify.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.superfish/js/hoverIntent.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.superfish/js/superfish.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js"></script>
<script type="text/javascript" src="/base/static/lib/datejs/date-en-US.js"></script>
<script type="text/javascript" src="/base/static/lib/json/json2.js"></script>
<script type="text/javascript" src="/base/static/src/js/base.js"></script>
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
<script type="text/javascript" src="/base/static/src/js/data.js"></script>
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
<script type="text/javascript" src="/base/static/src/js/form.js"></script>
<script type="text/javascript" src="/base/static/src/js/list.js"></script>
<script type="text/javascript" src="/base/static/src/js/list-editable.js"></script>
<script type="text/javascript" src="/base/static/src/js/tree.js"></script>
<script type="text/javascript" src="/base/static/src/js/search.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui.notify/css/ui.notify.css" />
<link rel="stylesheet" type="text/css" href="/base/static/lib/jquery.superfish/css/superfish.css" media="screen">
<link rel="stylesheet" href="/base/static/src/css/base.css" type="text/css"/>
<!--[if lte IE 7]>
<link rel="stylesheet" href="/base/static/src/css/base-ie7.css" type="text/css"/>
<![endif]-->
<script type="text/javascript">
$(function() {
QWeb = window.QWeb || new QWeb2.Engine();
var oe = openerp.init();
oe.base.webclient("oe");
});
</script>
</head>
<body id="oe" class="openerp">
</body>
</html>

View File

@ -30,6 +30,9 @@ body.openerp {
.openerp .oe-number {
text-align: right !important;
}
.openerp .oe_hide {
display: none !important;
}
/* STATES */
.openerp .on_logged {
@ -595,6 +598,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;
}
@ -604,6 +611,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 {
@ -643,9 +654,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;
@ -675,29 +688,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;
@ -1010,6 +1023,10 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
.openerp .closed-sidebar .toggle-sidebar {
border-left: none;
}
.openerp li.oe_sidebar_print {
padding-left: 20px;
background: 1px 3px url(../img/icons/gtk-print.png) no-repeat;
}
.openerp.kitten-mode-activated .main_table {
background: url(http://placekitten.com/g/1500/800) repeat;

View File

@ -0,0 +1,85 @@
.openerp .oe_export_row tr{
background-color: #FFFFFF;
font-size: 0.8em;
height: 22px;
}
.openerp tr.ui-selected td {
background-color: #CCCCCC;
}
.openerp .oe_export_requiredfield {
background-color: #D2D2FF;
}
.openerp .oe_export_readonlyfield{
background-color: #999999;
}
.openerp .oe_export_row:hover{
background-color: #F3F3F3;
}
.openerp .oe_export_fields_selector_export {
width: 100%;
height: 400px;
}
.openerp .oe_export_fields_selector_left {
width: 50%;
}
.openerp div#left_field_panel {
overflow: scroll;
width: 100%;
height: 400px;
border: solid #999999 1px;
}
.openerp .oe_export_fields_selector_center {
width: 102px;
}
.openerp .oe_export_fields_selector_right {
width: 45%;
height: 400px;
}
.openerp .oe_export_fields_selector_export select{
width: 100%;
height: 100%;
}
.openerp .oe_export_tree_header{
border: 0.5px solid #E3E3E3;
text-align: left;
white-space: nowrap;
padding: 4px 5px;
background: url(/base/static/src/img/header.gif);
}
.openerp table.tree_grid{
border: 1px solid #E3E3E3;
text-align: left;
white-space: nowrap;
background-color:#E3E3E3;
border-collapse: collapse;
width: 100%;
}
.openerp table.tree_grid a:hover {
color: blue;
border: none;
}
.openerp table.tree_grid a {
color: #5F5C5C;
border: none;
display: block;
}
.openerp .oe_export_button_export {
border: 1px solid #006;
background-color: #F3F3F3;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

View File

@ -1,71 +1,5 @@
/*---------------------------------------------------------
* 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
// OpenERP Web Boostrap
//---------------------------------------------------------
/**
@ -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;
}
};
@ -121,8 +57,8 @@
// OpenERP base module split
//---------------------------------------------------------
/** @namespace */
openerp.base = function(instance) {
openerp.base.core(instance);
openerp.base.dates(instance);
openerp.base.chrome(instance);
openerp.base.data(instance);
@ -135,9 +71,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 +80,15 @@ 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);
}
if (openerp.base.data_export) {
openerp.base.data_export(instance);
}
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -4,325 +4,15 @@
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.KeyNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.KeyNotFound# */ {
/**
* Thrown when a key could not be found in a mapping
*
* @constructs
* @extends openerp.base.NotFound
* @param {String} key the key which could not be found
*/
init: function (key) {
this.key = key;
},
toString: function () {
return "The key " + this.key + " was not found";
}
});
openerp.base.ObjectNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.ObjectNotFound# */ {
/**
* Thrown when an object path does not designate a valid class or object
* in the openerp hierarchy.
*
* @constructs
* @extends openerp.base.NotFound
* @param {String} path the invalid object path
*/
init: function (path) {
this.path = path;
},
toString: function () {
return "Could not find any object of path " + this.path;
}
});
openerp.base.Registry = Class.extend( /** @lends openerp.base.Registry# */ {
/**
* Stores a mapping of arbitrary key (strings) to object paths (as strings
* as well).
*
* Resolves those paths at query time in order to always fetch the correct
* object, even if those objects have been overloaded/replaced after the
* registry was created.
*
* An object path is simply a dotted name from the openerp root to the
* object pointed to (e.g. ``"openerp.base.Session"`` for an OpenERP
* session object).
*
* @constructs
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
this.map = mapping || {};
},
/**
* Retrieves the object matching the provided key string.
*
* @param {String} key the key to fetch the object for
* @returns {Class} the stored class, to initialize
*
* @throws {openerp.base.KeyNotFound} if the object was not in the mapping
* @throws {openerp.base.ObjectNotFound} if the object path was invalid
*/
get_object: function (key) {
var path_string = this.map[key];
if (path_string === undefined) {
throw new openerp.base.KeyNotFound(key);
}
var object_match = openerp;
var path = path_string.split('.');
// ignore first section
for(var i=1; i<path.length; ++i) {
object_match = object_match[path[i]];
if (object_match === undefined) {
throw new openerp.base.ObjectNotFound(path_string);
}
}
return object_match;
},
/**
* Tries a number of keys, and returns the first object matching one of
* the keys.
*
* @param {Array} keys a sequence of keys to fetch the object for
* @returns {Class} the first class found matching an object
*
* @throws {openerp.base.KeyNotFound} if none of the keys was in the mapping
* @trows {openerp.base.ObjectNotFound} if a found object path was invalid
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
try {
return this.get_object(keys[i]);
} catch (e) {
if (e instanceof openerp.base.KeyNotFound) {
continue;
}
throw e;
}
}
throw new openerp.base.KeyNotFound(keys.join(','));
},
/**
* Adds a new key and value to the registry.
*
* This method can be chained.
*
* @param {String} key
* @param {String} object_path fully qualified dotted object path
* @returns {openerp.base.Registry} itself
*/
add: function (key, object_path) {
this.map[key] = object_path;
return this;
},
/**
* Creates and returns a copy of the current mapping, with the provided
* mapping argument added in (replacing existing keys if needed)
*
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
clone: function (mapping) {
return new openerp.base.Registry(
_.extend({}, this.map, mapping || {}));
}
});
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]);
}
}
}
},
/**
* Controller start
* event binding, rpc and callback calling required to initialize the
* object can happen here
*
* Returns a promise object letting callers (subclasses and direct callers)
* know when this component is done starting
*
* @returns {jQuery.Deferred}
*/
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();
},
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) {
$('<pre></pre>').text(v).appendTo($('body'));
}
if(notify && this.notification) {
this.notification.notify("Logging:",v);
}
}
}
}
});
/**
* Generates an inherited class that replaces all the methods by null methods (methods
* that does nothing and always return undefined).
*
* @param {Class} claz
* @param {dict} add Additional functions to override.
* @return {Class}
*/
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() {};
}
}
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
});
}
});
openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.base.Session# */{
openerp.base.Session = openerp.base.Widget.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";
@ -501,7 +191,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
for(var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
if(cookie.indexOf(nameEQ) === 0) {
return decodeURIComponent(cookie.substring(nameEQ.length));
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
}
}
return null;
@ -517,7 +207,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
set_cookie: function (name, value, ttl) {
ttl = ttl || 24*60*60*365;
document.cookie = [
this.element_id + '|' + name + '=' + encodeURIComponent(value),
this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
'max-age=' + ttl,
'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
].join(';');
@ -528,15 +218,20 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
load_modules: function() {
var self = this;
this.rpc('/base/session/modules', {}, function(result) {
self.module_list = result['modules'];
self.module_list = result;
var modules = self.module_list.join(',');
self.rpc('/base/session/csslist', {mods: modules}, self.do_load_css);
self.rpc('/base/session/jslist', {"mods": modules}, self.debug ? self.do_load_modules_debug : self.do_load_modules_prod);
if(self.debug || true) {
self.rpc('/base/webclient/csslist', {"mods": modules}, self.do_load_css);
self.rpc('/base/webclient/jslist', {"mods": modules}, self.do_load_js);
} else {
self.do_load_css(["/base/webclient/css?mods="+modules]);
self.do_load_js(["/base/webclient/js?mods="+modules]);
}
openerp._modules_loaded = true;
});
},
do_load_css: function (result) {
_.each(result.files, function (file) {
do_load_css: function (files) {
_.each(files, function (file) {
$('head').append($('<link>', {
'href': file,
'rel': 'stylesheet',
@ -544,16 +239,23 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
}));
});
},
do_load_modules_debug: function(result) {
$LAB.setOptions({AlwaysPreserveOrder: true})
.script(result.files)
.wait(this.on_modules_loaded);
},
do_load_modules_prod: function() {
// load merged ones
// /base/session/css?mod=mod1,mod2,mod3
// /base/session/js?mod=mod1,mod2,mod3
// use $.getScript(your_3rd_party-script.js); ? i want to keep lineno !
do_load_js: function(files) {
var self = this;
if(files.length != 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = file;
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
self.do_load_js(files);
};
document.head.appendChild(tag);
} else {
this.on_modules_loaded();
}
},
on_modules_loaded: function() {
for(var j=0; j<this.module_list.length; j++) {
@ -570,209 +272,34 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
}
});
// A controller takes an already existing element
// new()
// start()
openerp.base.Controller = openerp.base.BasicController.extend( /** @lends openerp.base.Controller# */{
/**
* Controller manifest used to declare standard controller attributes
*/
controller_manifest: {
register: null,
template: "",
element_post_prefix: false
},
/**
* Controller registry,
*/
controller_registry: {
},
/**
* Add a new child controller
*/
controller_get: function(key) {
return this.controller_registry[key];
// OR should build it ? setting parent correctly ?
// function construct(constructor, args) {
// function F() {
// return constructor.apply(this, args);
// }
// F.prototype = constructor.prototype;
// return new F();
// }
// var obj = this.controller_registry[key];
// if(obj) {
// return construct(obj, Array.prototype.slice.call(arguments, 1));
// }
},
controller_new: function(key) {
var self;
// OR should contrustct it ? setting parent correctly ?
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
var obj = this.controller_registry[key];
if(obj) {
// TODO Prepend parent
return construct(obj, Array.prototype.slice.call(arguments, 1));
}
},
/**
* @constructs
* @extends openerp.base.BasicController
*/
init: function(parent_or_session, element_id) {
this._super(element_id);
this.controller_parent = null;
this.controller_children = [];
if(parent_or_session) {
if(parent_or_session.session) {
this.parent = parent_or_session;
this.session = this.parent.session;
if(this.parent.children) {
this.parent.children.push(this);
}
} else {
// TODO remove Backward compatilbility
this.session = parent_or_session;
}
}
// Apply manifest options
if(this.controller_manifest) {
var register = this.controller_manifest.register;
// TODO accept a simple string
if(register) {
for(var i=0; i<register.length; i++) {
this.controller_registry[register[i]] = this;
}
}
// TODO if post prefix
//this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
}
},
/**
* Performs a JSON-RPC call
*
* @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
*/
rpc: function(url, data, success, error) {
// TODO: support additional arguments ?
return this.session.rpc(url, data, success, error);
}
});
// A widget is a controller that doesnt take an element_id
// it render its own html that you should insert into the dom
// and bind it a start()
//
// new()
// render() and insert it place it where you want
// start()
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, session) {
this._super(session);
this.children = [];
this.parent = null;
this.set_parent(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;
},
/**
* "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 () {
var tmp_children = this.children;
this.children = [];
_.each(tmp_children, function(x) {
x.stop();
openerp.base.Notification = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.$element.notify({
speed: 500,
expires: 1500
});
if(this.$element != null) {
this.$element.remove();
}
this.set_parent(null);
this._super();
},
/**
* Set the parent of this component, also un-register the previous parent
* if there was one.
*
* @param {openerp.base.BaseWidget} parent The new parent.
*/
set_parent: function(parent) {
if(this.parent) {
this.parent.children = _.without(this.parent.children, this);
}
this.parent = parent;
if(this.parent) {
parent.children.push(this);
}
notify: function(title, text) {
this.$element.notify('create', {
title: title,
text: text
});
},
/**
* 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 : {}));
warn: function(title, text) {
this.$element.notify('create', 'oe_notification_alert', {
title: title,
text: text
});
}
});
openerp.base.Dialog = openerp.base.BaseWidget.extend({
openerp.base.Dialog = openerp.base.OldWidget.extend({
dialog_title: "",
identifier_prefix: 'dialog',
init: function (session, options) {
this._super(null, session);
init: function (parent, options) {
var self = this;
this._super(parent);
this.options = {
modal: true,
width: 'auto',
@ -782,7 +309,10 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
min_height: 0,
max_height: '100%',
autoOpen: false,
buttons: {}
buttons: {},
beforeClose: function () {
self.on_close();
}
};
for (var f in this) {
if (f.substr(0, 10) == 'on_button_') {
@ -845,10 +375,14 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
this.set_options(options);
this.$dialog.dialog(this.options).dialog('open');
},
close: function(options) {
close: function() {
// Closes the dialog but leave it in a state where it could be opened again.
this.$dialog.dialog('close');
},
on_close: function() {
},
stop: function () {
// Destroy widget
this.close();
this.$dialog.dialog('destroy');
}
@ -856,8 +390,8 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
openerp.base.CrashManager = openerp.base.Dialog.extend({
identifier_prefix: 'dialog_crash',
init: function(session) {
this._super(session);
init: function(parent) {
this._super(parent);
this.session.on_rpc_error.add(this.on_rpc_error);
},
on_button_Ok: function() {
@ -890,12 +424,9 @@ openerp.base.CrashManager = openerp.base.Dialog.extend({
}
});
openerp.base.Loading = openerp.base.Controller.extend({
controller_manifest: {
register: ["Loading"]
},
init: function(session, element_id) {
this._super(session, element_id);
openerp.base.Loading = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.count = 0;
this.session.on_rpc_request.add_first(this.on_rpc_event, 1);
this.session.on_rpc_response.add_last(this.on_rpc_event, -1);
@ -912,13 +443,13 @@ openerp.base.Loading = openerp.base.Controller.extend({
}
});
openerp.base.Database = openerp.base.Controller.extend({
openerp.base.Database = openerp.base.Widget.extend({
});
openerp.base.Login = openerp.base.Controller.extend({
openerp.base.Login = openerp.base.Widget.extend({
remember_creditentials: true,
init: function(session, element_id) {
this._super(session, element_id);
init: function(parent, element_id) {
this._super(parent, element_id);
this.has_local_storage = typeof(localStorage) != 'undefined';
this.selected_db = null;
this.selected_login = null;
@ -993,9 +524,9 @@ openerp.base.Login = openerp.base.Controller.extend({
}
});
openerp.base.Header = openerp.base.Controller.extend({
init: function(session, element_id) {
this._super(session, element_id);
openerp.base.Header = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
},
start: function() {
this.do_update();
@ -1007,9 +538,9 @@ openerp.base.Header = openerp.base.Controller.extend({
on_logout: function() {}
});
openerp.base.Menu = openerp.base.Controller.extend({
init: function(session, element_id, secondary_menu_id) {
this._super(session, element_id);
openerp.base.Menu = openerp.base.Widget.extend({
init: function(parent, element_id, secondary_menu_id) {
this._super(parent, element_id);
this.secondary_menu_id = secondary_menu_id;
this.$secondary_menu = $("#" + secondary_menu_id);
this.menu = false;
@ -1088,45 +619,43 @@ openerp.base.Menu = openerp.base.Controller.extend({
}
});
openerp.base.Homepage = openerp.base.Controller.extend({
openerp.base.Homepage = openerp.base.Widget.extend({
});
openerp.base.Preferences = openerp.base.Controller.extend({
openerp.base.Preferences = openerp.base.Widget.extend({
});
openerp.base.ImportExport = openerp.base.Controller.extend({
openerp.base.ImportExport = openerp.base.Widget.extend({
});
openerp.base.WebClient = openerp.base.Controller.extend({
openerp.base.WebClient = openerp.base.Widget.extend({
init: function(element_id) {
var self = this;
this._super(null, element_id);
QWeb.add_template("xml/base.xml");
QWeb.add_template("/base/static/src/xml/base.xml");
var params = {};
if(jQuery.param != undefined &&
jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
if(jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.$element.addClass("kitten-mode-activated");
}
this.$element.html(QWeb.render("Interface", params));
this.session = new openerp.base.Session("oe_errors");
this.loading = new openerp.base.Loading(this.session, "oe_loading");
this.crashmanager = new openerp.base.CrashManager(this.session);
this.session = new openerp.base.Session(this,"oe_errors");
this.loading = new openerp.base.Loading(this,"oe_loading");
this.crashmanager = new openerp.base.CrashManager(this);
this.crashmanager.start(false);
// Do you autorize this ?
openerp.base.Controller.prototype.notification = new openerp.base.Notification("oe_notification");
// Do you autorize this ? will be replaced by notify() in controller
openerp.base.Widget.prototype.notification = new openerp.base.Notification(this, "oe_notification");
this.header = new openerp.base.Header(this.session, "oe_header");
this.login = new openerp.base.Login(this.session, "oe_login");
this.header = new openerp.base.Header(this, "oe_header");
this.login = new openerp.base.Login(this, "oe_login");
this.header.on_logout.add(this.login.on_logout);
this.session.on_session_invalid.add(this.login.do_ask_login);
this.session.on_session_valid.add_last(this.header.do_update);
this.session.on_session_valid.add_last(this.on_logged);
this.menu = new openerp.base.Menu(this.session, "oe_menu", "oe_secondary_menu");
this.menu = new openerp.base.Menu(this, "oe_menu", "oe_secondary_menu");
this.menu.on_action.add(this.on_menu_action);
},
start: function() {
@ -1137,9 +666,9 @@ openerp.base.WebClient = openerp.base.Controller.extend({
this.notification.notify("OpenERP Client", "The openerp client has been initialized.");
},
on_logged: function() {
this.action_manager = new openerp.base.ActionManager(this.session, "oe_app");
this.action_manager = new openerp.base.ActionManager(this, "oe_app");
this.action_manager.start();
// if using saved actions, load the action and give it to action manager
var parameters = jQuery.deparam(jQuery.param.querystring());
if(parameters["s_action"] != undefined) {

View File

@ -0,0 +1,575 @@
/*---------------------------------------------------------
* OpenERP controller framework
*--------------------------------------------------------*/
openerp.base.core = function(openerp) {
/**
* John Resig Class with factory improvement
*/
(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;
};
})();
// todo change john resig class to keep window clean
openerp.base.Class = window.Class
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)
});
};
/**
* Generates an inherited class that replaces all the methods by null methods (methods
* that does nothing and always return undefined).
*
* @param {Class} claz
* @param {dict} add Additional functions to override.
* @return {Class}
*/
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() {};
}
}
if (prototype.prototype)
copy_proto(prototype.prototype);
};
copy_proto(claz.prototype);
newer.init = openerp.base.Widget.prototype.init;
var tmpclass = claz.extend(newer);
return tmpclass.extend(add || {});
};
/**
* Base error for lookup failure
*
* @class
*/
openerp.base.NotFound = openerp.base.Class.extend( /** @lends openerp.base.NotFound# */ {
});
openerp.base.KeyNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.KeyNotFound# */ {
/**
* Thrown when a key could not be found in a mapping
*
* @constructs
* @extends openerp.base.NotFound
* @param {String} key the key which could not be found
*/
init: function (key) {
this.key = key;
},
toString: function () {
return "The key " + this.key + " was not found";
}
});
openerp.base.ObjectNotFound = openerp.base.NotFound.extend( /** @lends openerp.base.ObjectNotFound# */ {
/**
* Thrown when an object path does not designate a valid class or object
* in the openerp hierarchy.
*
* @constructs
* @extends openerp.base.NotFound
* @param {String} path the invalid object path
*/
init: function (path) {
this.path = path;
},
toString: function () {
return "Could not find any object of path " + this.path;
}
});
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).
*
* Resolves those paths at query time in order to always fetch the correct
* object, even if those objects have been overloaded/replaced after the
* registry was created.
*
* An object path is simply a dotted name from the openerp root to the
* object pointed to (e.g. ``"openerp.base.Session"`` for an OpenERP
* session object).
*
* @constructs
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
this.map = mapping || {};
},
/**
* Retrieves the object matching the provided key string.
*
* @param {String} key the key to fetch the object for
* @returns {Class} the stored class, to initialize
*
* @throws {openerp.base.KeyNotFound} if the object was not in the mapping
* @throws {openerp.base.ObjectNotFound} if the object path was invalid
*/
get_object: function (key) {
var path_string = this.map[key];
if (path_string === undefined) {
throw new openerp.base.KeyNotFound(key);
}
var object_match = openerp;
var path = path_string.split('.');
// ignore first section
for(var i=1; i<path.length; ++i) {
object_match = object_match[path[i]];
if (object_match === undefined) {
throw new openerp.base.ObjectNotFound(path_string);
}
}
return object_match;
},
/**
* Tries a number of keys, and returns the first object matching one of
* the keys.
*
* @param {Array} keys a sequence of keys to fetch the object for
* @returns {Class} the first class found matching an object
*
* @throws {openerp.base.KeyNotFound} if none of the keys was in the mapping
* @trows {openerp.base.ObjectNotFound} if a found object path was invalid
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
try {
return this.get_object(keys[i]);
} catch (e) {
if (e instanceof openerp.base.KeyNotFound) {
continue;
}
throw e;
}
}
throw new openerp.base.KeyNotFound(keys.join(','));
},
/**
* Adds a new key and value to the registry.
*
* This method can be chained.
*
* @param {String} key
* @param {String} object_path fully qualified dotted object path
* @returns {openerp.base.Registry} itself
*/
add: function (key, object_path) {
this.map[key] = object_path;
return this;
},
/**
* Creates and returns a copy of the current mapping, with the provided
* mapping argument added in (replacing existing keys if needed)
*
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
clone: function (mapping) {
return new openerp.base.Registry(
_.extend({}, this.map, mapping || {}));
}
});
/**
* Utility class that any class is allowed to extend to easy common manipulations.
*
* It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
* logging facility.
*/
openerp.base.SessionAware = openerp.base.Class.extend({
init: function(session) {
this.session = session;
// 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]);
}
}
}
},
/**
* Performs a JSON-RPC call
*
* @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
*/
rpc: function(url, data, success, error) {
return this.session.rpc(url, data, success, error);
},
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) {
$('<pre></pre>').text(v).appendTo($('body'));
}
if(notify && this.notification) {
this.notification.notify("Logging:",v);
}
}
}
}
});
/**
* Base class for all visual components. Provides a lot of functionalities helpful
* for the management of a part of the DOM.
*
* Widget handles:
* - Rendering with QWeb.
* - Life-cycle management and parenting (when a parent is destroyed, all its children are
* destroyed too).
* - Insertion in DOM.
*
* Widget also extends SessionAware for ease of use.
*
* Guide to create implementations of the Widget class:
* ==============================================
*
* Here is a sample child class:
*
* MyWidget = openerp.base.Widget.extend({
* // the name of the QWeb template to use for rendering
* template: "MyQWebTemplate",
* // identifier prefix, it is useful to put an obvious one for debugging
* identifier_prefix: 'my-id-prefix-',
*
* init: function(parent) {
* this._super(parent);
* // stuff that you want to init before the rendering
* },
* start: function() {
* this._super();
* // stuff you want to make after the rendering, `this.$element` holds a correct value
* this.$element.find(".my_button").click(/* an example of event binding * /);
*
* // if you have some asynchronous operations, it's a good idea to return
* // a promise in start()
* var promise = this.rpc(...);
* return promise;
* }
* });
*
* Now this class can simply be used with the following syntax:
*
* var my_widget = new MyWidget(this);
* my_widget.appendTo($(".some-div"));
*
* With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
* DOM inside the ".some-div" div and its events were binded.
*
* And of course, when you don't need that widget anymore, just do:
*
* my_widget.stop();
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
openerp.base.Widget = openerp.base.SessionAware.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the default 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 generic identifier will be used.
*
* @type string
*/
identifier_prefix: 'generic-identifier-',
/**
* @constructs
* Construct the widget and set its parent if a parent is given.
*
* @param {Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling stop(), the current instance will be
* destroyed too. Can be null.
* @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
* to bind the current Widget to an already existing part of the DOM, which is not compatible
* with the DOM insertion methods provided by the current implementation of Widget. So
* for new components this argument should not be provided any more.
*/
init: function(parent, /** @deprecated */ element_id) {
this._super((parent || {}).session);
// if given an element_id, try to get the associated DOM element and save
// a reference in this.$element. Else just generate a unique identifier.
this.element_id = element_id;
this.element_id = this.element_id || _.uniqueId(this.identifier_prefix);
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : undefined;
this.widget_parent = parent;
this.widget_children = [];
if(parent && parent.widget_children) {
parent.widget_children.push(this);
}
// useful to know if the widget was destroyed and should not be used anymore
this.widget_is_stopped = false;
},
/**
* Render the current widget and appends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
appendTo: function(target) {
var self = this;
return this._render_and_insert(function(t) {
self.$element.appendTo(t);
}, target);
},
/**
* Render the current widget and prepends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
prependTo: function(target) {
var self = this;
return this._render_and_insert(function(t) {
self.$element.prependTo(t);
}, target);
},
/**
* Render the current widget and inserts it after to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
insertAfter: function(target) {
var self = this;
return this._render_and_insert(function(t) {
self.$element.insertAfter(t);
}, target);
},
/**
* Render the current widget and inserts it before to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
insertBefore: function(target) {
var self = this;
return this._render_and_insert(function(t) {
self.$element.insertBefore(t);
}, target);
},
_render_and_insert: function(insertion, target) {
var rendered = this.render();
this.$element = $(rendered);
if (target instanceof openerp.base.Widget)
target = target.$element;
insertion(target);
return this.start();
},
/**
* Renders the widget using QWeb, `this.template` must be defined.
* The context given to QWeb contains the "widget" key that references `this`.
*
* @param {object} additional Additional context arguments to pass to the template.
*/
render: function (additional) {
return QWeb.render(this.template, _.extend({widget: this}, additional || {}));
},
/**
* Method called after rendering. Mostly used to bind actions, perform asynchronous
* calls, etc...
*
* By convention, the method should return a promise to inform the caller when
* this widget has been initialized.
*
* @returns {jQuery.Deferred}
*/
start: function() {
if (!this.$element) {
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : undefined;
}
return $.Deferred().done().promise();
},
/**
* Destroys the current widget, also destory all its children before destroying itself.
*/
stop: function() {
_.each(_.clone(this.widget_children), function(el) {
el.stop();
});
if(this.$element != null) {
this.$element.remove();
}
if (this.widget_parent && this.widget_parent.widget_children) {
this.widget_parent.widget_children = _.without(this.widget_parent.widget_children, this);
}
this.widget_parent = null;
this.widget_is_stopped = true;
},
/**
* Inform the action manager to do an action. Of course, this suppose that
* the action manager can be found amongst the ancestors of the current widget.
* If that's not the case this method will simply return `false`.
*/
do_action: function(action, on_finished) {
if (this.widget_parent) {
return this.widget_parent.do_action(action, on_finished);
}
return false;
},
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
this._super(url, data). then(function() {
if (!self.widget_is_stopped)
def.resolve.apply(def, arguments);
}, function() {
if (!self.widget_is_stopped)
def.reject.apply(def, arguments);
});
return def.promise();
}
});
/**
* @deprecated
* For retro compatibility only, the only difference with is that render() uses
* directly `this` instead of context with a "widget" key.
*/
openerp.base.OldWidget = openerp.base.Widget.extend({
render: function (additional) {
return QWeb.render(this.template, _.extend(_.extend({}, this), additional || {}));
}
});
}
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -18,7 +18,7 @@ openerp.base.serialize_sort = function (criterion) {
}).join(', ');
};
openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{
openerp.base.DataGroup = openerp.base.Widget.extend( /** @lends openerp.base.DataGroup# */{
/**
* Management interface between views and grouped collections of OpenERP
* records.
@ -30,7 +30,7 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
* content of the current grouping level.
*
* @constructs
* @extends openerp.base.Controller
* @extends openerp.base.Widget
*
* @param {openerp.base.Session} session Current OpenERP session
* @param {String} model name of the model managed by this DataGroup
@ -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.
@ -238,19 +232,20 @@ openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend(
}
});
openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.DataSet# */{
openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.DataSet# */{
/**
* DateaManagement interface between views and the collection of selected
* OpenERP records (represents the view's state?)
*
* @constructs
* @extends openerp.base.Controller
* @extends openerp.base.Widget
*
* @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;
@ -296,14 +291,18 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
* Read the indexed record.
*/
read_index: function (fields, callback) {
var def = $.Deferred().then(callback);
if (_.isEmpty(this.ids)) {
return $.Deferred().reject().promise();
def.reject();
} else {
fields = fields || false;
return this.read_ids([this.ids[this.index]], fields, function(records) {
callback(records[0]);
return this.read_ids([this.ids[this.index]], fields).then(function(records) {
def.resolve(records[0]);
}, function() {
def.reject.apply(def, arguments);
});
}
return def.promise();
},
default_get: function(fields, callback) {
return this.rpc('/base/dataset/default_get', {
@ -382,15 +381,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 +395,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,10 +408,9 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});
openerp.base.DataSetSearch = openerp.base.DataSet.extend({
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;
@ -478,14 +474,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_.*/,
@ -499,24 +496,33 @@ openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({
this.to_create.push(cached);
this.cache.push(cached);
var to_return = $.Deferred().then(callback);
setTimeout(function() {to_return.resolve({result: cached.id});}, 0);
to_return.resolve({result: cached.id});
return to_return.promise();
},
write: function (id, data, callback) {
var self = this;
var record = _.detect(this.to_create, function(x) {return x.id === id;});
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
var dirty = false;
if (record) {
for (k in data) {
if (record.values[k] === undefined || record.values[k] !== data[k]) {
dirty = true;
break;
}
}
$.extend(record.values, data);
} else {
dirty = true;
record = {id: id, values: data};
self.to_write.push(record);
}
var cached = _.detect(this.cache, function(x) {return x.id === id;});
$.extend(cached.values, record.values);
this.on_change();
if (dirty)
this.on_change();
var to_return = $.Deferred().then(callback);
setTimeout(function () {to_return.resolve({result: true});}, 0);
to_return.resolve({result: true});
return to_return.promise();
},
unlink: function(ids, callback, error_callback) {
@ -590,7 +596,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);

View File

@ -0,0 +1,402 @@
openerp.base.data_export = function(openerp) {
openerp.base.DataExport = openerp.base.Dialog.extend({
init: function(parent, dataset) {
this._super(parent);
this.dataset = dataset;
},
start: function() {
var self = this;
self._super(false);
self.template = 'ExportTreeView';
self.dialog_title = "Export Data";
self.open({
modal: true,
width: '55%',
height: 'auto',
position: 'top',
buttons : {
"Close" : function() {
self.close();
},
"Export To File" : function() {
self.on_click_export_data();
}
},
close: function(event, ui){ self.close();}
});
self.on_show_exists_export_list();
self.$element.removeClass('ui-dialog-content ui-widget-content');
self.$element.find('#add_field').click(function() {
if ($('#field-tree-structure tr.ui-selected')) {
var fld = self.$element.find('#field-tree-structure tr.ui-selected').find('a');
for (var i = 0; i < fld.length; i++) {
var id = $(fld[i]).attr('id').split('-')[1];
var string = $(fld[i]).attr('string');
self.add_field(id, string);
}
self.$element.find('#field-tree-structure tr').removeClass('ui-selected');
}
});
self.$element.find('#remove_field').click(function() {
self.$element.find('#fields_list option:selected').remove();
});
self.$element.find('#remove_all_field').click(function() {
self.$element.find('#fields_list option').remove();
});
self.$element.find('#export_new_list').click(function() {
self.on_show_save_list();
});
var import_comp = self.$element.find('#import_compat option:selected').val(),
params = {
import_compat: parseInt(import_comp)
};
self.rpc('/base/export/get_fields', { model: self.dataset.model, params: params }, self.on_show_data);
self.$element.find('#import_compat').change(function() {
self.$element.find('#fields_list option').remove();
self.$element.find('#field-tree-structure').remove();
var import_comp = self.$element.find("#import_compat option:selected").val();
if (import_comp) {
var params = {
import_compat: parseInt(import_comp)
}
self.rpc("/base/export/get_fields", { model: self.dataset.model, params: params}, self.on_show_data);
}
});
},
on_show_exists_export_list: function() {
var self = this;
if (self.$element.find('#saved_export_list').is(':hidden')) {
self.$element.find('#ExistsExportList').show();
} else {
this.rpc('/base/export/exist_export_lists', { 'model': this.dataset.model}, function(export_list) {
if (export_list.length) {
self.$element.find('#ExistsExportList').append(QWeb.render('Exists.ExportList', {'existing_exports': export_list}));
self.$element.find('#saved_export_list').change(function() {
self.$element.find('#fields_list option').remove();
var export_id = self.$element.find('#saved_export_list option:selected').val();
if (export_id) {
self.rpc('/base/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field);
}
});
self.$element.find('#delete_export_list').click(function() {
var select_exp = self.$element.find('#saved_export_list option:selected');
if (select_exp.val()) {
self.rpc('/base/export/delete_export', { export_id: parseInt(select_exp.val())}, {});
select_exp.remove();
if (self.$element.find('#saved_export_list option').length <= 1) {
self.$element.find('#ExistsExportList').hide();
}
}
});
}
});
}
},
do_load_export_field: function(field_list) {
var export_node = this.$element.find("#fields_list");
for (var key in field_list) {
export_node.append(new Option(field_list[key], key));
}
},
on_show_save_list: function() {
var self = this;
var current_node = self.$element.find("#savenewlist");
if (!(current_node.find("label")).length) {
current_node.append(QWeb.render('ExportNewList'));
current_node.find("#add_export_list").click(function() {
var value = current_node.find("#savelist_name").val();
if (value) {
self.do_save_export_list(value);
} else {
alert("Pleae Enter Save Field List Name");
}
});
} else {
if (current_node.is(':hidden')) {
current_node.show();
current_node.find("#savelist_name").val("");
} else {
current_node.hide();
}
}
},
do_save_export_list: function(value) {
var self = this;
var export_field = self.get_fields();
if (export_field.length) {
self.rpc("/base/export/save_export_lists", {"model": self.dataset.model, "name":value, "field_list":export_field}, function(exp_id) {
if (exp_id) {
if (self.$element.find("#saved_export_list").length > 0) {
self.$element.find("#saved_export_list").append(new Option(value, exp_id));
} else {
self.on_show_exists_export_list();
}
if (self.$element.find("#saved_export_list").is(":hidden")) {
self.on_show_exists_export_list();
}
}
});
self.on_show_save_list();
self.$element.find("#fields_list option").remove();
}
},
on_click: function(id, result) {
var self = this;
self.field_id = id.split("-")[1];
var is_loaded = 0;
_.each(result, function(record) {
if (record['id'] == self.field_id && (record['children']).length >= 1) {
var model = record['params']['model'],
prefix = record['params']['prefix'],
name = record['params']['name'];
$(record['children']).each(function(e, childid) {
if (self.$element.find("tr[id='treerow-" + childid + "']").length > 0) {
if (self.$element.find("tr[id='treerow-" + childid + "']").is(':hidden')) {
is_loaded = -1;
} else {
is_loaded++;
}
}
});
if (is_loaded == 0) {
if (self.$element.find("tr[id='treerow-" + self.field_id +"']").find('img').attr('src') === '/base/static/src/img/expand.gif') {
if (model) {
var import_comp = self.$element.find("#import_compat option:selected").val();
var params = {
import_compat: parseInt(import_comp),
parent_field_type : record['field_type']
}
self.rpc("/base/export/get_fields", {
model: model,
prefix: prefix,
name: name,
field_parent : self.field_id,
params: params
}, function(results) {
self.on_show_data(results);
});
}
}
} else if (is_loaded > 0) {
self.showcontent(self.field_id, true);
} else {
self.showcontent(self.field_id, false);
}
}
});
},
on_show_data: function(result) {
var self = this;
var imp_cmpt = parseInt(self.$element.find("#import_compat option:selected").val());
var current_tr = self.$element.find("tr[id='treerow-" + self.field_id + "']");
if (current_tr.length >= 1) {
current_tr.find('img').attr('src','/base/static/src/img/collapse.gif');
current_tr.after(QWeb.render('ExportTreeView-Secondary.children', {'fields': result}));
} else {
self.$element.find('#left_field_panel').append(QWeb.render('ExportTreeView-Secondary', {'fields': result}));
}
_.each(result, function(record) {
if ((record.field_type == "one2many") && imp_cmpt) {
var o2m_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
o2m_fld.addClass("oe_export_readonlyfield");
}
if ((record.required == true) || record.required == "True") {
var required_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
required_fld.addClass("oe_export_requiredfield");
}
self.$element.find("img[id='parentimg-" + record.id +"']").click(function() {
self.on_click(this.id, result);
});
self.$element.find("tr[id='treerow-" + record.id + "']").click(function(e) {
if (e.shiftKey == true) {
var frst_click, scnd_click = '';
if (self.row_index == 0) {
self.row_index = this.rowIndex;
frst_click = self.$element.find("tr[id^='treerow-']")[self.row_index-1];
$(frst_click).addClass("ui-selected");
} else {
if (this.rowIndex >=self.row_index) {
for (i = (self.row_index-1); i < this.rowIndex; i++) {
scnd_click = self.$element.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
}
} else {
for (i = (self.row_index-1); i >= (this.rowIndex-1); i--) {
scnd_click = self.$element.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
}
}
}
}
self.row_index = this.rowIndex;
self.$element.find("tr[id='treerow-" + record.id + "']").keyup(function(e) {
self.row_index = 0;
});
var o2m_selection = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
if ($(o2m_selection).hasClass("oe_export_readonlyfield")) {
return false;
}
var selected = self.$element.find("tr.ui-selected");
if ($(this).hasClass("ui-selected") && (e.ctrlKey == true)) {
$(this).find('a').blur();
$(this).removeClass("ui-selected");
} else if ($(this).hasClass("ui-selected") && (e.ctrlKey == false) && (e.shiftKey == false)) {
selected.find('a').blur();
selected.removeClass("ui-selected");
$(this).find('a').focus();
$(this).addClass("ui-selected");
} else if (!$(this).hasClass("ui-selected") && (e.ctrlKey == false) && (e.shiftKey == false)) {
selected.find('a').blur();
selected.removeClass("ui-selected");
$(this).find('a').focus();
$(this).addClass("ui-selected");
} else if (!$(this).hasClass("ui-selected") && (e.ctrlKey == true)) {
$(this).find('a').focus();
$(this).addClass("ui-selected");
}
return false;
});
self.$element.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
var keyCode = e.keyCode || e.which;
arrow = {left: 37, up: 38, right: 39, down: 40 };
switch (keyCode) {
case arrow.left:
if ($(this).find('img').attr('src') === '/base/static/src/img/collapse.gif') {
self.on_click(this.id, result);
}
break;
case arrow.up:
var elem = this;
$(elem).removeClass("ui-selected");
while ($(elem).prev().is(":visible") == false) {
elem = $(elem).prev();
}
if (!$(elem).prev().find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(elem).prev().addClass("ui-selected");
}
$(elem).prev().find('a').focus();
break;
case arrow.right:
if ($(this).find('img').attr('src') == '/base/static/src/img/expand.gif') {
self.on_click(this.id, result);
}
break;
case arrow.down:
var elem = this;
$(elem).removeClass("ui-selected");
while($(elem).next().is(":visible") == false) {
elem = $(elem).next();
}
if (!$(elem).next().find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(elem).next().addClass("ui-selected");
}
$(elem).next().find('a').focus();
break;
}
});
self.$element.find("tr[id='treerow-" + record.id + "']").dblclick(function(e) {
var $o2m_selection = self.$element.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
if (!$o2m_selection.hasClass("oe_export_readonlyfield")) {
var field_id = $(this).find("a").attr("id");
if (field_id) {
self.add_field(field_id.split('-')[1], $(this).find("a").attr("string"));
}
}
});
});
self.$element.find('#fields_list').mouseover(function(event) {
if (event.relatedTarget) {
if (event.relatedTarget.attributes['id'] && event.relatedTarget.attributes['string']) {
var field_id = event.relatedTarget.attributes["id"]["value"];
if (field_id && field_id.split("-")[0] === 'export') {
if (!self.$element.find("tr[id='treerow-" + field_id.split("-")[1] + "']").find('#tree-column').hasClass("oe_export_readonlyfield")) {
self.add_field(field_id.split("-")[1], event.relatedTarget.attributes["string"]["value"]);
}
}
}
}
});
},
showcontent: function(id, flag) {
// show & hide the contents
var first_child = this.$element.find("tr[id='treerow-" + id + "']").find('img');
if (flag) {
first_child.attr('src', '/base/static/src/img/expand.gif');
}
else {
first_child.attr('src', '/base/static/src/img/collapse.gif');
}
var child_field = this.$element.find("tr[id^='treerow-" + id +"/']");
var child_len = (id.split("/")).length + 1;
for (var i = 0; i < child_field.length; i++) {
if (flag) {
$(child_field[i]).hide();
} else {
if (child_len == (child_field[i].id.split("/")).length) {
if ($(child_field[i]).find('img').attr('src') == '/base/static/src/img/collapse.gif') {
$(child_field[i]).find('img').attr('src', '/base/static/src/img/expand.gif');
}
$(child_field[i]).show();
}
}
}
},
add_field: function(field_id, string) {
var field_list = this.$element.find('#fields_list');
if (this.$element.find("#fields_list option[value='" + field_id + "']") && !this.$element.find("#fields_list option[value='" + field_id + "']").length) {
field_list.append(new Option(string, field_id));
}
},
get_fields: function() {
var export_field = [];
this.$element.find("#fields_list option").each(function() {
export_field.push($(this).val());
});
if (!export_field.length) {
alert('Please select fields to save export list...');
}
return export_field;
},
on_click_export_data: function() {
var self = this;
var export_field = {};
var flag = true;
self.$element.find("#fields_list option").each(function() {
export_field[$(this).val()] = $(this).text();
flag = false;
});
if (flag) {
alert('Please select fields to export...');
return;
}
var import_comp = self.$element.find("#import_compat option:selected").val(),
export_format = self.$element.find("#export_format").val();
self.rpc("/base/export/export_data", {
model: self.dataset.model,
fields: export_field,
ids: self.dataset.ids,
domain: self.dataset.domain,
import_compat: parseInt(import_comp),
export_format: export_format
}, function(data) {
window.location = "data:text/csv/excel;charset=utf8," + data;
self.close();
});
},
close: function() {
$(this.$dialog).remove();
this._super();
}
});
};

View File

@ -1,7 +1,7 @@
openerp.base.form = function (openerp) {
openerp.base.views.add('form', 'openerp.base.FormView');
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
@ -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.set_default_options();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@ -30,13 +30,14 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.datarecord = {};
this.ready = false;
this.show_invalid = true;
this.touched = false;
this.flags = this.view_manager.flags || {};
this.dirty = false;
this.default_focus_field = null;
this.default_focus_button = null;
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)
@ -47,14 +48,15 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
return def.promise();
} else {
var context = new openerp.base.CompoundContext(this.dataset.get_context());
if (this.view_manager.action && this.view_manager.action.context) {
context.add(this.view_manager.action.context);
}
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
toolbar:!!this.flags.sidebar, context: context}, this.on_loaded);
toolbar: this.options.sidebar, context: context}, this.on_loaded);
}
},
stop: function() {
if (this.sidebar) {
this.sidebar.attachments.stop();
this.sidebar.stop();
}
_.each(this.widgets, function(w) {
w.stop();
});
@ -83,22 +85,35 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
$('<xmp>' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600});
});
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.do_unfold();
this.sidebar.attachments = new openerp.base.form.SidebarAttachments(this.sidebar, this.sidebar.add_section('attachments', "Attachments"), this);
this.sidebar.add_toolbar(data.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
}
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);
this.$element.show();
if (this.sidebar) {
this.sidebar.$element.show();
}
return promise;
},
do_hide: function () {
this.$element.hide();
if (this.sidebar) {
this.sidebar.$element.hide();
}
},
on_record_loaded: function(record) {
if (!record) {
@ -107,28 +122,30 @@ 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();
this.$form_header.find('button.oe_form_button_new').show();
}
this.touched = false;
this.dirty = false;
this.datarecord = record;
for (var f in this.fields) {
var field = this.fields[f];
field.touched = false;
field.dirty = false;
field.set_value(this.datarecord[f] || false);
field.validate();
}
if (!record.id) {
// New record: Second pass in order to trigger the onchanges
this.touched = true;
this.dirty = true;
this.show_invalid = false;
for (var f in record) {
var field = this.fields[f];
if (field) {
field.touched = true;
field.dirty = true;
this.do_onchange(field);
}
}
@ -136,7 +153,9 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.on_form_changed();
this.show_invalid = this.ready = true;
this.do_update_pager(record.id == null);
this.do_update_sidebar();
if (this.sidebar) {
this.sidebar.attachments.do_update();
}
if (this.default_focus_field) {
this.default_focus_field.focus();
}
@ -245,7 +264,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
processed.push(field.name);
if (field.get_value() != value) {
field.set_value(value);
field.touched = true;
field.dirty = true;
if (_.indexOf(processed, field.name) < 0) {
this.do_onchange(field, processed);
}
@ -271,10 +290,14 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
},
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
@ -289,37 +312,40 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
if (!this.ready) {
return false;
}
var invalid = false,
var form_dirty = false,
form_invalid = false,
values = {},
first_invalid_field = null;
for (var f in this.fields) {
f = this.fields[f];
if (f.invalid) {
invalid = true;
if (!f.is_valid()) {
form_invalid = true;
f.update_dom();
if (!first_invalid_field) {
first_invalid_field = f;
}
} else if (f.touched) {
} else if (f.is_dirty()) {
form_dirty = true;
values[f.name] = f.get_value();
}
}
if (invalid) {
if (form_invalid) {
first_invalid_field.focus();
this.on_invalid();
return false;
} else {
} else if (form_dirty) {
this.log("About to save", values);
if (!this.datarecord.id) {
this.dataset.create(values, function(r) {
return this.dataset.create(values, function(r) {
self.on_created(r, success, prepend_on_create);
});
} else {
this.dataset.write(this.datarecord.id, values, function(r) {
return this.dataset.write(this.datarecord.id, values, function(r) {
self.on_saved(r, success);
});
}
return true;
} else {
return false;
}
},
do_save_edit: function() {
@ -333,7 +359,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
on_invalid: function() {
var msg = "<ul>";
_.each(this.fields, function(f) {
if (f.invalid) {
if (!f.is_valid()) {
msg += "<li>" + f.string + "</li>";
}
});
@ -377,7 +403,9 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.dataset.index = 0;
}
this.do_update_pager();
this.do_update_sidebar();
if (this.sidebar) {
this.sidebar.attachments.do_update();
}
this.notification.notify("Record created", "The record has been created with id #" + this.datarecord.id);
if (success) {
success(_.extend(r, {created: true}));
@ -394,52 +422,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
do_cancel: function () {
this.notification.notify("Cancelling form");
},
do_update_sidebar: function() {
if (this.flags.sidebar === false) {
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(),
[['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);*/
}
},
on_attachments_loaded: function(attachments) {
this.$sidebar = this.view_manager.sidebar.$element.find('.sidebar-attachments');
this.attachments = attachments;
this.$sidebar.html(QWeb.render('FormView.sidebar.attachments', this));
this.$sidebar.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
this.$sidebar.find('.oe-binary-file').change(this.on_attachment_changed);
},
on_attachment_changed: function(e) {
window[this.element_id + '_iframe'] = this.do_update_sidebar;
var $e = $(e.target);
if ($e.val() != '') {
this.$sidebar.find('form.oe-binary-form').submit();
$e.parent().find('input[type=file]').attr('disabled', 'true');
$e.parent().find('button').attr('disabled', 'true').find('img, span').toggle();
}
},
on_attachment_delete: function(e) {
var self = this, $e = $(e.currentTarget);
var name = _.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
if (confirm("Do you really want to delete the attachment " + name + " ?")) {
this.rpc('/base/dataset/unlink', {
model: 'ir.attachment',
ids: [parseInt($e.attr('data-id'))]
}, function(r) {
$e.parent().remove();
self.notification.notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
});
}
},
reload: function() {
if (this.datarecord.id) {
this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
@ -460,6 +442,54 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
/** @namespace */
openerp.base.form = {};
openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
init: function(parent, element_id, form_view) {
this._super(parent, element_id);
this.view = form_view;
},
do_update: function() {
if (!this.view.datarecord.id) {
this.on_attachments_loaded([]);
} else {
(new openerp.base.DataSetSearch(
this, 'ir.attachment', this.view.dataset.get_context(),
[['res_model', '=', this.view.dataset.model],
['res_id', '=', this.view.datarecord.id],
['type', 'in', ['binary', 'url']]])).read_slice(
['name', 'url', 'type'], false, false,
this.on_attachments_loaded);
}
},
on_attachments_loaded: function(attachments) {
this.attachments = attachments;
this.$element.html(QWeb.render('FormView.sidebar.attachments', this));
this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
},
on_attachment_changed: function(e) {
window[this.element_id + '_iframe'] = this.do_update;
var $e = $(e.target);
if ($e.val() != '') {
this.$element.find('form.oe-binary-form').submit();
$e.parent().find('input[type=file]').attr('disabled', 'true');
$e.parent().find('button').attr('disabled', 'true').find('img, span').toggle();
}
},
on_attachment_delete: function(e) {
var self = this, $e = $(e.currentTarget);
var name = _.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
if (confirm("Do you really want to delete the attachment " + name + " ?")) {
this.rpc('/base/dataset/unlink', {
model: 'ir.attachment',
ids: [parseInt($e.attr('data-id'))]
}, function(r) {
$e.parent().remove();
self.notification.notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
});
}
}
});
openerp.base.form.compute_domain = function(expr, fields) {
var stack = [];
for (var i = expr.length - 1; i >= 0; i--) {
@ -519,7 +549,7 @@ openerp.base.form.compute_domain = function(expr, fields) {
return _.all(stack);
};
openerp.base.form.Widget = openerp.base.Controller.extend({
openerp.base.form.Widget = openerp.base.Widget.extend({
template: 'Widget',
init: function(view, node) {
this.view = view;
@ -529,7 +559,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;
@ -685,6 +715,10 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "WidgetButton";
if (this.string) {
// We don't have button key bindings in the webclient
this.string = this.string.replace(/_/g, '');
}
if (node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
@ -696,7 +730,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
},
on_click: function(saved) {
var self = this;
if (!this.node.attrs.special && this.view.touched && saved !== true) {
if (!this.node.attrs.special && this.view.dirty && saved !== true) {
this.view.do_save(function() {
self.on_click(true);
});
@ -741,11 +775,12 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
if (this.node.tag == 'label' && this.node.attrs.colspan) {
this.is_field_label = false;
this.template = "WidgetParagraph";
this.colspan = this.node.attrs.colspan;
} else {
this.is_field_label = true;
this.template = "WidgetLabel";
this.colspan = 1;
}
this.colspan = 1;
},
render: function () {
if (this['for'] && this.type !== 'label') {
@ -776,7 +811,7 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
this.readonly = this.modifiers['readonly'] === true;
this.required = this.modifiers['required'] === true;
this.invalid = false;
this.touched = false;
this.dirty = false;
},
set_value: function(value) {
this.value = value;
@ -789,21 +824,29 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
get_value: function() {
return this.value;
},
is_valid: function() {
return !this.invalid;
},
is_dirty: function() {
return this.dirty;
},
get_on_change_value: function() {
return this.get_value();
},
update_dom: function() {
this._super.apply(this, arguments);
this.$element.toggleClass('disabled', this.readonly);
this.$element.toggleClass('required', this.required);
if (this.view.show_invalid) {
this.$element.toggleClass('invalid', this.invalid);
if (!this.disable_utility_classes) {
this.$element.toggleClass('disabled', this.readonly);
this.$element.toggleClass('required', this.required);
if (this.view.show_invalid) {
this.$element.toggleClass('invalid', !this.is_valid());
}
}
},
on_ui_change: function() {
this.touched = this.view.touched = true;
this.dirty = this.view.dirty = true;
this.validate();
if (!this.invalid) {
if (this.is_valid()) {
this.set_value_from_ui();
this.view.do_onchange(this);
this.view.on_form_changed();
@ -901,7 +944,7 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({
this.$element.find('button').click(this.on_button_clicked);
},
on_button_clicked: function() {
if (!this.value || this.invalid) {
if (!this.value || !this.is_valid()) {
this.notification.warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
location.href = 'mailto:' + this.value;
@ -942,7 +985,7 @@ openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
value = 0;
this.touched = true;
this.dirty = true;
}
var show_value = value.toFixed(2);
this.$element.find('input').val(show_value);
@ -962,7 +1005,7 @@ openerp.base.form.FieldInteger = openerp.base.form.FieldFloat.extend({
if (value === false || value === undefined) {
// TODO fme: check if GTK client default integers to 0 (like it does with floats)
value = 0;
this.touched = true;
this.dirty = true;
}
var show_value = parseInt(value, 10);
this.$element.find('input').val(show_value);
@ -1049,7 +1092,7 @@ openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
value = 0;
this.touched = true;
this.dirty = true;
}
var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60));
this.$element.find('input').val(show_value);
@ -1256,7 +1299,6 @@ openerp.base.form.dialog = function(content, 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;
}
@ -1289,7 +1331,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]);
@ -1377,7 +1419,7 @@ 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) {
@ -1419,7 +1461,7 @@ 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);
}).fail(function(error, event) {
@ -1431,7 +1473,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,
@ -1439,7 +1481,7 @@ 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(self, self.field.relation, self.build_context());
dataset.name_get([element_ids[0]], function(data) {
self._change_int_ext_value(data[0]);
pop.stop();
@ -1477,7 +1519,7 @@ 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[0]);
}).fail(function() {self.tmp_value = undefined;});
@ -1555,13 +1597,15 @@ 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();
this.disable_utility_classes = true;
},
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() {
@ -1585,20 +1629,31 @@ 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();
});
controller.on_pager_action.add_first(function() {
self.save_form_view();
});
controller.$element.find(".oe_form_button_save_edit").hide();
}
self.is_started.resolve();
});
this.viewmanager.on_mode_switch.add_first(function() {
self.save_form_view();
});
setTimeout(function () {
self.viewmanager.start();
}, 0);
@ -1609,7 +1664,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() {},
@ -1663,6 +1723,9 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this._super(value);
this.dataset.reset_ids(value);
}
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
}
$.when(this.is_started).then(function() {
self.reload_current_view();
});
@ -1687,9 +1750,50 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this.dataset.to_delete, function(x) {
return commands['delete'](x.id);}));
},
save_form_view: function() {
if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
this.viewmanager.views[this.viewmanager.active_view] &&
this.viewmanager.views[this.viewmanager.active_view].controller) {
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") {
var res = view.do_save();
if (res === false) {
// ignore
} else if (res.isRejected()) {
throw "Save or create on one2many dataset is not supposed to fail.";
} else if (!res.isResolved()) {
throw "Asynchronous get_value() is not supported in form view.";
}
return res;
}
}
return false;
},
is_valid: function() {
this.validate();
return this._super();
},
validate: function() {
this.invalid = false;
// TODO niv
var self = this;
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
if (self.viewmanager.active_view === "form") {
for (var f in view.fields) {
f = view.fields[f];
if (!f.is_valid()) {
this.invalid = true;
return;
}
}
}
},
is_dirty: function() {
this.save_form_view();
return this._super();
},
update_dom: function() {
this._super.apply(this, arguments);
this.$element.toggleClass('disabled', this.readonly);
}
});
@ -1700,13 +1804,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,
@ -1725,7 +1833,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,
@ -1752,15 +1860,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
});
@ -1803,8 +1909,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());
@ -1822,7 +1927,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();
@ -1830,7 +1935,7 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
}
});
openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
identifier_prefix: "selectcreatepopup",
template: "SelectCreatePopup",
/**
@ -1853,7 +1958,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
},
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") {
@ -1867,7 +1972,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
@ -1895,7 +2000,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;
@ -1910,7 +2015,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]);
@ -1935,8 +2040,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);
}
@ -1971,7 +2075,7 @@ openerp.base.form.SelectCreateListView = openerp.base.ListView.extend({
}
});
openerp.base.form.FormOpenPopup = openerp.base.BaseWidget.extend({
openerp.base.form.FormOpenPopup = openerp.base.OldWidget.extend({
identifier_prefix: "formopenpopup",
template: "FormOpenPopup",
/**
@ -1993,8 +2097,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;
@ -2005,7 +2108,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();
@ -2014,8 +2117,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);
}
@ -2199,8 +2301,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',

View File

@ -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(
'<progress value="%.2f" max="100.0">%.2f%%</progress>',
record.value, record.value);
value, value);
case 'many2one':
// name_get value format
return value[1];
default:
return record.value;
return value;
}
}
};
@ -87,9 +100,10 @@ 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.set_default_options();
this.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@ -97,10 +111,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
this.columns = [];
this.options = _.extend({}, this.defaults, options || {});
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,12 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
})
.val(self._limit || 'NaN');
});
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.add_toolbar(data.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
}
},
/**
* Configures the ListView pager based on the provided dataset's information
@ -347,16 +364,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]
};
});
});
},
/**
@ -385,15 +396,20 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
},
do_show: function () {
this.$element.show();
if (this.sidebar) {
this.sidebar.$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);
},
do_hide: function () {
this.$element.hide();
if (this.sidebar) {
this.sidebar.$element.hide();
}
this.hidden = true;
},
/**
@ -413,7 +429,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
model: this.model,
view_id: this.view_id,
context: this.dataset.get_context(),
toolbar: !!this.flags.sidebar
toolbar: this.options.sidebar
}, callback);
}
},
@ -421,7 +437,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 +467,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 +491,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 +520,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 +577,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 +584,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 +624,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 +642,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 +709,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 +847,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 +885,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 +1022,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 +1127,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 = $('<tbody>');
@ -1102,6 +1184,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(); }
});
});

View File

@ -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();
openerp.base.SearchView = openerp.base.Widget.extend({
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 select = this.$element.find(".oe_search-view-filters-management"),
val = select.val();
select.val("_filters");
var self = this;
var select = this.$element.find(".oe_search-view-filters-management");
var val = select.val();
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,8 +235,22 @@ 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 data = this.build_search_data();
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 = [];
@ -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,14 +386,13 @@ 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.Widget.extend( /** @lends openerp.base.search.Widget# */{
template: null,
/**
* Root class of all search widgets
*
* @constructs
* @extends openerp.base.Controller
* @extends openerp.base.Widget
*
* @param view the ancestor view of this widget
*/
@ -412,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
@ -483,8 +553,7 @@ 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: '=',
/**
@ -549,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();
@ -589,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({
@ -623,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
@ -703,29 +774,93 @@ 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({
openerp.base.search.ExtendedSearch = openerp.base.OldWidget.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() {
var group = new openerp.base.search.ExtendedSearchGroup(this, this.fields);
var render = group.render();
this.$element.find('.searchview_extended_groups_list').append(render);
group.start();
group.appendTo(this.$element.find('.searchview_extended_groups_list'));
this.check_last_element();
},
start: function () {
@ -754,7 +889,7 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
return null;
}
return _.reduce(this.children,
return _.reduce(this.widget_children,
function(mem, x) { return mem.concat(x.get_domain());}, []);
},
on_activate: function() {
@ -773,12 +908,14 @@ 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);
_.each(this.widget_children, function(x) {x.set_last_group(false);});
if (this.widget_children.length >= 1) {
this.widget_children[this.widget_children.length - 1].set_last_group(true);
}
}
});
openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.group',
identifier_prefix: 'extended-search-group',
init: function (parent, fields) {
@ -787,7 +924,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
},
add_prop: function() {
var prop = new openerp.base.search.ExtendedSearchProposition(this, this.fields);
var render = prop.render({'index': this.children.length - 1});
var render = prop.render({'index': this.widget_children.length - 1});
this.$element.find('.searchview_extended_propositions_list').append(render);
prop.start();
},
@ -804,7 +941,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
});
},
get_domain: function() {
var props = _(this.children).chain().map(function(x) {
var props = _(this.widget_children).chain().map(function(x) {
return x.get_proposition();
}).compact().value();
var choice = this.$element.find(".searchview_extended_group_choice").val();
@ -814,9 +951,9 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
props);
},
stop: function() {
var parent = this.parent;
if (this.parent.children.length == 1)
this.parent.hide();
var parent = this.widget_parent;
if (this.widget_parent.widget_children.length == 1)
this.widget_parent.hide();
this._super();
parent.check_last_element();
},
@ -828,7 +965,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
}
});
openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition',
identifier_prefix: 'extended-search-proposition',
init: function (parent, fields) {
@ -853,9 +990,12 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
});
},
stop: function() {
if (this.parent.children.length == 1)
this.parent.stop();
var parent;
if (this.widget_parent.widget_children.length == 1)
parent = this.widget_parent;
this._super();
if (parent)
parent.stop();
},
changed: function() {
var nval = this.$element.find(".searchview_extended_prop_field").val();
@ -915,7 +1055,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
}
});
openerp.base.search.ExtendedSearchProposition.Char = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Char = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.char',
identifier_prefix: 'extended-search-proposition-char',
operators: [
@ -932,7 +1072,7 @@ openerp.base.search.ExtendedSearchProposition.Char = openerp.base.BaseWidget.ext
return this.$element.val();
}
});
openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.datetime',
identifier_prefix: 'extended-search-proposition-datetime',
operators: [
@ -954,7 +1094,7 @@ openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.BaseWidget
});
}
});
openerp.base.search.ExtendedSearchProposition.Date = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Date = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.date',
identifier_prefix: 'extended-search-proposition-date',
operators: [
@ -976,7 +1116,7 @@ openerp.base.search.ExtendedSearchProposition.Date = openerp.base.BaseWidget.ext
});
}
});
openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.integer',
identifier_prefix: 'extended-search-proposition-integer',
operators: [
@ -995,7 +1135,7 @@ openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.BaseWidget.
return Math.round(value);
}
});
openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Float = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.float',
identifier_prefix: 'extended-search-proposition-float',
operators: [
@ -1014,7 +1154,7 @@ openerp.base.search.ExtendedSearchProposition.Float = openerp.base.BaseWidget.ex
return value;
}
});
openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.selection',
identifier_prefix: 'extended-search-proposition-selection',
operators: [
@ -1028,7 +1168,7 @@ openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.BaseWidge
return this.$element.val();
}
});
openerp.base.search.ExtendedSearchProposition.Boolean = openerp.base.BaseWidget.extend({
openerp.base.search.ExtendedSearchProposition.Boolean = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search.proposition.boolean',
identifier_prefix: 'extended-search-proposition-boolean',
operators: [

View File

@ -0,0 +1,15 @@
/*---------------------------------------------------------
* OpenERP base library
*---------------------------------------------------------*/
openerp.base.view_help = function(openerp) {
openerp.base.ProcessView = openerp.base.Widget.extend({
});
openerp.base.HelpView = openerp.base.Widget.extend({
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -2,10 +2,10 @@
* 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({
openerp.base.TreeView = openerp.base.Widget.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
*/

View File

@ -4,20 +4,32 @@
openerp.base.views = function(openerp) {
openerp.base.ActionManager = openerp.base.Controller.extend({
openerp.base.ActionManager = openerp.base.Widget.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) {
var self = this;
action.flags = _.extend({
@ -35,15 +47,12 @@ 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, {
title: action.name,
width: '50%'
});
var dialog = this.current_dialog = new openerp.base.ActionDialog(this, { title: action.name, width: '90%' });
if (on_closed) {
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) {
@ -52,12 +61,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();
}
break;
@ -84,9 +96,25 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
}
});
openerp.base.ViewManager = openerp.base.Controller.extend({
init: function(session, element_id, dataset, views) {
this._super(session, element_id);
openerp.base.ActionDialog = openerp.base.Dialog.extend({
identifier_prefix: 'action_dialog',
on_close: function() {
this._super(this, arguments);
if (this.close_callback) {
this.close_callback();
}
},
stop: function() {
this._super(this, arguments);
if (this.viewmanager) {
this.viewmanager.stop();
}
}
});
openerp.base.ViewManager = openerp.base.Widget.extend({
init: function(parent, element_id, dataset, views) {
this._super(parent, element_id);
this.model = dataset.model;
this.dataset = dataset;
this.searchview = null;
@ -95,13 +123,13 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
{return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
this.views = {};
this.flags = this.flags || {};
this.sidebar = new openerp.base.NullSidebar();
this.registry = openerp.base.views;
},
/**
* @returns {jQuery.Deferred} initial view loading promise
*/
start: function() {
this._super();
var self = this;
this.dataset.start();
this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src}));
@ -109,7 +137,12 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
self.on_mode_switch($(this).data('view-type'));
});
_.each(this.views_src, function(view) {
self.views[view.view_type] = $.extend({}, view, {controller: null});
self.views[view.view_type] = $.extend({}, view, {
controller : null,
options : _.extend({
sidebar_id : self.element_id + '_sidebar_' + view.view_type
}, self.flags)
});
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
@ -133,7 +166,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);
@ -187,7 +220,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
},
/**
* Event launched when a controller has been inited.
*
*
* @param {String} view_type type of view
* @param {String} view the inited controller
*/
@ -203,7 +236,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();
}
@ -245,45 +278,39 @@ 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) {
// Not elegant but allows to avoid flickering of SearchView#do_hide
this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false;
}
if (this.flags.sidebar) {
this.sidebar = new openerp.base.Sidebar(null, this);
}
},
start: function() {
var inital_view_loaded = this._super();
// init sidebar
if (this.flags.sidebar) {
this.$element.find('.view-manager-main-sidebar').html(this.sidebar.render());
this.sidebar.start();
}
var search_defaults = {};
_.each(this.action.context, function (value, key) {
var match = /^search_default_(.*)$/.exec(key);
@ -307,8 +334,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
},
stop: function() {
// should be replaced by automatic destruction implemented in BaseWidget
this.sidebar.stop();
// should be replaced by automatic destruction implemented in Widget
this._super();
},
/**
@ -335,96 +361,101 @@ 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) {
this._super(parent, view_manager.session);
this.view_manager = view_manager;
this.sections = [];
},
set_toolbar: function(toolbar) {
this.sections = [];
var self = this;
_.each([["print", "Reports"], ["action", "Actions"], ["relate", "Links"]], function(type) {
if (toolbar[type[0]].length == 0)
return;
var section = {elements:toolbar[type[0]], label:type[1]};
self.sections.push(section);
});
this.do_refresh(true);
},
do_refresh: function(new_view) {
var view = this.view_manager.active_view;
var the_condition = this.sections.length > 0 && _.detect(this.sections,
function(x) {return x.elements.length > 0;}) != undefined
&& (!new_view || view != 'list');
this.$element.toggleClass('open-sidebar', the_condition)
.toggleClass('closed-sidebar', !the_condition);
this.$element.html(QWeb.render("ViewManager.sidebar.internal", { sidebar: this, view: view }));
var self = this;
this.$element.find(".toggle-sidebar").click(function(e) {
self.$element.toggleClass('open-sidebar closed-sidebar');
e.stopPropagation();
e.preventDefault();
});
this.$element.find("a.oe_sidebar_action_a").click(function(e) {
var $this = jQuery(this);
var index = $this.attr("data-index").split('-');
var action = self.sections[index[0]].elements[index[1]];
action.flags = {
new_window : true
};
self.session.action_manager.do_action(action);
e.stopPropagation();
e.preventDefault();
});
openerp.base.Sidebar = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.items = {};
this.sections = {};
},
start: function() {
this._super();
this.do_refresh(false);
var self = this;
this._super(this, arguments);
this.$element.html(QWeb.render('Sidebar'));
this.$element.find(".toggle-sidebar").click(function(e) {
self.do_toggle();
});
},
add_toolbar: function(toolbar) {
var self = this;
_.each([['print', "Reports"], ['action', "Actions"], ['relate', "Links"]], function(type) {
var items = toolbar[type[0]];
if (items.length) {
for (var i = 0; i < items.length; i++) {
items[i] = {
label: items[i]['name'],
action: items[i],
classname: 'oe_sidebar_' + type[0]
}
}
self.add_section(type[0], type[1], items);
}
});
},
add_section: function(code, name, items) {
// For each section, we pass a name/label and optionally an array of items.
// If no items are passed, then the section will be created as a custom section
// returning back an element_id to be used by a custom controller.
// Else, the section is a standard section with items displayed as links.
// An item is a dictonary : {
// label: label to be displayed for the link,
// action: action to be launch when the link is clicked,
// callback: a function to be executed when the link is clicked,
// classname: optional dom class name for the line,
// title: optional title for the link
// }
// Note: The item should have one action or/and a callback
var self = this,
section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
if (items) {
for (var i = 0; i < items.length; i++) {
items[i].element_id = _.uniqueId(section_id + '_item_');
this.items[items[i].element_id] = items[i];
}
}
var $section = $(QWeb.render("Sidebar.section", {
section_id: section_id,
name: name,
classname: 'oe_sidebar_' + code,
items: items
}));
if (items) {
$section.find('a.oe_sidebar_action_a').click(function() {
var item = self.items[$(this).attr('id')];
if (item.callback) {
item.callback();
}
if (item.action) {
item.action.flags = item.action.flags || {};
item.action.flags.new_window = true;
self.do_action(item.action);
}
return false;
});
}
$section.appendTo(this.$element.find('div.sidebar-actions'));
this.sections[code] = $section;
return section_id;
},
do_fold: function() {
this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
},
do_unfold: function() {
this.$element.addClass('open-sidebar').removeClass('closed-sidebar');
},
do_toggle: function() {
this.$element.toggleClass('open-sidebar closed-sidebar');
}
});
openerp.base.NullSidebar = openerp.base.generate_null_object_class(openerp.base.Sidebar);
openerp.base.Export = openerp.base.Dialog.extend({
dialog_title: "Export",
template: 'ExportDialog',
identifier_prefix: 'export_dialog',
init: function (session, model, domain) {
this._super();
openerp.base.View = openerp.base.Widget.extend({
set_default_options: function(options) {
this.options = options || {};
_.defaults(this.options, {
// All possible views options should be defaulted here
sidebar_id: null,
sidebar: true
});
},
start: function () {
this._super();
this.$element.html(this.render());
},
on_button_Export: function() {
console.log("Export")
},
on_button_Cancel: function() {
this.$element.dialog("close");
}
});
openerp.base.View = openerp.base.Controller.extend({
/**
* Fetches and executes the action identified by ``action_data``.
*
@ -480,11 +511,65 @@ openerp.base.View = openerp.base.Controller.extend({
* Directly set a view to use instead of calling fields_view_get. This method must
* be called before start(). When an embedded view is set, underlying implementations
* of openerp.base.View must use the provided view instead of any other one.
*
*
* @param embedded_view A view.
*/
set_embedded_view: function(embedded_view) {
this.embedded_view = embedded_view;
},
set_common_sidebar_sections: function(sidebar) {
sidebar.add_section('customize', "Customize", [
{
label: "Manage Views",
callback: this.on_sidebar_manage_view,
title: "Manage views of the current object"
}, {
label: "Edit Workflow",
callback: this.on_sidebar_edit_workflow,
title: "Manage views of the current object",
classname: 'oe_hide oe_sidebar_edit_workflow'
}, {
label: "Customize Object",
callback: this.on_sidebar_customize_object,
title: "Manage views of the current object"
}
]);
sidebar.add_section('other', "Other Options", [
{
label: "Import",
callback: this.on_sidebar_import
}, {
label: "Export",
callback: this.on_sidebar_export
}, {
label: "Translate",
callback: this.on_sidebar_translate,
classname: 'oe_hide oe_sidebar_translate'
}, {
label: "View Log",
callback: this.on_sidebar_view_log,
classname: 'oe_hide oe_sidebar_view_log'
}
]);
},
on_sidebar_manage_view: function() {
console.log('Todo');
},
on_sidebar_edit_workflow: function() {
console.log('Todo');
},
on_sidebar_customize_object: function() {
console.log('Todo');
},
on_sidebar_import: function() {
},
on_sidebar_export: function() {
var export_view = new openerp.base.DataExport(this, this.dataset);
export_view.start(false);
},
on_sidebar_translate: function() {
},
on_sidebar_view_log: function() {
}
});
@ -493,12 +578,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

View File

@ -62,7 +62,7 @@
<td><label for="db">Database:</label></td>
<td>
<t t-if="!db_list">
<input type="text" name="db" value="trunk" autofocus="true"/>
<input type="text" name="db" t-att-value="selected_db || ''" autofocus="true"/>
</t>
<t t-if="db_list">
<select name="db">
@ -103,7 +103,7 @@
<div class="oe_login_right_pane">
<p>We think that daily job activities can be more intuitive, efficient, automated, .. and even fun.</p>
<h3>OpenERP's vision to be:</h3>
<table cellpadding="0" cellspacing="0" width="100%" style="border:none;">
<tbody>
<tr>
@ -135,7 +135,7 @@
</tr>
</tbody>
</table>
</div>
</t>
<t t-name="Header">
@ -143,8 +143,8 @@
<div class="company_logo" />
</a>
<h1 class="header_title" t-if="session.session_is_valid()">
<span class="company">$company</span> - (<span class="database">$database</span>)<br/>
<small class="username">$username session_id: <t t-esc="session.session_id"/></small>
<span class="database"><t t-esc="session.db"/></span> - <t t-esc="session.login"/> <br/>
<small class="username">rpc_session_id: <t t-esc="session.session_id"/></small>
</h1>
<div class="header_corner">
<ul class="block" t-if="session.session_is_valid()">
@ -232,45 +232,66 @@
</t>
</td>
<td class="view-manager-main-sidebar" height="100%">
<t t-foreach="views" t-as="view">
<div t-attf-id="#{prefix}_sidebar_#{view.view_type}" class="sidebar-main-div closed-sidebar" style="display: none"/>
</t>
</td>
</tr>
</table>
</t>
<table t-name="ListView">
<t t-name="Sidebar">
<a class="toggle-sidebar"></a>
<div class="sidebar-content">
<div class="sidebar-actions">
</div>
</div>
</t>
<t t-name="Sidebar.section">
<h2><t t-esc="name"/></h2>
<div t-att-id="section_id" t-att-class="classname">
<ul t-if="items">
<li t-foreach="items" t-as="item" t-att-class="item.classname">
<a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#">
<t t-esc="item.label"/>
</a>
</li>
</ul>
</div>
</t>
<table t-name="ListView" class="oe-listview-content">
<t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0)"/>
<t t-set="actions_span" t-value="Math.floor((options.deletable or options.addable) ? columns_count/2 : 0)"/>
<thead class="ui-widget-header">
<tr>
<th t-if="actions_span" t-att-colspan="actions_span"
class="oe-actions">
<t t-if="flags.action_buttons !== false">
<button type="button" class="oe-list-add"
t-if="options.addable">
<t t-esc="options.addable"/>
</button>
<button type="button" class="oe-list-delete"
t-if="options.selectable and options.deletable">
Delete
</button>
</t>
</th>
<th t-att-colspan="columns_count - actions_span"
class="oe-list-pager">
<t t-if="flags.pager !== false">
<button type="button" disabled="disabled"
data-pager-action="first">First</button>
<button type="button" disabled="disabled"
data-pager-action="previous"
>&lt;&lt;</button>
<tr t-if="options.action_buttons !== false or options.pager !== false">
<th t-att-colspan="columns_count">
<table>
<tr>
<td t-if="options.action_buttons !== false" class="oe-actions">
<button type="button" class="oe-list-add"
t-if="options.addable">
<t t-esc="options.addable"/>
</button>
<button type="button" class="oe-list-delete"
t-if="options.selectable and options.deletable">
Delete
</button>
</td>
<th t-if="options.pager !== false" class="oe-list-pager">
<button type="button" disabled="disabled"
data-pager-action="first">First</button>
<button type="button" disabled="disabled"
data-pager-action="previous"
>&lt;</button>
<span class="oe-pager-state">
</span>
<span class="oe-pager-state">
</span>
<button type="button" disabled="disabled"
data-pager-action="next">&gt;&gt;</button>
<button type="button" disabled="disabled"
data-pager-action="last">Last</button>
</t>
<button type="button" disabled="disabled"
data-pager-action="next">&gt;</button>
<button type="button" disabled="disabled"
data-pager-action="last">Last</button>
</th>
</tr>
</table>
</th>
</tr>
<tr t-if="options.header">
@ -294,7 +315,7 @@
<tr>
<td t-if="options.selectable"/>
<td t-foreach="aggregate_columns" t-as="column" class="oe-list-footer oe-number"
t-att-data-field="column.field" t-att-title="column.label">
t-att-data-field="column.id" t-att-title="column.label">
</td>
<td t-if="options.deletable"/>
</tr>
@ -312,7 +333,7 @@
t-att-data-index="row_index">
<t t-foreach="columns" t-as="column">
<td t-if="column.meta">
</td>
</t>
<th t-if="options.selectable" class="oe-record-selector" width="1">
@ -336,7 +357,7 @@
<t t-name="FormView">
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
<h2 class="oe_view_title"><t t-esc="view.fields_view.arch.attrs.string"/> <button class="oe_get_xml_view">xml</button></h2>
<div class="oe_form_buttons" t-if="view.flags.action_buttons !== false">
<div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
<!--<button type="button" class="oe_form_button_save">
<span class="oe_form_on_update">Save</span>
<span class="oe_form_on_create">Create</span>
@ -348,7 +369,7 @@
<!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
<button type="button" class="oe_form_button_new">New</button>
</div>
<div class="oe_form_pager" t-if="view.flags.pager !== false">
<div class="oe_form_pager" t-if="view.options.pager !== false">
<button type="button" data-pager-action="first">First</button>
<button type="button" data-pager-action="previous">&lt;&lt;</button>
@ -361,21 +382,20 @@
<t t-raw="frame.render()"/>
</t>
<t t-name="FormView.sidebar.attachments">
<h2>Attachments</h2>
<div class="oe-sidebar-attachments-toolbar">
<div class="oe-binary-file-set" style="float: right">
<form class="oe-binary-form" t-attf-target="#{element_id}_iframe"
method="post" enctype="multipart/form-data" action="/base/binary/upload_attachment">
<input type="hidden" name="session_id" t-att-value="session.session_id"/>
<input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/>
<input type="hidden" name="model" t-att-value="dataset.model"/>
<input type="hidden" name="id" t-att-value="datarecord.id"/>
<input type="hidden" name="model" t-att-value="view.dataset.model"/>
<input type="hidden" name="id" t-att-value="view.datarecord.id"/>
<button class="button" type="button">
<img src="/base/static/src/img/throbber.gif" width="16" height="16" style="display: none"/>
<span>Add</span>
</button>
<input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
t-att-onclick="datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/>
t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/>
</form>
<iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"> </iframe>
</div>
@ -385,7 +405,7 @@
<li t-foreach="attachments" t-as="attachment">
<t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="'/base/binary/saveas?session_id='
+ session.session_id + '&amp;model=ir.attachment&amp;id=' + attachment.id
+ '&amp;field=datas' + '&amp;fieldname=name' + '&amp;t=' + (new Date().getTime())"/>
+ '&amp;field=datas&amp;fieldname=name&amp;t=' + (new Date().getTime())"/>
<a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
<t t-esc="attachment.name"/>
</a>
@ -522,7 +542,7 @@
<img src="/base/static/src/img/icons/gtk-index.png"/></span>
<div t-att-id="widget.cm_id" class="contextMenu" style="display:none"><ul>
<li t-att-id="widget.cm_id + '_open'" style="color:grey">Open...</li>
<li t-att-id="widget.cm_id + '_create'">Create and Edit...</li>
<li t-att-id="widget.cm_id + '_create'">Create...</li>
<li t-att-id="widget.cm_id + '_search'">Search...</li>
</ul></div>
</div>
@ -665,14 +685,28 @@
<input type="reset" value="Clear"/>
<button class="oe_search-view-custom-filter-btn"><span>Advanced Filter</span></button>
<select class="oe_search-view-filters-management">
<option value="_filters">-- Filters --</option>
<option value="_actions">-- Actions --</option>
<option value="save_filter">Save Filter</option>
<option value="manage_filters">Manage Filters</option>
</select>
</div>
</form>
</t>
<t t-name="SearchView.managed-filters">
<option value="_filters">-- Filters --</option>
<t t-set="i" t-value="0"/>
<t t-foreach="filters" t-as="filter">
<option t-att-value="'get:' + i"><t t-esc="filter.name"/></option>
<t t-set="i" t-value="i+1"/>
</t>
<option value="_actions">-- Actions --</option>
<option value="save_filter">Save Filter</option>
<option value="manage_filters">Manage Filters</option>
</t>
<t t-name="SearchView.managed-filters.add">
<div>
<p>Filter Name:</p>
<input type="text"/>
<p>(Any existing filter with the same name will be replaced)</p>
</div>
</t>
<t t-name="SearchView.render_lines">
<table class="oe-searchview-render-line" border="0" cellspacing="0" cellpadding="0"
t-foreach="lines" t-as="line">
@ -787,7 +821,7 @@
<option value="all">All the following conditions must match</option>
<option value="none">None of the following conditions must match</option>
</select>
<a class="searchview_extended_delete_group"
<a class="searchview_extended_delete_group"
href="javascript:void(0)"><span></span></a>
<div class="searchview_extended_propositions_list">
</div>
@ -836,29 +870,6 @@
</t>
</select>
</t>
<t t-name="ViewManager.sidebar">
<div t-att-id="element_id" class="sidebar-main-div closed-sidebar">
</div>
</t>
<t t-name="ViewManager.sidebar.internal">
<a class="toggle-sidebar"></a>
<div class="sidebar-content">
<div class="sidebar-attachments" t-if="view == 'form'"> </div>
<div class="sidebar-actions">
<t t-foreach="sidebar.sections" t-as="section" t-if="section.elements.length">
<h2><t t-esc="section.label"/></h2>
<ul>
<li t-foreach="section.elements" t-as="element">
<a class="oe_sidebar_action_a" t-attf-data-index="#{section_index}-#{element_index}" href="#">
<t t-esc="element.name"/>
</a>
</li>
</ul>
</t>
</div>
</div>
</t>
<t t-name="DialogWarning">
<table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
<tr>
@ -933,4 +944,145 @@
.unwrap();
</t>
</t>
<t t-name="ExportView">
<a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a>
</t>
<t t-name="ExportTreeView">
<table class="view" style="background-color: #F3F3F3;">
<tr>
<td align="left">
This wizard will export all data that matches the current search criteria to a CSV file.
You can export all data or only the fields that can be reimported after modification.
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td class="label"><label>Export Type:</label></td>
<td>
<select id="import_compat" name="import_compat">
<option value="1">Import Compatible Export</option>
<option value="0">Export all Data</option>
</select>
</td>
<td class="label"><label>Export Format</label></td>
<td>
<select id="export_format" name="export_format">
<option value="csv">CSV</option>
<option value="xls">Excel</option>
</select>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="oe_export_fields_selector_export">
<tr>
<th class="oe_view_title" valign="bottom">Available fields</th>
<th class="oe_view_title"></th>
<th class="oe_view_title">Fields to export
<a style="color: blue; text-decoration: none;" href="#" id="export_new_list">Save fields list</a>
<div id="savenewlist"></div>
<div id="ExistsExportList"></div>
</th>
</tr>
<tr>
<td class="oe_export_fields_selector_left">
<div id="left_field_panel">
</div>
</td>
<td>
<table class="oe_export_fields_selector_center">
<tr>
<td align="center">
<button id="add_field" class="oe_export_button_export">Add</button>
</td>
</tr>
<tr>
<td align="center">
<button id="remove_field" class="oe_export_button_export">Remove</button>
</td>
</tr>
<tr>
<td align="center">
<button id="remove_all_field" class="oe_export_button_export">Remove All</button>
</td>
</tr>
</table>
</td>
<td class="oe_export_fields_selector_right">
<select name="fields_list" id="fields_list" multiple="multiple"></select>
</td>
</tr>
</table>
</td>
</tr>
</table>
</t>
<t t-name="ExportTreeView-Secondary">
<table id="field-tree-structure" class="oe_export_fields_selector_export" cellspacing="0" cellpadding="0">
<tr><th class="oe_export_tree_header"> Name </th></tr>
<t t-call="ExportTreeView-Secondary.children"/>
</table>
</t>
<t t-name="ExportTreeView-Secondary.children">
<t t-foreach="fields" t-as="field" >
<tr t-att-id="'treerow-' + field.id" class="oe_export_row">
<td>
<table class="tree_grid" border="0">
<tr class="oe_export_row">
<t t-foreach="(field.id).split('/')" t-as="level" >
<t t-if="(field.id).split('/')[0] != level">
<td width="18">&amp;nbsp;</td>
</t>
</t>
<td valign="top" align="left" style="cursor: pointer;" width="18">
<t t-if="(field.children).length >= 1">
<t t-if="(field.id).split('/').length != 3">
<img t-att-id="'parentimg-' + field.id" src="/base/static/src/img/expand.gif" width="16" height="16" border="0"/>
</t>
</t>
</td>
<td id="tree-column" valign="middle" align="left" style="cursor: pointer;">
<a t-att-id="'export-' + field.id" t-att-string="field.string" href="javascript: void(0);" style="text-decoration: none;">
<t t-esc="field.string"/>
</a>
</td>
</tr>
</table>
</td>
</tr>
</t>
</t>
<t t-name="ExportNewList">
<tr>
<th><label>Save as:</label></th>
<td><input size="10" type="text" id="savelist_name"/></td>
<td><button class="oe_export_button_export" id="add_export_list">Ok</button></td>
</tr>
</t>
<t t-name="Exists.ExportList">
<tr><th align="right"><label >Saved exports:</label></th></tr>
<tr align="left">
<td>
<select id="saved_export_list" style="width: 100%;">
<option></option>
<t t-foreach="existing_exports" t-as="export">
<option t-att-value="export.id"><t t-esc="export.name"/></option>
</t>
</select>
</td>
<td><button class="oe_export_button_export" id="delete_export_list">Delete</button></td>
</tr>
</t>
</templates>

View File

@ -18,15 +18,6 @@ $(document).ready(function () {
}
}};
var fuck_that_shit = {
action: {
flags: {}
},
sidebar: {
set_toolbar: function () {}
}
};
var openerp;
module("ListView", {
setup: function () {
@ -42,8 +33,7 @@ $(document).ready(function () {
test('render selection checkboxes', 2, function () {
var listview = new openerp.base.ListView(
fuck_that_shit, null,
'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
listview.on_loaded(fvg);
@ -60,8 +50,7 @@ $(document).ready(function () {
});
test('render no checkbox if selectable=false', 1, function () {
var listview = new openerp.base.ListView(
fuck_that_shit, null,
'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false,
null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false,
{selectable: false});
listview.on_loaded(fvg);
@ -75,8 +64,7 @@ $(document).ready(function () {
});
test('select a bunch of records', 2, function () {
var listview = new openerp.base.ListView(
fuck_that_shit, null, 'qunit-fixture',
{model: null, ids: [1, 2, 3], index: 0});
null, 'qunit-fixture', {model: null, ids: [1, 2, 3], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table({records: [
@ -94,8 +82,7 @@ $(document).ready(function () {
});
test('render deletion button if list is deletable', 1, function () {
var listview = new openerp.base.ListView(
fuck_that_shit, null, 'qunit-fixture',
{model: null, ids: [null, null, null], index: 0});
null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
listview.on_loaded(fvg);
@ -112,7 +99,7 @@ $(document).ready(function () {
2, function () {
var deleted;
var listview = new openerp.base.ListView(
fuck_that_shit, null, 'qunit-fixture',
null, 'qunit-fixture',
{model: null, unlink: function (ids) {
deleted = ids;
}, ids: [1, 2, 3], index: 0});
@ -132,7 +119,7 @@ $(document).ready(function () {
test('multiple records deletion', 1, function () {
var deleted;
var listview = new openerp.base.ListView(
fuck_that_shit, null, 'qunit-fixture',
null, 'qunit-fixture',
{model: null, unlink: function (ids) {
deleted = ids;
}, ids: [1, 2, 3], index: 0});

View File

@ -5,11 +5,11 @@
openerp.base_calendar = function(openerp) {
QWeb.add_template('/base_calendar/static/src/xml/base_calendar.xml');
openerp.base.views.add('calendar', 'openerp.base_calendar.CalendarView');
openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
openerp.base_calendar.CalendarView = openerp.base.View.extend({
// Dhtmlx scheduler ?
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 || new openerp.base.NullViewManager();
this.dataset = dataset;
this.dataset_index = 0;
this.model = dataset.model;
@ -194,7 +194,9 @@ openerp.base_calendar.CalendarView = openerp.base.Controller.extend({
// self.color_field
console.log('Reload Scheduler>>>')
},
do_search: function (domains, contexts, groupbys) {
this.notification.notify("Searching caldendar");
},
do_show: function () {
this.$element.show();
},

View File

@ -89,7 +89,7 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
}
}
});
new openerp.base.ViewManagerAction(this.session, dialog_id, action).start();
new openerp.base.ViewManagerAction(this, dialog_id, action).start();
// TODO: should bind ListView#select_record in order to catch record clicking
},
do_add_widget : function(action_manager) {
@ -227,7 +227,7 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
action_buttons : false,
pager: false
};
new openerp.base.ViewManagerAction(this.session,
new openerp.base.ViewManagerAction(this,
this.view.element_id + '_action_' + action.id, action).start();
},
render: function() {

View File

@ -5,7 +5,7 @@
openerp.base.diagram = function (openerp) {
openerp.base.views.add('diagram', 'openerp.base.DiagramView');
openerp.base.DiagramView = openerp.base.Controller.extend({
openerp.base.DiagramView = openerp.base.Widget.extend({
init: function(view_manager, session, element_id, dataset, view_id){
this._super(session, element_id);
this.view_manager = view_manager;

View File

@ -5,7 +5,7 @@
openerp.base_gantt = function (openerp) {
QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
openerp.base_gantt.GanttView = openerp.base.Controller.extend({
openerp.base_gantt.GanttView = openerp.base.Widget.extend({
init: function(view_manager, session, element_id, dataset, view_id) {

View File

@ -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);
});
}

View File

@ -1,6 +1,4 @@
<template>
<t t-name="GraphView">
<h2 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h2>
<div t-att-id="elem_id+'-'+chart+'chart'" style="height:300px;position:relative;"></div>
</t>
</template>
<div t-name="GraphView" t-att-id="elem_id+'-'+chart+'chart'"
style="height:300px;position:relative;"/>
</template>

View File

@ -0,0 +1,4 @@
.ui-collapsible-content-expanded {
display: block;
}

View File

@ -0,0 +1,217 @@
openerp.web_mobile.chrome_mobile = function(openerp) {
openerp.web_mobile.Shortcuts = openerp.base.Widget.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.Widget.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.Widget.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.Widget.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.Widget.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.Widget.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.Widget.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;
};
}

View File

@ -0,0 +1,104 @@
openerp.web_mobile.form_mobile = function (openerp) {
openerp.web_mobile.FormView = openerp.base.Widget.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;
},
});
}

View File

@ -0,0 +1,67 @@
openerp.web_mobile.list_mobile = function (openerp) {
openerp.web_mobile.ListView = openerp.base.Widget.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();
}
});
}

View File

@ -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);
};

View File

@ -2,11 +2,13 @@
<html style="height: 100%">
<head>
<meta charset="utf-8" />
<title>Login Page OpenERP mobile</title>
<title>OpenERP Web Mobile</title>
<link rel="stylesheet" href="/web_mobile/static/lib/jquery_mobile/css/jquery.mobile-1.0a4.1.css" />
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery/jquery-1.5.2.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script>
<script type="text/javascript" src="/web_mobile/static/lib/jquery_mobile/js/jquery.mobile-1.0a4.1.js"></script>
<script type="text/javascript" src="/base/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
<script type="text/javascript" src="/base/static/lib/underscore/underscore.js"></script>
@ -17,21 +19,23 @@
<script type="text/javascript" src="/base/static/src/js/base.js"></script>
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
<script type="text/javascript" src="/base/static/src/js/data.js"></script>
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/chrome_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/list_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/form_mobile.js"></script>
<link rel="stylesheet" href="/web_mobile/static/src/css/web_mobile.css"/>
<script type="text/javascript">
$(function() {
QWeb = window.QWeb || new QWeb2.Engine();
var oe = openerp.init();
openerp.web_mobile(oe);
oe.web_mobile.mobilewebclient("moe");
$("html").page();
});
</script>
</head>
</head>
<body>
<div id="moe" class="openerp" data-role="page"></div>

View File

@ -36,7 +36,7 @@
<span id="db_text" class="ui-btn-text">Choose Database...</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select data-native-menu="false" id="database" name="database" tabindex="-1">
<select id="database" name="database">
<t t-foreach="db_list" t-as="opt">
<option t-att-value="opt"><t t-esc="opt"/></option>
</t>
@ -67,12 +67,17 @@
<t t-name="ListView">
<div role="main" class="ui-content" data-role="content">
<ul data-role="listview" class="ui-listview">
<form class="ui-listview-filter ui-bar-c" role="search">
<div id="search-data" class="ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield ui-body-c">
<input id="searchid" placeholder="Filter items..." data-type="search" class="ui-input-text ui-body-null" autosearch="true" setTimeDefault="700"/>
</div>
</form>
<ul role="listbox" data-role="listview" class="ui-listview">
<li data-theme="c" class="ui-btn ui-btn-icon-right ui-li ui-btn-up-c" t-foreach="records" t-as="record">
<div class="ui-btn-inner ui-li">
<div class="ui-btn-text">
<a href="#" class="ui-link-inherit">
<span><t t-esc="record.name"/></span>
<a id="list-id" t-att-data-id="record[0]" href="#" class="ui-link-inherit">
<span><t t-esc="record[1]"/></span>
</a>
</div>
<span class="ui-icon ui-icon-arrow-r"></span>
@ -82,6 +87,151 @@
</div>
</t>
<t t-name="FormView">
<div role="main" class="ui-content" data-role="content">
<form>
<t t-foreach="get_fields" t-as="field">
<div data-role="fieldcontain">
<label class="ui-input-text">
<span>
<t t-if="!(field.attrs.string)" t-esc="fields[field.attrs.name].string"></t>
<t t-if="field.attrs.string" t-esc="field.attrs.string"></t>
</span>:
</label>
<t t-if="fields[field.attrs.name].type == 'char'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'boolean'">
<t t-if="values[field.attrs.name]">
<input type="checkbox" checked="yes"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input type="checkbox"/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'text'">
<input type="text" style="width: 100%"/><br/>
</t>
<t t-if="fields[field.attrs.name].type == 'selection'">
<div class="ui-select">
<div data-theme="c" class="ui-btn ui-btn-icon-right ui-btn-corner-all ui-shadow ui-btn-up-c">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">
<t t-if="values[field.attrs.name]">
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<t t-if="opt[0] == (values[field.attrs.name])">
<t t-esc="opt[1]"/>
</t>
</t>
</t>
</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select>
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<option t-att-value="opt[0]"><t t-esc="opt[1]"/></option>
</t>
</select>
</div>
</div>
</t>
<t t-if="fields[field.attrs.name].type == 'many2one'">
<div class="ui-select">
<div data-theme="c" class="ui-btn ui-btn-icon-right ui-btn-corner-all ui-shadow ui-btn-up-c">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">
<t t-esc="values[field.attrs.name][1]"/>
</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow"></span>
</span>
<select>
<t t-if="fields[field.attrs.name].selection">
<t t-foreach="fields[field.attrs.name].selection" t-as="opt">
<option t-att-value="opt[0]"><t t-esc="opt[1]"/></option>
</t>
</t>
</select>
</div>
</div>
</t>
<t t-if="fields[field.attrs.name].type == 'one2many'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'many2many'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'float'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'date'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
<t t-if="fields[field.attrs.name].type == 'datetime'">
<t t-if="values[field.attrs.name]">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" t-att-value="values[field.attrs.name]"/><br/>
</t>
<t t-if="!(values[field.attrs.name])">
<input class="ui-input-text ui-body-null ui-corner-all ui-shadow-inset ui-body-c" type="text" value=""/><br/>
</t>
</t>
</div>
</t>
<t t-if="notebooks">
<t t-foreach="notebooks.children" t-as="page">
<div class="info ui-collapsible-contain" data-collapsed="true" data-role="collapsible">
<h1 class="ui-collapsible-heading">
<a class="ui-collapsible-heading-toggle ui-btn ui-btn-icon-left ui-shadow ui-corner-all ui-btn-up-c" href="#" data-theme="c" t-id="page.attrs.string">
<span class="ui-btn-inner ui-corner-all">
<span class="ui-btn-text">
<t t-esc="page.attrs.string"></t>
<!-- <span class="ui-collapsible-heading-status"></span> -->
</span>
<span data-theme="d" class="ui-btn ui-btn-icon-left ui-btn-corner-all ui-shadow ui-btn-up-d">
<span class="ui-btn-inner ui-btn-corner-all ui-corner-all">
<span class="ui-btn-text"></span>
<span class="ui-icon ui-icon-shadow ui-icon-plus"></span>
</span>
</span>
</span>
</a>
<div class="ui-collapsible-content ui-collapsible-content-collapsed" aria-hidden="true">
<div class="detail"></div>
</div>
</h1>
</div>
</t>
</t>
</form>
</div>
</t>
<t t-name="Shortcuts">
<ul data-dividertheme="b" data-theme="c" data-inset="true" data-role="listview" class="ui-listview ui-listview-inset ui-corner-all ui-shadow">
<li data-role="list-divider" role="heading" class="ui-li ui-li-divider ui-btn ui-bar-b ui-corner-top ui-btn-up-undefined">

View File

@ -407,7 +407,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())
@ -445,7 +445,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):