[MERGE]Merge with trunk upto revision no 855.
bzr revid: kch@tinyerp.com-20110825054408-0b7063yvldh60u05
This commit is contained in:
@@ -169,7 +169,7 @@ var QWeb2 = {
|
||||
for (var i = 0; i < enu; i++) {
|
||||
_enu.push(i);
|
||||
}
|
||||
this.foreach(context, enu, as, old_dict, callback);
|
||||
this.foreach(context, _enu, as, old_dict, callback);
|
||||
} else {
|
||||
var index = 0;
|
||||
for (var k in enu) {
|
||||
|
||||
@@ -878,13 +878,20 @@ label.error {
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
.openerp img.ui-datepicker-trigger, .openerp img.ui-datepicker-trigger {
|
||||
.openerp img.ui-datepicker-trigger {
|
||||
margin-left: -20px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
.openerp img.oe_field_translate {
|
||||
margin-left: -21px;
|
||||
vertical-align: top;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
/* http://www.quirksmode.org/dom/inputfile.html
|
||||
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
||||
|
||||
@@ -566,6 +566,11 @@ openerp.base.Login = openerp.base.Widget.extend({
|
||||
openerp.base.Header = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
if (jQuery.deparam(jQuery.param.querystring()).debug !== undefined) {
|
||||
this.qs = '?debug'
|
||||
} else {
|
||||
this.qs = ''
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
return this.do_update();
|
||||
|
||||
@@ -10,6 +10,9 @@ openerp.base.core = function(openerp) {
|
||||
var initializing = false,
|
||||
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
||||
// The base Class implementation (does nothing)
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
openerp.base.Class = function(){};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
|
||||
@@ -38,6 +38,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
|
||||
this.registry = openerp.base.form.widgets;
|
||||
this.has_been_loaded = $.Deferred();
|
||||
this.$form_header = null;
|
||||
this.translatable_fields = [];
|
||||
_.defaults(this.options, {"always_show_new_button": true});
|
||||
},
|
||||
start: function() {
|
||||
@@ -461,7 +462,7 @@ openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
|
||||
['res_model', '=', this.view.dataset.model],
|
||||
['res_id', '=', this.view.datarecord.id],
|
||||
['type', 'in', ['binary', 'url']]
|
||||
])).read_slice(['name', 'url', 'type'], this.on_attachments_loaded);
|
||||
])).read_slice(['name', 'url', 'type'], {}, this.on_attachments_loaded);
|
||||
}
|
||||
},
|
||||
on_attachments_loaded: function(attachments) {
|
||||
@@ -839,6 +840,13 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
|
||||
this.invalid = false;
|
||||
this.dirty = false;
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
if (this.field.translate) {
|
||||
this.view.translatable_fields.push(this);
|
||||
this.$element.find('.oe_field_translate').click(this.on_translate);
|
||||
}
|
||||
},
|
||||
set_value: function(value) {
|
||||
this.value = value;
|
||||
this.invalid = false;
|
||||
@@ -850,6 +858,9 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
|
||||
},
|
||||
on_value_changed: function() {
|
||||
},
|
||||
on_translate: function() {
|
||||
this.view.open_translate_dialog(this);
|
||||
},
|
||||
get_value: function() {
|
||||
return this.value;
|
||||
},
|
||||
|
||||
@@ -149,6 +149,9 @@ openerp.base.format_cell = function (row_data, column, value_if_empty) {
|
||||
].join('')
|
||||
}
|
||||
|
||||
if (!row_data[column.id]) {
|
||||
return value_if_empty === undefined ? '' : value_if_empty;
|
||||
}
|
||||
return openerp.base.format_value(
|
||||
row_data[column.id].value, column, value_if_empty);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ openerp.base.list_editable = function (openerp) {
|
||||
if (!this.options.editable) {
|
||||
return this._super(event);
|
||||
}
|
||||
this.edit_record();
|
||||
this.edit_record($(event.currentTarget).data('id'));
|
||||
},
|
||||
/**
|
||||
* Checks if a record is being edited, and if so cancels it
|
||||
@@ -104,8 +104,8 @@ openerp.base.list_editable = function (openerp) {
|
||||
return cancelled.promise();
|
||||
}
|
||||
|
||||
if (this.edition_index !== null) {
|
||||
this.reload_record(this.edition_index, true).then(function () {
|
||||
if (this.edition_id != null) {
|
||||
this.reload_record(self.records.get(this.edition_id)).then(function () {
|
||||
cancelled.resolve();
|
||||
});
|
||||
} else {
|
||||
@@ -115,13 +115,14 @@ openerp.base.list_editable = function (openerp) {
|
||||
self.edition_form.stop();
|
||||
self.edition_form.$element.remove();
|
||||
delete self.edition_form;
|
||||
delete self.edition_index;
|
||||
delete self.edition_id;
|
||||
delete self.edition;
|
||||
});
|
||||
return cancelled.promise();
|
||||
},
|
||||
/**
|
||||
* Adapts this list's view description to be suitable to the inner form view of a row being edited.
|
||||
* Adapts this list's view description to be suitable to the inner form
|
||||
* view of a row being edited.
|
||||
*
|
||||
* @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
|
||||
*/
|
||||
@@ -140,8 +141,10 @@ openerp.base.list_editable = function (openerp) {
|
||||
render_row_as_form: function (row) {
|
||||
var self = this;
|
||||
this.cancel_pending_edition().then(function () {
|
||||
var record_id = $(row).data('id');
|
||||
var $new_row = $('<tr>', {
|
||||
id: _.uniqueId('oe-editable-row-'),
|
||||
'data-id': record_id,
|
||||
'class': $(row).attr('class') + ' oe_forms',
|
||||
click: function (e) {e.stopPropagation();}
|
||||
})
|
||||
@@ -174,7 +177,7 @@ openerp.base.list_editable = function (openerp) {
|
||||
self.$current.append($new_row);
|
||||
}
|
||||
self.edition = true;
|
||||
self.edition_index = self.dataset.index;
|
||||
self.edition_id = record_id;
|
||||
self.edition_form = _.extend(new openerp.base.FormView(
|
||||
self, $new_row.attr('id'), self.dataset, false), {
|
||||
template: 'ListView.row.form',
|
||||
@@ -199,6 +202,25 @@ openerp.base.list_editable = function (openerp) {
|
||||
});
|
||||
});
|
||||
},
|
||||
handle_onwrite: function (source_record_id) {
|
||||
var self = this;
|
||||
var on_write_callback = self.view.fields_view.arch.attrs.on_write;
|
||||
if (!on_write_callback) { return; }
|
||||
this.dataset.call(on_write_callback, [source_record_id], function (ids) {
|
||||
_(ids).each(function (id) {
|
||||
var record = self.records.get(id);
|
||||
if (!record) {
|
||||
// insert after the source record
|
||||
var index = self.records.indexOf(
|
||||
self.records.get(source_record_id)) + 1;
|
||||
record = new openerp.base.list.Record({id: id});
|
||||
self.records.add(record, {at: index});
|
||||
self.dataset.ids.splice(index, 0, id);
|
||||
}
|
||||
self.reload_record(record);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Saves the current row, and triggers the edition of its following
|
||||
* sibling if asked.
|
||||
@@ -208,9 +230,16 @@ openerp.base.list_editable = function (openerp) {
|
||||
save_row: function (edit_next) {
|
||||
var self = this;
|
||||
this.edition_form.do_save(function (result) {
|
||||
if (result.created && !self.edition_index) {
|
||||
self.edition_index = self.dataset.index;
|
||||
if (result.created && !self.edition_id) {
|
||||
self.records.add({id: result.result},
|
||||
{at: self.options.editable === 'top' ? 0 : null});
|
||||
self.edition_id = result.result;
|
||||
}
|
||||
var edited_record = self.records.get(self.edition_id),
|
||||
next_record = self.records.at(
|
||||
self.records.indexOf(edited_record) + 1);
|
||||
|
||||
self.handle_onwrite(self.edition_id);
|
||||
self.cancel_pending_edition().then(function () {
|
||||
$(self).trigger('saved', [self.dataset]);
|
||||
if (!edit_next) {
|
||||
@@ -220,8 +249,16 @@ openerp.base.list_editable = function (openerp) {
|
||||
self.new_record();
|
||||
return;
|
||||
}
|
||||
self.dataset.next();
|
||||
self.edit_record();
|
||||
var next_record_id;
|
||||
if (next_record) {
|
||||
next_record_id = next_record.get('id');
|
||||
self.dataset.index = _(self.dataset.ids)
|
||||
.indexOf(next_record_id);
|
||||
} else {
|
||||
self.dataset.index = 0;
|
||||
next_record_id = self.records.at(0).get('id');
|
||||
}
|
||||
self.edit_record(next_record_id);
|
||||
});
|
||||
}, this.options.editable === 'top');
|
||||
},
|
||||
@@ -234,14 +271,12 @@ openerp.base.list_editable = function (openerp) {
|
||||
/**
|
||||
* Edits record currently selected via dataset
|
||||
*/
|
||||
edit_record: function () {
|
||||
edit_record: function (record_id) {
|
||||
this.render_row_as_form(
|
||||
this.$current.children(
|
||||
_.sprintf('[data-index=%d]',
|
||||
this.dataset.index)));
|
||||
this.$current.find('[data-id=' + record_id + ']'));
|
||||
$(this).trigger(
|
||||
'edit',
|
||||
[this.rows[this.dataset.index].data.id.value, this.dataset]);
|
||||
[record_id, this.dataset]);
|
||||
},
|
||||
new_record: function () {
|
||||
this.dataset.index = null;
|
||||
|
||||
+447
-175
@@ -42,6 +42,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
* @borrows openerp.base.ActionExecutor#execute_action as #execute_action
|
||||
*/
|
||||
init: function(parent, element_id, dataset, view_id, options) {
|
||||
var self = this;
|
||||
this._super(parent, element_id);
|
||||
this.set_default_options(_.extend({}, this.defaults, options || {}));
|
||||
this.dataset = dataset;
|
||||
@@ -50,6 +51,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
|
||||
this.columns = [];
|
||||
|
||||
this.records = new Collection();
|
||||
|
||||
this.set_groups(new openerp.base.ListView.Groups(this));
|
||||
|
||||
if (this.dataset instanceof openerp.base.DataSetStatic) {
|
||||
@@ -57,6 +60,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
}
|
||||
|
||||
this.page = 0;
|
||||
this.records.bind('change', function (event, record, key) {
|
||||
if (!_(self.aggregate_columns).chain()
|
||||
.pluck('name').contains(key).value()) {
|
||||
return;
|
||||
}
|
||||
self.compute_aggregates();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Retrieves the view's number of records per page (|| section)
|
||||
@@ -336,9 +346,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
this.sidebar.$element.show();
|
||||
}
|
||||
if (this.hidden) {
|
||||
this.$element.find('.oe-listview-content').append(
|
||||
this.groups.apoptosis().render());
|
||||
this.hidden = false;
|
||||
this.reload_content();
|
||||
}
|
||||
},
|
||||
do_hide: function () {
|
||||
@@ -355,27 +363,29 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
* @param {Object} [context] context to send the server while loading the view
|
||||
*/
|
||||
reload_view: function (grouped, context) {
|
||||
var self = this;
|
||||
var callback = function (field_view_get) {
|
||||
var self = this, d = $.Deferred();
|
||||
d.then(function (field_view_get) {
|
||||
self.on_loaded(field_view_get, grouped);
|
||||
};
|
||||
});
|
||||
if (this.embedded_view) {
|
||||
return $.Deferred().then(callback).resolve({fields_view: this.embedded_view});
|
||||
d.resolve({fields_view: this.embedded_view});
|
||||
} else {
|
||||
return this.rpc('/base/listview/load', {
|
||||
this.rpc('/base/listview/load', {
|
||||
model: this.model,
|
||||
view_id: this.view_id,
|
||||
context: this.dataset.get_context(context),
|
||||
toolbar: this.options.sidebar
|
||||
}, callback);
|
||||
}, function () { d.resolve.apply(this, arguments); });
|
||||
}
|
||||
return d.promise();
|
||||
},
|
||||
/**
|
||||
* re-renders the content of the list view
|
||||
*/
|
||||
reload_content: function () {
|
||||
this.records.reset();
|
||||
this.$element.find('.oe-listview-content').append(
|
||||
this.groups.apoptosis().render(
|
||||
this.groups.render(
|
||||
$.proxy(this, 'compute_aggregates')));
|
||||
},
|
||||
/**
|
||||
@@ -403,8 +413,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
do_actual_search: function (results) {
|
||||
this.groups.datagroup = new openerp.base.DataGroup(
|
||||
this, this.model,
|
||||
this.dataset.get_domain(results.domain),
|
||||
this.dataset.get_context(results.context),
|
||||
results.domain,
|
||||
results.context,
|
||||
results.group_by);
|
||||
this.groups.datagroup.sort = this.dataset._sort;
|
||||
|
||||
@@ -426,7 +436,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
}
|
||||
var self = this;
|
||||
return $.when(this.dataset.unlink(ids)).then(function () {
|
||||
self.groups.drop_records(ids);
|
||||
_(ids).each(function (id) {
|
||||
self.records.remove(self.records.get(id));
|
||||
});
|
||||
self.compute_aggregates();
|
||||
});
|
||||
},
|
||||
@@ -456,16 +468,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
* @param {Function} callback should be called after the action is executed, if non-null
|
||||
*/
|
||||
do_button_action: function (name, id, callback) {
|
||||
var self = this,
|
||||
action = _.detect(this.columns, function (field) {
|
||||
var action = _.detect(this.columns, function (field) {
|
||||
return field.name === name;
|
||||
});
|
||||
if (!action) { return; }
|
||||
this.execute_action(action, this.dataset, id, function () {
|
||||
$.when(callback.apply(this, arguments).then(function () {
|
||||
self.compute_aggregates();
|
||||
}));
|
||||
});
|
||||
this.execute_action(action, this.dataset, id, callback);
|
||||
},
|
||||
/**
|
||||
* Handles the activation of a record (clicking on it)
|
||||
@@ -622,7 +629,41 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
|
||||
this.options = opts.options;
|
||||
this.columns = opts.columns;
|
||||
this.dataset = opts.dataset;
|
||||
this.rows = opts.rows;
|
||||
this.records = opts.records;
|
||||
|
||||
this.record_callbacks = {
|
||||
'remove': function (event, record) {
|
||||
var $row = self.$current.find(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var index = $row.data('index');
|
||||
$row.remove();
|
||||
self.refresh_zebra(index);
|
||||
},
|
||||
'reset': $.proxy(this, 'on_records_reset'),
|
||||
'change': function (event, record) {
|
||||
var $row = self.$current.find('[data-id=' + record.get('id') + ']');
|
||||
$row.replaceWith(self.render_record(record));
|
||||
},
|
||||
'add': function (ev, records, record, index) {
|
||||
var $new_row = $('<tr>').attr({
|
||||
'data-id': record.get('id')
|
||||
});
|
||||
|
||||
if (index === 0) {
|
||||
$new_row.prependTo(self.$current);
|
||||
} else {
|
||||
var previous_record = records.at(index-1),
|
||||
$previous_sibling = self.$current.find(
|
||||
'[data-id=' + previous_record.get('id') + ']');
|
||||
$new_row.insertAfter($previous_sibling);
|
||||
}
|
||||
|
||||
self.refresh_zebra(index, 1);
|
||||
}
|
||||
};
|
||||
_(this.record_callbacks).each(function (callback, event) {
|
||||
this.records.bind(event, callback);
|
||||
}, this);
|
||||
|
||||
this.$_element = $('<tbody class="ui-widget-content">')
|
||||
.appendTo(document.body)
|
||||
@@ -642,23 +683,24 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
|
||||
var $target = $(e.currentTarget),
|
||||
field = $target.closest('td').data('field'),
|
||||
$row = $target.closest('tr'),
|
||||
record_id = self.row_id($row),
|
||||
index = self.row_position($row);
|
||||
record_id = self.row_id($row);
|
||||
|
||||
$(self).trigger('action', [field, record_id, function () {
|
||||
return self.reload_record(index, true);
|
||||
return self.reload_record(self.records.get(record_id));
|
||||
}]);
|
||||
})
|
||||
.delegate('tr', 'click', function (e) {
|
||||
e.stopPropagation();
|
||||
self.dataset.index = self.row_position(e.currentTarget);
|
||||
self.dataset.index = self.records.indexOf(
|
||||
self.records.get(
|
||||
self.row_id(e.currentTarget)));
|
||||
self.row_clicked(e);
|
||||
});
|
||||
},
|
||||
row_clicked: function () {
|
||||
$(this).trigger(
|
||||
'row_link',
|
||||
[this.rows[this.dataset.index].data.id.value,
|
||||
[this.records.at(this.dataset.index).get('id'),
|
||||
this.dataset]);
|
||||
},
|
||||
render: function () {
|
||||
@@ -678,28 +720,16 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
|
||||
if (!this.options.selectable) {
|
||||
return [];
|
||||
}
|
||||
var rows = this.rows;
|
||||
var records = this.records;
|
||||
var result = {ids: [], records: []};
|
||||
this.$current.find('th.oe-record-selector input:checked')
|
||||
.closest('tr').each(function () {
|
||||
var record = {};
|
||||
_(rows[$(this).data('index')].data).each(function (obj, key) {
|
||||
record[key] = obj.value;
|
||||
});
|
||||
result.ids.push(record.id);
|
||||
result.records.push(record);
|
||||
var record = records.get($(this).data('id'));
|
||||
result.ids.push(record.get('id'));
|
||||
result.records.push(record.attributes);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Returns the index of the row in the list of rows.
|
||||
*
|
||||
* @param {Object} row the selected row
|
||||
* @returns {Number} the position of the row in this.rows
|
||||
*/
|
||||
row_position: function (row) {
|
||||
return $(row).data('index');
|
||||
},
|
||||
/**
|
||||
* Returns the identifier of the object displayed in the provided table
|
||||
* row
|
||||
@@ -708,119 +738,80 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
|
||||
* @returns {Number|String} the identifier of the row's object
|
||||
*/
|
||||
row_id: function (row) {
|
||||
return this.rows[this.row_position(row)].data.id.value;
|
||||
return $(row).data('id');
|
||||
},
|
||||
/**
|
||||
* Death signal, cleans up list
|
||||
* Death signal, cleans up list display
|
||||
*/
|
||||
apoptosis: function () {
|
||||
on_records_reset: function () {
|
||||
_(this.record_callbacks).each(function (callback, event) {
|
||||
this.records.unbind(event, callback);
|
||||
}, this);
|
||||
if (!this.$current) { return; }
|
||||
this.$current.remove();
|
||||
this.$current = null;
|
||||
this.$_element.remove();
|
||||
},
|
||||
get_records: function () {
|
||||
return _(this.rows).map(function (row) {
|
||||
var record = {};
|
||||
_(row.data).each(function (obj, key) {
|
||||
record[key] = obj.value;
|
||||
});
|
||||
return {count: 1, values: record};
|
||||
return this.records.map(function (record) {
|
||||
return {count: 1, values: record.attributes};
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Transforms a record from what is returned by a dataset read (a simple
|
||||
* mapping of ``$fieldname: $value``) to the format expected by list rows
|
||||
* and form views:
|
||||
* Reloads the provided record by re-reading its content from the server.
|
||||
*
|
||||
* data: {
|
||||
* $fieldname: {
|
||||
* value: $value
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* This format allows for the insertion of a bunch of metadata (names,
|
||||
* colors, etc...)
|
||||
*
|
||||
* @param {Object} record original record, in dataset format
|
||||
* @returns {Object} record displayable in a form or list view
|
||||
* @param {Record} record
|
||||
* @returns {$.Deferred} promise to the finalization of the reloading
|
||||
*/
|
||||
transform_record: function (record) {
|
||||
// TODO: colors handling
|
||||
var form_data = {},
|
||||
form_record = {data: form_data};
|
||||
|
||||
_(record).each(function (value, key) {
|
||||
form_data[key] = {value: value};
|
||||
});
|
||||
|
||||
return form_record;
|
||||
},
|
||||
/**
|
||||
* Reloads the record at index ``row_index`` in the list's rows.
|
||||
*
|
||||
* By default, simply re-renders the record. If the ``fetch`` parameter is
|
||||
* provided and ``true``, will first fetch the record anew.
|
||||
*
|
||||
* @param {Number} record_index index of the record to reload
|
||||
* @param {Boolean} fetch fetches the record from remote before reloading it
|
||||
*/
|
||||
reload_record: function (record_index, fetch) {
|
||||
var self = this;
|
||||
var read_p = null;
|
||||
if (fetch) {
|
||||
// save index to restore it later, if already set
|
||||
var old_index = this.dataset.index;
|
||||
this.dataset.index = record_index;
|
||||
read_p = this.dataset.read_index(
|
||||
_.pluck(_(this.columns).filter(function (r) {return r.tag === 'field';}), 'name'),
|
||||
function (record) {
|
||||
var form_record = self.transform_record(record);
|
||||
self.rows.splice(record_index, 1, form_record);
|
||||
self.dataset.index = old_index;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return $.when(read_p).then(function () {
|
||||
self.$current.children().eq(record_index)
|
||||
.replaceWith(self.render_record(record_index)); });
|
||||
reload_record: function (record) {
|
||||
return this.dataset.read_ids(
|
||||
[record.get('id')],
|
||||
_.pluck(_(this.columns).filter(function (r) {
|
||||
return r.tag === 'field';
|
||||
}), 'name'),
|
||||
function (records) {
|
||||
_(records[0]).each(function (value, key) {
|
||||
record.set(key, value, {silent: true});
|
||||
});
|
||||
record.trigger('change', record);
|
||||
}
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Renders a list record to HTML
|
||||
*
|
||||
* @param {Number} record_index index of the record to render in ``this.rows``
|
||||
* @param {Record} record index of the record to render in ``this.rows``
|
||||
* @returns {String} QWeb rendering of the selected record
|
||||
*/
|
||||
render_record: function (record_index) {
|
||||
render_record: function (record) {
|
||||
var index = this.records.indexOf(record);
|
||||
return QWeb.render('ListView.row', {
|
||||
columns: this.columns,
|
||||
options: this.options,
|
||||
row: this.rows[record_index],
|
||||
row_parity: (record_index % 2 === 0) ? 'even' : 'odd',
|
||||
row_index: record_index,
|
||||
record: record,
|
||||
row_parity: (index % 2 === 0) ? 'even' : 'odd',
|
||||
render_cell: openerp.base.format_cell
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Stops displaying the records matching the provided ids.
|
||||
* Fixes fixes the even/odd classes
|
||||
*
|
||||
* @param {Array} ids identifiers of the records to remove
|
||||
* @param {Number} [from_index] index from which to resequence
|
||||
* @param {Number} [offset = 0] selection offset for DOM, in case there are rows to ignore in the table
|
||||
*/
|
||||
drop_records: function (ids) {
|
||||
var self = this;
|
||||
_(this.rows).chain()
|
||||
.map(function (record, index) {
|
||||
return {index: index, id: record.data.id.value};
|
||||
}).filter(function (record) {
|
||||
return _(ids).contains(record.id);
|
||||
}).reverse()
|
||||
.each(function (record) {
|
||||
self.$current.find('tr:eq(' + record.index + ')').remove();
|
||||
self.rows.splice(record.index, 1);
|
||||
})
|
||||
refresh_zebra: function (from_index, offset) {
|
||||
offset = offset || 0;
|
||||
from_index = from_index || 0;
|
||||
var dom_offset = offset + from_index;
|
||||
var sel = dom_offset ? ':gt(' + (dom_offset - 1) + ')' : null;
|
||||
this.$current.children(sel).each(function (i, e) {
|
||||
var index = from_index + i;
|
||||
// reset record-index accelerators on rows and even/odd
|
||||
var even = index%2 === 0;
|
||||
$(e).toggleClass('even', even)
|
||||
.toggleClass('odd', !even);
|
||||
});
|
||||
}
|
||||
// drag and drop
|
||||
});
|
||||
openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.base.ListView.Groups# */{
|
||||
passtrough_events: 'action deleted row_link',
|
||||
@@ -830,17 +821,28 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
*
|
||||
* Provides events similar to those of
|
||||
* :js:class:`~openerp.base.ListView.List`
|
||||
*
|
||||
* @constructs
|
||||
* @param {openerp.base.ListView} view
|
||||
* @param {Object} [options]
|
||||
* @param {Collection} [options.records]
|
||||
* @param {Object} [options.options]
|
||||
* @param {Array} [options.columns]
|
||||
*/
|
||||
init: function (view) {
|
||||
init: function (view, options) {
|
||||
options = options || {};
|
||||
this.view = view;
|
||||
this.options = view.options;
|
||||
this.columns = view.columns;
|
||||
this.records = options.records || view.records;
|
||||
this.options = options.options || view.options;
|
||||
this.columns = options.columns || view.columns;
|
||||
this.datagroup = null;
|
||||
|
||||
this.$row = null;
|
||||
this.children = {};
|
||||
|
||||
this.page = 0;
|
||||
|
||||
this.records.bind('reset', $.proxy(this, 'on_records_reset'));
|
||||
},
|
||||
make_fragment: function () {
|
||||
return document.createDocumentFragment();
|
||||
@@ -901,7 +903,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
},
|
||||
close: function () {
|
||||
this.$row.children().last().empty();
|
||||
this.apoptosis();
|
||||
this.records.reset();
|
||||
},
|
||||
/**
|
||||
* Prefixes ``$node`` with floated spaces in order to indent it relative
|
||||
@@ -922,10 +924,11 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
var placeholder = this.make_fragment();
|
||||
_(datagroups).each(function (group) {
|
||||
if (self.children[group.value]) {
|
||||
self.children[group.value].apoptosis();
|
||||
self.records.proxy(group.value).reset();
|
||||
delete self.children[group.value];
|
||||
}
|
||||
var child = self.children[group.value] = new openerp.base.ListView.Groups(self.view, {
|
||||
records: self.records.proxy(group.value),
|
||||
options: self.options,
|
||||
columns: self.columns
|
||||
});
|
||||
@@ -1020,13 +1023,12 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
});
|
||||
},
|
||||
render_dataset: function (dataset) {
|
||||
var rows = [],
|
||||
self = this,
|
||||
var self = this,
|
||||
list = new openerp.base.ListView.List(this, {
|
||||
options: this.options,
|
||||
columns: this.columns,
|
||||
dataset: dataset,
|
||||
rows: rows
|
||||
records: this.records
|
||||
});
|
||||
this.bind_child_events(list);
|
||||
|
||||
@@ -1053,11 +1055,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
.attr('disabled', page === pages - 1);
|
||||
}
|
||||
|
||||
var form_records = _(records).map(
|
||||
$.proxy(list, 'transform_record'));
|
||||
|
||||
rows.splice(0, rows.length);
|
||||
rows.push.apply(rows, form_records);
|
||||
self.records.add(records, {silent: true});
|
||||
list.render();
|
||||
d.resolve(list);
|
||||
});
|
||||
@@ -1074,33 +1072,27 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
// ondrop, move relevant record & fix sequences
|
||||
list.$current.sortable({
|
||||
stop: function (event, ui) {
|
||||
var from = ui.item.data('index'),
|
||||
to = ui.item.prev().data('index') || 0;
|
||||
if (from === to) { return; }
|
||||
list.rows.splice(to, 0, list.rows.splice(from, 1)[0]);
|
||||
var to_move = list.records.get(ui.item.data('id')),
|
||||
target_id = ui.item.prev().data('id');
|
||||
|
||||
ui.item.parent().children().each(function (i, e) {
|
||||
// reset record-index accelerators on rows and even/odd
|
||||
var even = i%2 === 0;
|
||||
$(e).data('index', i)
|
||||
.toggleClass('even', even)
|
||||
.toggleClass('odd', !even);
|
||||
});
|
||||
list.records.remove(to_move);
|
||||
var to = target_id ? list.records.indexOf(list.records.get(target_id)) : 0;
|
||||
list.records.add(to_move, { at: to });
|
||||
|
||||
// resequencing time!
|
||||
var data, index = to,
|
||||
var record, index = to,
|
||||
// if drag to 1st row (to = 0), start sequencing from 0
|
||||
// (exclusive lower bound)
|
||||
seq = to ? list.rows[to - 1].data.sequence.value : 0;
|
||||
while (++seq, list.rows[index]) {
|
||||
data = list.rows[index].data;
|
||||
data.sequence.value = seq;
|
||||
seq = to ? list.records.at(to - 1).get('sequence') : 0;
|
||||
while (++seq, record = list.records.at(index++)) {
|
||||
// write are independent from one another, so we can just
|
||||
// launch them all at the same time and we don't really
|
||||
// give a fig about when they're done
|
||||
dataset.write(data.id.value, {sequence: seq});
|
||||
list.reload_record(index++);
|
||||
dataset.write(record.get('id'), {sequence: seq});
|
||||
record.set('sequence', seq);
|
||||
}
|
||||
|
||||
list.refresh_zebra();
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -1144,13 +1136,9 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
|
||||
return {ids: ids, records: records};
|
||||
},
|
||||
apoptosis: function () {
|
||||
_(this.children).each(function (child) {
|
||||
child.apoptosis();
|
||||
});
|
||||
on_records_reset: function () {
|
||||
this.children = {};
|
||||
$(this.elements).remove();
|
||||
return this;
|
||||
},
|
||||
get_records: function () {
|
||||
if (_(this.children).isEmpty()) {
|
||||
@@ -1163,22 +1151,306 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
|
||||
.map(function (child) {
|
||||
return child.get_records();
|
||||
}).flatten().value();
|
||||
},
|
||||
/**
|
||||
* Stops displaying the records with the linked ids, assumes these records
|
||||
* were deleted from the DB.
|
||||
*
|
||||
* This is the up-signal from the `deleted` event on groups and lists.
|
||||
*
|
||||
* @param {Array} ids list of identifier of the records to remove.
|
||||
*/
|
||||
drop_records: function (ids) {
|
||||
_.each(this.children, function (child) {
|
||||
child.drop_records(ids);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @extends openerp.base.Class
|
||||
*/
|
||||
var Events = {
|
||||
/**
|
||||
* @param {String} event event to listen to on the current object, null for all events
|
||||
* @param {Function} handler event handler to bind to the relevant event
|
||||
* @returns this
|
||||
*/
|
||||
bind: function (event, handler) {
|
||||
var calls = this['_callbacks'] || (this._callbacks = {});
|
||||
|
||||
if (event in calls) {
|
||||
calls[event].push(handler);
|
||||
} else {
|
||||
calls[event] = [handler];
|
||||
}
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* @param {String} event event to unbind on the current object
|
||||
* @param {function} [handler] specific event handler to remove (otherwise unbind all handlers for the event)
|
||||
* @returns this
|
||||
*/
|
||||
unbind: function (event, handler) {
|
||||
var calls = this._callbacks || {};
|
||||
if (!(event in calls)) { return this; }
|
||||
if (!handler) {
|
||||
delete calls[event];
|
||||
} else {
|
||||
var handlers = calls[event];
|
||||
handlers.splice(
|
||||
_(handlers).indexOf(handler),
|
||||
1);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* @param {String} event
|
||||
* @returns this
|
||||
*/
|
||||
trigger: function (event) {
|
||||
var calls;
|
||||
if (!(calls = this._callbacks)) { return this; }
|
||||
var callbacks = (calls[event] || []).concat(calls[null] || []);
|
||||
for(var i=0, length=callbacks.length; i<length; ++i) {
|
||||
callbacks[i].apply(this, arguments);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
var Record = openerp.base.Class.extend(/** @lends Record# */{
|
||||
/**
|
||||
* @constructs
|
||||
* @extends openerp.base.Class
|
||||
* @borrows Events#bind as this.bind
|
||||
* @borrows Events#trigger as this.trigger
|
||||
* @param {Object} [data]
|
||||
*/
|
||||
init: function (data) {
|
||||
this.attributes = data || {};
|
||||
},
|
||||
/**
|
||||
* @param {String} key
|
||||
* @returns {Object}
|
||||
*/
|
||||
get: function (key) {
|
||||
return this.attributes[key];
|
||||
},
|
||||
/**
|
||||
* @param key
|
||||
* @param value
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.silent=false]
|
||||
* @returns {Record}
|
||||
*/
|
||||
set: function (key, value, options) {
|
||||
options = options || {};
|
||||
var old_value = this.attributes[key];
|
||||
if (old_value === value) {
|
||||
return this;
|
||||
}
|
||||
this.attributes[key] = value;
|
||||
if (!options.silent) {
|
||||
this.trigger('change:' + key, this, value, old_value);
|
||||
this.trigger('change', this, key, value, old_value);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Converts the current record to the format expected by form views:
|
||||
*
|
||||
* .. code-block:: javascript
|
||||
*
|
||||
* data: {
|
||||
* $fieldname: {
|
||||
* value: $value
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @returns {Object} record displayable in a form view
|
||||
*/
|
||||
toForm: function () {
|
||||
var form_data = {};
|
||||
_(this.attributes).each(function (value, key) {
|
||||
form_data[key] = {value: value};
|
||||
});
|
||||
|
||||
return {data: form_data};
|
||||
}
|
||||
});
|
||||
Record.include(Events);
|
||||
var Collection = openerp.base.Class.extend(/** @lends Collection# */{
|
||||
/**
|
||||
* Smarter collections, with events, very strongly inspired by Backbone's.
|
||||
*
|
||||
* Using a "dumb" array of records makes synchronization between the
|
||||
* various serious
|
||||
*
|
||||
* @constructs
|
||||
* @extends openerp.base.Class
|
||||
* @borrows Events#bind as this.bind
|
||||
* @borrows Events#trigger as this.trigger
|
||||
* @param {Array} [records] records to initialize the collection with
|
||||
* @param {Object} [options]
|
||||
*/
|
||||
init: function (records, options) {
|
||||
options = options || {};
|
||||
_.bindAll(this, '_onRecordEvent');
|
||||
this.length = 0;
|
||||
this.records = [];
|
||||
this._byId = {};
|
||||
this._proxies = {};
|
||||
this._key = options.key;
|
||||
this._parent = options.parent;
|
||||
|
||||
if (records) {
|
||||
this.add(records);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Object|Array} record
|
||||
* @param {Object} [options]
|
||||
* @param {Number} [options.at]
|
||||
* @param {Boolean} [options.silent=false]
|
||||
* @returns this
|
||||
*/
|
||||
add: function (record, options) {
|
||||
options = options || {};
|
||||
var records = record instanceof Array ? record : [record];
|
||||
|
||||
for(var i=0, length=records.length; i<length; ++i) {
|
||||
var instance = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
|
||||
instance.bind(null, this._onRecordEvent);
|
||||
this._byId[instance.get('id')] = instance;
|
||||
if (options.at == undefined) {
|
||||
this.records.push(instance);
|
||||
if (!options.silent) {
|
||||
this.trigger('add', this, instance, this.records.length-1);
|
||||
}
|
||||
} else {
|
||||
var insertion_index = options.at + i;
|
||||
this.records.splice(insertion_index, 0, instance);
|
||||
if (!options.silent) {
|
||||
this.trigger('add', this, instance, insertion_index);
|
||||
}
|
||||
}
|
||||
this.length++;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a record by its index in the collection, can also take a group if
|
||||
* the collection is not degenerate
|
||||
*
|
||||
* @param {Number} index
|
||||
* @param {String} [group]
|
||||
* @returns {Record|undefined}
|
||||
*/
|
||||
at: function (index, group) {
|
||||
if (group) {
|
||||
var groups = group.split('.');
|
||||
return this._proxies[groups[0]].at(index, groups.join('.'));
|
||||
}
|
||||
return this.records[index];
|
||||
},
|
||||
/**
|
||||
* Get a record by its database id
|
||||
*
|
||||
* @param {Number} id
|
||||
* @returns {Record|undefined}
|
||||
*/
|
||||
get: function (id) {
|
||||
if (!_(this._proxies).isEmpty()) {
|
||||
var record = null;
|
||||
_(this._proxies).detect(function (proxy) {
|
||||
return record = proxy.get(id);
|
||||
});
|
||||
return record;
|
||||
}
|
||||
return this._byId[id];
|
||||
},
|
||||
/**
|
||||
* Builds a proxy (insert/retrieve) to a subtree of the collection, by
|
||||
* the subtree's group
|
||||
*
|
||||
* @param {String} section group path section
|
||||
* @returns {Collection}
|
||||
*/
|
||||
proxy: function (section) {
|
||||
return this._proxies[section] = new Collection(null, {
|
||||
parent: this,
|
||||
key: section
|
||||
}).bind(null, this._onRecordEvent);
|
||||
},
|
||||
/**
|
||||
* @param {Array} [records]
|
||||
* @returns this
|
||||
*/
|
||||
reset: function (records) {
|
||||
_(this._proxies).each(function (proxy) {
|
||||
proxy.reset();
|
||||
});
|
||||
this._proxies = {};
|
||||
this.length = 0;
|
||||
this.records = [];
|
||||
this._byId = {};
|
||||
if (records) {
|
||||
this.add(records);
|
||||
}
|
||||
this.trigger('reset', this);
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Removes the provided record from the collection
|
||||
*
|
||||
* @param {Record} record
|
||||
* @returns this
|
||||
*/
|
||||
remove: function (record) {
|
||||
var self = this;
|
||||
var index = _(this.records).indexOf(record);
|
||||
if (index === -1) {
|
||||
_(this._proxies).each(function (proxy) {
|
||||
proxy.remove(record);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
this.records.splice(index, 1);
|
||||
delete this._byId[record.get('id')];
|
||||
this.length--;
|
||||
this.trigger('remove', record, this);
|
||||
return this;
|
||||
},
|
||||
|
||||
_onRecordEvent: function (event, record, options) {
|
||||
// don't propagate reset events
|
||||
if (event === 'reset') { return; }
|
||||
this.trigger.apply(this, arguments);
|
||||
},
|
||||
|
||||
// underscore-type methods
|
||||
each: function (callback) {
|
||||
for(var section in this._proxies) {
|
||||
if (this._proxies.hasOwnProperty(section)) {
|
||||
this._proxies[section].each(callback);
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.length; ++i) {
|
||||
callback(this.records[i]);
|
||||
}
|
||||
},
|
||||
map: function (callback) {
|
||||
var results = [];
|
||||
this.each(function (record) {
|
||||
results.push(callback(record));
|
||||
});
|
||||
return results;
|
||||
},
|
||||
pluck: function (fieldname) {
|
||||
return this.map(function (record) {
|
||||
return record.get(fieldname);
|
||||
});
|
||||
},
|
||||
indexOf: function (record) {
|
||||
return _(this.records).indexOf(record);
|
||||
}
|
||||
});
|
||||
Collection.include(Events);
|
||||
openerp.base.list = {
|
||||
Events: Events,
|
||||
Record: Record,
|
||||
Collection: Collection
|
||||
}
|
||||
};
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
|
||||
|
||||
@@ -590,6 +590,20 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp
|
||||
{}, context,
|
||||
{own_values: {self: val}});
|
||||
},
|
||||
/**
|
||||
* Function creating the returned domain for the field, override this
|
||||
* methods in children if you only need to customize the field's domain
|
||||
* without more complex alterations or tests (and without the need to
|
||||
* change override the handling of filter_domain)
|
||||
*
|
||||
* @param {String} name the field's name
|
||||
* @param {String} operator the field's operator (either attribute-specified or default operator for the field
|
||||
* @param {Number|String} value parsed value for the field
|
||||
* @returns {Array<Array>} domain to include in the resulting search
|
||||
*/
|
||||
make_domain: function (name, operator, value) {
|
||||
return [[name, operator, value]];
|
||||
},
|
||||
get_domain: function () {
|
||||
var val = this.get_value();
|
||||
if (val === null || val === '') {
|
||||
@@ -598,11 +612,10 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp
|
||||
|
||||
var domain = this.attrs['filter_domain'];
|
||||
if (!domain) {
|
||||
return [[
|
||||
return this.make_domain(
|
||||
this.attrs.name,
|
||||
this.attrs.operator || this.default_operator,
|
||||
this.get_value()
|
||||
]];
|
||||
val);
|
||||
}
|
||||
return _.extend({}, domain, {own_values: {self: val}});
|
||||
}
|
||||
@@ -723,7 +736,22 @@ openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends ope
|
||||
return this.$element.val();
|
||||
}
|
||||
});
|
||||
openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({
|
||||
/**
|
||||
* Implementation of the ``datetime`` openerp field type:
|
||||
*
|
||||
* * Uses the same widget as the ``date`` field type (a simple date)
|
||||
*
|
||||
* * Builds a slighly more complex, it's a datetime range (includes time)
|
||||
* spanning the whole day selected by the date widget
|
||||
*
|
||||
* @class
|
||||
* @extends openerp.base.DateField
|
||||
*/
|
||||
openerp.base.search.DateTimeField = openerp.base.search.DateField.extend(/** @lends openerp.base.search.DateTimeField# */{
|
||||
make_domain: function (name, operator, value) {
|
||||
return ['&', [name, '>=', value + ' 00:00:00'],
|
||||
[name, '<=', value + ' 23:59:59']];
|
||||
}
|
||||
});
|
||||
openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
|
||||
init: function (view_section, field, view) {
|
||||
@@ -781,16 +809,16 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
|
||||
}
|
||||
return this._super(defaults);
|
||||
},
|
||||
get_domain: function () {
|
||||
make_domain: function (name, operator, value) {
|
||||
if (this.id && this.name) {
|
||||
if (this.$element.val() === this.name) {
|
||||
return [[this.attrs.name, '=', this.id]];
|
||||
if (value === this.name) {
|
||||
return [[name, '=', this.id]];
|
||||
} else {
|
||||
delete this.id;
|
||||
delete this.name;
|
||||
}
|
||||
}
|
||||
return this._super();
|
||||
return this._super(name, operator, value);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
openerp.base.views = function(openerp) {
|
||||
|
||||
var _t = openerp.base._t;
|
||||
|
||||
/**
|
||||
* Registry for all the client actions key: tag value: widget
|
||||
*/
|
||||
@@ -450,6 +452,106 @@ openerp.base.Sidebar = openerp.base.Widget.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.TranslateDialog = openerp.base.Dialog.extend({
|
||||
dialog_title: _t("Translations"),
|
||||
init: function(view) {
|
||||
this['on_button' + _t("Save")] = this.on_button_Save;
|
||||
this['on_button' + _t("Close")] = this.on_button_Close;
|
||||
this._super(view, {
|
||||
width: '80%',
|
||||
height: '80%'
|
||||
});
|
||||
this.view = view;
|
||||
this.view_type = view.fields_view.type || '';
|
||||
this.$fields_form = null;
|
||||
this.$view_form = null;
|
||||
this.$sidebar_form = null;
|
||||
this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name });
|
||||
this.languages = null;
|
||||
this.languages_loaded = $.Deferred();
|
||||
(new openerp.base.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
|
||||
[['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
$.when(this.languages_loaded).then(function() {
|
||||
self.$element.html(QWeb.render('TranslateDialog', { widget: self }));
|
||||
self.$element.tabs();
|
||||
if (!(self.view.translatable_fields && self.view.translatable_fields.length)) {
|
||||
self.hide_tabs('fields');
|
||||
self.select_tab('view');
|
||||
}
|
||||
self.$fields_form = self.$element.find('.oe_translation_form');
|
||||
});
|
||||
return this;
|
||||
},
|
||||
on_languages_loaded: function(langs) {
|
||||
this.languages = langs;
|
||||
this.languages_loaded.resolve();
|
||||
},
|
||||
do_load_fields_values: function(callback) {
|
||||
var self = this,
|
||||
deffered = [];
|
||||
this.$fields_form.find('.oe_trad_field').val('').removeClass('touched');
|
||||
_.each(self.languages, function(lg) {
|
||||
var deff = $.Deferred();
|
||||
deffered.push(deff);
|
||||
self.rpc('/base/dataset/get', {
|
||||
model: self.view.dataset.model,
|
||||
ids: [self.view.datarecord.id],
|
||||
fields: self.translatable_fields_keys,
|
||||
context: self.view.dataset.get_context({
|
||||
'lang': lg.code
|
||||
})
|
||||
}, function(values) {
|
||||
_.each(self.translatable_fields_keys, function(f) {
|
||||
self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || '');
|
||||
});
|
||||
deff.resolve();
|
||||
});
|
||||
});
|
||||
$.when.apply(null, deffered).then(callback);
|
||||
},
|
||||
show_tabs: function() {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().show();
|
||||
}
|
||||
},
|
||||
hide_tabs: function() {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().hide();
|
||||
}
|
||||
},
|
||||
select_tab: function(name) {
|
||||
this.show_tabs(name);
|
||||
var index = this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().index() - 1;
|
||||
this.$element.tabs('select', index);
|
||||
},
|
||||
open: function(field) {
|
||||
var self = this,
|
||||
super = this._super;
|
||||
$.when(this.languages_loaded).then(function() {
|
||||
if (self.view.translatable_fields && self.view.translatable_fields.length) {
|
||||
self.do_load_fields_values(function() {
|
||||
super.call(self);
|
||||
if (field) {
|
||||
// TODO: focus and scroll to field
|
||||
}
|
||||
});
|
||||
} else {
|
||||
super.call(self);
|
||||
}
|
||||
});
|
||||
},
|
||||
on_button_Save: function() {
|
||||
this.close();
|
||||
},
|
||||
on_button_Close: function() {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.View = openerp.base.Widget.extend({
|
||||
set_default_options: function(options) {
|
||||
this.options = options || {};
|
||||
@@ -461,6 +563,12 @@ openerp.base.View = openerp.base.Widget.extend({
|
||||
action_views_ids: {}
|
||||
});
|
||||
},
|
||||
open_translate_dialog: function(field) {
|
||||
if (!this.translate_dialog) {
|
||||
this.translate_dialog = new openerp.base.TranslateDialog(this).start();
|
||||
}
|
||||
this.translate_dialog.open(field);
|
||||
},
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
*
|
||||
@@ -542,7 +650,7 @@ openerp.base.View = openerp.base.Widget.extend({
|
||||
}, {
|
||||
label: "Translate",
|
||||
callback: this.on_sidebar_translate,
|
||||
classname: 'oe_hide oe_sidebar_translate'
|
||||
classname: 'oe_sidebar_translate'
|
||||
}, {
|
||||
label: "View Log",
|
||||
callback: this.on_sidebar_view_log,
|
||||
@@ -572,6 +680,7 @@ openerp.base.View = openerp.base.Widget.extend({
|
||||
export_view.start();
|
||||
},
|
||||
on_sidebar_translate: function() {
|
||||
this.open_translate_dialog();
|
||||
},
|
||||
on_sidebar_view_log: function() {
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Header">
|
||||
<a href="/" class="company_logo_link">
|
||||
<a t-att-href="'/' + qs" class="company_logo_link">
|
||||
<div class="company_logo" />
|
||||
</a>
|
||||
<h1 class="header_title" t-if="session.session_is_valid()">
|
||||
@@ -453,6 +453,40 @@
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="TranslateDialog">
|
||||
<ul class="oe_translate_tabs">
|
||||
<li><a t-attf-href="##{widget.element_id}_fields">Fields</a></li>
|
||||
<li><a t-attf-href="##{widget.element_id}_view">View labels</a></li>
|
||||
<li><a t-attf-href="##{widget.element_id}_sidebar">Sidebar Relates</a></li>
|
||||
</ul>
|
||||
<div t-attf-id="#{widget.element_id}_fields">
|
||||
<table t-if="widget.view.translatable_fields" class="oe_frame oe_forms oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td class="oe_form_separator" width="1%" nowrap="nowrap">
|
||||
<div class="separator horizontal">Field</div>
|
||||
</td>
|
||||
<th t-foreach="widget.languages" align="left">
|
||||
<div class="separator horizontal"><t t-esc="name"/></div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr t-foreach="widget.view.translatable_fields" t-as="field">
|
||||
<td class="oe_form_frame_cell" width="1%" nowrap="nowrap">
|
||||
<label class="oe_form_label"><t t-esc="field.string"/>:</label>
|
||||
</td>
|
||||
<td t-foreach="widget.languages" t-as="lg" class="oe_form_frame_cell">
|
||||
<input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" class="oe_trad_field" style="width: 100%"/>
|
||||
<textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" class="oe_trad_field" style="width: 100%"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div t-attf-id="#{widget.element_id}_view">
|
||||
Translate view
|
||||
</div>
|
||||
<div t-attf-id="#{widget.element_id}_sidebar">
|
||||
Translate sidebar
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="TreeView">
|
||||
<h2 class="oe_view_title"><t t-esc="title"/></h2>
|
||||
<select t-if="toolbar" style="width: 30%">
|
||||
@@ -551,11 +585,14 @@
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<t t-name="ListView.rows" t-foreach="rows" t-as="row">
|
||||
<t t-call="ListView.row"/>
|
||||
<t t-name="ListView.rows" t-foreach="records.length" t-as="index">
|
||||
<t t-call="ListView.row">
|
||||
<t t-set="record" t-value="records.at(index)"/>
|
||||
<t t-set="row_parity" t-value="index_parity"/>
|
||||
</t>
|
||||
</t>
|
||||
<tr t-name="ListView.row" t-att-class="row_parity"
|
||||
t-att-data-index="row_index">
|
||||
t-att-data-id="record.get('id')">
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
@@ -569,7 +606,7 @@
|
||||
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
|
||||
t-att-class="'oe-field-cell' + (align ? ' oe-number' : '')"
|
||||
t-att-data-field="column.id">
|
||||
<t t-raw="render_cell(row.data, column)"/>
|
||||
<t t-raw="render_cell(record.toForm().data, column)"/>
|
||||
</td>
|
||||
</t>
|
||||
<td t-if="options.deletable" class='oe-record-delete' width="1">
|
||||
@@ -699,8 +736,10 @@
|
||||
<input type="text" size="1"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type" style="width: 100%"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
/>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/base/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
</t>
|
||||
<t t-name="FieldEmail">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
@@ -731,11 +770,13 @@
|
||||
</table>
|
||||
</t>
|
||||
<t t-name="FieldText">
|
||||
<textarea rows="6" style="width: 100%;"
|
||||
<textarea rows="6"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
></textarea>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/base/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
</t>
|
||||
<t t-name="FieldDate">
|
||||
<input type="text" size="1" style="width: 100%"
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
$(document).ready(function () {
|
||||
var openerp,
|
||||
create = function (o) {
|
||||
if (typeof Object.create === 'function') {
|
||||
return Object.create(o);
|
||||
}
|
||||
function Cls() {}
|
||||
Cls.prototype = o;
|
||||
return new Cls;
|
||||
};
|
||||
module('list-events', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init();
|
||||
window.openerp.base.list(openerp);
|
||||
}
|
||||
});
|
||||
test('Simple event triggering', function () {
|
||||
var e = create(openerp.base.list.Events), passed = false;
|
||||
e.bind('foo', function () { passed = true; });
|
||||
e.trigger('foo');
|
||||
ok(passed);
|
||||
});
|
||||
test('Bind all', function () {
|
||||
var e = create(openerp.base.list.Events), event = null;
|
||||
e.bind(null, function (ev) { event = ev; });
|
||||
e.trigger('foo');
|
||||
strictEqual(event, 'foo');
|
||||
});
|
||||
test('Propagate trigger params', function () {
|
||||
var e = create(openerp.base.list.Events), p = false;
|
||||
e.bind(null, function (_, param) { p = param });
|
||||
e.trigger('foo', true);
|
||||
strictEqual(p, true)
|
||||
});
|
||||
test('Bind multiple callbacks', function () {
|
||||
var e = create(openerp.base.list.Events), count;
|
||||
e.bind('foo', function () { count++; })
|
||||
.bind('bar', function () { count++; })
|
||||
.bind(null, function () { count++; })
|
||||
.bind('foo', function () { count++; })
|
||||
.bind(null, function () { count++; })
|
||||
.bind(null, function () { count++; });
|
||||
|
||||
count = 0;
|
||||
e.trigger('foo');
|
||||
strictEqual(count, 5);
|
||||
|
||||
count = 0;
|
||||
e.trigger('bar');
|
||||
strictEqual(count, 4);
|
||||
|
||||
count = 0;
|
||||
e.trigger('baz');
|
||||
strictEqual(count, 3);
|
||||
});
|
||||
test('Mixin events', function () {
|
||||
var cls = openerp.base.Class.extend({
|
||||
method: function () { this.trigger('e'); }
|
||||
});
|
||||
cls.include(openerp.base.list.Events);
|
||||
var instance = new cls, triggered = false;
|
||||
|
||||
instance.bind('e', function () { triggered = true; });
|
||||
instance.method();
|
||||
|
||||
ok(triggered);
|
||||
});
|
||||
test('Unbind all handlers', function () {
|
||||
var e = create(openerp.base.list.Events), passed = 0;
|
||||
e.bind('foo', function () { passed++; });
|
||||
e.trigger('foo');
|
||||
strictEqual(passed, 1);
|
||||
e.unbind('foo');
|
||||
e.trigger('foo');
|
||||
strictEqual(passed, 1);
|
||||
});
|
||||
test('Unbind one handler', function () {
|
||||
var e = create(openerp.base.list.Events), p1 = 0, p2 = 0,
|
||||
h1 = function () { p1++; }, h2 = function () { p2++; };
|
||||
e.bind('foo', h1);
|
||||
e.bind('foo', h2);
|
||||
e.trigger('foo');
|
||||
strictEqual(p1, 1);
|
||||
strictEqual(p2, 1);
|
||||
e.unbind('foo', h1);
|
||||
e.trigger('foo');
|
||||
strictEqual(p1, 1);
|
||||
strictEqual(p2, 2);
|
||||
});
|
||||
|
||||
module('list-records', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init();
|
||||
window.openerp.base.list(openerp);
|
||||
}
|
||||
});
|
||||
test('Basic record initialization', function () {
|
||||
var r = new openerp.base.list.Record({qux: 3});
|
||||
r.set('foo', 1);
|
||||
r.set('bar', 2);
|
||||
strictEqual(r.get('foo'), 1);
|
||||
strictEqual(r.get('bar'), 2);
|
||||
strictEqual(r.get('qux'), 3);
|
||||
});
|
||||
test('Change all the things', function () {
|
||||
var r = new openerp.base.list.Record(), changed = false, field;
|
||||
r.bind('change', function () { changed = true; });
|
||||
r.bind(null, function (e) { field = field || e.split(':')[1]});
|
||||
r.set('foo', 1);
|
||||
strictEqual(r.get('foo'), 1);
|
||||
ok(changed);
|
||||
strictEqual(field, 'foo');
|
||||
});
|
||||
test('Change single field', function () {
|
||||
var r = new openerp.base.list.Record(), changed = 0;
|
||||
r.bind('change:foo', function () { changed++; });
|
||||
r.set('foo', 1);
|
||||
r.set('bar', 1);
|
||||
strictEqual(r.get('foo'), 1);
|
||||
strictEqual(r.get('bar'), 1);
|
||||
strictEqual(changed, 1);
|
||||
});
|
||||
|
||||
module('list-collections-degenerate', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init();
|
||||
window.openerp.base.list(openerp);
|
||||
}
|
||||
});
|
||||
test('Fetch from collection', function () {
|
||||
var c = new openerp.base.list.Collection();
|
||||
strictEqual(c.length, 0);
|
||||
c.add({id: 1, value: 2});
|
||||
c.add({id: 2, value: 3});
|
||||
c.add({id: 3, value: 5});
|
||||
c.add({id: 4, value: 7});
|
||||
strictEqual(c.length, 4);
|
||||
var r = c.at(2), r2 = c.get(1);
|
||||
|
||||
ok(r instanceof openerp.base.list.Record);
|
||||
strictEqual(r.get('id'), 3);
|
||||
strictEqual(r.get('value'), 5);
|
||||
|
||||
ok(r2 instanceof openerp.base.list.Record);
|
||||
strictEqual(r2.get('id'), 1);
|
||||
strictEqual(r2.get('value'), 2);
|
||||
});
|
||||
test('Add at index', function () {
|
||||
var c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
strictEqual(c.at(1).get('value'), 10);
|
||||
equal(c.at(3), undefined);
|
||||
c.add({id:4, value: 55}, {at: 1});
|
||||
strictEqual(c.at(1).get('value'), 55);
|
||||
strictEqual(c.at(3).get('value'), 20);
|
||||
});
|
||||
test('Remove record', function () {
|
||||
var c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
var record = c.get(2);
|
||||
strictEqual(c.length, 3);
|
||||
c.remove(record);
|
||||
strictEqual(c.length, 2);
|
||||
equal(c.get(2), undefined);
|
||||
strictEqual(c.at(1).get('value'), 20);
|
||||
});
|
||||
test('Reset', function () {
|
||||
var event, obj, c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
c.bind(null, function (e, instance) { event = e; obj = instance; });
|
||||
c.reset();
|
||||
strictEqual(c.length, 0);
|
||||
strictEqual(event, 'reset');
|
||||
strictEqual(obj, c);
|
||||
c.add([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
c.reset([{id: 42, value: 55}]);
|
||||
strictEqual(c.length, 1);
|
||||
strictEqual(c.get(42).get('value'), 55);
|
||||
});
|
||||
|
||||
test('Events propagation', function () {
|
||||
var values = [];
|
||||
var c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
c.bind('change:value', function (e, record, value) {
|
||||
values.push(value);
|
||||
});
|
||||
c.get(1).set('value', 6);
|
||||
c.get(2).set('value', 11);
|
||||
c.get(3).set('value', 21);
|
||||
deepEqual(values, [6, 11, 21]);
|
||||
});
|
||||
test('BTree', function () {
|
||||
var root = new openerp.base.list.Collection(),
|
||||
c = root.proxy('admin'),
|
||||
total = 0;
|
||||
c.add({id: 1, name: "Administrator", login: 'admin'});
|
||||
c.add({id: 3, name: "Demo", login: 'demo'});
|
||||
root.bind('change:wealth', function () {
|
||||
total = (root.get(1).get('wealth') || 0) + (root.get(3).get('wealth') || 0);
|
||||
});
|
||||
|
||||
strictEqual(total, 0);
|
||||
c.at(0).set('wealth', 42);
|
||||
strictEqual(total, 42);
|
||||
c.at(1).set('wealth', 5);
|
||||
strictEqual(total, 47);
|
||||
});
|
||||
|
||||
module('list-hofs', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init();
|
||||
window.openerp.base.list(openerp);
|
||||
}
|
||||
});
|
||||
test('each, degenerate', function () {
|
||||
var c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]), ids = [];
|
||||
c.each(function (record) {
|
||||
ids.push(record.get('id'));
|
||||
});
|
||||
deepEqual(
|
||||
ids, [1, 2, 3],
|
||||
'degenerate collections should be iterated in record order');
|
||||
});
|
||||
test('each, deep', function () {
|
||||
var root = new openerp.base.list.Collection(),
|
||||
ids = [];
|
||||
root.proxy('foo').add([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}]);
|
||||
root.proxy('bar').add([
|
||||
{id: 10, value: 5},
|
||||
{id: 20, value: 10},
|
||||
{id: 30, value: 20}]);
|
||||
root.each(function (record) {
|
||||
ids.push(record.get('id'));
|
||||
});
|
||||
// No contract on sub-collection iteration order (for now anyway)
|
||||
ids.sort(function (a, b) { return a - b; });
|
||||
deepEqual(
|
||||
ids, [1, 2, 3, 10, 20, 30],
|
||||
'tree collections should be deeply iterated');
|
||||
});
|
||||
test('map, degenerate', function () {
|
||||
var c = new openerp.base.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}
|
||||
]);
|
||||
var ids = c.map(function (record) {
|
||||
return record.get('id');
|
||||
});
|
||||
deepEqual(
|
||||
ids, [1, 2, 3],
|
||||
'degenerate collections should be iterated in record order');
|
||||
});
|
||||
test('map, deep', function () {
|
||||
var root = new openerp.base.list.Collection();
|
||||
root.proxy('foo').add([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
{id: 3, value: 20}]);
|
||||
root.proxy('bar').add([
|
||||
{id: 10, value: 5},
|
||||
{id: 20, value: 10},
|
||||
{id: 30, value: 20}]);
|
||||
var ids = root.map(function (record) {
|
||||
return record.get('id');
|
||||
});
|
||||
// No contract on sub-collection iteration order (for now anyway)
|
||||
ids.sort(function (a, b) { return a - b; });
|
||||
deepEqual(
|
||||
ids, [1, 2, 3, 10, 20, 30],
|
||||
'tree collections should be deeply iterated');
|
||||
});
|
||||
});
|
||||
@@ -44,5 +44,6 @@
|
||||
<script type="text/javascript" src="/base/static/test/class.js"></script>
|
||||
<script type="text/javascript" src="/base/static/test/registry.js"></script>
|
||||
<script type="text/javascript" src="/base/static/test/form.js"></script>
|
||||
<script type="text/javascript" src="/base/static/test/list-utils.js"></script>
|
||||
<script type="text/javascript" src="/base/static/test/formats.js"></script>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
import datetime
|
||||
import urllib
|
||||
import dateutil.relativedelta
|
||||
import functools
|
||||
import optparse
|
||||
@@ -446,7 +447,11 @@ class Root(object):
|
||||
#for the mobile web client we are supposed to use a different url to just add '/mobile'
|
||||
raise cherrypy.HTTPRedirect('/web_mobile/static/src/web_mobile.html', 301)
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect('/base/webclient/home', 301)
|
||||
if kw:
|
||||
qs = '?' + urllib.urlencode(kw)
|
||||
else:
|
||||
qs = ''
|
||||
raise cherrypy.HTTPRedirect('/base/webclient/home' + qs, 301)
|
||||
default.exposed = True
|
||||
|
||||
def main(argv):
|
||||
|
||||
Reference in New Issue
Block a user