From 12580e690aea8b9ea3a4862e547cf30e323c683a Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 8 Sep 2013 19:35:35 +0200 Subject: [PATCH 01/19] [IMP] openerp threaded, gevent, prefork service cleanup - unify signal handling - unify start and stop no new feature yet, it paves the way for - developement mode auto reload - graceful restart on HUP - multiprocessing and gevent on windows bzr revid: al@openerp.com-20130908173535-xomt5w7xmqtwkmyy --- openerp-long-polling | 11 -- openerp/__init__.py | 11 +- openerp/cli/server.py | 120 +------------ openerp/service/__init__.py | 116 +------------ openerp/service/cron.py | 76 -------- openerp/service/workers.py | 309 +++++++++++++++++++++++++++++++-- openerp/service/wsgi_server.py | 66 ------- 7 files changed, 317 insertions(+), 392 deletions(-) delete mode 100755 openerp-long-polling delete mode 100644 openerp/service/cron.py diff --git a/openerp-long-polling b/openerp-long-polling deleted file mode 100755 index 304cd01c1ee..00000000000 --- a/openerp-long-polling +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import gevent.monkey -gevent.monkey.patch_all() -import gevent_psycopg2 -gevent_psycopg2.monkey_patch() - -import openerp - -if __name__ == "__main__": - openerp.cli.main() diff --git a/openerp/__init__.py b/openerp/__init__.py index 22c8c98256a..161b06b0990 100644 --- a/openerp/__init__.py +++ b/openerp/__init__.py @@ -27,9 +27,16 @@ import sys # Is the server running with gevent. evented = False -if sys.modules.get("gevent") is not None: - evented = True +for i in sys.argv: + if i.startswith('--gevent'): + evented = True + break +if evented: + import gevent.monkey + gevent.monkey.patch_all() + import gevent_psycopg2 + gevent_psycopg2.monkey_patch() # 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 diff --git a/openerp/cli/server.py b/openerp/cli/server.py index fc1bf61b617..0c081b6156b 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -147,112 +147,15 @@ def import_translation(): cr.commit() cr.close() -# Variable keeping track of the number of calls to the signal handler defined -# below. This variable is monitored by ``quit_on_signals()``. -quit_signals_received = 0 - -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(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 - threads_info = dict([(th.ident, {'name': th.name, - 'uid': getattr(th,'uid','n/a')}) - for th in threading.enumerate()]) - code = [] - for threadId, stack in sys._current_frames().items(): - thread_info = threads_info.get(threadId) - code.append("\n# Thread: %s (id:%s) (uid:%s)" % \ - (thread_info and thread_info['name'] or 'n/a', - threadId, - thread_info and thread_info['uid'] or 'n/a')) - for filename, lineno, name, line in traceback.extract_stack(stack): - code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) - if line: - code.append(" %s" % (line.strip())) - _logger.info("\n".join(code)) - -def setup_signal_handlers(signal_handler): - """ Register the given signal handler. """ - SIGNALS = (signal.SIGINT, signal.SIGTERM) - if os.name == 'posix': - map(lambda sig: signal.signal(sig, signal_handler), SIGNALS) - signal.signal(signal.SIGQUIT, dumpstacks) - elif os.name == 'nt': - import win32api - win32api.SetConsoleCtrlHandler(lambda sig: signal_handler(sig, None), 1) - -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.) The try/except is for the win32 case. - try: - while quit_signals_received == 0: - time.sleep(60) - except KeyboardInterrupt: - pass - - config = openerp.tools.config - openerp.service.stop_services() - - if getattr(openerp, 'phoenix', False): - # like the phoenix, reborn from ashes... - openerp.service._reexec() - return - - if config['pidfile']: - 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) - - if openerp.tools.config.options["gevent"]: - openerp.evented = True - _logger.info('Using gevent mode') - import gevent.monkey - gevent.monkey.patch_all() - import gevent_psycopg2 - gevent_psycopg2.monkey_patch() - check_postgres_user() openerp.netsvc.init_logger() report_configuration() config = openerp.tools.config - setup_signal_handlers(signal_handler) - if config["test_file"]: run_test_file(config['db_name'], config['test_file']) sys.exit(0) @@ -265,28 +168,19 @@ def main(args): import_translation() sys.exit(0) - if not config["stop_after_init"]: - setup_pid_file() - # Some module register themselves when they are loaded so we need the - # services to be running before loading any registry. - 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() - + # preload registryies, needed for -u --stop_after_init rc = 0 if config['db_name']: for dbname in config['db_name'].split(','): if not preload_registry(dbname): rc += 1 - if config["stop_after_init"]: + if not config["stop_after_init"]: + setup_pid_file() + openerp.service.workers.start() + if config['pidfile']: + os.unlink(config['pidfile']) + else: sys.exit(rc) _logger.info('OpenERP server is running, waiting for connections...') diff --git a/openerp/service/__init__.py b/openerp/service/__init__.py index 2c6c80eb80c..02401d455e1 100644 --- a/openerp/service/__init__.py +++ b/openerp/service/__init__.py @@ -20,29 +20,12 @@ # ############################################################################## -import logging -import os -import signal -import subprocess -import sys -import threading -import time - -import cron -import wsgi_server - -import openerp -import openerp.modules -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 import model import report +import wsgi_server +import workers #.apidoc title: RPC Services @@ -55,100 +38,5 @@ import report low-level behavior of the wire. """ -_logger = logging.getLogger(__name__) - -def load_server_wide_modules(): - for m in openerp.conf.server_wide_modules: - try: - openerp.modules.module.load_openerp_module(m) - except Exception: - msg = '' - if m == 'web': - msg = """ -The `web` module is provided by the addons found in the `openerp-web` project. -Maybe you forgot to add those addons in your addons_path configuration.""" - _logger.exception('Failed to load server-wide module `%s`.%s', m, msg) - -start_internal_done = False -main_thread_id = threading.currentThread().ident - -def start_internal(): - global start_internal_done - if start_internal_done: - return - openerp.netsvc.init_logger() - - load_server_wide_modules() - start_internal_done = True - -def start_services(): - """ Start all services including http, and cron """ - start_internal() - # Start the WSGI server. - wsgi_server.start_service() - # Start the main cron thread. - if not openerp.evented: - cron.start_service() - -def stop_services(): - """ Stop all services. """ - # stop services - if not openerp.evented: - cron.stop_service() - wsgi_server.stop_service() - - _logger.info("Initiating shutdown") - _logger.info("Hit CTRL-C again or send a second signal to force the shutdown.") - - # Manually join() all threads before calling sys.exit() to allow a second signal - # to trigger _force_quit() in case some non-daemon threads won't exit cleanly. - # threading.Thread.join() should not mask signals (at least in python 2.5). - me = threading.currentThread() - _logger.debug('current thread: %r', me) - for thread in threading.enumerate(): - _logger.debug('process %r (%r)', thread, thread.isDaemon()) - if thread != me and not thread.isDaemon() and thread.ident != main_thread_id: - while thread.isAlive(): - _logger.debug('join and sleep') - # Need a busyloop here as thread.join() masks signals - # and would prevent the forced shutdown. - thread.join(0.05) - time.sleep(0.05) - - _logger.debug('--') - openerp.modules.registry.RegistryManager.delete_all() - logging.shutdown() - -def start_services_workers(): - import openerp.service.workers - openerp.multi_process = True - openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run() - -def _reexec(): - """reexecute openerp-server process with (nearly) the same arguments""" - 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) - args = stripped_sys_argv() - if not args or args[0] != exe: - args.insert(0, exe) - os.execv(sys.executable, args) - -def restart_server(): - if openerp.multi_process: - raise NotImplementedError("Multicorn is not supported (but gunicorn was)") - pid = openerp.wsgi.core.arbiter_pid - os.kill(pid, signal.SIGHUP) - else: - if os.name == 'nt': - def reborn(): - stop_services() - _reexec() - - # run in a thread to let the current thread return response to the caller. - threading.Thread(target=reborn).start() - else: - openerp.phoenix = True - os.kill(os.getpid(), signal.SIGINT) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/service/cron.py b/openerp/service/cron.py deleted file mode 100644 index f21b27f7fea..00000000000 --- a/openerp/service/cron.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2011 OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -""" Cron jobs scheduling - -Cron jobs are defined in the ir_cron table/model. This module deals with all -cron jobs, for all databases of a single OpenERP server instance. - -""" - -import logging -import threading -import time -from datetime import datetime - -import openerp - -_logger = logging.getLogger(__name__) - -SLEEP_INTERVAL = 60 # 1 min - -def cron_runner(number): - while True: - time.sleep(SLEEP_INTERVAL + number) # Steve Reich timing style - registries = openerp.modules.registry.RegistryManager.registries - _logger.debug('cron%d polling for jobs', number) - for db_name, registry in registries.items(): - while True and registry.ready: - acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name) - if not acquired: - break - -def start_service(): - """ Start the above runner function in a daemon thread. - - The thread is a typical daemon thread: it will never quit and must be - terminated when the main process exits - with no consequence (the processing - threads it spawns are not marked daemon). - - """ - - # Force call to strptime just before starting the cron thread - # to prevent time.strptime AttributeError within the thread. - # See: http://bugs.python.org/issue7980 - datetime.strptime('2012-01-01', '%Y-%m-%d') - - for i in range(openerp.tools.config['max_cron_threads']): - def target(): - cron_runner(i) - t = threading.Thread(target=target, name="openerp.service.cron.cron%d" % i) - t.setDaemon(True) - t.start() - _logger.debug("cron%d started!" % i) - -def stop_service(): - pass - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/service/workers.py b/openerp/service/workers.py index c8b8b84bca2..382d581720f 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -1,7 +1,7 @@ #----------------------------------------------------------- -# Multicorn, multiprocessing inspired by gunicorn -# TODO rename class: Multicorn -> Arbiter ? +# Threaded, Gevent and Prefork Servers #----------------------------------------------------------- +import datetime import errno import fcntl import logging @@ -13,10 +13,14 @@ import select import signal import socket import sys +import threading import time +import traceback import subprocess import os.path +import wsgi_server + import werkzeug.serving try: from setproctitle import setproctitle @@ -25,11 +29,238 @@ except ImportError: import openerp import openerp.tools.config as config +from openerp.release import nt_service_name from openerp.tools.misc import stripped_sys_argv _logger = logging.getLogger(__name__) -class Multicorn(object): +SLEEP_INTERVAL = 60 # 1 min + +#---------------------------------------------------------- +# Common +#---------------------------------------------------------- + +class CommonServer(object): + def __init__(self, app): + # TODO Change the xmlrpc_* options to http_* + self.app = app + # config + self.interface = config['xmlrpc_interface'] or '0.0.0.0' + self.port = config['xmlrpc_port'] + # runtime + self.pid = os.getpid() + + def dumpstacks(self): + """ 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 + threads_info = dict([(th.ident, {'name': th.name, + 'uid': getattr(th,'uid','n/a')}) + for th in threading.enumerate()]) + code = [] + for threadId, stack in sys._current_frames().items(): + thread_info = threads_info.get(threadId) + code.append("\n# Thread: %s (id:%s) (uid:%s)" % \ + (thread_info and thread_info['name'] or 'n/a', + threadId, + thread_info and thread_info['uid'] or 'n/a')) + for filename, lineno, name, line in traceback.extract_stack(stack): + code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) + if line: + code.append(" %s" % (line.strip())) + _logger.info("\n".join(code)) + + def close_socket(self, sock): + """ Closes a socket instance cleanly + :param sock: the network socket to close + :type sock: socket.socket + """ + try: + sock.shutdown(socket.SHUT_RDWR) + except socket.error, e: + # On OSX, socket shutdowns both sides if any side closes it + # causing an error 57 'Socket is not connected' on shutdown + # of the other side (or something), see + # http://bugs.python.org/issue4397 + # note: stdlib fixed test, not behavior + if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']: + raise + sock.close() + +#---------------------------------------------------------- +# Threaded +#---------------------------------------------------------- + +class ThreadedServer(CommonServer): + def __init__(self, app): + super(ThreadedServer, self).__init__(app) + self.main_thread_id = threading.currentThread().ident + # Variable keeping track of the number of calls to the signal handler defined + # below. This variable is monitored by ``quit_on_signals()``. + self.quit_signals_received = 0 + + #self.socket = None + #self.queue = [] + self.httpd = None + + def signal_handler(self, sig, frame): + if sig in [signal.SIGINT,signal.SIGTERM]: + # shutdown on kill -INT or -TERM + self.quit_signals_received += 1 + if self.quit_signals_received > 1: + # logging.shutdown was already called at this point. + sys.stderr.write("Forced shutdown.\n") + os._exit(0) + elif sig == signal.SIGHUP: + # restart on kill -HUP + openerp.phoenix = True + self.quit_signals_received += 1 + elif sig == signal.SIGQUIT: + # dump stacks on kill -3 + self.dumpstacks() + + def cron_thread(self, number): + while True: + time.sleep(SLEEP_INTERVAL + number) # Steve Reich timing style + registries = openerp.modules.registry.RegistryManager.registries + _logger.debug('cron%d polling for jobs', number) + for db_name, registry in registries.items(): + while True and registry.ready: + acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name) + if not acquired: + break + + def cron_spawn(self): + """ Start the above runner function in a daemon thread. + + The thread is a typical daemon thread: it will never quit and must be + terminated when the main process exits - with no consequence (the processing + threads it spawns are not marked daemon). + + """ + # Force call to strptime just before starting the cron thread + # to prevent time.strptime AttributeError within the thread. + # See: http://bugs.python.org/issue7980 + datetime.datetime.strptime('2012-01-01', '%Y-%m-%d') + for i in range(openerp.tools.config['max_cron_threads']): + def target(): + self.cron_thread(i) + t = threading.Thread(target=target, name="openerp.service.cron.cron%d" % i) + t.setDaemon(True) + t.start() + _logger.debug("cron%d started!" % i) + + def http_thread(self): + self.httpd = werkzeug.serving.make_server(self.interface, self.port, self.app, threaded=True) + self.httpd.serve_forever() + + def http_spawn(self): + threading.Thread(target=self.http_thread).start() + _logger.info('HTTP service (werkzeug) running on %s:%s', self.interface, self.port) + + def start(self): + _logger.debug("Setting signal handlers") + if os.name == 'posix': + signal.signal(signal.SIGINT, self.signal_handler) + signal.signal(signal.SIGTERM, self.signal_handler) + signal.signal(signal.SIGCHLD, self.signal_handler) + signal.signal(signal.SIGHUP, self.signal_handler) + signal.signal(signal.SIGQUIT, self.signal_handler) + elif os.name == 'nt': + import win32api + win32api.SetConsoleCtrlHandler(lambda sig: signal_handler(sig, None), 1) + self.cron_spawn() + self.http_spawn() + + def stop(self): + """ Shutdown the WSGI server. Wait for non deamon threads. + """ + _logger.info("Initiating shutdown") + _logger.info("Hit CTRL-C again or send a second signal to force the shutdown.") + + self.httpd.shutdown() + self.close_socket(self.httpd.socket) + + # Manually join() all threads before calling sys.exit() to allow a second signal + # to trigger _force_quit() in case some non-daemon threads won't exit cleanly. + # threading.Thread.join() should not mask signals (at least in python 2.5). + me = threading.currentThread() + _logger.debug('current thread: %r', me) + for thread in threading.enumerate(): + _logger.debug('process %r (%r)', thread, thread.isDaemon()) + if thread != me and not thread.isDaemon() and thread.ident != main_thread_id: + while thread.isAlive(): + _logger.debug('join and sleep') + # Need a busyloop here as thread.join() masks signals + # and would prevent the forced shutdown. + thread.join(0.05) + time.sleep(0.05) + + _logger.debug('--') + openerp.modules.registry.RegistryManager.delete_all() + logging.shutdown() + + def run(self): + """ Start the http server and the cron thread then wait for a signal. + + The first SIGINT or SIGTERM signal will initiate a graceful shutdown while + a second one if any will force an immediate exit. + """ + self.start() + + # Wait for a first signal to be handled. (time.sleep will be interrupted + # by the signal handler.) The try/except is for the win32 case. + try: + while self.quit_signals_received == 0: + time.sleep(60) + except KeyboardInterrupt: + pass + + self.stop() + +#---------------------------------------------------------- +# Gevent +#---------------------------------------------------------- + +class GeventServer(CommonServer): + def __init__(self, app): + super(GeventServer, self).__init__(app) + self.port = config['longpolling_port'] + self.httpd = None + + def watch_parent(self, 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 start(self): + import gevent + from gevent.wsgi import WSGIServer + gevent.spawn(self.watch_parent) + self.httpd = WSGIServer((self.interface, self.port), self.app) + self.httpd.serve_forever() + + def stop(self): + import gevent + self.httpd.stop() + gevent.shutdown() + + def run(self): + self.start() + self.stop() + +#---------------------------------------------------------- +# Prefork +#---------------------------------------------------------- + +class Multicorn(CommonServer): """ Multiprocessing inspired by (g)unicorn. Multicorn currently uses accept(2) as dispatching method between workers but we plan to replace it by a more intelligent dispatcher to will parse @@ -92,10 +323,8 @@ class Multicorn(object): 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 + nargs = stripped_sys_argv('--pidfile','--workers') + nargs += ['--gevent'] popen = subprocess.Popen(nargs) self.long_polling_pid = popen.pid @@ -122,6 +351,13 @@ class Multicorn(object): sig = self.queue.pop(0) if sig in [signal.SIGINT,signal.SIGTERM]: raise KeyboardInterrupt + elif sig == signal.SIGHUP: + # restart on kill -HUP + openerp.phoenix = True + raise KeyboardInterrupt + elif sig == signal.SIGQUIT: + # dump stacks on kill -3 + self.dumpstacks() def process_zombie(self): # reap dead workers @@ -211,7 +447,6 @@ class Multicorn(object): for pid in self.workers.keys(): self.worker_kill(pid, signal.SIGTERM) self.socket.close() - openerp.cli.server.quit_signals_received = 1 def run(self): self.start() @@ -389,7 +624,7 @@ class WorkerCron(Worker): def sleep(self): # Really sleep once all the databases have been processed. if self.db_index == 0: - interval = 60 + self.pid % 10 # chorus effect + interval = SLEEP_INTERVAL + self.pid % 10 # chorus effect time.sleep(interval) def _db_list(self): @@ -437,12 +672,66 @@ class WorkerCron(Worker): os.nice(10) # mommy always told me to be nice with others... Worker.start(self) self.multi.socket.close() - openerp.service.start_internal() # chorus effect: make cron workers do not all start at first database mct = config['max_cron_threads'] p = float(self.pid % mct) / mct self.db_index = int(len(self._db_list()) * p) +#---------------------------------------------------------- +# start/stop public api +#---------------------------------------------------------- + +server = None + +def load_server_wide_modules(): + for m in openerp.conf.server_wide_modules: + try: + openerp.modules.module.load_openerp_module(m) + except Exception: + msg = '' + if m == 'web': + msg = """ +The `web` module is provided by the addons found in the `openerp-web` project. +Maybe you forgot to add those addons in your addons_path configuration.""" + _logger.exception('Failed to load server-wide module `%s`.%s', m, msg) + +def _reexec(): + """reexecute openerp-server process with (nearly) the same arguments""" + 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) + args = stripped_sys_argv() + if not args or args[0] != exe: + args.insert(0, exe) + os.execv(sys.executable, args) + +def start(): + """ Start the openerp http server and cron processor. + """ + load_server_wide_modules() + if config['workers']: + openerp.multi_process = True + server = Multicorn(openerp.service.wsgi_server.application) + elif openerp.evented: + server = GeventServer(openerp.service.wsgi_server.application) + else: + server = ThreadedServer(openerp.service.wsgi_server.application) + server.run() + + # like the legend of the phoenix, all ends with beginnings + if getattr(openerp, 'phoenix', False): + _reexec() + sys.exit(0) + +def restart_server(): + """ Restart the server + """ + if os.name == 'nt': + # run in a thread to let the current thread return response to the caller. + threading.Thread(target=_reexec).start() + else: + os.kill(server.pid, signal.SIGHUP) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/service/wsgi_server.py b/openerp/service/wsgi_server.py index acbfc67dd0b..002f8c248b9 100644 --- a/openerp/service/wsgi_server.py +++ b/openerp/service/wsgi_server.py @@ -400,8 +400,6 @@ def application_unproxied(environ, start_response): if hasattr(threading.current_thread(), 'dbname'): del threading.current_thread().dbname - openerp.service.start_internal() - # Try all handlers until one returns some result (i.e. not None). wsgi_handlers = [wsgi_xmlrpc_1, wsgi_xmlrpc, wsgi_xmlrpc_legacy, wsgi_webdav] wsgi_handlers += module_handlers @@ -422,69 +420,5 @@ def application(environ, start_response): else: return application_unproxied(environ, start_response) -# The WSGI server, started by start_server(), stopped by stop_server(). -httpd = None - -def serve(interface, port, threaded): - """ Serve HTTP requests via werkzeug development server. - - Calling this function is blocking, you might want to call it in its own - thread. - """ - - global httpd - if not openerp.evented: - httpd = werkzeug.serving.make_server(interface, port, application, threaded=threaded) - else: - from gevent.wsgi import WSGIServer - httpd = WSGIServer((interface, port), application) - httpd.serve_forever() - -def start_service(): - """ Call serve() in its own thread. - - The WSGI server can be shutdown with stop_server() below. - """ - # TODO Change the xmlrpc_* options to http_* - interface = config['xmlrpc_interface'] or '0.0.0.0' - port = config['xmlrpc_port'] - _logger.info('HTTP service (werkzeug) running on %s:%s', interface, port) - 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. - - The server is supposed to have been started by start_server() above. - """ - if httpd: - if not openerp.evented: - httpd.shutdown() - close_socket(httpd.socket) - else: - import gevent - httpd.stop() - gevent.shutdown() - -def close_socket(sock): - """ Closes a socket instance cleanly - - :param sock: the network socket to close - :type sock: socket.socket - """ - try: - sock.shutdown(socket.SHUT_RDWR) - except socket.error, e: - # On OSX, socket shutdowns both sides if any side closes it - # causing an error 57 'Socket is not connected' on shutdown - # of the other side (or something), see - # http://bugs.python.org/issue4397 - # note: stdlib fixed test, not behavior - if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']: - raise - sock.close() - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 093dec2ab01cd5cd8e3f1962cd707f4afdaea17f Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 9 Sep 2013 15:10:24 +0200 Subject: [PATCH 02/19] [FIX] Add the platform lib [IMP] Log when the Long Polling Service is running bzr revid: stw@openerp.com-20130909131024-23an5cvt1qaa90su --- openerp/service/workers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openerp/service/workers.py b/openerp/service/workers.py index 382d581720f..73df8cc5aaf 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -18,6 +18,7 @@ import time import traceback import subprocess import os.path +import platform import wsgi_server @@ -245,6 +246,7 @@ class GeventServer(CommonServer): from gevent.wsgi import WSGIServer gevent.spawn(self.watch_parent) self.httpd = WSGIServer((self.interface, self.port), self.app) + _logger.info('Evented Service (longpolling) running on %s:%s', self.interface, self.port) self.httpd.serve_forever() def stop(self): From f8c0310d8ea8364b13a1a6b7c51862f1760a4ca9 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 9 Sep 2013 15:11:23 +0200 Subject: [PATCH 03/19] [FIX] Use psycogreen instead of gevent-psycopg2 green-psycopg2 has been deprecated by the developer, he asks to use an alternative, in this case, the alternative is psycogreen https://bitbucket.org/dvarrazzo/psycogreen bzr revid: stw@openerp.com-20130909131123-u0gqhvxqwv13yqke --- openerp/__init__.py | 4 ++-- openerpcommand/web.py | 4 ++-- setup.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openerp/__init__.py b/openerp/__init__.py index 161b06b0990..2cb3bce21d6 100644 --- a/openerp/__init__.py +++ b/openerp/__init__.py @@ -35,8 +35,8 @@ for i in sys.argv: if evented: import gevent.monkey gevent.monkey.patch_all() - import gevent_psycopg2 - gevent_psycopg2.monkey_patch() + import psycogreen.gevent + psycogreen.gevent.patch_psycopg() # 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 diff --git a/openerpcommand/web.py b/openerpcommand/web.py index 5875978ad32..5d9d429244b 100644 --- a/openerpcommand/web.py +++ b/openerpcommand/web.py @@ -30,9 +30,9 @@ def run(args): import gevent import gevent.monkey import gevent.wsgi - import gevent_psycopg2 + import psycogreen.gevent gevent.monkey.patch_all() - gevent_psycopg2.monkey_patch() + psycogreen.gevent.patch_psycopg() import threading import openerp import openerp.cli.server diff --git a/setup.py b/setup.py index ff801a97933..4f4ffe6e505 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ setuptools.setup( 'feedparser', 'gdata', 'gevent', - 'gevent-psycopg2', + 'psycogreen', 'Jinja2', 'lxml', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/ 'mako', From 2a6d0299d97251d93e1a10976096b2354f8f625b Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 9 Sep 2013 22:08:25 +0200 Subject: [PATCH 04/19] revert to use a separate executable for gevent, add --dev option bzr revid: al@openerp.com-20130909200825-sm5c3invmg7tt70j --- openerp-gevent | 13 +++++++++++++ openerp/__init__.py | 10 ---------- openerp/service/workers.py | 4 +++- openerp/tools/config.py | 4 ++-- 4 files changed, 18 insertions(+), 13 deletions(-) create mode 100755 openerp-gevent diff --git a/openerp-gevent b/openerp-gevent new file mode 100755 index 00000000000..e1c9fa797e1 --- /dev/null +++ b/openerp-gevent @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import gevent.monkey +gevent.monkey.patch_all() +import psycogreen.gevent +psycogreen.gevent.patch_psycopg() + +import openerp + +openerp.evented = True + +if __name__ == "__main__": + openerp.cli.main() diff --git a/openerp/__init__.py b/openerp/__init__.py index 2cb3bce21d6..fe439838497 100644 --- a/openerp/__init__.py +++ b/openerp/__init__.py @@ -27,16 +27,6 @@ import sys # Is the server running with gevent. evented = False -for i in sys.argv: - if i.startswith('--gevent'): - evented = True - break - -if evented: - import gevent.monkey - gevent.monkey.patch_all() - import psycogreen.gevent - psycogreen.gevent.patch_psycopg() # 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 diff --git a/openerp/service/workers.py b/openerp/service/workers.py index 73df8cc5aaf..50aa8f245d0 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -326,7 +326,9 @@ class Multicorn(CommonServer): def long_polling_spawn(self): nargs = stripped_sys_argv('--pidfile','--workers') - nargs += ['--gevent'] + cmd = nargs[0] + cmd = os.path.join(os.path.dirname(cmd), "openerp-gevent") + nargs[0] = cmd popen = subprocess.Popen(nargs) self.long_polling_pid = popen.pid diff --git a/openerp/tools/config.py b/openerp/tools/config.py index cc3c187c775..a399f6d133c 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -106,7 +106,7 @@ class configmanager(object): help="specify additional addons paths (separated by commas).", action="callback", callback=self._check_addons_path, nargs=1, type="string") group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web") - group.add_option("--gevent", dest="gevent", action="store_true", my_default=False, help="Activate the GEvent mode, this also desactivate the cron.") + group.add_option("--dev", dest="dev", action="store_true", my_default=False, help="Activate the developer mode. (code and views auto-reload).") parser.add_option_group(group) # XML-RPC / HTTP @@ -399,7 +399,7 @@ class configmanager(object): 'list_db', 'xmlrpcs', 'proxy_mode', 'test_file', 'test_enable', 'test_commit', 'test_report_directory', 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent', - 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'gevent' + 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'dev' ] for arg in keys: From 3bc9a499d4e872176db809c80222edd5d6cd6f11 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Mon, 9 Sep 2013 22:17:06 +0200 Subject: [PATCH 05/19] add signals to increase/decrease number of workers bzr revid: al@openerp.com-20130909201706-sllijtol7p6mi5nr --- openerp/service/workers.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openerp/service/workers.py b/openerp/service/workers.py index 50aa8f245d0..0e7e15fcc39 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -362,6 +362,12 @@ class Multicorn(CommonServer): elif sig == signal.SIGQUIT: # dump stacks on kill -3 self.dumpstacks() + elif sig == signal.SIGTTIN: + # increase number of workers + self.population += 1 + elif sig == signal.SIGTTOUT: + # decrease number of workers + self.population -= 1 def process_zombie(self): # reap dead workers @@ -423,10 +429,14 @@ class Multicorn(CommonServer): # by a signal simulating a pseudo SA_RESTART. We write to a pipe in the # signal handler to overcome this behaviour self.pipe = self.pipe_new() - # set signal + # set signal handlers signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler) + signal.signal(signal.SIGHUP, self.signal_handler) signal.signal(signal.SIGCHLD, self.signal_handler) + signal.signal(signal.SIGQUIT, self.signal_handler) + signal.signal(signal.SIGTTIN, self.signal_handler) + signal.signal(signal.SIGTTOUT, self.signal_handler) # listen to socket self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) From 65c0538a8359c5836a3e42ac6c2c0e739d4d6bff Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 10 Sep 2013 01:05:53 +0200 Subject: [PATCH 06/19] autoreload, first working version for both python and data, still wip bzr revid: al@openerp.com-20130909230553-jn26ue5qenv0sd3p --- openerp/service/workers.py | 117 +++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 10 deletions(-) diff --git a/openerp/service/workers.py b/openerp/service/workers.py index 0e7e15fcc39..f2f4c60d5db 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -41,6 +41,97 @@ SLEEP_INTERVAL = 60 # 1 min # Common #---------------------------------------------------------- +class AutoReload(object): + def __init__(self): + self.files = {} + import pyinotify + class EventHandler(pyinotify.ProcessEvent): + def __init__(self, autoreload): + self.autoreload = autoreload + + def process_IN_CREATE(self, event): + _logger.debug('File created: %s', event.pathname) + self.autoreload.files[event.pathname] = 1 + + def process_IN_MODIFY(self, event): + _logger.debug('File modified: %s', event.pathname) + self.autoreload.files[event.pathname] = 1 + + self.wm = pyinotify.WatchManager() + self.handler = EventHandler(self) + self.notifier = pyinotify.Notifier(self.wm, self.handler, timeout=0) + mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE # IN_MOVED_FROM, IN_MOVED_TO ? + for path in openerp.tools.config.options["addons_path"].split(','): + _logger.info('Watching addons folder %s', path) + self.wm.add_watch(path, mask, rec=True) + + def process_data(self, touched_files): + # pyinotify notifier + fs modiciation tracker + from openerp.modules.module import load_information_from_description_file as load_manifest + addons_path = openerp.tools.config.options["addons_path"].split(',') + registries = openerp.modules.registry.RegistryManager.registries + keys = ['data', 'demo', 'test', 'init_xml', 'update_xml', 'demo_xml'] + # This will only work for loaded registies, so we have to use -d + # al: proposed to move this code in the registry manager so it can be lazy + for db_name, registry in registries.items(): + cr = registry.db.cursor() + try: + for tfile in touched_files: + for path in addons_path: + if tfile.startswith(path): + # find out wich addons path the file belongs to + # and extract it's module name + right = tfile[len(path) + 1:].split('/') + if len(right) < 2: + continue + module = right[0] + relname = "/".join(right[1:]) + domain = [('name', '=', module), ('state', 'in', ['installed', 'to upgrade'])] + if registry.get('ir.module.module').search(cr, openerp.SUPERUSER_ID, domain): + manifest = load_manifest(module) + kind = [key for key in keys if relname in manifest[key]] + if kind: + _logger.info('Updating changed xml file: %s', tfile) + idref = {} + openerp.tools.convert_file(cr, module, relname, idref, mode='update', kind=kind[0]) + cr.commit() + except Exception,e: + _logger.exception(e) + finally: + cr.close() + + def process_python(self, files): + # process python changes + py_files = [i for i in files if i.endswith('.py')] + py_errors = [] + # TODO keep python errors until they are ok + if py_files: + for i in py_files: + try: + source = open(i, 'rb').read() + '\n' + compile(source, i, 'exec') + except SyntaxError: + py_errors.append(i) + if py_errors: + _logger.info('autoreload: python code change detected, errors found') + for i in py_errors: + _logger.info('autoreload: SyntaxError %s',i) + else: + _logger.info('autoreload: python code updated, autoreload activated') + restart_server() + + def check(self): + # Check if some files have been touched in the addons path. + # If true, check if the touched file belongs to an installed module + # in any of the database used in the registry manager. + while self.notifier.check_events(0): + self.notifier.read_events() + self.notifier.process_events() + l = self.files.keys() + self.files.clear() + self.process_data(l) + self.process_python(l) + class CommonServer(object): def __init__(self, app): # TODO Change the xmlrpc_* options to http_* @@ -101,8 +192,8 @@ class ThreadedServer(CommonServer): self.quit_signals_received = 0 #self.socket = None - #self.queue = [] self.httpd = None + self.autoreload = None def signal_handler(self, sig, frame): if sig in [signal.SIGINT,signal.SIGTERM]: @@ -152,7 +243,11 @@ class ThreadedServer(CommonServer): _logger.debug("cron%d started!" % i) def http_thread(self): - self.httpd = werkzeug.serving.make_server(self.interface, self.port, self.app, threaded=True) + def app(e,s): + if self.autoreload: + self.autoreload.check() + return self.app(e,s) + self.httpd = werkzeug.serving.make_server(self.interface, self.port, app, threaded=True) self.httpd.serve_forever() def http_spawn(self): @@ -172,6 +267,7 @@ class ThreadedServer(CommonServer): win32api.SetConsoleCtrlHandler(lambda sig: signal_handler(sig, None), 1) self.cron_spawn() self.http_spawn() + self.autoreload = AutoReload() def stop(self): """ Shutdown the WSGI server. Wait for non deamon threads. @@ -189,7 +285,7 @@ class ThreadedServer(CommonServer): _logger.debug('current thread: %r', me) for thread in threading.enumerate(): _logger.debug('process %r (%r)', thread, thread.isDaemon()) - if thread != me and not thread.isDaemon() and thread.ident != main_thread_id: + if thread != me and not thread.isDaemon() and thread.ident != self.main_thread_id: while thread.isAlive(): _logger.debug('join and sleep') # Need a busyloop here as thread.join() masks signals @@ -262,11 +358,11 @@ class GeventServer(CommonServer): # Prefork #---------------------------------------------------------- -class Multicorn(CommonServer): +class PreforkServer(CommonServer): """ Multiprocessing inspired by (g)unicorn. - Multicorn currently uses accept(2) as dispatching method between workers - but we plan to replace it by a more intelligent dispatcher to will parse - the first HTTP request line. + PreforkServer (aka Multicorn) currently uses accept(2) as dispatching + method between workers but we plan to replace it by a more intelligent + dispatcher to will parse the first HTTP request line. """ def __init__(self, app): # config @@ -617,12 +713,12 @@ class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer): def __init__(self, app): werkzeug.serving.BaseWSGIServer.__init__(self, "1", "1", app) def server_bind(self): - # we dont bind beause we use the listen socket of Multicorn#socket + # we dont bind beause we use the listen socket of PreforkServer#socket # instead we close the socket if self.socket: self.socket.close() def server_activate(self): - # dont listen as we use Multicorn#socket + # dont listen as we use PreforkServer#socket pass class WorkerCron(Worker): @@ -723,10 +819,11 @@ def _reexec(): def start(): """ Start the openerp http server and cron processor. """ + global server load_server_wide_modules() if config['workers']: openerp.multi_process = True - server = Multicorn(openerp.service.wsgi_server.application) + server = PreforkServer(openerp.service.wsgi_server.application) elif openerp.evented: server = GeventServer(openerp.service.wsgi_server.application) else: From 082b1dc9fc91200d4eeb9a78c05930dbfac510c6 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 10 Sep 2013 01:19:46 +0200 Subject: [PATCH 07/19] restore the original gevent detection bzr revid: al@openerp.com-20130909231946-aglho23enxhfwm0i --- openerp-gevent | 2 -- openerp/__init__.py | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp-gevent b/openerp-gevent index e1c9fa797e1..55044976492 100755 --- a/openerp-gevent +++ b/openerp-gevent @@ -7,7 +7,5 @@ psycogreen.gevent.patch_psycopg() import openerp -openerp.evented = True - if __name__ == "__main__": openerp.cli.main() diff --git a/openerp/__init__.py b/openerp/__init__.py index fe439838497..a06c85d0c21 100644 --- a/openerp/__init__.py +++ b/openerp/__init__.py @@ -27,6 +27,8 @@ 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 From 0afff9aae3497d2621bd9b3d5abd56531f60fb95 Mon Sep 17 00:00:00 2001 From: ima-openerp Date: Wed, 18 Sep 2013 12:28:54 +0530 Subject: [PATCH 08/19] [IMP]removed unused method which is defined only for web shortcuts module. bzr revid: ishwarmalvi13@gmail.com-20130918065854-u0aiyu5e1z7tf09g --- addons/web/controllers/main.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 651e5c42a97..81d37b5aae0 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1010,13 +1010,6 @@ class Menu(http.Controller): """ return request.session.model('ir.ui.menu').get_needaction_data(menu_ids, request.context) - @http.route('/web/menu/action', type='json', auth="user") - def action(self, menu_id): - # still used by web_shortcut - actions = load_actions_from_ir_values('action', 'tree_but_open', - [('ir.ui.menu', menu_id)], False) - return {"action": actions} - class DataSet(http.Controller): @http.route('/web/dataset/search_read', type='json', auth="user") From 0a75a6b5ff175feef9348cdbbc4ab2d90f686304 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sat, 5 Oct 2013 23:22:40 +0200 Subject: [PATCH 09/19] simplier autoreload working bzr revid: al@openerp.com-20131005212240-5lp8tgwukeg5wwdq --- openerp/addons/base/module/module.py | 2 +- openerp/service/workers.py | 185 +++++++++++++++------------ openerp/tools/config.py | 3 +- 3 files changed, 103 insertions(+), 87 deletions(-) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index 39d59a4ab2c..32306fbd0ff 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -726,7 +726,7 @@ class module(osv.osv): if already_installed: # in this case, force server restart to reload python code... cr.commit() - openerp.service.restart_server() + openerp.service.restart() return { 'type': 'ir.actions.client', 'tag': 'home', diff --git a/openerp/service/workers.py b/openerp/service/workers.py index f2f4c60d5db..f7d6f97b85b 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -6,21 +6,19 @@ import errno import fcntl import logging import os +import os.path +import platform import psutil import random import resource import select import signal import socket +import subprocess import sys import threading import time import traceback -import subprocess -import os.path -import platform - -import wsgi_server import werkzeug.serving try: @@ -33,17 +31,60 @@ import openerp.tools.config as config from openerp.release import nt_service_name from openerp.tools.misc import stripped_sys_argv +import wsgi_server + _logger = logging.getLogger(__name__) SLEEP_INTERVAL = 60 # 1 min #---------------------------------------------------------- -# Common +# Werkzeug WSGI servers patched +#---------------------------------------------------------- + +class BaseWSGIServerNoBind(werkzeug.serving.BaseWSGIServer): + """ werkzeug Base WSGI Server patched to skip socket binding. PreforkServer + use this class, sets the socket and calls the process_request() manually + """ + def __init__(self, app): + werkzeug.serving.BaseWSGIServer.__init__(self, "1", "1", app) + def server_bind(self): + # we dont bind beause we use the listen socket of PreforkServer#socket + # instead we close the socket + if self.socket: + self.socket.close() + def server_activate(self): + # dont listen as we use PreforkServer#socket + pass + +# MAybe NOT useful BECAUSE of SOCKET_REUSE, need to test + +class ThreadedWSGIServerReloadable(werkzeug.serving.ThreadedWSGIServer): + """ werkzeug Threaded WSGI Server patched to allow reusing a listen socket + given by the environement, this is used by autoreload to keep the listen + socket open when a reload happens. + """ + def server_bind(self): + envfd = os.environ.get('OPENERP_AUTO_RELOAD_FD') + if envfd: + self.reload_socket = socket.fromfd + # close os.close()fd if fd has been diplucated ?! + else: + self.reload_socket = False + super(ThreadedWSGIServerReloadable, self).server_bind() + + def server_activate(self): + if not self.reload_socket: + super(ThreadedWSGIServerReloadable, self).server_activate() + +#---------------------------------------------------------- +# AutoReload watcher #---------------------------------------------------------- class AutoReload(object): - def __init__(self): + def __init__(self, server): + self.server = server self.files = {} + self.modules = {} import pyinotify class EventHandler(pyinotify.ProcessEvent): def __init__(self, autoreload): @@ -65,40 +106,22 @@ class AutoReload(object): _logger.info('Watching addons folder %s', path) self.wm.add_watch(path, mask, rec=True) - def process_data(self, touched_files): - # pyinotify notifier + fs modiciation tracker - from openerp.modules.module import load_information_from_description_file as load_manifest + def process_data(self, files): + xml_files = [i for i in files if i.endswith('.xml')] addons_path = openerp.tools.config.options["addons_path"].split(',') - registries = openerp.modules.registry.RegistryManager.registries - keys = ['data', 'demo', 'test', 'init_xml', 'update_xml', 'demo_xml'] - # This will only work for loaded registies, so we have to use -d - # al: proposed to move this code in the registry manager so it can be lazy - for db_name, registry in registries.items(): - cr = registry.db.cursor() - try: - for tfile in touched_files: - for path in addons_path: - if tfile.startswith(path): - # find out wich addons path the file belongs to - # and extract it's module name - right = tfile[len(path) + 1:].split('/') - if len(right) < 2: - continue - module = right[0] - relname = "/".join(right[1:]) - domain = [('name', '=', module), ('state', 'in', ['installed', 'to upgrade'])] - if registry.get('ir.module.module').search(cr, openerp.SUPERUSER_ID, domain): - manifest = load_manifest(module) - kind = [key for key in keys if relname in manifest[key]] - if kind: - _logger.info('Updating changed xml file: %s', tfile) - idref = {} - openerp.tools.convert_file(cr, module, relname, idref, mode='update', kind=kind[0]) - cr.commit() - except Exception,e: - _logger.exception(e) - finally: - cr.close() + for i in xml_files: + for path in addons_path: + if i.startswith(path): + # find out wich addons path the file belongs to + # and extract it's module name + right = i[len(path) + 1:].split('/') + if len(right) < 2: + continue + module = right[0] + self.modules[module]=1 + if self.modules: + _logger.info('autoreload: xml change detected, autoreload activated') + restart() def process_python(self, files): # process python changes @@ -118,19 +141,30 @@ class AutoReload(object): _logger.info('autoreload: SyntaxError %s',i) else: _logger.info('autoreload: python code updated, autoreload activated') - restart_server() + restart() - def check(self): + def check_thread(self): # Check if some files have been touched in the addons path. # If true, check if the touched file belongs to an installed module # in any of the database used in the registry manager. - while self.notifier.check_events(0): - self.notifier.read_events() - self.notifier.process_events() - l = self.files.keys() - self.files.clear() - self.process_data(l) - self.process_python(l) + while 1: + while self.notifier.check_events(1000): + self.notifier.read_events() + self.notifier.process_events() + l = self.files.keys() + self.files.clear() + self.process_data(l) + self.process_python(l) + + def run(self): + t = threading.Thread(target=self.check_thread) + t.setDaemon(True) + t.start() + _logger.info('AutoReload watcher running') + +#---------------------------------------------------------- +# Servers: Threaded, Gevented and Prefork +#---------------------------------------------------------- class CommonServer(object): def __init__(self, app): @@ -179,10 +213,6 @@ class CommonServer(object): raise sock.close() -#---------------------------------------------------------- -# Threaded -#---------------------------------------------------------- - class ThreadedServer(CommonServer): def __init__(self, app): super(ThreadedServer, self).__init__(app) @@ -193,7 +223,6 @@ class ThreadedServer(CommonServer): #self.socket = None self.httpd = None - self.autoreload = None def signal_handler(self, sig, frame): if sig in [signal.SIGINT,signal.SIGTERM]: @@ -244,10 +273,8 @@ class ThreadedServer(CommonServer): def http_thread(self): def app(e,s): - if self.autoreload: - self.autoreload.check() return self.app(e,s) - self.httpd = werkzeug.serving.make_server(self.interface, self.port, app, threaded=True) + self.httpd = ThreadedWSGIServerReloadable(self.interface, self.port, app) self.httpd.serve_forever() def http_spawn(self): @@ -267,7 +294,6 @@ class ThreadedServer(CommonServer): win32api.SetConsoleCtrlHandler(lambda sig: signal_handler(sig, None), 1) self.cron_spawn() self.http_spawn() - self.autoreload = AutoReload() def stop(self): """ Shutdown the WSGI server. Wait for non deamon threads. @@ -315,9 +341,8 @@ class ThreadedServer(CommonServer): self.stop() -#---------------------------------------------------------- -# Gevent -#---------------------------------------------------------- + def reload(self): + os.kill(self.pid, signal.SIGHUP) class GeventServer(CommonServer): def __init__(self, app): @@ -354,10 +379,6 @@ class GeventServer(CommonServer): self.start() self.stop() -#---------------------------------------------------------- -# Prefork -#---------------------------------------------------------- - class PreforkServer(CommonServer): """ Multiprocessing inspired by (g)unicorn. PreforkServer (aka Multicorn) currently uses accept(2) as dispatching @@ -684,7 +705,7 @@ class WorkerHTTP(Worker): # Prevent fd inherientence close_on_exec flags = fcntl.fcntl(client, fcntl.F_GETFD) | fcntl.FD_CLOEXEC fcntl.fcntl(client, fcntl.F_SETFD, flags) - # do request using WorkerBaseWSGIServer monkey patched with socket + # do request using BaseWSGIServerNoBind monkey patched with socket self.server.socket = client # tolerate broken pipe when the http client closes the socket before # receiving the full reply @@ -705,21 +726,7 @@ class WorkerHTTP(Worker): def start(self): Worker.start(self) - self.server = WorkerBaseWSGIServer(self.multi.app) - -class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer): - """ werkzeug WSGI Server patched to allow using an external listen socket - """ - def __init__(self, app): - werkzeug.serving.BaseWSGIServer.__init__(self, "1", "1", app) - def server_bind(self): - # we dont bind beause we use the listen socket of PreforkServer#socket - # instead we close the socket - if self.socket: - self.socket.close() - def server_activate(self): - # dont listen as we use PreforkServer#socket - pass + self.server = BaseWSGIServerNoBind(self.multi.app) class WorkerCron(Worker): """ Cron workers """ @@ -806,12 +813,13 @@ The `web` module is provided by the addons found in the `openerp-web` project. Maybe you forgot to add those addons in your addons_path configuration.""" _logger.exception('Failed to load server-wide module `%s`.%s', m, msg) -def _reexec(): +def _reexec(updated_modules=None): """reexecute openerp-server process with (nearly) the same arguments""" 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) args = stripped_sys_argv() + args += ["-u", ','.join(updated_modules)] if not args or args[0] != exe: args.insert(0, exe) os.execv(sys.executable, args) @@ -828,14 +836,22 @@ def start(): server = GeventServer(openerp.service.wsgi_server.application) else: server = ThreadedServer(openerp.service.wsgi_server.application) + + if config['auto_reload']: + autoreload = AutoReload(server) + autoreload.run() + server.run() # like the legend of the phoenix, all ends with beginnings if getattr(openerp, 'phoenix', False): - _reexec() + modules = [] + if config['auto_reload']: + modules = autoreload.modules.keys() + _reexec(modules) sys.exit(0) -def restart_server(): +def restart(): """ Restart the server """ if os.name == 'nt': @@ -844,5 +860,4 @@ def restart_server(): else: os.kill(server.pid, signal.SIGHUP) - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tools/config.py b/openerp/tools/config.py index a399f6d133c..05242b538c2 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -246,6 +246,7 @@ class configmanager(object): # Advanced options group = optparse.OptionGroup(parser, "Advanced options") + group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload') group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode') group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False, help="stop the server after its initialization") @@ -399,7 +400,7 @@ class configmanager(object): 'list_db', 'xmlrpcs', 'proxy_mode', 'test_file', 'test_enable', 'test_commit', 'test_report_directory', 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent', - 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'dev' + 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'dev', 'auto_reload' ] for arg in keys: From c52df72706844589ca8bc4c3fac3fe8550612c23 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sat, 5 Oct 2013 23:58:21 +0200 Subject: [PATCH 10/19] fix signal name bzr revid: al@openerp.com-20131005215821-r60pgkgsrp1lo2ug --- openerp/service/workers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/service/workers.py b/openerp/service/workers.py index f7d6f97b85b..e9b0615e70c 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -482,7 +482,7 @@ class PreforkServer(CommonServer): elif sig == signal.SIGTTIN: # increase number of workers self.population += 1 - elif sig == signal.SIGTTOUT: + elif sig == signal.SIGTTOU: # decrease number of workers self.population -= 1 @@ -553,7 +553,7 @@ class PreforkServer(CommonServer): signal.signal(signal.SIGCHLD, self.signal_handler) signal.signal(signal.SIGQUIT, self.signal_handler) signal.signal(signal.SIGTTIN, self.signal_handler) - signal.signal(signal.SIGTTOUT, self.signal_handler) + signal.signal(signal.SIGTTOU, self.signal_handler) # listen to socket self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) From e6b18fc7aac067d6e905a0f1a68d89670e959fc3 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 00:18:29 +0200 Subject: [PATCH 11/19] rename server bzr revid: al@openerp.com-20131005221829-a18vx5ag6ef4ufee --- openerp/addons/base/module/module.py | 2 +- openerp/cli/server.py | 2 +- openerp/service/__init__.py | 2 +- openerp/service/{workers.py => server.py} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename openerp/service/{workers.py => server.py} (100%) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index 6f986ac01b3..6279a5fe6ae 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -716,7 +716,7 @@ class module(osv.osv): if already_installed: # in this case, force server restart to reload python code... cr.commit() - openerp.service.restart() + openerp.service.server.restart() return { 'type': 'ir.actions.client', 'tag': 'home', diff --git a/openerp/cli/server.py b/openerp/cli/server.py index 0c081b6156b..24987c098e4 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -177,7 +177,7 @@ def main(args): if not config["stop_after_init"]: setup_pid_file() - openerp.service.workers.start() + openerp.service.server.start() if config['pidfile']: os.unlink(config['pidfile']) else: diff --git a/openerp/service/__init__.py b/openerp/service/__init__.py index 02401d455e1..6e3ae985fab 100644 --- a/openerp/service/__init__.py +++ b/openerp/service/__init__.py @@ -25,7 +25,7 @@ import db import model import report import wsgi_server -import workers +import server #.apidoc title: RPC Services diff --git a/openerp/service/workers.py b/openerp/service/server.py similarity index 100% rename from openerp/service/workers.py rename to openerp/service/server.py From 156094df3deb1f8779eeff1cc1f8a3597fcea530 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 01:56:39 +0200 Subject: [PATCH 12/19] [FIX] preliminary code for socket activation, to avoid connection refused during restart bzr revid: al@openerp.com-20131005235639-vifvbhu4o17lf780 --- openerp/service/server.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openerp/service/server.py b/openerp/service/server.py index e9b0615e70c..cddb0bee444 100644 --- a/openerp/service/server.py +++ b/openerp/service/server.py @@ -56,7 +56,9 @@ class BaseWSGIServerNoBind(werkzeug.serving.BaseWSGIServer): # dont listen as we use PreforkServer#socket pass -# MAybe NOT useful BECAUSE of SOCKET_REUSE, need to test +# _reexec() should set LISTEN_* to avoid connection refused during reload time. It +# should also work with systemd socket activation. This is currently untested +# and not yet used. class ThreadedWSGIServerReloadable(werkzeug.serving.ThreadedWSGIServer): """ werkzeug Threaded WSGI Server patched to allow reusing a listen socket @@ -64,10 +66,11 @@ class ThreadedWSGIServerReloadable(werkzeug.serving.ThreadedWSGIServer): socket open when a reload happens. """ def server_bind(self): - envfd = os.environ.get('OPENERP_AUTO_RELOAD_FD') - if envfd: - self.reload_socket = socket.fromfd - # close os.close()fd if fd has been diplucated ?! + envfd = os.environ.get('LISTEN_FDS') + if envfd and os.environ.get('LISTEN_PID') == str(os.getpid()): + self.reload_socket = True + self.socket = socket.fromfd(int(envfd), socket.AF_INET, socket.SOCK_STREAM) + # should we os.close(int(envfd)) ? it seem python duplicate the fd. else: self.reload_socket = False super(ThreadedWSGIServerReloadable, self).server_bind() From 780ae0829472ea777a002de9279d658c311e6683 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 12:21:31 +0200 Subject: [PATCH 13/19] [IMP] add an easier to use XML-RPC faultCode compliant protocol remove the /openerp/* routes, they were never used because the protocol was too different from the historical /xmlrpc/ routes. Instead we introduce a /xmlrpc/2/ route identical to /xmlrpc/ with the only difference that it returns int faultCodes making it compliant with the XML-RPC specification. bzr revid: al@openerp.com-20131006102131-707kxc9zyd1mvlps --- openerp/service/wsgi_server.py | 103 +++++++-------------------------- openerp/tests/common.py | 10 ++-- openerp/tests/test_xmlrpc.py | 17 +++--- 3 files changed, 34 insertions(+), 96 deletions(-) diff --git a/openerp/service/wsgi_server.py b/openerp/service/wsgi_server.py index 002f8c248b9..506a07d0295 100644 --- a/openerp/service/wsgi_server.py +++ b/openerp/service/wsgi_server.py @@ -57,13 +57,7 @@ RPC_FAULT_CODE_WARNING = 2 RPC_FAULT_CODE_ACCESS_DENIED = 3 RPC_FAULT_CODE_ACCESS_ERROR = 4 -# The new (6.1) versioned RPC paths. -XML_RPC_PATH = '/openerp/xmlrpc' -XML_RPC_PATH_1 = '/openerp/xmlrpc/1' -JSON_RPC_PATH = '/openerp/jsonrpc' -JSON_RPC_PATH_1 = '/openerp/jsonrpc/1' - -def xmlrpc_return(start_response, service, method, params, legacy_exceptions=False): +def xmlrpc_return(start_response, service, method, params, string_faultcode=False): """ Helper to call a service's method with some params, using a wsgi-supplied ``start_response`` callback. @@ -81,14 +75,14 @@ def xmlrpc_return(start_response, service, method, params, legacy_exceptions=Fal result = openerp.netsvc.dispatch_rpc(service, method, params) response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None) except Exception, e: - if legacy_exceptions: - response = xmlrpc_handle_exception_legacy(e) + if string_faultcode: + response = xmlrpc_handle_exception_string(e) else: - response = xmlrpc_handle_exception(e) + response = xmlrpc_handle_exception_int(e) start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))]) return [response] -def xmlrpc_handle_exception(e): +def xmlrpc_handle_exception_int(e): if isinstance(e, openerp.osv.orm.except_orm): # legacy fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value)) response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) @@ -121,7 +115,7 @@ def xmlrpc_handle_exception(e): response = xmlrpclib.dumps(fault, allow_none=None, encoding=None) return response -def xmlrpc_handle_exception_legacy(e): +def xmlrpc_handle_exception_string(e): if isinstance(e, openerp.osv.orm.except_orm): fault = xmlrpclib.Fault('warning -- ' + e.name + '\n\n' + e.value, '') response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) @@ -146,82 +140,29 @@ def xmlrpc_handle_exception_legacy(e): response = xmlrpclib.dumps(fault, allow_none=None, encoding=None) return response -def wsgi_xmlrpc_1(environ, start_response): - """ The main OpenERP WSGI handler.""" - if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith(XML_RPC_PATH_1): - length = int(environ['CONTENT_LENGTH']) - data = environ['wsgi.input'].read(length) - - params, method = xmlrpclib.loads(data) - - path = environ['PATH_INFO'][len(XML_RPC_PATH_1):] - if path.startswith('/'): path = path[1:] - if path.endswith('/'): path = path[:-1] - path = path.split('/') - - # All routes are hard-coded. - - # No need for a db segment. - if len(path) == 1: - service = path[0] - - if service == 'common': - if method in ('server_version',): - service = 'db' - return xmlrpc_return(start_response, service, method, params) - - # A db segment must be given. - elif len(path) == 2: - service, db_name = path - params = (db_name,) + params - - return xmlrpc_return(start_response, service, method, params) - - # A db segment and a model segment must be given. - elif len(path) == 3 and path[0] == 'model': - service, db_name, model_name = path - params = (db_name,) + params[:2] + (model_name,) + params[2:] - service = 'object' - return xmlrpc_return(start_response, service, method, params) - - # The body has been read, need to raise an exception (not return None). - fault = xmlrpclib.Fault(RPC_FAULT_CODE_CLIENT_ERROR, '') - response = xmlrpclib.dumps(fault, allow_none=None, encoding=None) - start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))]) - return [response] - def wsgi_xmlrpc(environ, start_response): - """ WSGI handler to return the versions.""" - if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith(XML_RPC_PATH): - length = int(environ['CONTENT_LENGTH']) - data = environ['wsgi.input'].read(length) + """ Two routes are available for XML-RPC - params, method = xmlrpclib.loads(data) + /xmlrpc/ route returns faultCode as strings. This is a historic + violation of the protocol kept for compatibility. - path = environ['PATH_INFO'][len(XML_RPC_PATH):] - if path.startswith('/'): path = path[1:] - if path.endswith('/'): path = path[:-1] - path = path.split('/') - - # All routes are hard-coded. - - if len(path) == 1 and path[0] == '' and method in ('version',): - return xmlrpc_return(start_response, 'common', method, ()) - - # The body has been read, need to raise an exception (not return None). - fault = xmlrpclib.Fault(RPC_FAULT_CODE_CLIENT_ERROR, '') - response = xmlrpclib.dumps(fault, allow_none=None, encoding=None) - start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))]) - return [response] - -def wsgi_xmlrpc_legacy(environ, start_response): + /xmlrpc/2/ is a new route that returns faultCode as int and is + therefore fully compliant. + """ if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'): length = int(environ['CONTENT_LENGTH']) data = environ['wsgi.input'].read(length) - path = environ['PATH_INFO'][len('/xmlrpc/'):] # expected to be one of db, object, ... + + # Distinguish betweed the 2 faultCode modes + string_faultcode = True + if environ['PATH_INFO'].startswith('/xmlrpc/2/'): + service = environ['PATH_INFO'][len('/xmlrpc/2/'):] + string_faultcode = False + else: + service = environ['PATH_INFO'][len('/xmlrpc/'):] params, method = xmlrpclib.loads(data) - return xmlrpc_return(start_response, path, method, params, True) + return xmlrpc_return(start_response, service, method, params, string_faultcode) def wsgi_webdav(environ, start_response): pi = environ['PATH_INFO'] @@ -401,7 +342,7 @@ def application_unproxied(environ, start_response): del threading.current_thread().dbname # Try all handlers until one returns some result (i.e. not None). - wsgi_handlers = [wsgi_xmlrpc_1, wsgi_xmlrpc, wsgi_xmlrpc_legacy, wsgi_webdav] + wsgi_handlers = [wsgi_xmlrpc, wsgi_webdav] wsgi_handlers += module_handlers for handler in wsgi_handlers: result = handler(environ, start_response) diff --git a/openerp/tests/common.py b/openerp/tests/common.py index f8472e81fcf..df00e61f140 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -135,11 +135,11 @@ class RpcCase(unittest2.TestCase): self.proxy.object_60 = xmlrpclib.ServerProxy(url_60 + 'object') #self.proxy.edi_60 = xmlrpclib.ServerProxy(url_60 + 'edi') - # Use the new (6.1) API. - self.proxy.url_61 = url_61 = 'http://%s:%d/openerp/xmlrpc/1/' % (HOST, PORT) - self.proxy.common_61 = xmlrpclib.ServerProxy(url_61 + 'common') - self.proxy.db_61 = xmlrpclib.ServerProxy(url_61 + 'db') - self.proxy.model_61 = xmlrpclib.ServerProxy(url_61 + 'model/' + DB) + # Use the new (8) API. + self.proxy.url_8 = url_8 = 'http://%s:%d/xmlrpc/2/' % (HOST, PORT) + self.proxy.common_8 = xmlrpclib.ServerProxy(url_8 + 'common') + self.proxy.db_8 = xmlrpclib.ServerProxy(url_8 + 'db') + self.proxy.object_8 = xmlrpclib.ServerProxy(url_8 + 'object') @classmethod def generate_database_name(cls): diff --git a/openerp/tests/test_xmlrpc.py b/openerp/tests/test_xmlrpc.py index 8c9d6911e7b..12bc828cb29 100644 --- a/openerp/tests/test_xmlrpc.py +++ b/openerp/tests/test_xmlrpc.py @@ -47,21 +47,18 @@ class test_xmlrpc(common.RpcCase): def test_xmlrpc_ir_model_search(self): """ Try a search on the object service. """ - ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, - 'ir.model', 'search', []) + o = self.proxy.object_60 + ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', []) assert ids - ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, - 'ir.model', 'search', [], {}) + ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [], {}) assert ids - def test_xmlrpc_61_ir_model_search(self): + def test_xmlrpc_8_ir_model_search(self): """ Try a search on the object service. """ - - proxy = xmlrpclib.ServerProxy(self.proxy.url_61 + 'model/' + DB + - '/ir.model') - ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', []) + o = self.proxy.object_8 + ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', []) assert ids - ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', [], {}) + ids = o.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD, 'ir.model', 'search', [], {}) assert ids # This test was written to test the creation of a new RPC endpoint, not From a32bf724f2d4495844765f22c9b80a2134dd3eb6 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 12:24:04 +0200 Subject: [PATCH 14/19] [IMP] ir_actions cleanups. Remove deprecated old style wizards. Remove unsed get_model() function. Move crons into Actions menuitem. Move todos into Actions menuitem. bzr revid: al@openerp.com-20131006102404-s46md70ahydihiih --- openerp/addons/base/ir/ir_actions.py | 120 +++++------------- openerp/addons/base/ir/ir_actions.xml | 84 ++---------- openerp/addons/base/ir/ir_cron_view.xml | 3 +- openerp/addons/base/ir/ir_model.py | 2 + openerp/addons/base/ir/ir_rule_view.xml | 10 ++ .../addons/base/security/ir.model.access.csv | 2 - 6 files changed, 56 insertions(+), 165 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index e13602d6a77..d8198f7270a 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -54,10 +54,9 @@ class actions(osv.osv): _defaults = { 'usage': lambda *a: False, } -actions() -class report_xml(osv.osv): +class ir_actions_report_xml(osv.osv): def _report_content(self, cursor, user, ids, name, arg, context=None): res = {} @@ -174,9 +173,8 @@ class report_xml(osv.osv): 'attachment': False, } -report_xml() -class act_window(osv.osv): +class ir_actions_act_window(osv.osv): _name = 'ir.actions.act_window' _table = 'ir_act_window' _inherit = 'ir.actions.actions' @@ -291,7 +289,7 @@ class act_window(osv.osv): ids_int = isinstance(ids, (int, long)) if ids_int: ids = [ids] - results = super(act_window, self).read(cr, uid, ids, fields=fields, context=context, load=load) + results = super(ir_actions_act_window, self).read(cr, uid, ids, fields=fields, context=context, load=load) if not fields or 'help' in fields: context = dict(context or {}) @@ -328,8 +326,6 @@ class act_window(osv.osv): res_id = dataobj.browse(cr, uid, data_id, context).res_id return self.read(cr, uid, res_id, [], context) -act_window() - VIEW_TYPES = [ ('tree', 'Tree'), ('form', 'Form'), @@ -337,7 +333,7 @@ VIEW_TYPES = [ ('calendar', 'Calendar'), ('gantt', 'Gantt'), ('kanban', 'Kanban')] -class act_window_view(osv.osv): +class ir_actions_act_window_view(osv.osv): _name = 'ir.actions.act_window.view' _table = 'ir_act_window_view' _rec_name = 'view_id' @@ -354,33 +350,22 @@ class act_window_view(osv.osv): 'multi': False, } def _auto_init(self, cr, context=None): - super(act_window_view, self)._auto_init(cr, context) + super(ir_actions_act_window_view, self)._auto_init(cr, context) cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'act_window_view_unique_mode_per_action\'') if not cr.fetchone(): cr.execute('CREATE UNIQUE INDEX act_window_view_unique_mode_per_action ON ir_act_window_view (act_window_id, view_mode)') -act_window_view() -class act_wizard(osv.osv): - _name = 'ir.actions.wizard' + +class ir_actions_act_window_close(osv.osv): + _name = 'ir.actions.act_window_close' _inherit = 'ir.actions.actions' - _table = 'ir_act_wizard' - _sequence = 'ir_actions_id_seq' - _order = 'name' - _columns = { - 'name': fields.char('Wizard Info', size=64, required=True, translate=True), - 'type': fields.char('Action Type', size=32, required=True), - 'wiz_name': fields.char('Wizard Name', size=64, required=True), - 'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the wizard will not be displayed on the right toolbar of a form view."), - 'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups'), - 'model': fields.char('Object', size=64), - } + _table = 'ir_actions' _defaults = { - 'type': 'ir.actions.wizard', - 'multi': False, + 'type': 'ir.actions.act_window_close', } -act_wizard() -class act_url(osv.osv): + +class ir_actions_act_url(osv.osv): _name = 'ir.actions.act_url' _table = 'ir_act_url' _inherit = 'ir.actions.actions' @@ -400,55 +385,9 @@ class act_url(osv.osv): 'type': 'ir.actions.act_url', 'target': 'new' } -act_url() - -def model_get(self, cr, uid, context=None): - wkf_pool = self.pool.get('workflow') - ids = wkf_pool.search(cr, uid, []) - osvs = wkf_pool.read(cr, uid, ids, ['osv']) - - res = [] - mpool = self.pool.get('ir.model') - for osv in osvs: - model = osv.get('osv') - id = mpool.search(cr, uid, [('model','=',model)]) - name = mpool.read(cr, uid, id)[0]['name'] - res.append((model, name)) - - return res - -class ir_model_fields(osv.osv): - _inherit = 'ir.model.fields' - _rec_name = 'field_description' - _columns = { - 'complete_name': fields.char('Complete Name', size=64, select=1), - } -ir_model_fields() - -class server_object_lines(osv.osv): - _name = 'ir.server.object.lines' - _sequence = 'ir_actions_id_seq' - _columns = { - 'server_id': fields.many2one('ir.actions.server', 'Related Server Action'), - 'col1': fields.many2one('ir.model.fields', 'Field', required=True), - 'value': fields.text('Value', required=True, help="Expression containing a value specification. \n" - "When Formula type is selected, this field may be a Python expression " - " that can use the same values as for the condition field on the server action.\n" - "If Value type is selected, the value will be used directly without evaluation."), - 'type': fields.selection([ - ('value', 'Value'), - ('equation', 'Python expression') - ], 'Evaluation Type', required=True, change_default=True), - } - _defaults = { - 'type': 'value', - } -## -# Actions that are run on the server side -# -class actions_server(osv.osv): +class ir_actions_server(osv.osv): """ Server actions model. Server action work on a base model and offer various type of actions that can be executed automatically, for example using base action rules, of manually, by adding the action in the 'More' contextual @@ -1029,16 +968,26 @@ class actions_server(osv.osv): return res -class act_window_close(osv.osv): - _name = 'ir.actions.act_window_close' - _inherit = 'ir.actions.actions' - _table = 'ir_actions' - _defaults = { - 'type': 'ir.actions.act_window_close', +class ir_server_object_lines(osv.osv): + _name = 'ir.server.object.lines' + _sequence = 'ir_actions_id_seq' + _columns = { + 'server_id': fields.many2one('ir.actions.server', 'Related Server Action'), + 'col1': fields.many2one('ir.model.fields', 'Field', required=True), + 'value': fields.text('Value', required=True, help="Expression containing a value specification. \n" + "When Formula type is selected, this field may be a Python expression " + " that can use the same values as for the condition field on the server action.\n" + "If Value type is selected, the value will be used directly without evaluation."), + 'type': fields.selection([ + ('value', 'Value'), + ('equation', 'Python expression') + ], 'Evaluation Type', required=True, change_default=True), + } + _defaults = { + 'type': 'value', } -act_window_close() -# This model use to register action services. + TODO_STATES = [('open', 'To Do'), ('done', 'Done')] TODO_TYPES = [('manual', 'Launch Manually'),('once', 'Launch Manually Once'), @@ -1139,9 +1088,8 @@ Launch Manually Once: after having been launched manually, it sets automatically 'todo': len(total) - len(done) } -ir_actions_todo() -class act_client(osv.osv): +class ir_actions_act_client(osv.osv): _name = 'ir.actions.client' _inherit = 'ir.actions.actions' _table = 'ir_act_client' @@ -1182,6 +1130,8 @@ class act_client(osv.osv): 'context': '{}', } -act_client() + + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/openerp/addons/base/ir/ir_actions.xml b/openerp/addons/base/ir/ir_actions.xml index db5beff5e0f..843dc5df35b 100644 --- a/openerp/addons/base/ir/ir_actions.xml +++ b/openerp/addons/base/ir/ir_actions.xml @@ -1,7 +1,9 @@ - + + + ir.actions.actions ir.actions.actions @@ -34,7 +36,6 @@ - Actions ir.actions.act_window @@ -46,7 +47,7 @@ - + ir.actions.report.xml @@ -121,7 +122,6 @@ - Reports ir.actions.act_window @@ -132,6 +132,8 @@ + + ir.actions.windows.tree ir.actions.act_window @@ -145,7 +147,6 @@ - ir.actions.windows.form ir.actions.act_window @@ -221,7 +222,6 @@ - Window Actions ir.actions.act_window @@ -243,66 +243,7 @@ - - ir.actions.wizard.tree - ir.actions.wizard - - - - - - - - - - - ir.actions.wizard - ir.actions.wizard - -
- - - - - - -
- - - ir.actions.wizard.search - ir.actions.wizard - - - - - - - - - Wizards - ir.actions.act_window - ir.actions.wizard - form - - - - - - - - Property multi-company - - - ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] - - - + Server Action @@ -514,7 +455,6 @@ - Server Actions ir.actions.server @@ -527,7 +467,6 @@ - ir.actions.server.search ir.actions.server @@ -542,7 +481,6 @@ - Server Actions ir.actions.act_window @@ -573,7 +511,6 @@ - ir.actions.todo Config Wizard Steps @@ -602,7 +539,6 @@ - ir.actions.todo ir.actions.todo.select @@ -614,7 +550,6 @@ - Configuration Wizards ir.actions.todo @@ -622,9 +557,7 @@ form The configuration wizards are used to help you configure a new instance of OpenERP. They are launched during the installation of new modules, but you can choose to restart some wizards manually from this menu. - - - + Run Remaining Action Todo True @@ -638,6 +571,5 @@ if config.get('type') not in ('ir.actions.act_window_close',): -
diff --git a/openerp/addons/base/ir/ir_cron_view.xml b/openerp/addons/base/ir/ir_cron_view.xml index edbc2d74b86..63c8994b48b 100644 --- a/openerp/addons/base/ir/ir_cron_view.xml +++ b/openerp/addons/base/ir/ir_cron_view.xml @@ -86,8 +86,7 @@ {'active_test': False} - - + diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 3b709934a27..6630251d2fb 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -219,9 +219,11 @@ class ir_model(osv.osv): class ir_model_fields(osv.osv): _name = 'ir.model.fields' _description = "Fields" + _rec_name = 'field_description' _columns = { 'name': fields.char('Name', required=True, size=64, select=1), + 'complete_name': fields.char('Complete Name', size=64, select=1), 'model': fields.char('Object Name', size=64, required=True, select=1, help="The technical name of the model this field belongs to"), 'relation': fields.char('Object Relation', size=64, diff --git a/openerp/addons/base/ir/ir_rule_view.xml b/openerp/addons/base/ir/ir_rule_view.xml index 9448af058ea..6a1e3aead43 100644 --- a/openerp/addons/base/ir/ir_rule_view.xml +++ b/openerp/addons/base/ir/ir_rule_view.xml @@ -85,5 +85,15 @@ + + + + Property multi-company + + + ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] + + + diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index fe5d622ca82..9783d37874f 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -85,8 +85,6 @@ "access_ir_actions_act_window_close_group_system","ir_actions_act_window_close_group_system","model_ir_actions_act_window_close","group_system",1,1,1,1 "access_ir_actions_report_xml_all","ir_actions_report_xml","model_ir_actions_report_xml",,1,0,0,0 "access_ir_actions_report_xml_group_system","ir_actions_report_xml_group_system","model_ir_actions_report_xml","group_system",1,1,1,1 -"access_ir_actions_wizard_all","ir_actions_wizard","model_ir_actions_wizard",,1,0,0,0 -"access_ir_actions_wizard_group_system","ir_actions_wizard_group_system","model_ir_actions_wizard","group_system",1,1,1,1 "access_ir_actions_todo_group_system","ir_actions_todo group system","model_ir_actions_todo","group_system",1,1,1,1 "access_workflow_all","workflow_all","model_workflow",,1,0,0,0 "access_workflow_group_system","workflow_group_system","model_workflow","group_system",1,1,1,1 From 881a76dbcf47c877feb5efa14943010309d22eaf Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 13:26:08 +0200 Subject: [PATCH 15/19] [IMP] Technical menu reorg, remove depreacted requests. Moved low levels object to database structure. Removed res.request models bu keep res.request.link, res.request.link is used to flag models worth to be linked by references fields. Moved currency and state to the approriate directory. bzr revid: al@openerp.com-20131006112608-3qucrevmkf43kq20 --- openerp/addons/base/__openerp__.py | 6 +- openerp/addons/base/base_menu.xml | 1 - .../addons/base/data/administration-hover.png | Bin 2119 -> 0 bytes openerp/addons/base/data/administration.png | Bin 1252 -> 0 bytes openerp/addons/base/data/sales-hover.png | Bin 2324 -> 0 bytes openerp/addons/base/data/sales.png | Bin 1234 -> 0 bytes openerp/addons/base/ir/ir_attachment_view.xml | 2 +- .../base/{data => res}/res.country.state.csv | 0 .../res_currency_data.xml} | 0 openerp/addons/base/res/res_partner_demo.xml | 8 -- openerp/addons/base/res/res_request.py | 76 ------------------ openerp/addons/base/res/res_request_view.xml | 5 +- .../addons/base/security/ir.model.access.csv | 4 - 13 files changed, 6 insertions(+), 96 deletions(-) delete mode 100644 openerp/addons/base/data/administration-hover.png delete mode 100644 openerp/addons/base/data/administration.png delete mode 100644 openerp/addons/base/data/sales-hover.png delete mode 100644 openerp/addons/base/data/sales.png rename openerp/addons/base/{data => res}/res.country.state.csv (100%) rename openerp/addons/base/{currency_data.xml => res/res_currency_data.xml} (100%) diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index f2df88506f5..fd2d514791b 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -35,21 +35,21 @@ The kernel of OpenERP, needed for all installation. 'depends': [], 'data': [ 'base_data.xml', - 'currency_data.xml', + 'res/res_currency_data.xml', 'res/res_country_data.xml', 'security/base_security.xml', 'base_menu.xml', 'res/res_security.xml', 'res/res_config.xml', - 'data/res.country.state.csv', + 'res/res.country.state.csv', 'ir/wizard/wizard_menu_view.xml', 'ir/ir_actions.xml', - 'ir/ir_attachment_view.xml', 'ir/ir_config_parameter_view.xml', 'ir/ir_cron_view.xml', 'ir/ir_filters.xml', 'ir/ir_mail_server_view.xml', 'ir/ir_model_view.xml', + 'ir/ir_attachment_view.xml', 'ir/ir_rule_view.xml', 'ir/ir_sequence_view.xml', 'ir/ir_translation_view.xml', diff --git a/openerp/addons/base/base_menu.xml b/openerp/addons/base/base_menu.xml index 2e6a6d3a27f..19f1e8465e2 100644 --- a/openerp/addons/base/base_menu.xml +++ b/openerp/addons/base/base_menu.xml @@ -27,7 +27,6 @@ - Open Settings Menu diff --git a/openerp/addons/base/data/administration-hover.png b/openerp/addons/base/data/administration-hover.png deleted file mode 100644 index 5d10833d2e45ea8d9017861fe3c38e03a1cdb5ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2119 zcmV-N2)Or&P)n!doG!o{XjAE#d` zsZ$iGTNSEA2C7{etj5W%OAoG+YOhrhvQ87Sva+*L5wxtOwO$pqY&W(|3ARufxX#YF z)6=^a8@yf-y<`}_wy(fk8^c^3!-`VGRu;ry6U1v2#aI@_S`Efw zAjV`I$Z8(RUmM9{AIW4N%3&JHW*N$K8OvrN%witRWgpFIBF)Xs&R!DEXdlmO9nWti z&|wwOa3RrbBhhgt(R&)v<>b*ncb8crV#{DA{`}*?cJ3gfH2KA=!;J+I%S5f-Bp6D%*T6+kPn9ek$94 zE8BlC+leyVek$C5F5HVI-GD3If-K#GEZu`I-i9vTh%erzP~V9!-;FWgiZS4hGT@Uo z;KHrph%e!fGvSXl;*mAtlP%(tHsayoI^&!>Iapd*%<(xj{o;>BAKINZ2=AJ_4pg-oLLFcYd=gqk2rbXze zMd-X}=-ScgpEv2JOX;XZ>8VHQtVik5z3Qn?>aI!ZzK!dwPwTHs>$Onpx`FH2$?ULA z?6OPjvrO%&OYN^y?X^wq?d|TYOzyZ=@6D(1zF6?Zj_}#S@wiLzcZf4~`u3kZ-9AaQ{cw_S+?0)aRn?eMW! z_y7mQ&Vm_#Oiy=pRb^#naHw`qujN5gqvclbq19QHk#9!C8_}VL0RsjM7%*VKfB^&g zK+eDQ=ihjq)(f-uod^F1lAnG3qkE^{-aoIYJvYf;4ogY#1^n~6{PV{i*Mlp?_1~W^ zzj>v&er{WL=ArZQBf)tu@_O^m>3Y2I?@Z?#J0{b6y-7a!$>&GJ7cq@D|5SPZuYa#! z-%vk3xHemF*_!?1i+w#gD1Z9p%T&Zw+P0s(yX$u^U0!c!t}gQ*eUBO-g5`~!-~IY5 zU(Qh1dq4cbo?F`0H5F+dy-nEKP|Do#*Uw^!Hj+Tpzw!Qq`@KBT=_ejH6GTBkk?k)j zt0YAWHE&sNA2aG4QK)w(huSwMIA3q-{3xT%I@cp))OAKiY%@h6U{CUS-^9$&)mc{Yhe_DN#D` z+Coy$&>5H)4GhRJc_#5f#aO1$G}>sJ5RwV&VEC6bX$+E^%x;nD-ky}R#%oYWpr8nA zBH4()h{0Qv)!gmvNv$)DDQ&9ym0wX!J`WOHs$yS~ST~}2j^^iq7*&eu z>}Vy&gE~6hY6^z1sCtL%WEoz%Z|;@WBPt4#nMAa0TR@ZHbB}-4U&4#=DS=XW>foc+ z7;wSaz$HOFC0HL>iiY|E5^hHADm{QNsH-O01_F-U=BBEf?)d%L==R-1q_|xK*pt=f}HhMlaUGfXm&pd$BO??`}YyG9~d7C_#zMQbk6e@vzJNwb;&5Lampnv%81w>GBoB zv|WE>Eu$N0@yWg<*W{0%d+^Vf9)`1RZa@CR?we;ek5@nb<-rfeHGQz{i?7_<@8!w< x!LR<-i-UQ(y?>7Z0|pEjFkrxd0Ruh`{1?h5tE%|~;HLlp002ovPDHLkV1g)CBk%wK diff --git a/openerp/addons/base/data/administration.png b/openerp/addons/base/data/administration.png deleted file mode 100644 index ba4e403ff33c19d3c8c553453a692905a535a827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1252 zcmVlarU1mzbEC zo12@Rot>YbpP-z`(%9 z#>UOf&C}D<+S=OU;o<4&>Fw?9@9*#P^Yi=r`WJoAD*ylh0d!JMQvg8b*k%9#1Jy}H zK~!ko?O08(+e8#S=h}{wyp~piif-8;Au0r@sQ-V-3f64-5<&v;9_`DA?>#KWV>{&K zDPzjcM^>UqX6DTOzGGm)f&~i}ELgB$!GaQ0*H8U&C#y2*=b!!hKOE<)O>=_n>#LX6 zSI@lL!&>FhUcP((g1l*L^UXN?y0M%8F3Fo+$H~y{T-(DdCQ&GF-|k*nj1$zf-xia+ z`*~X{Ylmv?f!*I-{+HDOx^0f=Sz*5a_@X4d@cS3Pe9%HrKZpKD{ciK2#s$Cm8qWv{ z5CFsd?&bG|Brl#e5FkQ?z=$AR(%@U_F|EGGQ$d zO0qDlI+of*`2hq9@?LdG8X*J_0ur+r-W8L$n-HT`lR2_v5Cz$EMU=a6q*WTV-hcB{ z+`+CkBTh6bOasM3enS8TA}&w594CSlgTlmrZ7~&y0U9rJJR->vlhZNI9pgkC0V=Fz|RV0db`EC!S77WGM;N1gOjb0uc^)2w+Fa01O+mFE(X0{?nhf z(-faFzDHj+B&&$==p?{slTPMK%UImV1{;FtTFfbwzi-X(}k+bBgeB}sj7NVGno40C6Y z5C|JuIw^njoG>RjBrndq&MB;eY0@j?&KOE9CfNr&5E9G#E+`Z!A1U8K2Dep!0TH7b+fC;Z{mgwYN{7_?2%Q2-SZ3 zY*pOVzx@5LqSqp}{iyh2+N@tlyp|yhWZf)p_7*HyuwcQ01q&9uAN&IZR3T!09?22_ O0000MUZEbCGWjS(jZF6c)d3bn-hlh-I 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 - + diff --git a/openerp/addons/base/data/res.country.state.csv b/openerp/addons/base/res/res.country.state.csv similarity index 100% rename from openerp/addons/base/data/res.country.state.csv rename to openerp/addons/base/res/res.country.state.csv diff --git a/openerp/addons/base/currency_data.xml b/openerp/addons/base/res/res_currency_data.xml similarity index 100% rename from openerp/addons/base/currency_data.xml rename to openerp/addons/base/res/res_currency_data.xml diff --git a/openerp/addons/base/res/res_partner_demo.xml b/openerp/addons/base/res/res_partner_demo.xml index a8f0aa4eec7..3f5d50efa27 100644 --- a/openerp/addons/base/res/res_partner_demo.xml +++ b/openerp/addons/base/res/res_partner_demo.xml @@ -410,15 +410,7 @@ vauxoo.com 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 - - - ASUSTEK-Subject - - - diff --git a/openerp/addons/base/res/res_request.py b/openerp/addons/base/res/res_request.py index e91ebacbe2a..5a01f146d97 100644 --- a/openerp/addons/base/res/res_request.py +++ b/openerp/addons/base/res/res_request.py @@ -29,64 +29,6 @@ def _links_get(self, cr, uid, context=None): res = obj.read(cr, uid, ids, ['object', 'name'], context) return [(r['object'], r['name']) for r in res] -class res_request(osv.osv): - _name = 'res.request' - - def request_send(self, cr, uid, ids, *args): - for id in ids: - cr.execute('update res_request set state=%s,date_sent=%s where id=%s', ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), id)) - cr.execute('select act_from,act_to,body,date_sent from res_request where id=%s', (id,)) - values = cr.dictfetchone() - if values['body'] and (len(values['body']) > 128): - values['name'] = values['body'][:125] + '...' - else: - values['name'] = values['body'] or '/' - values['req_id'] = id - self.pool.get('res.request.history').create(cr, uid, values) - return True - - def request_reply(self, cr, uid, ids, *args): - for id in ids: - cr.execute("update res_request set state='active', act_from=%s, act_to=act_from, trigger_date=NULL, body='' where id=%s", (uid,id)) - return True - - def request_close(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'closed'}) - return True - - def request_get(self, cr, uid): - cr.execute('select id from res_request where act_to=%s and (trigger_date<=%s or trigger_date is null) and active=True and state != %s', (uid,time.strftime('%Y-%m-%d'), 'closed')) - ids = map(lambda x:x[0], cr.fetchall()) - cr.execute('select id from res_request where act_from=%s and (act_to<>%s) and (trigger_date<=%s or trigger_date is null) and active=True and state != %s', (uid,uid,time.strftime('%Y-%m-%d'), 'closed')) - ids2 = map(lambda x:x[0], cr.fetchall()) - return ids, ids2 - - _columns = { - 'create_date': fields.datetime('Created Date', readonly=True), - 'name': fields.char('Subject', states={'waiting':[('readonly',True)],'active':[('readonly',True)],'closed':[('readonly',True)]}, required=True, size=128), - 'active': fields.boolean('Active'), - 'priority': fields.selection([('0','Low'),('1','Normal'),('2','High')], 'Priority', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}, required=True), - 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True, states={'closed':[('readonly',True)]}, select=1), - 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}, select=1), - 'body': fields.text('Request', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), - 'date_sent': fields.datetime('Date', readonly=True), - 'trigger_date': fields.datetime('Trigger Date', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}, select=1), - 'ref_partner_id':fields.many2one('res.partner', 'Partner Ref.', states={'closed':[('readonly',True)]}), - 'ref_doc1':fields.reference('Document Ref 1', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), - 'ref_doc2':fields.reference('Document Ref 2', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), - 'state': fields.selection([('draft','draft'),('waiting','waiting'),('active','active'),('closed','closed')], 'Status', required=True, readonly=True), - 'history': fields.one2many('res.request.history','req_id', 'History') - } - _defaults = { - 'act_from': lambda obj,cr,uid,context=None: uid, - 'state': 'draft', - 'active': True, - 'priority': '1', - } - _order = 'priority desc, trigger_date, create_date desc' - _table = 'res_request' -res_request() - class res_request_link(osv.osv): _name = 'res.request.link' _columns = { @@ -98,25 +40,7 @@ class res_request_link(osv.osv): 'priority': 5, } _order = 'priority' -res_request_link() -class res_request_history(osv.osv): - _name = 'res.request.history' - _columns = { - 'name': fields.char('Summary', size=128, states={'active':[('readonly',True)],'waiting':[('readonly',True)]}, required=True), - 'req_id': fields.many2one('res.request', 'Request', required=True, ondelete='cascade', select=True), - 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True), - 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)]}), - 'body': fields.text('Body', states={'waiting':[('readonly',True)]}), - 'date_sent': fields.datetime('Date sent', states={'waiting':[('readonly',True)]}, required=True) - } - _defaults = { - 'name': 'NoName', - 'act_from': lambda obj,cr,uid,context=None: uid, - 'act_to': lambda obj,cr,uid,context=None: uid, - 'date_sent': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } -res_request_history() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/res/res_request_view.xml b/openerp/addons/base/res/res_request_view.xml index 7ad47a53c2e..03713f79926 100644 --- a/openerp/addons/base/res/res_request_view.xml +++ b/openerp/addons/base/res/res_request_view.xml @@ -35,16 +35,15 @@ - - Request Reference Types + Referencable Models ir.actions.act_window res.request.link form - + diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index 9783d37874f..1efd28b9afa 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -69,10 +69,6 @@ "access_res_partner_category_group_partner_manager","res_partner_category group_partner_manager","model_res_partner_category","group_partner_manager",1,1,1,1 "access_res_partner_title_group_user","res_partner_title group_user","model_res_partner_title","group_partner_manager",1,1,1,1 "access_res_partner_title_group_partner_manager","res_partner_title group_partner_manager","model_res_partner_title",,1,0,0,0 -"access_res_request_group_user","res_request group_user","model_res_request","base.group_user",1,1,1,1 -"access_res_request_history_group_user","res_request_history group_user","model_res_request_history","base.group_user",1,1,1,1 -"access_res_request_group_user_all","res_request group_user all","model_res_request",,1,0,0,0 -"access_res_request_history_group_user_all","res_request_history group_user all","model_res_request_history",,1,0,0,0 "access_res_request_link_group_system","res_request_link group_system","model_res_request_link","group_system",1,1,1,1 "access_res_request_link_group_user","res_request_link group_user","model_res_request_link",,1,0,0,0 "access_res_users_all","res_users all","model_res_users",,1,0,0,0 From f1faa55cb6e3f77588ce6e0a431d7a376c883738 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 13:47:04 +0200 Subject: [PATCH 16/19] rename links get, allow reference fields without size. bzr revid: al@openerp.com-20131006114704-mkbheq3l355u21jb --- openerp/addons/base/res/res_request.py | 6 +----- openerp/osv/fields.py | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/res/res_request.py b/openerp/addons/base/res/res_request.py index 5a01f146d97..a565f3c7844 100644 --- a/openerp/addons/base/res/res_request.py +++ b/openerp/addons/base/res/res_request.py @@ -19,11 +19,9 @@ # ############################################################################## -import time - from openerp.osv import osv, fields -def _links_get(self, cr, uid, context=None): +def referencable_models(self, cr, uid, context=None): obj = self.pool.get('res.request.link') ids = obj.search(cr, uid, [], context=context) res = obj.read(cr, uid, ids, ['object', 'name'], context) @@ -41,7 +39,5 @@ class res_request_link(osv.osv): } _order = 'priority' - - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 3f0642c9229..372d7e463aa 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -182,7 +182,7 @@ class reference(_column): _type = 'reference' _classic_read = False # post-process to handle missing target - def __init__(self, string, selection, size, **args): + def __init__(self, string, selection, size=None, **args): _column.__init__(self, string=string, size=size, selection=selection, **args) def get(self, cr, obj, ids, name, uid=None, context=None, values=None): From 9757c088729d4b0f182f87522e36007836fa788f Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 13:58:08 +0200 Subject: [PATCH 17/19] use referencable models instead of _get_links bzr revid: al@openerp.com-20131006115808-etkyq34ar1pqh2pb --- addons/base_calendar/base_calendar.py | 28 +-------------------------- addons/crm/crm.py | 7 ------- addons/crm/crm_lead.py | 5 +++-- addons/crm_claim/crm_claim.py | 3 ++- addons/crm_helpdesk/crm_helpdesk.py | 5 +++-- addons/resource/resource_view.xml | 2 +- 6 files changed, 10 insertions(+), 40 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 6194324e183..397bd193757 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -114,19 +114,6 @@ def real_id2base_calendar_id(real_id, recurrent_date): return '%d-%s' % (real_id, recurrent_date) return real_id -def _links_get(self, cr, uid, context=None): - """ - Get request link. - @param cr: the current row, from the database cursor - @param uid: the current user's ID for security checks - @param context: a standard dictionary for contextual values - @return: list of dictionary which contain object and name and id - """ - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context=context) - return [(r['object'], r['name']) for r in res] - html_invitation = """ @@ -307,19 +294,6 @@ class calendar_attendee(osv.osv): return result - def _links_get(self, cr, uid, context=None): - """ - Get request link for ref field in calendar attendee. - @param cr: the current row, from the database cursor - @param uid: the current user's id for security checks - @param context: A standard dictionary for contextual values - @return: list of dictionary which contain object and name and id - """ - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context=context) - return [(r['object'], r['name']) for r in res] - def _lang_get(self, cr, uid, context=None): """ Get language for language selection field. @@ -385,7 +359,7 @@ property or property parameter."), 'event_end_date': fields.function(_compute_data, \ string='Event End Date', type="datetime", \ multi='event_end_date'), - 'ref': fields.reference('Event Ref', selection=_links_get, size=128), + 'ref': fields.reference('Event Ref', selection=openerp.addons.base.res.res_request.referencable_models, size=128), 'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"), } _defaults = { diff --git a/addons/crm/crm.py b/addons/crm/crm.py index dc7ebf3ca95..98581b91666 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -258,13 +258,6 @@ class crm_case_resource_type(osv.osv): 'section_id': fields.many2one('crm.case.section', 'Sales Team'), } -def _links_get(self, cr, uid, context=None): - """Gets links value for reference field""" - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context) - return [(r['object'], r['name']) for r in res] - class crm_payment_mode(osv.osv): """ Payment Mode for Fund """ _name = "crm.payment.mode" diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index fcfec50d344..bb080a95399 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -23,6 +23,7 @@ import crm from datetime import datetime from operator import itemgetter +import openerp from openerp import SUPERUSER_ID from openerp import tools from openerp.addons.base.res.res_partner import format_address @@ -258,8 +259,8 @@ class crm_lead(format_address, osv.osv): # Only used for type opportunity 'probability': fields.float('Success Rate (%)', group_operator="avg"), 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), - 'ref': fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), + 'ref': fields.reference('Reference', selection=openerp.addons.base.res.res_request.referencable_models), + 'ref2': fields.reference('Reference 2', selection=openerp.addons.base.res.res_request.referencable_models), 'phone': fields.char("Phone", size=64), 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), 'date_action': fields.date('Next Action Date', select=True), diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index a3b09c272f6..1b9cb0b0c50 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -19,6 +19,7 @@ # ############################################################################## +import openerp from openerp.addons.crm import crm from openerp.osv import fields, osv from openerp import tools @@ -83,7 +84,7 @@ class crm_claim(osv.osv): 'date_deadline': fields.date('Deadline'), 'date_closed': fields.datetime('Closed', readonly=True), 'date': fields.datetime('Claim Date', select=True), - 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), + 'ref': fields.reference('Reference', selection=openerp.addons.base.res.res_request.referencable_models), 'categ_id': fields.many2one('crm.case.categ', 'Category', \ domain="[('section_id','=',section_id),\ ('object_id.model', '=', 'crm.claim')]"), diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index 80a86c87c7c..5f84d45f974 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -19,6 +19,7 @@ # ############################################################################## +import openerp from openerp.addons.crm import crm from openerp.osv import fields, osv from openerp import tools @@ -53,8 +54,8 @@ class crm_helpdesk(osv.osv): 'email_cc': fields.text('Watchers Emails', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'email_from': fields.char('Email', size=128, help="Destination email for email gateway"), 'date': fields.datetime('Date'), - 'ref' : fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128), + 'ref': fields.reference('Reference', selection=openerp.addons.base.res.res_request.referencable_models), + 'ref2': fields.reference('Reference 2', selection=openerp.addons.base.res.res_request.referencable_models), 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel."), 'planned_revenue': fields.float('Planned Revenue'), 'planned_cost': fields.float('Planned Costs'), diff --git a/addons/resource/resource_view.xml b/addons/resource/resource_view.xml index 5d573214187..8954adae80a 100644 --- a/addons/resource/resource_view.xml +++ b/addons/resource/resource_view.xml @@ -251,7 +251,7 @@ tree,form,calendar - + From 80df504ed1372cc5eb9432649897815059bfcc03 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 14:46:55 +0200 Subject: [PATCH 18/19] [REM] web_shortcut bzr revid: al@openerp.com-20131006124655-kxjp9kbzc07j9584 --- addons/web_shortcuts/LICENSE | 15 -- addons/web_shortcuts/__init__.py | 21 --- addons/web_shortcuts/__openerp__.py | 44 ------ addons/web_shortcuts/i18n/cs.po | 25 --- addons/web_shortcuts/i18n/da.po | 25 --- addons/web_shortcuts/i18n/de.po | 25 --- addons/web_shortcuts/i18n/el.po | 25 --- addons/web_shortcuts/i18n/en_GB.po | 25 --- addons/web_shortcuts/i18n/es.po | 25 --- addons/web_shortcuts/i18n/fr.po | 25 --- addons/web_shortcuts/i18n/hr.po | 25 --- addons/web_shortcuts/i18n/hu.po | 25 --- addons/web_shortcuts/i18n/it.po | 25 --- addons/web_shortcuts/i18n/lt.po | 25 --- addons/web_shortcuts/i18n/mk.po | 25 --- addons/web_shortcuts/i18n/mn.po | 25 --- addons/web_shortcuts/i18n/nl.po | 25 --- addons/web_shortcuts/i18n/pl.po | 25 --- addons/web_shortcuts/i18n/pt.po | 25 --- addons/web_shortcuts/i18n/pt_BR.po | 25 --- addons/web_shortcuts/i18n/ro.po | 25 --- addons/web_shortcuts/i18n/ru.po | 25 --- addons/web_shortcuts/i18n/sl.po | 25 --- addons/web_shortcuts/i18n/sv.po | 25 --- addons/web_shortcuts/i18n/th.po | 25 --- addons/web_shortcuts/i18n/tr.po | 25 --- addons/web_shortcuts/i18n/vi.po | 25 --- addons/web_shortcuts/i18n/web_shortcuts.pot | 24 --- addons/web_shortcuts/i18n/zh_CN.po | 25 --- addons/web_shortcuts/i18n/zh_TW.po | 25 --- .../web_shortcuts/static/description/icon.png | Bin 5369 -> 0 bytes .../static/src/css/web_shortcuts.css | 60 ------- .../static/src/js/web_shortcuts.js | 149 ------------------ .../static/src/xml/web_shortcuts.xml | 24 --- 34 files changed, 987 deletions(-) delete mode 100644 addons/web_shortcuts/LICENSE delete mode 100644 addons/web_shortcuts/__init__.py delete mode 100644 addons/web_shortcuts/__openerp__.py delete mode 100644 addons/web_shortcuts/i18n/cs.po delete mode 100644 addons/web_shortcuts/i18n/da.po delete mode 100644 addons/web_shortcuts/i18n/de.po delete mode 100644 addons/web_shortcuts/i18n/el.po delete mode 100644 addons/web_shortcuts/i18n/en_GB.po delete mode 100644 addons/web_shortcuts/i18n/es.po delete mode 100644 addons/web_shortcuts/i18n/fr.po delete mode 100644 addons/web_shortcuts/i18n/hr.po delete mode 100644 addons/web_shortcuts/i18n/hu.po delete mode 100644 addons/web_shortcuts/i18n/it.po delete mode 100644 addons/web_shortcuts/i18n/lt.po delete mode 100644 addons/web_shortcuts/i18n/mk.po delete mode 100644 addons/web_shortcuts/i18n/mn.po delete mode 100644 addons/web_shortcuts/i18n/nl.po delete mode 100644 addons/web_shortcuts/i18n/pl.po delete mode 100644 addons/web_shortcuts/i18n/pt.po delete mode 100644 addons/web_shortcuts/i18n/pt_BR.po delete mode 100644 addons/web_shortcuts/i18n/ro.po delete mode 100644 addons/web_shortcuts/i18n/ru.po delete mode 100644 addons/web_shortcuts/i18n/sl.po delete mode 100644 addons/web_shortcuts/i18n/sv.po delete mode 100644 addons/web_shortcuts/i18n/th.po delete mode 100644 addons/web_shortcuts/i18n/tr.po delete mode 100644 addons/web_shortcuts/i18n/vi.po delete mode 100644 addons/web_shortcuts/i18n/web_shortcuts.pot delete mode 100644 addons/web_shortcuts/i18n/zh_CN.po delete mode 100644 addons/web_shortcuts/i18n/zh_TW.po delete mode 100644 addons/web_shortcuts/static/description/icon.png delete mode 100644 addons/web_shortcuts/static/src/css/web_shortcuts.css delete mode 100644 addons/web_shortcuts/static/src/js/web_shortcuts.js delete mode 100644 addons/web_shortcuts/static/src/xml/web_shortcuts.xml diff --git a/addons/web_shortcuts/LICENSE b/addons/web_shortcuts/LICENSE deleted file mode 100644 index ff64be448e9..00000000000 --- a/addons/web_shortcuts/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -OpenERP, Open Source Management Solution -Copyright © 2010-2011 OpenERP SA (). - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public Lice -along with this program. If not, see . diff --git a/addons/web_shortcuts/__init__.py b/addons/web_shortcuts/__init__.py deleted file mode 100644 index 99b6a1491e8..00000000000 --- a/addons/web_shortcuts/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010 OpenERP s.a. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/web_shortcuts/__openerp__.py b/addons/web_shortcuts/__openerp__.py deleted file mode 100644 index aa23ed2bfa6..00000000000 --- a/addons/web_shortcuts/__openerp__.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-2011 OpenERP s.a. (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -{ - 'name': 'Web Shortcuts', - 'version': '1.0', - 'category': 'Tools', - 'description': """ -Enable shortcuts feature in the web client. -=========================================== - -Add a Shortcut icon in the systray in order to access the user's shortcuts (if any). - -Add a Shortcut icon besides the views title in order to add/remove a shortcut. - """, - 'author': 'OpenERP SA', - 'website': 'http://openerp.com', - 'depends': ['base'], - 'data': [], - 'js' : ['static/src/js/web_shortcuts.js'], - 'css' : ['static/src/css/web_shortcuts.css'], - 'qweb' : ['static/src/xml/*.xml'], - 'installable': True, - 'auto_install': False, -} - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/web_shortcuts/i18n/cs.po b/addons/web_shortcuts/i18n/cs.po deleted file mode 100644 index fe42953dceb..00000000000 --- a/addons/web_shortcuts/i18n/cs.po +++ /dev/null @@ -1,25 +0,0 @@ -# Czech translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-02-20 13:43+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Czech \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Přidat / Odebrat zkratku..." diff --git a/addons/web_shortcuts/i18n/da.po b/addons/web_shortcuts/i18n/da.po deleted file mode 100644 index 3cbeed065b5..00000000000 --- a/addons/web_shortcuts/i18n/da.po +++ /dev/null @@ -1,25 +0,0 @@ -# Danish translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-09-15 20:22+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Danish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-16 05:42+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Tilføj/fjern genvej ..." diff --git a/addons/web_shortcuts/i18n/de.po b/addons/web_shortcuts/i18n/de.po deleted file mode 100644 index c6e3f2ba0d8..00000000000 --- a/addons/web_shortcuts/i18n/de.po +++ /dev/null @@ -1,25 +0,0 @@ -# German translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-04 07:22+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" -"Language-Team: German \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Hinzufügen/Entfernen Lesezeichen ..." diff --git a/addons/web_shortcuts/i18n/el.po b/addons/web_shortcuts/i18n/el.po deleted file mode 100644 index d8ee859a736..00000000000 --- a/addons/web_shortcuts/i18n/el.po +++ /dev/null @@ -1,25 +0,0 @@ -# Greek translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-09-30 20:14+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Greek \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-10-01 05:18+0000\n" -"X-Generator: Launchpad (build 16774)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "" diff --git a/addons/web_shortcuts/i18n/en_GB.po b/addons/web_shortcuts/i18n/en_GB.po deleted file mode 100644 index b80ba0e48f7..00000000000 --- a/addons/web_shortcuts/i18n/en_GB.po +++ /dev/null @@ -1,25 +0,0 @@ -# English (United Kingdom) translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-08-23 16:45+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: English (United Kingdom) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Add / Remove Shortcut..." diff --git a/addons/web_shortcuts/i18n/es.po b/addons/web_shortcuts/i18n/es.po deleted file mode 100644 index bb120c90dd6..00000000000 --- a/addons/web_shortcuts/i18n/es.po +++ /dev/null @@ -1,25 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-12 12:54+0000\n" -"Last-Translator: Pedro Manuel Baeza \n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Añadir / Eliminar acceso directo..." diff --git a/addons/web_shortcuts/i18n/fr.po b/addons/web_shortcuts/i18n/fr.po deleted file mode 100644 index 1a2a90becc1..00000000000 --- a/addons/web_shortcuts/i18n/fr.po +++ /dev/null @@ -1,25 +0,0 @@ -# French translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-01 08:11+0000\n" -"Last-Translator: WANTELLET Sylvain \n" -"Language-Team: French \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Ajouter / supprimer un raccourci..." diff --git a/addons/web_shortcuts/i18n/hr.po b/addons/web_shortcuts/i18n/hr.po deleted file mode 100644 index 47a0f8f35cd..00000000000 --- a/addons/web_shortcuts/i18n/hr.po +++ /dev/null @@ -1,25 +0,0 @@ -# Croatian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-10 06:51+0000\n" -"Last-Translator: Goran Kliska \n" -"Language-Team: Croatian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Dodaj / ukloni prečac..." diff --git a/addons/web_shortcuts/i18n/hu.po b/addons/web_shortcuts/i18n/hu.po deleted file mode 100644 index c91a6c93d36..00000000000 --- a/addons/web_shortcuts/i18n/hu.po +++ /dev/null @@ -1,25 +0,0 @@ -# Hungarian translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-01-04 12:01+0000\n" -"Last-Translator: krnkris \n" -"Language-Team: Hungarian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Billentyűkombináció hozzáadása/eltávolítása" diff --git a/addons/web_shortcuts/i18n/it.po b/addons/web_shortcuts/i18n/it.po deleted file mode 100644 index e08a8ad8ca2..00000000000 --- a/addons/web_shortcuts/i18n/it.po +++ /dev/null @@ -1,25 +0,0 @@ -# Italian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-11-29 07:33+0000\n" -"Last-Translator: Davide Corio \n" -"Language-Team: Italian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Aggiungi / Rimuovi Scorciatoia..." diff --git a/addons/web_shortcuts/i18n/lt.po b/addons/web_shortcuts/i18n/lt.po deleted file mode 100644 index 9f564849598..00000000000 --- a/addons/web_shortcuts/i18n/lt.po +++ /dev/null @@ -1,25 +0,0 @@ -# Lithuanian translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-04-24 18:29+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Lithuanian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Pridėti / pašalinti trumpinį..." diff --git a/addons/web_shortcuts/i18n/mk.po b/addons/web_shortcuts/i18n/mk.po deleted file mode 100644 index 21e7f37e589..00000000000 --- a/addons/web_shortcuts/i18n/mk.po +++ /dev/null @@ -1,25 +0,0 @@ -# Macedonian translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-03-01 14:20+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Macedonian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Додади / Отстрани кратенка" diff --git a/addons/web_shortcuts/i18n/mn.po b/addons/web_shortcuts/i18n/mn.po deleted file mode 100644 index ffded75a811..00000000000 --- a/addons/web_shortcuts/i18n/mn.po +++ /dev/null @@ -1,25 +0,0 @@ -# Mongolian translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-02-07 11:26+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Mongolian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Шуут холбоосыг Нэмэх / Устгах ..." diff --git a/addons/web_shortcuts/i18n/nl.po b/addons/web_shortcuts/i18n/nl.po deleted file mode 100644 index 0f542966caf..00000000000 --- a/addons/web_shortcuts/i18n/nl.po +++ /dev/null @@ -1,25 +0,0 @@ -# Dutch translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-11-27 19:29+0000\n" -"Last-Translator: Erwin van der Ploeg (BAS Solutions) \n" -"Language-Team: Dutch \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Toevoegen/verwijderen snelkoppeling" diff --git a/addons/web_shortcuts/i18n/pl.po b/addons/web_shortcuts/i18n/pl.po deleted file mode 100644 index 54553cf463d..00000000000 --- a/addons/web_shortcuts/i18n/pl.po +++ /dev/null @@ -1,25 +0,0 @@ -# Polish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-17 10:08+0000\n" -"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" -"Language-Team: Polish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Dodaj / Usuń skrót" diff --git a/addons/web_shortcuts/i18n/pt.po b/addons/web_shortcuts/i18n/pt.po deleted file mode 100644 index c7cf8d637b2..00000000000 --- a/addons/web_shortcuts/i18n/pt.po +++ /dev/null @@ -1,25 +0,0 @@ -# Portuguese translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-05 10:09+0000\n" -"Last-Translator: Andrei Talpa (multibase.pt) \n" -"Language-Team: Portuguese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Adicionar / Remover atalho..." diff --git a/addons/web_shortcuts/i18n/pt_BR.po b/addons/web_shortcuts/i18n/pt_BR.po deleted file mode 100644 index 63e42f4c0b1..00000000000 --- a/addons/web_shortcuts/i18n/pt_BR.po +++ /dev/null @@ -1,25 +0,0 @@ -# Brazilian Portuguese translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-11-29 23:53+0000\n" -"Last-Translator: Luiz Fernando M.França (Sig Informática) \n" -"Language-Team: Brazilian Portuguese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Adicionar / Remover atalho ..." diff --git a/addons/web_shortcuts/i18n/ro.po b/addons/web_shortcuts/i18n/ro.po deleted file mode 100644 index 350c9fe7522..00000000000 --- a/addons/web_shortcuts/i18n/ro.po +++ /dev/null @@ -1,25 +0,0 @@ -# Romanian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-10 07:32+0000\n" -"Last-Translator: ERPSystems.ro \n" -"Language-Team: Romanian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Adauga / Sterge Shortcut..." diff --git a/addons/web_shortcuts/i18n/ru.po b/addons/web_shortcuts/i18n/ru.po deleted file mode 100644 index d7d092ab708..00000000000 --- a/addons/web_shortcuts/i18n/ru.po +++ /dev/null @@ -1,25 +0,0 @@ -# Russian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-25 12:18+0000\n" -"Last-Translator: Chertykov Denis \n" -"Language-Team: Russian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Добавить / Удалить ярлык..." diff --git a/addons/web_shortcuts/i18n/sl.po b/addons/web_shortcuts/i18n/sl.po deleted file mode 100644 index 480bf6e1f04..00000000000 --- a/addons/web_shortcuts/i18n/sl.po +++ /dev/null @@ -1,25 +0,0 @@ -# Slovenian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-18 22:32+0000\n" -"Last-Translator: Dušan Laznik (Mentis) \n" -"Language-Team: Slovenian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Dodaj / Odstrani bližnjico..." diff --git a/addons/web_shortcuts/i18n/sv.po b/addons/web_shortcuts/i18n/sv.po deleted file mode 100644 index 51d285d148d..00000000000 --- a/addons/web_shortcuts/i18n/sv.po +++ /dev/null @@ -1,25 +0,0 @@ -# Swedish translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-01-26 15:07+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Swedish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Lägg till/ta bort genväg..." diff --git a/addons/web_shortcuts/i18n/th.po b/addons/web_shortcuts/i18n/th.po deleted file mode 100644 index 87ee37b52cc..00000000000 --- a/addons/web_shortcuts/i18n/th.po +++ /dev/null @@ -1,25 +0,0 @@ -# Thai translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-06-17 05:48+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Thai \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "เพิ่ม / ลบ ทางลัด ..." diff --git a/addons/web_shortcuts/i18n/tr.po b/addons/web_shortcuts/i18n/tr.po deleted file mode 100644 index 077c845c317..00000000000 --- a/addons/web_shortcuts/i18n/tr.po +++ /dev/null @@ -1,25 +0,0 @@ -# Turkish translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-02-04 14:33+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Turkish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Kısayol Ekle/Kaldır..." diff --git a/addons/web_shortcuts/i18n/vi.po b/addons/web_shortcuts/i18n/vi.po deleted file mode 100644 index 4f6227de670..00000000000 --- a/addons/web_shortcuts/i18n/vi.po +++ /dev/null @@ -1,25 +0,0 @@ -# Vietnamese translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-06-27 16:15+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Vietnamese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "Thêm / Xóa đường dẫn ...." diff --git a/addons/web_shortcuts/i18n/web_shortcuts.pot b/addons/web_shortcuts/i18n/web_shortcuts.pot deleted file mode 100644 index 2babdff3864..00000000000 --- a/addons/web_shortcuts/i18n/web_shortcuts.pot +++ /dev/null @@ -1,24 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * web_shortcuts -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 7.0alpha\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-21 17:06+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "" - diff --git a/addons/web_shortcuts/i18n/zh_CN.po b/addons/web_shortcuts/i18n/zh_CN.po deleted file mode 100644 index 2c125e77d74..00000000000 --- a/addons/web_shortcuts/i18n/zh_CN.po +++ /dev/null @@ -1,25 +0,0 @@ -# Chinese (Simplified) translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-11-27 14:18+0000\n" -"Last-Translator: 盈通 ccdos \n" -"Language-Team: Chinese (Simplified) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "添加/删除快捷方式..." diff --git a/addons/web_shortcuts/i18n/zh_TW.po b/addons/web_shortcuts/i18n/zh_TW.po deleted file mode 100644 index b7c58ae4c93..00000000000 --- a/addons/web_shortcuts/i18n/zh_TW.po +++ /dev/null @@ -1,25 +0,0 @@ -# Chinese (Traditional) translation for openobject-addons -# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2013. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2013-01-31 03:39+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Chinese (Traditional) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-09-12 06:37+0000\n" -"X-Generator: Launchpad (build 16761)\n" - -#. module: web_shortcuts -#. openerp-web -#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 -#, python-format -msgid "Add / Remove Shortcut..." -msgstr "新增/移除 捷徑..." diff --git a/addons/web_shortcuts/static/description/icon.png b/addons/web_shortcuts/static/description/icon.png deleted file mode 100644 index 44347e95f4586548085405afc3960bf9f3be042e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5369 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
_BCl2Uf| zqcAyzl(I<(@tYMNFni1`v^@F^&Yd#Y8GrLhhc>olrN*oYeC1W% zx@eA^(OTwUCLk41>p0tX()f*=cxKH;T2Yf!FGwk~YM`@4=0!a(gxGSyoy2ZDpDo`z zhq-c}>(|@VK*er@v_tyt*LgQ{Syo9WS>?tEAv9g*%4Z+s+Ovjpx}jsVMTsnbjkmA6 zg*O}r3G6~iwj~?Gas?qo5wKu%4MXp`g!6{=p-u?z03PZriTt?r zcOyTr5V)qOfbjUyl)U^Jdp&h~3H%lKb9XbJa)nF=*>qq+MHt`c{uI6R{-Kq*Bq|~} zm{r}~%nvLDPDh0SIx0MQzbh%SrxKYI<>4rMU6j4+rIgKC*V)^COaJqRUXTU;`NsbN X7N_QJWu=O_00000NkvXXu0mjfz5O5B diff --git a/addons/web_shortcuts/static/src/css/web_shortcuts.css b/addons/web_shortcuts/static/src/css/web_shortcuts.css deleted file mode 100644 index 5d4e34f53b9..00000000000 --- a/addons/web_shortcuts/static/src/css/web_shortcuts.css +++ /dev/null @@ -1,60 +0,0 @@ -/* Shortcuts*/ -.oe_systray_shortcuts .oe_star_off { - color: #eee; -} -.oe_shortcuts_toggle { - height: 20px; - margin-top: 3px; - padding: 0; - width: 24px; - cursor: pointer; - display: block; - background: url(/web/static/src/img/add-shortcut.png) no-repeat center center; - float: left; -} -.oe_shortcuts_remove{ - background: url(/web/static/src/img/remove-shortcut.png) no-repeat center center; -} -.oe_shortcuts { - position: absolute; - margin: 0; - padding: 6px 15px; - top: 37px; - left: 197px; - right: 0; - height: 17px; - line-height: 1.2; -} -.oe_shortcuts ul { - display: block; - overflow: hidden; - list-style: none; - white-space: nowrap; - padding: 0; - margin: 0; -} -.oe_shortcuts li { - cursor: pointer; - display: -moz-inline-stack; - display: inline-block; - display: inline; /*IE7 */ - color: #fff; - text-align: center; - border-left: 1px solid #909090; - padding: 0 4px; - font-size: 80%; - font-weight: normal; - vertical-align: top; -} - -.oe_shortcuts li:hover { - background-color: #666; -} -.oe_shortcuts li:first-child { - border-left: none; - padding-left: 0; -} -.oe_systray_shortcuts_items > li > a:hover { - overflow:visible; - white-space: normal; -} \ No newline at end of file diff --git a/addons/web_shortcuts/static/src/js/web_shortcuts.js b/addons/web_shortcuts/static/src/js/web_shortcuts.js deleted file mode 100644 index 045bc5b5ae6..00000000000 --- a/addons/web_shortcuts/static/src/js/web_shortcuts.js +++ /dev/null @@ -1,149 +0,0 @@ -/*############################################################################ -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2011-2012 OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################*/ - -openerp.web_shortcuts = function (instance) { - -var QWeb = instance.web.qweb, - _t = instance.web._t; - -instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({ - template: 'Systray.Shortcuts', - - init: function() { - this._super(); - this.on('load', this, this.load); - this.on('add', this, this.add); - this.on('display', this, this.display); - this.on('remove', this, this.remove); - this.on('click', this, this.click); - this.model = new instance.web.Model('ir.ui.view_sc'); - }, - start: function() { - var self = this; - this._super(); - this.trigger('load'); - this.$el.on('click', '.oe_systray_shortcuts_items a', function() { - self.trigger('click', $(this)); - }); - }, - load: function() { - var self = this; - this.$el.find('.oe_systray_shortcuts_items').empty(); - return this.model.call('get_sc', [ - instance.session.uid, - 'ir.ui.menu', - instance.web.pyeval.eval('context', {}) - ]).done(function(shortcuts) { - _.each(shortcuts, function(sc) { - self.trigger('display', sc); - }); - }); - }, - add: function (sc) { - var self = this; - this.model.call('create', [sc]).then(function(out){ - self.trigger('load'); - }); - }, - display: function(sc) { - var self = this; - this.$el.find('.oe_systray_shortcuts_items').append(); - var $sc = $(QWeb.render('Systray.Shortcuts.Item', {'shortcut': sc})); - $sc.appendTo(self.$el.find('.oe_systray_shortcuts_items')); - }, - remove: function (menu_id) { - var menu_id = this.session.active_id; - var $shortcut = this.$el.find('.oe_systray_shortcuts_items li a[data-id=' + menu_id + ']'); - var shortcut_id = $shortcut.data('shortcut-id'); - $shortcut.remove(); - this.model.call('unlink', [shortcut_id]); - }, - click: function($link) { - var self = this, - id = $link.data('id'); - self.session.active_id = id; - // TODO: Use do_action({menu_id: id, type: 'ir.actions.menu'}) - self.rpc('/web/menu/action', {'menu_id': id}).done(function(ir_menu_data) { - if (ir_menu_data.action.length){ - instance.webclient.on_menu_action({action_id: ir_menu_data.action[0][2].id}); - } - }); - this.$el.find('.oe_systray_shortcuts').trigger('mouseout'); - }, - has: function(menu_id) { - return !!this.$el.find('a[data-id=' + menu_id + ']').length; - } -}); - -instance.web.UserMenu.include({ - do_update: function() { - var self = this; - this._super.apply(this, arguments); - this.update_promise.done(function() { - if (self.shortcuts) { - self.shortcuts.trigger('load'); - } else { - self.shortcuts = new instance.web_shortcuts.Shortcuts(self); - self.shortcuts.appendTo(instance.webclient.$el.find('.oe_systray')); - } - }); - }, -}); - -instance.web.ViewManagerAction.include({ - switch_mode: function (view_type, no_store) { - var self = this; - this._super.apply(this, arguments).done(function() { - self.shortcut_check(self.views[view_type]); - }); - }, - shortcut_check : function(view) { - var self = this; - var shortcuts_menu = instance.webclient.user_menu.shortcuts; - var grandparent = this.getParent() && this.getParent().getParent(); - // display shortcuts if on the first view for the action - var $shortcut_toggle = this.$el.find('.oe_shortcuts_toggle'); - if (!this.action.name || - !(view.view_type === this.views_src[0].view_type - && view.view_id === this.views_src[0].view_id)) { - $shortcut_toggle.hide(); - return; - } - // Anonymous users don't have user_menu - if (shortcuts_menu) { - $shortcut_toggle.toggleClass('oe_shortcuts_remove', shortcuts_menu.has(self.session.active_id)); - $shortcut_toggle.unbind("click").click(function() { - if ($shortcut_toggle.hasClass("oe_shortcuts_remove")) { - shortcuts_menu.trigger('remove', self.session.active_id); - } else { - shortcuts_menu.trigger('add', { - 'user_id': self.session.uid, - 'res_id': self.session.active_id, - 'resource': 'ir.ui.menu', - 'name': self.action.name - }); - } - $shortcut_toggle.toggleClass("oe_shortcuts_remove"); - }); - } - } -}); - -}; diff --git a/addons/web_shortcuts/static/src/xml/web_shortcuts.xml b/addons/web_shortcuts/static/src/xml/web_shortcuts.xml deleted file mode 100644 index 0b9701f9f1a..00000000000 --- a/addons/web_shortcuts/static/src/xml/web_shortcuts.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
- 7 -
    -
-
- -
  • - - - -
  • -
    - - - - - -
    From 4c84a49afea4b7178f3c9b1ae9cc74fe225601d2 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 6 Oct 2013 15:12:25 +0200 Subject: [PATCH 19/19] [REM] shortcuts, deprecated by browser bookmarks bzr revid: al@openerp.com-20131006131225-jguvkys2ngwsd6er --- addons/hr/hr.py | 19 +------------------ addons/hr/hr_view.xml | 7 ------- addons/product/__openerp__.py | 1 - addons/product/product_shortcut_data.xml | 13 ------------- addons/share/wizard/share_wizard.py | 22 ++-------------------- 5 files changed, 3 insertions(+), 59 deletions(-) delete mode 100644 addons/product/product_shortcut_data.xml diff --git a/addons/hr/hr.py b/addons/hr/hr.py index d19f1f7de8b..bf58fdd76f8 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -346,26 +346,9 @@ class res_users(osv.osv): _name = 'res.users' _inherit = 'res.users' - def create(self, cr, uid, data, context=None): - user_id = super(res_users, self).create(cr, uid, data, context=context) - - # add shortcut unless 'noshortcut' is True in context - if not(context and context.get('noshortcut', False)): - data_obj = self.pool.get('ir.model.data') - try: - data_id = data_obj._get_id(cr, uid, 'hr', 'ir_ui_view_sc_employee') - view_id = data_obj.browse(cr, uid, data_id, context=context).res_id - self.pool.get('ir.ui.view_sc').copy(cr, uid, view_id, default = { - 'user_id': user_id}, context=context) - except: - # Tolerate a missing shortcut. See product/product.py for similar code. - _logger.debug('Skipped meetings shortcut for user "%s".', data.get('name',' - - Employees - ir.ui.menu - - - - hr.employee.tree diff --git a/addons/product/__openerp__.py b/addons/product/__openerp__.py index fdadda2e4d4..da17f7d8f6a 100644 --- a/addons/product/__openerp__.py +++ b/addons/product/__openerp__.py @@ -54,7 +54,6 @@ Print product labels with barcode. 'product_data.xml', 'product_report.xml', 'product_view.xml', - 'product_shortcut_data.xml', 'pricelist_view.xml', 'partner_view.xml', 'process/product_process.xml' diff --git a/addons/product/product_shortcut_data.xml b/addons/product/product_shortcut_data.xml deleted file mode 100644 index 4f48445a197..00000000000 --- a/addons/product/product_shortcut_data.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Products - ir.ui.menu - - - - - - diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index f6ee6379ef1..2e8d4d9373e 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -289,32 +289,14 @@ class share_wizard(osv.TransientModel): return created_ids, existing_ids - def _create_shortcut(self, cr, uid, values, context=None): + def _create_action(self, cr, uid, values, context=None): if context is None: context = {} new_context = context.copy() for key in context: if key.startswith('default_'): del new_context[key] - - dataobj = self.pool.get('ir.model.data') - menu_id = dataobj._get_id(cr, uid, 'base', 'menu_administration_shortcut') - shortcut_menu_id = int(dataobj.read(cr, uid, menu_id, ['res_id'], new_context)['res_id']) action_id = self.pool.get('ir.actions.act_window').create(cr, UID_ROOT, values, new_context) - menu_data = {'name': values['name'], - 'sequence': 10, - 'action': 'ir.actions.act_window,'+str(action_id), - 'parent_id': shortcut_menu_id, - 'icon': 'STOCK_JUSTIFY_FILL'} - menu_obj = self.pool.get('ir.ui.menu') - menu_id = menu_obj.create(cr, UID_ROOT, menu_data) - sc_data = {'name': values['name'], 'sequence': UID_ROOT,'res_id': menu_id } - self.pool.get('ir.ui.view_sc').create(cr, uid, sc_data, new_context) - - # update menu cache - user_groups = set(self.pool.get('res.users').read(cr, UID_ROOT, uid, ['groups_id'])['groups_id']) - key = (cr.dbname, shortcut_menu_id, tuple(user_groups)) - menu_obj._cache[key] = True return action_id def _cleanup_action_context(self, context_str, user_id): @@ -383,7 +365,7 @@ class share_wizard(osv.TransientModel): values = self._shared_action_def(cr, uid, wizard_data, context=None) user_obj = self.pool.get('res.users') for user_id in user_ids: - action_id = self._create_shortcut(cr, user_id, values) + action_id = self._create_action(cr, user_id, values) if make_home: # We do this only for new share users, as existing ones already have their initial home # action. Resetting to the default menu does not work well as the menu is rather empty