[FIX] translate: skip XML comments when extracting terms from views
[odoo/odoo.git] / openerp / tools / config.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import ConfigParser
24 import optparse
25 import os
26 import sys
27 import openerp
28 import openerp.conf
29 import openerp.loglevels as loglevels
30 import logging
31 import openerp.release as release
32
33 class MyOption (optparse.Option, object):
34     """ optparse Option with two additional attributes.
35
36     The list of command line options (getopt.Option) is used to create the
37     list of the configuration file options. When reading the file, and then
38     reading the command line arguments, we don't want optparse.parse results
39     to override the configuration file values. But if we provide default
40     values to optparse, optparse will return them and we can't know if they
41     were really provided by the user or not. A solution is to not use
42     optparse's default attribute, but use a custom one (that will be copied
43     to create the default values of the configuration file).
44
45     """
46     def __init__(self, *opts, **attrs):
47         self.my_default = attrs.pop('my_default', None)
48         super(MyOption, self).__init__(*opts, **attrs)
49
50 #.apidoc title: Server Configuration Loader
51
52 def check_ssl():
53     try:
54         from OpenSSL import SSL
55         import socket
56
57         return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
58     except:
59         return False
60
61 class configmanager(object):
62     def __init__(self, fname=None):
63         # Options not exposed on the command line. Command line options will be added
64         # from optparse's parser.
65         self.options = {
66             'admin_passwd': 'admin',
67             'csv_internal_sep': ',',
68             'login_message': False,
69             'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
70             'reportgz': False,
71             'root_path': None,
72         }
73
74         # Not exposed in the configuration file.
75         self.blacklist_for_save = set(
76             ['publisher_warranty_url', 'load_language', 'root_path',
77             'init', 'save', 'config', 'update', 'stop_after_init'])
78
79         # dictionary mapping option destination (keys in self.options) to MyOptions.
80         self.casts = {}
81
82         self.misc = {}
83         self.config_file = fname
84         self.has_ssl = check_ssl()
85
86         self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'TEST', 'DEBUG', 'NOTSET')])
87
88         version = "%s %s" % (release.description, release.version)
89         self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
90
91         # Server startup config
92         group = optparse.OptionGroup(parser, "Common options")
93         group.add_option("-c", "--config", dest="config", help="specify alternate config file")
94         group.add_option("-s", "--save", action="store_true", dest="save", default=False,
95                           help="save configuration to ~/.openerp_serverrc")
96         group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
97         group.add_option("-u", "--update", dest="update",
98                           help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
99         group.add_option("--without-demo", dest="without_demo",
100                           help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
101                           my_default=False)
102         group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
103                         help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.")
104         group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
105         group.add_option("--addons-path", dest="addons_path",
106                          help="specify additional addons paths (separated by commas).",
107                          action="callback", callback=self._check_addons_path, nargs=1, type="string")
108         group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
109         parser.add_option_group(group)
110
111         # XML-RPC / HTTP
112         group = optparse.OptionGroup(parser, "XML-RPC Configuration")
113         group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
114                          help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
115         group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
116                          help="specify the TCP port for the XML-RPC protocol", type="int")
117         group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
118                          help="disable the XML-RPC protocol")
119         group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
120                          help="Enable correct behavior when behind a reverse proxy")
121         parser.add_option_group(group)
122
123         # XML-RPC / HTTPS
124         title = "XML-RPC Secure Configuration"
125         if not self.has_ssl:
126             title += " (disabled as ssl is unavailable)"
127
128         group = optparse.OptionGroup(parser, title)
129         group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
130                          help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
131         group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
132                          help="specify the TCP port for the XML-RPC Secure protocol", type="int")
133         group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
134                          help="disable the XML-RPC Secure protocol")
135         group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
136                          help="specify the certificate file for the SSL connection")
137         group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
138                          help="specify the private key file for the SSL connection")
139         parser.add_option_group(group)
140
141         # NET-RPC
142         group = optparse.OptionGroup(parser, "NET-RPC Configuration")
143         group.add_option("--netrpc-interface", dest="netrpc_interface", my_default='',
144                          help="specify the TCP IP address for the NETRPC protocol")
145         group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
146                          help="specify the TCP port for the NETRPC protocol", type="int")
147         group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
148                          help="disable the NETRPC protocol")
149         parser.add_option_group(group)
150
151         # WEB
152         # TODO move to web addons after MetaOption merge
153         group = optparse.OptionGroup(parser, "Web interface Configuration")
154         group.add_option("--db-filter", dest="dbfilter", default='.*',
155                          help="Filter listed database", metavar="REGEXP")
156         parser.add_option_group(group)
157
158         # Static HTTP
159         group = optparse.OptionGroup(parser, "Static HTTP service")
160         group.add_option("--static-http-enable", dest="static_http_enable", action="store_true", my_default=False, help="enable static HTTP service for serving plain HTML files")
161         group.add_option("--static-http-document-root", dest="static_http_document_root", help="specify the directory containing your static HTML files (e.g '/var/www/')")
162         group.add_option("--static-http-url-prefix", dest="static_http_url_prefix", help="specify the URL root prefix where you want web browsers to access your static HTML files (e.g '/')")
163         parser.add_option_group(group)
164
165         # Testing Group
166         group = optparse.OptionGroup(parser, "Testing Configuration")
167         group.add_option("--test-file", dest="test_file", my_default=False,
168                          help="Launch a YML test file.")
169         group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
170                          help="If set, will save sample of all reports in this directory.")
171         group.add_option("--test-enable", action="store_true", dest="test_enable",
172                          my_default=False, help="Enable YAML and unit tests.")
173         group.add_option("--test-commit", action="store_true", dest="test_commit",
174                          my_default=False, help="Commit database changes performed by YAML or XML tests.")
175         parser.add_option_group(group)
176
177         # Logging Group
178         group = optparse.OptionGroup(parser, "Logging Configuration")
179         group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
180         group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
181         group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
182         group.add_option('--log-handler', action="append", default=[':INFO'], my_default=[':INFO'], metavar="PREFIX:LEVEL", help='setup a handler at LEVEL for a given PREFIX. An empty PREFIX indicates the root logger. This option can be repeated. Example: "openerp.orm:DEBUG" or "werkzeug:CRITICAL" (default: ":INFO")')
183         group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.request:DEBUG')
184         group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG')
185         group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.http:DEBUG')
186         group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
187         # For backward-compatibility, map the old log levels to something
188         # quite close.
189         levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
190             'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
191         group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
192             my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
193
194         parser.add_option_group(group)
195
196         # SMTP Group
197         group = optparse.OptionGroup(parser, "SMTP Configuration")
198         group.add_option('--email-from', dest='email_from', my_default=False,
199                          help='specify the SMTP email address for sending email')
200         group.add_option('--smtp', dest='smtp_server', my_default='localhost',
201                          help='specify the SMTP server for sending email')
202         group.add_option('--smtp-port', dest='smtp_port', my_default=25,
203                          help='specify the SMTP port', type="int")
204         group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
205                          help='specify the SMTP server support SSL or not')
206         group.add_option('--smtp-user', dest='smtp_user', my_default=False,
207                          help='specify the SMTP username for sending email')
208         group.add_option('--smtp-password', dest='smtp_password', my_default=False,
209                          help='specify the SMTP password for sending email')
210         parser.add_option_group(group)
211
212         group = optparse.OptionGroup(parser, "Database related options")
213         group.add_option("-d", "--database", dest="db_name", my_default=False,
214                          help="specify the database name")
215         group.add_option("-r", "--db_user", dest="db_user", my_default=False,
216                          help="specify the database user name")
217         group.add_option("-w", "--db_password", dest="db_password", my_default=False,
218                          help="specify the database password")
219         group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
220         group.add_option("--db_host", dest="db_host", my_default=False,
221                          help="specify the database host")
222         group.add_option("--db_port", dest="db_port", my_default=False,
223                          help="specify the database port", type="int")
224         group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
225                          help="specify the the maximum number of physical connections to posgresql")
226         group.add_option("--db-template", dest="db_template", my_default="template1",
227                          help="specify a custom database template to create a new database")
228         parser.add_option_group(group)
229
230         group = optparse.OptionGroup(parser, "Internationalisation options",
231             "Use these options to translate OpenERP to another language."
232             "See i18n section of the user manual. Option '-d' is mandatory."
233             "Option '-l' is mandatory in case of importation"
234             )
235         group.add_option('--load-language', dest="load_language",
236                          help="specifies the languages for the translations you want to be loaded")
237         group.add_option('-l', "--language", dest="language",
238                          help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
239         group.add_option("--i18n-export", dest="translate_out",
240                          help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
241         group.add_option("--i18n-import", dest="translate_in",
242                          help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
243         group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
244                          help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
245         group.add_option("--modules", dest="translate_modules",
246                          help="specify modules to export. Use in combination with --i18n-export")
247         parser.add_option_group(group)
248
249         security = optparse.OptionGroup(parser, 'Security-related options')
250         security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
251                             help="disable the ability to return the list of databases")
252         parser.add_option_group(security)
253
254         # Advanced options
255         group = optparse.OptionGroup(parser, "Advanced options")
256         group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
257         group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
258                           help="stop the server after its initialization")
259         group.add_option("-t", "--timezone", dest="timezone", my_default=False,
260                          help="specify reference timezone for the server (e.g. Europe/Brussels")
261         group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
262                          help="Force a limit on the maximum number of records kept in the virtual "
263                               "osv_memory tables. The default is False, which means no count-based limit.",
264                          type="int")
265         group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
266                          help="Force a limit on the maximum age of records kept in the virtual "
267                               "osv_memory tables. This is a decimal value expressed in hours, "
268                               "and the default is 1 hour.",
269                          type="float")
270         group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
271                          help="Maximum number of threads processing concurrently cron jobs.",
272                          type="int")
273         group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
274                          help="Use the unaccent function provided by the database when available.")
275         parser.add_option_group(group)
276
277         group = optparse.OptionGroup(parser, "Multiprocessing options")
278         # TODO sensible default for the three following limits.
279         group.add_option("--workers", dest="workers", my_default=0,
280                          help="Specify the number of workers, 0 disable prefork mode.",
281                          type="int")
282         group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
283                          help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request.",
284                          type="int")
285         group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
286                          help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail.",
287                          type="int")
288         group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
289                          help="Maximum allowed CPU time per request.",
290                          type="int")
291         group.add_option("--limit-time-real", dest="limit_time_real", my_default=60,
292                          help="Maximum allowed Real time per request. ",
293                          type="int")
294         group.add_option("--limit-request", dest="limit_request", my_default=8192,
295                          help="Maximum number of request to be processed per worker.",
296                          type="int")
297         parser.add_option_group(group)
298
299         # Copy all optparse options (i.e. MyOption) into self.options.
300         for group in parser.option_groups:
301             for option in group.option_list:
302                 self.options[option.dest] = option.my_default
303                 self.casts[option.dest] = option
304
305         self.parse_config(None, False)
306
307     def parse_config(self, args=None, complete=True):
308         """ Parse the configuration file (if any) and the command-line
309         arguments.
310
311         This method initializes openerp.tools.config and openerp.conf (the
312         former should be removed in the furture) with library-wide
313         configuration values.
314
315         This method must be called before proper usage of this library can be
316         made.
317
318         Typical usage of this method:
319
320             openerp.tools.config.parse_config(sys.argv[1:])
321
322         :param complete: this is a hack used in __init__(), leave it to True.
323
324         """
325         if args is None:
326             args = []
327         opt, args = self.parser.parse_args(args)
328
329         def die(cond, msg):
330             if cond:
331                 self.parser.error(msg)
332
333         # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
334         die(args, "unrecognized parameters: '%s'" % " ".join(args))
335
336         die(bool(opt.syslog) and bool(opt.logfile),
337             "the syslog and logfile options are exclusive")
338
339         die(opt.translate_in and (not opt.language or not opt.db_name),
340             "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
341
342         die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
343             "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
344
345         die(opt.translate_out and (not opt.db_name),
346             "the i18n-export option cannot be used without the database (-d) option")
347
348         # Check if the config file exists (-c used, but not -s)
349         die(not opt.save and opt.config and not os.path.exists(opt.config),
350             "The config file '%s' selected with -c/--config doesn't exist, "\
351             "use -s/--save if you want to generate it"%(opt.config))
352
353         # place/search the config file on Win32 near the server installation
354         # (../etc from the server)
355         # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write,
356         # else he won't be able to save the configurations, or even to start the server...
357         if os.name == 'nt':
358             rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
359         else:
360             rcfilepath = os.path.expanduser('~/.openerp_serverrc')
361
362         self.rcfile = os.path.abspath(
363             self.config_file or opt.config \
364                 or os.environ.get('OPENERP_SERVER') or rcfilepath)
365         self.load()
366
367
368         # Verify that we want to log or not, if not the output will go to stdout
369         if self.options['logfile'] in ('None', 'False'):
370             self.options['logfile'] = False
371         # the same for the pidfile
372         if self.options['pidfile'] in ('None', 'False'):
373             self.options['pidfile'] = False
374
375         # if defined dont take the configfile value even if the defined value is None
376         keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
377                 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
378                 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
379                 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
380                 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
381                 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
382                 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
383                 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
384                 ]
385
386         for arg in keys:
387             # Copy the command-line argument...
388             if getattr(opt, arg):
389                 self.options[arg] = getattr(opt, arg)
390             # ... or keep, but cast, the config file value.
391             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
392                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
393
394         # if defined but None take the configfile value
395         keys = [
396             'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
397             'debug_mode', 'smtp_ssl', 'load_language',
398             'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
399             'list_db', 'xmlrpcs', 'proxy_mode',
400             'test_file', 'test_enable', 'test_commit', 'test_report_directory',
401             'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
402             'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request'
403         ]
404
405         for arg in keys:
406             # Copy the command-line argument...
407             if getattr(opt, arg) is not None:
408                 self.options[arg] = getattr(opt, arg)
409             # ... or keep, but cast, the config file value.
410             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
411                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
412
413         self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
414         if not self.options['addons_path'] or self.options['addons_path']=='None':
415             self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
416         else:
417             self.options['addons_path'] = ",".join(
418                     os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
419                       for x in self.options['addons_path'].split(','))
420
421         self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
422         self.options["demo"] = not opt.without_demo and self.options['init'] or {}
423         self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
424         self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
425         self.options['translate_modules'].sort()
426
427         # TODO checking the type of the parameters should be done for every
428         # parameters, not just the timezone.
429         # The call to get_server_timezone() sets the timezone; this should
430         # probably done here.
431         if self.options['timezone']:
432             # Prevent the timezone to be True. (The config file parsing changes
433             # the string 'True' to the boolean value True. It would be probably
434             # be better to remove that conversion.)
435             die(not isinstance(self.options['timezone'], basestring),
436                 "Invalid timezone value in configuration or environment: %r.\n"
437                 "Please fix this in your configuration." %(self.options['timezone']))
438
439             # If an explicit TZ was provided in the config, make sure it is known
440             try:
441                 import pytz
442                 pytz.timezone(self.options['timezone'])
443             except pytz.UnknownTimeZoneError:
444                 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
445             except:
446                 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
447                 pass
448
449         if opt.pg_path:
450             self.options['pg_path'] = opt.pg_path
451
452         if self.options.get('language', False):
453             if len(self.options['language']) > 5:
454                 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
455
456         if not self.options['db_user']:
457             try:
458                 import getpass
459                 self.options['db_user'] = getpass.getuser()
460             except:
461                 self.options['db_user'] = None
462
463         die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
464
465         if self.options['db_password']:
466             if sys.platform == 'win32' and not self.options['db_host']:
467                 self.options['db_host'] = 'localhost'
468             #if self.options['db_host']:
469             #    self._generate_pgpassfile()
470
471         if opt.save:
472             self.save()
473
474         openerp.conf.max_cron_threads = self.options['max_cron_threads']
475
476         openerp.conf.addons_paths = self.options['addons_path'].split(',')
477         if opt.server_wide_modules:
478             openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
479         else:
480             openerp.conf.server_wide_modules = ['web']
481         if complete:
482             openerp.modules.module.initialize_sys_path()
483             openerp.modules.loading.open_openerp_namespace()
484
485     def _generate_pgpassfile(self):
486         """
487         Generate the pgpass file with the parameters from the command line (db_host, db_user,
488         db_password)
489
490         Used because pg_dump and pg_restore can not accept the password on the command line.
491         """
492         is_win32 = sys.platform == 'win32'
493         if is_win32:
494             filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
495         else:
496             filename = os.path.join(os.environ['HOME'], '.pgpass')
497
498         text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
499
500         if os.path.exists(filename):
501             content = [x.strip() for x in file(filename, 'r').readlines()]
502             if text_to_add in content:
503                 return
504
505         fp = file(filename, 'a+')
506         fp.write(text_to_add + "\n")
507         fp.close()
508
509         if is_win32:
510             try:
511                 import _winreg
512             except ImportError:
513                 _winreg = None
514             x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
515             y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
516             _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
517             _winreg.CloseKey(y)
518             _winreg.CloseKey(x)
519         else:
520             import stat
521             os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
522
523     def _is_addons_path(self, path):
524         for f in os.listdir(path):
525             modpath = os.path.join(path, f)
526             if os.path.isdir(modpath):
527                 def hasfile(filename):
528                     return os.path.isfile(os.path.join(modpath, filename))
529                 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
530                     return True
531         return False
532
533     def _check_addons_path(self, option, opt, value, parser):
534         ad_paths = []
535         for path in value.split(','):
536             path = path.strip()
537             res = os.path.abspath(os.path.expanduser(path))
538             if not os.path.isdir(res):
539                 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
540             if not self._is_addons_path(res):
541                 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
542             ad_paths.append(res)
543
544         setattr(parser.values, option.dest, ",".join(ad_paths))
545
546     def load(self):
547         p = ConfigParser.ConfigParser()
548         try:
549             p.read([self.rcfile])
550             for (name,value) in p.items('options'):
551                 if value=='True' or value=='true':
552                     value = True
553                 if value=='False' or value=='false':
554                     value = False
555                 self.options[name] = value
556             #parse the other sections, as well
557             for sec in p.sections():
558                 if sec == 'options':
559                     continue
560                 if not self.misc.has_key(sec):
561                     self.misc[sec]= {}
562                 for (name, value) in p.items(sec):
563                     if value=='True' or value=='true':
564                         value = True
565                     if value=='False' or value=='false':
566                         value = False
567                     self.misc[sec][name] = value
568         except IOError:
569             pass
570         except ConfigParser.NoSectionError:
571             pass
572
573     def save(self):
574         p = ConfigParser.ConfigParser()
575         loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
576         p.add_section('options')
577         for opt in sorted(self.options.keys()):
578             if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
579                 continue
580             if opt in self.blacklist_for_save:
581                 continue
582             if opt in ('log_level',):
583                 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
584             else:
585                 p.set('options', opt, self.options[opt])
586
587         for sec in sorted(self.misc.keys()):
588             p.add_section(sec)
589             for opt in sorted(self.misc[sec].keys()):
590                 p.set(sec,opt,self.misc[sec][opt])
591
592         # try to create the directories and write the file
593         try:
594             rc_exists = os.path.exists(self.rcfile)
595             if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
596                 os.makedirs(os.path.dirname(self.rcfile))
597             try:
598                 p.write(file(self.rcfile, 'w'))
599                 if not rc_exists:
600                     os.chmod(self.rcfile, 0600)
601             except IOError:
602                 sys.stderr.write("ERROR: couldn't write the config file\n")
603
604         except OSError:
605             # what to do if impossible?
606             sys.stderr.write("ERROR: couldn't create the config directory\n")
607
608     def get(self, key, default=None):
609         return self.options.get(key, default)
610
611     def get_misc(self, sect, key, default=None):
612         return self.misc.get(sect,{}).get(key, default)
613
614     def __setitem__(self, key, value):
615         self.options[key] = value
616
617     def __getitem__(self, key):
618         return self.options[key]
619
620 config = configmanager()
621
622
623 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: