[MERGE] Forward-port of 7.0 bugfixes, up to rev. 3912
revision-id: nicolas.vanhoren@openerp.com-20130430094843-9m1629m517vjtm1o bzr revid: xmo@openerp.com-20130424102025-w33zyopm96r7q09p bzr revid: odo@openerp.com-20130430103337-i9wuj8zf3h41h1ay
This commit is contained in:
commit
ba5710962e
|
@ -13,6 +13,7 @@ This module provides the core of the OpenERP Web Client.
|
|||
'auto_install': True,
|
||||
'post_load': 'wsgi_postload',
|
||||
'js' : [
|
||||
"static/src/fixbind.js",
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
"static/lib/datejs/core.js",
|
||||
"static/lib/datejs/parser.js",
|
||||
|
@ -76,6 +77,7 @@ This module provides the core of the OpenERP Web Client.
|
|||
"static/test/class.js",
|
||||
"static/test/registry.js",
|
||||
"static/test/form.js",
|
||||
"static/test/data.js",
|
||||
"static/test/list-utils.js",
|
||||
"static/test/formats.js",
|
||||
"static/test/rpc.js",
|
||||
|
|
|
@ -643,6 +643,18 @@ class WebClient(openerpweb.Controller):
|
|||
|
||||
content, checksum = concat_files((f[0] for f in files), reader)
|
||||
|
||||
# move up all @import and @charset rules to the top
|
||||
matches = []
|
||||
def push(matchobj):
|
||||
matches.append(matchobj.group(0))
|
||||
return ''
|
||||
|
||||
content = re.sub(re.compile("(@charset.+;$)", re.M), push, content)
|
||||
content = re.sub(re.compile("(@import.+;$)", re.M), push, content)
|
||||
|
||||
matches.append(content)
|
||||
content = '\n'.join(matches)
|
||||
|
||||
return make_conditional(
|
||||
req, req.make_response(content, [('Content-Type', 'text/css')]),
|
||||
last_modified, checksum)
|
||||
|
@ -1363,19 +1375,30 @@ class Binary(openerpweb.Controller):
|
|||
elif dbname is None:
|
||||
dbname = db_monodb(req)
|
||||
|
||||
if uid is None:
|
||||
if not uid:
|
||||
uid = openerp.SUPERUSER_ID
|
||||
|
||||
if not dbname:
|
||||
image_data = self.placeholder(req, 'logo.png')
|
||||
else:
|
||||
registry = openerp.modules.registry.RegistryManager.get(dbname)
|
||||
with registry.cursor() as cr:
|
||||
user = registry.get('res.users').browse(cr, uid, uid)
|
||||
if user.company_id.logo_web:
|
||||
image_data = user.company_id.logo_web.decode('base64')
|
||||
else:
|
||||
image_data = self.placeholder(req, 'nologo.png')
|
||||
try:
|
||||
# create an empty registry
|
||||
registry = openerp.modules.registry.Registry(dbname.lower())
|
||||
with registry.cursor() as cr:
|
||||
cr.execute("""SELECT c.logo_web
|
||||
FROM res_users u
|
||||
LEFT JOIN res_company c
|
||||
ON c.id = u.company_id
|
||||
WHERE u.id = %s
|
||||
""", (uid,))
|
||||
row = cr.fetchone()
|
||||
if row and row[0]:
|
||||
image_data = str(row[0]).decode('base64')
|
||||
else:
|
||||
image_data = self.placeholder(req, 'nologo.png')
|
||||
except Exception:
|
||||
image_data = self.placeholder(req, 'logo.png')
|
||||
|
||||
headers = [
|
||||
('Content-Type', 'image/png'),
|
||||
('Content-Length', len(image_data)),
|
||||
|
@ -1420,7 +1443,7 @@ class Action(openerpweb.Controller):
|
|||
else:
|
||||
return False
|
||||
|
||||
class Export(View):
|
||||
class Export(openerpweb.Controller):
|
||||
_cp_path = "/web/export"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
|
@ -1561,7 +1584,7 @@ class Export(View):
|
|||
(prefix + '/' + k, prefix_string + '/' + v)
|
||||
for k, v in self.fields_info(req, model, export_fields).iteritems())
|
||||
|
||||
#noinspection PyPropertyDefinition
|
||||
class ExportFormat(object):
|
||||
@property
|
||||
def content_type(self):
|
||||
""" Provides the format's content type """
|
||||
|
@ -1609,7 +1632,7 @@ class Export(View):
|
|||
('Content-Type', self.content_type)],
|
||||
cookies={'fileToken': int(token)})
|
||||
|
||||
class CSVExport(Export):
|
||||
class CSVExport(ExportFormat, http.Controller):
|
||||
_cp_path = '/web/export/csv'
|
||||
fmt = {'tag': 'csv', 'label': 'CSV'}
|
||||
|
||||
|
@ -1644,7 +1667,7 @@ class CSVExport(Export):
|
|||
fp.close()
|
||||
return data
|
||||
|
||||
class ExcelExport(Export):
|
||||
class ExcelExport(ExportFormat, http.Controller):
|
||||
_cp_path = '/web/export/xls'
|
||||
fmt = {
|
||||
'tag': 'xls',
|
||||
|
@ -1683,7 +1706,7 @@ class ExcelExport(Export):
|
|||
fp.close()
|
||||
return data
|
||||
|
||||
class Reports(View):
|
||||
class Reports(openerpweb.Controller):
|
||||
_cp_path = "/web/report"
|
||||
POLLING_DELAY = 0.25
|
||||
TYPES_MAPPING = {
|
||||
|
|
|
@ -19,6 +19,7 @@ import time
|
|||
import traceback
|
||||
import urlparse
|
||||
import uuid
|
||||
import errno
|
||||
|
||||
import babel.core
|
||||
import simplejson
|
||||
|
@ -200,7 +201,7 @@ class JsonRequest(WebRequest):
|
|||
_logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest))
|
||||
response['id'] = self.jsonrequest.get('id')
|
||||
response["result"] = method(self, **self.params)
|
||||
except session.AuthenticationError:
|
||||
except session.AuthenticationError, e:
|
||||
se = serialize_exception(e)
|
||||
error = {
|
||||
'code': 100,
|
||||
|
@ -354,17 +355,31 @@ def httprequest(f):
|
|||
addons_module = {}
|
||||
addons_manifest = {}
|
||||
controllers_class = []
|
||||
controllers_class_path = {}
|
||||
controllers_object = {}
|
||||
controllers_object_path = {}
|
||||
controllers_path = {}
|
||||
|
||||
class ControllerType(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(ControllerType, cls).__init__(name, bases, attrs)
|
||||
controllers_class.append(("%s.%s" % (cls.__module__, cls.__name__), cls))
|
||||
name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
|
||||
controllers_class.append(name_class)
|
||||
path = attrs.get('_cp_path')
|
||||
if path not in controllers_class_path:
|
||||
controllers_class_path[path] = name_class
|
||||
|
||||
class Controller(object):
|
||||
__metaclass__ = ControllerType
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
subclasses = [c for c in cls.__subclasses__() if c._cp_path == cls._cp_path]
|
||||
if subclasses:
|
||||
name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
|
||||
cls = type(name, tuple(reversed(subclasses)), {})
|
||||
|
||||
return object.__new__(cls)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Session context manager
|
||||
#----------------------------------------------------------
|
||||
|
@ -476,8 +491,15 @@ def session_path():
|
|||
except Exception:
|
||||
username = "unknown"
|
||||
path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
||||
if not os.path.exists(path):
|
||||
try:
|
||||
os.mkdir(path, 0700)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
# directory exists: ensure it has the correct permissions
|
||||
# this will fail if the directory is not owned by the current user
|
||||
os.chmod(path, 0700)
|
||||
else:
|
||||
raise
|
||||
return path
|
||||
|
||||
class Root(object):
|
||||
|
@ -557,10 +579,11 @@ class Root(object):
|
|||
addons_manifest[module] = manifest
|
||||
self.statics['/%s/static' % module] = path_static
|
||||
|
||||
for k, v in controllers_class:
|
||||
if k not in controllers_object:
|
||||
o = v()
|
||||
controllers_object[k] = o
|
||||
for k, v in controllers_class_path.items():
|
||||
if k not in controllers_object_path and hasattr(v[1], '_cp_path'):
|
||||
o = v[1]()
|
||||
controllers_object[v[0]] = o
|
||||
controllers_object_path[k] = o
|
||||
if hasattr(o, '_cp_path'):
|
||||
controllers_path[o._cp_path] = o
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -123,6 +123,7 @@ $sheet-padding: 16px
|
|||
font-size: 1px
|
||||
letter-spacing: -1px
|
||||
color: transparent
|
||||
text-shadow: none
|
||||
font-weight: normal
|
||||
&:before
|
||||
font: 21px "mnmliconsRegular"
|
||||
|
@ -133,6 +134,7 @@ $sheet-padding: 16px
|
|||
font-size: 1px
|
||||
letter-spacing: -1px
|
||||
color: transparent
|
||||
text-shadow: none
|
||||
font-weight: normal
|
||||
&:before
|
||||
font: $size "entypoRegular"
|
||||
|
@ -1979,9 +1981,9 @@ $sheet-padding: 16px
|
|||
.oe_form_field_float input
|
||||
width: 7em
|
||||
.oe_form_field_date input
|
||||
width: 7.5em
|
||||
width: 100px
|
||||
.oe_form_field_datetime input
|
||||
width: 11.5em
|
||||
width: 150px
|
||||
// }}}
|
||||
// FormView.fields_binary {{{
|
||||
/* http://www.quirksmode.org/dom/inputfile.html
|
||||
|
@ -2558,6 +2560,8 @@ div.ui-widget-overlay
|
|||
.placeholder
|
||||
color: $tag-border !important
|
||||
font-style: italic !important
|
||||
.oe_form_binary_file
|
||||
width: 80px
|
||||
.oe_form_field_boolean input
|
||||
background: #fff
|
||||
.db_option_table .oe_form_field_selection
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Fix old versions of Webkit (such as ones used on iOS < 6 or PhantomJS <= 1.7)
|
||||
// which does not have Function.prototype.bind function
|
||||
|
||||
// Use moz polyfill:
|
||||
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5 internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function () {},
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
|
@ -251,9 +251,9 @@ instance.web.CrashManager = instance.web.Class.extend({
|
|||
if (handler) {
|
||||
new (handler)(this, error).display();
|
||||
return;
|
||||
};
|
||||
}
|
||||
if (error.data.name === "openerp.addons.web.session SessionExpiredException") {
|
||||
this.show_warning({type: "Session Expired", data: { message: "Your OpenERP session expired. Please refresh the current web page." }});
|
||||
this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }});
|
||||
return;
|
||||
}
|
||||
if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning"
|
||||
|
@ -530,16 +530,11 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
|||
'login': 'admin',
|
||||
'password': form_obj['create_admin_pwd'],
|
||||
'login_successful': function() {
|
||||
var action = {
|
||||
type: "ir.actions.client",
|
||||
tag: 'reload',
|
||||
params: {
|
||||
url_search : {
|
||||
db: form_obj['db_name'],
|
||||
},
|
||||
}
|
||||
};
|
||||
self.do_action(action);
|
||||
var url = '/?db=' + form_obj['db_name'];
|
||||
if (self.session.debug) {
|
||||
url += '&debug';
|
||||
}
|
||||
instance.web.redirect(url);
|
||||
},
|
||||
},
|
||||
_push_me: false,
|
||||
|
@ -918,9 +913,7 @@ instance.web.Menu = instance.web.Widget.extend({
|
|||
self.reflow();
|
||||
// launch the fetch of needaction counters, asynchronous
|
||||
if (!_.isEmpty(menu_data.all_menu_ids)) {
|
||||
this.rpc("/web/menu/load_needaction", {menu_ids: menu_data.all_menu_ids}).done(function(r) {
|
||||
self.on_needaction_loaded(r);
|
||||
});
|
||||
this.do_load_needaction(menu_data.all_menu_ids);
|
||||
}
|
||||
});
|
||||
var lazyreflow = _.debounce(this.reflow.bind(this), 200);
|
||||
|
@ -946,7 +939,7 @@ instance.web.Menu = instance.web.Widget.extend({
|
|||
this.data = {data: data};
|
||||
this.renderElement();
|
||||
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
|
||||
this.$el.on('click', 'a[data-menu]', this.on_menu_click);
|
||||
this.$el.on('click', 'a[data-menu]', this.on_top_menu_click);
|
||||
// Hide second level submenus
|
||||
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
|
||||
if (self.current_menu) {
|
||||
|
@ -955,6 +948,12 @@ instance.web.Menu = instance.web.Widget.extend({
|
|||
this.trigger('menu_loaded', data);
|
||||
this.has_been_loaded.resolve();
|
||||
},
|
||||
do_load_needaction: function (menu_ids) {
|
||||
var self = this;
|
||||
return this.rpc("/web/menu/load_needaction", {'menu_ids': menu_ids}).done(function(r) {
|
||||
self.on_needaction_loaded(r);
|
||||
});
|
||||
},
|
||||
on_needaction_loaded: function(data) {
|
||||
var self = this;
|
||||
this.needaction_data = data;
|
||||
|
@ -1086,11 +1085,36 @@ instance.web.Menu = instance.web.Widget.extend({
|
|||
}
|
||||
this.open_menu(id);
|
||||
},
|
||||
do_reload_needaction: function () {
|
||||
var self = this;
|
||||
self.do_load_needaction([self.current_menu]).then(function () {
|
||||
self.trigger("need_action_reloaded");
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Jquery event handler for menu click
|
||||
*
|
||||
* @param {Event} ev the jquery event
|
||||
*/
|
||||
on_top_menu_click: function(ev) {
|
||||
var self = this;
|
||||
var id = $(ev.currentTarget).data('menu');
|
||||
var menu_ids = [id];
|
||||
var menu = _.filter(this.data.data.children, function (menu) {return menu.id == id;})[0];
|
||||
function add_menu_ids (menu) {
|
||||
if (menu.children) {
|
||||
_.each(menu.children, function (menu) {
|
||||
menu_ids.push(menu.id);
|
||||
add_menu_ids(menu);
|
||||
});
|
||||
}
|
||||
};
|
||||
add_menu_ids(menu);
|
||||
self.do_load_needaction(menu_ids).then(function () {
|
||||
self.trigger("need_action_reloaded");
|
||||
});
|
||||
this.on_menu_click(ev);
|
||||
},
|
||||
on_menu_click: function(ev) {
|
||||
ev.preventDefault();
|
||||
var needaction = $(ev.target).is('div.oe_menu_counter');
|
||||
|
|
|
@ -112,24 +112,27 @@ instance.web.Query = instance.web.Class.extend({
|
|||
* @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null}
|
||||
*/
|
||||
group_by: function (grouping) {
|
||||
if (grouping === undefined) {
|
||||
return null;
|
||||
var ctx = instance.web.pyeval.eval(
|
||||
'context', this._model.context(this._context));
|
||||
|
||||
// undefined passed in explicitly (!)
|
||||
if (_.isUndefined(grouping)) {
|
||||
grouping = [];
|
||||
}
|
||||
|
||||
if (!(grouping instanceof Array)) {
|
||||
grouping = _.toArray(arguments);
|
||||
}
|
||||
if (_.isEmpty(grouping)) { return null; }
|
||||
if (_.isEmpty(grouping) && !ctx['group_by_no_leaf']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var ctx = instance.web.pyeval.eval(
|
||||
'context', this._model.context(this._context));
|
||||
return this._model.call('read_group', {
|
||||
groupby: grouping,
|
||||
fields: _.uniq(grouping.concat(this._fields || [])),
|
||||
domain: this._model.domain(this._filter),
|
||||
context: this._model.context(this._context),
|
||||
context: ctx,
|
||||
offset: this._offset,
|
||||
limit: this._limit,
|
||||
orderby: instance.web.serialize_sort(this._order_by) || false
|
||||
|
@ -325,7 +328,7 @@ instance.web.Model = instance.web.Class.extend({
|
|||
* Fetches the model's domain, combined with the provided domain if any
|
||||
*
|
||||
* @param {Array} [domain] to combine with the model's internal domain
|
||||
* @returns The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain
|
||||
* @returns {instance.web.CompoundDomain} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain
|
||||
*/
|
||||
domain: function (domain) {
|
||||
if (!domain) { return this._domain; }
|
||||
|
@ -337,7 +340,7 @@ instance.web.Model = instance.web.Class.extend({
|
|||
* combined with the provided context if any
|
||||
*
|
||||
* @param {Object} [context] to combine with the model's internal context
|
||||
* @returns The union of the user's context and the model's internal context, as well as the provided context if any. In that order.
|
||||
* @returns {instance.web.CompoundContext} The union of the user's context and the model's internal context, as well as the provided context if any. In that order.
|
||||
*/
|
||||
context: function (context) {
|
||||
return new instance.web.CompoundContext(
|
||||
|
@ -604,6 +607,9 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
|
|||
alter_ids: function(n_ids) {
|
||||
this.ids = n_ids;
|
||||
},
|
||||
remove_ids: function (ids) {
|
||||
this.alter_ids(_(this.ids).difference(ids));
|
||||
},
|
||||
/**
|
||||
* Resequence records.
|
||||
*
|
||||
|
@ -701,22 +707,28 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
|
|||
get_domain: function (other_domain) {
|
||||
this._model.domain(other_domain);
|
||||
},
|
||||
alter_ids: function (ids) {
|
||||
this._super(ids);
|
||||
if (this.index !== null && this.index >= this.ids.length) {
|
||||
this.index = this.ids.length > 0 ? this.ids.length - 1 : 0;
|
||||
}
|
||||
},
|
||||
remove_ids: function (ids) {
|
||||
var before = this.ids.length;
|
||||
this._super(ids);
|
||||
if (this._length) {
|
||||
this._length -= (before - this.ids.length);
|
||||
}
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
return this._super(ids).done(function(result) {
|
||||
self.ids = _(self.ids).difference(ids);
|
||||
if (self._length) {
|
||||
self._length -= 1;
|
||||
}
|
||||
if (self.index !== null) {
|
||||
self.index = self.index <= self.ids.length - 1 ?
|
||||
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
|
||||
}
|
||||
self.remove_ids( ids);
|
||||
self.trigger("dataset_changed", ids, callback, error_callback);
|
||||
});
|
||||
},
|
||||
size: function () {
|
||||
if (this._length !== undefined) {
|
||||
if (this._length != null) {
|
||||
return this._length;
|
||||
}
|
||||
return this._super();
|
||||
|
|
|
@ -314,4 +314,34 @@ instance.web.auto_date_to_str = function(value, type) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* performs a half up rounding with arbitrary precision, correcting for float loss of precision
|
||||
* See the corresponding float_round() in server/tools/float_utils.py for more info
|
||||
* @param {Number} the value to be rounded
|
||||
* @param {Number} a non zero precision parameter. eg: 0.01 rounds to two digits.
|
||||
*/
|
||||
instance.web.round_precision = function(value, precision){
|
||||
if(!value){
|
||||
return 0;
|
||||
}else if(!precision){
|
||||
throw new Error('round_precision(...): Cannot round value: '+value+' with a precision of zero (or undefined)');
|
||||
}
|
||||
var normalized_value = value / precision;
|
||||
var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2);
|
||||
var epsilon = Math.pow(2, epsilon_magnitude - 53);
|
||||
normalized_value += normalized_value >= 0 ? epsilon : -epsilon;
|
||||
var rounded_value = Math.round(normalized_value);
|
||||
return rounded_value * precision;
|
||||
};
|
||||
|
||||
/**
|
||||
* performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
|
||||
* See the corresponding float_round() in server/tools/float_utils.py for more info
|
||||
* @param {Number} the value to be rounded
|
||||
* @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
|
||||
*/
|
||||
instance.web.round_decimals = function(value, decimals){
|
||||
return instance.web.round_precision(value, Math.pow(10,-decimals));
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -326,7 +326,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
|||
}
|
||||
},
|
||||
'autocompleteopen': function () {
|
||||
this.$el.autocomplete('widget').css('z-index', 3);
|
||||
this.$el.autocomplete('widget').css('z-index', 1004);
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
@ -1039,7 +1039,9 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
|
|||
facet.values.each(function (v) {
|
||||
var i = _(self.filters).indexOf(v.get('value'));
|
||||
if (i === -1) { return; }
|
||||
$filters.eq(i).addClass('oe_selected');
|
||||
$filters.filter(function () {
|
||||
return Number($(this).data('index')) === i;
|
||||
}).addClass('oe_selected');
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
@ -1129,7 +1131,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
|
|||
});
|
||||
},
|
||||
toggle_filter: function (e) {
|
||||
this.toggle(this.filters[$(e.target).index()]);
|
||||
this.toggle(this.filters[Number($(e.target).data('index'))]);
|
||||
},
|
||||
toggle: function (filter) {
|
||||
this.view.query.toggle(this.make_facet([this.make_value(filter)]));
|
||||
|
@ -1337,20 +1339,22 @@ instance.web.search.CharField = instance.web.search.Field.extend( /** @lends ins
|
|||
}
|
||||
});
|
||||
instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{
|
||||
value_from: function () {
|
||||
if (!this.$el.val()) {
|
||||
return null;
|
||||
}
|
||||
var val = this.parse(this.$el.val()),
|
||||
check = Number(this.$el.val());
|
||||
if (isNaN(val) || val !== check) {
|
||||
this.$el.addClass('error');
|
||||
throw new instance.web.search.Invalid(
|
||||
this.attrs.name, this.$el.val(), this.error_message);
|
||||
}
|
||||
this.$el.removeClass('error');
|
||||
return val;
|
||||
}
|
||||
complete: function (value) {
|
||||
var val = this.parse(value);
|
||||
if (isNaN(val)) { return $.when(); }
|
||||
var label = _.str.sprintf(
|
||||
_t("Search %(field)s for: %(value)s"), {
|
||||
field: '<em>' + this.attrs.string + '</em>',
|
||||
value: '<strong>' + _.str.escapeHTML(value) + '</strong>'});
|
||||
return $.when([{
|
||||
label: label,
|
||||
facet: {
|
||||
category: this.attrs.string,
|
||||
field: this,
|
||||
values: [{label: value, value: val}]
|
||||
}
|
||||
}]);
|
||||
},
|
||||
});
|
||||
/**
|
||||
* @class
|
||||
|
|
|
@ -176,9 +176,9 @@ openerp.testing = {};
|
|||
});
|
||||
|
||||
QUnit.module(testing.current_module + '.' + name, {_oe: options});
|
||||
body(testing.case);
|
||||
body(testing['case']);
|
||||
};
|
||||
testing.case = function (name, options, callback) {
|
||||
testing['case'] = function (name, options, callback) {
|
||||
if (_.isFunction(options)) {
|
||||
callback = options;
|
||||
options = {};
|
||||
|
|
|
@ -91,6 +91,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
init: function(parent, dataset, view_id, options) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.ViewManager = parent;
|
||||
this.set_default_options(options);
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
|
@ -720,6 +721,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return this.save().done(function(result) {
|
||||
self.trigger("save", result);
|
||||
self.to_view_mode();
|
||||
}).then(function(result) {
|
||||
self.ViewManager.ActionManager.__parentedParent.menu.do_reload_needaction();
|
||||
});
|
||||
},
|
||||
on_button_cancel: function(event) {
|
||||
|
@ -765,7 +768,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
this.has_been_loaded.done(function() {
|
||||
if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
|
||||
self.dataset.unlink([self.datarecord.id]).done(function() {
|
||||
self.execute_pager_action('next');
|
||||
if (self.dataset.size()) {
|
||||
self.execute_pager_action('next');
|
||||
} else {
|
||||
self.do_action('history_back');
|
||||
}
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
|
@ -802,6 +809,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
if (save_obj.error)
|
||||
return $.Deferred().reject();
|
||||
return $.when.apply($, save_obj.ret);
|
||||
}).done(function() {
|
||||
self.$el.removeClass('oe_form_dirty');
|
||||
});
|
||||
},
|
||||
_process_save: function(save_obj) {
|
||||
|
@ -1021,7 +1030,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return value;
|
||||
}
|
||||
var fields = _.chain(this.fields)
|
||||
.map(function (field, name) {
|
||||
.map(function (field) {
|
||||
var value = field.get_value();
|
||||
// ignore fields which are empty, invisible, readonly, o2m
|
||||
// or m2m
|
||||
|
@ -1036,7 +1045,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
name: field.name,
|
||||
string: field.string,
|
||||
value: value,
|
||||
displayed: display(field, value),
|
||||
|
@ -1047,10 +1056,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
.value();
|
||||
var conditions = _.chain(self.fields)
|
||||
.filter(function (field) { return field.field.change_default; })
|
||||
.map(function (field, name) {
|
||||
.map(function (field) {
|
||||
var value = field.get_value();
|
||||
return {
|
||||
name: name,
|
||||
name: field.name,
|
||||
string: field.string,
|
||||
value: value,
|
||||
displayed: display(field, value),
|
||||
|
@ -1444,6 +1453,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
$(this).children().each(function() {
|
||||
var $td = $(this),
|
||||
$child = $td.children(':first');
|
||||
if ($child.attr('cell-class')) {
|
||||
$td.addClass($child.attr('cell-class'));
|
||||
}
|
||||
switch ($child[0].tagName.toLowerCase()) {
|
||||
case 'separator':
|
||||
break;
|
||||
|
@ -1520,7 +1532,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
if (! page.__ic)
|
||||
return;
|
||||
page.__ic.on("change:effective_invisible", null, function() {
|
||||
if (!page.__ic.get('effective_invisible')) {
|
||||
if (!page.__ic.get('effective_invisible') && page.autofocus) {
|
||||
$new_notebook.tabs('select', i);
|
||||
return;
|
||||
}
|
||||
|
@ -2126,7 +2138,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
value without triggering a re-rendering.
|
||||
*/
|
||||
internal_set_value: function(value_) {
|
||||
var tmp = this.no_render;
|
||||
var tmp = this.no_rerender;
|
||||
this.no_rerender = true;
|
||||
this.set({'value': value_});
|
||||
this.no_rerender = tmp;
|
||||
|
@ -2296,7 +2308,8 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
|
|||
return this.get('value') === '' || this._super();
|
||||
},
|
||||
focus: function() {
|
||||
this.$('input:first')[0].focus();
|
||||
var input = this.$('input:first')[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
|
@ -2393,7 +2406,8 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
|
|||
this._super.apply(this, [value_]);
|
||||
},
|
||||
focus: function () {
|
||||
this.$('input:first').select();
|
||||
var $input = this.$('input:first');
|
||||
return $input.length ? $input.select() : false;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2413,6 +2427,42 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
|||
this.$input = this.$el.find('input.oe_datepicker_master');
|
||||
this.$input_picker = this.$el.find('input.oe_datepicker_container');
|
||||
|
||||
$.datepicker.setDefaults({
|
||||
clearText: _t('Clear'),
|
||||
clearStatus: _t('Erase the current date'),
|
||||
closeText: _t('Done'),
|
||||
closeStatus: _t('Close without change'),
|
||||
prevText: _t('<Prev'),
|
||||
prevStatus: _t('Show the previous month'),
|
||||
nextText: _t('Next>'),
|
||||
nextStatus: _t('Show the next month'),
|
||||
currentText: _t('Today'),
|
||||
currentStatus: _t('Show the current month'),
|
||||
monthNames: Date.CultureInfo.monthNames,
|
||||
monthNamesShort: Date.CultureInfo.abbreviatedMonthNames,
|
||||
monthStatus: _t('Show a different month'),
|
||||
yearStatus: _t('Show a different year'),
|
||||
weekHeader: _t('Wk'),
|
||||
weekStatus: _t('Week of the year'),
|
||||
dayNames: Date.CultureInfo.dayNames,
|
||||
dayNamesShort: Date.CultureInfo.abbreviatedDayNames,
|
||||
dayNamesMin: Date.CultureInfo.shortestDayNames,
|
||||
dayStatus: _t('Set DD as first week day'),
|
||||
dateStatus: _t('Select D, M d'),
|
||||
firstDay: Date.CultureInfo.firstDayOfWeek,
|
||||
initStatus: _t('Select a date'),
|
||||
isRTL: false
|
||||
});
|
||||
$.timepicker.setDefaults({
|
||||
timeOnlyTitle: _t('Choose Time'),
|
||||
timeText: _t('Time'),
|
||||
hourText: _t('Hour'),
|
||||
minuteText: _t('Minute'),
|
||||
secondText: _t('Second'),
|
||||
currentText: _t('Now'),
|
||||
closeText: _t('Done')
|
||||
});
|
||||
|
||||
this.picker({
|
||||
onClose: this.on_picker_select,
|
||||
onSelect: this.on_picker_select,
|
||||
|
@ -2539,9 +2589,8 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
|
|||
return this.get('value') === '' || this._super();
|
||||
},
|
||||
focus: function() {
|
||||
if (this.datewidget && this.datewidget.$input) {
|
||||
this.datewidget.$input[0].focus();
|
||||
}
|
||||
var input = this.datewidget && this.datewidget.$input[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
|
@ -2622,9 +2671,8 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
|
|||
return this.get('value') === '' || this._super();
|
||||
},
|
||||
focus: function($el) {
|
||||
if (!this.get("effective_readonly") && this.$textarea) {
|
||||
this.$textarea[0].focus();
|
||||
}
|
||||
var input = !this.get("effective_readonly") && this.$textarea && this.$textarea[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
|
@ -2707,7 +2755,8 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
|
|||
this.$checkbox[0].checked = this.get('value');
|
||||
},
|
||||
focus: function() {
|
||||
this.$checkbox[0].focus();
|
||||
var input = this.$checkbox && this.$checkbox[0];
|
||||
return input ? input.focus() : false;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2793,7 +2842,8 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan
|
|||
}
|
||||
},
|
||||
focus: function() {
|
||||
this.$('select:first')[0].focus();
|
||||
var input = this.$('select:first')[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
|
@ -3336,7 +3386,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
}
|
||||
if (! no_recurse) {
|
||||
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
|
||||
dataset.name_get([self.get("value")]).done(function(data) {
|
||||
this.alive(dataset.name_get([self.get("value")])).done(function(data) {
|
||||
self.display_value["" + self.get("value")] = data[0][1];
|
||||
self.render_value(true);
|
||||
});
|
||||
|
@ -3401,9 +3451,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
return ! this.get("value");
|
||||
},
|
||||
focus: function () {
|
||||
if (!this.get('effective_readonly')) {
|
||||
this.$input && this.$input[0].focus();
|
||||
}
|
||||
var input = !this.get('effective_readonly') && this.$input && this.$input[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
_quick_create: function() {
|
||||
this.no_ed = true;
|
||||
|
@ -4263,7 +4312,8 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
|
|||
this.set({'value': _.uniq(this.get('value').concat([id]))});
|
||||
},
|
||||
focus: function () {
|
||||
this.$text[0].focus();
|
||||
var input = this.$text && this.$text[0];
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
|
|
|
@ -578,7 +578,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
this.no_leaf = !!context['group_by_no_leaf'];
|
||||
this.grouped = !!group_by;
|
||||
|
||||
return this.load_view(context).then(
|
||||
return this.alive(this.load_view(context)).then(
|
||||
this.proxy('reload_content'));
|
||||
},
|
||||
/**
|
||||
|
@ -895,8 +895,9 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
|
||||
this.record_callbacks = {
|
||||
'remove': function (event, record) {
|
||||
var $row = self.$current.children(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var id = record.get('id');
|
||||
self.dataset.remove_ids([id])
|
||||
var $row = self.$current.children('[data-id=' + id + ']');
|
||||
var index = $row.data('index');
|
||||
$row.remove();
|
||||
},
|
||||
|
|
|
@ -521,6 +521,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
|||
return x;
|
||||
}
|
||||
});
|
||||
this.ActionManager = parent;
|
||||
this.views = {};
|
||||
this.flags = flags || {};
|
||||
this.registry = instance.web.views;
|
||||
|
@ -1004,7 +1005,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
|
|||
switch_mode: function (view_type, no_store, options) {
|
||||
var self = this;
|
||||
|
||||
return $.when(this._super.apply(this, arguments)).done(function () {
|
||||
return this.alive($.when(this._super.apply(this, arguments))).done(function () {
|
||||
var controller = self.views[self.active_view].controller;
|
||||
self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
|
||||
view: controller,
|
||||
|
@ -1252,6 +1253,7 @@ instance.web.View = instance.web.Widget.extend({
|
|||
view_type: undefined,
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
this._super(parent);
|
||||
this.ViewManager = parent;
|
||||
this.dataset = dataset;
|
||||
this.view_id = view_id;
|
||||
this.set_default_options(options);
|
||||
|
@ -1323,7 +1325,6 @@ instance.web.View = instance.web.Widget.extend({
|
|||
}
|
||||
};
|
||||
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
||||
|
||||
var handler = function (action) {
|
||||
if (action && action.constructor == Object) {
|
||||
var ncontext = new instance.web.CompoundContext(context);
|
||||
|
@ -1360,7 +1361,11 @@ instance.web.View = instance.web.Widget.extend({
|
|||
}
|
||||
}
|
||||
args.push(context);
|
||||
return dataset.call_button(action_data.name, args).then(handler);
|
||||
return dataset.call_button(action_data.name, args).then(handler).then(function () {
|
||||
if (self.ViewManager.ActionManager) {
|
||||
self.ViewManager.ActionManager.__parentedParent.menu.do_reload_needaction();
|
||||
}
|
||||
});
|
||||
} else if (action_data.type=="action") {
|
||||
return this.rpc('/web/action/load', {
|
||||
action_id: action_data.name,
|
||||
|
|
|
@ -650,7 +650,7 @@
|
|||
<li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
|
||||
<t t-call="HiddenInputFile">
|
||||
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
|
||||
<t t-set="fileupload_action">/web/binary/upload_attachment</t>
|
||||
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
|
||||
<input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/>
|
||||
<input type="hidden" name="id" t-att-value="widget.model_id"/>
|
||||
<input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
|
||||
|
@ -1552,8 +1552,9 @@
|
|||
<t t-esc="attrs.string"/>
|
||||
</button>
|
||||
<ul t-name="SearchView.filters">
|
||||
<li t-foreach="widget.filters" t-as="filter" t-if="filter.visible()"
|
||||
t-att-title="filter.attrs.string ? filter.attrs.help : undefined">
|
||||
<li t-foreach="widget.filters" t-as="filter" t-if="!filter.visible || filter.visible()"
|
||||
t-att-title="filter.attrs.string ? filter.attrs.help : undefined"
|
||||
t-att-data-index="filter_index">
|
||||
<t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
openerp.testing.section('data.model.group_by', {
|
||||
rpc: 'mock',
|
||||
dependencies: ['web.data'],
|
||||
}, function (test) {
|
||||
var group_result = [{
|
||||
bar: 3, bar_count: 5, __context: {}, __domain: [['bar', '=', 3]],
|
||||
}, {
|
||||
bar: 5, bar_count: 3, __context: {}, __domain: [['bar', '=', 5]],
|
||||
}, {
|
||||
bar: 8, bar_count: 0, __context: {}, __domain: [['bar', '=', 8]],
|
||||
}];
|
||||
test('basic', {asserts: 7}, function (instance, $fix, mock) {
|
||||
var m = new instance.web.Model('foo');
|
||||
mock('foo:read_group', function (args, kwargs) {
|
||||
deepEqual(kwargs.fields, ['bar'],
|
||||
"should read grouping field");
|
||||
deepEqual(kwargs.groupby, ['bar'],
|
||||
"should have single grouping field");
|
||||
return group_result;
|
||||
});
|
||||
mock('/web/dataset/search_read', function (args) {
|
||||
deepEqual(args.params.domain, [['bar', '=', 3]],
|
||||
"should have domain matching that of group_by result");
|
||||
return {records: [
|
||||
{bar: 3, id: 1},
|
||||
{bar: 3, id: 2},
|
||||
{bar: 3, id: 4},
|
||||
{bar: 3, id: 8},
|
||||
{bar: 3, id: 16}
|
||||
], length: 5};
|
||||
});
|
||||
|
||||
return m.query().group_by('bar')
|
||||
.then(function (groups) {
|
||||
ok(groups, "should have data");
|
||||
equal(groups.length, 3, "should have three results");
|
||||
var first = groups[0];
|
||||
ok(first.attributes.has_children, "should have children");
|
||||
return first.query().all();
|
||||
}).done(function (first) {
|
||||
equal(first.length, 5, "should have 5 records")
|
||||
});
|
||||
});
|
||||
test('noleaf', {asserts: 5}, function (instance, $fix, mock) {
|
||||
var m = new instance.web.Model('foo', {group_by_no_leaf: true});
|
||||
mock('foo:read_group', function (args, kwargs) {
|
||||
deepEqual(kwargs.fields, ['bar'],
|
||||
"should read grouping field");
|
||||
deepEqual(kwargs.groupby, ['bar'],
|
||||
"should have single grouping field");
|
||||
|
||||
return group_result;
|
||||
});
|
||||
return m.query().group_by('bar')
|
||||
.then(function (groups) {
|
||||
ok(groups, "should have data");
|
||||
equal(groups.length, 3, "should have three results");
|
||||
ok(!groups[0].attributes.has_children,
|
||||
"should not have children because no_leaf");
|
||||
})
|
||||
});
|
||||
test('nogroup', {rpc: false}, function (instance, $f, mock) {
|
||||
var m = new instance.web.Model('foo');
|
||||
strictEqual(m.query().group_by(), null, "should not group");
|
||||
});
|
||||
test('empty.noleaf', {asserts: 1}, function (instance, $f, mock) {
|
||||
var m = new instance.web.Model('foo', {group_by_no_leaf: true});
|
||||
mock('foo:read_group', function (args, kwargs) {
|
||||
return [{__context: [], __domain: []}];
|
||||
});
|
||||
return m.query().group_by().done(function (groups) {
|
||||
strictEqual(groups.length, 1,
|
||||
"should generate a single fake-ish group");
|
||||
});
|
||||
});
|
||||
});
|
|
@ -614,6 +614,59 @@ openerp.testing.section('search.completions', {
|
|||
{relation: 'dummy.model'}, view);
|
||||
return f.complete("bob");
|
||||
});
|
||||
test('Integer: invalid', {asserts: 1}, function (instance) {
|
||||
var view = {inputs: []};
|
||||
var f = new instance.web.search.IntegerField(
|
||||
{attrs: {string: "Dummy"}}, {}, view);
|
||||
return f.complete("qux")
|
||||
.done(function (completions) {
|
||||
ok(!completions, "non-number => no completion");
|
||||
});
|
||||
});
|
||||
test('Integer: non-zero', {asserts: 5}, function (instance) {
|
||||
var view = {inputs: []};
|
||||
var f = new instance.web.search.IntegerField(
|
||||
{attrs: {string: "Dummy"}}, {}, view);
|
||||
return f.complete("-2")
|
||||
.done(function (completions) {
|
||||
equal(completions.length, 1, "number fields provide 1 completion only");
|
||||
var facet = new instance.web.search.Facet(completions[0].facet);
|
||||
equal(facet.get('category'), f.attrs.string);
|
||||
equal(facet.get('field'), f);
|
||||
var value = facet.values.at(0);
|
||||
equal(value.get('label'), "-2");
|
||||
equal(value.get('value'), -2);
|
||||
});
|
||||
});
|
||||
test('Integer: zero', {asserts: 3}, function (instance) {
|
||||
var view = {inputs: []};
|
||||
var f = new instance.web.search.IntegerField(
|
||||
{attrs: {string: "Dummy"}}, {}, view);
|
||||
return f.complete("0")
|
||||
.done(function (completions) {
|
||||
equal(completions.length, 1, "number fields provide 1 completion only");
|
||||
var facet = new instance.web.search.Facet(completions[0].facet);
|
||||
var value = facet.values.at(0);
|
||||
equal(value.get('label'), "0");
|
||||
equal(value.get('value'), 0);
|
||||
});
|
||||
});
|
||||
test('Float: non-zero', {asserts: 5}, function (instance) {
|
||||
var view = {inputs: []};
|
||||
var f = new instance.web.search.FloatField(
|
||||
{attrs: {string: "Dummy"}}, {}, view);
|
||||
return f.complete("42.37")
|
||||
.done(function (completions) {
|
||||
equal(completions.length, 1, "float fields provide 1 completion only");
|
||||
var facet = new instance.web.search.Facet(completions[0].facet);
|
||||
equal(facet.get('category'), f.attrs.string);
|
||||
equal(facet.get('field'), f);
|
||||
var value = facet.values.at(0);
|
||||
equal(value.get('label'), "42.37");
|
||||
equal(value.get('value'), 42.37);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
openerp.testing.section('search.serialization', {
|
||||
dependencies: ['web.search'],
|
||||
|
@ -1361,7 +1414,7 @@ openerp.testing.section('search.invisible', {
|
|||
}, ['<search>',
|
||||
'<field name="field0"/>',
|
||||
'<field name="field1" modifiers="{"invisible": true}"/>',
|
||||
'</search>'].join());
|
||||
'</search>'].join(''));
|
||||
return view.appendTo($fix)
|
||||
.then(function () {
|
||||
var done = $.Deferred();
|
||||
|
@ -1380,7 +1433,7 @@ openerp.testing.section('search.invisible', {
|
|||
'<search>',
|
||||
'<filter string="filter 0"/>',
|
||||
'<filter string="filter 1" modifiers="{"invisible": true}"/>',
|
||||
'</search>'].join());
|
||||
'</search>'].join(''));
|
||||
return view.appendTo($fix)
|
||||
.then(function () {
|
||||
var $fs = $fix.find('.oe_searchview_filters ul');
|
||||
|
@ -1400,6 +1453,26 @@ openerp.testing.section('search.invisible', {
|
|||
return done;
|
||||
});
|
||||
});
|
||||
test('invisible-previous-sibling', {asserts: 3}, function (instance, $fix, mock) {
|
||||
var view = makeView(instance, mock, {}, [
|
||||
'<search>',
|
||||
'<filter string="filter 0" context="{"test": 0}"/>',
|
||||
'<filter string="filter 1" modifiers="{"invisible": true}" context="{"test": 1}"/>',
|
||||
'<filter string="filter 2" modifiers="{"invisible": true}" context="{"test": 2}"/>',
|
||||
'<filter string="filter 3" context="{"test": 3}"/>',
|
||||
'</search>'].join(''));
|
||||
return view.appendTo($fix)
|
||||
.done(function () {
|
||||
// Select filter 3
|
||||
$fix.find('.oe_searchview_filters ul li:contains("filter 3")').click();
|
||||
equal(view.query.length, 1, "should have selected a filter");
|
||||
var facet = view.query.at(0);
|
||||
strictEqual(facet.values.at(0).get('label'), "filter 3",
|
||||
"should have correctly labelled the facet");
|
||||
deepEqual(view.build_search_data().contexts, [{test: 3}],
|
||||
"should have built correct context");
|
||||
});
|
||||
});
|
||||
// Invisible filter groups should not appear in the drawer
|
||||
// Group invisibility should be inherited by children
|
||||
test('group-invisibility', {asserts: 6}, function (instance, $fix, mock) {
|
||||
|
|
|
@ -265,6 +265,12 @@ instance.web_calendar.CalendarView = instance.web.View.extend({
|
|||
//To parse Events we have to convert date Format
|
||||
var res_events = [],
|
||||
sidebar_items = {};
|
||||
var selection_label = {};
|
||||
if(this.fields[this.color_field].selection) {
|
||||
_(this.fields[this.color_field].selection).each(function(value){
|
||||
selection_label[value[0]] = value[1];
|
||||
});
|
||||
}
|
||||
for (var e = 0; e < events.length; e++) {
|
||||
var evt = events[e];
|
||||
if (!evt[this.date_start]) {
|
||||
|
@ -274,6 +280,9 @@ instance.web_calendar.CalendarView = instance.web.View.extend({
|
|||
if (this.color_field) {
|
||||
var filter = evt[this.color_field];
|
||||
if (filter) {
|
||||
if(this.fields[this.color_field].selection) {
|
||||
filter = selection_label[filter];
|
||||
}
|
||||
var filter_value = (typeof filter === 'object') ? filter[0] : filter;
|
||||
if (typeof(fn_filter) === 'function' && !fn_filter(filter_value)) {
|
||||
continue;
|
||||
|
@ -339,9 +348,13 @@ instance.web_calendar.CalendarView = instance.web.View.extend({
|
|||
},
|
||||
get_event_data: function(event_obj) {
|
||||
var data = {
|
||||
name: event_obj.text
|
||||
name: event_obj.text || scheduler.locale.labels.new_event
|
||||
};
|
||||
data[this.date_start] = instance.web.datetime_to_str(event_obj.start_date);
|
||||
if (this.fields[this.date_start].type == 'date') {
|
||||
data[this.date_start] = instance.web.date_to_str(event_obj.start_date)
|
||||
}else {
|
||||
data[this.date_start] = instance.web.datetime_to_str(event_obj.start_date)
|
||||
}
|
||||
if (this.date_stop) {
|
||||
data[this.date_stop] = instance.web.datetime_to_str(event_obj.end_date);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import openerp
|
||||
|
||||
class DiagramView(openerp.addons.web.controllers.main.View):
|
||||
class DiagramView(openerp.addons.web.http.Controller):
|
||||
_cp_path = "/web_diagram/diagram"
|
||||
|
||||
@openerp.addons.web.http.jsonrequest
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -80,6 +80,8 @@
|
|||
position: relative
|
||||
top: +8px
|
||||
font-weight: bold
|
||||
.oe_kanban_header:hover .oe_kanban_group_length
|
||||
display: none
|
||||
.ui-sortable-placeholder
|
||||
border: 1px solid rgba(0,0,0,0.1)
|
||||
visibility: visible !important
|
||||
|
|
|
@ -261,10 +261,13 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
|
|||
var remaining = groups.length - 1,
|
||||
groups_array = [];
|
||||
return $.when.apply(null, _.map(groups, function (group, index) {
|
||||
var def = $.when([]);
|
||||
var dataset = new instance.web.DataSetSearch(self, self.dataset.model,
|
||||
new instance.web.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain());
|
||||
return dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit })
|
||||
.then(function (records) {
|
||||
if (group.attributes.length >= 1) {
|
||||
def = dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit });
|
||||
}
|
||||
return def.then(function(records) {
|
||||
self.nb_records += records.length;
|
||||
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
|
||||
groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset);
|
||||
|
|
|
@ -15,7 +15,7 @@ instance.web.ViewManagerAction.include({
|
|||
}
|
||||
evt.currentTarget.selectedIndex = 0;
|
||||
}else{
|
||||
return this._super.apply(this,arguments);
|
||||
return this._super.apply(this,arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -232,10 +232,11 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
|
|||
return main_object;
|
||||
},
|
||||
parse_xml: function(arch, view_id) {
|
||||
//First element of att_list must be element tagname.
|
||||
main_object = {
|
||||
'level': 0,
|
||||
'id': this.xml_element_id +=1,
|
||||
'att_list': [],
|
||||
'att_list': ["view"],
|
||||
'name': _.str.sprintf("<view view_id = %s>", view_id),
|
||||
'child_id': []
|
||||
};
|
||||
|
@ -535,15 +536,22 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
|
|||
var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
|
||||
parent_tr = self.get_object_by_id(parseInt($(parent_tr).attr('id').replace(/[^0-9]+/g, '')), this.one_object['main_object'], [])[0].att_list[0];
|
||||
_.each([tr, parent_tr],function(element) {
|
||||
var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
|
||||
var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
|
||||
property_to_check.push(value);
|
||||
});
|
||||
field_dataset.call( 'fields_get', []).done(function(result) {
|
||||
var fields = _.keys(result);
|
||||
fields.push(" "),fields.sort();
|
||||
self.on_add_node(property_to_check, fields);
|
||||
self.on_add_node(property_to_check, fields, self.inject_position(parent_tr,tr));
|
||||
});
|
||||
},
|
||||
inject_position : function(parent_tag,current_tag){
|
||||
if(parent_tag == "view")
|
||||
return ['Inside'];
|
||||
if(current_tag == "field")
|
||||
return ['After','Before'];
|
||||
return ['After','Before','Inside'];
|
||||
},
|
||||
do_node_edit: function(side) {
|
||||
var self = this;
|
||||
var result = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], []);
|
||||
|
@ -637,12 +645,12 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
|
|||
var children = _.filter(xml_arch.childNodes[0].childNodes, function (child) {
|
||||
return child.nodeType == 1;
|
||||
});
|
||||
arch.arch = _.detect(children, function(xml_child) {
|
||||
var inherited_view = _.detect(children, function(xml_child) {
|
||||
var temp_obj = self.create_View_Node(xml_child),
|
||||
insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list));
|
||||
if (insert.length == _.uniq(check_list).length ) {return xml_child;}
|
||||
});
|
||||
xml_arch = QWeb.load_xml(arch.arch);
|
||||
xml_arch = QWeb.load_xml(instance.web.xml_to_str(inherited_view));
|
||||
}
|
||||
return self.do_save_xml(xml_arch.documentElement, obj[0].child_id[0],obj[0].child_id, move_direct, update_values,arch);
|
||||
},
|
||||
|
@ -941,11 +949,11 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
|
|||
});
|
||||
return def.promise();
|
||||
},
|
||||
on_add_node: function(properties, fields){
|
||||
on_add_node: function(properties, fields, position){
|
||||
var self = this;
|
||||
var render_list = [{'name': 'node_type','selection': _.keys(_CHILDREN).sort(), 'value': 'field', 'string': 'Node Type','type': 'selection'},
|
||||
{'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'},
|
||||
{'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}];
|
||||
{'name': 'position','selection': position, 'value': false, 'string': 'Position','type': 'selection'}];
|
||||
this.add_widget = [];
|
||||
this.add_node_dialog = new instance.web.Dialog(this,{
|
||||
title: _t("Properties"),
|
||||
|
@ -1186,7 +1194,7 @@ var _CHILDREN = {
|
|||
//e.g.:xyz 'td' : ['field']
|
||||
};
|
||||
// Generic html_tag list and can be added html tag in future. It's support above _CHILDREN dict's *html_tag* by default.
|
||||
// For specific child node one has to define tag above and specify children tag in list. Like above xyz example.
|
||||
// For specific child node one has to define tag above and specify children tag in list. Like above xyz example.
|
||||
var html_tag = ['div','h1','h2','h3','h4','h5','h6','td','tr'];
|
||||
|
||||
var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
|
||||
|
|
Loading…
Reference in New Issue