[MERGE]latest trunk-proto61.
bzr revid: kch@tinyerp.com-20110525055220-efvm850pbkjalouc
This commit is contained in:
+112
-109
@@ -439,9 +439,12 @@ class DataSet(openerpweb.Controller):
|
||||
class DataGroup(openerpweb.Controller):
|
||||
_cp_path = "/base/group"
|
||||
@openerpweb.jsonrequest
|
||||
def read(self, request, model, group_by_fields, domain=None, context=None):
|
||||
def read(self, request, model, group_by_fields, domain=None):
|
||||
Model = request.session.model(model)
|
||||
return Model.read_group(domain or False, False, group_by_fields, 0, False, context or False)
|
||||
|
||||
return Model.read_group(
|
||||
domain or [], False, group_by_fields, 0, False,
|
||||
dict(request.context, group_by=group_by_fields))
|
||||
|
||||
class View(openerpweb.Controller):
|
||||
def fields_view_get(self, request, model, view_id, view_type,
|
||||
@@ -555,6 +558,113 @@ class FormView(View):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'form', toolbar=toolbar)
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
class ListView(View):
|
||||
_cp_path = "/base/listview"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id, toolbar=False):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar)
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
def fields_view_get(self, request, model, view_id, view_type="tree",
|
||||
transform=True, toolbar=False, submenu=False):
|
||||
""" Sets @editable on the view's arch if it isn't already set and
|
||||
``set_editable`` is present in the request context
|
||||
"""
|
||||
view = super(ListView, self).fields_view_get(
|
||||
request, model, view_id, view_type, transform, toolbar, submenu)
|
||||
|
||||
view_attributes = view['arch']['attrs']
|
||||
if request.context.get('set_editable')\
|
||||
and 'editable' not in view_attributes:
|
||||
view_attributes['editable'] = 'bottom'
|
||||
return view
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def fill(self, request, model, id, domain,
|
||||
offset=0, limit=False, sort=None):
|
||||
return self.do_fill(request, model, id, domain, offset, limit, sort)
|
||||
|
||||
def do_fill(self, request, model, id, domain,
|
||||
offset=0, limit=False, sort=None):
|
||||
""" Returns all information needed to fill a table:
|
||||
|
||||
* view with processed ``editable`` flag
|
||||
* fields (columns) with processed ``invisible`` flag
|
||||
* rows with processed ``attrs`` and ``colors``
|
||||
|
||||
.. note:: context is passed through ``request`` parameter
|
||||
|
||||
:param request: OpenERP request
|
||||
:type request: openerpweb.openerpweb.JsonRequest
|
||||
:type str model: OpenERP model for this list view
|
||||
:type int id: view_id, or False if none provided
|
||||
:param list domain: the search domain to search for
|
||||
:param int offset: search offset, for pagination
|
||||
:param int limit: search limit, for pagination
|
||||
:returns: hell if I have any idea yet
|
||||
"""
|
||||
view = self.fields_view_get(request, model, id, toolbar=True)
|
||||
|
||||
rows = DataSet().do_search_read(request, model,
|
||||
offset=offset, limit=limit,
|
||||
domain=domain, sort=sort)
|
||||
eval_context = request.session.evaluation_context(
|
||||
request.context)
|
||||
|
||||
if sort:
|
||||
sort_criteria = sort.split(',')[0].split(' ')
|
||||
view['sorted'] = {
|
||||
'field': sort_criteria[0],
|
||||
'reversed': sort_criteria[1] == 'DESC'
|
||||
}
|
||||
else:
|
||||
view['sorted'] = {}
|
||||
return {
|
||||
'view': view,
|
||||
'records': [
|
||||
{'data': dict((key, {'value': value})
|
||||
for key, value in row.iteritems()),
|
||||
'color': self.process_colors(view, row, eval_context)}
|
||||
for row in rows
|
||||
]
|
||||
}
|
||||
|
||||
def process_colors(self, view, row, context):
|
||||
colors = view['arch']['attrs'].get('colors')
|
||||
|
||||
if not colors:
|
||||
return None
|
||||
|
||||
color = [
|
||||
pair.split(':')[0]
|
||||
for pair in colors.split(';')
|
||||
if eval(pair.split(':')[1], dict(context, **row))
|
||||
]
|
||||
|
||||
if not color:
|
||||
return None
|
||||
elif len(color) == 1:
|
||||
return color[0]
|
||||
return 'maroon'
|
||||
|
||||
class SearchView(View):
|
||||
_cp_path = "/base/searchview"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'search')
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def fields_get(self, req, model):
|
||||
Model = req.session.model(model)
|
||||
fields = Model.fields_get()
|
||||
return {'fields': fields}
|
||||
|
||||
class Binary(openerpweb.Controller):
|
||||
_cp_path = "/base/binary"
|
||||
|
||||
@openerpweb.httprequest
|
||||
def image(self, request, session_id, model, id, field, **kw):
|
||||
cherrypy.response.headers['Content-Type'] = 'image/png'
|
||||
@@ -612,113 +722,6 @@ class FormView(View):
|
||||
args = [False, e.message]
|
||||
return out % (simplejson.dumps(callback), simplejson.dumps(args))
|
||||
|
||||
class ListView(View):
|
||||
_cp_path = "/base/listview"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id, toolbar=False):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar)
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
def fields_view_get(self, request, model, view_id, view_type="tree",
|
||||
transform=True, toolbar=False, submenu=False):
|
||||
""" Sets @editable on the view's arch if it isn't already set and
|
||||
``set_editable`` is present in the request context
|
||||
"""
|
||||
view = super(ListView, self).fields_view_get(
|
||||
request, model, view_id, view_type, transform, toolbar, submenu)
|
||||
|
||||
view_attributes = view['arch']['attrs']
|
||||
if request.context.get('set_editable')\
|
||||
and 'editable' not in view_attributes:
|
||||
view_attributes['editable'] = 'bottom'
|
||||
return view
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def fill(self, request, model, id, domain,
|
||||
offset=0, limit=False, sort=None):
|
||||
return self.do_fill(request, model, id, domain, offset, limit, sort)
|
||||
|
||||
def do_fill(self, request, model, id, domain,
|
||||
offset=0, limit=False, sort=None):
|
||||
""" Returns all information needed to fill a table:
|
||||
|
||||
* view with processed ``editable`` flag
|
||||
* fields (columns) with processed ``invisible`` flag
|
||||
* rows with processed ``attrs`` and ``colors``
|
||||
|
||||
.. note:: context is passed through ``request`` parameter
|
||||
|
||||
:param request: OpenERP request
|
||||
:type request: openerpweb.openerpweb.JsonRequest
|
||||
:type str model: OpenERP model for this list view
|
||||
:type int id: view_id, or False if none provided
|
||||
:param list domain: the search domain to search for
|
||||
:param int offset: search offset, for pagination
|
||||
:param int limit: search limit, for pagination
|
||||
:returns: hell if I have any idea yet
|
||||
"""
|
||||
view = self.fields_view_get(request, model, id, toolbar=True)
|
||||
|
||||
print sort
|
||||
rows = DataSet().do_search_read(request, model,
|
||||
offset=offset, limit=limit,
|
||||
domain=domain, sort=sort)
|
||||
eval_context = request.session.evaluation_context(
|
||||
request.context)
|
||||
|
||||
if sort:
|
||||
sort_criteria = sort.split(',')[0].split(' ')
|
||||
print sort, sort_criteria
|
||||
view['sorted'] = {
|
||||
'field': sort_criteria[0],
|
||||
'reversed': sort_criteria[1] == 'DESC'
|
||||
}
|
||||
else:
|
||||
view['sorted'] = {}
|
||||
return {
|
||||
'view': view,
|
||||
'records': [
|
||||
{'data': dict((key, {'value': value})
|
||||
for key, value in row.iteritems()),
|
||||
'color': self.process_colors(view, row, eval_context)}
|
||||
for row in rows
|
||||
]
|
||||
}
|
||||
|
||||
def process_colors(self, view, row, context):
|
||||
colors = view['arch']['attrs'].get('colors')
|
||||
|
||||
if not colors:
|
||||
return None
|
||||
|
||||
color = [
|
||||
pair.split(':')[0]
|
||||
for pair in colors.split(';')
|
||||
if eval(pair.split(':')[1], dict(context, **row))
|
||||
]
|
||||
|
||||
if not color:
|
||||
return None
|
||||
elif len(color) == 1:
|
||||
return color[0]
|
||||
return 'maroon'
|
||||
|
||||
|
||||
class SearchView(View):
|
||||
_cp_path = "/base/searchview"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'search')
|
||||
return {'fields_view': fields_view}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def fields_get(self, req, model):
|
||||
Model = req.session.model(model)
|
||||
fields = Model.fields_get()
|
||||
return {'fields': fields}
|
||||
|
||||
class Action(openerpweb.Controller):
|
||||
_cp_path = "/base/action"
|
||||
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
// Underscore.string
|
||||
// (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
|
||||
// Underscore.strings is freely distributable under the terms of the MIT license.
|
||||
// Documentation: https://github.com/edtsech/underscore.string
|
||||
// Some code is borrowed from MooTools and Alexandru Marasteanu.
|
||||
|
||||
// Version 1.1.4
|
||||
|
||||
(function(){
|
||||
// ------------------------- Baseline setup ---------------------------------
|
||||
|
||||
// Establish the root object, "window" in the browser, or "global" on the server.
|
||||
var root = this;
|
||||
|
||||
var nativeTrim = String.prototype.trim;
|
||||
|
||||
function str_repeat(i, m) {
|
||||
for (var o = []; m > 0; o[--m] = i);
|
||||
return o.join('');
|
||||
}
|
||||
|
||||
function defaultToWhiteSpace(characters){
|
||||
if (characters) {
|
||||
return _s.escapeRegExp(characters);
|
||||
}
|
||||
return '\\s';
|
||||
}
|
||||
|
||||
var _s = {
|
||||
|
||||
isBlank: function(str){
|
||||
return !!str.match(/^\s*$/);
|
||||
},
|
||||
|
||||
capitalize : function(str) {
|
||||
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
|
||||
},
|
||||
|
||||
chop: function(str, step){
|
||||
step = step || str.length;
|
||||
var arr = [];
|
||||
for (var i = 0; i < str.length;) {
|
||||
arr.push(str.slice(i,i + step));
|
||||
i = i + step;
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
clean: function(str){
|
||||
return _s.strip(str.replace(/\s+/g, ' '));
|
||||
},
|
||||
|
||||
count: function(str, substr){
|
||||
var count = 0, index;
|
||||
for (var i=0; i < str.length;) {
|
||||
index = str.indexOf(substr, i);
|
||||
index >= 0 && count++;
|
||||
i = i + (index >= 0 ? index : 0) + substr.length;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
|
||||
chars: function(str) {
|
||||
return str.split('');
|
||||
},
|
||||
|
||||
escapeHTML: function(str) {
|
||||
return String(str||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
},
|
||||
|
||||
unescapeHTML: function(str) {
|
||||
return String(str||'').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
},
|
||||
|
||||
escapeRegExp: function(str){
|
||||
// From MooTools core 1.2.4
|
||||
return String(str||'').replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||
},
|
||||
|
||||
insert: function(str, i, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(i, 0, substr);
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
includes: function(str, needle){
|
||||
return str.indexOf(needle) !== -1;
|
||||
},
|
||||
|
||||
join: function(sep) {
|
||||
// TODO: Could this be faster by converting
|
||||
// arguments to Array and using array.join(sep)?
|
||||
sep = String(sep);
|
||||
var str = "";
|
||||
for (var i=1; i < arguments.length; i += 1) {
|
||||
str += String(arguments[i]);
|
||||
if ( i !== arguments.length-1 ) {
|
||||
str += sep;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
lines: function(str) {
|
||||
return str.split("\n");
|
||||
},
|
||||
|
||||
// reverse: function(str){
|
||||
// return Array.prototype.reverse.apply(str.split('')).join('');
|
||||
// },
|
||||
|
||||
splice: function(str, i, howmany, substr){
|
||||
var arr = str.split('');
|
||||
arr.splice(i, howmany, substr);
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
startsWith: function(str, starts){
|
||||
return str.length >= starts.length && str.substring(0, starts.length) === starts;
|
||||
},
|
||||
|
||||
endsWith: function(str, ends){
|
||||
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
|
||||
},
|
||||
|
||||
succ: function(str){
|
||||
var arr = str.split('');
|
||||
arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
titleize: function(str){
|
||||
var arr = str.split(' '),
|
||||
word;
|
||||
for (var i=0; i < arr.length; i++) {
|
||||
word = arr[i].split('');
|
||||
if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
|
||||
i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
|
||||
}
|
||||
return arr.join('');
|
||||
},
|
||||
|
||||
camelize: function(str){
|
||||
return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
},
|
||||
|
||||
underscored: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
|
||||
},
|
||||
|
||||
dasherize: function(str){
|
||||
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
|
||||
},
|
||||
|
||||
trim: function(str, characters){
|
||||
if (!characters && nativeTrim) {
|
||||
return nativeTrim.call(str);
|
||||
}
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
ltrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
|
||||
},
|
||||
|
||||
rtrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
truncate: function(str, length, truncateStr){
|
||||
truncateStr = truncateStr || '...';
|
||||
return str.slice(0,length) + truncateStr;
|
||||
},
|
||||
|
||||
words: function(str, delimiter) {
|
||||
delimiter = delimiter || " ";
|
||||
return str.split(delimiter);
|
||||
},
|
||||
|
||||
|
||||
pad: function(str, length, padStr, type) {
|
||||
|
||||
var padding = '';
|
||||
var padlen = 0;
|
||||
|
||||
if (!padStr) { padStr = ' '; }
|
||||
else if (padStr.length > 1) { padStr = padStr[0]; }
|
||||
switch(type) {
|
||||
case "right":
|
||||
padlen = (length - str.length);
|
||||
padding = str_repeat(padStr, padlen);
|
||||
str = str+padding;
|
||||
break;
|
||||
case "both":
|
||||
padlen = (length - str.length);
|
||||
padding = {
|
||||
'left' : str_repeat(padStr, Math.ceil(padlen/2)),
|
||||
'right': str_repeat(padStr, Math.floor(padlen/2))
|
||||
};
|
||||
str = padding.left+str+padding.right;
|
||||
break;
|
||||
default: // "left"
|
||||
padlen = (length - str.length);
|
||||
padding = str_repeat(padStr, padlen);;
|
||||
str = padding+str;
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
lpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr);
|
||||
},
|
||||
|
||||
rpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'right');
|
||||
},
|
||||
|
||||
lrpad: function(str, length, padStr) {
|
||||
return _s.pad(str, length, padStr, 'both');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Credits for this function goes to
|
||||
* http://www.diveintojavascript.com/projects/sprintf-for-javascript
|
||||
*
|
||||
* Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
|
||||
* All rights reserved.
|
||||
* */
|
||||
sprintf: function(){
|
||||
|
||||
var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
|
||||
while (f) {
|
||||
if (m = /^[^\x25]+/.exec(f)) {
|
||||
o.push(m[0]);
|
||||
}
|
||||
else if (m = /^\x25{2}/.exec(f)) {
|
||||
o.push('%');
|
||||
}
|
||||
else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
|
||||
if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
|
||||
throw('Too few arguments.');
|
||||
}
|
||||
if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
|
||||
throw('Expecting number but found ' + typeof(a));
|
||||
}
|
||||
switch (m[7]) {
|
||||
case 'b': a = a.toString(2); break;
|
||||
case 'c': a = String.fromCharCode(a); break;
|
||||
case 'd': a = parseInt(a); break;
|
||||
case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
|
||||
case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
|
||||
case 'o': a = a.toString(8); break;
|
||||
case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
|
||||
case 'u': a = Math.abs(a); break;
|
||||
case 'x': a = a.toString(16); break;
|
||||
case 'X': a = a.toString(16).toUpperCase(); break;
|
||||
}
|
||||
a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
|
||||
c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
|
||||
x = m[5] - String(a).length - s.length;
|
||||
p = m[5] ? str_repeat(c, x) : '';
|
||||
o.push(s + (m[4] ? a + p : p + a));
|
||||
}
|
||||
else {
|
||||
throw('Huh ?!');
|
||||
}
|
||||
f = f.substring(m[0].length);
|
||||
}
|
||||
return o.join('');
|
||||
}
|
||||
}
|
||||
|
||||
// Aliases
|
||||
|
||||
_s.strip = _s.trim;
|
||||
_s.lstrip = _s.ltrim;
|
||||
_s.rstrip = _s.rtrim;
|
||||
_s.center = _s.lrpad
|
||||
_s.ljust = _s.lpad
|
||||
_s.rjust = _s.rpad
|
||||
|
||||
// CommonJS module is defined
|
||||
if (typeof window === 'undefined' && typeof module !== 'undefined') {
|
||||
// Export module
|
||||
module.exports = _s;
|
||||
|
||||
// Integrate with Underscore.js
|
||||
} else if (typeof root._ !== 'undefined') {
|
||||
root._.mixin(_s);
|
||||
|
||||
// Or define it
|
||||
} else {
|
||||
root._ = _s;
|
||||
}
|
||||
|
||||
}());
|
||||
@@ -1,161 +0,0 @@
|
||||
// Underscore.strings
|
||||
// (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
|
||||
// Underscore.strings is freely distributable under the terms of the MIT license.
|
||||
// Documentation: http://bitbucket.org/epeli/underscore.strings/
|
||||
// Some code is borrowed from MooTools and Alexandru Marasteanu.
|
||||
|
||||
// Version 1.0
|
||||
|
||||
|
||||
(function(){
|
||||
// ------------------------- Baseline setup ---------------------------------
|
||||
|
||||
// Establish the root object, "window" in the browser, or "global" on the server.
|
||||
var root = this;
|
||||
|
||||
var nativeTrim = String.prototype.trim;
|
||||
|
||||
function str_repeat(i, m) {
|
||||
for (var o = []; m > 0; o[--m] = i);
|
||||
return o.join('');
|
||||
}
|
||||
|
||||
function defaultToWhiteSpace(characters){
|
||||
if (characters) {
|
||||
return _s.escapeRegExp(characters);
|
||||
}
|
||||
return '\\s';
|
||||
}
|
||||
|
||||
var _s;
|
||||
_s = root._s = {
|
||||
|
||||
capitalize : function(str) {
|
||||
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
|
||||
},
|
||||
|
||||
join: function(sep) {
|
||||
// TODO: Could this be faster by converting
|
||||
// arguments to Array and using array.join(sep)?
|
||||
sep = String(sep);
|
||||
var str = "";
|
||||
for (var i=1; i < arguments.length; i += 1) {
|
||||
str += String(arguments[i]);
|
||||
if ( i !== arguments.length-1 ) {
|
||||
str += sep;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
escapeRegExp: function(str){
|
||||
// From MooTools core 1.2.4
|
||||
return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||
},
|
||||
|
||||
reverse: function(str){
|
||||
return Array.prototype.reverse.apply(str.split('')).join('');
|
||||
},
|
||||
|
||||
contains: function(str, needle){
|
||||
return str.indexOf(needle) !== -1;
|
||||
},
|
||||
|
||||
clean: function(str){
|
||||
return _s.strip(str.replace(/\s+/g, ' '));
|
||||
},
|
||||
|
||||
trim: function(str, characters){
|
||||
if (!characters && nativeTrim) {
|
||||
return nativeTrim.call(str);
|
||||
}
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
|
||||
|
||||
ltrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
|
||||
},
|
||||
|
||||
rtrim: function(str, characters){
|
||||
characters = defaultToWhiteSpace(characters);
|
||||
return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
|
||||
},
|
||||
|
||||
|
||||
startsWith: function(str, starts){
|
||||
return str.length >= starts.length && str.substring(0, starts.length) === starts;
|
||||
},
|
||||
|
||||
|
||||
endsWith: function(str, ends){
|
||||
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Credits for this function goes to
|
||||
* http://www.diveintojavascript.com/projects/sprintf-for-javascript
|
||||
*
|
||||
* Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
|
||||
* All rights reserved.
|
||||
* */
|
||||
sprintf: function(){
|
||||
|
||||
var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
|
||||
while (f) {
|
||||
if (m = /^[^\x25]+/.exec(f)) {
|
||||
o.push(m[0]);
|
||||
}
|
||||
else if (m = /^\x25{2}/.exec(f)) {
|
||||
o.push('%');
|
||||
}
|
||||
else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
|
||||
if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
|
||||
throw('Too few arguments.');
|
||||
}
|
||||
if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
|
||||
throw('Expecting number but found ' + typeof(a));
|
||||
}
|
||||
switch (m[7]) {
|
||||
case 'b': a = a.toString(2); break;
|
||||
case 'c': a = String.fromCharCode(a); break;
|
||||
case 'd': a = parseInt(a); break;
|
||||
case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
|
||||
case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
|
||||
case 'o': a = a.toString(8); break;
|
||||
case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
|
||||
case 'u': a = Math.abs(a); break;
|
||||
case 'x': a = a.toString(16); break;
|
||||
case 'X': a = a.toString(16).toUpperCase(); break;
|
||||
}
|
||||
a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
|
||||
c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
|
||||
x = m[5] - String(a).length - s.length;
|
||||
p = m[5] ? str_repeat(c, x) : '';
|
||||
o.push(s + (m[4] ? a + p : p + a));
|
||||
}
|
||||
else {
|
||||
throw('Huh ?!');
|
||||
}
|
||||
f = f.substring(m[0].length);
|
||||
}
|
||||
return o.join('');
|
||||
}
|
||||
}
|
||||
|
||||
// Some aliases
|
||||
root._s.strip = _s.trim;
|
||||
root._s.lstrip = _s.ltrim;
|
||||
root._s.rstrip = _s.rtrim;
|
||||
|
||||
|
||||
// Integrate with Underscore.js
|
||||
if (root._) {
|
||||
root._.mixin(root._s);
|
||||
}
|
||||
|
||||
}());
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/underscore/underscore.strings.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/underscore/underscore.string.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/qweb/qweb2.js"></script>
|
||||
<!-- Uncomment in order to use legacy QWeb
|
||||
<script type="text/javascript" src="/base/static/lib/qweb/qweb.js"></script>
|
||||
@@ -20,7 +20,7 @@
|
||||
<script type="text/javascript" src="/base/static/lib/jquery.superfish/js/superfish.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/datejs/date-en-US.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/json/json2.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/json/json2.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/base.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
|
||||
@@ -28,8 +28,9 @@
|
||||
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/form.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/list.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/tree.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/search.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/m2o.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/m2o.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css" />
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui.notify/css/ui.notify.css" />
|
||||
@@ -51,5 +52,3 @@
|
||||
<body id="oe" class="openerp">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
@@ -95,42 +95,64 @@ body.openerp {
|
||||
|
||||
.openerp .menu {
|
||||
height: 34px;
|
||||
background: url(../img/menu-bg.png) repeat-x;
|
||||
background: #cc4e45; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #cc4e45 0%, #b52d20 8%, #7a211a 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#cc4e45), color-stop(8%,#b52d20), color-stop(100%,#7a211a)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #cc4e45 0%,#b52d20 8%,#7a211a 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #cc4e45 0%,#b52d20 8%,#7a211a 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #cc4e45 0%,#b52d20 8%,#7a211a 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#CC4E45', endColorstr='#7A211A',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #cc4e45 0%,#b52d20 8%,#7a211a 100%); /* W3C */
|
||||
}
|
||||
.openerp .menu td {
|
||||
text-align: center;
|
||||
padding:0;
|
||||
}
|
||||
.openerp .menu a {
|
||||
display: block;
|
||||
padding: 0 0 0 3px;
|
||||
background: url(../img/menu-item.png) no-repeat;
|
||||
display:block;
|
||||
min-width: 60px;
|
||||
height: 20px;
|
||||
margin: 3px 2px;
|
||||
padding: 0 8px;
|
||||
|
||||
background: #bd5e54; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #bd5e54 0%, #90322a 60%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#bd5e54), color-stop(60%,#90322a)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #bd5e54 0%,#90322a 60%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #bd5e54 0%,#90322a 60%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #bd5e54 0%,#90322a 60%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#BD5E54', endColorstr='#90322A',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #bd5e54 0%,#90322a 60%); /* W3C */
|
||||
|
||||
border: 1px solid #6E2A24;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
|
||||
color: #eee;
|
||||
text-shadow: #222 0 1px 0;
|
||||
text-decoration: none;
|
||||
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
margin: 2px 1px;
|
||||
height: 23px;
|
||||
}
|
||||
.openerp .menu span {
|
||||
display: block;
|
||||
min-width: 60px;
|
||||
height: 23px;
|
||||
padding: 0 9px 0 6px;
|
||||
background: url(../img/menu-item.png) 100% 0 no-repeat;
|
||||
line-height: 23px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 85%;
|
||||
}
|
||||
.openerp .menu a:hover,
|
||||
.openerp .menu a:focus,
|
||||
.openerp .menu a.active {
|
||||
background-position: 0 -23px;
|
||||
background: #c6c6c6; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #c6c6c6 0%, #5c5c5c 7%, #969595 86%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c6c6c6), color-stop(7%,#5c5c5c), color-stop(86%,#969595)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #c6c6c6 0%,#5c5c5c 7%,#969595 86%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #c6c6c6 0%,#5c5c5c 7%,#969595 86%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #c6c6c6 0%,#5c5c5c 7%,#969595 86%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#C6C6C6', endColorstr='#969595',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #c6c6c6 0%,#5c5c5c 7%,#969595 86%); /* W3C */
|
||||
/* for ie */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#5c5c5c', endColorstr='#969595',GradientType=0 ); /* IE6-9 */
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
.openerp .menu a:hover span,
|
||||
.openerp .menu a:focus span,
|
||||
.openerp .menu a.active span {
|
||||
background-position: 100% -23px;
|
||||
}
|
||||
/* Secondary Menu */
|
||||
.openerp .secondary_menu {
|
||||
width: 200px;
|
||||
@@ -149,8 +171,19 @@ body.openerp {
|
||||
}
|
||||
.openerp .secondary_menu h3 {
|
||||
padding: 0 0 2px;
|
||||
background: url(../img/secondary_menu-bg.gif) 0 100% repeat-x;
|
||||
background: #949292; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #949292 0%, #6d6b6b 87%, #282828 99%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#949292), color-stop(87%,#6d6b6b), color-stop(99%,#282828)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #949292 0%,#6d6b6b 87%,#282828 99%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #949292 0%,#6d6b6b 87%,#282828 99%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #949292 0%,#6d6b6b 87%,#282828 99%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#949292', endColorstr='#282828',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #949292 0%,#6d6b6b 87%,#282828 99%); /* W3C */
|
||||
/* for ie9 */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#949292', endColorstr='#5B5A5A',GradientType=0 ); /* IE6-9 */
|
||||
border: none;
|
||||
/* overriding jquery ui */
|
||||
-moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px;
|
||||
}
|
||||
.openerp .secondary_menu h4 {
|
||||
padding: 0 0 2px 10px;
|
||||
@@ -170,19 +203,31 @@ body.openerp {
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 #333;
|
||||
}
|
||||
.openerp .secondary_menu h3 a {
|
||||
background: url(../img/secondary_menu-h3-bg.png) 0 100% repeat-x;
|
||||
}
|
||||
.openerp .secondary_menu a.leaf:hover,
|
||||
.openerp .secondary_menu a.leaf:active,
|
||||
.openerp .secondary_menu a.leaf.active,
|
||||
.openerp .secondary_menu h4:hover,
|
||||
.openerp .secondary_menu h4:active,
|
||||
.openerp .secondary_menu h4.active,
|
||||
.openerp .secondary_menu h4.active {
|
||||
background: #ffffff; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #ffffff 0%, #d8d8d8 11%, #afafaf 86%, #333333 91%, #5a5858 96%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(11%,#d8d8d8), color-stop(86%,#afafaf), color-stop(91%,#333333), color-stop(96%,#5a5858)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#5A5858',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* W3C */
|
||||
/* overriding jquery ui */
|
||||
-moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; border-top-right-radius: 0;
|
||||
|
||||
color: #3f3d3d;
|
||||
text-shadow: #fff 0 1px 0;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.openerp .secondary_menu h4:hover a,
|
||||
.openerp .secondary_menu h4:active a,
|
||||
.openerp .secondary_menu h4.active a {
|
||||
background: url(../img/secondary_menu-link-bg.gif) repeat-x;
|
||||
color: #3f3d3d;
|
||||
text-shadow: #fff 0 1px 0;
|
||||
border: none !important;
|
||||
@@ -522,12 +567,12 @@ body.openerp {
|
||||
-moz-border-radius-topright: 7px;
|
||||
border-top-right-radius: 7px;
|
||||
}
|
||||
.openerp .oe-listview table tbody tr:last-child th:first-child {
|
||||
.openerp .oe-listview table tbody:last-child tr:last-child th:first-child {
|
||||
-webkit-border-bottom-left-radius: 7px;
|
||||
-moz-border-radius-bottomleft: 7px;
|
||||
border-bottom-left-radius: 7px;
|
||||
}
|
||||
.openerp .oe-listview table tbody tr:last-child td:last-child {
|
||||
.openerp .oe-listview table tbody:last-child tr:last-child td:last-child {
|
||||
-webkit-border-bottom-right-radius: 7px;
|
||||
-moz-border-radius-bottomright: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
@@ -628,7 +673,7 @@ body.openerp {
|
||||
/* http://www.quirksmode.org/dom/inputfile.html
|
||||
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
||||
*/
|
||||
.openerp button.oe-binary-file-set {
|
||||
.openerp .oe-binary-file-set {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@@ -713,25 +758,25 @@ body.openerp {
|
||||
}
|
||||
|
||||
.openerp .sidebar-main-div {
|
||||
height:100%;
|
||||
height: 100%;
|
||||
border-left: 1px solid #D2CFCF;
|
||||
}
|
||||
|
||||
.openerp .sidebar-sub-div {
|
||||
padding:0;
|
||||
margin:0;
|
||||
width:180px;
|
||||
border-left: 1px solid #D2CFCF;
|
||||
height:100%;
|
||||
.openerp .sidebar-content {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 180px;
|
||||
height: 100%;
|
||||
font-family: Ubuntu, Helvetica, sans-serif;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.openerp .closed-sidebar .sidebar-sub-div {
|
||||
width:22px;
|
||||
.openerp .closed-sidebar .sidebar-content {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.openerp .closed-sidebar .sidebar-displaying-div {
|
||||
display:none;
|
||||
.openerp .closed-sidebar .sidebar-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openerp .sidebar-main-div a {
|
||||
@@ -752,7 +797,16 @@ body.openerp {
|
||||
padding-left: 10px;
|
||||
padding-right: 21px;
|
||||
height: 21px;
|
||||
background: url(../img/sideheader-a-bg.png) repeat-x;
|
||||
|
||||
background: #ffffff; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #ffffff 0%, #ebe9e9 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#ebe9e9)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#EBE9E9',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
|
||||
|
||||
border: 1px solid #D2CFCF;
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
@@ -777,12 +831,15 @@ body.openerp {
|
||||
background: url(../img/toggle-a-bg.png);
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
.openerp .open-sidebar .toggle-sidebar {
|
||||
margin-left: 158px;
|
||||
background-position: 21px 0;
|
||||
position: absolute;
|
||||
}
|
||||
.openerp .closed-sidebar .toggle-sidebar {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.openerp.kitten-mode-activated .main_table {
|
||||
@@ -809,7 +866,7 @@ body.openerp {
|
||||
.openerp.kitten-mode-activated .menu span {
|
||||
background: none;
|
||||
}
|
||||
.openerp.kitten-mode-activated .sidebar-displaying-div li a,
|
||||
.openerp.kitten-mode-activated .sidebar-content li a,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content h2.oe_view_title,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content a.searchview_group_string,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content label {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 B |
Binary file not shown.
|
Before Width: | Height: | Size: 505 B |
Binary file not shown.
|
Before Width: | Height: | Size: 149 B |
Binary file not shown.
|
Before Width: | Height: | Size: 126 B |
@@ -129,6 +129,7 @@ openerp.base = function(instance) {
|
||||
openerp.base.views(instance);
|
||||
openerp.base.search(instance);
|
||||
openerp.base.list(instance);
|
||||
openerp.base.tree(instance);
|
||||
openerp.base.m2o(instance);
|
||||
openerp.base.form(instance);
|
||||
};
|
||||
|
||||
@@ -231,6 +231,32 @@ openerp.base.BasicController = Class.extend( /** @lends openerp.base.BasicContro
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 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);
|
||||
var init = openerp.base.BasicController.prototype.init;
|
||||
newer.init = init;
|
||||
var tmpclass = claz.extend(newer);
|
||||
return tmpclass.extend(add || {});
|
||||
}
|
||||
|
||||
openerp.base.Notification = openerp.base.BasicController.extend({
|
||||
init: function(element_id) {
|
||||
this._super(element_id);
|
||||
|
||||
@@ -22,23 +22,113 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
|
||||
* @param {Array} domain search domain for this DataGroup
|
||||
* @param {Object} context context of the DataGroup's searches
|
||||
* @param {Array} group_by sequence of fields by which to group
|
||||
* @param {Number} [level=0] nesting level of the group
|
||||
*/
|
||||
init: function(session, model, domain, context, group_by) {
|
||||
init: function(session, model, domain, context, group_by, level) {
|
||||
if (group_by) {
|
||||
if (group_by.length || context['group_by_no_leaf']) {
|
||||
return new openerp.base.ContainerDataGroup(
|
||||
session, model, domain, context, group_by, level);
|
||||
} else {
|
||||
return new openerp.base.GrouplessDataGroup(
|
||||
session, model, domain, context, level);
|
||||
}
|
||||
}
|
||||
|
||||
this._super(session, null);
|
||||
this.model = model;
|
||||
this.context = context;
|
||||
this.domain = domain;
|
||||
|
||||
this.group_by = group_by;
|
||||
this.level = level || 0;
|
||||
},
|
||||
cls: 'DataGroup'
|
||||
});
|
||||
openerp.base.ContainerDataGroup = openerp.base.DataGroup.extend(
|
||||
/** @lends openerp.base.ContainerDataGroup# */ {
|
||||
/**
|
||||
*
|
||||
* @constructs
|
||||
* @extends openerp.base.DataGroup
|
||||
*
|
||||
* @param session
|
||||
* @param model
|
||||
* @param domain
|
||||
* @param context
|
||||
* @param group_by
|
||||
* @param level
|
||||
*/
|
||||
init: function (session, model, domain, context, group_by, level) {
|
||||
this._super(session, model, domain, context, null, level);
|
||||
|
||||
this.groups = null;
|
||||
this.group_by = group_by;
|
||||
},
|
||||
/**
|
||||
* The format returned by ``read_group`` is absolutely dreadful:
|
||||
*
|
||||
* * A ``__context`` key provides future grouping levels
|
||||
* * A ``__domain`` key provides the domain for the next search
|
||||
* * The current grouping value is provided through the name of the
|
||||
* current grouping name e.g. if currently grouping on ``user_id``, then
|
||||
* the ``user_id`` value for this group will be provided through the
|
||||
* ``user_id`` key.
|
||||
* * Similarly, the number of items in the group (not necessarily direct)
|
||||
* is provided via ``${current_field}_count``
|
||||
* * Other aggregate fields are just dumped there
|
||||
*
|
||||
* This function slightly improves the grouping records by:
|
||||
*
|
||||
* * Adding a ``grouped_on`` property providing the current grouping field
|
||||
* * Adding a ``value`` and a ``length`` properties which replace the
|
||||
* ``$current_field`` and ``${current_field}_count`` ones
|
||||
* * Moving aggregate values into an ``aggregates`` property object
|
||||
*
|
||||
* Context and domain keys remain as-is, they should not be used externally
|
||||
* but in case they're needed...
|
||||
*
|
||||
* @param {Object} group ``read_group`` record
|
||||
*/
|
||||
transform_group: function (group) {
|
||||
var field_name = this.group_by[0];
|
||||
// In cases where group_by_no_leaf and no group_by, the result of
|
||||
// read_group has aggregate fields but no __context or __domain.
|
||||
// Create default (empty) values for those so that things don't break
|
||||
var fixed_group = _.extend(
|
||||
{__context: {group_by: []}, __domain: []},
|
||||
group);
|
||||
|
||||
var aggregates = {};
|
||||
_(fixed_group).each(function (value, key) {
|
||||
if (key.indexOf('__') === 0
|
||||
|| key === field_name
|
||||
|| key === field_name + '_count') {
|
||||
return;
|
||||
}
|
||||
aggregates[key] = value;
|
||||
});
|
||||
|
||||
return {
|
||||
__context: fixed_group.__context,
|
||||
__domain: fixed_group.__domain,
|
||||
|
||||
grouped_on: field_name,
|
||||
// if terminal group (or no group) and group_by_no_leaf => use group.__count
|
||||
length: fixed_group[field_name + '_count'] || fixed_group.__count,
|
||||
value: fixed_group[field_name],
|
||||
|
||||
openable: !(this.context['group_by_no_leaf']
|
||||
&& fixed_group.__context.group_by.length === 0),
|
||||
|
||||
aggregates: aggregates
|
||||
};
|
||||
},
|
||||
fetch: function () {
|
||||
// internal method
|
||||
var d = new $.Deferred();
|
||||
var self = this;
|
||||
|
||||
if (this.groups) {
|
||||
// disable caching for now, not sure what I should do there
|
||||
if (false && this.groups) {
|
||||
d.resolveWith(this, [this.groups]);
|
||||
} else {
|
||||
this.rpc('/base/group/read', {
|
||||
@@ -47,20 +137,10 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
|
||||
domain: this.domain,
|
||||
group_by_fields: this.group_by
|
||||
}, function () { }).then(function (response) {
|
||||
self.groups = response.result;
|
||||
// read_group results are annoying: they use the name of the
|
||||
// field grouped on to hold the value and the count, so no
|
||||
// generic access to those values is possible.
|
||||
// Alias them to `value` and `length`.
|
||||
d.resolveWith(self, [_(response.result).map(function (group) {
|
||||
var field_name = self.group_by[0];
|
||||
return _.extend({}, group, {
|
||||
// provide field used for grouping
|
||||
grouped_on: field_name,
|
||||
length: group[field_name + '_count'],
|
||||
value: group[field_name]
|
||||
});
|
||||
})]);
|
||||
var data_groups = _(response.result).map(
|
||||
_.bind(self.transform_group, self));
|
||||
self.groups = data_groups;
|
||||
d.resolveWith(self, [data_groups]);
|
||||
}, function () {
|
||||
d.rejectWith.apply(d, self, [arguments]);
|
||||
});
|
||||
@@ -68,55 +148,7 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
|
||||
return d.promise();
|
||||
},
|
||||
/**
|
||||
* Retrieves the content of an item in the DataGroup, which results in
|
||||
* either a DataSet or new DataGroup instance.
|
||||
*
|
||||
* Calling :js:func:`~openerp.base.DataGroup.get` without having called
|
||||
* :js:func:`~openerp.base.DataGroup.list` beforehand will likely result
|
||||
* in an error.
|
||||
*
|
||||
* The resulting :js:class:`~openerp.base.DataGroup` or
|
||||
* :js:class:`~openerp.base.DataSet` will be provided through the relevant
|
||||
* callback function. In both functions, the current DataGroup will be
|
||||
* provided as context (``this``)
|
||||
*
|
||||
* @param {Number} index the index of the group to open in the datagroup's collection
|
||||
* @param {Function} ifDataSet executed if the item results in a DataSet, provided with the new dataset as parameter
|
||||
* @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the new datagroup as parameter
|
||||
*/
|
||||
get: function (index, ifDataSet, ifDataGroup) {
|
||||
var group = this.groups[index];
|
||||
if (!group) {
|
||||
throw new Error("No group at index " + index);
|
||||
}
|
||||
|
||||
var child_context = _.extend({}, this.context, group.__context);
|
||||
if (group.__context.group_by.length) {
|
||||
var datagroup = new openerp.base.DataGroup(
|
||||
this.session, this.model, group.__domain, child_context,
|
||||
group.__context.group_by);
|
||||
ifDataGroup.call(this, datagroup);
|
||||
} else {
|
||||
var dataset = new openerp.base.DataSetSearch(this.session, this.model);
|
||||
dataset.domain = group.__domain;
|
||||
dataset.context = child_context;
|
||||
ifDataSet.call(this, dataset);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Gathers the content of the current data group (the current grouping
|
||||
* level), and provides it via a promise object to which callbacks should
|
||||
* be added::
|
||||
*
|
||||
* datagroup.list().then(function (list) {
|
||||
* // manipulate list here
|
||||
* });
|
||||
*
|
||||
* The argument to the callback is the list of elements fetched, the
|
||||
* context (``this``) is the datagroup itself.
|
||||
*
|
||||
* The items of a list are the standard objects returned by ``read_group``
|
||||
* with three properties added:
|
||||
* The items of a list have the following properties:
|
||||
*
|
||||
* ``length``
|
||||
* the number of records contained in the group (and all of its
|
||||
@@ -131,11 +163,44 @@ openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.bas
|
||||
* ``value``
|
||||
* the value which led to this group (this is the value all contained
|
||||
* records have for the current ``grouped_on`` field name).
|
||||
*
|
||||
* @returns {$.Deferred}
|
||||
* ``aggregates``
|
||||
* a mapping of other aggregation fields provided by ``read_group``
|
||||
*/
|
||||
list: function () {
|
||||
return this.fetch();
|
||||
list: function (ifGroups, ifRecords) {
|
||||
var self = this;
|
||||
this.fetch().then(function (group_records) {
|
||||
ifGroups(_(group_records).map(function (group) {
|
||||
var child_context = _.extend({}, self.context, group.__context);
|
||||
return _.extend(
|
||||
new openerp.base.DataGroup(
|
||||
self.session, self.model, group.__domain,
|
||||
child_context, child_context.group_by,
|
||||
self.level + 1),
|
||||
group);
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend(
|
||||
/** @lends openerp.base.GrouplessDataGroup# */ {
|
||||
/**
|
||||
*
|
||||
* @constructs
|
||||
* @extends openerp.base.DataGroup
|
||||
*
|
||||
* @param session
|
||||
* @param model
|
||||
* @param domain
|
||||
* @param context
|
||||
* @param level
|
||||
*/
|
||||
init: function (session, model, domain, context, level) {
|
||||
this._super(session, model, domain, context, null, level);
|
||||
},
|
||||
list: function (ifGroups, ifRecords) {
|
||||
ifRecords(_.extend(
|
||||
new openerp.base.DataSetSearch(this.session, this.model),
|
||||
{domain: this.domain, context: this.context}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -303,6 +368,7 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
|
||||
offset: offset,
|
||||
limit: limit
|
||||
}, function (records) {
|
||||
self.ids.splice(0, self.ids.length);
|
||||
self.offset = offset;
|
||||
self.count = records.length; // TODO: get real count
|
||||
for (var i=0; i < records.length; i++ ) {
|
||||
|
||||
@@ -17,7 +17,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
*/
|
||||
init: function(view_manager, session, element_id, dataset, view_id) {
|
||||
this._super(session, element_id);
|
||||
this.view_manager = view_manager;
|
||||
this.view_manager = view_manager || new openerp.base.NullViewManager();
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
@@ -29,12 +29,12 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
this.ready = false;
|
||||
this.show_invalid = true;
|
||||
this.touched = false;
|
||||
this.flags = view_manager.action.flags || {};
|
||||
this.flags = this.view_manager.action.flags || {};
|
||||
},
|
||||
start: function() {
|
||||
//this.log('Starting FormView '+this.model+this.view_id)
|
||||
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
|
||||
toolbar:!!this.view_manager.sidebar}, this.on_loaded);
|
||||
toolbar:!!this.flags.sidebar}, this.on_loaded);
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
var self = this;
|
||||
@@ -56,10 +56,8 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
this.$element.find('#' + this.element_id + '_header button.oe_form_button_cancel').click(this.do_cancel);
|
||||
this.$element.find('#' + this.element_id + '_header button.oe_form_button_new').click(this.on_button_new);
|
||||
|
||||
// sidebar stuff
|
||||
if (this.view_manager.sidebar) {
|
||||
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
|
||||
}
|
||||
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
|
||||
this.view_manager.sidebar.do_refresh.add_last(this.on_sidebar_refreshed);
|
||||
},
|
||||
do_show: function () {
|
||||
var self = this;
|
||||
@@ -72,9 +70,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
} else {
|
||||
this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
|
||||
}
|
||||
if (this.view_manager && this.view_manager.sidebar) {
|
||||
this.view_manager.sidebar.refresh(true);
|
||||
}
|
||||
this.view_manager.sidebar.do_refresh(true);
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
@@ -307,6 +303,10 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
do_cancel: function () {
|
||||
this.notification.notify("Cancelling form");
|
||||
},
|
||||
on_sidebar_refreshed: function(new_view) {
|
||||
var sidebar = this.view_manager.sidebar;
|
||||
// TODO: Add attachment WIP
|
||||
},
|
||||
reload: function() {
|
||||
if (this.datarecord.id) {
|
||||
this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
|
||||
@@ -782,7 +782,7 @@ openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({
|
||||
this._super.apply(this, arguments);
|
||||
if (!this.invalid) {
|
||||
var time = this.value.split(':');
|
||||
this.value = parseInt(time[0], 10) + parseInt(time[1], 10) / 60;
|
||||
this.set_value(parseInt(time[0], 10) + parseInt(time[1], 10) / 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -913,12 +913,6 @@ openerp.base.form.FieldMany2OneDatasSet = openerp.base.DataSetStatic.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldMany2OneViewManager = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, dataset, views) {
|
||||
this._super(session, element_id, dataset, views);
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
@@ -929,8 +923,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
|
||||
this.$element = $('#' + this.element_id);
|
||||
this.dataset = new openerp.base.form.FieldMany2OneDatasSet(this.session, this.field.relation);
|
||||
var views = [ [false,"list"], [false,"form"] ];
|
||||
this.viewmanager = new openerp.base.form.FieldMany2OneViewManager(this.view.session, this.element_id, this.dataset, views);
|
||||
new openerp.base.m2o(this.viewmanager, this.$element, this.field.relation, this.dataset, this.session)
|
||||
new openerp.base.m2o(this.$element, this.field.relation, this.dataset, this.session)
|
||||
this.$element.find('input').change(this.on_ui_change);
|
||||
},
|
||||
set_value: function(value) {
|
||||
@@ -968,6 +961,7 @@ openerp.base.form.FieldOne2ManyDatasSet = openerp.base.DataSetStatic.extend({
|
||||
openerp.base.form.FieldOne2ManyViewManager = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, dataset, views) {
|
||||
this._super(session, element_id, dataset, views);
|
||||
this.action = {flags:{}};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -991,7 +985,6 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
|
||||
this.log("o2m.set_value",value);
|
||||
this.viewmanager.dataset.ids = value;
|
||||
this.viewmanager.dataset.count = value.length;
|
||||
this.viewmanager.views.list.controller.do_update();
|
||||
}
|
||||
},
|
||||
get_value: function(value) {
|
||||
@@ -1017,8 +1010,10 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.dataset = new openerp.base.DataSetMany2Many(this.session, this.field.relation);
|
||||
this.list_view = new openerp.base.form.Many2ManyListView(undefined, this.view.session,
|
||||
this.list_id, this.dataset, false, {'selected': false, 'addable': 'Add'});
|
||||
//TODO: switch to non selectable once xmo has corrected the bug related to that
|
||||
this.list_view = new openerp.base.form.Many2ManyListView(null, this.view.session,
|
||||
this.list_id, this.dataset, false, {'selectable-': false,
|
||||
'addable': 'Add'});
|
||||
var self = this;
|
||||
this.list_view.m2m_field = this;
|
||||
this.list_view.start();
|
||||
@@ -1126,8 +1121,8 @@ openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
|
||||
if (this.searchview) {
|
||||
this.searchview.stop();
|
||||
}
|
||||
this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search",
|
||||
this.dataset, false, {});
|
||||
this.searchview = new openerp.base.SearchView(null, this.session,
|
||||
this.element_id + "_search", this.dataset, false, {});
|
||||
this.searchview.on_search.add(function(domains, contexts, groupbys) {
|
||||
self.view_list.do_search.call(
|
||||
self, domains, contexts, groupbys);
|
||||
@@ -1144,7 +1139,8 @@ openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
|
||||
self.stop();
|
||||
});
|
||||
self.view_list = new openerp.base.form.Many2XPopupListView( null, self.session,
|
||||
self.element_id + "_view_list", self.dataset, false);
|
||||
self.element_id + "_view_list", self.dataset, false,
|
||||
{'deletable': false});
|
||||
self.view_list.popup = self;
|
||||
self.view_list.do_show();
|
||||
self.view_list.start();
|
||||
@@ -1165,7 +1161,7 @@ openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
|
||||
this.searchview.hide();
|
||||
this.view_list.$element.hide();
|
||||
this.dataset.index = null;
|
||||
this.view_form = new openerp.base.FormView({}, this.session,
|
||||
this.view_form = new openerp.base.FormView(null, this.session,
|
||||
this.element_id + "_view_form", this.dataset, false);
|
||||
this.view_form.start();
|
||||
this.view_form.on_loaded.add_last(function() {
|
||||
@@ -1191,7 +1187,7 @@ openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
|
||||
});
|
||||
|
||||
openerp.base.form.Many2XPopupListView = openerp.base.ListView.extend({
|
||||
switch_to_record: function(index) {
|
||||
select_record: function(index) {
|
||||
this.popup.on_select_element(this.dataset.ids[index]);
|
||||
}
|
||||
});
|
||||
@@ -1226,14 +1222,16 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({
|
||||
}
|
||||
return size.toFixed(2) + ' ' + units[i];
|
||||
},
|
||||
on_file_change: function() {
|
||||
on_file_change: function(e) {
|
||||
// TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper
|
||||
// http://www.html5rocks.com/tutorials/file/dndfiles/
|
||||
// http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
|
||||
window[this.iframe] = this.on_file_uploaded;
|
||||
this.$element.find('form.oe-binary-form input[name=session_id]').val(this.session.session_id);
|
||||
this.$element.find('form.oe-binary-form').submit();
|
||||
this.toggle_progress();
|
||||
if ($(e.target).val() != '') {
|
||||
this.$element.find('form.oe-binary-form input[name=session_id]').val(this.session.session_id);
|
||||
this.$element.find('form.oe-binary-form').submit();
|
||||
this.toggle_progress();
|
||||
}
|
||||
},
|
||||
toggle_progress: function() {
|
||||
this.$element.find('.oe-binary-progress, .oe-binary').toggle();
|
||||
@@ -1253,7 +1251,7 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({
|
||||
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
||||
},
|
||||
on_save_as: function() {
|
||||
var url = '/base/formview/saveas?session_id=' + this.session.session_id + '&model=' +
|
||||
var url = '/base/binary/saveas?session_id=' + this.session.session_id + '&model=' +
|
||||
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name +
|
||||
'&fieldname=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime())
|
||||
window.open(url);
|
||||
@@ -1327,7 +1325,7 @@ openerp.base.form.FieldBinaryImage = openerp.base.form.FieldBinary.extend({
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
this.set_image_maxwidth();
|
||||
var url = '/base/formview/image?session_id=' + this.session.session_id + '&model=' +
|
||||
var url = '/base/binary/image?session_id=' + this.session.session_id + '&model=' +
|
||||
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime())
|
||||
this.$image.attr('src', url);
|
||||
}
|
||||
|
||||
+326
-152
@@ -1,4 +1,5 @@
|
||||
openerp.base.list = function (openerp) {
|
||||
'use strict';
|
||||
openerp.base.views.add('list', 'openerp.base.ListView');
|
||||
openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ {
|
||||
defaults: {
|
||||
@@ -45,30 +46,21 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
init: function(view_manager, session, element_id, dataset, view_id, options) {
|
||||
var self = this;
|
||||
this._super(session, element_id);
|
||||
this.view_manager = view_manager;
|
||||
this.view_manager = view_manager || new openerp.base.NullViewManager();
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
|
||||
this.columns = [];
|
||||
this.rows = [];
|
||||
|
||||
this.options = _.extend({}, this.defaults, options || {});
|
||||
this.flags = {};
|
||||
if (view_manager && view_manager.action) {
|
||||
this.flags = view_manager.action.flags;
|
||||
}
|
||||
this.flags = this.view_manager.action.flags;
|
||||
|
||||
this.list = new openerp.base.ListView.List({
|
||||
options: this.options,
|
||||
columns: this.columns,
|
||||
rows: this.rows
|
||||
});
|
||||
this.groups = new openerp.base.ListView.Groups({
|
||||
this.groups = new openerp.base.ListView.Groups(this, {
|
||||
options: this.options,
|
||||
columns: this.columns
|
||||
});
|
||||
$([this.list, this.groups]).bind({
|
||||
$(this.groups).bind({
|
||||
'selected': function (e, selection) {
|
||||
self.$element.find('#oe-list-delete')
|
||||
.toggle(!!selection.length);
|
||||
@@ -76,18 +68,26 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
'deleted': function (e, ids) {
|
||||
self.do_delete(ids);
|
||||
},
|
||||
'action': function (e, action_name, id) {
|
||||
'action': function (e, action_name, id, callback) {
|
||||
var action = _.detect(self.columns, function (field) {
|
||||
return field.name === action_name;
|
||||
});
|
||||
if (!action) { return; }
|
||||
// TODO: not supposed to reload everything, I think
|
||||
self.execute_action(
|
||||
action, self.dataset, self.session.action_manager,
|
||||
id, self.do_reload);
|
||||
id, function () {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
'row_link': function (e, index) {
|
||||
self.select_record(index);
|
||||
'row_link': function (e, index, id, dataset) {
|
||||
_.extend(self.dataset, {
|
||||
domain: dataset.domain,
|
||||
context: dataset.context
|
||||
}).read_slice([], null, null, function () {
|
||||
self.select_record(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -103,7 +103,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
return this.rpc("/base/listview/load", {
|
||||
model: this.model,
|
||||
view_id: this.view_id,
|
||||
toolbar: this.view_manager ? !!this.view_manager.sidebar : false
|
||||
toolbar: !!this.flags.sidebar
|
||||
}, this.on_loaded);
|
||||
},
|
||||
/**
|
||||
@@ -127,41 +127,15 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
* @param {Object} data.fields_view fields_view_get result (processed)
|
||||
* @param {Object} data.fields_view.fields mapping of fields for the current model
|
||||
* @param {Object} data.fields_view.arch current list view descriptor
|
||||
* @param {Array} columns columns to move to the front (and make visible)
|
||||
*/
|
||||
on_loaded: function(data) {
|
||||
on_loaded: function(data, columns) {
|
||||
var self = this;
|
||||
this.fields_view = data.fields_view;
|
||||
//this.log(this.fields_view);
|
||||
this.name = "" + this.fields_view.arch.attrs.string;
|
||||
|
||||
var fields = this.fields_view.fields;
|
||||
var domain_computer = openerp.base.form.compute_domain;
|
||||
|
||||
this.columns.splice(0, this.columns.length);
|
||||
this.columns.push.apply(this.columns, _(this.fields_view.arch.children).chain()
|
||||
.map(function (field) {
|
||||
var name = field.attrs.name;
|
||||
var column = _.extend({id: name, tag: field.tag},
|
||||
field.attrs, fields[name]);
|
||||
// attrs computer
|
||||
if (column.attrs) {
|
||||
var attrs = eval('(' + column.attrs + ')');
|
||||
column.attrs_for = function (fields) {
|
||||
var result = {};
|
||||
for (var attr in attrs) {
|
||||
result[attr] = domain_computer(attrs[attr], fields);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
column.attrs_for = function () { return {}; };
|
||||
}
|
||||
return column;
|
||||
}).value());
|
||||
|
||||
this.visible_columns = _.filter(this.columns, function (column) {
|
||||
return column.invisible !== '1';
|
||||
});
|
||||
this.setup_columns(this.fields_view.fields, columns);
|
||||
|
||||
if (!this.fields_view.sorted) { this.fields_view.sorted = {}; }
|
||||
|
||||
@@ -181,49 +155,57 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
self.do_reload();
|
||||
});
|
||||
|
||||
var $table = this.$element.find('table');
|
||||
this.list.move_to($table);
|
||||
|
||||
// sidebar stuff
|
||||
if (this.view_manager && this.view_manager.sidebar) {
|
||||
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
|
||||
}
|
||||
this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
|
||||
},
|
||||
/**
|
||||
* Fills the table with the provided records after emptying it
|
||||
* Sets up the listview's columns: merges view and fields data, move
|
||||
* grouped-by columns to the front of the columns list and make them all
|
||||
* visible.
|
||||
*
|
||||
* TODO: should also re-load the table itself, as e.g. columns may have changed
|
||||
*
|
||||
* @param {Object} result filling result
|
||||
* @param {Array} [result.view] the new view (wrapped fields_view_get result)
|
||||
* @param {Array} result.records records the records to fill the list view with
|
||||
* @param {Object} fields fields_view_get's fields section
|
||||
* @param {Array} groupby_columns columns the ListView is grouped by
|
||||
*/
|
||||
do_fill_table: function(result) {
|
||||
if (result.view) {
|
||||
this.on_loaded({fields_view: result.view});
|
||||
}
|
||||
var records = result.records;
|
||||
setup_columns: function (fields, groupby_columns) {
|
||||
var domain_computer = openerp.base.form.compute_domain;
|
||||
|
||||
this.rows.splice(0, this.rows.length);
|
||||
this.rows.push.apply(this.rows, records);
|
||||
|
||||
// Keep current selected record, if it's still in our new search
|
||||
var current_record_id = this.dataset.ids[this.dataset.index];
|
||||
this.dataset.ids = _(records).chain().map(function (record) {
|
||||
return record.data.id.value;
|
||||
}).value();
|
||||
this.dataset.index = _.indexOf(this.dataset.ids, current_record_id);
|
||||
if (this.dataset.index < 0) {
|
||||
this.dataset.index = 0;
|
||||
}
|
||||
var noop = function () { return {}; };
|
||||
var field_to_column = function (field) {
|
||||
var name = field.attrs.name;
|
||||
var column = _.extend({id: name, tag: field.tag},
|
||||
field.attrs, fields[name]);
|
||||
// attrs computer
|
||||
if (column.attrs) {
|
||||
var attrs = eval('(' + column.attrs + ')');
|
||||
column.attrs_for = function (fields) {
|
||||
var result = {};
|
||||
for (var attr in attrs) {
|
||||
result[attr] = domain_computer(attrs[attr], fields);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
column.attrs_for = noop;
|
||||
}
|
||||
return column;
|
||||
};
|
||||
|
||||
this.dataset.count = this.dataset.ids.length;
|
||||
var results = this.rows.length;
|
||||
this.$element.find('table')
|
||||
.find('.oe-pager-last').text(results).end()
|
||||
.find('.oe-pager-total').text(results);
|
||||
this.columns.splice(0, this.columns.length);
|
||||
this.columns.push.apply(
|
||||
this.columns,
|
||||
_(this.fields_view.arch.children).map(field_to_column));
|
||||
if (groupby_columns) {
|
||||
this.columns.unshift({
|
||||
id: '_group', tag: '', string: "Group", meta: true,
|
||||
attrs_for: function () { return {}; }
|
||||
}, {
|
||||
id: '_count', tag: '', string: '#', meta: true,
|
||||
attrs_for: function () { return {}; }
|
||||
});
|
||||
}
|
||||
|
||||
this.list.refresh();
|
||||
this.visible_columns = _.filter(this.columns, function (column) {
|
||||
return column.invisible !== '1';
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Used to handle a click on a table row, if no other handler caught the
|
||||
@@ -252,12 +234,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
if (this.hidden) {
|
||||
this.do_reload();
|
||||
this.$element.find('table').append(
|
||||
this.groups.apoptosis().render());
|
||||
this.hidden = false;
|
||||
}
|
||||
if (this.view_manager && this.view_manager.sidebar) {
|
||||
this.view_manager.sidebar.refresh(true);
|
||||
}
|
||||
this.view_manager.sidebar.do_refresh(true);
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
@@ -265,12 +246,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
},
|
||||
/**
|
||||
* Reloads the search view based on the current settings (dataset & al)
|
||||
*
|
||||
* @param {Array} [primary_columns] columns to bring to the front of the
|
||||
* sequence
|
||||
*/
|
||||
do_reload: function () {
|
||||
// TODO: need to do 5 billion tons of pre-processing, bypass
|
||||
// DataSet for now
|
||||
//self.dataset.read_slice(self.dataset.fields, 0, self.limit,
|
||||
// self.do_fill_table);
|
||||
do_reload: function (primary_columns) {
|
||||
// TODO: should just fields_view_get I think
|
||||
var self = this;
|
||||
this.dataset.offset = 0;
|
||||
this.dataset.limit = false;
|
||||
return this.rpc('/base/listview/fill', {
|
||||
@@ -279,7 +261,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
'context': this.dataset.context,
|
||||
'domain': this.dataset.domain,
|
||||
'sort': this.dataset.sort && this.dataset.sort()
|
||||
}, this.do_fill_table);
|
||||
}, function (result) {
|
||||
if (result.view) {
|
||||
self.on_loaded({fields_view: result.view}, primary_columns);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Event handler for a search, asks for the computation/folding of domains
|
||||
@@ -297,24 +283,21 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
contexts: contexts,
|
||||
group_by_seq: groupbys
|
||||
}, function (results) {
|
||||
// TODO: handle non-empty results.group_by with read_group
|
||||
self.dataset.context = results.context;
|
||||
self.dataset.domain = results.domain;
|
||||
if (results.group_by.length) {
|
||||
self.groups.datagroup = new openerp.base.DataGroup(
|
||||
self.session, self.dataset.model,
|
||||
results.domain, results.context,
|
||||
results.group_by);
|
||||
self.$element.html(self.groups.render());
|
||||
return;
|
||||
self.groups.datagroup = new openerp.base.DataGroup(
|
||||
self.session, self.dataset.model,
|
||||
results.domain, results.context,
|
||||
results.group_by);
|
||||
|
||||
if (_.isEmpty(results.group_by) && !results.context['group_by_no_leaf']) {
|
||||
results.group_by = null;
|
||||
}
|
||||
return self.do_reload();
|
||||
self.do_reload(results.group_by).then(function () {
|
||||
self.$element.find('table').append(self.groups.render());
|
||||
});
|
||||
});
|
||||
},
|
||||
do_update: function () {
|
||||
var self = this;
|
||||
//self.dataset.read_ids(self.dataset.ids, self.dataset.fields, self.do_fill_table);
|
||||
},
|
||||
/**
|
||||
* Handles the signal to delete a line from the DOM
|
||||
*
|
||||
@@ -345,7 +328,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
self.rows.splice(record.index, 1);
|
||||
});
|
||||
// TODO only refresh modified rows
|
||||
self.list.refresh();
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -362,8 +344,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
* Handles deletion of all selected lines
|
||||
*/
|
||||
do_delete_selected: function () {
|
||||
this.do_delete(
|
||||
this.list.get_selection());
|
||||
this.do_delete(this.groups.get_selection());
|
||||
}
|
||||
// TODO: implement reorder (drag and drop rows)
|
||||
});
|
||||
@@ -401,6 +382,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
|
||||
|
||||
this.options = opts.options;
|
||||
this.columns = opts.columns;
|
||||
this.dataset = opts.dataset;
|
||||
this.rows = opts.rows;
|
||||
|
||||
this.$_element = $('<tbody class="ui-widget-content">')
|
||||
@@ -427,21 +409,16 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
|
||||
$(self).trigger(
|
||||
'row_link',
|
||||
[self.row_position(e.currentTarget),
|
||||
self.row_id(e.currentTarget)]);
|
||||
self.row_id(e.currentTarget),
|
||||
self.dataset]);
|
||||
});
|
||||
},
|
||||
move_to: function (element) {
|
||||
this.$current = this.$_element.clone(true).appendTo(element);
|
||||
this.render();
|
||||
return this;
|
||||
},
|
||||
render: function () {
|
||||
if (this.$current) {
|
||||
this.$current.remove();
|
||||
}
|
||||
this.$current = this.$_element.clone(true);
|
||||
this.$current.empty().append($(QWeb.render('ListView.rows', this)));
|
||||
return this;
|
||||
},
|
||||
refresh: function () {
|
||||
this.render();
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Gets the ids of all currently selected records, if any
|
||||
@@ -475,6 +452,14 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
|
||||
*/
|
||||
row_id: function (row) {
|
||||
return this.rows[this.row_position(row)].data.id.value;
|
||||
},
|
||||
/**
|
||||
* Death signal, cleans up list
|
||||
*/
|
||||
apoptosis: function () {
|
||||
if (!this.$current) { return; }
|
||||
this.$current.remove();
|
||||
this.$current = null;
|
||||
}
|
||||
// drag and drop
|
||||
// editable?
|
||||
@@ -487,44 +472,233 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
|
||||
* Provides events similar to those of
|
||||
* :js:class:`~openerp.base.ListView.List`
|
||||
*/
|
||||
init: function (opts) {
|
||||
init: function (view, opts) {
|
||||
this.view = view;
|
||||
this.options = opts.options;
|
||||
this.columns = opts.columns;
|
||||
this.datagroup = {};
|
||||
|
||||
this.sections = [];
|
||||
this.children = {};
|
||||
},
|
||||
make_level: function (datagroup) {
|
||||
var self = this, $root = $('<dl>');
|
||||
datagroup.list().then(function (list) {
|
||||
_(list).each(function (group, index) {
|
||||
var $title = $('<dt>')
|
||||
.text(group.grouped_on + ': ' + group.value + ' (' + group.length + ')')
|
||||
.appendTo($root);
|
||||
$title.click(function () {
|
||||
datagroup.get(index, function (new_dataset) {
|
||||
var $content = $('<ul>').appendTo(
|
||||
$('<dd>').insertAfter($title));
|
||||
new_dataset.read_slice([], null, null, function (records) {
|
||||
_(records).each(function (record) {
|
||||
$('<li>')
|
||||
.appendTo($content)
|
||||
.text(_(record).map(function (value, key) {
|
||||
return key + ': ' + value;
|
||||
}).join(', '));
|
||||
});
|
||||
});
|
||||
}, function (new_datagroup) {
|
||||
console.log(new_datagroup);
|
||||
$('<dd>')
|
||||
.insertAfter($title)
|
||||
.append(self.make_level(new_datagroup));
|
||||
});
|
||||
});
|
||||
});
|
||||
pad: function ($row) {
|
||||
if (this.options.selectable) {
|
||||
$row.append('<td>');
|
||||
}
|
||||
},
|
||||
make_fragment: function () {
|
||||
return document.createDocumentFragment();
|
||||
},
|
||||
/**
|
||||
* Returns a DOM node after which a new tbody can be inserted, so that it
|
||||
* follows the provided row.
|
||||
*
|
||||
* Necessary to insert the result of a new group or list view within an
|
||||
* existing groups render, without losing track of the groups's own
|
||||
* elements
|
||||
*
|
||||
* @param {HTMLTableRowElement} row the row after which the caller wants to insert a body
|
||||
* @returns {HTMLTableSectionElement} element after which a tbody can be inserted
|
||||
*/
|
||||
point_insertion: function (row) {
|
||||
var $row = $(row);
|
||||
var red_letter_tbody = $row.closest('tbody')[0];
|
||||
|
||||
var $next_siblings = $row.nextAll();
|
||||
if ($next_siblings.length) {
|
||||
var $root_kanal = $('<tbody>').insertAfter(red_letter_tbody);
|
||||
|
||||
$root_kanal.append($next_siblings);
|
||||
this.elements.splice(
|
||||
_.indexOf(this.elements, red_letter_tbody),
|
||||
0,
|
||||
$root_kanal[0]);
|
||||
}
|
||||
return red_letter_tbody;
|
||||
},
|
||||
open_group: function (e, group) {
|
||||
var row = e.currentTarget;
|
||||
|
||||
if (this.children[group.value]) {
|
||||
this.children[group.value].apoptosis();
|
||||
delete this.children[group.value];
|
||||
}
|
||||
var prospekt = this.children[group.value] = new openerp.base.ListView.Groups(this.view, {
|
||||
options: this.options,
|
||||
columns: this.columns
|
||||
});
|
||||
return $root;
|
||||
this.bind_child_events(prospekt);
|
||||
prospekt.datagroup = group;
|
||||
prospekt.render().insertAfter(
|
||||
this.point_insertion(row));
|
||||
$(row).find('span.ui-icon')
|
||||
.removeClass('ui-icon-triangle-1-e')
|
||||
.addClass('ui-icon-triangle-1-s');
|
||||
},
|
||||
/**
|
||||
* Prefixes ``$node`` with floated spaces in order to indent it relative
|
||||
* to its own left margin/baseline
|
||||
*
|
||||
* @param {jQuery} $node jQuery object to indent
|
||||
* @param {Number} level current nesting level, >= 1
|
||||
* @returns {jQuery} the indentation node created
|
||||
*/
|
||||
indent: function ($node, level) {
|
||||
return $('<span>')
|
||||
.css({'float': 'left', 'white-space': 'pre'})
|
||||
.text(new Array(level).join(' '))
|
||||
.prependTo($node);
|
||||
},
|
||||
render_groups: function (datagroups) {
|
||||
var self = this;
|
||||
var placeholder = this.make_fragment();
|
||||
_(datagroups).each(function (group) {
|
||||
var $row = $('<tr>');
|
||||
if (group.openable) {
|
||||
$row.click(function (e) {
|
||||
if (!$row.data('open')) {
|
||||
$row.data('open', true);
|
||||
self.open_group(e, group);
|
||||
} else {
|
||||
$row.removeData('open')
|
||||
.find('span.ui-icon')
|
||||
.removeClass('ui-icon-triangle-1-s')
|
||||
.addClass('ui-icon-triangle-1-e');
|
||||
_(self.children).each(function (child) {child.apoptosis();});
|
||||
}
|
||||
});
|
||||
}
|
||||
placeholder.appendChild($row[0]);
|
||||
|
||||
|
||||
var $group_column = $('<th>').appendTo($row);
|
||||
if (group.grouped_on) {
|
||||
// Don't fill this if group_by_no_leaf but no group_by
|
||||
$group_column
|
||||
.text((group.value instanceof Array ? group.value[1] : group.value));
|
||||
if (group.openable) {
|
||||
// Make openable if not terminal group & group_by_no_leaf
|
||||
$group_column
|
||||
.prepend('<span class="ui-icon ui-icon-triangle-1-e" style="float: left;">');
|
||||
}
|
||||
}
|
||||
self.indent($group_column, group.level);
|
||||
// count column
|
||||
$('<td>').text(group.length).appendTo($row);
|
||||
|
||||
self.pad($row);
|
||||
_(self.columns).chain()
|
||||
.filter(function (column) {return !column.invisible;})
|
||||
.each(function (column) {
|
||||
if (column.meta) {
|
||||
// do not do anything
|
||||
} else if (column.id in group.aggregates) {
|
||||
var value = group.aggregates[column.id];
|
||||
var format;
|
||||
if (column.type === 'integer') {
|
||||
format = "%.0f";
|
||||
} else if (column.type === 'float') {
|
||||
format = "%.2f";
|
||||
}
|
||||
$('<td>')
|
||||
.text(_.sprintf(format, value))
|
||||
.appendTo($row);
|
||||
} else {
|
||||
$row.append('<td>');
|
||||
}
|
||||
});
|
||||
});
|
||||
return placeholder;
|
||||
},
|
||||
bind_child_events: function (child) {
|
||||
var $this = $(this),
|
||||
self = this;
|
||||
$(child).bind('selected', function (e) {
|
||||
// can have selections spanning multiple links
|
||||
$this.trigger(e, [self.get_selection()]);
|
||||
}).bind('action', function (e, name, id, callback) {
|
||||
if (!callback) {
|
||||
callback = function () {
|
||||
var $prev = child.$current.prev();
|
||||
if (!$prev.is('tbody')) {
|
||||
// ungrouped
|
||||
$(self.elements[0]).replaceWith(self.render());
|
||||
} else {
|
||||
// ghetto reload child (and its siblings)
|
||||
$prev.children().last().click();
|
||||
}
|
||||
};
|
||||
}
|
||||
$this.trigger(e, [name, id, callback]);
|
||||
}).bind('deleted row_link', function (e) {
|
||||
// additional positional parameters are provided to trigger as an
|
||||
// Array, following the event type or event object, but are
|
||||
// provided to the .bind event handler as *args.
|
||||
// Convert our *args back into an Array in order to trigger them
|
||||
// on the group itself, so it can ultimately be forwarded wherever
|
||||
// it's supposed to go.
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
$this.trigger.call($this, e, args);
|
||||
});
|
||||
},
|
||||
render_dataset: function (dataset) {
|
||||
var rows = [],
|
||||
list = new openerp.base.ListView.List({
|
||||
options: this.options,
|
||||
columns: this.columns,
|
||||
dataset: dataset,
|
||||
rows: rows
|
||||
});
|
||||
this.bind_child_events(list);
|
||||
|
||||
var d = new $.Deferred();
|
||||
this.view.rpc('/base/listview/fill', {
|
||||
model: dataset.model,
|
||||
id: this.view.view_id,
|
||||
context: dataset.context,
|
||||
domain: dataset.domain,
|
||||
sort: dataset.sort && dataset.sort()
|
||||
}, function (result) {
|
||||
rows.splice(0, rows.length);
|
||||
rows.push.apply(rows, result.records);
|
||||
list.render();
|
||||
d.resolve(list);
|
||||
});
|
||||
return d.promise();
|
||||
},
|
||||
render: function () {
|
||||
return this.make_level(this.datagroup);
|
||||
var self = this;
|
||||
var $element = $('<tbody>');
|
||||
this.elements = [$element[0]];
|
||||
this.datagroup.list(function (groups) {
|
||||
$element[0].appendChild(
|
||||
self.render_groups(groups));
|
||||
}, function (dataset) {
|
||||
self.render_dataset(dataset).then(function (list) {
|
||||
self.children[null] = list;
|
||||
self.elements =
|
||||
[list.$current.replaceAll($element)[0]];
|
||||
});
|
||||
});
|
||||
return $element;
|
||||
},
|
||||
/**
|
||||
* Returns the ids of all selected records for this group
|
||||
*/
|
||||
get_selection: function () {
|
||||
return _(this.children).chain()
|
||||
.map(function (child) {
|
||||
return child.get_selection();
|
||||
})
|
||||
.flatten()
|
||||
.value();
|
||||
},
|
||||
apoptosis: function () {
|
||||
_(this.children).each(function (child) {
|
||||
child.apoptosis();
|
||||
});
|
||||
$(this.elements).remove();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
openerp.base.m2o = function(openerp){
|
||||
|
||||
openerp.base.m2o = openerp.base.Controller.extend({
|
||||
init: function(view_manager, element_id, model, dataset, session){
|
||||
init: function(element_id, model, dataset, session){
|
||||
this._super(element_id, model, dataset, session);
|
||||
|
||||
this.view_manager = view_manager
|
||||
this.session = session;
|
||||
this.element = element_id.find('input');
|
||||
this.button = element_id.find('div');
|
||||
@@ -74,7 +73,8 @@ openerp.base.m2o = function(openerp){
|
||||
minWidth: 800
|
||||
});
|
||||
self.element.val('');
|
||||
var event_form = new openerp.base.FormView(self.view_manager, self.session, element_id, self.dataset, false);
|
||||
var event_form = new openerp.base.FormView(null,
|
||||
self.session, element_id, self.dataset, false);
|
||||
event_form.start();
|
||||
});
|
||||
$input.val(self.element.data( "autocomplete" ).term);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* OpenERP base library
|
||||
*---------------------------------------------------------*/
|
||||
|
||||
openerp.base.view_tree = function(openerp) {
|
||||
openerp.base.tree = function(openerp) {
|
||||
|
||||
openerp.base.views.add('tree', 'openerp.base.TreeView');
|
||||
openerp.base.TreeView = openerp.base.Controller.extend({
|
||||
@@ -21,7 +21,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
do_action: function(action) {
|
||||
var self = this;
|
||||
action.flags = _.extend({
|
||||
sidebar : true,
|
||||
sidebar : action.target != 'new',
|
||||
search_view : true,
|
||||
new_window : false,
|
||||
views_switcher : true,
|
||||
@@ -82,6 +82,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
this.views_src = views;
|
||||
this.views = {};
|
||||
this.flags = this.flags || {};
|
||||
this.sidebar = new openerp.base.NullSidebar();
|
||||
},
|
||||
/**
|
||||
* @returns {jQuery.Deferred} initial view loading promise
|
||||
@@ -198,6 +199,14 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.base.ViewManager, {
|
||||
init: function() {
|
||||
this._super();
|
||||
this.action = {flags: {}};
|
||||
this.sidebar = new openerp.base.NullSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, action) {
|
||||
var dataset;
|
||||
@@ -213,7 +222,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
this.flags = this.action.flags || {};
|
||||
if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
|
||||
// Not elegant but allows to avoid flickering of SearchView#do_hide
|
||||
this.flags.search_view = this.flags.pager = this.flags.action_buttons = false;
|
||||
this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false;
|
||||
}
|
||||
if (this.flags.sidebar) {
|
||||
this.sidebar = new openerp.base.Sidebar(null, this);
|
||||
@@ -223,7 +232,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
var inital_view_loaded = this._super();
|
||||
|
||||
// init sidebar
|
||||
if (this.sidebar) {
|
||||
if (this.flags.sidebar) {
|
||||
this.$element.find('.view-manager-main-sidebar').html(this.sidebar.render());
|
||||
this.sidebar.start();
|
||||
}
|
||||
@@ -250,9 +259,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
},
|
||||
stop: function() {
|
||||
// should be replaced by automatic destruction implemented in BaseWidget
|
||||
if (this.sidebar) {
|
||||
this.sidebar.stop();
|
||||
}
|
||||
this.sidebar.stop();
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
@@ -295,9 +302,9 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
||||
var section = {elements:toolbar[type[0]], label:type[1]};
|
||||
self.sections.push(section);
|
||||
});
|
||||
this.refresh(true);
|
||||
this.do_refresh(true);
|
||||
},
|
||||
refresh: function(new_view) {
|
||||
do_refresh: function(new_view) {
|
||||
var view = this.view_manager.active_view;
|
||||
the_condition = this.sections.length > 0 && _.detect(this.sections,
|
||||
function(x) {return x.elements.length > 0;}) != undefined
|
||||
@@ -309,21 +316,20 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
||||
this.$element.addClass('open-sidebar');
|
||||
this.$element.removeClass('closed-sidebar');
|
||||
}
|
||||
|
||||
|
||||
this.$element.html(QWeb.render("ViewManager.sidebar.internal",this));
|
||||
|
||||
|
||||
var self = this;
|
||||
this.$element.find(".toggle-sidebar").click(function(e) {
|
||||
self.$element.toggleClass('open-sidebar closed-sidebar');
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
this.$element.find("a.oe_sidebar_action_a").click(function(e) {
|
||||
var $this = jQuery(this);
|
||||
var i = $this.attr("data-i");
|
||||
var j = $this.attr("data-j");
|
||||
var action = self.sections[i].elements[j];
|
||||
var index = $this.attr("data-index").split('-');
|
||||
var action = self.sections[index[0]].elements[index[1]];
|
||||
action.flags = {
|
||||
new_window : true
|
||||
}
|
||||
@@ -334,10 +340,12 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
this.refresh(false);
|
||||
this.do_refresh(false);
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.NullSidebar = openerp.base.generate_null_object_class(openerp.base.Sidebar);
|
||||
|
||||
openerp.base.View = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<t t-foreach="data.children" t-as="menu">
|
||||
<td>
|
||||
<a href="#" t-att-data-menu="menu.id">
|
||||
<span><t t-esc="menu.name"/></span>
|
||||
<t t-esc="menu.name"/>
|
||||
</a>
|
||||
</td>
|
||||
</t>
|
||||
@@ -147,7 +147,7 @@
|
||||
<div t-attf-id="#{prefix}_view_#{view[1]}"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="view-manager-main-sidebar">
|
||||
<td class="view-manager-main-sidebar" height="100%">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -187,9 +187,14 @@
|
||||
</th>
|
||||
</tr>
|
||||
<tr t-if="options.header">
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<th t-if="column.meta">
|
||||
<t t-esc="column.string"/>
|
||||
</th>
|
||||
</t>
|
||||
<th t-if="options.selectable"/>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<th t-if="column.invisible !== '1'" t-att-data-id="column.id"
|
||||
<th t-if="!column.meta and column.invisible !== '1'" t-att-data-id="column.id"
|
||||
t-att-class="((options.sortable and column.tag !== 'button') ? 'oe-sortable' : null)">
|
||||
<t t-if="column.tag !== 'button'">
|
||||
<t t-esc="column.string"/>
|
||||
@@ -210,17 +215,22 @@
|
||||
</t>
|
||||
</t>
|
||||
<tr t-name="ListView.row" t-att-style="style" t-att-class="row_parity">
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
</td>
|
||||
</t>
|
||||
<th t-if="options.selectable" class="oe-record-selector">
|
||||
<input type="checkbox"/>
|
||||
</th>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<t t-set="attrs" t-value="column.attrs_for(row.data)"/>
|
||||
<td t-if="column.invisible !== '1'" t-att-title="column.help"
|
||||
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
|
||||
class="oe-field-cell" t-att-data-field="column.id">
|
||||
<t t-set="attrs" t-value="column.attrs_for(row.data)"/>
|
||||
<t t-if="!attrs.invisible">
|
||||
<t t-set="is_button" t-value="column.tag === 'button'"/>
|
||||
<!-- TODO: get correct widget from form -->
|
||||
<t t-if="!is_button and row['data'][column.id].value">
|
||||
<t t-if="!is_button and row['data'][column.id].value !== false">
|
||||
<t t-set="value" t-value="row['data'][column.id].value"/>
|
||||
<t t-esc="value instanceof Array ? value[1] : value"/>
|
||||
</t>
|
||||
@@ -416,22 +426,28 @@
|
||||
<tr>
|
||||
<td align="center" valign="bottom" height="25">
|
||||
<div class="oe-binary">
|
||||
<!-- TODO: input@type=file inside a button won't work in firefox. Use links instead -->
|
||||
<button class="button oe-binary-file-set" type="button" title="Set Image">
|
||||
<img src="/base/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
||||
<form class="oe-binary-form" t-att-target="widget.iframe"
|
||||
method="post" enctype="multipart/form-data" action="/base/formview/upload">
|
||||
<input type="hidden" name="session_id" value=""/>
|
||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||
</form>
|
||||
</button>
|
||||
<button class="button oe-binary-file-save" type="button" title="Save As">
|
||||
<img src="/base/static/src/img/icons/gtk-save.png"/>
|
||||
</button>
|
||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||
<img src="/base/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
||||
</button>
|
||||
<table cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="oe-binary-file-set" style="width: 40px; height:22px;">
|
||||
<form class="oe-binary-form" t-att-target="widget.iframe"
|
||||
method="post" enctype="multipart/form-data" action="/base/binary/upload">
|
||||
<input type="hidden" name="session_id" value=""/>
|
||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||
<button class="button" type="button" title="Set Image">
|
||||
<img src="/base/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
||||
</button>
|
||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||
<img src="/base/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="oe-binary-progress" style="display: none">
|
||||
<img src="/base/static/src/img/throbber.gif" width="16" height="16"/>
|
||||
@@ -453,25 +469,36 @@
|
||||
/>
|
||||
</td>
|
||||
<td class="oe-binary" nowrap="true">
|
||||
<!-- TODO: input@type=file inside a button won't work in firefox. Use links instead -->
|
||||
<button class="button oe-binary-file-set" type="button" title="Select file">
|
||||
<img src="/base/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
||||
<span>Select</span>
|
||||
<form class="oe-binary-form" t-att-target="widget.iframe"
|
||||
method="post" enctype="multipart/form-data" action="/base/formview/upload">
|
||||
<input type="hidden" name="session_id" value=""/>
|
||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||
</form>
|
||||
</button>
|
||||
<button class="button oe-binary-file-save" type="button" title="Save As">
|
||||
<img src="/base/static/src/img/icons/gtk-save.png"/>
|
||||
<span>Save As</span>
|
||||
</button>
|
||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||
<img src="/base/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
||||
<span>Clear</span>
|
||||
</button>
|
||||
<table cellspacing="0" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="oe-binary-file-set" style="width: 80px; height:22px;">
|
||||
<form class="oe-binary-form" t-att-target="widget.iframe"
|
||||
method="post" enctype="multipart/form-data" action="/base/binary/upload">
|
||||
<input type="hidden" name="session_id" value=""/>
|
||||
<input type="hidden" name="callback" t-att-value="widget.iframe"/>
|
||||
<button class="button" type="button" title="Set Image">
|
||||
<img src="/base/static/src/img/icons/STOCK_DIRECTORY.png"/>
|
||||
<span>Select</span>
|
||||
</button>
|
||||
<input type="file" class="oe-binary-file" name="ufile"/>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="button oe-binary-file-save" type="button" title="Save As">
|
||||
<img src="/base/static/src/img/icons/gtk-save.png"/>
|
||||
<span>Save As</span>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class="button oe-binary-file-clear" type="button" title="Clear">
|
||||
<img src="/base/static/src/img/icons/STOCK_MISSING_IMAGE.png"/>
|
||||
<span>Clear</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="oe-binary-progress" style="display: none" nowrap="true">
|
||||
<img src="/base/static/src/img/throbber.gif" width="16" height="16"/>
|
||||
@@ -671,24 +698,17 @@
|
||||
</t>
|
||||
<t t-name="ViewManager.sidebar.internal">
|
||||
<a class="toggle-sidebar"></a>
|
||||
<div t-att-id="element_id" class="sidebar-sub-div">
|
||||
<div class="sidebar-displaying-div">
|
||||
<t t-set="i" t-value="1-1"/> <!-- al do stupid things -->
|
||||
<t t-foreach="sections" t-as="section">
|
||||
<t t-if="section.elements.length > 0">
|
||||
<h2><t t-esc="section.label"/></h2>
|
||||
<ul>
|
||||
<t t-set="j" t-value="1-1"/>
|
||||
<t t-foreach="section.elements" t-as="element">
|
||||
<li><a class="oe_sidebar_action_a" t-att-data-i="i" t-att-data-j="j" href="#">
|
||||
<t t-esc="element.name"/></a></li>
|
||||
<t t-set="j" t-value="j+1"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
<t t-set="i" t-value="i+1"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
<t t-foreach="sections" t-as="section" t-if="section.elements.length">
|
||||
<h2><t t-esc="section.label"/></h2>
|
||||
<ul>
|
||||
<li t-foreach="section.elements" t-as="element">
|
||||
<a class="oe_sidebar_action_a" t-attf-data-index="#{section_index}-#{element_index}" href="#">
|
||||
<t t-esc="element.name"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="DialogWarning">
|
||||
|
||||
@@ -18,6 +18,15 @@ $(document).ready(function () {
|
||||
}
|
||||
}};
|
||||
|
||||
var fuck_that_shit = {
|
||||
action: {
|
||||
flags: {}
|
||||
},
|
||||
sidebar: {
|
||||
set_toolbar: function () {}
|
||||
}
|
||||
};
|
||||
|
||||
var openerp;
|
||||
module("ListView", {
|
||||
setup: function () {
|
||||
@@ -31,28 +40,27 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
asyncTest('render selection checkboxes', 2, function () {
|
||||
test('render selection checkboxes', 2, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null,
|
||||
fuck_that_shit, null,
|
||||
'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]}).then(function () {
|
||||
ok(are(listview.$element.find('tbody th'),
|
||||
'.oe-record-selector'));
|
||||
ok(are(listview.$element.find('tbody th input'),
|
||||
':checkbox:not([name])'));
|
||||
start();
|
||||
});
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]});
|
||||
|
||||
ok(are(listview.$element.find('tbody th'),
|
||||
'.oe-record-selector'));
|
||||
ok(are(listview.$element.find('tbody th input'),
|
||||
':checkbox:not([name])'));
|
||||
});
|
||||
asyncTest('render no checkbox if selectable=false', 1, function () {
|
||||
test('render no checkbox if selectable=false', 1, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null,
|
||||
fuck_that_shit, null,
|
||||
'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false,
|
||||
{selectable: false});
|
||||
|
||||
@@ -62,33 +70,32 @@ $(document).ready(function () {
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]}).then(function () {
|
||||
equal(listview.$element.find('tbody th').length, 0);
|
||||
start();
|
||||
});
|
||||
]});
|
||||
equal(listview.$element.find('tbody th').length, 0);
|
||||
});
|
||||
asyncTest('select a bunch of records', 2, function () {
|
||||
test('select a bunch of records', 2, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, ids: [1, 2, 3], index: 0});
|
||||
fuck_that_shit, null, 'qunit-fixture',
|
||||
{model: null, ids: [1, 2, 3], index: 0});
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
deepEqual(listview.get_selection(), [3]);
|
||||
listview.$element.find('tbody th input:eq(1)')
|
||||
.attr('checked', true);
|
||||
deepEqual(listview.get_selection(), [2, 3]);
|
||||
start();
|
||||
});
|
||||
]});
|
||||
// TODO: find less intrusive way to get selection count of list view?
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
deepEqual(listview.list.get_selection(), [3]);
|
||||
listview.$element.find('tbody th input:eq(1)')
|
||||
.attr('checked', true);
|
||||
deepEqual(listview.list.get_selection(), [2, 3]);
|
||||
});
|
||||
asyncTest('render deletion button if list is deletable', 1, function () {
|
||||
test('render deletion button if list is deletable', 1, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
|
||||
fuck_that_shit, null, 'qunit-fixture',
|
||||
{model: null, ids: [null, null, null], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
@@ -96,18 +103,17 @@ $(document).ready(function () {
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]}).then(function () {
|
||||
equal(
|
||||
listview.$element.find('tbody tr td.oe-record-delete button').length,
|
||||
3);
|
||||
start();
|
||||
});
|
||||
]});
|
||||
equal(
|
||||
listview.$element.find('tbody tr td.oe-record-delete button').length,
|
||||
3);
|
||||
});
|
||||
asyncTest('deletion button should lead on deletion in the dataset',
|
||||
test('deletion button should lead on deletion in the dataset',
|
||||
2, function () {
|
||||
var deleted;
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
|
||||
fuck_that_shit, null, 'qunit-fixture',
|
||||
{model: null, unlink: function (ids) {
|
||||
deleted = ids;
|
||||
}, ids: [1, 2, 3], index: 0});
|
||||
|
||||
@@ -117,18 +123,17 @@ $(document).ready(function () {
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(2) button').click();
|
||||
deepEqual(deleted, [3]);
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(0) button').click();
|
||||
deepEqual(deleted, [1]);
|
||||
start();
|
||||
});
|
||||
]});
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(2) button').click();
|
||||
deepEqual(deleted, [3]);
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(0) button').click();
|
||||
deepEqual(deleted, [1]);
|
||||
});
|
||||
asyncTest('multiple records deletion', 1, function () {
|
||||
test('multiple records deletion', 1, function () {
|
||||
var deleted;
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
|
||||
fuck_that_shit, null, 'qunit-fixture',
|
||||
{model: null, unlink: function (ids) {
|
||||
deleted = ids;
|
||||
}, ids: [1, 2, 3], index: 0});
|
||||
|
||||
@@ -138,15 +143,13 @@ $(document).ready(function () {
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
listview.$element.find('tbody th input:eq(1)')
|
||||
.attr('checked', true);
|
||||
]});
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
listview.$element.find('tbody th input:eq(1)')
|
||||
.attr('checked', true);
|
||||
|
||||
listview.$element.find('#oe-list-delete').click();
|
||||
deepEqual(deleted, [2, 3]);
|
||||
start();
|
||||
});
|
||||
listview.$element.find('#oe-list-delete').click();
|
||||
deepEqual(deleted, [2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,173 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# setup from TinERP
|
||||
# taken from straw http://www.nongnu.org/straw/index.html
|
||||
# taken from gnomolicious http://www.nongnu.org/gnomolicious/
|
||||
# adapted by Nicolas Évrard <nicoe@altern.org>
|
||||
#
|
||||
# doc/migrate is not included since about 6.1-dev
|
||||
# doc/tests is not included
|
||||
# python25-compat/*py should be in the openerp (and imported appropriately)
|
||||
|
||||
import sys
|
||||
import os
|
||||
from os.path import join, isfile
|
||||
import glob
|
||||
import re
|
||||
import sys
|
||||
from setuptools import setup
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
name = 'openerp-web-proto'
|
||||
version = '6.0.1'
|
||||
description = "Web Client of OpenERP, the Enterprise Management Software"
|
||||
long_description = "OpenERP Web is the web client of the OpenERP, a free enterprise management software"
|
||||
author = "OpenERP S.A."
|
||||
author_email = "info@openerp.com"
|
||||
support_email = 'support@openerp.com'
|
||||
url = "http://www.openerp.com/"
|
||||
download_url = ''
|
||||
license = "OEPL"
|
||||
|
||||
# Backports os.walk with followlinks from python 2.6.
|
||||
# Needed to add all addons files to data_files for Windows packaging.
|
||||
def walk_followlinks(top, topdown=True, onerror=None, followlinks=False):
|
||||
from os.path import join, isdir, islink
|
||||
from os import listdir, error
|
||||
|
||||
try:
|
||||
names = listdir(top)
|
||||
except error, err:
|
||||
if onerror is not None:
|
||||
onerror(err)
|
||||
return
|
||||
|
||||
dirs, nondirs = [], []
|
||||
for name in names:
|
||||
if isdir(join(top, name)):
|
||||
dirs.append(name)
|
||||
else:
|
||||
nondirs.append(name)
|
||||
|
||||
if topdown:
|
||||
yield top, dirs, nondirs
|
||||
for name in dirs:
|
||||
path = join(top, name)
|
||||
if followlinks or not islink(path):
|
||||
for x in walk_followlinks(path, topdown, onerror, followlinks):
|
||||
yield x
|
||||
if not topdown:
|
||||
yield top, dirs, nondirs
|
||||
|
||||
if sys.version_info < (2, 6):
|
||||
os.walk = walk_followlinks
|
||||
|
||||
py2exe_keywords = {}
|
||||
py2exe_data_files = []
|
||||
if os.name == 'nt':
|
||||
version_dash_incompatible = False
|
||||
if 'bdist_rpm' in sys.argv:
|
||||
version_dash_incompatible = True
|
||||
try:
|
||||
import py2exe
|
||||
py2exe_keywords['console'] = [
|
||||
{ "script": "openerp-server",
|
||||
"icon_resources": [(1, join("pixmaps","openerp-icon.ico"))],
|
||||
}]
|
||||
py2exe_keywords['options'] = {
|
||||
"py2exe": {
|
||||
"skip_archive": 1,
|
||||
"optimize": 2,
|
||||
"dist_dir": 'dist',
|
||||
"packages": [
|
||||
"lxml", "lxml.builder", "lxml._elementpath", "lxml.etree",
|
||||
"lxml.objectify", "decimal", "xml", "xml", "xml.dom", "xml.xpath",
|
||||
"encodings", "dateutil", "pychart", "PIL", "pyparsing",
|
||||
"pydot", "asyncore","asynchat", "reportlab", "vobject",
|
||||
"HTMLParser", "select", "mako", "poplib",
|
||||
"imaplib", "smtplib", "email", "yaml", "DAV",
|
||||
"uuid", "commands", "openerp",
|
||||
],
|
||||
"excludes" : ["Tkconstants","Tkinter","tcl"],
|
||||
}
|
||||
}
|
||||
# TODO is it still necessary now that we don't use the library.zip file?
|
||||
def data_files():
|
||||
'''For Windows, we consider all the addons as data files.
|
||||
It seems also that package_data below isn't honored by py2exe.'''
|
||||
files = []
|
||||
os.chdir('openerp')
|
||||
for (dp, dn, names) in os.walk('addons'):
|
||||
files.append((join('openerp',dp), map(lambda x: join('openerp', dp, x), names)))
|
||||
os.chdir('..')
|
||||
files.append(('openerp', [join('openerp', 'import_xml.rng'),]))
|
||||
from py2exe_utils import opts
|
||||
version_dash_incompatible = True
|
||||
except ImportError:
|
||||
opts = {}
|
||||
if version_dash_incompatible:
|
||||
version = version.split('-')[0]
|
||||
|
||||
# copy pytz/timzeone
|
||||
# TODO check if we have to also copy dateutil's timezone data.
|
||||
import pytz
|
||||
# Make sure the layout of pytz hasn't changed
|
||||
assert (pytz.__file__.endswith('__init__.pyc') or
|
||||
pytz.__file__.endswith('__init__.py')), pytz.__file__
|
||||
pytz_dir = os.path.dirname(pytz.__file__)
|
||||
FILE_PATTERNS = \
|
||||
r'.+\.(py|cfg|po|pot|mo|txt|rst|gif|png|jpg|ico|mako|html|js|css|htc|swf)$'
|
||||
def find_data_files(source, patterns=FILE_PATTERNS):
|
||||
file_matcher = re.compile(patterns, re.I)
|
||||
out = []
|
||||
for base, _, files in os.walk(source):
|
||||
cur_files = []
|
||||
for f in files:
|
||||
if file_matcher.match(f):
|
||||
cur_files.append(os.path.join(base, f))
|
||||
if cur_files:
|
||||
out.append(
|
||||
(base, cur_files))
|
||||
|
||||
saved_dir = os.getcwd()
|
||||
os.chdir(pytz_dir)
|
||||
for dp, dn, names in os.walk('zoneinfo'):
|
||||
files.append((join('pytz',dp), map(lambda x: join(pytz_dir, dp, x), names)))
|
||||
os.chdir(saved_dir)
|
||||
return out
|
||||
|
||||
return files
|
||||
py2exe_data_files = data_files()
|
||||
|
||||
execfile(join('openerp', 'release.py'))
|
||||
|
||||
setup(name = name,
|
||||
version = version,
|
||||
description = description,
|
||||
long_description = long_desc,
|
||||
url = url,
|
||||
author = author,
|
||||
author_email = author_email,
|
||||
classifiers = filter(None, classifiers.split("\n")),
|
||||
license = license,
|
||||
data_files = [
|
||||
(join('man', 'man1'), ['man/openerp-server.1']),
|
||||
(join('man', 'man5'), ['man/openerp_serverrc.5']),
|
||||
('doc', filter(isfile, glob.glob('doc/*'))),
|
||||
] + py2exe_data_files,
|
||||
scripts = ['openerp-server'],
|
||||
packages = find_packages(),
|
||||
include_package_data = True,
|
||||
package_data = {
|
||||
'': ['*.yml', '*.xml', '*.po', '*.pot', '*.csv'],
|
||||
},
|
||||
dependency_links = ['http://download.gna.org/pychart/'],
|
||||
install_requires = [
|
||||
# We require the same version as caldav for lxml.
|
||||
'lxml==2.1.5',
|
||||
'mako',
|
||||
'python-dateutil',
|
||||
'psycopg2',
|
||||
# TODO the pychart package we include in openerp corresponds to PyChart 1.37.
|
||||
# It seems there is a single difference, which is a spurious print in generate_docs.py.
|
||||
# It is probably safe to move to PyChart 1.39 (the latest one).
|
||||
# (Let setup.py choose the latest one, and we should check we can remove pychart from
|
||||
# our tree.)
|
||||
'pychart',
|
||||
'pydot',
|
||||
'pytz',
|
||||
'reportlab',
|
||||
'caldav',
|
||||
'pyyaml',
|
||||
'pywebdav',
|
||||
'feedparser',
|
||||
],
|
||||
extras_require = {
|
||||
'SSL' : ['pyopenssl'],
|
||||
},
|
||||
**py2exe_keywords
|
||||
setup(
|
||||
name=name,
|
||||
version=version,
|
||||
description=description,
|
||||
long_description=long_description,
|
||||
author=author,
|
||||
author_email=author_email,
|
||||
url=url,
|
||||
download_url=download_url,
|
||||
license=license,
|
||||
install_requires=[
|
||||
"CherryPy >= 3.1.2",
|
||||
"Babel >= 0.9.4",
|
||||
"simplejson >= 2.0.9",
|
||||
"python-dateutil >= 1.4.1",
|
||||
"pytz",
|
||||
],
|
||||
tests_require=[
|
||||
'unittest2',
|
||||
'mock',
|
||||
],
|
||||
test_suite = 'unittest2.collector',
|
||||
zip_safe=False,
|
||||
packages=[
|
||||
'addons',
|
||||
'addons.base',
|
||||
'addons.base.controllers',
|
||||
'addons.base_calendar',
|
||||
'addons.base_hello',
|
||||
'openerpweb',
|
||||
],
|
||||
classifiers=[
|
||||
'Development Status :: 6 - Production/Stable',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Environment :: Web Environment',
|
||||
'Topic :: Office/Business :: Financial',
|
||||
],
|
||||
scripts=['scripts/openerp-web'],
|
||||
data_files=(find_data_files('addons')
|
||||
+ opts.pop('data_files', [])
|
||||
),
|
||||
**opts
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user