[MERGE] updates from trunk
bzr revid: xmo@openerp.com-20110802094717-yxmi281j1l139tws
This commit is contained in:
@@ -20,12 +20,13 @@
|
||||
"static/lib/qweb/qweb2.js",
|
||||
"static/lib/underscore/underscore.js",
|
||||
"static/lib/underscore/underscore.string.js",
|
||||
"static/src/js/base.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/core.js",
|
||||
"static/src/js/dates.js",
|
||||
"static/src/js/chrome.js",
|
||||
"static/src/js/controller.js",
|
||||
"static/src/js/views.js",
|
||||
"static/src/js/data.js",
|
||||
"static/src/js/dates.js",
|
||||
"static/src/js/data_export.js",
|
||||
"static/src/js/form.js",
|
||||
"static/src/js/list.js",
|
||||
"static/src/js/list-editable.js",
|
||||
@@ -37,5 +38,6 @@
|
||||
"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",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import openerpweb.nonliterals
|
||||
|
||||
import cherrypy
|
||||
import xmlrpclib
|
||||
import csv
|
||||
|
||||
# Should move to openerpweb.Xml2Json
|
||||
class Xml2Json:
|
||||
@@ -305,7 +306,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)
|
||||
@@ -316,7 +317,7 @@ class Session(openerpweb.Controller):
|
||||
group_by_sequence.append(group_by)
|
||||
else:
|
||||
group_by_sequence.extend(group_by)
|
||||
|
||||
|
||||
return {
|
||||
'context': context,
|
||||
'domain': domain,
|
||||
@@ -329,7 +330,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.
|
||||
@@ -352,7 +353,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.
|
||||
@@ -487,7 +488,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:
|
||||
@@ -594,7 +595,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)
|
||||
@@ -757,7 +758,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`
|
||||
@@ -986,4 +987,216 @@ 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):
|
||||
fp = 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):
|
||||
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()
|
||||
workbook.save(fp)
|
||||
fp.seek(0)
|
||||
data = fp.read()
|
||||
fp.close()
|
||||
#return data.decode('ISO-8859-1')
|
||||
return unicode(data, 'utf-8', 'replace')
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ body.openerp {
|
||||
.openerp .oe-number {
|
||||
text-align: right !important;
|
||||
}
|
||||
.openerp .oe_hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* STATES */
|
||||
.openerp .on_logged,
|
||||
@@ -1169,3 +1172,10 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.openerp .dhx_mini_calendar {
|
||||
-moz-box-shadow: none;
|
||||
-khtml-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -1,5 +1,5 @@
|
||||
//---------------------------------------------------------
|
||||
// OpenERP initialisation and black magic about the pool
|
||||
// OpenERP Web Boostrap
|
||||
//---------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -57,9 +57,8 @@
|
||||
// OpenERP base module split
|
||||
//---------------------------------------------------------
|
||||
|
||||
/** @namespace */
|
||||
openerp.base = function(instance) {
|
||||
openerp.base.controller(instance);
|
||||
openerp.base.core(instance);
|
||||
openerp.base.dates(instance);
|
||||
openerp.base.chrome(instance);
|
||||
openerp.base.data(instance);
|
||||
@@ -87,6 +86,9 @@ openerp.base = function(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:
|
||||
@@ -4,241 +4,7 @@
|
||||
|
||||
openerp.base.chrome = function(openerp) {
|
||||
|
||||
/**
|
||||
* 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 || {}));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* OpenERP session aware controller
|
||||
* a controller takes an already existing dom element and manage it
|
||||
*/
|
||||
openerp.base.Controller = openerp.base.Controller.extend( /** @lends openerp.base.Controller# */{
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
if(this.controller_parent && this.controller_parent.session) {
|
||||
this.session = this.controller_parent.session;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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);
|
||||
},
|
||||
do_action: function(action, on_finished) {
|
||||
return this.parent.do_action(action, on_finished);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* OpenERP session aware widget
|
||||
* A widget is a controller that doesnt take an element_id
|
||||
* it render its own html render() that you should insert into the dom
|
||||
* and bind it at start()
|
||||
*/
|
||||
openerp.base.BaseWidget = openerp.base.Controller.extend({
|
||||
/**
|
||||
* The name of the QWeb template that will be used for rendering. Must be
|
||||
* redefined in subclasses or the render() method can not be used.
|
||||
*
|
||||
* @type string
|
||||
*/
|
||||
template: null,
|
||||
/**
|
||||
* The prefix used to generate an id automatically. Should be redefined in
|
||||
* subclasses. If it is not defined, a default identifier will be used.
|
||||
*
|
||||
* @type string
|
||||
*/
|
||||
identifier_prefix: 'generic-identifier',
|
||||
/**
|
||||
* Base class for widgets. Handle rendering (based on a QWeb template),
|
||||
* identifier generation, parenting and destruction of the widget.
|
||||
* Also initialize the identifier.
|
||||
*
|
||||
* @constructs
|
||||
* @params {openerp.base.search.BaseWidget} parent The parent widget.
|
||||
*/
|
||||
init: function (parent) {
|
||||
this._super(parent);
|
||||
this.make_id(this.identifier_prefix);
|
||||
},
|
||||
/**
|
||||
* Sets and returns a globally unique identifier for the widget.
|
||||
*
|
||||
* If a prefix is appended, the identifier will be appended to it.
|
||||
*
|
||||
* @params sections prefix sections, empty/falsy sections will be removed
|
||||
*/
|
||||
make_id: function () {
|
||||
this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
|
||||
return this.element_id;
|
||||
},
|
||||
/**
|
||||
* Render the widget. This.template must be defined.
|
||||
* The content of the current object is passed as context to the template.
|
||||
*
|
||||
* @param {object} additional Additional context arguments to pass to the template.
|
||||
*/
|
||||
render: function (additional) {
|
||||
return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
|
||||
},
|
||||
/**
|
||||
* "Starts" the widgets. Called at the end of the rendering, this allows
|
||||
* to get a jQuery object referring to the DOM ($element attribute).
|
||||
*/
|
||||
start: function () {
|
||||
this._super();
|
||||
var tmp = document.getElementById(this.element_id);
|
||||
this.$element = tmp ? $(tmp) : null;
|
||||
},
|
||||
/**
|
||||
* "Stops" the widgets. Called when the view destroys itself, this
|
||||
* lets the widgets clean up after themselves.
|
||||
*/
|
||||
stop: function () {
|
||||
if(this.$element != null) {
|
||||
this.$element.remove();
|
||||
}
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.Session# */{
|
||||
openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Session# */{
|
||||
/**
|
||||
* @constructs
|
||||
* @param element_id to use for exception reporting
|
||||
@@ -506,7 +272,7 @@ openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.S
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Notification = openerp.base.Controller.extend({
|
||||
openerp.base.Notification = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.$element.notify({
|
||||
@@ -528,7 +294,7 @@ openerp.base.Notification = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
||||
openerp.base.Dialog = openerp.base.OldWidget.extend({
|
||||
dialog_title: "",
|
||||
identifier_prefix: 'dialog',
|
||||
init: function (parent, options) {
|
||||
@@ -544,8 +310,8 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
||||
max_height: '100%',
|
||||
autoOpen: false,
|
||||
buttons: {},
|
||||
close: function () {
|
||||
self.stop();
|
||||
beforeClose: function () {
|
||||
self.on_close();
|
||||
}
|
||||
};
|
||||
for (var f in this) {
|
||||
@@ -609,7 +375,15 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
||||
this.set_options(options);
|
||||
this.$dialog.dialog(this.options).dialog('open');
|
||||
},
|
||||
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');
|
||||
}
|
||||
});
|
||||
@@ -650,7 +424,7 @@ openerp.base.CrashManager = openerp.base.Dialog.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Loading = openerp.base.Controller.extend({
|
||||
openerp.base.Loading = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.count = 0;
|
||||
@@ -669,7 +443,7 @@ openerp.base.Loading = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Database = openerp.base.Controller.extend({
|
||||
openerp.base.Database = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, option_id) {
|
||||
this._super(parent, element_id);
|
||||
this.$option_id = $('#' + option_id);
|
||||
@@ -968,7 +742,7 @@ openerp.base.Database = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Login = openerp.base.Controller.extend({
|
||||
openerp.base.Login = openerp.base.Widget.extend({
|
||||
remember_creditentials: true,
|
||||
|
||||
init: function(parent, element_id) {
|
||||
@@ -986,9 +760,6 @@ openerp.base.Login = openerp.base.Controller.extend({
|
||||
this.selected_login = this.selected_login || "admin";
|
||||
this.selected_password = this.selected_password || "a";
|
||||
}
|
||||
if(this.parent && this.parent.session) {
|
||||
this.session = this.parent.session;
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
@@ -1069,11 +840,9 @@ openerp.base.Login = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Header = openerp.base.Controller.extend({
|
||||
openerp.base.Header = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
if(parent)
|
||||
this.session = parent.session;
|
||||
},
|
||||
start: function() {
|
||||
this.do_update();
|
||||
@@ -1085,7 +854,7 @@ openerp.base.Header = openerp.base.Controller.extend({
|
||||
on_logout: function() {}
|
||||
});
|
||||
|
||||
openerp.base.Menu = openerp.base.Controller.extend({
|
||||
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;
|
||||
@@ -1166,16 +935,16 @@ 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) {
|
||||
this._super(null, element_id);
|
||||
|
||||
@@ -1192,7 +961,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
|
||||
this.crashmanager.start(false);
|
||||
|
||||
// Do you autorize this ? will be replaced by notify() in controller
|
||||
openerp.base.Controller.prototype.notification = new openerp.base.Notification(this, "oe_notification");
|
||||
openerp.base.Widget.prototype.notification = new openerp.base.Notification(this, "oe_notification");
|
||||
|
||||
this.header = new openerp.base.Header(this, "oe_header");
|
||||
this.login = new openerp.base.Login(this, "oe_login");
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
/*---------------------------------------------------------
|
||||
* OpenERP controller framework
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
openerp.base.controller = function(instance) {
|
||||
/**
|
||||
* 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
|
||||
instance.base.Class = window.Class
|
||||
|
||||
instance.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}
|
||||
*/
|
||||
instance.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 = instance.base.Controller.prototype.init;
|
||||
var tmpclass = claz.extend(newer);
|
||||
return tmpclass.extend(add || {});
|
||||
};
|
||||
|
||||
/**
|
||||
* OpenERP Controller
|
||||
* TODO merge BaseWidget with Controller
|
||||
*/
|
||||
instance.base.Controller = instance.base.Class.extend( /** @lends instance.base.Controller# */{
|
||||
/**
|
||||
* @constructs
|
||||
* rpc operations, event binding and callback calling should be done in
|
||||
* start() instead of init so that events can be hooked in between.
|
||||
*/
|
||||
init: function(parent, element_id) {
|
||||
this.element_id = element_id;
|
||||
this.$element = $('#' + element_id);
|
||||
if (element_id) {
|
||||
instance.screen[element_id] = this;
|
||||
}
|
||||
// save the parent children relationship
|
||||
this.controller_parent = parent;
|
||||
this.controller_children = [];
|
||||
if(parent && parent.controller_children) {
|
||||
parent.controller_children.push(this);
|
||||
}
|
||||
// backward compatibility
|
||||
this.parent = this.controller_parent;
|
||||
this.children = this.controller_children;
|
||||
|
||||
// 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] = instance.base.callback(this, this[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Event binding, rpc and callback calling required to initialize the
|
||||
* object should 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() {
|
||||
if (this.parent && this.parent.children) {
|
||||
this.parent.children = _.without(this.parent.children, this);
|
||||
this.parent.controller_children = this.parent.children;
|
||||
}
|
||||
this.parent = null;
|
||||
this.controller_parent = null;
|
||||
},
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
@@ -0,0 +1,572 @@
|
||||
/*---------------------------------------------------------
|
||||
* 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)
|
||||
openerp.base.Class = function(){};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
openerp.base.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.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-',
|
||||
/**
|
||||
* Construct the widget and set its parent if a parent is given.
|
||||
*
|
||||
* @constructs
|
||||
* @param {openerp.base.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:
|
||||
@@ -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
|
||||
@@ -232,13 +232,13 @@ openerp.base.StaticDataGroup = openerp.base.GrouplessDataGroup.extend( /** @lend
|
||||
}
|
||||
});
|
||||
|
||||
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 {String} model the OpenERP model this dataset will manage
|
||||
*/
|
||||
@@ -291,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', {
|
||||
@@ -492,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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
@@ -20,7 +20,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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;
|
||||
@@ -31,8 +30,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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;
|
||||
@@ -50,9 +48,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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.options.sidebar, context: context}, this.on_loaded);
|
||||
}
|
||||
@@ -93,9 +88,10 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
if (this.options.sidebar && this.options.sidebar_id) {
|
||||
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
|
||||
this.sidebar.start();
|
||||
this.sidebar.attachments = new openerp.base.form.SidebarAttachments(this.sidebar, this.sidebar.add_section("Attachments"), this);
|
||||
this.sidebar.add_toolbar(data.fields_view.toolbar);
|
||||
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();
|
||||
},
|
||||
@@ -134,22 +130,22 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -268,7 +264,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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);
|
||||
}
|
||||
@@ -316,37 +312,40 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
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() {
|
||||
@@ -360,7 +359,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
on_invalid: function() {
|
||||
var msg = "<ul>";
|
||||
_.each(this.fields, function(f) {
|
||||
if (f.invalid) {
|
||||
if (!f.is_valid()) {
|
||||
msg += "<li>" + f.string + "</li>";
|
||||
}
|
||||
});
|
||||
@@ -443,7 +442,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
/** @namespace */
|
||||
openerp.base.form = {};
|
||||
|
||||
openerp.base.form.SidebarAttachments = openerp.base.Controller.extend({
|
||||
openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, form_view) {
|
||||
this._super(parent, element_id);
|
||||
this.view = form_view;
|
||||
@@ -550,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;
|
||||
@@ -716,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;
|
||||
@@ -727,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);
|
||||
});
|
||||
@@ -772,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') {
|
||||
@@ -807,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;
|
||||
@@ -820,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();
|
||||
@@ -932,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;
|
||||
@@ -973,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);
|
||||
@@ -993,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);
|
||||
@@ -1080,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);
|
||||
@@ -1287,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;
|
||||
}
|
||||
|
||||
@@ -1470,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, 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();
|
||||
@@ -1587,6 +1598,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
|
||||
this.template = "FieldOne2Many";
|
||||
this.is_started = $.Deferred();
|
||||
this.form_last_update = $.Deferred();
|
||||
this.disable_utility_classes = true;
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -1632,9 +1644,16 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
|
||||
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);
|
||||
@@ -1704,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();
|
||||
});
|
||||
@@ -1728,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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1797,7 +1860,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
|
||||
|
||||
var self = this;
|
||||
|
||||
this.dataset = new openerp.base.form.Many2ManyDataSet( this, 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();
|
||||
@@ -1872,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",
|
||||
/**
|
||||
@@ -2012,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",
|
||||
/**
|
||||
|
||||
@@ -111,7 +111,6 @@ 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));
|
||||
|
||||
@@ -272,6 +271,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
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);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
openerp.base.search = function(openerp) {
|
||||
|
||||
openerp.base.SearchView = openerp.base.Controller.extend({
|
||||
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();
|
||||
@@ -386,13 +386,13 @@ openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base
|
||||
': [' + 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
|
||||
*/
|
||||
@@ -851,7 +851,7 @@ 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, model) {
|
||||
@@ -860,9 +860,7 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
|
||||
},
|
||||
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 () {
|
||||
@@ -891,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() {
|
||||
@@ -910,14 +908,14 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
|
||||
}
|
||||
},
|
||||
check_last_element: function() {
|
||||
_.each(this.children, function(x) {x.set_last_group(false);});
|
||||
if (this.children.length >= 1) {
|
||||
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) {
|
||||
@@ -926,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();
|
||||
},
|
||||
@@ -943,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();
|
||||
@@ -953,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();
|
||||
},
|
||||
@@ -967,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) {
|
||||
@@ -992,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();
|
||||
@@ -1054,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: [
|
||||
@@ -1071,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: [
|
||||
@@ -1093,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: [
|
||||
@@ -1115,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: [
|
||||
@@ -1134,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: [
|
||||
@@ -1153,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: [
|
||||
@@ -1167,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: [
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
openerp.base.view_help = function(openerp) {
|
||||
|
||||
openerp.base.ProcessView = openerp.base.Controller.extend({
|
||||
openerp.base.ProcessView = openerp.base.Widget.extend({
|
||||
});
|
||||
|
||||
openerp.base.HelpView = openerp.base.Controller.extend({
|
||||
openerp.base.HelpView = openerp.base.Widget.extend({
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
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(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
@@ -98,18 +98,21 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
|
||||
openerp.base.ActionDialog = openerp.base.Dialog.extend({
|
||||
identifier_prefix: 'action_dialog',
|
||||
stop: function() {
|
||||
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.Controller.extend({
|
||||
openerp.base.ViewManager = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, dataset, views) {
|
||||
this._super(parent, element_id);
|
||||
this.model = dataset.model;
|
||||
@@ -126,6 +129,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
* @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}));
|
||||
@@ -216,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
|
||||
*/
|
||||
@@ -330,7 +334,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
// should be replaced by automatic destruction implemented in BaseWidget
|
||||
// should be replaced by automatic destruction implemented in Widget
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
@@ -357,10 +361,11 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
openerp.base.Sidebar = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.items = {};
|
||||
this.sections = {};
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
@@ -382,11 +387,11 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
classname: 'oe_sidebar_' + type[0]
|
||||
}
|
||||
}
|
||||
self.add_section(type[1], items);
|
||||
self.add_section(type[0], type[1], items);
|
||||
}
|
||||
});
|
||||
},
|
||||
add_section: function(name, 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.
|
||||
@@ -395,11 +400,12 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
// 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: optionnal dom class name for the line,
|
||||
// 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_');
|
||||
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_');
|
||||
@@ -409,6 +415,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
var $section = $(QWeb.render("Sidebar.section", {
|
||||
section_id: section_id,
|
||||
name: name,
|
||||
classname: 'oe_sidebar_' + code,
|
||||
items: items
|
||||
}));
|
||||
if (items) {
|
||||
@@ -426,6 +433,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
});
|
||||
}
|
||||
$section.appendTo(this.$element.find('div.sidebar-actions'));
|
||||
this.sections[code] = $section;
|
||||
return section_id;
|
||||
},
|
||||
do_fold: function() {
|
||||
@@ -439,26 +447,7 @@ openerp.base.Sidebar = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.Export = openerp.base.Dialog.extend({
|
||||
dialog_title: "Export",
|
||||
template: 'ExportDialog',
|
||||
identifier_prefix: 'export_dialog',
|
||||
init: function (session, model, domain) {
|
||||
this._super();
|
||||
},
|
||||
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({
|
||||
openerp.base.View = openerp.base.Widget.extend({
|
||||
set_default_options: function(options) {
|
||||
this.options = options || {};
|
||||
_.defaults(this.options, {
|
||||
@@ -522,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() {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -294,7 +294,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>
|
||||
@@ -326,7 +326,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Header">
|
||||
@@ -439,10 +439,10 @@
|
||||
</t>
|
||||
<t t-name="Sidebar.section">
|
||||
<h2><t t-esc="name"/></h2>
|
||||
<div t-att-id="section_id">
|
||||
<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" href="#">
|
||||
<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>
|
||||
@@ -452,11 +452,11 @@
|
||||
<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)"/>
|
||||
<thead class="ui-widget-header">
|
||||
<tr t-if="flags.action_buttons !== false or flags.pager !== false">
|
||||
<tr t-if="options.action_buttons !== false or options.pager !== false">
|
||||
<th t-att-colspan="columns_count">
|
||||
<table>
|
||||
<tr>
|
||||
<td t-if="flags.action_buttons !== false" class="oe-actions">
|
||||
<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"/>
|
||||
@@ -466,7 +466,7 @@
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
<th t-if="flags.pager !== false" class="oe-list-pager">
|
||||
<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"
|
||||
@@ -524,7 +524,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">
|
||||
@@ -548,7 +548,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>
|
||||
@@ -560,7 +560,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"><<</button>
|
||||
|
||||
@@ -1012,7 +1012,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>
|
||||
@@ -1135,4 +1135,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">&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>
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
"depends": ['base'],
|
||||
"js": [
|
||||
'static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.js',
|
||||
'static/lib/dhtmlxScheduler/codebase/ext/dhtmlxscheduler_minical.js',
|
||||
'static/src/js/calendar.js'
|
||||
],
|
||||
"css": ['static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.css'],
|
||||
"css": ['static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.css',
|
||||
'static/lib/dhtmlxScheduler/codebase/ext/dhtmlxscheduler_ext.css'
|
||||
],
|
||||
'active': True
|
||||
}
|
||||
|
||||
@@ -1,356 +1,10 @@
|
||||
from base.controllers.main import View
|
||||
import openerpweb, time, math, re, datetime as DT, pytz
|
||||
|
||||
COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
|
||||
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
|
||||
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
|
||||
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ]
|
||||
|
||||
_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ]
|
||||
|
||||
DT_SERVER_FORMATS = {
|
||||
'datetime' : '%Y-%m-%d %H:%M:%S',
|
||||
'date' : '%Y-%m-%d',
|
||||
'time' : '%H:%M:%S'
|
||||
}
|
||||
|
||||
DT_FORMAT_INFO = {'datetime' : ('%Y-%m-%d %H:%M:%S', DT.datetime, 0, 6),
|
||||
'date': ('%Y-%m-%d', DT.date, 0, 3),
|
||||
'time': ('%H:%M:%S', DT.time, 3, 6)}
|
||||
|
||||
def choice_colors(n):
|
||||
if n > len(COLOR_PALETTE):
|
||||
return _colorline[0:-1:len(_colorline) / (n + 1)]
|
||||
elif n:
|
||||
return COLOR_PALETTE[:n]
|
||||
return []
|
||||
import openerpweb
|
||||
|
||||
class CalendarView(View):
|
||||
_cp_path = "/base_calendar/calendarview"
|
||||
|
||||
mode = 'month'
|
||||
date_start = None
|
||||
date_delay = None
|
||||
date_stop = None
|
||||
color_field = None
|
||||
day_length = 8
|
||||
use_search = False
|
||||
selected_day = None
|
||||
date_format = '%Y-%m-%d'
|
||||
info_fields = []
|
||||
fields = {}
|
||||
events = []
|
||||
|
||||
colors = {}
|
||||
color_values = []
|
||||
|
||||
remote_timezone = 'utc'
|
||||
client_timezone = False
|
||||
|
||||
calendar_fields = {}
|
||||
concurrency_info = None
|
||||
|
||||
ids = []
|
||||
model = ''
|
||||
domain = []
|
||||
context = {}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'calendar')
|
||||
return {'fields_view':fields_view}
|
||||
|
||||
def convert(self, event):
|
||||
fields = [self.date_start]
|
||||
if self.date_stop:
|
||||
fields.append(self.date_stop)
|
||||
|
||||
for fld in fields:
|
||||
fld_type = self.fields[fld]['type']
|
||||
fmt = DT_SERVER_FORMATS[fld_type]
|
||||
if event[fld] and fmt:
|
||||
event[fld] = time.strptime(event[fld], fmt)
|
||||
|
||||
# default start/stop time is 9:00 AM / 5:00 PM
|
||||
if fld_type == 'date' and event[fld]:
|
||||
ds = list(event[fld])
|
||||
if fld == self.date_start:
|
||||
ds[3] = 9
|
||||
elif fld == self.date_stop:
|
||||
ds[3] = 17
|
||||
event[fld] = tuple(ds)
|
||||
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def schedule_events(self, req, **kw):
|
||||
self.model = kw['model']
|
||||
self.mode = kw.get('mode') or self.mode or 'month'
|
||||
self.fields = kw['fields']
|
||||
self.color_field = kw.get('color_field') or self.color_field or None
|
||||
self.colors = kw.get('colors') or {}
|
||||
self.calendar_fields = kw['calendar_fields']
|
||||
self.info_fields = kw['info_fields']
|
||||
self.date_start = self.calendar_fields['date_start']['name']
|
||||
self.domain = kw.get('domain') or []
|
||||
|
||||
self.remote_timezone = req.session.remote_timezone
|
||||
self.client_timezone = req.session.client_timezone
|
||||
|
||||
if self.calendar_fields.get('date_stop'):
|
||||
self.date_stop = self.calendar_fields['date_stop']['name']
|
||||
|
||||
if self.calendar_fields.get('date_delay'):
|
||||
self.date_delay = self.calendar_fields['date_delay']['name']
|
||||
|
||||
model = req.session.model(self.model)
|
||||
event_ids = model.search(self.domain)
|
||||
|
||||
self.events = model.read(event_ids, self.fields.keys())
|
||||
result = []
|
||||
self.date_format = req.session._lang and req.session._lang['date_format']
|
||||
|
||||
if self.color_field:
|
||||
for evt in self.events:
|
||||
key = evt[self.color_field]
|
||||
name = key
|
||||
value = key
|
||||
if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
|
||||
name = key[0]
|
||||
value = key[-1]
|
||||
evt[self.color_field] = key = key[-1]
|
||||
if isinstance(key, tuple): # M2O
|
||||
value, name = key
|
||||
self.colors[key] = (name, value, None)
|
||||
|
||||
colors = choice_colors(len(self.colors))
|
||||
for i, (key, value) in enumerate(self.colors.items()):
|
||||
self.colors[key] = [value[0], value[1], colors[i]]
|
||||
|
||||
for evt in self.events:
|
||||
self.convert(evt)
|
||||
a = self.get_event_widget(evt)
|
||||
result.append(a)
|
||||
|
||||
return {'result':result,'sidebar':self.colors}
|
||||
|
||||
def parsedatetime(self, string):
|
||||
|
||||
kind = 'datetime'
|
||||
|
||||
if '-' in string and ':' in string:
|
||||
kind = 'datetime'
|
||||
elif '-' in string:
|
||||
kind = 'date'
|
||||
elif ':' in string:
|
||||
kind = 'time'
|
||||
|
||||
fmt, obj, i, j = DT_FORMAT_INFO[kind]
|
||||
return obj(*time.strptime(string, fmt)[i:j])
|
||||
|
||||
def parse_datetime(self, value, kind="datetime", as_timetuple=False):
|
||||
server_format = DT_SERVER_FORMATS[kind]
|
||||
local_format = self.date_format
|
||||
if not value:
|
||||
return False
|
||||
|
||||
if isinstance(value, (time.struct_time, tuple)):
|
||||
value = time.strftime(local_format, value)
|
||||
|
||||
try:
|
||||
value = time.strptime(value, local_format)
|
||||
except ValueError:
|
||||
try:
|
||||
# might be in server format already (e.g. filter domain)
|
||||
value = time.strptime(value, server_format)
|
||||
except ValueError:
|
||||
try:
|
||||
dt = list(time.localtime())
|
||||
dt[2] = int(value)
|
||||
value = tuple(dt)
|
||||
except:
|
||||
return False
|
||||
|
||||
if kind == "datetime":
|
||||
try:
|
||||
value = self.tz_convert(value, 'parse')
|
||||
except Exception,e:
|
||||
print "*******************Error in timezone parsing *********",e
|
||||
|
||||
if as_timetuple:
|
||||
return value
|
||||
|
||||
return time.strftime(server_format, value)
|
||||
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def edit_events(self, req,**kw):
|
||||
data = {}
|
||||
ds = self.parsedatetime(kw['start_date'])
|
||||
de = self.parsedatetime(kw['end_date'])
|
||||
data[kw['calendar_fields']['date_start']['name']] = self.parse_datetime(ds.timetuple())
|
||||
|
||||
if 'date_stop' in kw['calendar_fields']:
|
||||
data[kw['calendar_fields']['date_stop']['name']] = self.parse_datetime(de.timetuple())
|
||||
elif 'date_delay' in kw['calendar_fields']:
|
||||
day_length = kw['calendar_fields']['day_length']
|
||||
|
||||
tds = time.mktime(ds.timetuple())
|
||||
tde = time.mktime(de.timetuple())
|
||||
|
||||
n = (tde - tds) / (60 * 60)
|
||||
|
||||
if n > day_length:
|
||||
d = math.floor(n / 24)
|
||||
h = n % 24
|
||||
|
||||
n = d * day_length + h
|
||||
|
||||
data[kw['calendar_fields']['date_delay']['name']] = n
|
||||
error = None
|
||||
try:
|
||||
req.session.model(kw['model']).write([int(kw['id'])], data)
|
||||
except Exception, e:
|
||||
error = e
|
||||
return error
|
||||
|
||||
def tz_convert(self, struct_time, action):
|
||||
# if no client timezone is configured, consider the client is in the same
|
||||
# timezone as the server
|
||||
lzone = pytz.timezone(self.client_timezone or self.remote_timezone)
|
||||
szone = pytz.timezone(self.remote_timezone)
|
||||
dt = DT.datetime.fromtimestamp(time.mktime(struct_time))
|
||||
|
||||
if action == 'parse':
|
||||
fromzone = lzone
|
||||
tozone = szone
|
||||
elif action == 'format':
|
||||
fromzone = szone
|
||||
tozone = lzone
|
||||
else:
|
||||
raise Exception("_tz_convert action should be 'parse' or 'format'. Not '%s'" % (action, ))
|
||||
|
||||
localized_original_datetime = fromzone.localize(dt, is_dst=True)
|
||||
destination_datetime = localized_original_datetime.astimezone(tozone)
|
||||
return destination_datetime.timetuple()
|
||||
|
||||
def format_datetime(self, value, kind="datetime", as_timetuple=False):
|
||||
"""Convert date value to the local datetime considering timezone info.
|
||||
|
||||
@param value: the date value
|
||||
@param kind: type of the date value (date, time or datetime)
|
||||
@param as_timetuple: return timetuple
|
||||
|
||||
@type value: basestring or time.time_tuple)
|
||||
|
||||
@return: string or timetuple
|
||||
"""
|
||||
|
||||
server_format = DT_SERVER_FORMATS[kind]
|
||||
local_format = self.date_format
|
||||
|
||||
if not value:
|
||||
return ''
|
||||
|
||||
if isinstance(value, (time.struct_time, tuple)):
|
||||
value = time.strftime(server_format, value)
|
||||
|
||||
if isinstance(value, DT.datetime):
|
||||
value = value
|
||||
try:
|
||||
value = DT.datetime.strptime(value[:10], server_format)
|
||||
return value.strftime(local_format)
|
||||
except:
|
||||
return ''
|
||||
|
||||
value = value.strip()
|
||||
|
||||
# remove trailing miliseconds
|
||||
value = re.sub("(.*?)(\s+\d{2}:\d{2}:\d{2})(\.\d+)?$", "\g<1>\g<2>", value)
|
||||
|
||||
# add time part in value if missing
|
||||
if kind == 'datetime' and not re.search('\s+\d{2}:\d{2}:\d{2}?$', value):
|
||||
value += ' 00:00:00'
|
||||
|
||||
# remove time part from value
|
||||
elif kind == 'date':
|
||||
value = re.sub('\s+\d{2}:\d{2}:\d{2}(\.\d+)?$', '', value)
|
||||
|
||||
value = time.strptime(value, server_format)
|
||||
|
||||
if kind == "datetime":
|
||||
try:
|
||||
value = self.tz_convert(value, 'format')
|
||||
except Exception, e:
|
||||
print "\n\n\n************ Error in timezone formatting", e
|
||||
|
||||
if as_timetuple:
|
||||
return value
|
||||
|
||||
return time.strftime(local_format, value)
|
||||
|
||||
def get_event_widget(self, event):
|
||||
title = '' # the title
|
||||
description = [] # the description
|
||||
|
||||
if self.info_fields:
|
||||
|
||||
f = self.info_fields[0]
|
||||
s = event[f]
|
||||
|
||||
if isinstance(s, (tuple, list)): s = s[-1]
|
||||
|
||||
title = s
|
||||
for f in self.info_fields[1:]:
|
||||
s = event[f]
|
||||
if isinstance(s, (tuple, list)):
|
||||
s = s[-1]
|
||||
if s:
|
||||
description.append(str(s))
|
||||
|
||||
starts = event.get(self.date_start)
|
||||
ends = event.get(self.date_delay) or 1.0
|
||||
span = 0
|
||||
|
||||
if starts and ends:
|
||||
|
||||
n = 0
|
||||
h = ends
|
||||
|
||||
if ends == self.day_length:
|
||||
span = 1
|
||||
|
||||
elif ends > self.day_length:
|
||||
n = ends / self.day_length
|
||||
h = ends % self.day_length
|
||||
|
||||
n = int(math.floor(n))
|
||||
|
||||
if h > 0:
|
||||
span = n + 1
|
||||
else:
|
||||
span = n
|
||||
ends = time.localtime(time.mktime(starts) + (h * 60 * 60) + (n * 24 * 60 * 60))
|
||||
|
||||
if starts and self.date_stop:
|
||||
|
||||
ends = event.get(self.date_stop)
|
||||
if not ends:
|
||||
ends = time.localtime(time.mktime(starts) + 60 * 60)
|
||||
|
||||
tds = time.mktime(starts)
|
||||
tde = time.mktime(ends)
|
||||
|
||||
if tds >= tde:
|
||||
tde = tds + 60 * 60
|
||||
ends = time.localtime(tde)
|
||||
|
||||
n = (tde - tds) / (60 * 60)
|
||||
|
||||
if n >= self.day_length:
|
||||
span = math.ceil(n / 24)
|
||||
|
||||
starts = self.format_datetime(starts, "datetime", True)
|
||||
ends = self.format_datetime(ends, "datetime", True)
|
||||
title = title.strip()
|
||||
description = ', '.join(description).strip()
|
||||
return {'id': event['id'], 'start_date': str(DT.datetime(*starts[:6])), 'end_date': str(DT.datetime(*ends[:6])), 'text': title, 'title': description, 'color': self.colors[event[self.color_field]][-1]}
|
||||
def load(self, req, model, view_id, toolbar=False):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'calendar', toolbar=toolbar)
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
@@ -7,218 +7,381 @@ 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.View.extend({
|
||||
// Dhtmlx scheduler ?
|
||||
init: function(parent, element_id, dataset, view_id) {
|
||||
init: function(parent, element_id, dataset, view_id, options) {
|
||||
this._super(parent, element_id);
|
||||
this.view_manager = parent || new openerp.base.NullViewManager();
|
||||
this.set_default_options();
|
||||
this.dataset = dataset;
|
||||
this.dataset_index = 0;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
this.fields_view = {};
|
||||
this.widgets = {};
|
||||
this.widgets_counter = 0;
|
||||
this.fields = this.dataset.fields ? this.dataset.fields: {};
|
||||
this.datarecord = {};
|
||||
this.name = "";
|
||||
this.date_start = "";
|
||||
this.date_delay = "";
|
||||
this.date_stop = "";
|
||||
this.color_field = "";
|
||||
this.day_lenth = 8;
|
||||
this.colors = [];
|
||||
this.color_values = [];
|
||||
this.calendar_fields = {};
|
||||
this.info_fields = [];
|
||||
this.domain = this.dataset._domain ? this.dataset._domain: [];
|
||||
this.context = {};
|
||||
},
|
||||
start: function() {
|
||||
this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
|
||||
},
|
||||
on_loaded: function(result) {
|
||||
var self = this;
|
||||
var params = {};
|
||||
this.fields_view = result.fields_view;
|
||||
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
|
||||
this.view_id = this.fields_view.view_id;
|
||||
|
||||
this.date_start = this.fields_view.arch.attrs.date_start;
|
||||
this.date_delay = this.fields_view.arch.attrs.date_delay;
|
||||
this.date_stop = this.fields_view.arch.attrs.date_stop;
|
||||
|
||||
this.colors = this.fields_view.arch.attrs.colors;
|
||||
this.day_length = this.fields_view.arch.attrs.day_length || 8;
|
||||
this.color_field = this.fields_view.arch.attrs.color;
|
||||
this.fields = this.fields_view.fields;
|
||||
|
||||
//* Calendar Fields *
|
||||
this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']};
|
||||
|
||||
if(this.date_delay)
|
||||
this.calendar_fields['date_delay'] = {'name': this.date_delay, 'kind': this.fields[this.date_delay]['type']};
|
||||
|
||||
if(this.date_stop)
|
||||
this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']};
|
||||
|
||||
this.calendar_fields['day_length'] = this.day_length;
|
||||
//* ------- *
|
||||
|
||||
for(var fld=0;fld<this.fields_view.arch.children.length;fld++) {
|
||||
this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
|
||||
}
|
||||
|
||||
|
||||
this.load_scheduler();
|
||||
},
|
||||
|
||||
load_scheduler:function() {
|
||||
var self = this;
|
||||
|
||||
var params = {};
|
||||
|
||||
params['model'] = this.model;
|
||||
params['calendar_fields'] = this.calendar_fields;
|
||||
params['info_fields'] = this.info_fields;
|
||||
params['fields'] = this.fields;
|
||||
params['color_field'] = this.color_field;
|
||||
params['domain'] = this.domain;
|
||||
params['colors'] = this.colors;
|
||||
|
||||
/*
|
||||
* Start dhtmlx Schedular
|
||||
*/
|
||||
scheduler.clearAll();
|
||||
scheduler.config.xml_date="%Y-%m-%d %H:%i";
|
||||
scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
|
||||
|
||||
this.rpc(
|
||||
'/base_calendar/calendarview/schedule_events',
|
||||
params,
|
||||
function(result) {
|
||||
self.schedule_events(result);
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
schedule_events: function(result) {
|
||||
var self = this;
|
||||
var res = result.result;
|
||||
var sidebar = result.sidebar;
|
||||
this.$element.html(QWeb.render("CalendarView", {"view": this, "fields_view": this.fields_view, "sidebar":sidebar, "calendar":this}));
|
||||
|
||||
// Initialize Sceduler
|
||||
scheduler.init('openerp_scheduler',null,"month");
|
||||
scheduler.parse(res,"json");
|
||||
jQuery('#dhx_minical_icon').bind('click', this.mini_calendar);
|
||||
|
||||
|
||||
// To Change Event
|
||||
scheduler.attachEvent(
|
||||
'onEventChanged'
|
||||
,function(event_id, event_object) {
|
||||
self.edit_event(event_id, event_object)
|
||||
});
|
||||
|
||||
/*
|
||||
* Create Sidebar
|
||||
*/
|
||||
jQuery('#calendar-sidebar').append(
|
||||
jQuery('<table>',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'})
|
||||
)
|
||||
for(s in sidebar) {
|
||||
jQuery('#cal-sidebar-option').append(
|
||||
jQuery('<tr>').append(
|
||||
jQuery('<td>').append(
|
||||
jQuery('<div>')
|
||||
.append(
|
||||
jQuery('<input>',
|
||||
{
|
||||
'type': 'checkbox',
|
||||
'id':sidebar[s][0],
|
||||
'value':sidebar[s][0]
|
||||
}).bind('click',function(){
|
||||
self.reload_scheduler()
|
||||
}),
|
||||
sidebar[s][1]
|
||||
)
|
||||
.css('background-color',sidebar[s][sidebar[s].length-1])
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
convert_date_format: function(start_date, end_date) {
|
||||
var params = {};
|
||||
params['start_date'] = start_date.getFullYear() +'-' + start_date.getMonth()+'-' + start_date.getDate()+' '+start_date.getHours()+':'+start_date.getMinutes()+':'+start_date.getSeconds();
|
||||
if(end_date) {
|
||||
params['end_date'] = end_date.getFullYear() +'-' + end_date.getMonth()+'-' + end_date.getDate()+' '+end_date.getHours()+':'+end_date.getMinutes()+':'+end_date.getSeconds();
|
||||
}
|
||||
return params;
|
||||
},
|
||||
|
||||
edit_event: function(evt_id, evt_object) {
|
||||
var dates = this.convert_date_format(evt_object.start_date, evt_object.end_date);
|
||||
this.rpc(
|
||||
'/base_calendar/calendarview/edit_events',
|
||||
{
|
||||
'start_date': dates.start_date,
|
||||
'end_date': dates.end_date,
|
||||
'id': evt_id,
|
||||
'model': this.model,
|
||||
'info_fields': this.info_fields,
|
||||
'fields': this.fields,
|
||||
'calendar_fields': this.calendar_fields
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
mini_calendar: function() {
|
||||
|
||||
if(scheduler.isCalendarVisible()) {
|
||||
scheduler.destroyCalendar();
|
||||
} else {
|
||||
scheduler.renderCalendar({
|
||||
position:"dhx_minical_icon",
|
||||
date:scheduler._date,
|
||||
navigation:true,
|
||||
handler:function(date,calendar){
|
||||
scheduler.setCurrentView(date);
|
||||
scheduler.destroyCalendar()
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
reload_scheduler: function() {
|
||||
// self.color_field
|
||||
console.log('Reload Scheduler>>>')
|
||||
},
|
||||
do_search: function (domains, contexts, groupbys) {
|
||||
this.notification.notify("Searching caldendar");
|
||||
this.domain = this.dataset.domain || [];
|
||||
this.context = this.dataset.context || {};
|
||||
this.has_been_loaded = $.Deferred();
|
||||
this.options = options || {};
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
start: function() {
|
||||
this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id, 'toolbar': true}, this.on_loaded);
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.calendar_fields = {};
|
||||
this.ids = this.dataset.ids;
|
||||
this.color_values = [];
|
||||
this.info_fields = [];
|
||||
|
||||
this.fields_view = data.fields_view;
|
||||
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
|
||||
this.view_id = this.fields_view.view_id;
|
||||
|
||||
this.date_start = this.fields_view.arch.attrs.date_start;
|
||||
this.date_delay = this.fields_view.arch.attrs.date_delay;
|
||||
this.date_stop = this.fields_view.arch.attrs.date_stop;
|
||||
|
||||
this.colors = this.fields_view.arch.attrs.colors;
|
||||
this.day_length = this.fields_view.arch.attrs.day_length || 8;
|
||||
this.color_field = this.fields_view.arch.attrs.color;
|
||||
this.fields = this.fields_view.fields;
|
||||
|
||||
//* Calendar Fields *
|
||||
this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']};
|
||||
|
||||
if (this.date_delay) {
|
||||
this.calendar_fields['date_delay'] = {'name': this.date_delay, 'kind': this.fields[this.date_delay]['type']};
|
||||
}
|
||||
|
||||
if (this.date_stop) {
|
||||
this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']};
|
||||
}
|
||||
|
||||
for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
|
||||
this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
|
||||
}
|
||||
this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
|
||||
|
||||
if (this.options.sidebar && this.options.sidebar_id) {
|
||||
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
|
||||
this.sidebar.start();
|
||||
this.sidebar.navigator = new openerp.base_calendar.SidebarNavigator(this.sidebar, this.sidebar.add_section('navigator', "Navigator"), this);
|
||||
this.sidebar.responsible = new openerp.base_calendar.SidebarResponsible(this.sidebar, this.sidebar.add_section('responsible', "Responsible"), this);
|
||||
this.sidebar.add_toolbar(data.fields_view.toolbar);
|
||||
this.set_common_sidebar_sections(this.sidebar);
|
||||
this.sidebar.do_unfold();
|
||||
this.sidebar.do_fold.add_last(this.resize_scheduler);
|
||||
this.sidebar.do_unfold.add_last(this.resize_scheduler);
|
||||
this.sidebar.do_toggle.add_last(this.resize_scheduler);
|
||||
}
|
||||
|
||||
this.init_scheduler();
|
||||
this.load_scheduler();
|
||||
this.has_been_loaded.resolve();
|
||||
},
|
||||
init_scheduler: function() {
|
||||
var self = this;
|
||||
scheduler.config.api_date = "%Y-%m-%d %H:%M:%S";
|
||||
scheduler.config.details_on_dblclick = true;
|
||||
scheduler.config.details_on_create = true;
|
||||
|
||||
if (this.fields[this.date_start]['type'] == 'time') {
|
||||
scheduler.config.xml_date = "%H:%M:%S";
|
||||
} else {
|
||||
scheduler.config.xml_date = "%Y-%m-%d %H:%M:%S";
|
||||
}
|
||||
|
||||
this.mode = this.mode || 'month';
|
||||
|
||||
scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
|
||||
|
||||
// Initialize Sceduler
|
||||
scheduler.init('openerp_scheduler', null, this.mode);
|
||||
|
||||
// Event Options Click,edit
|
||||
scheduler.attachEvent('onDblClick', this.popup_event);
|
||||
|
||||
scheduler.attachEvent('onEventCreated', function(event_id, e) {
|
||||
//Replace default Lightbox with Popup Form of new Event
|
||||
scheduler.showLightbox = function() {
|
||||
//Delete Newly created Event,Later we reload Scheduler
|
||||
scheduler.deleteEvent(event_id);
|
||||
self.popup_event();
|
||||
}
|
||||
});
|
||||
|
||||
scheduler.attachEvent('onBeforeEventChanged', function(event_obj, native_event, is_new) {
|
||||
var is_event_exist = $.inArray(event_obj.id, self.dataset.ids);
|
||||
if (is_event_exist >= 0 || !is_new) {
|
||||
// try to save Event.
|
||||
var data = {};
|
||||
self.mode = scheduler._mode;
|
||||
var date_format = self.calendar_fields.date_start.kind == 'time' ? 'HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss';
|
||||
data[self.date_start] = event_obj.start_date.toString(date_format);
|
||||
|
||||
if (self.date_stop) {
|
||||
data[self.date_stop] = event_obj.end_date.toString(date_format);
|
||||
}
|
||||
|
||||
if (self.date_delay) {
|
||||
var tds = (event_obj.start_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.start_date.getOrdinalNumber() < 0);
|
||||
var tde = (event_obj.end_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.end_date.getOrdinalNumber() < 0);
|
||||
var n = (tde - tds) / (60 * 60);
|
||||
if (n > self.day_length) {
|
||||
var d = Math.floor(n / 24),
|
||||
h = n % 24;
|
||||
n = d * self.day_length + h;
|
||||
}
|
||||
data[self.date_delay] = n;
|
||||
}
|
||||
self.dataset.write(event_obj.id, data, self.load_scheduler);
|
||||
} else {
|
||||
// new Event.
|
||||
return true;
|
||||
}
|
||||
});
|
||||
scheduler.renderCalendar({
|
||||
container: this.sidebar.navigator.element_id,
|
||||
navigation: true,
|
||||
date: scheduler._date,
|
||||
handler: function(date, calendar) {
|
||||
scheduler.setCurrentView(date, 'day');
|
||||
}
|
||||
});
|
||||
},
|
||||
resize_scheduler: function() {
|
||||
scheduler.setCurrentView(scheduler._date);
|
||||
},
|
||||
load_scheduler: function() {
|
||||
var self = this;
|
||||
this.dataset.read_slice([], 0, false, function(events) {
|
||||
if (self.session.locale_code) {
|
||||
// TODO: replace $LAB
|
||||
$LAB.setOptions({AlwaysPreserveOrder: true}).script([
|
||||
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_' + self.session.locale_code + '.js',
|
||||
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_recurring_' + self.session.locale_code + '.js'
|
||||
]).wait(function() {
|
||||
self.schedule_events(events);
|
||||
});
|
||||
} else {
|
||||
self.schedule_events(events);
|
||||
}
|
||||
});
|
||||
},
|
||||
schedule_events: function(events) {
|
||||
var self = this;
|
||||
scheduler.clearAll();
|
||||
|
||||
//To parse Events we have to convert date Format
|
||||
var res_events = [],
|
||||
sidebar_items = [],
|
||||
sidebar_ids = [];
|
||||
for (var e = 0; e < events.length; e++) {
|
||||
var evt = events[e];
|
||||
if (!evt[this.date_start]) {
|
||||
this.notification.warn("Start date is not defined for event :", evt['id']);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.fields[this.date_start]['type'] == 'date') {
|
||||
evt[this.date_start] = openerp.base.parse_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss');
|
||||
}
|
||||
if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
|
||||
evt[this.date_stop] = openerp.base.parse_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss');
|
||||
}
|
||||
if (this.color_field) {
|
||||
var user = evt[this.color_field];
|
||||
if (user) {
|
||||
if (_.indexOf(sidebar_ids, user[0]) === -1) {
|
||||
sidebar_items.push({
|
||||
id: user[0],
|
||||
name: user[1],
|
||||
// TODO: use color table
|
||||
color: '#dddddd'
|
||||
});
|
||||
sidebar_ids.push(user[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
res_events.push(this.convert_event(evt));
|
||||
}
|
||||
|
||||
scheduler.parse(res_events, 'json');
|
||||
this.resize_scheduler();
|
||||
this.sidebar.responsible.on_events_loaded(sidebar_items);
|
||||
},
|
||||
convert_event: function(event) {
|
||||
var starts = event[this.date_start],
|
||||
ends = event[this.date_delay] || 1,
|
||||
span = 0,
|
||||
res_text = '',
|
||||
res_description = [];
|
||||
|
||||
var parse_start_date = openerp.base.parse_datetime(starts);
|
||||
if (event[this.date_stop]) {
|
||||
var parse_end_date = openerp.base.parse_datetime(event[this.date_stop]);
|
||||
}
|
||||
if (this.info_fields) {
|
||||
var fld = event[this.info_fields[0]];
|
||||
|
||||
if (typeof fld == 'object') {
|
||||
res_text = fld[fld.length -1];
|
||||
} else {
|
||||
res_text = fld;
|
||||
}
|
||||
|
||||
var sliced_info_fields = this.info_fields.slice(1);
|
||||
for (sl_fld in sliced_info_fields) {
|
||||
var slc_fld = event[sliced_info_fields[sl_fld]];
|
||||
|
||||
if (typeof slc_fld == 'object') {
|
||||
res_description.push(slc_fld[slc_fld.length - 1]);
|
||||
} else {
|
||||
if(slc_fld) res_description.push(slc_fld);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (starts && ends) {
|
||||
var n = 0,
|
||||
h = ends;
|
||||
if (ends == this.day_length) {
|
||||
span = 1;
|
||||
} else if (ends > this.day_length) {
|
||||
n = ends / this.day_length;
|
||||
h = ends % this.day_length;
|
||||
n = parseInt(Math.floor(n));
|
||||
|
||||
if (h > 0) {
|
||||
span = n + 1;
|
||||
} else {
|
||||
span = n;
|
||||
}
|
||||
}
|
||||
var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60)));
|
||||
ends = parse_start_date;
|
||||
}
|
||||
|
||||
if (starts && this.date_stop) {
|
||||
ends = parse_end_date;
|
||||
if (event[this.date_stop] == undefined) {
|
||||
var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60)));
|
||||
ends = parse_start_date;
|
||||
}
|
||||
var tds = parse_start_date.getTime(),
|
||||
tde = parse_end_date.getTime();
|
||||
|
||||
if (tds >= tde) {
|
||||
tde = tds + 60 * 60;
|
||||
parse_end_date.setTime(tde);
|
||||
ends = parse_end_date;
|
||||
}
|
||||
n = (tde - tds) / (60 * 60);
|
||||
if (n >= this.day_length) {
|
||||
span = Math.ceil(n / 24);
|
||||
}
|
||||
}
|
||||
return {
|
||||
'start_date': parse_start_date.toString('yyyy-MM-dd HH:mm:ss'),
|
||||
'end_date': ends.toString('yyyy-MM-dd HH:mm:ss'),
|
||||
'text': res_text,
|
||||
'id': event['id'],
|
||||
'title': res_description.join()
|
||||
}
|
||||
},
|
||||
do_search: function(domains, contexts, groupbys) {
|
||||
var self = this;
|
||||
this.rpc('/base/session/eval_domain_and_context', {
|
||||
domains: domains,
|
||||
contexts: contexts,
|
||||
group_by_seq: groupbys
|
||||
}, function (results) {
|
||||
// TODO: handle non-empty results.group_by with read_group
|
||||
self.dataset.context = self.context = results.context;
|
||||
self.dataset.domain = self.domain = results.domain;
|
||||
self.dataset.read_slice(_.keys(self.fields), 0, self.limit, function(events) {
|
||||
self.schedule_events(events);
|
||||
});
|
||||
});
|
||||
},
|
||||
do_show: function () {
|
||||
var self = this;
|
||||
$.when(this.has_been_loaded).then(function() {
|
||||
self.$element.show();
|
||||
if (self.sidebar) {
|
||||
self.sidebar.$element.show();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
if (this.sidebar) {
|
||||
this.sidebar.$element.hide();
|
||||
}
|
||||
},
|
||||
popup_event: function(event_id) {
|
||||
var self = this;
|
||||
if (event_id) event_id = parseInt(event_id, 10);
|
||||
var action = {
|
||||
res_model: this.dataset.model,
|
||||
res_id: event_id,
|
||||
views: [[false, 'form']],
|
||||
type: 'ir.actions.act_window',
|
||||
view_type: 'form',
|
||||
view_mode: 'form',
|
||||
flags : {
|
||||
search_view: false,
|
||||
sidebar : false,
|
||||
views_switcher : false,
|
||||
action_buttons : false,
|
||||
pager: false
|
||||
}
|
||||
}
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
var dialog = $('<div>', {
|
||||
'id': element_id
|
||||
}).dialog({
|
||||
modal: true,
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
$(this).dialog("destroy");
|
||||
},
|
||||
Save: function() {
|
||||
var view_manager = action_manager.viewmanager;
|
||||
var _dialog = this;
|
||||
view_manager.views[view_manager.active_view].controller.do_save(function(r) {
|
||||
$(_dialog).dialog("destroy");
|
||||
// self.start();
|
||||
self.load_scheduler();
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
var action_manager = new openerp.base.ActionManager(this, element_id);
|
||||
action_manager.start();
|
||||
action_manager.do_action(action);
|
||||
//Default_get
|
||||
if (!event_id) {
|
||||
this.dataset.index = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//openerp.base.Action = openerp.base.Action.extend({
|
||||
// do_action_window: function(action) {
|
||||
// this._super.apply(this,arguments);
|
||||
// for(var i = 0; i < action.views.length; i++) {
|
||||
// if(action.views[i][1] == "calendar") {
|
||||
// this.calendar_id = action.views[i][0];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// // IF there is a view calender
|
||||
// // if(this.calendar_id
|
||||
// },
|
||||
//});
|
||||
openerp.base_calendar.SidebarResponsible = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, view) {
|
||||
this._super(parent, element_id);
|
||||
this.view = view;
|
||||
},
|
||||
on_events_loaded: function(users) {
|
||||
this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { users : users }));
|
||||
// TODO: bind checkboxes reload sheduler
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base_calendar.SidebarNavigator = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, view) {
|
||||
this._super(parent, element_id);
|
||||
this.view = view;
|
||||
},
|
||||
on_events_loaded: function(events) {
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
<template>
|
||||
<t t-name="CalendarView">
|
||||
<h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
|
||||
<table class="calendar-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td>
|
||||
<div style="height: 1000px;width: 100%;">
|
||||
<div id="calendar-sidebar">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:85%;" align="left">
|
||||
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 1000px;width: 100%;">
|
||||
<div class="dhx_cal_navline">
|
||||
<div class="dhx_cal_prev_button"/>
|
||||
<div class="dhx_cal_next_button"/>
|
||||
<div class="dhx_cal_today_button"/>
|
||||
<div class="dhx_cal_date"/>
|
||||
<div class="dhx_minical_icon" id="dhx_minical_icon"/>
|
||||
<div class="dhx_cal_tab" name="day_tab" style="right:204px;"/>
|
||||
<div class="dhx_cal_tab" name="week_tab" style="right:140px;"/>
|
||||
<div class="dhx_cal_tab" name="month_tab" style="right:76px;"/>
|
||||
</div>
|
||||
<div class="dhx_cal_header">
|
||||
</div>
|
||||
<div class="dhx_cal_data">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-name="CalendarView">
|
||||
<h3 class="title"><t t-esc="fields_view.arch.attrs.string"/></h3>
|
||||
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 1000px;">
|
||||
<div class="dhx_cal_navline">
|
||||
<div class="dhx_cal_prev_button"/>
|
||||
<div class="dhx_cal_next_button"/>
|
||||
<div class="dhx_cal_today_button"/>
|
||||
<div class="dhx_cal_date"/>
|
||||
<div class="dhx_cal_tab" name="day_tab" style="right:204px;"/>
|
||||
<div class="dhx_cal_tab" name="week_tab" style="right:140px;"/>
|
||||
<div class="dhx_cal_tab" name="month_tab" style="right:76px;"/>
|
||||
</div>
|
||||
<div class="dhx_cal_header">
|
||||
</div>
|
||||
<div class="dhx_cal_data">
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="CalendarView.sidebar.responsible">
|
||||
<div t-foreach="users" t-as="user" class="oe_calendar_responsible" t-attf-style="background: #{user.color}">
|
||||
<input type="checkbox" name="selection" t-att-value="user.id"/>
|
||||
<span><t t-esc="user.name"/></span>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
openerp.web_mobile.chrome_mobile = function(openerp) {
|
||||
openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({
|
||||
openerp.web_mobile.Shortcuts = openerp.base.Widget.extend({
|
||||
init: function(session, element_id) {
|
||||
this._super(session, element_id);
|
||||
},
|
||||
@@ -19,7 +19,7 @@ openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({
|
||||
this.listview.start();
|
||||
}
|
||||
});
|
||||
openerp.web_mobile.Header = openerp.base.Controller.extend({
|
||||
openerp.web_mobile.Header = openerp.base.Widget.extend({
|
||||
init: function(session, element_id) {
|
||||
this._super(session, element_id);
|
||||
},
|
||||
@@ -36,7 +36,7 @@ openerp.web_mobile.Header = openerp.base.Controller.extend({
|
||||
}
|
||||
}
|
||||
});
|
||||
openerp.web_mobile.Secondary = openerp.base.Controller.extend({
|
||||
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;
|
||||
@@ -74,7 +74,7 @@ openerp.web_mobile.Secondary = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web_mobile.Menu = openerp.base.Controller.extend({
|
||||
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;
|
||||
@@ -106,7 +106,7 @@ openerp.web_mobile.Menu = openerp.base.Controller.extend({
|
||||
this.secondary.start();
|
||||
}
|
||||
});
|
||||
openerp.web_mobile.Options = openerp.base.Controller.extend({
|
||||
openerp.web_mobile.Options = openerp.base.Widget.extend({
|
||||
init: function(session, element_id) {
|
||||
this._super(session, element_id);
|
||||
},
|
||||
@@ -121,7 +121,7 @@ openerp.web_mobile.Options = openerp.base.Controller.extend({
|
||||
this.login.start();
|
||||
}
|
||||
});
|
||||
openerp.web_mobile.Login = openerp.base.Controller.extend({
|
||||
openerp.web_mobile.Login = openerp.base.Widget.extend({
|
||||
init: function(session, element_id) {
|
||||
this._super(session, element_id);
|
||||
},
|
||||
@@ -190,7 +190,7 @@ openerp.web_mobile.Login = openerp.base.Controller.extend({
|
||||
});
|
||||
}
|
||||
});
|
||||
openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({
|
||||
openerp.web_mobile.MobileWebClient = openerp.base.Widget.extend({
|
||||
init: function(element_id) {
|
||||
var self = this;
|
||||
this._super(null, element_id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
openerp.web_mobile.form_mobile = function (openerp) {
|
||||
openerp.web_mobile.FormView = openerp.base.Controller.extend({
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
openerp.web_mobile.list_mobile = function (openerp) {
|
||||
openerp.web_mobile.ListView = openerp.base.Controller.extend({
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user