diff options
| author | Christophe Simonis <chs@openerp.com> | 2013-06-13 19:39:00 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@openerp.com> | 2013-06-13 19:39:00 +0200 |
| commit | a61e7cb0c80ae773cab9753bd8a4e7e888bbf415 (patch) | |
| tree | 019a235869a5e087055700895d4584c80e2f9afb /openerp | |
| parent | 5ed0ac2614b9430e61dae472d657a21c037d6ee9 (diff) | |
| parent | 180e6e41d81e28df43b41e3cb6131225d4169a28 (diff) | |
[MERGE] forward port of branch saas-1 up to revid 4887 chs@openerp.com-20130612153934-qyp6pb3bc4za4taf
bzr revid: chs@openerp.com-20130613172414-dln3g4j0ykcwaa02
bzr revid: chs@openerp.com-20130613173900-xl7rh321nnw2b04b
Diffstat (limited to 'openerp')
| -rw-r--r-- | openerp/__init__.py | 10 | ||||
| -rw-r--r-- | openerp/addons/base/base_data.xml | 6 | ||||
| -rw-r--r-- | openerp/addons/base/currency_data.xml | 1 | ||||
| -rw-r--r-- | openerp/addons/base/res/res_bank_view.xml | 20 | ||||
| -rw-r--r-- | openerp/addons/base/res/res_company.py | 4 | ||||
| -rw-r--r-- | openerp/addons/base/res/res_partner.py | 5 | ||||
| -rw-r--r-- | openerp/addons/base/res/res_partner_view.xml | 9 | ||||
| -rw-r--r-- | openerp/addons/base/res/res_users.py | 2 | ||||
| -rw-r--r-- | openerp/addons/base/test/test_osv_expression.yml | 5 | ||||
| -rw-r--r-- | openerp/cli/server.py | 22 | ||||
| -rw-r--r-- | openerp/report/render/rml2pdf/trml2pdf.py | 15 | ||||
| -rw-r--r-- | openerp/service/__init__.py | 7 | ||||
| -rw-r--r-- | openerp/service/workers.py | 69 | ||||
| -rw-r--r-- | openerp/service/wsgi_server.py | 5 | ||||
| -rw-r--r-- | openerp/tools/misc.py | 22 |
15 files changed, 123 insertions, 79 deletions
diff --git a/openerp/__init__.py b/openerp/__init__.py index 285c6af85d9..22c8c98256a 100644 --- a/openerp/__init__.py +++ b/openerp/__init__.py @@ -23,6 +23,14 @@ """ +import sys + +# Is the server running with gevent. +evented = False +if sys.modules.get("gevent") is not None: + evented = True + + # Make sure the OpenERP server runs in UTC. This is especially necessary # under Windows as under Linux it seems the real import of time is # sufficiently deferred so that setting the TZ environment variable @@ -61,8 +69,6 @@ wsgi.register_wsgi_handler = wsgi.wsgi_server.register_wsgi_handler # its own copy of the data structure and we don't need to care about # locks between threads. multi_process = False -# Is the server running with gevent. -evented = False def registry(database_name): """ diff --git a/openerp/addons/base/base_data.xml b/openerp/addons/base/base_data.xml index 2922dd82dc4..6a47325db39 100644 --- a/openerp/addons/base/base_data.xml +++ b/openerp/addons/base/base_data.xml @@ -49,8 +49,6 @@ <field name="symbol">€</field> <field name="rounding">0.01</field> <field name="accuracy">4</field> - <!-- Company ID will be set later --> - <field name="company_id" eval="None"/> </record> <record id="rateEUR" model="res.currency.rate"> <field name="rate">1.0</field> @@ -86,10 +84,6 @@ Administrator</field> <field name="company_id" ref="main_company"/> </record> - <record id="EUR" model="res.currency"> - <field name="company_id" ref="main_company"/> - </record> - <record id="ir_mail_server_localhost0" model="ir.mail_server"> <field name="name">localhost</field> <field name="smtp_host">localhost</field> diff --git a/openerp/addons/base/currency_data.xml b/openerp/addons/base/currency_data.xml index 96ff81cc0d1..e38f777d584 100644 --- a/openerp/addons/base/currency_data.xml +++ b/openerp/addons/base/currency_data.xml @@ -466,7 +466,6 @@ <field name="rounding">0.01</field> <field name="accuracy">4</field> <field name="symbol">¢</field> - <field name="company_id" ref="main_company"/> </record> <record id="rateCRC" model="res.currency.rate"> <field name="rate">691.3153</field> diff --git a/openerp/addons/base/res/res_bank_view.xml b/openerp/addons/base/res/res_bank_view.xml index 1c289d07141..7c1e07e37fb 100644 --- a/openerp/addons/base/res/res_bank_view.xml +++ b/openerp/addons/base/res/res_bank_view.xml @@ -53,7 +53,13 @@ <field name="res_model">res.bank</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> - <field name="help">Manage bank records you want to be used in the system.</field> + <field name="help"> + <p class="oe_view_nocontent_create"> + Click to create a new bank. + </p><p> + Manage bank records you want to be used in the system. + </p> + </field> </record> <menuitem action="action_res_bank_form" id="menu_action_res_bank_form" parent="base.menu_config_address_book" sequence="11" groups="base.group_no_one"/> @@ -152,7 +158,17 @@ <field name="res_model">res.partner.bank</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> - <field name="help">Configure your company's bank accounts and select those that must appear on the report footer. You can reorder bank accounts from the list view. If you use the accounting application of OpenERP, journals and accounts will be created automatically based on these data.</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a bank account. + </p><p> + Configure your company's bank accounts and select those that must appear on the report footer. + You can reorder bank accounts from the list view. + </p> + <p> + If you use the accounting application of OpenERP, journals and accounts will be created automatically based on these data. + </p> + </field> </record> <menuitem action="action_res_partner_bank_account_form" id="menu_action_res_partner_bank_form" diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py index 02dfefe1e38..03fcb133d9f 100644 --- a/openerp/addons/base/res/res_company.py +++ b/openerp/addons/base/res/res_company.py @@ -280,7 +280,7 @@ class res_company(osv.osv): <frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/> <stylesheet> <!-- Set here the default font to use for all <para> tags --> - <parastyle name='Normal' fontName="DejaVu Sans"/> + <paraStyle name='Normal' fontName="DejaVu Sans"/> </stylesheet> <pageGraphics> <fill color="black"/> @@ -318,7 +318,7 @@ class res_company(osv.osv): <frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/> <stylesheet> <!-- Set here the default font to use for all <para> tags --> - <parastyle name='Normal' fontName="DejaVu Sans"/> + <paraStyle name='Normal' fontName="DejaVu Sans"/> <paraStyle name="main_footer" fontSize="8.0" alignment="CENTER"/> <paraStyle name="main_header" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> </stylesheet> diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index 89b9448290e..566ee6066dd 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -608,9 +608,8 @@ class res_partner(osv.osv, format_address): query_args['limit'] = limit cr.execute('''SELECT partner.id FROM res_partner partner LEFT JOIN res_partner company ON partner.parent_id = company.id - WHERE partner.email ''' + operator +''' %(name)s - OR partner.name || ' (' || COALESCE(company.name,'') || ')' - ''' + operator + ' %(name)s ' + limit_str, query_args) + WHERE partner.email ''' + operator +''' %(name)s OR + partner.display_name ''' + operator + ' %(name)s ' + limit_str, query_args) ids = map(lambda x: x[0], cr.fetchall()) ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context) if ids: diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml index ccf74206967..2e7b6f0c14d 100644 --- a/openerp/addons/base/res/res_partner_view.xml +++ b/openerp/addons/base/res/res_partner_view.xml @@ -580,7 +580,14 @@ <field name="type">ir.actions.act_window</field> <field name="res_model">res.partner.category</field> <field name="view_type">form</field> - <field name="help">Manage the partner tags to better classify them for tracking and analysis purposes. A partner may have several tags and tags have a hierarchical structure: a partner with a tag has also the parent tags.</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a new partner tags. + </p><p> + Manage the partner tags to better classify them for tracking and analysis purposes. + A partner may have several tags and tags have a hierarchical structure: a partner with a tag has also the parent tags. + </p> + </field> </record> <menuitem action="action_partner_category_form" id="menu_partner_category_form" name="Partner Tags" sequence="4" parent="menu_config_address_book" groups="base.group_no_one"/> diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index 72d9269487b..93d6d307484 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -845,6 +845,7 @@ class users_view(osv.osv): 'string': app and app.name or _('Other'), 'selection': [(False, '')] + [(g.id, g.name) for g in gs], 'help': '\n'.join(tips), + 'exportable': False, } else: # boolean group fields @@ -853,6 +854,7 @@ class users_view(osv.osv): 'type': 'boolean', 'string': g.name, 'help': g.comment, + 'exportable': False, } return res diff --git a/openerp/addons/base/test/test_osv_expression.yml b/openerp/addons/base/test/test_osv_expression.yml index 1516f090e94..111ce40f2ce 100644 --- a/openerp/addons/base/test/test_osv_expression.yml +++ b/openerp/addons/base/test/test_osv_expression.yml @@ -375,6 +375,11 @@ currency_ids3 = self.pool.get('res.currency').search(cr, uid, [('id', 'not in', [])]) assert currency_ids1 == currency_ids2 == currency_ids3, 'All 3 results should have be the same: all currencies' default_company = self.browse(cr, uid, 1) + + # Due to currency data definition change (in relation with bug 1111298), this test now needs + # a manual setup where all currencies are assigned to the default company. + self.pool['res.currency'].write(cr, uid, currency_ids1, {'company_id': default_company.id}) + # one2many towards same model res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child diff --git a/openerp/cli/server.py b/openerp/cli/server.py index 2ee9e53b3ca..fc1bf61b617 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -221,6 +221,18 @@ def quit_on_signals(): os.unlink(config['pidfile']) sys.exit(0) +def watch_parent(beat=4): + import gevent + ppid = os.getppid() + while True: + if ppid != os.getppid(): + pid = os.getpid() + _logger.info("LongPolling (%s) Parent changed", pid) + # suicide !! + os.kill(pid, signal.SIGTERM) + return + gevent.sleep(beat) + def main(args): check_root_user() openerp.tools.config.parse_config(args) @@ -257,9 +269,15 @@ def main(args): setup_pid_file() # Some module register themselves when they are loaded so we need the # services to be running before loading any registry. - if config['workers']: - openerp.service.start_services_workers() + if not openerp.evented: + if config['workers']: + openerp.service.start_services_workers() + else: + openerp.service.start_services() else: + config['xmlrpc_port'] = config['longpolling_port'] + import gevent + gevent.spawn(watch_parent) openerp.service.start_services() rc = 0 diff --git a/openerp/report/render/rml2pdf/trml2pdf.py b/openerp/report/render/rml2pdf/trml2pdf.py index 243cc02a0b3..2052b128ad9 100644 --- a/openerp/report/render/rml2pdf/trml2pdf.py +++ b/openerp/report/render/rml2pdf/trml2pdf.py @@ -160,7 +160,7 @@ class _rml_styles(object,): for style in node.findall('paraStyle'): sname = style.get('name') self.styles[sname] = self._para_style_update(style) - if sname in self.default_style: + if self.default_style.has_key(sname): for key, value in self.styles[sname].items(): setattr(self.default_style[sname], key, value) else: @@ -277,11 +277,24 @@ class _rml_doc(object): fname = font.get('fontFile').encode('ascii') if name not in pdfmetrics._fonts: pdfmetrics.registerFont(TTFont(name, fname)) + #by default, we map the fontName to each style (bold, italic, bold and italic), so that + #if there isn't any font defined for one of these style (via a font family), the system + #will fallback on the normal font. addMapping(name, 0, 0, name) #normal addMapping(name, 0, 1, name) #italic addMapping(name, 1, 0, name) #bold addMapping(name, 1, 1, name) #italic and bold + #if registerFontFamily is defined, we register the mapping of the fontName to use for each style. + for font_family in node.findall('registerFontFamily'): + family_name = font_family.get('normal').encode('ascii') + if font_family.get('italic'): + addMapping(family_name, 0, 1, font_family.get('italic').encode('ascii')) + if font_family.get('bold'): + addMapping(family_name, 1, 0, font_family.get('bold').encode('ascii')) + if font_family.get('boldItalic'): + addMapping(family_name, 1, 1, font_family.get('boldItalic').encode('ascii')) + def setTTFontMapping(self,face, fontname, filename, mode='all'): from reportlab.lib.fonts import addMapping from reportlab.pdfbase import pdfmetrics diff --git a/openerp/service/__init__.py b/openerp/service/__init__.py index d66904615ba..2c6c80eb80c 100644 --- a/openerp/service/__init__.py +++ b/openerp/service/__init__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). -# Copyright (C) 2010-2012 OpenERP SA (<http://www.openerp.com>) +# Copyright (C) 2010-2013 OpenERP SA (<http://www.openerp.com>) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -37,6 +37,7 @@ import openerp.netsvc import openerp.osv from openerp.release import nt_service_name import openerp.tools +from openerp.tools.misc import stripped_sys_argv import common import db @@ -128,9 +129,7 @@ def _reexec(): if openerp.tools.osutil.is_running_as_nt_service(): subprocess.call('net stop {0} && net start {0}'.format(nt_service_name), shell=True) exe = os.path.basename(sys.executable) - strip_args = ['-d', '-u'] - a = sys.argv[:] - args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args] + args = stripped_sys_argv() if not args or args[0] != exe: args.insert(0, exe) os.execv(sys.executable, args) diff --git a/openerp/service/workers.py b/openerp/service/workers.py index 9a6485b8fd3..1075342919d 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -14,6 +14,8 @@ import signal import socket import sys import time +import subprocess +import os.path import werkzeug.serving try: @@ -23,6 +25,7 @@ except ImportError: import openerp import openerp.tools.config as config +from openerp.tools.misc import stripped_sys_argv _logger = logging.getLogger(__name__) @@ -35,7 +38,6 @@ class Multicorn(object): def __init__(self, app): # config self.address = (config['xmlrpc_interface'] or '0.0.0.0', config['xmlrpc_port']) - self.long_polling_address = (config['xmlrpc_interface'] or '0.0.0.0', config['longpolling_port']) self.population = config['workers'] self.timeout = config['limit_time_real'] self.limit_request = config['limit_request'] @@ -46,10 +48,10 @@ class Multicorn(object): self.socket = None self.workers_http = {} self.workers_cron = {} - self.workers_longpolling = {} self.workers = {} self.generation = 0 self.queue = [] + self.long_polling_pid = None def pipe_new(self): pipe = os.pipe() @@ -89,6 +91,14 @@ class Multicorn(object): worker.run() sys.exit(0) + def long_polling_spawn(self): + nargs = stripped_sys_argv('--pidfile') + cmd = nargs[0] + cmd = os.path.join(os.path.dirname(cmd), "openerp-long-polling") + nargs[0] = cmd + popen = subprocess.Popen(nargs) + self.long_polling_pid = popen.pid + def worker_pop(self, pid): if pid in self.workers: _logger.debug("Worker (%s) unregistered",pid) @@ -143,8 +153,8 @@ class Multicorn(object): self.worker_spawn(WorkerHTTP, self.workers_http) while len(self.workers_cron) < config['max_cron_threads']: self.worker_spawn(WorkerCron, self.workers_cron) - while len(self.workers_longpolling) < 1: - self.worker_spawn(WorkerLongPolling, self.workers_longpolling) + if not self.long_polling_pid: + self.long_polling_spawn() def sleep(self): try: @@ -183,14 +193,11 @@ class Multicorn(object): self.socket.setblocking(0) self.socket.bind(self.address) self.socket.listen(8*self.population) - # long polling socket - self.long_polling_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.long_polling_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.long_polling_socket.setblocking(0) - self.long_polling_socket.bind(self.long_polling_address) - self.long_polling_socket.listen(8) def stop(self, graceful=True): + if self.long_polling_pid is not None: + self.worker_kill(self.long_polling_pid, signal.SIGTERM) + self.long_polling_pid = None if graceful: _logger.info("Stopping gracefully") limit = time.time() + self.timeout @@ -353,49 +360,8 @@ class WorkerHTTP(Worker): def start(self): Worker.start(self) - self.multi.long_polling_socket.close() self.server = WorkerBaseWSGIServer(self.multi.app) -class WorkerLongPolling(Worker): - """ Long polling workers """ - def __init__(self, multi): - super(WorkerLongPolling, self).__init__(multi) - # Disable the watchdog feature for this kind of worker. - self.watchdog_timeout = None - - def watch_parent(self): - import gevent - while True: - if self.ppid != os.getppid(): - _logger.info("WorkerLongPolling (%s) Parent changed", self.pid) - os.kill(os.getpid(), signal.SIGTERM) - return - gevent.sleep(self.multi.beat) - - def start(self): - openerp.evented = True - _logger.info('Using gevent mode') - import gevent.monkey - gevent.monkey.patch_all() - import gevent_psycopg2 - gevent_psycopg2.monkey_patch() - from openerp.modules.registry import RegistryManager - from gevent.coros import RLock - RegistryManager.registries_lock = RLock() - - Worker.start(self) - self.multi.socket.close() - - import gevent - watcher = gevent.spawn(self.watch_parent) - - log = _logger.getChild(self.__class__.__name__) - log.write = lambda msg: log.info(msg.strip()) - - from gevent.wsgi import WSGIServer - self.server = WSGIServer(self.multi.long_polling_socket, self.multi.app, log=log) - self.server.serve_forever() - class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer): """ werkzeug WSGI Server patched to allow using an external listen socket """ @@ -469,7 +435,6 @@ class WorkerCron(Worker): def start(self): Worker.start(self) self.multi.socket.close() - self.multi.long_polling_socket.close() openerp.service.start_internal() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/service/wsgi_server.py b/openerp/service/wsgi_server.py index df201825d31..acbfc67dd0b 100644 --- a/openerp/service/wsgi_server.py +++ b/openerp/service/wsgi_server.py @@ -449,7 +449,10 @@ def start_service(): interface = config['xmlrpc_interface'] or '0.0.0.0' port = config['xmlrpc_port'] _logger.info('HTTP service (werkzeug) running on %s:%s', interface, port) - threading.Thread(target=serve, args=(interface, port, True)).start() + if not openerp.evented: + threading.Thread(target=serve, args=(interface, port, True)).start() + else: + serve(interface, port, True) def stop_service(): """ Initiate the shutdown of the WSGI server. diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 1b476fb9b2a..7a54d6b9b6e 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). -# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>). +# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -37,7 +37,7 @@ import time import zipfile from collections import defaultdict from datetime import datetime -from itertools import islice, izip +from itertools import islice, izip, groupby from lxml import etree from which import which from threading import local @@ -1059,4 +1059,22 @@ class CountingStream(object): raise StopIteration() return val +def stripped_sys_argv(*strip_args): + """Return sys.argv with some arguments stripped, suitable for reexecution or subprocesses""" + strip_args = sorted(set(strip_args) | set(['-s', '--save', '-d', '--database', '-u', '--update', '-i', '--init'])) + assert all(config.parser.has_option(s) for s in strip_args) + takes_value = dict((s, config.parser.get_option(s).takes_value()) for s in strip_args) + + longs, shorts = list(tuple(y) for _, y in groupby(strip_args, lambda x: x.startswith('--'))) + longs_eq = tuple(l + '=' for l in longs if takes_value[l]) + + args = sys.argv[:] + + def strip(args, i): + return args[i].startswith(shorts) \ + or args[i].startswith(longs_eq) or (args[i] in longs) \ + or (i >= 1 and (args[i - 1] in strip_args) and takes_value[args[i - 1]]) + + return [x for i, x in enumerate(args) if not strip(args, i)] + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
