summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorJigar Amin - OpenERP <jam@tinyerp.com>2012-09-13 10:06:44 +0530
committerJigar Amin - OpenERP <jam@tinyerp.com>2012-09-13 10:06:44 +0530
commit188eff46ecade0774bf40fbafbe84e31e801b4c2 (patch)
treedf139787a70cfe8deeaa8544e3a12966aa4abc58 /addons/web/static/src/js
parent14ed53e196441985e6242b4bcad4f0ed411646a8 (diff)
parentcd004c2c77e95cf3ab3c2889806b5ce54d107874 (diff)
[merge] lp:openerp-web
bzr revid: jam@tinyerp.com-20120913043644-t41gs5n4kp8a0j0e
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/data_import.js393
-rw-r--r--addons/web/static/src/js/view_form.js33
-rw-r--r--addons/web/static/src/js/view_list.js9
-rw-r--r--addons/web/static/src/js/views.js12
4 files changed, 22 insertions, 425 deletions
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
deleted file mode 100644
index 3c677b0ec26..00000000000
--- a/addons/web/static/src/js/data_import.js
+++ /dev/null
@@ -1,393 +0,0 @@
-openerp.web.data_import = function(instance) {
-var QWeb = instance.web.qweb,
- _t = instance.web._t;
-/**
- * Safari does not deal well at all with raw JSON data being returned. As a
- * result, we're going to cheat by using a pseudo-jsonp: instead of getting
- * JSON data in the iframe, we're getting a ``script`` tag which consists of a
- * function call and the returned data (the json dump).
- *
- * The function is an auto-generated name bound to ``window``, which calls
- * back into the callback provided here.
- *
- * @param {Object} form the form element (DOM or jQuery) to use in the call
- * @param {Object} attributes jquery.form attributes object
- * @param {Function} callback function to call with the returned data
- */
-function jsonp(form, attributes, callback) {
- attributes = attributes || {};
- var options = {jsonp: _.uniqueId('import_callback_')};
- window[options.jsonp] = function () {
- delete window[options.jsonp];
- callback.apply(null, arguments);
- };
- if ('data' in attributes) {
- _.extend(attributes.data, options);
- } else {
- _.extend(attributes, {data: options});
- }
- $(form).ajaxSubmit(attributes);
-}
-
-instance.web.DataImport = instance.web.Dialog.extend({
- template: 'ImportDataView',
- dialog_title: {toString: function () { return _t("Import Data"); }},
- init: function(parent, dataset){
- var self = this;
- this._super(parent, {});
- this.model = parent.model;
- this.fields = [];
- this.all_fields = [];
- this.fields_with_defaults = [];
- this.required_fields = null;
-
- var convert_fields = function (root, prefix) {
- prefix = prefix || '';
- _(root.fields).each(function (f) {
- self.all_fields.push(prefix + f.name);
- if (f.fields) {
- convert_fields(f, prefix + f.id + '/');
- }
- });
- };
- this.ready = $.Deferred.queue().then(function () {
- self.required_fields = _(self.fields).chain()
- .filter(function (field) {
- return field.required &&
- !_.include(self.fields_with_defaults, field.id); })
- .pluck('id')
- .uniq()
- .value();
- convert_fields(self);
- self.all_fields.sort();
- });
- },
- start: function() {
- var self = this;
- this._super();
- this.open({
- buttons: [
- {text: _t("Close"), click: function() { self.destroy(); }},
- {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe_import_dialog_button'}
- ],
- close: function(event, ui) {
- self.destroy();
- }
- });
- this.toggle_import_button(false);
- this.$el.find('#csvfile').change(this.on_autodetect_data);
- this.$el.find('fieldset').change(this.on_autodetect_data);
- this.$el.delegate('fieldset legend', 'click', function() {
- $(this).parent().toggleClass('oe_closed');
- });
- this.ready.push(new instance.web.DataSet(this, this.model).call(
- 'fields_get', [], function (fields) {
- self.graft_fields(fields);
- self.ready.push(new instance.web.DataSet(self, self.model)
- .default_get(_.pluck(self.fields, 'id')).then(function (fields) {
- _.each(fields, function(val, key) {
- if (val) {
- self.fields_with_defaults.push(key);
- }
- });
- })
- )
- }));
- },
- graft_fields: function (fields, parent, level) {
- parent = parent || this;
- level = level || 0;
-
- var self = this;
- if (level === 0) {
- parent.fields.push({
- id: 'id',
- name: 'id',
- string: _t('External ID'),
- required: false
- });
- }
- _(fields).each(function (field, field_name) {
- // Ignore spec for id field
- // Don't import function fields (function and related)
- if (field_name === 'id') {
- return;
- }
- // Skip if there's no state which could disable @readonly,
- // if a field is ever always readonly we can't import it
- if (field.readonly) {
- // no states at all
- if (_.isEmpty(field.states)) { return; }
- // no state altering @readonly
- if (!_.any(field.states, function (modifiers) {
- return _(modifiers).chain().pluck(0).contains('readonly').value();
- })) { return; }
- }
-
- var f = {
- id: field_name,
- name: field_name,
- string: field.string,
- required: field.required
- };
-
- switch (field.type) {
- case 'many2many':
- case 'many2one':
- // push a copy for the bare many2one field, to allow importing
- // using name_search too - even if we default to exporting the XML ID
- var many2one_field = _.extend({}, f)
- parent.fields.push(many2one_field);
- f.name += '/id';
- break;
- case 'one2many':
- f.name += '/id';
- f.fields = [];
- // only fetch sub-fields to a depth of 2 levels
- if (level < 2) {
- self.ready.push(new instance.web.DataSet(self, field.relation).call(
- 'fields_get', [], function (fields) {
- self.graft_fields(fields, f, level+1);
- }));
- }
- break;
- }
- parent.fields.push(f);
- });
- },
- toggle_import_button: function (newstate) {
- instance.web.dialog(this.$el, 'widget')
- .find('.oe_import_dialog_button')
- .button('option', 'disabled', !newstate);
- },
- do_import: function() {
- if(!this.$el.find('#csvfile').val()) { return; }
- var lines_to_skip = parseInt(this.$el.find('#csv_skip').val(), 10);
- var with_headers = this.$el.find('#file_has_headers').prop('checked');
- if (!lines_to_skip && with_headers) {
- lines_to_skip = 1;
- }
- var indices = [], fields = [];
- this.$el.find(".sel_fields").each(function(index, element) {
- var val = element.value;
- if (!val) {
- return;
- }
- indices.push(index);
- fields.push(val);
- });
-
- jsonp(this.$el.find('#import_data'), {
- url: '/web/import/import_data',
- data: {
- model: this.model,
- meta: JSON.stringify({
- skip: lines_to_skip,
- indices: indices,
- fields: fields
- })
- }
- }, this.on_import_results);
- },
- on_autodetect_data: function() {
- if(!this.$el.find('#csvfile').val()) { return; }
- jsonp(this.$el.find('#import_data'), {
- url: '/web/import/detect_data'
- }, this.on_import_results);
- },
- on_import_results: function(results) {
- this.$el.find('#result').empty();
- var headers, result_node = this.$el.find("#result");
-
- if (results['error']) {
- result_node.append(QWeb.render('ImportView.error', {
- 'error': results['error']}));
- this.$el.find('fieldset').removeClass('oe_closed');
- return;
- }
- if (results['success']) {
- if (this.getParent().getParent().active_view == "list") {
- this.getParent().reload_content();
- }
- this.destroy();
- return;
- }
-
- if (results['records']) {
- var lines_to_skip = parseInt(this.$el.find('#csv_skip').val(), 10),
- with_headers = this.$el.find('#file_has_headers').prop('checked');
- headers = with_headers ? results.records[0] : null;
-
- result_node.append(QWeb.render('ImportView.result', {
- 'headers': headers,
- 'records': lines_to_skip ? results.records.slice(lines_to_skip)
- : with_headers ? results.records.slice(1)
- : results.records
- }));
- this.$el.find('fieldset').addClass('oe_closed');
- }
- this.$el.find('form').removeClass('oe_import_no_result');
-
- this.$el.delegate('.oe_m2o_drop_down_button', 'click', function () {
- $(this).prev('input').focus();
- });
-
- var self = this;
- this.ready.then(function () {
- var $fields = self.$el.find('.sel_fields').bind('blur', function () {
- if (this.value && !_(self.all_fields).contains(this.value)) {
- this.value = '';
- }
- }).autocomplete({
- minLength: 0,
- source: self.all_fields,
- change: self.on_check_field_values
- }).focus(function () {
- $(this).autocomplete('search');
- });
- // Column auto-detection
- _(headers).each(function (header, index) {
- var field_name = self.match_column_to_field(header);
- if (field_name) {
- $fields.eq(index).val(field_name);
- }
- });
- self.on_check_field_values();
- });
- },
- /**
- * Returns the name of the field (nested) matching the provided column name
- *
- * @param {String} name column name to look for
- * @param {Array} [fields] fields to look into for the provided name
- * @returns {String|undefined}
- */
- match_column_to_field: function (name, fields) {
- fields = fields || this.fields;
- var f;
- f = _(fields).detect(function (field) {
- return field.name === name
- });
- if (!f) {
- f = _(fields).detect(function (field) {
- // TODO: levenshtein between header and field.string
- return field.string.toLowerCase() === name.toLowerCase();
- });
- }
- if (f) { return f.name; }
-
- // if ``name`` is a path (o2m), we need to recurse through its .fields
- var index = name.indexOf('/');
- if (index === -1) { return undefined; }
- // Get the first path section, try to find the matching field
- var column_name = name.substring(0, index);
- f = _(fields).detect(function (field) {
- // field.name for o2m is $foo/id, so we want to match on id
- return field.id === column_name;
- });
- if (!f) {
- f = _(fields).detect(function (field) {
- return field.string.toLowerCase() === column_name.toLowerCase();
- });
- }
- if (!f) { return undefined; }
-
- // if we found a matching field for the first path section, recurse in
- // its own .fields to try and get the rest of the path matched
- var rest = this.match_column_to_field(
- name.substring(index+1), f.fields);
- if (!rest) { return undefined; }
- return f.id + '/' + rest;
- },
- /**
- * Looks through all the field selections, and tries to find if two
- * (or more) columns were matched to the same model field.
- *
- * Returns a map of the multiply-mapped fields to an array of offending
- * columns (not actually columns, but the inputs containing the same field
- * names).
- *
- * Also has the side-effect of marking the discovered inputs with the class
- * ``duplicate_fld``.
- *
- * @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
- */
- find_duplicate_fields: function() {
- // Maps values to DOM nodes, in order to discover duplicates
- var values = {}, duplicates = {};
- this.$el.find(".sel_fields").each(function(index, element) {
- var value = element.value;
- var $el = $(element).removeClass('duplicate_fld');
- if (!value) { return; }
-
- if (!(value in values)) {
- values[value] = element;
- } else {
- var same_valued_field = values[value];
- if (value in duplicates) {
- duplicates[value].push(element);
- } else {
- duplicates[value] = [same_valued_field, element];
- }
- $el.add(same_valued_field).addClass('duplicate_fld');
- }
- });
- return duplicates;
- },
- on_check_field_values: function () {
- this.$el.find("#message, #msg").remove();
-
- var required_valid = this.check_required();
-
- var duplicates = this.find_duplicate_fields();
- if (_.isEmpty(duplicates)) {
- this.toggle_import_button(required_valid);
- } else {
- var $err = $('<div id="msg" style="color: red;">'+_t("Destination fields should only be selected once, some fields are selected more than once:")+'</div>').insertBefore(this.$el.find('#result'));
- var $dupes = $('<dl>').appendTo($err);
- _(duplicates).each(function(elements, value) {
- $('<dt>').text(value).appendTo($dupes);
- _(elements).each(function(element) {
- var cell = $(element).closest('td');
- $('<dd>').text(cell.parent().children().index(cell)).appendTo($dupes);
- });
- });
- this.toggle_import_button(false);
- }
-
- },
- check_required: function() {
- var self = this;
- if (!self.required_fields.length) { return true; }
-
- // Resolve field id based on column name, as there may be
- // several ways to provide the value for a given field and
- // thus satisfy the requirement.
- // (e.g. m2o_id or m2o_id/id columns may be provided)
- var resolve_field_id = function(column_name) {
- var f = _.detect(self.fields, function(field) {
- return field.name === column_name;
- });
- if (!f) { return column_name; };
- return f.id;
- };
-
- var selected_fields = _(this.$el.find('.sel_fields').get()).chain()
- .pluck('value')
- .compact()
- .map(resolve_field_id)
- .value();
-
- var missing_fields = _.difference(this.required_fields, selected_fields);
- if (missing_fields.length) {
- this.$el.find("#result").before('<div id="message" style="color:red">' + _t("*Required Fields are not selected :") + missing_fields + '.</div>');
- return false;
- }
- return true;
- },
- destroy: function() {
- this.$el.remove();
- this._super();
- }
-});
-};
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 08c9b96660e..7d328f012fe 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -321,7 +321,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
this.datarecord = record;
this._actualize_mode();
- this.set({ 'title' : record.id ? record.display_name : "New record" });
+ this.set({ 'title' : record.id ? record.display_name : "New" });
if (this.qweb) {
this.kill_current_form();
@@ -1399,6 +1399,10 @@ 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')) {
+ $new_notebook.tabs('select', i);
+ return;
+ }
var current = $new_notebook.tabs("option", "selected");
if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible"))
return;
@@ -2400,10 +2404,6 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc
template: 'FieldTextHtml',
init: function() {
this._super.apply(this, arguments);
- if (this.field.type !== 'html' && ! this.options.safe) {
- throw new Error(_.str.sprintf(
- _t("Error with field %s, it is not allowed to use the widget 'html' with any other field type than 'html'"), this.string));
- }
},
initialize_content: function() {
var self = this;
@@ -2437,7 +2437,7 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc
},
render_value: function() {
if (! this.get("effective_readonly")) {
- this.$textarea.val(this.get('value'));
+ this.$textarea.val(this.get('value') || '');
this._updating_editor = true;
this.$cleditor.updateFrame();
this._updating_editor = false;
@@ -2712,8 +2712,10 @@ instance.web.form.CompletionFieldMixin = {
_create_context: function(name) {
var tmp = {};
var field = (this.options || {}).create_name_field;
- if (field !== false && (this.options || {}).quick_create !== false)
- tmp["default_" + field] = name || "name";
+ if (field === undefined)
+ field = "name";
+ if (field !== false && name && (this.options || {}).quick_create !== false)
+ tmp["default_" + field] = name;
return tmp;
},
};
@@ -2773,7 +2775,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
this.$drop_down = this.$el.find(".oe_m2o_drop_down_button");
this.$follow_button = $(".oe_m2o_cm_button", this.$el);
- this.$follow_button.click(function() {
+ this.$follow_button.click(function(ev) {
+ ev.preventDefault();
if (!self.get('value')) {
self.focus();
return;
@@ -2951,14 +2954,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
var lines = _.escape(str).split("\n");
var link = "";
var follow = "";
- if (! this.options.highlight_first_line) {
- link = lines.join("<br />");
- } else {
- link = lines[0];
- follow = _.rest(lines).join("<br />");
- if (follow)
- link += "<br />";
- }
+ link = lines[0];
+ follow = _.rest(lines).join("<br />");
+ if (follow)
+ link += "<br />";
var $link = this.$el.find('.oe_form_uri')
.unbind('click')
.html(link);
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index df61e9bd6bd..015cd1636f1 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -21,8 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
- // if true, the 'Import', 'Export', etc... buttons will be shown
- 'import_enabled': true,
},
/**
* Core class for list-type displays.
@@ -281,7 +279,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
self.reload_content();
});
- // Add button and Import link
+ // Add button
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
if (this.options.$buttons) {
@@ -292,10 +290,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
.prop('disabled', grouped);
- this.$buttons.on('click', '.oe_list_button_import', function() {
- self.on_sidebar_import();
- return false;
- });
}
// Pager
@@ -358,7 +352,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.sidebar = new instance.web.Sidebar(this);
this.sidebar.appendTo(this.options.$sidebar);
this.sidebar.add_items('other', _.compact([
- self.is_action_enabled('create') && { label: _t("Import"), callback: this.on_sidebar_import },
{ label: _t("Export"), callback: this.on_sidebar_export },
self.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
]));
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index d0a5b1841bb..eab00d8b295 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -20,7 +20,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$el.on('click', '.oe_breadcrumb_item', this.on_breadcrumb_clicked);
+ this.$el.on('click', 'a.oe_breadcrumb_item', this.on_breadcrumb_clicked);
},
dialog_stop: function () {
if (this.dialog) {
@@ -95,7 +95,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
break;
}
}
- var subindex = $e.parent().find('.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
+ var subindex = $e.parent().find('a.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
this.select_breadcrumb(index, subindex);
},
select_breadcrumb: function(index, subindex) {
@@ -148,7 +148,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
for (var j = 0; j < tit.length; j += 1) {
var label = _.escape(tit[j]);
if (i === this.breadcrumbs.length - 1 && j === tit.length - 1) {
- titles.push(label);
+ titles.push(_.str.sprintf('<span class="oe_breadcrumb_item">%s</span>', label));
} else {
titles.push(_.str.sprintf('<a href="#" class="oe_breadcrumb_item" data-id="%s">%s</a>', item.id, label));
}
@@ -566,7 +566,8 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
return controller.get('title');
});
- if (next && next.action && next.action.res_id && self.active_view === 'form' && self.model === next.action.res_model && id === next.action.res_id) {
+ if (next && next.action && next.action.res_id && self.dataset &&
+ self.active_view === 'form' && self.dataset.model === next.action.res_model && id === next.action.res_id) {
// If the current active view is a formview and the next item in the breadcrumbs
// is an action on same object (model / res_id), then we omit the current formview's title
titles.pop();
@@ -1270,9 +1271,6 @@ instance.web.View = instance.web.Widget.extend({
},
do_search: function(view) {
},
- on_sidebar_import: function() {
- new instance.web.DataImport(this, this.dataset).open();
- },
on_sidebar_export: function() {
new instance.web.DataExport(this, this.dataset).open();
},