diff options
| author | Jigar Amin - OpenERP <jam@tinyerp.com> | 2012-09-13 10:06:44 +0530 |
|---|---|---|
| committer | Jigar Amin - OpenERP <jam@tinyerp.com> | 2012-09-13 10:06:44 +0530 |
| commit | 188eff46ecade0774bf40fbafbe84e31e801b4c2 (patch) | |
| tree | df139787a70cfe8deeaa8544e3a12966aa4abc58 /addons/web/static/src/js | |
| parent | 14ed53e196441985e6242b4bcad4f0ed411646a8 (diff) | |
| parent | cd004c2c77e95cf3ab3c2889806b5ce54d107874 (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.js | 393 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_form.js | 33 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list.js | 9 | ||||
| -rw-r--r-- | addons/web/static/src/js/views.js | 12 |
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(); }, |
