diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index e23246d7288..87af7474daf 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -13,6 +13,7 @@ import os import re import simplejson import time +import urllib import urllib2 import zlib from xml.etree import ElementTree @@ -293,9 +294,9 @@ def manifest_list(req, extension, mods=None, db=None): if not req.debug: path = '/web/webclient/' + extension if mods is not None: - path += '?mods=' + mods + path += '?' + urllib.urlencode({'mods': mods}) elif db: - path += '?db=' + db + path += '?' + urllib.urlencode({'db': db}) return [path] files = manifest_glob(req, extension, addons=mods, db=db) i_am_diabetic = req.httprequest.environ["QUERY_STRING"].count("no_sugar") >= 1 or \ @@ -1310,7 +1311,7 @@ class Binary(openerpweb.Controller): 'id': attachment_id } except Exception: - args = {'error':e.faultCode } + args = {'error': "Something horrible happened"} return out % (simplejson.dumps(callback), simplejson.dumps(args)) @openerpweb.httprequest diff --git a/addons/web/controllers/testing.py b/addons/web/controllers/testing.py index 5692139f002..b6327bf4622 100644 --- a/addons/web/controllers/testing.py +++ b/addons/web/controllers/testing.py @@ -32,12 +32,12 @@ NOMODULE_TEMPLATE = Template(u""" -""") +""", default_filters=['h']) NOTFOUND = Template(u"""

Unable to find the module [${module}], please check that the module name is correct and the module is on OpenERP's path.

<< Back to tests -""") +""", default_filters=['h']) TESTING = Template(u""" <%def name="to_path(module, p)">/${module}/${p} @@ -51,9 +51,9 @@ TESTING = Template(u""" @@ -83,7 +83,7 @@ TESTING = Template(u""" % endif % endfor -""") +""", default_filters=['h']) class TestRunnerController(http.Controller): _cp_path = '/web/tests' diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 5d5ca46b4ec..1c4f2509fa7 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,4 +1,4 @@ -@charset "UTF-8"; +@charset "utf-8"; @font-face { font-family: "mnmliconsRegular"; src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); @@ -1263,7 +1263,7 @@ color: white; padding: 2px 4px; margin: 1px 6px 0 0; - border: 1px solid lightgrey; + border: 1px solid lightGray; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1295,7 +1295,7 @@ transform: scale(1.1); } .openerp .oe_secondary_submenu .oe_active { - border-top: 1px solid lightgrey; + border-top: 1px solid lightGray; border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); @@ -1415,7 +1415,13 @@ display: inline-block; overflow: hidden; } +.openerp .oe_view_manager { + display: table; + height: inherit; + width: 100%; +} .openerp .oe_view_manager .oe_view_manager_body { + display: table-row; height: inherit; } .openerp .oe_view_manager .oe_view_manager_view_kanban { @@ -2251,7 +2257,7 @@ } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; - color: darkgreen; + color: darkGreen; vertical-align: top; position: relative; top: -4px; @@ -2323,6 +2329,7 @@ width: 100%; display: inline-block; padding: 2px 2px 2px 0px; + vertical-align: top; } .openerp .oe_form .oe_form_field input { margin: 0px; @@ -2366,7 +2373,6 @@ white-space: nowrap; } .openerp .oe_form .oe_form_field_boolean { - padding-top: 4px; width: auto; } .openerp .oe_form .oe_datepicker_container { @@ -3088,8 +3094,25 @@ color: #333333; } +@-moz-document url-prefix() { + .openerp .oe_view_manager .oe_view_manager_switch li { + line-height: 21px; + } + .openerp .oe_searchview .oe_searchview_search { + top: -1px; + } + .openerp .oe_form_field_many2one .oe_m2o_cm_button { + line-height: 18px; + } + .openerp .oe_secondary_submenu { + line-height: 14px; + } + .openerp .oe_webclient .oe_star_on, .openerp .oe_webclient .oe_star_off { + top: 0px; + } +} + .kitten-mode-activated { - background-image: url(http://placekitten.com/g/1365/769); background-size: cover; background-attachment: fixed; } @@ -3318,6 +3341,7 @@ div.ui-widget-overlay { overflow: hidden !important; } } + .blockUI.blockOverlay { background-color: black; opacity: 0.6; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index b4665808caf..0a73728678d 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1137,7 +1137,11 @@ $sheet-padding: 16px // }}} // ViewManager common {{{ .oe_view_manager + display: table + height: inherit + width: 100% .oe_view_manager_body + display: table-row height: inherit .oe_view_manager_view_kanban height: inherit @@ -1845,6 +1849,7 @@ $sheet-padding: 16px width: 100% display: inline-block padding: 2px 2px 2px 0px + vertical-align: top input margin: 0px input[type="text"], input[type="password"], input[type="file"], select @@ -1872,7 +1877,6 @@ $sheet-padding: 16px .oe_form_field_datetime white-space: nowrap .oe_form_field_boolean - padding-top: 4px width: auto .oe_datepicker_container display: none @@ -2434,9 +2438,22 @@ $sheet-padding: 16px float: right color: #333 // }}} +@-moz-document url-prefix() + .openerp + .oe_view_manager .oe_view_manager_switch li + line-height: 21px + .oe_searchview .oe_searchview_search + top: -1px + .oe_form_field_many2one .oe_m2o_cm_button + line-height: 18px + .oe_secondary_submenu + line-height: 14px + .oe_webclient + .oe_star_on, .oe_star_off + top: 0px + // Kitten Mode {{{ .kitten-mode-activated - background-image: url(http://placekitten.com/g/1365/769) background-size: cover background-attachment: fixed >* diff --git a/addons/web/static/src/img/back-enable.jpg b/addons/web/static/src/img/back-enable.jpg new file mode 100644 index 00000000000..79a7098f6da Binary files /dev/null and b/addons/web/static/src/img/back-enable.jpg differ diff --git a/addons/web/static/src/img/icons/gtk-normal.png b/addons/web/static/src/img/icons/gtk-normal.png new file mode 100644 index 00000000000..c153cc1c528 Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-normal.png differ diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 86d0a20ed20..eaa4bcbfe1c 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1084,7 +1084,7 @@ instance.web.UserMenu = instance.web.Widget.extend({ if (!self.session.uid) return; var func = new instance.web.Model("res.users").get_func("read"); - return func(self.session.uid, ["name", "company_id"]).then(function(res) { + return self.alive(func(self.session.uid, ["name", "company_id"])).then(function(res) { var topbar_name = res.name; if(instance.session.debug) topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.session.db); @@ -1100,6 +1100,9 @@ instance.web.UserMenu = instance.web.Widget.extend({ }; this.update_promise = this.update_promise.then(fct, fct); }, + on_menu_help: function() { + window.open('http://help.openerp.com', '_blank'); + }, on_menu_logout: function() { this.trigger('user_logout'); }, @@ -1213,6 +1216,7 @@ instance.web.WebClient = instance.web.Client.extend({ return $.when(this._super()).then(function() { if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) { $("body").addClass("kitten-mode-activated"); + $("body").css("background-image", "url(" + instance.session.origin + "/web/static/src/img/back-enable.jpg" + ")"); if ($.blockUI) { $.blockUI.defaults.message = ''; } @@ -1299,7 +1303,7 @@ instance.web.WebClient = instance.web.Client.extend({ }, logo_edit: function(ev) { var self = this; - new instance.web.Model("res.users").get_func("read")(this.session.uid, ["company_id"]).then(function(res) { + new self.alive(instance.web.Model("res.users").get_func("read")(this.session.uid, ["company_id"])).then(function(res) { self.rpc("/web/action/load", { action_id: "base.action_res_company_form" }).done(function(result) { result.res_id = res['company_id'][0]; result.target = "new"; @@ -1320,7 +1324,7 @@ instance.web.WebClient = instance.web.Client.extend({ }, check_timezone: function() { var self = this; - return new instance.web.Model('res.users').call('read', [[this.session.uid], ['tz_offset']]).then(function(result) { + return self.alive(new instance.web.Model('res.users').call('read', [[this.session.uid], ['tz_offset']])).then(function(result) { var user_offset = result[0]['tz_offset']; var offset = -(new Date().getTimezoneOffset()); // _.str.sprintf()'s zero front padding is buggy with signed decimals, so doing it manually @@ -1396,8 +1400,9 @@ instance.web.WebClient = instance.web.Client.extend({ }, on_hashchange: function(event) { var self = this; - var state = event.getState(true); - if (!_.isEqual(this._current_state, state)) { + var stringstate = event.getState(false); + if (!_.isEqual(this._current_state, stringstate)) { + var state = event.getState(true); if(!state.action && state.menu_id) { self.menu.has_been_loaded.done(function() { self.menu.do_reload().done(function() { @@ -1409,13 +1414,13 @@ instance.web.WebClient = instance.web.Client.extend({ this.action_manager.do_load_state(state, !!this._current_state); } } - this._current_state = state; + this._current_state = stringstate; }, do_push_state: function(state) { this.set_title(state.title); delete state.title; var url = '#' + $.param(state); - this._current_state = _.clone(state); + this._current_state = $.deparam($.param(state), false); // stringify all values $.bbq.pushState(url); this.trigger('state_pushed', state); }, @@ -1425,9 +1430,10 @@ instance.web.WebClient = instance.web.Client.extend({ .then(function (result) { return self.action_mutex.exec(function() { if (options.needaction) { - result.context = new instance.web.CompoundContext( - result.context, - {search_default_message_unread: true}); + result.context = new instance.web.CompoundContext(result.context, { + search_default_message_unread: true, + search_disable_custom_filters: true, + }); } var completed = $.Deferred(); $.when(self.action_manager.do_action(result, { diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index edd23e14522..63e22672bd7 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -228,6 +228,38 @@ instance.web.ParentedMixin = { isDestroyed : function() { return this.__parentedDestroyed; }, + /** + Utility method to only execute asynchronous actions if the current + object has not been destroyed. + + @param {Promise} promise The promise representing the asynchronous action. + @param {bool} reject Defaults to false. If true, the returned promise will be + rejected with no arguments if the current object is destroyed. If false, + the returned promise will never be resolved nor rejected. + @returns {Promise} A promise that will mirror the given promise if everything goes + fine but will either be rejected with no arguments or never resolved if the + current object is destroyed. + */ + alive: function(promise, reject) { + var def = $.Deferred(); + var self = this; + promise.done(function() { + if (! self.isDestroyed()) { + if (! reject) + def.resolve.apply(def, arguments); + else + def.reject(); + } + }).fail(function() { + if (! self.isDestroyed()) { + if (! reject) + def.reject.apply(def, arguments); + else + def.reject(); + } + }); + return def.promise(); + }, /** * Inform the object it should destroy itself, releasing any * resource it could have reserved. @@ -495,16 +527,7 @@ instance.web.Controller = instance.web.Class.extend(instance.web.PropertiesMixin return false; }, rpc: function(url, data, options) { - var def = $.Deferred(); - var self = this; - instance.session.rpc(url, data, options).done(function() { - if (!self.isDestroyed()) - def.resolve.apply(def, arguments); - }).fail(function() { - if (!self.isDestroyed()) - def.reject.apply(def, arguments); - }); - return def.promise(); + return this.alive(instance.session.rpc(url, data, options)); } }); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 12acf4039f4..edbec1626b0 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -690,7 +690,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea return filter.user_id && filter.is_default; }); if (personal_filter) { - this.custom_filters.enable_filter(personal_filter, true); + this.custom_filters.toggle_filter(personal_filter, true); return; } @@ -698,7 +698,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea return !filter.user_id && filter.is_default; }); if (global_filter) { - this.custom_filters.enable_filter(global_filter, true); + this.custom_filters.toggle_filter(global_filter, true); return; } } @@ -1466,12 +1466,15 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ }, complete: function (needle) { var self = this; - // TODO: context // FIXME: "concurrent" searches (multiple requests, mis-ordered responses) + var context = instance.web.pyeval.eval( + 'contexts', [this.view.dataset.get_context()]); return this.model.call('name_search', [], { name: needle, + args: instance.web.pyeval.eval( + 'domains', this.attrs.domain ? [this.attrs.domain] : [], context), limit: 8, - context: {} + context: context }).then(function (results) { if (_.isEmpty(results)) { return null; } return [{label: self.attrs.string}].concat( @@ -1541,6 +1544,9 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ }) .on('reset', this.proxy('clear_selection')); this.$el.on('submit', 'form', this.proxy('save_current')); + this.$el.on('click', 'input[type=checkbox]', function() { + $(this).siblings('input[type=checkbox]').prop('checked', false); + }); this.$el.on('click', 'h4', function () { self.$el.toggleClass('oe_opened'); }); @@ -1591,6 +1597,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ get_groupby: function () { return [filter.context]; }, get_domain: function () { return filter.domain; } }, + _id: filter['id'], is_custom_filter: true, values: [{label: filter.name, value: null}] }; @@ -1632,10 +1639,18 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ } $filter.unbind('click').click(function () { - self.enable_filter(filter); + self.toggle_filter(filter); }); }, - enable_filter: function (filter, preventSearch) { + toggle_filter: function (filter, preventSearch) { + var current = this.view.query.find(function (facet) { + return facet.get('_id') === filter.id; + }); + if (current) { + this.view.query.remove(current); + this.$filters[this.key_for(filter)].removeClass('oe_selected'); + return; + } this.view.query.reset([this.facet_for(filter)], { preventSearch: preventSearch || false}); this.$filters[this.key_for(filter)].addClass('oe_selected'); @@ -1800,6 +1815,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @ template: 'SearchView.extended_search.proposition', events: { 'change .searchview_extended_prop_field': 'changed', + 'change .searchview_extended_prop_op': 'operator_changed', 'click .searchview_extended_delete_prop': function (e) { e.stopPropagation(); this.getParent().remove_proposition(this); @@ -1831,6 +1847,17 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @ this.select_field(_.detect(this.fields, function(x) {return x.name == nval;})); } }, + operator_changed: function (e) { + var $value = this.$('.searchview_extended_prop_value'); + switch ($(e.target).val()) { + case '∃': + case '∄': + $value.hide(); + break; + default: + $value.show(); + } + }, /** * Selects the provided field object * @@ -1859,7 +1886,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @ .text(String(operator.text)) .appendTo(self.$('.searchview_extended_prop_op')); }); - var $value_loc = this.$('.searchview_extended_prop_value').empty(); + var $value_loc = this.$('.searchview_extended_prop_value').show().empty(); this.value.appendTo($value_loc); }, @@ -1867,19 +1894,12 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @ if ( this.attrs.selected == null) return null; var field = this.attrs.selected; - var op = this.$('.searchview_extended_prop_op')[0]; - var operator = op.options[op.selectedIndex]; + var op_select = this.$('.searchview_extended_prop_op')[0]; + var operator = op_select.options[op_select.selectedIndex]; + return { - label: _.str.sprintf(_t('%(field)s %(operator)s "%(value)s"'), { - field: field.string, - // According to spec, HTMLOptionElement#label should return - // HTMLOptionElement#text when not defined/empty, but it does - // not in older Webkit (between Safari 5.1.5 and Chrome 17) and - // Gecko (pre Firefox 7) browsers, so we need a manual fallback - // for those - operator: operator.label || operator.text, - value: this.value}), - value: [field.name, operator.value, this.value.get_value()] + label: this.value.get_label(field, operator), + value: this.value.get_domain(field, operator), }; } }); @@ -1889,6 +1909,37 @@ instance.web.search.ExtendedSearchProposition.Field = instance.web.Widget.extend this._super(parent); this.field = field; }, + get_label: function (field, operator) { + var format; + switch (operator.value) { + case '∃': case '∄': format = _t('%(field)s %(operator)s'); break; + default: format = _t('%(field)s %(operator)s "%(value)s"'); break; + } + return this.format_label(format, field, operator); + }, + format_label: function (format, field, operator) { + return _.str.sprintf(format, { + field: field.string, + // According to spec, HTMLOptionElement#label should return + // HTMLOptionElement#text when not defined/empty, but it does + // not in older Webkit (between Safari 5.1.5 and Chrome 17) and + // Gecko (pre Firefox 7) browsers, so we need a manual fallback + // for those + operator: operator.label || operator.text, + value: this + }); + }, + get_domain: function (field, operator) { + switch (operator.value) { + case '∃': return this.make_domain(field.name, '!=', false); + case '∄': return this.make_domain(field.name, '=', false); + default: return this.make_domain( + field.name, operator.value, this.get_value()); + } + }, + make_domain: function (field, operator, value) { + return [field, operator, value]; + }, /** * Returns a human-readable version of the value, in case the "logical" * and the "semantic" values of a field differ (as for selection fields, @@ -1908,7 +1959,9 @@ instance.web.search.ExtendedSearchProposition.Char = instance.web.search.Extende {value: "ilike", text: _lt("contains")}, {value: "not ilike", text: _lt("doesn't contain")}, {value: "=", text: _lt("is equal to")}, - {value: "!=", text: _lt("is not equal to")} + {value: "!=", text: _lt("is not equal to")}, + {value: "∃", text: _lt("is set")}, + {value: "∄", text: _lt("is not set")} ], get_value: function() { return this.$el.val(); @@ -1922,7 +1975,9 @@ instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.Ext {value: ">", text: _lt("greater than")}, {value: "<", text: _lt("less than")}, {value: ">=", text: _lt("greater or equal than")}, - {value: "<=", text: _lt("less or equal than")} + {value: "<=", text: _lt("less or equal than")}, + {value: "∃", text: _lt("is set")}, + {value: "∄", text: _lt("is not set")} ], /** * Date widgets live in view_form which is not yet loaded when this is @@ -1956,7 +2011,9 @@ instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.Exte {value: ">", text: _lt("greater than")}, {value: "<", text: _lt("less than")}, {value: ">=", text: _lt("greater or equal than")}, - {value: "<=", text: _lt("less or equal than")} + {value: "<=", text: _lt("less or equal than")}, + {value: "∃", text: _lt("is set")}, + {value: "∄", text: _lt("is not set")} ], toString: function () { return this.$el.val(); @@ -1981,7 +2038,9 @@ instance.web.search.ExtendedSearchProposition.Float = instance.web.search.Extend {value: ">", text: _lt("greater than")}, {value: "<", text: _lt("less than")}, {value: ">=", text: _lt("greater or equal than")}, - {value: "<=", text: _lt("less or equal than")} + {value: "<=", text: _lt("less or equal than")}, + {value: "∃", text: _lt("is set")}, + {value: "∄", text: _lt("is not set")} ], toString: function () { return this.$el.val(); @@ -1999,7 +2058,9 @@ instance.web.search.ExtendedSearchProposition.Selection = instance.web.search.Ex template: 'SearchView.extended_search.proposition.selection', operators: [ {value: "=", text: _lt("is")}, - {value: "!=", text: _lt("is not")} + {value: "!=", text: _lt("is not")}, + {value: "∃", text: _lt("is set")}, + {value: "∄", text: _lt("is not set")} ], toString: function () { var select = this.$el[0]; @@ -2016,7 +2077,10 @@ instance.web.search.ExtendedSearchProposition.Boolean = instance.web.search.Exte {value: "=", text: _lt("is true")}, {value: "!=", text: _lt("is false")} ], - toString: function () { return ''; }, + get_label: function (field, operator) { + return this.format_label( + _t('%(field)s %(operator)s'), field, operator); + }, get_value: function() { return true; } diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 87bf7c1c49a..9432a106f6f 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -247,13 +247,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM do_load_state: function(state, warm) { if (state.id && this.datarecord.id != state.id) { - if (!this.dataset.get_id_index(state.id)) { + if (this.dataset.get_id_index(state.id) === null) { this.dataset.ids.push(state.id); } this.dataset.select_id(state.id); - if (warm) { - this.do_show(); - } + this.do_show({ reload: warm }); } }, /** @@ -511,9 +509,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var on_change = widget.node.attrs.on_change; if (on_change) { var change_spec = self.parse_on_change(on_change, widget); - var id = [self.datarecord.id == null ? [] : [self.datarecord.id]]; - def = new instance.web.Model(self.dataset.model).call( - change_spec.method, id.concat(change_spec.args)); + var ids = []; + if (self.datarecord.id && !instance.web.BufferedDataSet.virtual_id_regex.test(self.datarecord.id)) { + // In case of a o2m virtual id, we should pass an empty ids list + ids.push(self.datarecord.id); + } + def = self.alive(new instance.web.Model(self.dataset.model).call( + change_spec.method, [ids].concat(change_spec.args))); } else { def = $.when({}); } @@ -531,9 +533,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var condition = fieldname + '=' + value_; if (value_) { - return new instance.web.Model('ir.values').call( + return self.alive(new instance.web.Model('ir.values').call( 'get_defaults', [self.model, condition] - ).then(function (results) { + )).then(function (results) { if (!results.length) { return response; } @@ -1191,6 +1193,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt $('button', doc).each(function() { $(this).attr('data-button-type', $(this).attr('type')).attr('type', 'button'); }); + // IE's html parser is also a css parser. How convenient... + $('board', doc).each(function() { + $(this).attr('layout', $(this).attr('style')); + }); return $('
').append(instance.web.xml_to_str(doc)); }, render_to: function($target) { @@ -2327,7 +2333,7 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({ this._super(); } else { var tmp = this.get('value'); - var s = /(\w+):(.+)/.exec(tmp); + var s = /(\w+):(.+)|^\.{0,2}\//.exec(tmp); if (!s) { tmp = "http://" + this.get('value'); } @@ -2376,6 +2382,7 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({ type_of_date: "datetime", events: { 'change .oe_datepicker_master': 'change_datetime', + 'dragstart img.oe_datepicker_trigger': function () { return false; }, }, init: function(parent) { this._super(parent); @@ -2395,6 +2402,11 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({ showButtonPanel: true, firstDay: Date.CultureInfo.firstDayOfWeek }); + // Some clicks in the datepicker dialog are not stopped by the + // datepicker and "bubble through", unexpectedly triggering the bus's + // click event. Prevent that. + this.picker('widget').click(function (e) { e.stopPropagation(); }); + this.$el.find('img.oe_datepicker_trigger').click(function() { if (self.get("effective_readonly") || self.picker('widget').is(':visible')) { self.$input.focus(); @@ -2970,7 +2982,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc case $.ui.keyCode.DOWN: e.stopPropagation(); } - } + }, + 'dragstart .oe_m2o_drop_down_button img': function () { return false; }, + 'dragstart .oe_m2o_cm_button': function () { return false; } }, init: function(field_manager, node) { this._super(field_manager, node); @@ -3060,9 +3074,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc this.$input.keydown(input_changed); this.$input.change(input_changed); this.$drop_down.click(function() { + self.$input.focus(); if (self.$input.autocomplete("widget").is(":visible")) { - self.$input.autocomplete("close"); - self.$input.focus(); + self.$input.autocomplete("close"); } else { if (self.get("value") && ! self.floating) { self.$input.autocomplete("search", ""); @@ -3071,6 +3085,15 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc } } }); + + // Autocomplete close on dialog content scroll + var close_autocomplete = _.debounce(function() { + if (self.$input.autocomplete("widget").is(":visible")) { + self.$input.autocomplete("close"); + } + }, 50); + this.$input.closest(".ui-dialog .ui-dialog-content").on('scroll', this, close_autocomplete); + self.ed_def = $.Deferred(); self.uned_def = $.Deferred(); var ed_delay = 200; @@ -4107,6 +4130,13 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in focus: function () { this.$text[0].focus(); }, + set_dimensions: function (height, width) { + this._super(height, width); + this.$("textarea").css({ + width: width, + minHeight: height + }); + }, }); /** @@ -4463,6 +4493,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ * options: * -readonly: only applicable when not in creation mode, default to false * - alternative_form_view + * - view_id * - write_function * - read_function * - create_function @@ -4529,7 +4560,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ _.extend(options, { $buttons: this.$buttonpane, }); - this.view_form = new instance.web.FormView(this, this.dataset, false, options); + this.view_form = new instance.web.FormView(this, this.dataset, this.options.view_id || false, options); if (this.options.alternative_form_view) { this.view_form.set_embedded_view(this.options.alternative_form_view); } @@ -4558,6 +4589,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ }); var $cbutton = self.$buttonpane.find(".oe_abstractformpopup-form-close"); $cbutton.click(function() { + self.view_form.trigger('on_button_cancel'); self.check_exit(); }); self.view_form.do_show(); @@ -5236,8 +5268,20 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false; this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false; this.set({value: false}); + this.selection = []; + this.set("selection", []); + this.selection_dm = new instance.web.DropMisordered(); }, start: function() { + this.field_manager.on("view_content_has_changed", this, this.calc_domain); + this.calc_domain(); + this.on("change:value", this, this.get_selection); + this.on("change:evaluated_selection_domain", this, this.get_selection); + this.get_selection(); + this.on("change:selection", this, function() { + this.selection = this.get("selection"); + this.render_value(); + }); if (this.options.clickable) { this.$el.on('click','li',this.on_click_stage); } @@ -5254,15 +5298,20 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ }, render_value: function() { var self = this; - self.get_selection().done(function() { - var content = QWeb.render("FieldStatus.content", {widget: self}); - self.$el.html(content); - var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}"); - var color = colors[self.get('value')]; - if (color) { - self.$("oe_active").css("color", color); - } - }); + var content = QWeb.render("FieldStatus.content", {widget: self}); + self.$el.html(content); + var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}"); + var color = colors[self.get('value')]; + if (color) { + self.$("oe_active").css("color", color); + } + }, + calc_domain: function() { + var d = instance.web.pyeval.eval('domain', this.build_domain()); + domain = ['|', ['id', '=', this.get('value')]].concat(d); + if (! _.isEqual(domain, this.get("evaluated_selection_domain"))) { + this.set("evaluated_selection_domain", domain); + } }, /** Get the selection and render it * selection: [[identifier, value_to_display], ...] @@ -5271,32 +5320,37 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ */ get_selection: function() { var self = this; - self.selection = []; - if (this.field.type == "many2one") { - var domain = []; - if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) { - var d = instance.web.pyeval.eval('domain', self.build_domain()); - domain = ['|', ['id', '=', self.get('value')]].concat(d); - } - var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain); - return ds.read_slice(['name'], {}).then(function (records) { - for(var i = 0; i < records.length; i++) { - self.selection.push([records[i].id, records[i].name]); - } - }); - } else { - // For field type selection filter values according to - // statusbar_visible attribute of the field. For example: - // statusbar_visible="draft,open". - var selection = this.field.selection; - for(var i=0; i < selection.length; i++) { - var key = selection[i][0]; - if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) { - this.selection.push(selection[i]); + var selection = []; + + var calculation = _.bind(function() { + if (this.field.type == "many2one") { + var domain = []; + var ds = new instance.web.DataSetSearch(this, this.field.relation, + self.build_context(), this.get("evaluated_selection_domain")); + return ds.read_slice(['name'], {}).then(function (records) { + for(var i = 0; i < records.length; i++) { + selection.push([records[i].id, records[i].name]); + } + }); + } else { + // For field type selection filter values according to + // statusbar_visible attribute of the field. For example: + // statusbar_visible="draft,open". + var select = this.field.selection; + for(var i=0; i < select.length; i++) { + var key = select[i][0]; + if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) { + selection.push(select[i]); + } } + return $.when(); } - return $.when(); - } + }, this); + this.selection_dm.add(calculation()).then(function () { + if (! _.isEqual(selection, self.get("selection"))) { + self.set("selection", selection); + } + }); }, on_click_stage: function (ev) { var self = this; @@ -5340,8 +5394,8 @@ instance.web.form.FieldMonetary = instance.web.form.FieldFloat.extend({ this.set({"currency_info": null}); return; } - return this.ci_dm.add(new instance.web.Model("res.currency").query(["symbol", "position"]) - .filter([["id", "=", self.get("currency")]]).first()).then(function(res) { + return this.ci_dm.add(self.alive(new instance.web.Model("res.currency").query(["symbol", "position"]) + .filter([["id", "=", self.get("currency")]]).first())).then(function(res) { self.set({"currency_info": res}); }); }, diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index fbbfabcacca..d23bedf1d1c 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -1186,7 +1186,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. } }); instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.web.ListView.Groups# */{ - passtrough_events: 'action deleted row_link', + passthrough_events: 'action deleted row_link', /** * Grouped display for the ListView. Handles basic DOM events and interacts * with the :js:class:`~DataGroup` bound to it. @@ -1406,7 +1406,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we // can have selections spanning multiple links var selection = self.get_selection(); $this.trigger(e, [selection.ids, selection.records]); - }).bind(this.passtrough_events, function (e) { + }).bind(this.passthrough_events, function (e) { // additional positional parameters are provided to trigger as an // Array, following the event type or event object, but are // provided to the .bind event handler as *args. @@ -2212,7 +2212,7 @@ instance.web.list.Binary = instance.web.list.Column.extend({ if (value && value.substr(0, 10).indexOf(' ') == -1) { download_url = "data:application/octet-stream;base64," + value; } else { - download_url = this.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id}); + download_url = instance.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id}); if (this.filename) { download_url += '&filename_field=' + this.filename; } diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index efc442de2a9..f1bd7c4325e 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -132,6 +132,15 @@ openerp.web.list_editable = function (instance) { var self = this; // tree/@editable takes priority on everything else if present. var result = this._super(data, grouped); + + // In case current editor was started previously, also has to run + // when toggling from editable to non-editable in case form widgets + // have setup global behaviors expecting themselves to exist + // somehow. + this.editor.destroy(); + // Editor is not restartable due to formview not being restartable + this.editor = this.make_editor(); + if (this.editable()) { this.$el.addClass('oe_list_editable'); // FIXME: any hook available to ensure this is only done once? @@ -143,10 +152,6 @@ openerp.web.list_editable = function (instance) { e.preventDefault(); self.cancel_edition(); }); - this.editor.destroy(); - // Editor is not restartable due to formview not being - // restartable - this.editor = this.make_editor(); var editor_ready = this.editor.prependTo(this.$el) .done(this.proxy('setup_events')); @@ -795,7 +800,7 @@ openerp.web.list_editable = function (instance) { }); instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{ - passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved", + passthrough_events: instance.web.ListView.Groups.prototype.passthrough_events + " edit saved", get_row_for: function (record) { return _(this.children).chain() .invoke('get_row_for', record) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 9fd49367171..f5638004b47 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -190,6 +190,18 @@ instance.web.ActionManager = instance.web.Widget.extend({ }); state = _.extend(params || {}, state); } + if (this.inner_action.context) { + var active_id = this.inner_action.context.active_id; + if (active_id) { + state["active_id"] = active_id; + } + var active_ids = this.inner_action.context.active_ids; + if (active_ids && !(active_ids.length === 1 && active_ids[0] === active_id)) { + // We don't push active_ids if it's a single element array containing the active_id + // This makes the url shorter in most cases. + state["active_ids"] = this.inner_action.context.active_ids.join(','); + } + } } if(!this.dialog) { this.getParent().do_push_state(state); @@ -212,8 +224,22 @@ instance.web.ActionManager = instance.web.Widget.extend({ } else { var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action; if (run_action) { + var add_context = {}; + if (state.active_id) { + add_context.active_id = state.active_id; + } + if (state.active_ids) { + // The jQuery BBQ plugin does some parsing on values that are valid integers. + // It means that if there's only one item, it will do parseInt() on it, + // otherwise it will keep the comma seperated list as string. + add_context.active_ids = state.active_ids.toString().split(',').map(function(id) { + return parseInt(id, 10) || id; + }); + } else if (state.active_id) { + add_context.active_ids = [state.active_id]; + } this.null_action(); - action_loaded = this.do_action(state.action); + action_loaded = this.do_action(state.action, { additional_context: add_context }); $.when(action_loaded || null).done(function() { instance.webclient.menu.has_been_loaded.done(function() { if (self.inner_action && self.inner_action.id) { @@ -249,12 +275,25 @@ instance.web.ActionManager = instance.web.Widget.extend({ } }); }, + /** + * Execute an OpenERP action + * + * @param {Number|String|Object} Can be either an action id, a client action or an action descriptor. + * @param {Object} [options] + * @param {Boolean} [options.clear_breadcrumbs=false] Clear the breadcrumbs history list + * @param {Function} [options.on_reverse_breadcrumb] Callback to be executed whenever an anterior breadcrumb item is clicked on. + * @param {Function} [options.on_close] Callback to be executed when the dialog is closed (only relevant for target=new actions) + * @param {Function} [options.action_menu_id] Manually set the menu id on the fly. + * @param {Object} [options.additional_context] Additional context to be merged with the action's context. + * @return {jQuery.Deferred} Action loaded + */ do_action: function(action, options) { options = _.defaults(options || {}, { clear_breadcrumbs: false, on_reverse_breadcrumb: function() {}, on_close: function() {}, action_menu_id: null, + additional_context: {}, }); if (action === false) { action = { type: 'ir.actions.act_window_close' }; @@ -269,9 +308,13 @@ instance.web.ActionManager = instance.web.Widget.extend({ } // Ensure context & domain are evaluated and can be manipulated/used - if (action.context) { - action.context = instance.web.pyeval.eval( - 'context', action.context); + var ncontext = new instance.web.CompoundContext(options.additional_context, action.context || {}); + action.context = instance.web.pyeval.eval('context', ncontext); + if (action.context.active_id || action.context.active_ids) { + // Here we assume that when an `active_id` or `active_ids` is used + // in the context, we are in a `related` action, so we disable the + // searchview's default custom filters. + action.context.search_disable_custom_filters = true; } if (action.domain) { action.domain = instance.web.pyeval.eval( @@ -539,7 +582,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ _.each(_.keys(self.views), function(view_name) { var controller = self.views[view_name].controller; if (controller) { - var container = self.$el.find(".oe_view_manager_view_" + view_name + ":first"); + var container = self.$el.find("> .oe_view_manager_body > .oe_view_manager_view_" + view_name); if (view_name === view_type) { container.show(); controller.do_show(view_options || {}); @@ -582,7 +625,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ controller.on('switch_mode', self, this.switch_mode); controller.on('previous_view', self, this.prev_view); - var container = this.$el.find(".oe_view_manager_view_" + view_type); + var container = this.$el.find("> .oe_view_manager_body > .oe_view_manager_view_" + view_type); var view_promise = controller.appendTo(container); this.views[view_type].controller = controller; this.views[view_type].deferred.resolve(view_type); @@ -597,6 +640,12 @@ instance.web.ViewManager = instance.web.Widget.extend({ self.trigger("controller_inited",view_type,controller); }); }, + /** + * @returns {Number|Boolean} the view id of the given type, false if not found + */ + get_view_id: function(view_type) { + return this.views[view_type] && this.views[view_type].view_id || false; + }, set_title: function(title) { this.$el.find('.oe_view_title_text:first').text(title); }, @@ -953,10 +1002,11 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({ }); }, do_create_view: function(view_type) { - var r = this._super.apply(this, arguments); - var view = this.views[view_type].controller; - view.set({ 'title': this.action.name }); - return r; + var self = this; + return this._super.apply(this, arguments).then(function() { + var view = self.views[view_type].controller; + view.set({ 'title': self.action.name }); + }); }, get_action_manager: function() { var cur = this; @@ -1270,11 +1320,6 @@ instance.web.View = instance.web.Widget.extend({ active_ids: [record_id], active_model: dataset.model }); - if (("" + action.context).match(/\bactive_id\b/)) { - // Special case: when the context is evaluted using - // the active_id, we want to disable the custom filters. - ncontext.add({ search_disable_custom_filters: true }); - } } ncontext.add(action.context || {}); action.context = ncontext; @@ -1502,13 +1547,22 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) { } }; instance.web.xml_to_str = function(node) { + var str = ""; if (window.XMLSerializer) { - return (new XMLSerializer()).serializeToString(node); + str = (new XMLSerializer()).serializeToString(node); } else if (window.ActiveXObject) { - return node.xml; + str = node.xml; } else { throw new Error(_t("Could not serialize XML")); } + // Browsers won't deal with self closing tags except br, hr, input, ... + // http://stackoverflow.com/questions/97522/what-are-all-the-valid-self-closing-elements-in-xhtml-as-implemented-by-the-maj + // + // The following regex is a bit naive but it's ok for the xmlserializer output + str = str.replace(/<([a-z]+)([^<>]*)\s*\/\s*>/g, function(match, tag, attrs) { + return "<" + tag + attrs + ">"; + }); + return str; }; /** diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 7604814fe1d..4ceb3a468d5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -119,7 +119,7 @@ - + @@ -403,8 +403,9 @@ @@ -1299,7 +1300,7 @@ File - /web/binary/upload_attachment + /web/binary/upload_attachment diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js index 5f177c0a363..904af63f590 100644 --- a/addons/web/static/test/search.js +++ b/addons/web/static/test/search.js @@ -318,6 +318,28 @@ openerp.testing.section('defaults', { "facet value should match provided default's selection"); }); }); + test("M2O default: value array", {asserts: 2}, function (instance, $s, mock) { + var view = {inputs: []}, id = 5; + var f = new instance.web.search.ManyToOneField( + {attrs: {name: 'dummy', string: 'Dummy'}}, + {relation: 'dummy.model.name'}, + view); + mock('dummy.model.name:name_get', function (args) { + equal(args[0], id); + return [[id, "DumDumDum"]]; + }); + return f.facet_for_defaults({dummy: [id]}) + .done(function (facet) { + var model = facet; + if (!(model instanceof instance.web.search.Facet)) { + model = new instance.web.search.Facet(facet); + } + deepEqual( + model.values.toJSON(), + [{label: "DumDumDum", value: id}], + "should support default as a singleton"); + }); + }); test("M2O default: value", {asserts: 1}, function (instance, $s, mock) { var view = {inputs: []}, id = 4; var f = new instance.web.search.ManyToOneField( @@ -330,6 +352,15 @@ openerp.testing.section('defaults', { ok(!facet, "an invalid m2o default should yield a non-facet"); }); }); + test("M2O default: values", {rpc: false}, function (instance) { + var view = {inputs: []}; + var f = new instance.web.search.ManyToOneField( + {attrs: {name: 'dummy', string: 'Dummy'}}, + {relation: 'dummy.model.name'}, + view); + raises(function () { f.facet_for_defaults({dummy: [6, 7]}) }, + "should not accept multiple default values"); + }) }); openerp.testing.section('completions', { dependencies: ['web.search'], @@ -526,7 +557,7 @@ openerp.testing.section('completions', { return [[42, "choice 1"], [43, "choice @"]]; }); - var view = {inputs: []}; + var view = {inputs: [], dataset: {get_context: function () {}}}; var f = new instance.web.search.ManyToOneField( {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view); return f.complete("bob") @@ -555,7 +586,7 @@ openerp.testing.section('completions', { strictEqual(kwargs.name, 'bob'); return []; }); - var view = {inputs: []}; + var view = {inputs: [], dataset: {get_context: function () {}}}; var f = new instance.web.search.ManyToOneField( {attrs: {string: 'Dummy'}}, {relation: 'dummy.model'}, view); return f.complete("bob") @@ -563,6 +594,26 @@ openerp.testing.section('completions', { ok(!c, "no match should yield no completion"); }); }); + test("M2O filtered", {asserts: 2}, function (instance, $s, mock) { + mock('dummy.model:name_search', function (args, kwargs) { + deepEqual(args, [], "should have no positional arguments"); + deepEqual(kwargs, { + name: 'bob', + limit: 8, + args: [['foo', '=', 'bar']], + context: {flag: 1}, + }, "should use filtering domain"); + return [[42, "Match"]]; + }); + var view = { + inputs: [], + dataset: {get_context: function () { return {flag: 1}; }} + }; + var f = new instance.web.search.ManyToOneField( + {attrs: {string: 'Dummy', domain: '[["foo", "=", "bar"]]'}}, + {relation: 'dummy.model'}, view); + return f.complete("bob"); + }); }); openerp.testing.section('search-serialization', { dependencies: ['web.search'], @@ -1038,6 +1089,44 @@ openerp.testing.section('saved_filters', { "should not be checked anymore"); }); }); + test('toggling', {asserts: 2}, function (instance, $fix, mock) { + var view = makeSearchView(instance); + mock('ir.filters:get_filters', function () { + return [{name: 'filter name', user_id: 42, id: 1}]; + }); + + return view.appendTo($fix) + .done(function () { + var $row = $fix.find('.oe_searchview_custom li:first').click(); + equal(view.query.length, 1, "should have one facet"); + $row.click(); + equal(view.query.length, 0, "should have removed facet"); + }); + }); + test('replacement', {asserts: 4}, function (instance, $fix, mock) { + var view = makeSearchView(instance); + mock('ir.filters:get_filters', function () { + return [ + {name: 'f', user_id: 42, id: 1, context: {'private': 1}}, + {name: 'f', user_id: false, id: 2, context: {'private': 0}} + ]; + }); + return view.appendTo($fix) + .done(function () { + $fix.find('.oe_searchview_custom li:eq(0)').click(); + equal(view.query.length, 1, "should have one facet"); + deepEqual( + view.query.at(0).get('field').get_context(), + {'private': 1}, + "should have selected first filter"); + $fix.find('.oe_searchview_custom li:eq(1)').click(); + equal(view.query.length, 1, "should have one facet"); + deepEqual( + view.query.at(0).get('field').get_context(), + {'private': 0}, + "should have selected second filter"); + }); + }); }); openerp.testing.section('advanced', { dependencies: ['web.search'], diff --git a/addons/web_calendar/i18n/ko.po b/addons/web_calendar/i18n/ko.po new file mode 100644 index 00000000000..4f351dae541 --- /dev/null +++ b/addons/web_calendar/i18n/ko.po @@ -0,0 +1,217 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 07:04+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:153 +#, python-format +msgid "New event" +msgstr "신규 이벤트" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:156 +#, python-format +msgid "Details" +msgstr "상세 내용" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:154 +#, python-format +msgid "Save" +msgstr "저장" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:101 +#, python-format +msgid "Calendar view has a 'date_delay' type != float" +msgstr "달력 화면에 'date_delay'유형의 != 플로트가 있습니다" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:149 +#, python-format +msgid "Today" +msgstr "오늘" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:151 +#, python-format +msgid "Week" +msgstr "주" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:163 +#, python-format +msgid "Full day" +msgstr "하루 종일" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:161 +#: code:addons/web_calendar/static/src/js/calendar.js:174 +#, python-format +msgid "Description" +msgstr "설명" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:160 +#, python-format +msgid "Event will be deleted permanently, are you sure?" +msgstr "이벤트가 영구 삭제될 것입니다. 확실하십니까?" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/xml/web_calendar.xml:8 +#: code:addons/web_calendar/static/src/xml/web_calendar.xml:9 +#, python-format +msgid " " +msgstr " " + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:173 +#, python-format +msgid "Date" +msgstr "날짜" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:470 +#, python-format +msgid "Edit: " +msgstr "편집: " + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:150 +#, python-format +msgid "Day" +msgstr "일" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:157 +#, python-format +msgid "Edit" +msgstr "편집" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:169 +#, python-format +msgid "Enabled" +msgstr "활성화 됨" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:166 +#, python-format +msgid "Do you want to edit the whole set of repeated events?" +msgstr "반복 이벤트의 전체 세트를 편집하시겠습니까?" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:82 +#, python-format +msgid "Filter" +msgstr "필터" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:167 +#, python-format +msgid "Repeat event" +msgstr "이벤트 반복" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:172 +#: code:addons/web_calendar/static/src/js/calendar.js:180 +#, python-format +msgid "Agenda" +msgstr "의제" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:162 +#, python-format +msgid "Time period" +msgstr "시한" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:158 +#, python-format +msgid "Delete" +msgstr "삭제" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:152 +#, python-format +msgid "Month" +msgstr "월" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:168 +#, python-format +msgid "Disabled" +msgstr "비활성화 됨" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:435 +#, python-format +msgid "Create: " +msgstr "생성: " + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:177 +#, python-format +msgid "Year" +msgstr "년도" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:155 +#, python-format +msgid "Cancel" +msgstr "취소" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:28 +#, python-format +msgid "Calendar" +msgstr "달력" + +#. module: web_calendar +#. openerp-web +#: code:addons/web_calendar/static/src/js/calendar.js:93 +#, python-format +msgid "Calendar view has not defined 'date_start' attribute." +msgstr "달력 화면이 'date_start' 속성을 정의하지 않았습니다." diff --git a/addons/web_calendar/static/src/css/web_calendar.css b/addons/web_calendar/static/src/css/web_calendar.css index d40a7a2940a..214c777dbdc 100644 --- a/addons/web_calendar/static/src/css/web_calendar.css +++ b/addons/web_calendar/static/src/css/web_calendar.css @@ -18,7 +18,7 @@ .openerp .oe_calendar .dhx_cal_select_menu .dhx_menu_icon.icon_edit { display: none; } -.openerp .oe_calendar .dhx_cal_navline { +.openerp .oe_calendar .dhx_cal_navline, .openerp .oe_calendar .dhx_cal_header { z-index: auto; } .openerp .oe_calendar.oe_cal_month .dhx_cal_data table tr td:last-child div.dhx_month_body { diff --git a/addons/web_calendar/static/src/css/web_calendar.sass b/addons/web_calendar/static/src/css/web_calendar.sass index f6b6a1a5eeb..e4986981609 100644 --- a/addons/web_calendar/static/src/css/web_calendar.sass +++ b/addons/web_calendar/static/src/css/web_calendar.sass @@ -20,7 +20,7 @@ // Dhtmlx Scheduler css overrides .dhx_cal_select_menu .dhx_menu_icon.icon_edit display: none - .dhx_cal_navline + .dhx_cal_navline, .dhx_cal_header // dhtmlx sets the index to 3 (in glossy theme) // I didn't found the reason yet but it overlaps the // dropdown menus and I want to avoid a z-index war. diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 9cac4db09d5..3c9a8a6fa9d 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -414,6 +414,20 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ self.slow_create(event_id, event_obj); }); }, + get_form_popup_infos: function() { + var parent = this.getParent(); + var infos = { + view_id: false, + title: this.name, + }; + if (parent instanceof instance.web.ViewManager) { + infos.view_id = parent.get_view_id('form'); + if (parent instanceof instance.web.ViewManagerAction && parent.action && parent.action.name) { + infos.title = parent.action.name; + } + } + return infos; + }, slow_create: function(event_id, event_obj) { var self = this; if (this.current_mode() === 'month') { @@ -431,9 +445,11 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ }); var something_saved = false; var pop = new instance.web.form.FormOpenPopup(this); + var pop_infos = this.get_form_popup_infos(); pop.show_element(this.dataset.model, null, this.dataset.get_context(defaults), { - title: _t("Create: ") + ' ' + this.name, + title: _.str.sprintf(_t("Create: %s"), pop_infos.title), disable_multiple_selection: true, + view_id: pop_infos.view_id, }); pop.on('closed', self, function() { if (!something_saved) { @@ -465,9 +481,11 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ }); } else { var pop = new instance.web.form.FormOpenPopup(this); + var pop_infos = this.get_form_popup_infos(); var id_from_dataset = this.dataset.ids[index]; // dhtmlx scheduler loses id's type pop.show_element(this.dataset.model, id_from_dataset, this.dataset.get_context(), { - title: _t("Edit: ") + this.name + title: _.str.sprintf(_t("Edit: %s"), pop_infos.title), + view_id: pop_infos.view_id, }); pop.on('write_completed', self, function(){ self.reload_event(id_from_dataset); diff --git a/addons/web_diagram/i18n/ko.po b/addons/web_diagram/i18n/ko.po new file mode 100644 index 00000000000..7a1f0661e88 --- /dev/null +++ b/addons/web_diagram/i18n/ko.po @@ -0,0 +1,106 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 07:09+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:252 +#: code:addons/web_diagram/static/src/js/diagram.js:317 +#, python-format +msgid "Open: " +msgstr "열기: " + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:215 +#, python-format +msgid "" +"Deleting this node cannot be undone.\n" +"It will also delete all connected transitions.\n" +"\n" +"Are you sure ?" +msgstr "" +"해당 노드를 삭제하는 작업은 복구할 수 없습니다.\n" +"또한 모든 연결된 전환을 삭제할 것입니다.\n" +"\n" +"확실하십니까?" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/xml/base_diagram.xml:13 +#, python-format +msgid "New Node" +msgstr "신규 노드" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:310 +#: code:addons/web_diagram/static/src/js/diagram.js:329 +#, python-format +msgid "Transition" +msgstr "전환" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:11 +#, python-format +msgid "Diagram" +msgstr "도표" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:244 +#: code:addons/web_diagram/static/src/js/diagram.js:278 +#, python-format +msgid "Activity" +msgstr "활동" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:420 +#, python-format +msgid "%d / %d" +msgstr "%d / %d" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:283 +#: code:addons/web_diagram/static/src/js/diagram.js:335 +#, python-format +msgid "Create:" +msgstr "생성:" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:185 +#, python-format +msgid "Are you sure?" +msgstr "확실하십니까?" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:233 +#, python-format +msgid "" +"Deleting this transition cannot be undone.\n" +"\n" +"Are you sure ?" +msgstr "" +"해당 전환을 삭제하는 작업은 복구할 수 없습니다.\n" +"\n" +"확실하십니까 ?" diff --git a/addons/web_gantt/i18n/ko.po b/addons/web_gantt/i18n/ko.po new file mode 100644 index 00000000000..c4b00160e42 --- /dev/null +++ b/addons/web_gantt/i18n/ko.po @@ -0,0 +1,32 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 06:02+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_gantt +#. openerp-web +#: code:addons/web_gantt/static/src/xml/web_gantt.xml:10 +#, python-format +msgid "Create" +msgstr "생성" + +#. module: web_gantt +#. openerp-web +#: code:addons/web_gantt/static/src/js/gantt.js:11 +#, python-format +msgid "Gantt" +msgstr "간트" diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js index e6b12038126..f30fa6a83e6 100644 --- a/addons/web_gantt/static/src/js/gantt.js +++ b/addons/web_gantt/static/src/js/gantt.js @@ -24,8 +24,8 @@ instance.web_gantt.GanttView = instance.web.View.extend({ var self = this; this.fields_view = fields_view_get; this.$el.addClass(this.fields_view.arch.attrs['class']); - return new instance.web.Model(this.dataset.model) - .call('fields_get').then(function (fields) { + return self.alive(new instance.web.Model(this.dataset.model) + .call('fields_get')).then(function (fields) { self.fields = fields; self.has_been_loaded.resolve(); }); diff --git a/addons/web_graph/i18n/ko.po b/addons/web_graph/i18n/ko.po new file mode 100644 index 00000000000..272e68c4d8c --- /dev/null +++ b/addons/web_graph/i18n/ko.po @@ -0,0 +1,137 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 06:04+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:12 +#, python-format +msgid "Bars" +msgstr "막대" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:33 +#, python-format +msgid "Show Data" +msgstr "데이터 보기" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/js/graph.js:22 +#, python-format +msgid "Graph" +msgstr "그래프" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:25 +#, python-format +msgid "Inside" +msgstr "내부" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:3 +#, python-format +msgid "í" +msgstr "í" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:11 +#, python-format +msgid "Pie" +msgstr "파이" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:28 +#, python-format +msgid "Actions" +msgstr "액션" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:7 +#, python-format +msgid "Graph Mode" +msgstr "그래프 모드" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:18 +#, python-format +msgid "Radar" +msgstr "레이더" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:34 +#, python-format +msgid "Download as PNG" +msgstr "PNG 형식으로 다운로드" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:26 +#, python-format +msgid "Top" +msgstr "상단" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:24 +#, python-format +msgid "Hidden" +msgstr "숨겨짐" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:3 +#, python-format +msgid "Graph Options" +msgstr "그래프 옵션" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:14 +#, python-format +msgid "Lines" +msgstr "선" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:20 +#, python-format +msgid "Legend" +msgstr "범례" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:32 +#, python-format +msgid "Switch Axis" +msgstr "축 전환" + +#. module: web_graph +#. openerp-web +#: code:addons/web_graph/static/src/xml/web_graph.xml:15 +#, python-format +msgid "Areas" +msgstr "영역" diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index 35ad0aaaea8..0ae3f7c1508 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -246,7 +246,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ var result = []; var ticks = {}; - return obj.call("fields_view_get", [view_id, 'graph']).then(function(tmp) { + return this.alive(obj.call("fields_view_get", [view_id, 'graph']).then(function(tmp) { view_get = tmp; fields = view_get['fields']; var toload = _.select(group_by, function(x) { return fields[x] === undefined }); @@ -368,7 +368,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ 'ticks': _.map(ticks, function(el, key) { return [el, key] }) }; return res; - }); + })); }, // Render the graph and update menu styles diff --git a/addons/web_kanban/i18n/ko.po b/addons/web_kanban/i18n/ko.po new file mode 100644 index 00000000000..74c113c9cd2 --- /dev/null +++ b/addons/web_kanban/i18n/ko.po @@ -0,0 +1,160 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 05:59+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:704 +#, python-format +msgid "Edit column" +msgstr "열 편집" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:418 +#, python-format +msgid "An error has occured while moving the record to this group." +msgstr "기록을 해당 그룹으로 옮기는 동안 오류가 발생했습니다." + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:10 +#, python-format +msgid "Kanban" +msgstr "간판" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:557 +#, python-format +msgid "Undefined" +msgstr "정의되지 않음" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:723 +#, python-format +msgid "Are you sure to remove this column ?" +msgstr "정말로 해당 열을 제거하시겠습니다?" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:42 +#, python-format +msgid "Edit" +msgstr "편집" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:190 +#, python-format +msgid "Add column" +msgstr "열 추가" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:1100 +#, python-format +msgid "Create: " +msgstr "다음 생성: " + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:24 +#, python-format +msgid "Add a new column" +msgstr "열 추가" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:694 +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:40 +#, python-format +msgid "Fold" +msgstr "접기" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:91 +#, python-format +msgid "Add" +msgstr "추가" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:35 +#, python-format +msgid "Quick create" +msgstr "빠른 추가" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:936 +#, python-format +msgid "Are you sure you want to delete this record ?" +msgstr "정말로 다음 기록을 삭제하시겠습니까 ?" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/js/kanban.js:694 +#, python-format +msgid "Unfold" +msgstr "펼치기" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:73 +#, python-format +msgid "Show more... (" +msgstr "더 보기... (" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:92 +#, python-format +msgid "Cancel" +msgstr "취소" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:73 +#, python-format +msgid "remaining)" +msgstr "남음)" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:22 +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:91 +#, python-format +msgid "or" +msgstr "또는" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:51 +#, python-format +msgid "99+" +msgstr "99+" + +#. module: web_kanban +#. openerp-web +#: code:addons/web_kanban/static/src/xml/web_kanban.xml:43 +#, python-format +msgid "Delete" +msgstr "삭제" diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 3083a9e30a5..eaba760292c 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -92,12 +92,21 @@ overflow: hidden; text-overflow: ellipsis; } -.openerp .oe_kanban_view .oe_kanban_group_title .oe_kanban_group_length { +.openerp .oe_kanban_view .oe_kanban_group_length { + text-align: center; + display: none; +} +.openerp .oe_kanban_view .oe_kanban_group_length .oe_tag { + position: relative; + top: +8px; + font-weight: bold; +} +.openerp .oe_kanban_view .oe_fold_column .oe_kanban_group_length { position: absolute; top: -1px; right: -14px; - text-align: center; float: right; + display: block; } .openerp .oe_kanban_view .oe_kanban_header:hover .oe_kanban_group_length { display: none; @@ -141,7 +150,8 @@ .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title, .openerp .oe_kanban_view .oe_kanban_group_folded.oe_kanban_column *, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_aggregates, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_add { display: none; } -.openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title_vertical { +.openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title_vertical, +.openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_length { display: block; } .openerp .oe_kanban_view .oe_kanban_group_folded .oe_dropdown_kanban { @@ -163,7 +173,7 @@ display: none; position: relative; opacity: 0.75; - top: 20px; + top: 26px; } .openerp .oe_kanban_view .oe_kanban_add, .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle { margin-left: 4px; @@ -175,6 +185,7 @@ } .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle { top: -2px; + height: 14px; } .openerp .oe_kanban_view .oe_kanban_card, .openerp .oe_kanban_view .oe_dropdown_toggle { cursor: pointer; diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index 6802a257556..ec964c8819f 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -54,6 +54,13 @@ &.oe_kanban_grouped .oe_kanban_dummy_cell background: url(/web/static/src/img/form_sheetbg.png) width: 100% + .oe_kanban_group_length + text-align: center + display: none + .oe_tag + position: relative + top: +8px + font-weight: bold .ui-sortable-placeholder border: 1px solid rgba(0,0,0,0.1) visibility: visible !important @@ -118,11 +125,12 @@ white-space: nowrap overflow: hidden text-overflow: ellipsis + .oe_fold_column .oe_kanban_group_length position: absolute top: -1px right: -14px - text-align: center + display: block float: right &.oe_kanban_grouped .oe_kanban_column, .oe_kanban_group_header @@ -159,7 +167,7 @@ .oe_kanban_group_folded .oe_kanban_group_title, &.oe_kanban_column *, .oe_kanban_aggregates, .oe_kanban_add display: none - .oe_kanban_group_title_vertical + .oe_kanban_group_title_vertical, .oe_kanban_group_length display: block .oe_dropdown_kanban left: -5px @@ -178,7 +186,7 @@ display: none position: relative opacity: 0.75 - top: 20px + top: 26px // }}} // KanbanQuickCreate {{{ .oe_kanban_add, .oe_kanban_header .oe_dropdown_toggle @@ -189,6 +197,7 @@ top: -8px .oe_kanban_header .oe_dropdown_toggle top: -2px + height: 14px; .oe_kanban_card, .oe_dropdown_toggle cursor: pointer display: inline-block diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 7d6f88b6def..651fcbc4019 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -134,7 +134,9 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ switch (node.tag) { case 'field': if (this.fields_view.fields[node.attrs.name].type === 'many2many') { - this.many2manys.push(node.attrs.name); + if (_.indexOf(this.many2manys, node.attrs.name) < 0) { + this.many2manys.push(node.attrs.name); + } node.tag = 'div'; node.attrs['class'] = (node.attrs['class'] || '') + ' oe_form_field oe_tags'; } else { @@ -227,14 +229,15 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.search_domain = domain; this.search_context = context; this.search_group_by = group_by; - $.when(this.has_been_loaded).done(function() { + return $.when(this.has_been_loaded).then(function() { self.group_by = group_by.length ? group_by[0] : self.fields_view.arch.attrs.default_group_by; self.group_by_field = self.fields_view.fields[self.group_by] || {}; self.grouped_by_m2o = (self.group_by_field.type === 'many2one'); self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o); self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o); - var grouping = new instance.web.Model(self.dataset.model, context, domain).query().group_by(self.group_by); - $.when(grouping).done(function(groups) { + var grouping_fields = self.group_by ? [self.group_by].concat(_.keys(self.aggregates)) : undefined; + var grouping = new instance.web.Model(self.dataset.model, context, domain).query().group_by(grouping_fields); + return self.alive($.when(grouping)).done(function(groups) { if (groups) { self.do_process_groups(groups); } else { @@ -635,6 +638,7 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ self.view.dataset.ids = ids.concat(self.dataset.ids); self.do_add_records(records); self.compute_cards_auto_height(); + self.view.postprocess_m2m_tags(); return records; }); }, diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 67ee563c14d..f943541d007 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -34,16 +34,21 @@
]
-
- í - +
+
+ + 99+ +
+ í + +
@@ -57,7 +62,9 @@
- + + +
diff --git a/addons/web_view_editor/i18n/ko.po b/addons/web_view_editor/i18n/ko.po new file mode 100644 index 00000000000..578c03aa0b3 --- /dev/null +++ b/addons/web_view_editor/i18n/ko.po @@ -0,0 +1,184 @@ +# Korean translation for openerp-web +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-02-26 08:08+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-02-27 04:53+0000\n" +"X-Generator: Launchpad (build 16506)\n" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:164 +#, python-format +msgid "The following fields are invalid :" +msgstr "다음 필드는 유효하지 않습니다 :" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:63 +#, python-format +msgid "Create" +msgstr "생성" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:986 +#, python-format +msgid "New Field" +msgstr "신규 필드" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:386 +#, python-format +msgid "Do you really wants to create an inherited view here?" +msgstr "정말로 여기에 전승된 화면을 생성하길 원하십니까?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:396 +#, python-format +msgid "Preview" +msgstr "미리보기" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:183 +#, python-format +msgid "Do you really want to remove this view?" +msgstr "정말로 해당 화면을 제거하시겠습니까?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:90 +#, python-format +msgid "Save" +msgstr "저장" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:393 +#, python-format +msgid "Select an element" +msgstr "요소 선택" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:828 +#: code:addons/web_view_editor/static/src/js/view_editor.js:954 +#, python-format +msgid "Update" +msgstr "업데이트" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:263 +#, python-format +msgid "Please select view in list :" +msgstr "목록에서 화면을 선택하세요 :" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:37 +#, python-format +msgid "Manage Views (%s)" +msgstr "화면 관리 (%s)" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:13 +#, python-format +msgid "Manage Views" +msgstr "화면 관리" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:825 +#: code:addons/web_view_editor/static/src/js/view_editor.js:951 +#, python-format +msgid "Properties" +msgstr "속성" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:64 +#, python-format +msgid "Edit" +msgstr "편집" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:14 +#, python-format +msgid "Could not find current view declaration" +msgstr "현재 화면 선언을 찾을 수 없음" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:382 +#, python-format +msgid "Inherited View" +msgstr "전승 화면" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:65 +#, python-format +msgid "Remove" +msgstr "제거" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:516 +#, python-format +msgid "Do you really want to remove this node?" +msgstr "정말로 이 노드를 삭제하시겠습니까?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:390 +#, python-format +msgid "Can't Update View" +msgstr "화면을 업데이트 할 수 없음" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:379 +#, python-format +msgid "View Editor %d - %s" +msgstr "화면 편집기 %d - %s" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:112 +#: code:addons/web_view_editor/static/src/js/view_editor.js:846 +#: code:addons/web_view_editor/static/src/js/view_editor.js:974 +#, python-format +msgid "Cancel" +msgstr "취소" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:66 +#: code:addons/web_view_editor/static/src/js/view_editor.js:413 +#, python-format +msgid "Close" +msgstr "닫기" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:88 +#, python-format +msgid "Create a view (%s)" +msgstr "화면 생성 (%s)"