openerp.web.list = function (instance) {
var _t = instance.web._t,
- _lt = instance.web._lt;
+ _lt = instance.web._lt;
var QWeb = instance.web.qweb;
instance.web.views.add('list', 'instance.web.ListView');
instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListView# */ {
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
+ //whether the editable property of the view has to be disabled
+ 'disable_editable_mode': false,
+ },
+ view_type: 'tree',
+ events: {
+ 'click thead th.oe_sortable[data-id]': 'sort_by_column'
},
/**
* Core class for list-type displays.
});
this.no_leaf = false;
- this.on('view_load', self, self.load_list);
+ this.grouped = false;
+ },
+ view_loading: function(r) {
+ return this.load_list(r);
},
set_default_options: function (options) {
this._super(options);
*/
start: function() {
this.$el.addClass('oe_list');
- return this.reload_view(null, null, true);
+ return this._super();
},
/**
* Returns the style for the provided record in the current view (from the
* @param {Object} data.fields_view.arch current list view descriptor
* @param {Boolean} grouped Is the list view grouped
*/
- load_list: function(data, grouped) {
+ load_list: function(data) {
var self = this;
this.fields_view = data;
this.name = "" + this.fields_view.arch.attrs.string;
}).value();
}
- this.setup_columns(this.fields_view.fields, grouped);
+ this.setup_columns(this.fields_view.fields, this.grouped);
this.$el.html(QWeb.render(this._template, this));
this.$el.addClass(this.fields_view.arch.attrs['class']);
- // add css classes that reflect the (absence of) access rights
- this.$el.toggleClass('oe_list_cannot_create', !this.is_action_enabled('create'))
- .toggleClass('oe_list_cannot_edit', !this.is_action_enabled('edit'))
- .toggleClass('oe_list_cannot_delete', !this.is_action_enabled('delete'));
-
// Head hook
// Selecting records
this.$el.find('.oe_list_record_selector').click(function(){
'selected', [selection.ids, selection.records]);
});
- // Sorting columns
- this.$el.find('thead').delegate('th.oe_sortable[data-id]', 'click', function (e) {
- e.stopPropagation();
- var $this = $(this);
- self.dataset.sort($this.data('id'));
- if($this.hasClass("sortdown") || $this.hasClass("sortup")) {
- $this.toggleClass("sortdown").toggleClass("sortup");
- } else {
- $this.toggleClass("sortdown");
- }
- $this.siblings('.oe_sortable').removeClass("sortup sortdown");
-
- self.reload_content();
- });
-
// Add button
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
}
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
- .prop('disabled', grouped);
+ .prop('disabled', this.grouped);
}
// Pager
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.sidebar.$el.hide();
}
- this.trigger('list_view_loaded', data, grouped);
+ //Sort
+ if(this.dataset._sort.length){
+ if(this.dataset._sort[0].indexOf('-') == -1){
+ this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
+ }else {
+ this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
+ }
+ }
+ this.trigger('list_view_loaded', data, this.grouped);
+ },
+ sort_by_column: function (e) {
+ e.stopPropagation();
+ var $column = $(e.currentTarget);
+ this.dataset.sort($column.data('id'));
+ if($column.hasClass("sortdown") || $column.hasClass("sortup")) {
+ $column.toggleClass("sortup sortdown");
+ } else {
+ $column.addClass("sortdown");
+ }
+ $column.siblings('.oe_sortable').removeClass("sortup sortdown");
+
+ this.reload_content();
},
/**
* Configures the ListView pager based on the provided dataset's information
var total = dataset.size();
var limit = this.limit() || total;
- this.$pager.toggle(total !== 0);
+ if (total == 0)
+ this.$pager.hide();
+ else
+ this.$pager.css("display", "");
this.$pager.toggleClass('oe_list_pager_single_page', (total <= limit));
var spager = '-';
if (total) {
if (range_stop > total) {
range_stop = total;
}
- spager = _.str.sprintf('%d-%d of %d', range_start, range_stop, total);
+ spager = _.str.sprintf(_t("%d-%d of %d"), range_start, range_stop, total);
}
this.$pager.find('.oe_list_pager_state').text(spager);
/**
* Reloads the list view based on the current settings (dataset & al)
*
+ * @deprecated
* @param {Boolean} [grouped] Should the list be displayed grouped
* @param {Object} [context] context to send the server while loading the view
*/
reload_view: function (grouped, context, initial) {
- var self = this;
- var callback = function (field_view_get) {
- self.load_list(field_view_get, grouped);
- };
- if (this.embedded_view) {
- return $.Deferred().then(callback).resolve(this.embedded_view);
- } else {
- return this.rpc('/web/view/load', {
- model: this.model,
- view_id: this.view_id,
- view_type: "tree",
- context: this.dataset.get_context(context),
- toolbar: !!this.options.$sidebar
- }).then(callback);
- }
+ return this.load_view(context);
},
/**
* re-renders the content of the list view
return this.reload_content();
},
reload_record: function (record) {
+ var self = this;
return this.dataset.read_ids(
[record.get('id')],
_.pluck(_(this.columns).filter(function (r) {
return r.tag === 'field';
}), 'name')
- ).then(function (records) {
+ ).done(function (records) {
+ if (!records[0]) {
+ self.records.remove(record);
+ return;
+ }
_(records[0]).each(function (value, key) {
record.set(key, value, {silent: true});
});
group_by = null;
}
this.no_leaf = !!context['group_by_no_leaf'];
+ this.grouped = !!group_by;
- this.reload_view(!!group_by, context).then(
+ return this.load_view(context).then(
this.proxy('reload_content'));
},
/**
return;
}
var self = this;
- return $.when(this.dataset.unlink(ids)).then(function () {
+ return $.when(this.dataset.unlink(ids)).done(function () {
_(ids).each(function (id) {
self.records.remove(self.records.get(id));
});
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
if (this.sidebar) {
+ this.options.$sidebar.show();
this.sidebar.$el.show();
}
);
var create_nocontent = this.$buttons;
this.$el.find('.oe_view_nocontent').click(function() {
- create_nocontent.effect('bounce', {distance: 18, times: 5}, 150);
+ create_nocontent.openerpBounce();
});
}
});
var $row;
if (attribute === 'id') {
if (old_value) {
- throw new Error("Setting 'id' attribute on existing record "
- + JSON.stringify(record.attributes));
+ throw new Error(_.str.sprintf( _t("Setting 'id' attribute on existing record %s"),
+ JSON.stringify(record.attributes) ));
}
if (!_.contains(self.dataset.ids, value)) {
// add record to dataset if not already in (added by
}, this);
this.$current = $('<tbody>')
+ .delegate('input[readonly=readonly]', 'click', function (e) {
+ /*
+ Against all logic and sense, as of right now @readonly
+ apparently does nothing on checkbox and radio inputs, so
+ the trick of using @readonly to have, well, readonly
+ checkboxes (which still let clicks go through) does not
+ work out of the box. We *still* need to preventDefault()
+ on the event, otherwise the checkbox's state *will* toggle
+ on click
+ */
+ e.preventDefault();
+ })
.delegate('th.oe_list_record_selector', 'click', function (e) {
e.stopPropagation();
var selection = self.get_selection();
field = $target.closest('td').data('field'),
$row = $target.closest('tr'),
record_id = self.row_id($row);
+
+ if ($target.attr('disabled')) {
+ return;
+ }
+ $target.attr('disabled', 'disabled');
// note: $.data converts data to number if it's composed only
// of digits, nice when storing actual numbers, not nice when
// storing strings composed only of digits. Force the action
// name to be a string
$(self).trigger('action', [field.toString(), record_id, function (id) {
+ $target.removeAttr('disabled');
return self.reload_record(self.records.get(id));
}]);
})
if (row_id) {
e.stopPropagation();
if (!self.dataset.select_id(row_id)) {
- throw new Error("Could not find id in dataset");
+ throw new Error(_t("Could not find id in dataset"));
}
self.row_clicked(e);
}
// set) a human-readable version. m2o does not have this issue
// because the non-human-readable is just a number, where the
// human-readable version is a pair
- if (value && (ref_match = /([\w\.]+),(\d+)/.exec(value))) {
+ if (value && (ref_match = /^([\w\.]+),(\d+)$/.exec(value))) {
// reference values are in the shape "$model,$id" (as a
// string), we need to split and name_get this pair in order
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
- new instance.web.DataSet(this.view, model).name_get([id]).then(function(names) {
+ new instance.web.DataSet(this.view, model).name_get([id]).done(function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
// and let the various registered events handle refreshing the
// row
new instance.web.DataSet(this.view, column.relation)
- .name_get([value]).then(function (names) {
+ .name_get([value]).done(function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
} else if (column.type === 'many2many') {
value = record.get(column.id);
// non-resolved (string) m2m values are arrays
- if (value instanceof Array && !_.isEmpty(value)) {
+ if (value instanceof Array && !_.isEmpty(value)
+ && !record.get(column.id + '__display')) {
var ids;
// they come in two shapes:
if (value[0] instanceof Array) {
var command = value[0];
// 1. an array of m2m commands (usually (6, false, ids))
if (command[0] !== 6) {
- throw new Error(_t("Unknown m2m command ") + command[0]);
+ throw new Error(_.str.sprintf( _t("Unknown m2m command %s"), command[0]));
}
ids = command[2];
} else {
ids = value;
}
new instance.web.Model(column.relation)
- .call('name_get', [ids]).then(function (names) {
- record.set(column.id, _(names).pluck(1).join(', '));
- })
+ .call('name_get', [ids]).done(function (names) {
+ // FIXME: nth horrible hack in this poor listview
+ record.set(column.id + '__display',
+ _(names).pluck(1).join(', '));
+ record.set(column.id, ids);
+ });
+ // temp empty value
+ record.set(column.id, false);
}
}
return column.format(record.toForm().data, {
process_modifiers: false
});
} catch (e) {
- group_label = row_data[group_column.id].value;
+ group_label = _.str.escapeHTML(row_data[group_column.id].value);
}
- $group_column.text(_.str.sprintf("%s (%d)",
+ // group_label is html-clean (through format or explicit
+ // escaping if format failed), can inject straight into HTML
+ $group_column.html(_.str.sprintf(_t("%s (%d)"),
group_label, group.length));
if (group.length && group.openable) {
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name');
var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
//TODO xmo: investigate why we need to put the setTimeout
- $.async_when().then(function() {dataset.read_slice(fields, options).then(function (records) {
- // FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
- if (self.records.length) {
- self.records.reset(null, {silent: true});
- }
- if (!self.datagroup.openable) {
- view.configure_pager(dataset);
- } else {
- if (dataset.size() == records.length) {
- // only one page
- self.$row.find('td.oe_list_group_pagination').empty();
+ $.async_when().done(function() {
+ dataset.read_slice(fields, options).done(function (records) {
+ // FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
+ if (self.records.length) {
+ self.records.reset(null, {silent: true});
+ }
+ if (!self.datagroup.openable) {
+ view.configure_pager(dataset);
} else {
- var pages = Math.ceil(dataset.size() / limit);
- self.$row
- .find('.oe_list_pager_state')
- .text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
- page: page + 1,
- page_count: pages
- }))
- .end()
- .find('button[data-pager-action=previous]')
- .css('visibility',
- page === 0 ? 'hidden' : '')
- .end()
- .find('button[data-pager-action=next]')
- .css('visibility',
- page === pages - 1 ? 'hidden' : '');
+ if (dataset.size() == records.length) {
+ // only one page
+ self.$row.find('td.oe_list_group_pagination').empty();
+ } else {
+ var pages = Math.ceil(dataset.size() / limit);
+ self.$row
+ .find('.oe_list_pager_state')
+ .text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
+ page: page + 1,
+ page_count: pages
+ }))
+ .end()
+ .find('button[data-pager-action=previous]')
+ .css('visibility',
+ page === 0 ? 'hidden' : '')
+ .end()
+ .find('button[data-pager-action=next]')
+ .css('visibility',
+ page === pages - 1 ? 'hidden' : '');
+ }
}
- }
- self.records.add(records, {silent: true});
- list.render();
- d.resolve(list);
- if (_.isEmpty(records)) {
- view.no_result();
- }
- });});
+ self.records.add(records, {silent: true});
+ list.render();
+ d.resolve(list);
+ if (_.isEmpty(records)) {
+ view.no_result();
+ }
+ });
+ });
return d.promise();
},
setup_resequence_rows: function (list, dataset) {
// Accounting > Taxes > Taxes, child tax accounts)
// when synchronous (without setTimeout)
(function (dataset, id, seq) {
- $.async_when().then(function () {
+ $.async_when().done(function () {
var attrs = {};
attrs[seqname] = seq;
dataset.write(id, attrs);
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
- self.render_dataset(dataset).then(function (list) {
+ self.render_dataset(dataset).done(function (list) {
self.children[null] = list;
self.elements =
[list.$current.replaceAll($el)[0]];
}
});
-var DataGroup = instance.web.CallbackEnabled.extend({
+var DataGroup = instance.web.Class.extend({
init: function(parent, model, domain, context, group_by, level) {
- this._super(parent, null);
this.model = new instance.web.Model(model, context, domain);
this.group_by = group_by;
this.context = context;
list: function (fields, ifGroups, ifRecords) {
var self = this;
var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
- $.when(query).then(function (querygroups) {
+ $.when(query).done(function (querygroups) {
// leaf node
if (!querygroups) {
var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
} else if (val instanceof Array) {
output[k] = val[0];
} else {
- throw new Error("Can't convert value " + val + " to context");
+ throw new Error(_.str.sprintf(_t("Can't convert value %s to context"), val));
}
}
return output;
'field.progressbar': 'instance.web.list.ProgressBar',
'field.handle': 'instance.web.list.Handle',
'button': 'instance.web.list.Button',
+ 'field.many2onebutton': 'instance.web.list.Many2OneButton',
+ 'field.many2many': 'instance.web.list.Many2Many'
});
instance.web.list.columns.for_ = function (id, field, node) {
var description = _.extend({tag: node.tag}, field, node.attrs);
* @private
*/
_format: function (row_data, options) {
- return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
+ return _.str.sprintf('<input type="checkbox" %s readonly="readonly"/>',
row_data[this.id].value ? 'checked="checked"' : '');
}
});
*/
_format: function (row_data, options) {
var text = _t("Download");
- var download_url = _.str.sprintf(
- '/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
- instance.session.session_id, options.model, this.id, options.id);
- if (this.filename) {
- download_url += '&filename_field=' + this.filename;
- if (row_data[this.filename]) {
- text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
- row_data[this.filename].value, {type: 'char'}));
+ var value = row_data[this.id].value;
+ var download_url;
+ if (value && value.substr(0, 10).indexOf(' ') == -1) {
+ download_url = "data:application/octet-stream;base64," + value;
+ } else {
+ download_url = this.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
+ if (this.filename) {
+ download_url += '&filename_field=' + this.filename;
}
}
- return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
+ if (this.filename && row_data[this.filename]) {
+ text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
+ row_data[this.filename].value, {type: 'char'}));
+ }
+ return _.template('<a href="<%-href%>"><%-text%></a> (<%-size%>)', {
text: text,
href: download_url,
- size: row_data[this.id].value
+ size: instance.web.binary_to_binsize(value),
});
}
});
return '<div class="oe_list_handle">';
}
});
+instance.web.list.Many2OneButton = instance.web.list.Column.extend({
+ _format: function (row_data, options) {
+ this.has_value = !!row_data[this.id].value;
+ this.icon = this.has_value ? 'gtk-yes' : 'gtk-no';
+ this.string = this.has_value ? _t('View') : _t('Create');
+ return QWeb.render('Many2OneButton.cell', {
+ 'widget': this,
+ 'prefix': instance.session.prefix,
+ });
+ },
+});
+instance.web.list.Many2Many = instance.web.list.Column.extend({
+ _format: function (row_data, options) {
+ if (!_.isEmpty(row_data[this.id].value)) {
+ // If value, use __display version for printing
+ row_data[this.id] = row_data[this.id + '__display'];
+ }
+ return this._super(row_data, options);
+ }
+});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: