From 29cb20313e795693f33e932151907fad05b1f8e2 Mon Sep 17 00:00:00 2001 From: "Mayur Maheshwari (OpenERP)" Date: Thu, 18 Aug 2011 14:24:34 +0530 Subject: [PATCH 01/41] [IMP]base:Added field Company registry after TaxID bzr revid: mma@tinyerp.com-20110818085434-2upimzqd2ty1nysa --- openerp/addons/base/base_update.xml | 1 + openerp/addons/base/res/res_company.py | 1 + 2 files changed, 2 insertions(+) diff --git a/openerp/addons/base/base_update.xml b/openerp/addons/base/base_update.xml index 6f4dbf6a1f9..b2e941b28bd 100644 --- a/openerp/addons/base/base_update.xml +++ b/openerp/addons/base/base_update.xml @@ -229,6 +229,7 @@ + diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py index b0ebeb8ac27..1aa6eb9597e 100644 --- a/openerp/addons/base/res/res_company.py +++ b/openerp/addons/base/res/res_company.py @@ -143,6 +143,7 @@ class res_company(osv.osv): 'fax': fields.function(_get_address_data, fnct_inv=_set_address_data, size=64, type='char', string="Fax", multi='address'), 'website': fields.related('partner_id', 'website', string="Website", type="char", size=64), 'vat': fields.related('partner_id', 'vat', string="Tax ID", type="char", size=32), + 'company_registry': fields.char('Company Registry', size=64), } def on_change_header(self, cr, uid, ids, phone, email, fax, website, vat, context={}): val = [] From 71b057aa3402346ccfc4364d20a81a96b5e632f8 Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Fri, 19 Aug 2011 16:03:22 +0530 Subject: [PATCH 02/41] [FIX]Fix add remove shortcut problem on switch view and update session. bzr revid: vme@tinyerp.com-20110819103322-7cph3n3ey7z39gkj --- addons/base/static/src/js/chrome.js | 16 ++++++++++++---- addons/base/static/src/js/views.js | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 3aff7b5e402..ec666cf8c8a 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -584,13 +584,17 @@ openerp.base.Header = openerp.base.Widget.extend({ sc.binding = {}; $(sc.binding).bind({ 'add': function (e, attrs) { - var $shortcut = $('
  • ', { - 'data-id': attrs.res_id + shortcuts_ds.create(attrs, function (out) { + var $shortcut = $('
  • ', { + 'data-shortcut-id':out.result, + 'data-id': attrs.res_id }).text(attrs.name) .appendTo(self.$element.find('.oe-shortcuts ul')); - shortcuts_ds.create(attrs, function (out) { - $shortcut.data('shortcut-id', out.result); + attrs.id = out.result; + sc.splice(sc.lenght,0,attrs); + sc.push.apply(sc); }); + }, 'remove-current': function () { var menu_id = self.session.active_id; @@ -599,6 +603,9 @@ openerp.base.Header = openerp.base.Widget.extend({ var shortcut_id = $shortcut.data('shortcut-id'); $shortcut.remove(); shortcuts_ds.unlink([shortcut_id]); + sc1 = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id}); + sc.splice(0, sc.length); + sc.push.apply(sc ,sc1); } }); } @@ -609,6 +616,7 @@ openerp.base.Header = openerp.base.Widget.extend({ self.$element.find('.oe-shortcuts') .html(QWeb.render('Shortcuts', {'shortcuts': shortcuts})) .undelegate('li', 'click') + .delegate('li', 'click', function(e) { e.stopPropagation(); var id = $(this).data('id'); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index ad75268156f..7a15f9a37ad 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -342,6 +342,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({ shortcut_add_remove: function() { var self = this; var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle'); + $shortcut_toggle.unbind("click").click(); $shortcut_toggle.click(function() { if ($shortcut_toggle.hasClass("oe-shortcut-remove")) { $(self.session.shortcuts.binding).trigger('remove-current'); From a97bb0476ca74b2fa43bc58d408b68d78477610e Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Tue, 23 Aug 2011 14:31:21 +0530 Subject: [PATCH 03/41] [fix]Optimize code in add event bzr revid: vme@tinyerp.com-20110823090121-gk2iayz1eavjwdfg --- addons/base/static/src/js/chrome.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index ec666cf8c8a..ef8779152bc 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -585,28 +585,26 @@ openerp.base.Header = openerp.base.Widget.extend({ $(sc.binding).bind({ 'add': function (e, attrs) { shortcuts_ds.create(attrs, function (out) { - var $shortcut = $('
  • ', { - 'data-shortcut-id':out.result, - 'data-id': attrs.res_id + $('
  • ', { + 'data-shortcut-id':out.result, + 'data-id': attrs.res_id }).text(attrs.name) .appendTo(self.$element.find('.oe-shortcuts ul')); attrs.id = out.result; - sc.splice(sc.lenght,0,attrs); - sc.push.apply(sc); + sc.push(attrs); }); - - }, + }, 'remove-current': function () { var menu_id = self.session.active_id; var $shortcut = self.$element - .find('.oe-shortcuts li[data-id=' + menu_id + ']'); + .find('.oe-shortcuts li[data-id=' + menu_id + ']'); var shortcut_id = $shortcut.data('shortcut-id'); $shortcut.remove(); shortcuts_ds.unlink([shortcut_id]); - sc1 = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id}); + var sc_new = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id}); sc.splice(0, sc.length); - sc.push.apply(sc ,sc1); - } + sc.push.apply(sc,sc_new); + } }); } return this.rpc('/base/session/sc_list', {}, function(shortcuts) { From 180ffb18a14715f1af17bfa2f94213d457e6a861 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Tue, 23 Aug 2011 12:33:43 +0200 Subject: [PATCH 04/41] [REF] openerp-server: streamlining startup script. bzr revid: vmt@openerp.com-20110823103343-rvy2zkre8bo9cq6g --- openerp-server | 269 ++++++++++++++++++++----------------- openerp/tools/translate.py | 3 +- 2 files changed, 144 insertions(+), 128 deletions(-) diff --git a/openerp-server b/openerp-server index 00da3ac640a..9c9a8c2bde7 100755 --- a/openerp-server +++ b/openerp-server @@ -30,9 +30,6 @@ GNU Public Licence. (c) 2003-TODAY, Fabien Pinckaers - OpenERP s.a. """ -#---------------------------------------------------------- -# python imports -#---------------------------------------------------------- import logging import os import signal @@ -45,139 +42,128 @@ import openerp __author__ = openerp.release.author __version__ = openerp.release.version -if os.name == 'posix': - import pwd - # We DON't log this using the standard logger, because we might mess - # with the logfile's permissions. Just do a quick exit here. - if pwd.getpwuid(os.getuid())[0] == 'root' : - sys.stderr.write("Attempted to run OpenERP server as root. This is not good, aborting.\n") +def check_root_user(): + """ Exit if the process’s user is 'root' (on POSIX system).""" + if os.name == 'posix': + import pwd + if pwd.getpwuid(os.getuid())[0] == 'root' : + sys.stderr.write("Running as user 'root' is a security risk, aborting.\n") + sys.exit(1) + +def check_postgres_user(): + """ Exit if the configured database user is 'postgres'.""" + config = openerp.tools.config + logger = logging.getLogger('server') + if config['db_user'] == 'postgres': + sys.stderr.write("Using the database user 'postgres' is a security risk, aborting.") sys.exit(1) -#----------------------------------------------------------------------- -# parse the command line -#----------------------------------------------------------------------- -openerp.tools.config.parse_config(sys.argv[1:]) -config = openerp.tools.config +def report_configuration(): + """ Log the server version and some configuration values.""" + config = openerp.tools.config + logger = logging.getLogger('server') + logger.info("OpenERP version %s", __version__) + for name, value in [('addons paths', config['addons_path']), + ('database hostname', config['db_host'] or 'localhost'), + ('database port', config['db_port'] or '5432'), + ('database user', config['db_user'])]: + logger.info("%s: %s", name, value) -#---------------------------------------------------------- -# get logger -#---------------------------------------------------------- -openerp.netsvc.init_logger() -logger = logging.getLogger('server') +# TODO is a \n better? +def setup_pid_file(): + config = openerp.tools.config + if config['pidfile']: + fd = open(config['pidfile'], 'w') + pidtext = "%d" % (os.getpid()) + fd.write(pidtext) + fd.close() -logger.info("OpenERP version - %s", __version__) -for name, value in [('addons_path', config['addons_path']), - ('database hostname', config['db_host'] or 'localhost'), - ('database port', config['db_port'] or '5432'), - ('database user', config['db_user'])]: - logger.info("%s - %s", name, value) +def run_registry(dbname): + """ Preload a registry, possibly run a test file, and start the cron.""" + db, pool = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) -# Don't allow if the connection to PostgreSQL done by postgres user -if config['db_user'] == 'postgres': - logger.error("Connecting to the database as 'postgres' user is forbidden, as it present major security issues. Shutting down.") - sys.exit(1) + if config["test_file"]: + cr = db.cursor() + logger = logging.getLogger('server') + logger.info('loading test file %s', config["test_file"]) + openerp.tools.convert_yaml_import(cr, 'base', file(config["test_file"]), {}, 'test', True) + cr.rollback() + cr.close() -#---------------------------------------------------------- -# init net service -#---------------------------------------------------------- -logger.info('initialising distributed objects services') + pool.get('ir.cron').restart(db.dbname) -#---------------------------------------------------------- -# Load and update databases if requested -#---------------------------------------------------------- +def export_translation(): + config = openerp.tools.config + dbname = config['db_name'] + logger = logging.getLogger('server') -if not ( config["stop_after_init"] or \ - config["translate_in"] or \ - config["translate_out"] ): - openerp.osv.osv.start_object_proxy() - openerp.service.web_services.start_web_services() + if config["language"]: + msg = "language %s" % (config["language"],) + else: + msg = "new language" + logger.info('writing translation file for %s to %s', msg, + config["translate_out"]) + + fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower() + buf = file(config["translate_out"], "w") + cr = openerp.pooler.get_db(dbname).cursor() + openerp.tools.trans_export(config["language"], + config["translate_modules"] or ["all"], buf, fileformat, cr) + cr.close() + buf.close() + + logger.info('translation file written successfully') + +def import_translation(): + config = openerp.tools.config + context = {'overwrite': config["overwrite_existing_translations"]} + dbname = config['db_name'] + + cr = openerp.pooler.get_db(dbname).cursor() + openerp.tools.trans_load( cr, config["translate_in"], config["language"], + context=context) + openerp.tools.trans_update_res_ids(cr) + cr.commit() + cr.close() + +def start_services(): http_server = openerp.service.http_server netrpc_server = openerp.service.netrpc_server + + # Instantiate local services (this is a legacy design). + openerp.osv.osv.start_object_proxy() + # Export (for RPC) services. + openerp.service.web_services.start_web_services() + + # Initialize the HTTP stack. http_server.init_servers() http_server.init_xmlrpc() http_server.init_static_http() netrpc_server.init_servers() -if config['db_name']: - for dbname in config['db_name'].split(','): - db, pool = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) - cr = db.cursor() + # Start the main cron thread. + openerp.netsvc.start_agent() - if config["test_file"]: - logger.info('loading test file %s', config["test_file"]) - openerp.tools.convert_yaml_import(cr, 'base', file(config["test_file"]), {}, 'test', True) - cr.rollback() - - pool.get('ir.cron').restart(db.dbname) - - cr.close() - -#---------------------------------------------------------- -# translation stuff -#---------------------------------------------------------- -if config["translate_out"]: - if config["language"]: - msg = "language %s" % (config["language"],) - else: - msg = "new language" - logger.info('writing translation file for %s to %s', msg, config["translate_out"]) - - fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower() - buf = file(config["translate_out"], "w") - dbname = config['db_name'] - cr = openerp.pooler.get_db(dbname).cursor() - openerp.tools.trans_export(config["language"], config["translate_modules"] or ["all"], buf, fileformat, cr) - cr.close() - buf.close() - - logger.info('translation file written successfully') - sys.exit(0) - -if config["translate_in"]: - context = {'overwrite': config["overwrite_existing_translations"]} - dbname = config['db_name'] - cr = openerp.pooler.get_db(dbname).cursor() - openerp.tools.trans_load(cr, - config["translate_in"], - config["language"], - context=context) - openerp.tools.trans_update_res_ids(cr) - cr.commit() - cr.close() - sys.exit(0) - -#---------------------------------------------------------------------------------- -# if we don't want the server to continue to run after initialization, we quit here -#---------------------------------------------------------------------------------- -if config["stop_after_init"]: - sys.exit(0) - -openerp.netsvc.start_agent() - -#---------------------------------------------------------- -# Launch Servers -#---------------------------------------------------------- - -LST_SIGNALS = ['SIGINT', 'SIGTERM'] - -SIGNALS = dict( - [(getattr(signal, sign), sign) for sign in LST_SIGNALS] -) + # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC). + openerp.netsvc.Server.startAll() quit_signals_received = 0 -def handler(signum, frame): - """ - :param signum: the signal number +def signal_handler(sig, frame): + """ Signal handler: exit ungracefully on the second handled signal. + + :param sig: the signal number :param frame: the interrupted stack frame or None """ global quit_signals_received quit_signals_received += 1 if quit_signals_received > 1: + # logging.shutdown was already called at this point. sys.stderr.write("Forced shutdown.\n") os._exit(0) -def dumpstacks(signum, frame): +def dumpstacks(sig, frame): + """ Signal handler: dump a stack trace for each existing thread.""" # code from http://stackoverflow.com/questions/132058/getting-stack-trace-from-a-running-python-application#answer-2569696 # modified for python 2.5 compatibility thread_map = dict(threading._active, **threading._limbo) @@ -191,20 +177,32 @@ def dumpstacks(signum, frame): code.append(" %s" % (line.strip())) logging.getLogger('dumpstacks').info("\n".join(code)) -for signum in SIGNALS: - signal.signal(signum, handler) +def setup_signal_handlers(): + SIGNALS = map(lambda x: getattr(signal, "SIG%s" % x), "INT TERM".split()) + map(lambda sig: signal.signal(sig, signal_handler), SIGNALS) + if os.name == 'posix': + signal.signal(signal.SIGQUIT, dumpstacks) -if os.name == 'posix': - signal.signal(signal.SIGQUIT, dumpstacks) +def quit_on_signals(): + """ Wait for one or two signals then shutdown the server. + + The first SIGINT or SIGTERM signal will initiate a graceful shutdown while + a second one if any will force an immediate exit. + + """ + # Wait for a first signal to be handled. (time.sleep will be interrupted + # by the signal handler.) + while quit_signals_received == 0: + time.sleep(60) -def quit(): openerp.netsvc.Agent.quit() openerp.netsvc.Server.quitAll() + config = openerp.tools.config if config['pidfile']: os.unlink(config['pidfile']) - logger = logging.getLogger('shutdown') - logger.info("Initiating OpenERP Server shutdown") - logger.info("Hit CTRL-C again or send a second signal to immediately terminate the server...") + logger = logging.getLogger('server') + logger.info("Initiating shutdown") + logger.info("Hit CTRL-C again or send a second signal to force the sutdown.") logging.shutdown() # manually join() all threads before calling sys.exit() to allow a second signal @@ -219,19 +217,36 @@ def quit(): time.sleep(0.05) sys.exit(0) -if config['pidfile']: - fd = open(config['pidfile'], 'w') - pidtext = "%d" % (os.getpid()) - fd.write(pidtext) - fd.close() +if __name__ == "__main__": -openerp.netsvc.Server.startAll() + check_root_user() + openerp.tools.config.parse_config(sys.argv[1:]) + check_postgres_user() + openerp.netsvc.init_logger() + report_configuration() -logger.info('OpenERP server is running, waiting for connections...') + config = openerp.tools.config -while quit_signals_received == 0: - time.sleep(60) + if config["translate_out"]: + export_translation() + sys.exit(0) -quit() + if config["translate_in"]: + import_translation() + sys.exit(0) + + if config['db_name']: + for dbname in config['db_name'].split(','): + run_registry(dbname) + + if config["stop_after_init"]: + sys.exit(0) + + setup_pid_file() + setup_signal_handlers() + start_services() + logger = logging.getLogger('server') + logger.info('OpenERP server is running, waiting for connections...') + quit_on_signals() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 4b9adb144bd..277965c5449 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -458,7 +458,8 @@ def trans_export(lang, modules, buffer, format, cr): tar.close() else: - raise Exception(_('Bad file format')) + raise Exception(_('Unrecognized extension: must be one of ' + '.csv, .po, or .tgz (received .%s).' % format)) newlang = not bool(lang) if newlang: From 37643a0909c265ed2d62aaafec995f638933de14 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 23 Aug 2011 16:13:00 +0200 Subject: [PATCH 05/41] [ADD] basics of on_write, split ListView.reload_record(index) in ListView.reload_record(record) and ListView.reload_record_at_index(index) migrate most calls to reload_record(record) bzr revid: xmo@openerp.com-20110823141300-zujxs2oaunqwhprt --- addons/base/static/src/js/list-editable.js | 19 ++++++++++++- addons/base/static/src/js/list.js | 31 +++++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 8810d902346..1932688783d 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -105,7 +105,7 @@ openerp.base.list_editable = function (openerp) { } if (this.edition_index !== null) { - this.reload_record(this.edition_index).then(function () { + this.reload_record_at_index(this.edition_index).then(function () { cancelled.resolve(); }); } else { @@ -201,6 +201,21 @@ openerp.base.list_editable = function (openerp) { }); }); }, + handle_onwrite: function (record_id, index) { + var self = this; + var on_write_callback = self.view.fields_view.arch.attrs.on_write; + if (!on_write_callback) { return; } + this.dataset.call(on_write_callback, [record_id], function (ids) { + _(ids).each(function (id) { + var record = self.records.get(id); + if (!record) { + record = new openerp.base.list.Record({id: id}); + self.records.add(record, {at: index}); + } + self.reload_record(record); + }); + }); + }, /** * Saves the current row, and triggers the edition of its following * sibling if asked. @@ -215,6 +230,8 @@ openerp.base.list_editable = function (openerp) { {at: self.options.editable === 'top' ? 0 : null}); self.edition_index = self.dataset.index; } + self.handle_onwrite(self.dataset.ids[self.dataset.index], + self.dataset.index); self.cancel_pending_edition().then(function () { $(self).trigger('saved', [self.dataset]); if (!edit_next) { diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 95db36f82de..b2faaa55faf 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -672,11 +672,10 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. var $target = $(e.currentTarget), field = $target.closest('td').data('field'), $row = $target.closest('tr'), - record_id = self.row_id($row), - index = self.row_position($row); + record_id = self.row_id($row); $(self).trigger('action', [field, record_id, function () { - return self.reload_record(index); + return self.reload_record(self.records.get(record_id)); }]); }) .delegate('tr', 'click', function (e) { @@ -757,25 +756,31 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. /** * Reloads the record at index ``row_index`` in the list's rows. * - * By default, simply re-renders the record. If the ``fetch`` parameter is - * provided and ``true``, will first fetch the record anew. - * * @param {Number} record_index index of the record to reload - * @param {Boolean} fetch fetches the record from remote before reloading it + * @returns {$.Deferred} promise to the finalization of the reloading */ - reload_record: function (record_index) { + reload_record_at_index: function (record_index) { var r = this.records.at(record_index); + return this.reload_record(r); + }, + /** + * Reloads the provided record by re-reading its content from the server. + * + * @param {Record} record + * @returns {$.Deferred} promise to the finalization of the reloading + */ + reload_record: function (record) { return this.dataset.read_ids( - [r.get('id')], + [record.get('id')], _.pluck(_(this.columns).filter(function (r) { return r.tag === 'field'; }), 'name'), - function (record) { - _(record[0]).each(function (value, key) { - r.set(key, value, {silent: true}); + function (records) { + _(records[0]).each(function (value, key) { + record.set(key, value, {silent: true}); }); - r.trigger('change', r); + record.trigger('change', record); } ); }, From 00a7458bc42f7f3f94e166cb9f656a192c4542fb Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 23 Aug 2011 16:42:35 +0200 Subject: [PATCH 06/41] [IMP] remember to insert the relevant record ids into the dataset if they're not there bzr revid: xmo@openerp.com-20110823144235-g09rkurd1ndrrkme --- addons/base/static/src/js/list-editable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 1932688783d..e14eb02b3a2 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -211,6 +211,7 @@ openerp.base.list_editable = function (openerp) { if (!record) { record = new openerp.base.list.Record({id: id}); self.records.add(record, {at: index}); + self.dataset.ids.splice(index, 0, id); } self.reload_record(record); }); From ae4d1d5e3bdaf54121bcc9f17de06f009d0075f7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 10:53:03 +0200 Subject: [PATCH 07/41] [IMP] to maintain correct synchro, listview should avoid relying on indexes as much as possible. Get rid on indexes on rows, and of List.row_position bzr revid: xmo@openerp.com-20110824085303-6h3wggzynpic0mcz --- addons/base/static/src/js/list-editable.js | 10 ++--- addons/base/static/src/js/list.js | 44 +++++++++------------- addons/base/static/src/xml/base.xml | 3 +- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index e14eb02b3a2..8d94bb0f601 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -143,7 +143,6 @@ openerp.base.list_editable = function (openerp) { var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), 'data-id': $(row).data('id'), - 'data-index': $(row).data('index'), 'class': $(row).attr('class') + ' oe_forms', click: function (e) {e.stopPropagation();} }) @@ -201,14 +200,16 @@ openerp.base.list_editable = function (openerp) { }); }); }, - handle_onwrite: function (record_id, index) { + handle_onwrite: function (source_record_id) { var self = this; var on_write_callback = self.view.fields_view.arch.attrs.on_write; if (!on_write_callback) { return; } - this.dataset.call(on_write_callback, [record_id], function (ids) { + this.dataset.call(on_write_callback, [source_record_id], function (ids) { _(ids).each(function (id) { var record = self.records.get(id); if (!record) { + var index = self.records.indexOf( + self.records.get(source_record_id)); record = new openerp.base.list.Record({id: id}); self.records.add(record, {at: index}); self.dataset.ids.splice(index, 0, id); @@ -231,8 +232,7 @@ openerp.base.list_editable = function (openerp) { {at: self.options.editable === 'top' ? 0 : null}); self.edition_index = self.dataset.index; } - self.handle_onwrite(self.dataset.ids[self.dataset.index], - self.dataset.index); + self.handle_onwrite(self.dataset.ids[self.dataset.index]); self.cancel_pending_edition().then(function () { $(self).trigger('saved', [self.dataset]); if (!edit_next) { diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index b2faaa55faf..aa6f960bec9 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -641,7 +641,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. 'reset': $.proxy(this, 'on_records_reset'), 'change': function (event, record) { var $row = self.$current.find('[data-id=' + record.get('id') + ']'); - $row.replaceWith(self.render_record($row.data('index'))); + $row.replaceWith(self.render_record(record)); }, 'add': function (ev, records, record, index) { $('').attr({ @@ -680,7 +680,9 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. }) .delegate('tr', 'click', function (e) { e.stopPropagation(); - self.dataset.index = self.row_position(e.currentTarget); + self.dataset.index = self.records.indexOf( + self.records.get( + self.row_id(e.currentTarget))); self.row_clicked(e); }); }, @@ -717,15 +719,6 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. }); return result; }, - /** - * Returns the index of the row in the list of rows. - * - * @param {Object} row the selected row - * @returns {Number} the position of the row in this.rows - */ - row_position: function (row) { - return $(row).data('index'); - }, /** * Returns the identifier of the object displayed in the provided table * row @@ -787,21 +780,21 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. /** * Renders a list record to HTML * - * @param {Number} record_index index of the record to render in ``this.rows`` + * @param {Record} record index of the record to render in ``this.rows`` * @returns {String} QWeb rendering of the selected record */ - render_record: function (record_index) { + render_record: function (record) { + var index = this.records.indexOf(record); return QWeb.render('ListView.row', { columns: this.columns, options: this.options, - record: this.records.at(record_index), - row_parity: (record_index % 2 === 0) ? 'even' : 'odd', - row_index: record_index, + record: record, + row_parity: (index % 2 === 0) ? 'even' : 'odd', render_cell: openerp.base.format_cell }); }, /** - * Fixes @data-index on all table rows, and fixes the even/odd classes + * Fixes fixes the even/odd classes * * @param {Number} [from_index] index from which to resequence * @param {Number} [offset = 0] selection offset for DOM, in case there are rows to ignore in the table @@ -815,8 +808,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. var index = from_index + i; // reset record-index accelerators on rows and even/odd var even = index%2 === 0; - $(e).attr('data-index', index) - .toggleClass('even', even) + $(e).toggleClass('even', even) .toggleClass('odd', !even); }); } @@ -1080,14 +1072,12 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas // ondrop, move relevant record & fix sequences list.$current.sortable({ stop: function (event, ui) { - var from = ui.item.data('index'), - to = ui.item.prev().data('index') || 0; - if (from === to) { return; } - var to_move = list.records.at(from); - list.records.remove(to_move); - list.records.add(to_move, {at: to}); + var to_move = list.records.get(ui.item.data('id')), + target_id = ui.item.prev().data('id'); - list.refresh_zebra(); + list.records.remove(to_move); + var to = target_id ? list.records.indexOf(list.records.get(target_id)) : 0; + list.records.add(to_move, { at: to }); // resequencing time! var record, index = to, @@ -1101,6 +1091,8 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas dataset.write(record.get('id'), {sequence: seq}); record.set('sequence', seq); } + + list.refresh_zebra(); } }); }, diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 89b452a6760..879d98d15a6 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -554,12 +554,11 @@ - + t-att-data-id="record.get('id')"> From 2afcf2130b33653cf471bfcb49e2d8e542bd064a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 14:21:24 +0200 Subject: [PATCH 08/41] [ADD] pluck method to collection bzr revid: xmo@openerp.com-20110824122124-xr4g92x6jlw85rbg --- addons/base/static/src/js/list.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index aa6f960bec9..b84c286fd57 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -1437,6 +1437,11 @@ var Collection = openerp.base.Class.extend(/** @lends Collection# */{ }); return results; }, + pluck: function (fieldname) { + return this.map(function (record) { + return record.get(fieldname); + }); + }, indexOf: function (record) { return _(this.records).indexOf(record); } From a0486acd740ac07ebb94fa3867307da769e7b854 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 14:22:51 +0200 Subject: [PATCH 09/41] [FIX] some more usages of indexes in list and list edition (should now be index-free except as transient values), and implement on_write support remove ListView.List#reload_record_at_index as everything is now id-based to avoid desync issues bzr revid: xmo@openerp.com-20110824122251-9vw96qbn7snnkq3j --- addons/base/static/src/js/list-editable.js | 47 ++++++++++++++-------- addons/base/static/src/js/list.js | 25 ++++++------ 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 8d94bb0f601..1cd43fd6ea8 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -92,7 +92,7 @@ openerp.base.list_editable = function (openerp) { if (!this.options.editable) { return this._super(event); } - this.edit_record(); + this.edit_record($(event.currentTarget).data('id')); }, /** * Checks if a record is being edited, and if so cancels it @@ -104,8 +104,8 @@ openerp.base.list_editable = function (openerp) { return cancelled.promise(); } - if (this.edition_index !== null) { - this.reload_record_at_index(this.edition_index).then(function () { + if (this.edition_id != null) { + this.reload_record(self.records.get(this.edition_id)).then(function () { cancelled.resolve(); }); } else { @@ -115,13 +115,14 @@ openerp.base.list_editable = function (openerp) { self.edition_form.stop(); self.edition_form.$element.remove(); delete self.edition_form; - delete self.edition_index; + delete self.edition_id; delete self.edition; }); return cancelled.promise(); }, /** - * Adapts this list's view description to be suitable to the inner form view of a row being edited. + * Adapts this list's view description to be suitable to the inner form + * view of a row being edited. * * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows. */ @@ -140,9 +141,10 @@ openerp.base.list_editable = function (openerp) { render_row_as_form: function (row) { var self = this; this.cancel_pending_edition().then(function () { + var record_id = $(row).data('id'); var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), - 'data-id': $(row).data('id'), + 'data-id': record_id, 'class': $(row).attr('class') + ' oe_forms', click: function (e) {e.stopPropagation();} }) @@ -175,7 +177,7 @@ openerp.base.list_editable = function (openerp) { self.$current.append($new_row); } self.edition = true; - self.edition_index = self.dataset.index; + self.edition_id = record_id; self.edition_form = _.extend(new openerp.base.FormView( self, $new_row.attr('id'), self.dataset, false), { template: 'ListView.row.form', @@ -208,8 +210,9 @@ openerp.base.list_editable = function (openerp) { _(ids).each(function (id) { var record = self.records.get(id); if (!record) { + // insert after the source record var index = self.records.indexOf( - self.records.get(source_record_id)); + self.records.get(source_record_id)) + 1; record = new openerp.base.list.Record({id: id}); self.records.add(record, {at: index}); self.dataset.ids.splice(index, 0, id); @@ -227,12 +230,16 @@ openerp.base.list_editable = function (openerp) { save_row: function (edit_next) { var self = this; this.edition_form.do_save(function (result) { - if (result.created && !self.edition_index) { + if (result.created && !self.edition_id) { self.records.add({id: result.result}, {at: self.options.editable === 'top' ? 0 : null}); - self.edition_index = self.dataset.index; + self.edition_id = result.result; } - self.handle_onwrite(self.dataset.ids[self.dataset.index]); + var edited_record = self.records.get(self.edition_id), + next_record = self.records.at( + self.records.indexOf(edited_record) + 1); + + self.handle_onwrite(self.edition_id); self.cancel_pending_edition().then(function () { $(self).trigger('saved', [self.dataset]); if (!edit_next) { @@ -242,8 +249,16 @@ openerp.base.list_editable = function (openerp) { self.new_record(); return; } - self.dataset.next(); - self.edit_record(); + var next_record_id; + if (next_record) { + next_record_id = next_record.get('id'); + self.dataset.index = _(self.dataset.ids) + .indexOf(next_record_id); + } else { + self.dataset.index = 0; + next_record_id = self.records.at(0).get('id'); + } + self.edit_record(next_record_id); }); }, this.options.editable === 'top'); }, @@ -256,12 +271,12 @@ openerp.base.list_editable = function (openerp) { /** * Edits record currently selected via dataset */ - edit_record: function () { + edit_record: function (record_id) { this.render_row_as_form( - this.$current.children(':eq(' + this.dataset.index + ')')); + this.$current.find('[data-id=' + record_id + ']')); $(this).trigger( 'edit', - [this.records.at(this.dataset.index).get('id'), this.dataset]); + [record_id, this.dataset]); }, new_record: function () { this.dataset.index = null; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index b84c286fd57..0e5544961b4 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -644,9 +644,19 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. $row.replaceWith(self.render_record(record)); }, 'add': function (ev, records, record, index) { - $('').attr({ + var $new_row = $('').attr({ 'data-id': record.get('id') - }).insertAfter(self.$current.children(':eq(' + index + ')')); + }); + + if (index === 0) { + $new_row.prependTo(self.$current); + } else { + var previous_record = records.at(index-1), + $previous_sibling = self.$current.find( + '[data-id=' + previous_record.get('id') + ']'); + $new_row.insertAfter($previous_sibling); + } + self.refresh_zebra(index, 1); } }; @@ -746,17 +756,6 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. return {count: 1, values: record.attributes}; }); }, - /** - * Reloads the record at index ``row_index`` in the list's rows. - * - * @param {Number} record_index index of the record to reload - * @returns {$.Deferred} promise to the finalization of the reloading - */ - reload_record_at_index: function (record_index) { - var r = this.records.at(record_index); - - return this.reload_record(r); - }, /** * Reloads the provided record by re-reading its content from the server. * From 777857b269d821ea621b0563f4e03c429c8f4472 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 15:16:06 +0200 Subject: [PATCH 10/41] [FIX] weird race condition on listview init: in some cases, would not wait for the initial view loading before letting the viewmanager launch a search, making initial load overwrite search ending with an empty list bzr revid: xmo@openerp.com-20110824131606-yokgoe3dz9ltaz2l --- addons/base/static/src/js/list.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 0e5544961b4..47c04537ddc 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -363,20 +363,21 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {Object} [context] context to send the server while loading the view */ reload_view: function (grouped, context) { - var self = this; - var callback = function (field_view_get) { + var self = this, d = $.Deferred(); + d.then(function (field_view_get) { self.on_loaded(field_view_get, grouped); - }; + }); if (this.embedded_view) { - return $.Deferred().then(callback).resolve({fields_view: this.embedded_view}); + d.resolve({fields_view: this.embedded_view}); } else { - return this.rpc('/base/listview/load', { + this.rpc('/base/listview/load', { model: this.model, view_id: this.view_id, context: this.dataset.get_context(context), toolbar: this.options.sidebar - }, callback); + }, function () { d.resolve.apply(this, arguments); }); } + return d.promise(); }, /** * re-renders the content of the list view From 04cb566ad9b44523325f6a4d3fb175f327cf15b0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 16:52:05 +0200 Subject: [PATCH 11/41] [IMP] datetime search field: use date widget, make field's default domain span the whole day (using range) search API improvement: Field gains a make_domain method, called by get_domain to build the default domain for the field. make_domain is called with the field's name, operator and current values (fully computed), the default implementation is to just shove them all into a domain. This lets fields customize the default domain for the field without having to reimplement (or breaking) the filter_domain handling bzr revid: xmo@openerp.com-20110824145205-2wnig9duebja9jrk --- addons/base/static/src/js/search.js | 31 ++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 63ccec088a6..7573c39d399 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -590,6 +590,20 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp {}, context, {own_values: {self: val}}); }, + /** + * Function creating the returned domain for the field, override this + * methods in children if you only need to customize the field's domain + * without more complex alterations or tests (and without the need to + * change override the handling of filter_domain) + * + * @param {String} name the field's name + * @param {String} operator the field's operator (either attribute-specified or default operator for the field + * @param {Number|String} value parsed value for the field + * @returns {Array} domain to include in the resulting search + */ + make_domain: function (name, operator, value) { + return [[name, operator, value]]; + }, get_domain: function () { var val = this.get_value(); if (val === null || val === '') { @@ -598,11 +612,10 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp var domain = this.attrs['filter_domain']; if (!domain) { - return [[ + return this.make_domain( this.attrs.name, this.attrs.operator || this.default_operator, - this.get_value() - ]]; + val); } return _.extend({}, domain, {own_values: {self: val}}); } @@ -724,6 +737,10 @@ openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends ope } }); openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({ + make_domain: function (name, operator, value) { + return ['&', [name, '>=', value + ' 00:00:00'], + [name, '<=', value + ' 23:59:59']]; + } }); openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({ init: function (view_section, field, view) { @@ -781,16 +798,16 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({ } return this._super(defaults); }, - get_domain: function () { + make_domain: function (name, operator, value) { if (this.id && this.name) { - if (this.$element.val() === this.name) { - return [[this.attrs.name, '=', this.id]]; + if (value === this.name) { + return [[name, '=', this.id]]; } else { delete this.id; delete this.name; } } - return this._super(); + return this._super(name, operator, value); } }); From 88200c8bbfe252b5ec8ce8f41e137eb8a289c244 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 24 Aug 2011 16:56:47 +0200 Subject: [PATCH 12/41] [IMP] add docstring to openerp.base.search.DateTimeField bzr revid: xmo@openerp.com-20110824145647-h4mbaz4sjjjccacv --- addons/base/static/src/js/search.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 7573c39d399..99f07784160 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -736,7 +736,18 @@ openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends ope return this.$element.val(); } }); -openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({ +/** + * Implementation of the ``datetime`` openerp field type: + * + * * Uses the same widget as the ``date`` field type (a simple date) + * + * * Builds a slighly more complex, it's a datetime range (includes time) + * spanning the whole day selected by the date widget + * + * @class + * @extends openerp.base.DateField + */ +openerp.base.search.DateTimeField = openerp.base.search.DateField.extend(/** @lends openerp.base.search.DateTimeField# */{ make_domain: function (name, operator, value) { return ['&', [name, '>=', value + ' 00:00:00'], [name, '<=', value + ' 23:59:59']]; From ffeff63fb69e64ecdb62810c5c1d7d4fc9d80239 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 24 Aug 2011 17:13:57 +0200 Subject: [PATCH 13/41] [ADD] Translate dialog (WIP) bzr revid: fme@openerp.com-20110824151357-nln9mnkw13uyng32 --- addons/base/static/src/css/base.css | 9 ++- addons/base/static/src/js/form.js | 13 +++- addons/base/static/src/js/views.js | 111 +++++++++++++++++++++++++++- addons/base/static/src/xml/base.xml | 42 ++++++++++- 4 files changed, 170 insertions(+), 5 deletions(-) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 242167480f3..cd420ea8c0d 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -878,13 +878,20 @@ label.error { position: relative; vertical-align: top; } -.openerp img.ui-datepicker-trigger, .openerp img.ui-datepicker-trigger { +.openerp img.ui-datepicker-trigger { margin-left: -20px; vertical-align: middle; cursor: pointer; position: relative; top: -1px; } +.openerp img.oe_field_translate { + margin-left: -21px; + vertical-align: top; + cursor: pointer; + position: relative; + top: 4px; +} /* http://www.quirksmode.org/dom/inputfile.html * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 5da048a17b9..7a4d3c92d89 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -38,6 +38,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi this.registry = openerp.base.form.widgets; this.has_been_loaded = $.Deferred(); this.$form_header = null; + this.translatable_fields = []; _.defaults(this.options, {"always_show_new_button": true}); }, start: function() { @@ -461,7 +462,7 @@ openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({ ['res_model', '=', this.view.dataset.model], ['res_id', '=', this.view.datarecord.id], ['type', 'in', ['binary', 'url']] - ])).read_slice(['name', 'url', 'type'], this.on_attachments_loaded); + ])).read_slice(['name', 'url', 'type'], {}, this.on_attachments_loaded); } }, on_attachments_loaded: function(attachments) { @@ -839,6 +840,13 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ this.invalid = false; this.dirty = false; }, + start: function() { + this._super.apply(this, arguments); + if (this.field.translate) { + this.view.translatable_fields.push(this); + this.$element.find('.oe_field_translate').click(this.on_translate); + } + }, set_value: function(value) { this.value = value; this.invalid = false; @@ -850,6 +858,9 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ }, on_value_changed: function() { }, + on_translate: function() { + this.view.open_translate_dialog(this); + }, get_value: function() { return this.value; }, diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index cb9b7d364cf..c6782150297 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -4,6 +4,8 @@ openerp.base.views = function(openerp) { +var _t = openerp.base._t; + /** * Registry for all the client actions key: tag value: widget */ @@ -450,6 +452,106 @@ openerp.base.Sidebar = openerp.base.Widget.extend({ } }); +openerp.base.TranslateDialog = openerp.base.Dialog.extend({ + dialog_title: _t("Translations"), + init: function(view) { + this['on_button' + _t("Save")] = this.on_button_Save; + this['on_button' + _t("Close")] = this.on_button_Close; + this._super(view, { + width: '80%', + height: '80%' + }); + this.view = view; + this.view_type = view.fields_view.type || ''; + this.$fields_form = null; + this.$view_form = null; + this.$sidebar_form = null; + this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name }); + this.languages = null; + this.languages_loaded = $.Deferred(); + (new openerp.base.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), + [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded); + }, + start: function() { + var self = this; + this._super(); + $.when(this.languages_loaded).then(function() { + self.$element.html(QWeb.render('TranslateDialog', { widget: self })); + self.$element.tabs(); + if (!(self.view.translatable_fields && self.view.translatable_fields.length)) { + self.hide_tabs('fields'); + self.select_tab('view'); + } + self.$fields_form = self.$element.find('.oe_translation_form'); + }); + return this; + }, + on_languages_loaded: function(langs) { + this.languages = langs; + this.languages_loaded.resolve(); + }, + do_load_fields_values: function(callback) { + var self = this, + deffered = []; + this.$fields_form.find('.oe_trad_field').val('').removeClass('touched'); + _.each(self.languages, function(lg) { + var deff = $.Deferred(); + deffered.push(deff); + self.rpc('/base/dataset/get', { + model: self.view.dataset.model, + ids: [self.view.datarecord.id], + fields: self.translatable_fields_keys, + context: self.view.dataset.get_context({ + 'lang': lg.code + }) + }, function(values) { + _.each(self.translatable_fields_keys, function(f) { + self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || ''); + }); + deff.resolve(); + }); + }); + $.when.apply(null, deffered).then(callback); + }, + show_tabs: function() { + for (var i = 0; i < arguments.length; i++) { + this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().show(); + } + }, + hide_tabs: function() { + for (var i = 0; i < arguments.length; i++) { + this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().hide(); + } + }, + select_tab: function(name) { + this.show_tabs(name); + var index = this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().index() - 1; + this.$element.tabs('select', index); + }, + open: function(field) { + var self = this, + super = this._super; + $.when(this.languages_loaded).then(function() { + if (self.view.translatable_fields && self.view.translatable_fields.length) { + self.do_load_fields_values(function() { + super.call(self); + if (field) { + // TODO: focus and scroll to field + } + }); + } else { + super.call(self); + } + }); + }, + on_button_Save: function() { + this.close(); + }, + on_button_Close: function() { + this.close(); + } +}); + openerp.base.View = openerp.base.Widget.extend({ set_default_options: function(options) { this.options = options || {}; @@ -461,6 +563,12 @@ openerp.base.View = openerp.base.Widget.extend({ action_views_ids: {} }); }, + open_translate_dialog: function(field) { + if (!this.translate_dialog) { + this.translate_dialog = new openerp.base.TranslateDialog(this).start(); + } + this.translate_dialog.open(field); + }, /** * Fetches and executes the action identified by ``action_data``. * @@ -542,7 +650,7 @@ openerp.base.View = openerp.base.Widget.extend({ }, { label: "Translate", callback: this.on_sidebar_translate, - classname: 'oe_hide oe_sidebar_translate' + classname: 'oe_sidebar_translate' }, { label: "View Log", callback: this.on_sidebar_view_log, @@ -570,6 +678,7 @@ openerp.base.View = openerp.base.Widget.extend({ export_view.start(); }, on_sidebar_translate: function() { + this.open_translate_dialog(); }, on_sidebar_view_log: function() { } diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 879d98d15a6..69aac831386 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -453,6 +453,40 @@ + + +
    + + + + + + + + + +
    +
    Field
    +
    +
    +
    + + + + +
    +
    +
    + Translate view +
    +
    + Translate sidebar +
    +

    +
    @@ -734,11 +770,13 @@
    - + Date: Wed, 24 Aug 2011 17:57:23 +0200 Subject: [PATCH 14/41] [IMP] res_company/res_user: removed unused methods _get_child_ids/company_get. bzr revid: vmt@openerp.com-20110824155723-6wk2v8rb1gekbavv --- openerp/addons/base/res/res_company.py | 5 ----- openerp/addons/base/res/res_user.py | 5 ----- 2 files changed, 10 deletions(-) diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py index b0ebeb8ac27..fc63ac42dd9 100644 --- a/openerp/addons/base/res/res_company.py +++ b/openerp/addons/base/res/res_company.py @@ -189,11 +189,6 @@ class res_company(osv.osv): return rule.company_dest_id.id return user.company_id.id - def _get_child_ids(self, cr, uid, uid2, context={}): - company = self.pool.get('res.users').company_get(cr, uid, uid2) - ids = self._get_company_children(cr, uid, company) - return ids - @tools.cache() def _get_company_children(self, cr, uid=None, company=None): if not company: diff --git a/openerp/addons/base/res/res_user.py b/openerp/addons/base/res/res_user.py index a2bf9721e42..09360996396 100644 --- a/openerp/addons/base/res/res_user.py +++ b/openerp/addons/base/res/res_user.py @@ -346,10 +346,6 @@ class users(osv.osv): 'menu_tips':True } - @tools.cache() - def company_get(self, cr, uid, uid2, context=None): - return self._get_company(cr, uid, context=context, uid2=uid2) - # User can write to a few of her own fields (but not her groups for example) SELF_WRITEABLE_FIELDS = ['menu_tips','view', 'password', 'signature', 'action_id', 'company_id', 'user_email'] @@ -369,7 +365,6 @@ class users(osv.osv): res = super(users, self).write(cr, uid, ids, values, context=context) # clear caches linked to the users - self.company_get.clear_cache(cr.dbname) self.pool.get('ir.model.access').call_cache_clearing_methods(cr) clear = partial(self.pool.get('ir.rule').clear_cache, cr) map(clear, ids) From de955d098388688206f10c4858b178ad054acd88 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 10:18:33 +0200 Subject: [PATCH 15/41] [REV] xmo@openerp.com-20110824131606-yokgoe3dz9ltaz2l, has nothing to do with the issue and does not actually fix it (just fudges some timing so it happens slightly less often) bzr revid: xmo@openerp.com-20110825081833-a1vbcdm59mrzf2d0 --- addons/base/static/src/js/list.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 47c04537ddc..9797b5dff99 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -120,7 +120,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi */ start: function() { this.$element.addClass('oe-listview'); - return this.reload_view(); + return this.reload_view(null, null, true); }, /** * Called after loading the list view's description, sets up such things @@ -362,22 +362,22 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {Boolean} [grouped] Should the list be displayed grouped * @param {Object} [context] context to send the server while loading the view */ - reload_view: function (grouped, context) { - var self = this, d = $.Deferred(); - d.then(function (field_view_get) { + reload_view: function (grouped, context, initial) { + var self = this; + var callback = function (field_view_get) { + console.log('loaded', initial ? 'initial' : 'subsequent'); self.on_loaded(field_view_get, grouped); - }); + }; if (this.embedded_view) { - d.resolve({fields_view: this.embedded_view}); + return $.Deferred().then(callback).resolve({fields_view: this.embedded_view}); } else { - this.rpc('/base/listview/load', { + return this.rpc('/base/listview/load', { model: this.model, view_id: this.view_id, context: this.dataset.get_context(context), toolbar: this.options.sidebar - }, function () { d.resolve.apply(this, arguments); }); + }, callback); } - return d.promise(); }, /** * re-renders the content of the list view From d2da68ad8b66e2c93c4552cb47bde7a34378c635 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 10:22:21 +0200 Subject: [PATCH 16/41] [FIX] actually fix race condition on listview init: incorrect on_mode_switch override in xmo@openerp.com-20110816123510-gcx0dk8b2sgox5oi did not propagate primise from ViewManager's on_mode_switch bzr revid: xmo@openerp.com-20110825082221-tasmo3je0wu4iw6h --- addons/base/static/src/js/views.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index c6782150297..7f6d78d9495 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -325,8 +325,9 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({ } }, on_mode_switch: function (view_type) { - this._super(view_type); - this.shortcut_check(this.views[view_type]); + return $.when( + this._super(view_type), + this.shortcut_check(this.views[view_type])); }, shortcut_check : function(view) { var self = this; From 6e7a9a1a3c7e00e7aa24fcfb37b22c815e0c257c Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 25 Aug 2011 11:42:12 +0200 Subject: [PATCH 17/41] [IMP] Form fields now uses niv's parse/format methods bzr revid: fme@openerp.com-20110825094212-31l90vq66riiabl4 --- addons/base/static/src/js/form.js | 94 ++++++------------------------- 1 file changed, 17 insertions(+), 77 deletions(-) diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 7a4d3c92d89..3280b7e0d81 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -949,7 +949,7 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; + var show_value = openerp.base.format_value(value, this, ''); this.$element.find('input').val(show_value); }, update_dom: function() { @@ -957,16 +957,16 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({ this.$element.find('input').attr('disabled', this.readonly); }, set_value_from_ui: function() { - this.value = this.$element.find('input').val(); + this.value = openerp.base.parse_value(this.$element.find('input').val(), this); this._super(); }, validate: function() { this.invalid = false; - var value = this.$element.find('input').val(); - if (value === "") { - this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); + try { + var value = openerp.base.parse_value(this.$element.find('input').val(), this, ''); + this.invalid = this.required && value === ''; + } catch(e) { + this.invalid = true; } }, focus: function() { @@ -978,7 +978,6 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldEmail"; - this.validation_regex = /@/; }, start: function() { this._super.apply(this, arguments); @@ -993,8 +992,7 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; - this.$element.find('a').attr('href', 'mailto:' + show_value); + this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val()); } }); @@ -1017,44 +1015,13 @@ openerp.base.form.FieldUrl = openerp.base.form.FieldChar.extend({ }); openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^-?\d+(\.\d+)?$/; - }, set_value: function(value) { - this._super.apply(this, [value]); if (value === false || value === undefined) { // As in GTK client, floats default to 0 value = 0; this.dirty = true; } - var show_value = value.toFixed(2); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - this.value = Number(this.$element.find('input').val().replace(/,/g, '.')); - this._super(); - } -}); - -openerp.base.form.FieldInteger = openerp.base.form.FieldFloat.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^-?\d+$/; - }, - set_value: function(value) { this._super.apply(this, [value]); - if (value === false || value === undefined) { - // TODO fme: check if GTK client default integers to 0 (like it does with floats) - value = 0; - this.dirty = true; - } - var show_value = parseInt(value, 10); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - this.value = Number(this.$element.find('input').val()); - this._super(); } }); @@ -1063,7 +1030,6 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ this._super(view, node); this.template = "FieldDate"; this.jqueryui_object = 'datetimepicker'; - this.validation_regex = /^\d+-\d+-\d+( \d+:\d+(:\d+)?)?$/; }, start: function() { this._super.apply(this, arguments); @@ -1103,8 +1069,6 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ var value = this.$element.find('input').val(); if (value === "") { this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); } else { this.invalid = !this.$element.find('input')[this.jqueryui_object]('getDate'); } @@ -1120,39 +1084,15 @@ openerp.base.form.FieldDate = openerp.base.form.FieldDatetime.extend({ init: function(view, node) { this._super(view, node); this.jqueryui_object = 'datepicker'; - this.validation_regex = /^\d+-\d+-\d+$/; }, parse: openerp.base.str_to_date, format: openerp.base.date_to_str }); -openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^\d+:\d+$/; - }, - set_value: function(value) { - this._super.apply(this, [value]); - if (value === false || value === undefined) { - // As in GTK client, floats default to 0 - value = 0; - this.dirty = true; - } - var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60)); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - var time = this.$element.find('input').val().split(':'); - this.set_value(parseInt(time[0], 10) + parseInt(time[1], 10) / 60); - this._super(); - } -}); - openerp.base.form.FieldText = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldText"; - this.validation_regex = null; }, start: function() { this._super.apply(this, arguments); @@ -1160,7 +1100,7 @@ openerp.base.form.FieldText = openerp.base.form.Field.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; + var show_value = openerp.base.format_value(value, this, ''); this.$element.find('textarea').val(show_value); }, update_dom: function() { @@ -1168,16 +1108,16 @@ openerp.base.form.FieldText = openerp.base.form.Field.extend({ this.$element.find('textarea').attr('disabled', this.readonly); }, set_value_from_ui: function() { - this.value = this.$element.find('textarea').val(); + this.value = openerp.base.parse_value(this.$element.find('textarea').val(), this); this._super(); }, validate: function() { this.invalid = false; - var value = this.$element.find('textarea').val(); - if (value === "") { - this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); + try { + var value = openerp.base.parse_value(this.$element.find('textarea').val(), this, ''); + this.invalid = this.required && value === ''; + } catch(e) { + this.invalid = true; } }, focus: function() { @@ -2440,9 +2380,9 @@ openerp.base.form.widgets = new openerp.base.Registry({ 'reference' : 'openerp.base.form.FieldReference', 'boolean' : 'openerp.base.form.FieldBoolean', 'float' : 'openerp.base.form.FieldFloat', - 'integer': 'openerp.base.form.FieldInteger', + 'integer': 'openerp.base.form.FieldFloat', + 'float_time': 'openerp.base.form.FieldFloat', 'progressbar': 'openerp.base.form.FieldProgressBar', - 'float_time': 'openerp.base.form.FieldFloatTime', 'image': 'openerp.base.form.FieldBinaryImage', 'binary': 'openerp.base.form.FieldBinaryFile' }); From 3da1145220c86e8e2b52c883ce34078fa4fde2a6 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 25 Aug 2011 11:48:06 +0200 Subject: [PATCH 18/41] [IMP] Improved form's datetime widget so the content is always visible even when view size is shrinked down bzr revid: fme@openerp.com-20110825094806-hfiagqp54gf9rzha --- addons/base/static/src/css/base.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index cd420ea8c0d..46364c4fa1d 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -870,6 +870,9 @@ label.error { .openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .button { height: 22px; } +.openerp .oe_forms input.field_datetime { + min-width: 11em; +} .openerp .oe_forms .button { color: #4c4c4c; white-space: nowrap; From 455928127fa2ed44a6f7fda14cab57052031c89f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 12:06:43 +0200 Subject: [PATCH 19/41] [FIX] safari/javascriptcore blows up on 'super' because it thinks it's a keyword bzr revid: xmo@openerp.com-20110825100643-k7nbcla8h38siozr --- addons/base/static/src/js/views.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index 7f6d78d9495..9e981e7c3e2 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -531,17 +531,17 @@ openerp.base.TranslateDialog = openerp.base.Dialog.extend({ }, open: function(field) { var self = this, - super = this._super; + sup = this._super; $.when(this.languages_loaded).then(function() { if (self.view.translatable_fields && self.view.translatable_fields.length) { self.do_load_fields_values(function() { - super.call(self); + sup.call(self); if (field) { // TODO: focus and scroll to field } }); } else { - super.call(self); + sup.call(self); } }); }, From 5864a23352e200eeab37b0b1e6b8c466e8261e5b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 12:27:56 +0200 Subject: [PATCH 20/41] [REM] leftover debugging code bzr revid: xmo@openerp.com-20110825102756-b3d8jf7yud0m98ol --- addons/base/static/src/js/list.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 9797b5dff99..e83454deebb 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -365,7 +365,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi reload_view: function (grouped, context, initial) { var self = this; var callback = function (field_view_get) { - console.log('loaded', initial ? 'initial' : 'subsequent'); self.on_loaded(field_view_get, grouped); }; if (this.embedded_view) { From 259892f837588c7b47c81cfe513d245e4526226f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 12:33:36 +0200 Subject: [PATCH 21/41] [FIX] selector for config overview widget: widget root has moved, .oe-dashboard-config-overview is now on this. so don't include it bzr revid: xmo@openerp.com-20110825103336-nwyybou1hghaudud --- addons/base_dashboard/static/src/js/dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_dashboard/static/src/js/dashboard.js b/addons/base_dashboard/static/src/js/dashboard.js index 8cf94a43362..e5b06a13461 100644 --- a/addons/base_dashboard/static/src/js/dashboard.js +++ b/addons/base_dashboard/static/src/js/dashboard.js @@ -321,7 +321,7 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ $progress.progressbar({value: $progress.data('completion')}); var self = this; - this.$element.find('div.oe-dashboard-config-overview ul') + this.$element.find('dl') .delegate('input', 'click', function (e) { // switch todo status e.stopImmediatePropagation(); From a5bb1a265b2eb1c217742db34330f28aa0fb78e8 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 25 Aug 2011 14:10:51 +0200 Subject: [PATCH 22/41] [fix] small bug with dates localization bzr revid: nicolas.vanhoren@openerp.com-20110825121051-x86pjeyfnnb65ucr --- addons/base/static/src/js/core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/base/static/src/js/core.js b/addons/base/static/src/js/core.js index fca993ffef9..57b3a8cb637 100644 --- a/addons/base/static/src/js/core.js +++ b/addons/base/static/src/js/core.js @@ -567,8 +567,8 @@ openerp.base.Session = openerp.base.CallbackEnabled.extend( /** @lends openerp.b self.do_load_js(file_list.concat(files)); }); } else { - self.do_load_css(file_list.concat(["/base/webclient/css?mods="+modules])); - self.do_load_js(["/base/webclient/js?mods="+modules]); + self.do_load_css(["/base/webclient/css?mods="+modules]); + self.do_load_js(file_list.concat(["/base/webclient/js?mods="+modules])); } openerp._modules_loaded = true; }); From 93aa853ae6c047d91e0958607fad73b1a4c5a271 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 25 Aug 2011 14:17:53 +0200 Subject: [PATCH 23/41] [imp] changed format/parse functions for dates to use date.js format instead of the openerp one bzr revid: nicolas.vanhoren@openerp.com-20110825121753-w3acs2fhbwm157a6 --- addons/base/static/src/js/formats.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/addons/base/static/src/js/formats.js b/addons/base/static/src/js/formats.js index c141aad908c..ed25b28c16d 100644 --- a/addons/base/static/src/js/formats.js +++ b/addons/base/static/src/js/formats.js @@ -45,8 +45,8 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_datetime(value); try { - return value.format(_.sprintf("%s %s", openerp.base._t.database.parameters.date_format, - openerp.base._t.database.parameters.time_format)); + return value.toString(_.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, + Date.CultureInfo.formatPatterns.longTime)); } catch (e) { return value.format("%m/%d/%Y %H:%M:%S"); } @@ -55,7 +55,7 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_date(value); try { - return value.format(openerp.base._t.database.parameters.date_format); + return value.toString(Date.CultureInfo.formatPatterns.shortDate); } catch (e) { return value.format("%m/%d/%Y"); } @@ -63,7 +63,7 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_time(value); try { - return value.format(openerp.base._t.database.parameters.time_format); + return value.toString(Date.CultureInfo.formatPatterns.longTime); } catch (e) { return value.format("%H:%M:%S"); } @@ -108,17 +108,27 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) { case 'progressbar': return openerp.base.parse_value(value, {type: "float"}); case 'datetime': - var tmp = Date.parse(value); + var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, + Date.CultureInfo.formatPatterns.longTime)); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid datetime"; case 'date': - var tmp = Date.parse(value); + var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.shortDate); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid date"; case 'time': - var tmp = Date.parse(value); + var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.longTime); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid time"; From b5a429d008536f6410e82b24ee12ec481b6d6397 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 14:30:49 +0200 Subject: [PATCH 24/41] [FIX] fnct_inv in client actions params, fix docstring for fnct_inv (fnct_inv only takes a single record id, not a sequence of ids) bzr revid: xmo@openerp.com-20110825123049-bmvebm86gqgr6w43 --- openerp/addons/base/ir/ir_actions.py | 5 ++--- openerp/osv/fields.py | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 0b60d389287..67c36b277dd 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -936,10 +936,9 @@ class act_client(osv.osv): for record in self.browse(cr, uid, ids, context=context) ]) - def _set_params(self, cr, uid, ids, field_name, field_value, arg, context): + def _set_params(self, cr, uid, id, field_name, field_value, arg, context): assert isinstance(field_value, dict), "params can only be dictionaries" - for record in self.browse(cr, uid, ids, context=context): - record.write({field_name: repr(field_value)}) + self.write(cr, uid, id, {'params_store': repr(field_value)}, context=context) _columns = { 'tag': fields.char('Client action tag', size=64, required=True, diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 782cc8e796a..fb973d2e521 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -852,12 +852,13 @@ class function(_column): This callable implements the write operation for the function field and must have the following signature: - .. function:: fnct_inv(model, cr, uid, ids, field_name, field_value, fnct_inv_arg, context) + .. function:: fnct_inv(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context) Callable that implements the ``write`` operation for the function field. :param orm_template model: model to which the field belongs (should be ``self`` for a model method) + :param int id: the identifier of the object to write on :param str field_name: name of the field to set :param fnct_inv_arg: arbitrary value passed when declaring the function field :return: True From b8cd28fb355de91b9ec736be4f42d77afef0a137 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 25 Aug 2011 14:34:45 +0200 Subject: [PATCH 25/41] [FIX] widgets client action handler: one action per widget bzr revid: xmo@openerp.com-20110825123445-ayhaeipasl4imvqt --- .../base_dashboard/static/src/js/dashboard.js | 61 +++++++++---------- .../static/src/xml/base_dashboard.xml | 9 ++- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/addons/base_dashboard/static/src/js/dashboard.js b/addons/base_dashboard/static/src/js/dashboard.js index e5b06a13461..3f9da51b157 100644 --- a/addons/base_dashboard/static/src/js/dashboard.js +++ b/addons/base_dashboard/static/src/js/dashboard.js @@ -372,39 +372,36 @@ openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({ } }); openerp.base.client_actions.add( - 'board.home.widgets', 'openerp.base_dashboard.Widgets'); -openerp.base_dashboard.Widgets = openerp.base.View.extend({ - template: 'HomeWidgets', - start: function () { - return new openerp.base.DataSetSearch( - this, 'res.widget.user', null, - ['|', ['user_id', '=', false], - ['user_id', '=', parseInt(this.session.uid, 10)]]) - .read_slice(['widget_id', 'user_id'], {}, this.on_widgets_list_loaded); + 'board.home.widgets', 'openerp.base_dashboard.Widget'); +openerp.base_dashboard.Widget = openerp.base.View.extend(/** @lends openerp.base_dashboard.Widgets# */{ + template: 'HomeWidget', + /** + * Initializes a "HomeWidget" client widget: handles the display of a given + * res.widget objects in an OpenERP view (mainly a dashboard). + * + * @constructs + * @extends openerp.base.View + * @param {Object} parent + * @param {Object} options + * @param {Number} options.widget_id + */ + init: function (parent, options) { + this._super(parent); + this.widget_id = options.widget_id; }, - on_widgets_list_loaded: function (user_widgets) { - var self = this; - var widget_user = {}; - _(user_widgets).each(function (widget) { - widget['widget_id'] = widget['widget_id'][0]; - widget_user[widget['widget_id']] = { - removable: widget['user_id'] !== false, - user_widget_id: widget['id'] - }; - }); - new openerp.base.DataSetSearch(this, 'res.widget') - .read_ids(_(user_widgets).pluck('widget_id'), ['title'], function (widgets) { - _(widgets).each(function (widget) { - _.extend(widget, widget_user[widget['id']]); - }); - var url = _.sprintf( - '/base_dashboard/widgets/content?session_id=%s&widget_id=', - self.session.session_id); - self.$element.html(QWeb.render('HomeWidgets', { - widgets: widgets, - url: url - })); - }); + start: function () { + return new openerp.base.DataSet(this, 'res.widget').read_ids( + [this.widget_id], ['title'], this.on_widget_loaded); + }, + on_widget_loaded: function (widgets) { + var widget = widgets[0]; + var url = _.sprintf( + '/base_dashboard/widgets/content?session_id=%s&widget_id=%d', + this.session.session_id, widget.id); + this.$element.html(QWeb.render('HomeWidget.content', { + widget: widget, + url: url + })); } }); }; diff --git a/addons/base_dashboard/static/src/xml/base_dashboard.xml b/addons/base_dashboard/static/src/xml/base_dashboard.xml index 51657dfd068..b60e466964b 100644 --- a/addons/base_dashboard/static/src/xml/base_dashboard.xml +++ b/addons/base_dashboard/static/src/xml/base_dashboard.xml @@ -107,10 +107,9 @@ -
    -
    +
    +

    -