diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 678eacd7f3e..269aa13d58e 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -26,6 +26,7 @@ This module provides the core of the OpenERP Web Client. "static/lib/spinjs/spin.js", "static/lib/jquery.autosize/jquery.autosize.js", "static/lib/jquery.blockUI/jquery.blockUI.js", + "static/lib/jquery.placeholder/jquery.placeholder.js", "static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js", "static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js", "static/lib/jquery.ui.notify/js/jquery.notify.js", diff --git a/addons/web/http.py b/addons/web/http.py index 8595e38446f..e05d74e0873 100644 --- a/addons/web/http.py +++ b/addons/web/http.py @@ -93,6 +93,14 @@ class WebRequest(object): if not self.session: self.session = session.OpenERPSession() self.httpsession[self.session_id] = self.session + + # set db/uid trackers - they're cleaned up at the WSGI + # dispatching phase in openerp.service.wsgi_server.application + if self.session._db: + threading.current_thread().dbname = self.session._db + if self.session._uid: + threading.current_thread().uid = self.session._uid + self.context = self.params.pop('context', {}) self.debug = self.params.pop('debug', False) is not False # Determine self.lang diff --git a/addons/web/i18n/ru.po b/addons/web/i18n/ru.po index 92aff4e5bd2..e46666b183a 100644 --- a/addons/web/i18n/ru.po +++ b/addons/web/i18n/ru.po @@ -8,14 +8,14 @@ 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: 2012-12-25 10:26+0000\n" -"Last-Translator: Paul Korotkov \n" +"PO-Revision-Date: 2013-03-01 13:52+0000\n" +"Last-Translator: Denis Karataev \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-26 05:18+0000\n" -"X-Generator: Launchpad (build 16378)\n" +"X-Launchpad-Export-Date: 2013-03-02 05:23+0000\n" +"X-Generator: Launchpad (build 16514)\n" #. module: web #. openerp-web @@ -400,7 +400,7 @@ msgstr "Выбор:" #: code:addons/web/static/src/js/view_form.js:871 #, python-format msgid "The following fields are invalid:" -msgstr "Следующие поля заполнены неверны:" +msgstr "Следующие поля заполнены неверно:" #. module: web #: code:addons/web/controllers/main.py:847 @@ -2385,7 +2385,7 @@ msgstr "Запись o2m должна быть сохранена перед и #: code:addons/web/static/src/js/chrome.js:509 #, python-format msgid "Backed" -msgstr "" +msgstr "Резервированна" #. module: web #. openerp-web diff --git a/addons/web/static/lib/jquery.placeholder/jquery.placeholder.js b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.js new file mode 100755 index 00000000000..d8051fa269d --- /dev/null +++ b/addons/web/static/lib/jquery.placeholder/jquery.placeholder.js @@ -0,0 +1,157 @@ +/*! http://mths.be/placeholder v2.0.7 by @mathias */ +;(function(window, document, $) { + + var isInputSupported = 'placeholder' in document.createElement('input'), + isTextareaSupported = 'placeholder' in document.createElement('textarea'), + prototype = $.fn, + valHooks = $.valHooks, + hooks, + placeholder; + + if (isInputSupported && isTextareaSupported) { + + placeholder = prototype.placeholder = function() { + return this; + }; + + placeholder.input = placeholder.textarea = true; + + } else { + + placeholder = prototype.placeholder = function() { + var $this = this; + $this + .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') + .not('.placeholder') + .bind({ + 'focus.placeholder': clearPlaceholder, + 'blur.placeholder': setPlaceholder + }) + .data('placeholder-enabled', true) + .trigger('blur.placeholder'); + return $this; + }; + + placeholder.input = isInputSupported; + placeholder.textarea = isTextareaSupported; + + hooks = { + 'get': function(element) { + var $element = $(element); + return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; + }, + 'set': function(element, value) { + var $element = $(element); + if (!$element.data('placeholder-enabled')) { + return element.value = value; + } + if (value == '') { + element.value = value; + // Issue #56: Setting the placeholder causes problems if the element continues to have focus. + if (element != document.activeElement) { + // We can't use `triggerHandler` here because of dummy text/password inputs :( + setPlaceholder.call(element); + } + } else if ($element.hasClass('placeholder')) { + clearPlaceholder.call(element, true, value) || (element.value = value); + } else { + element.value = value; + } + // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 + return $element; + } + }; + + isInputSupported || (valHooks.input = hooks); + isTextareaSupported || (valHooks.textarea = hooks); + + $(function() { + // Look for forms + $(document).delegate('form', 'submit.placeholder', function() { + // Clear the placeholder values so they don't get submitted + var $inputs = $('.placeholder', this).each(clearPlaceholder); + setTimeout(function() { + $inputs.each(setPlaceholder); + }, 10); + }); + }); + + // Clear placeholder values upon page reload + $(window).bind('beforeunload.placeholder', function() { + $('.placeholder').each(function() { + this.value = ''; + }); + }); + + } + + function args(elem) { + // Return an object of element attributes + var newAttrs = {}, + rinlinejQuery = /^jQuery\d+$/; + $.each(elem.attributes, function(i, attr) { + if (attr.specified && !rinlinejQuery.test(attr.name)) { + newAttrs[attr.name] = attr.value; + } + }); + return newAttrs; + } + + function clearPlaceholder(event, value) { + var input = this, + $input = $(input); + if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { + if ($input.data('placeholder-password')) { + $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); + // If `clearPlaceholder` was called from `$.valHooks.input.set` + if (event === true) { + return $input[0].value = value; + } + $input.focus(); + } else { + input.value = ''; + $input.removeClass('placeholder'); + input == document.activeElement && input.select(); + } + } + } + + function setPlaceholder() { + var $replacement, + input = this, + $input = $(input), + $origInput = $input, + id = this.id; + if (input.value == '') { + if (input.type == 'password') { + if (!$input.data('placeholder-textinput')) { + try { + $replacement = $input.clone().attr({ 'type': 'text' }); + } catch(e) { + $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); + } + $replacement + .removeAttr('name') + .data({ + 'placeholder-password': true, + 'placeholder-id': id + }) + .bind('focus.placeholder', clearPlaceholder); + $input + .data({ + 'placeholder-textinput': $replacement, + 'placeholder-id': id + }) + .before($replacement); + } + $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); + // Note: `$input[0] != input` now! + } + $input.addClass('placeholder'); + $input[0].value = $input.attr('placeholder'); + } else { + $input.removeClass('placeholder'); + } + } + +}(this, document, jQuery)); \ No newline at end of file diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 25c682facd6..789205a5121 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"); @@ -3147,6 +3147,10 @@ div.ui-widget-overlay { border-radius: 3px; } +.openerp_ie .placeholder { + color: #afafb6 !important; + font-style: italic !important; +} .openerp_ie .oe_form_field_boolean input { background: white; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index cb385f9a8da..48b1c0efa35 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -2483,6 +2483,9 @@ div.ui-widget-overlay // Internet Explorer 9+ specifics {{{ .openerp_ie + .placeholder + color: $tag-border !important + font-style: italic !important .oe_form_field_boolean input background: #fff .db_option_table .oe_form_field_selection diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index bb74369989d..bfde707312c 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -232,13 +232,17 @@ instance.web.ParentedMixin = { 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. + @param {$.Deferred} promise The promise representing the asynchronous + action. + @param {bool} [reject=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 + or rejected. + @returns {$.Deferred} 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(); diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index bbff633c687..c5a2bd10888 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -860,8 +860,16 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ } return completion.promise(); }, - call_button: function (method, args) { - var id = args[0][0], index; + /** + * Invalidates caching of a record in the dataset to ensure the next read + * of that record will hit the server. + * + * Of use when an action is going to remote-alter a record which will then + * need to be reloaded, e.g. action button. + * + * @param {Object} id record to remove from the BDS's cache + */ + evict_record: function (id) { for(var i=0, len=this.cache.length; i