cwconfig.py
changeset 1808 aa09e20dd8c0
parent 1802 d628defebc17
child 1948 887ed691c635
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     2 """common configuration utilities for cubicweb
     2 """common configuration utilities for cubicweb
     3 
     3 
     4 :organization: Logilab
     4 :organization: Logilab
     5 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     5 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     7 
       
     8 .. envvar:: CW_CUBES_PATH
       
     9 
       
    10    Augments the default search path for cubes
       
    11 
     7 """
    12 """
     8 __docformat__ = "restructuredtext en"
    13 __docformat__ = "restructuredtext en"
     9 
    14 
    10 import sys
    15 import sys
    11 import os
    16 import os
    16 from logilab.common.logging_ext import set_log_methods, init_log
    21 from logilab.common.logging_ext import set_log_methods, init_log
    17 from logilab.common.configuration import (Configuration, Method,
    22 from logilab.common.configuration import (Configuration, Method,
    18                                           ConfigurationMixIn, merge_options)
    23                                           ConfigurationMixIn, merge_options)
    19 
    24 
    20 from cubicweb import CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, ConfigurationError
    25 from cubicweb import CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, ConfigurationError
    21 from cubicweb.toolsutils import env_path, read_config, create_dir
    26 from cubicweb.toolsutils import env_path, create_dir
    22 
    27 
    23 CONFIGURATIONS = []
    28 CONFIGURATIONS = []
    24 
    29 
    25 _ = unicode
    30 _ = unicode
    26 
    31 
    54     if len(modes) != 1:
    59     if len(modes) != 1:
    55         raise ConfigurationError('unable to guess configuration from %r %s'
    60         raise ConfigurationError('unable to guess configuration from %r %s'
    56                                  % (directory, modes))
    61                                  % (directory, modes))
    57     return modes[0]
    62     return modes[0]
    58 
    63 
    59 # XXX generate this according to the configuration (repository/all-in-one/web)
       
    60 VREGOPTIONS = []
       
    61 for registry in ('etypes', 'hooks', 'controllers', 'actions', 'components',
       
    62                  'views', 'templates', 'boxes', 'contentnavigation', 'urlrewriting',
       
    63                  'facets'):
       
    64     VREGOPTIONS.append(('disable-%s'%registry,
       
    65                         {'type' : 'csv', 'default': (),
       
    66                          'help': 'list of identifier of application objects from the %s registry to disable'%registry,
       
    67                          'group': 'appobjects', 'inputlevel': 2,
       
    68                          }))
       
    69 VREGOPTIONS = tuple(VREGOPTIONS)
       
    70 
    64 
    71 # persistent options definition
    65 # persistent options definition
    72 PERSISTENT_OPTIONS = (
    66 PERSISTENT_OPTIONS = (
    73     ('encoding',
    67     ('encoding',
    74      {'type' : 'string',
    68      {'type' : 'string',
    75       'default': 'UTF-8',
    69       'default': 'UTF-8',
    76       'help': _('user interface encoding'),
    70       'help': _('user interface encoding'),
    77       'group': 'ui', 'sitewide': True,
    71       'group': 'ui', 'sitewide': True,
    78       }),    
    72       }),
    79     ('language',
    73     ('language',
    80      {'type' : 'string',
    74      {'type' : 'string',
    81       'default': 'en',
    75       'default': 'en',
    82       'vocabulary': Method('available_languages'),
    76       'vocabulary': Method('available_languages'),
    83       'help': _('language of the user interface'),
    77       'help': _('language of the user interface'),
    84       'group': 'ui', 
    78       'group': 'ui',
    85       }),
    79       }),
    86     ('date-format',
    80     ('date-format',
    87      {'type' : 'string',
    81      {'type' : 'string',
    88       'default': '%Y/%m/%d',
    82       'default': '%Y/%m/%d',
    89       'help': _('how to format date in the ui ("man strftime" for format description)'),
    83       'help': _('how to format date in the ui ("man strftime" for format description)'),
    90       'group': 'ui', 
    84       'group': 'ui',
    91       }),
    85       }),
    92     ('datetime-format',
    86     ('datetime-format',
    93      {'type' : 'string',
    87      {'type' : 'string',
    94       'default': '%Y/%m/%d %H:%M',
    88       'default': '%Y/%m/%d %H:%M',
    95       'help': _('how to format date and time in the ui ("man strftime" for format description)'),
    89       'help': _('how to format date and time in the ui ("man strftime" for format description)'),
    96       'group': 'ui', 
    90       'group': 'ui',
    97       }),
    91       }),
    98     ('time-format',
    92     ('time-format',
    99      {'type' : 'string',
    93      {'type' : 'string',
   100       'default': '%H:%M',
    94       'default': '%H:%M',
   101       'help': _('how to format time in the ui ("man strftime" for format description)'),
    95       'help': _('how to format time in the ui ("man strftime" for format description)'),
   102       'group': 'ui', 
    96       'group': 'ui',
   103       }),
    97       }),
   104     ('float-format',
    98     ('float-format',
   105      {'type' : 'string',
    99      {'type' : 'string',
   106       'default': '%.3f',
   100       'default': '%.3f',
   107       'help': _('how to format float numbers in the ui'),
   101       'help': _('how to format float numbers in the ui'),
   108       'group': 'ui', 
   102       'group': 'ui',
   109       }),
   103       }),
   110     ('default-text-format',
   104     ('default-text-format',
   111      {'type' : 'choice',
   105      {'type' : 'choice',
   112       'choices': ('text/plain', 'text/rest', 'text/html'),
   106       'choices': ('text/plain', 'text/rest', 'text/html'),
   113       'default': 'text/html', # use fckeditor in the web ui
   107       'default': 'text/html', # use fckeditor in the web ui
   114       'help': _('default text format for rich text fields.'),
   108       'help': _('default text format for rich text fields.'),
   115       'group': 'ui', 
   109       'group': 'ui',
   116       }),
   110       }),
   117     ('short-line-size',
   111     ('short-line-size',
   118      {'type' : 'int',
   112      {'type' : 'int',
   119       'default': 40,
   113       'default': 40,
   120       'help': _('maximum number of characters in short description'),
   114       'help': _('maximum number of characters in short description'),
   123     )
   117     )
   124 
   118 
   125 def register_persistent_options(options):
   119 def register_persistent_options(options):
   126     global PERSISTENT_OPTIONS
   120     global PERSISTENT_OPTIONS
   127     PERSISTENT_OPTIONS = merge_options(PERSISTENT_OPTIONS + options)
   121     PERSISTENT_OPTIONS = merge_options(PERSISTENT_OPTIONS + options)
   128                 
   122 
   129 CFGTYPE2ETYPE_MAP = {
   123 CFGTYPE2ETYPE_MAP = {
   130     'string': 'String',
   124     'string': 'String',
   131     'choice': 'String',
   125     'choice': 'String',
   132     'yn':     'Boolean',
   126     'yn':     'Boolean',
   133     'int':    'Int',
   127     'int':    'Int',
   134     'float' : 'Float',
   128     'float' : 'Float',
   135     }
   129     }
   136     
   130 
   137 class CubicWebNoAppConfiguration(ConfigurationMixIn):
   131 class CubicWebNoAppConfiguration(ConfigurationMixIn):
   138     """base class for cubicweb configuration without a specific instance directory
   132     """base class for cubicweb configuration without a specific instance directory
   139     """
   133     """
   140     __metaclass__ = metaconfiguration
   134     __metaclass__ = metaconfiguration
   141     # to set in concrete configuration
   135     # to set in concrete configuration
   155         CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
   149         CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
   156     else:
   150     else:
   157         mode = 'installed'
   151         mode = 'installed'
   158         CUBES_DIR = '/usr/share/cubicweb/cubes/'
   152         CUBES_DIR = '/usr/share/cubicweb/cubes/'
   159 
   153 
   160     options = VREGOPTIONS + (
   154     options = (
   161        ('log-threshold',
   155        ('log-threshold',
   162          {'type' : 'string', # XXX use a dedicated type?
   156          {'type' : 'string', # XXX use a dedicated type?
   163           'default': 'ERROR',
   157           'default': 'ERROR',
   164           'help': 'server\'s log level',
   158           'help': 'server\'s log level',
   165           'group': 'main', 'inputlevel': 1,
   159           'group': 'main', 'inputlevel': 1,
   193          {'type' : 'string',
   187          {'type' : 'string',
   194           'default': None,
   188           'default': None,
   195           'help': 'web server root url',
   189           'help': 'web server root url',
   196           'group': 'main', 'inputlevel': 1,
   190           'group': 'main', 'inputlevel': 1,
   197           }),
   191           }),
       
   192         ('use-request-subdomain',
       
   193          {'type' : 'yn',
       
   194           'default': None,
       
   195           'help': ('if set, base-url subdomain is replaced by the request\'s '
       
   196                    'host, to help managing sites with several subdomains in a '
       
   197                    'single cubicweb instance'),
       
   198           'group': 'main', 'inputlevel': 1,
       
   199           }),
   198         ('mangle-emails',
   200         ('mangle-emails',
   199          {'type' : 'yn',
   201          {'type' : 'yn',
   200           'default': False,
   202           'default': False,
   201           'help': "don't display actual email addresses but mangle them if \
   203           'help': "don't display actual email addresses but mangle them if \
   202 this option is set to yes",
   204 this option is set to yes",
   203           'group': 'email', 'inputlevel': 2,
   205           'group': 'email', 'inputlevel': 2,
   204           }),
   206           }),
       
   207         ('disable-appobjects',
       
   208          {'type' : 'csv', 'default': (),
       
   209           'help': 'comma separated list of identifiers of application objects (<registry>.<oid>) to disable',
       
   210           'group': 'appobjects', 'inputlevel': 2,
       
   211           }),
   205         )
   212         )
   206     # static and class methods used to get application independant resources ##
   213     # static and class methods used to get application independant resources ##
   207         
   214 
   208     @staticmethod
   215     @staticmethod
   209     def cubicweb_version():
   216     def cubicweb_version():
   210         """return installed cubicweb version"""
   217         """return installed cubicweb version"""
   211         from logilab.common.changelog import Version
   218         from logilab.common.changelog import Version
   212         from cubicweb import __pkginfo__
   219         from cubicweb import __pkginfo__
   213         version = __pkginfo__.numversion
   220         version = __pkginfo__.numversion
   214         assert len(version) == 3, version
   221         assert len(version) == 3, version
   215         return Version(version)
   222         return Version(version)
   216     
   223 
   217     @staticmethod
   224     @staticmethod
   218     def persistent_options_configuration():
   225     def persistent_options_configuration():
   219         return Configuration(options=PERSISTENT_OPTIONS)
   226         return Configuration(options=PERSISTENT_OPTIONS)
   220 
   227 
   221     @classmethod
   228     @classmethod
   224         library views and data may be found)
   231         library views and data may be found)
   225         """
   232         """
   226         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
   233         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
   227             return join(CW_SOFTWARE_ROOT, 'web')
   234             return join(CW_SOFTWARE_ROOT, 'web')
   228         return cls.cube_dir('shared')
   235         return cls.cube_dir('shared')
   229         
   236 
   230     @classmethod
   237     @classmethod
   231     def i18n_lib_dir(cls):
   238     def i18n_lib_dir(cls):
   232         """return application's i18n directory"""
   239         """return application's i18n directory"""
   233         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
   240         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
   234             return join(CW_SOFTWARE_ROOT, 'i18n')
   241             return join(CW_SOFTWARE_ROOT, 'i18n')
   240         for directory in cls.cubes_search_path():
   247         for directory in cls.cubes_search_path():
   241             for cube in os.listdir(directory):
   248             for cube in os.listdir(directory):
   242                 if isdir(join(directory, cube)) and not cube in ('CVS', '.svn', 'shared', '.hg'):
   249                 if isdir(join(directory, cube)) and not cube in ('CVS', '.svn', 'shared', '.hg'):
   243                     cubes.add(cube)
   250                     cubes.add(cube)
   244         return sorted(cubes)
   251         return sorted(cubes)
   245     
   252 
   246     @classmethod
   253     @classmethod
   247     def cubes_search_path(cls):
   254     def cubes_search_path(cls):
   248         """return the path of directories where cubes should be searched"""
   255         """return the path of directories where cubes should be searched"""
   249         path = []
   256         path = []
   250         try:
   257         try:
   255         except KeyError:
   262         except KeyError:
   256             pass
   263             pass
   257         if not cls.CUBES_DIR in path:
   264         if not cls.CUBES_DIR in path:
   258             path.append(cls.CUBES_DIR)
   265             path.append(cls.CUBES_DIR)
   259         return path
   266         return path
   260     
   267 
   261     @classmethod
   268     @classmethod
   262     def cube_dir(cls, cube):
   269     def cube_dir(cls, cube):
   263         """return the cube directory for the given cube id,
   270         """return the cube directory for the given cube id,
   264         raise ConfigurationError if it doesn't exists
   271         raise ConfigurationError if it doesn't exists
   265         """
   272         """
   271 
   278 
   272     @classmethod
   279     @classmethod
   273     def cube_migration_scripts_dir(cls, cube):
   280     def cube_migration_scripts_dir(cls, cube):
   274         """cube migration scripts directory"""
   281         """cube migration scripts directory"""
   275         return join(cls.cube_dir(cube), 'migration')
   282         return join(cls.cube_dir(cube), 'migration')
   276     
   283 
   277     @classmethod
   284     @classmethod
   278     def cube_pkginfo(cls, cube):
   285     def cube_pkginfo(cls, cube):
   279         """return the information module for the given cube"""
   286         """return the information module for the given cube"""
   280         cube = CW_MIGRATION_MAP.get(cube, cube)
   287         cube = CW_MIGRATION_MAP.get(cube, cube)
   281         try:
   288         try:
   284             raise ConfigurationError('unable to find packaging information for '
   291             raise ConfigurationError('unable to find packaging information for '
   285                                      'cube %s (%s: %s)' % (cube, ex.__class__.__name__, ex))
   292                                      'cube %s (%s: %s)' % (cube, ex.__class__.__name__, ex))
   286 
   293 
   287     @classmethod
   294     @classmethod
   288     def cube_version(cls, cube):
   295     def cube_version(cls, cube):
   289         """return the version of the cube located in the given directory        
   296         """return the version of the cube located in the given directory
   290         """
   297         """
   291         from logilab.common.changelog import Version
   298         from logilab.common.changelog import Version
   292         version = cls.cube_pkginfo(cube).numversion
   299         version = cls.cube_pkginfo(cube).numversion
   293         assert len(version) == 3, version
   300         assert len(version) == 3, version
   294         return Version(version)
   301         return Version(version)
   347                         try:
   354                         try:
   348                             deps.remove(cube)
   355                             deps.remove(cube)
   349                         except KeyError:
   356                         except KeyError:
   350                             continue
   357                             continue
   351         return tuple(reversed(cubes))
   358         return tuple(reversed(cubes))
   352     
   359 
   353     @classmethod
   360     @classmethod
   354     def cls_adjust_sys_path(cls):
   361     def cls_adjust_sys_path(cls):
   355         """update python path if necessary"""
   362         """update python path if necessary"""
   356         cubes_parent_dir = normpath(join(cls.CUBES_DIR, '..'))
   363         cubes_parent_dir = normpath(join(cls.CUBES_DIR, '..'))
   357         if not cubes_parent_dir in sys.path:
   364         if not cubes_parent_dir in sys.path:
   385                 try:
   392                 try:
   386                     __import__('cubes.%s' % cube)
   393                     __import__('cubes.%s' % cube)
   387                 except:
   394                 except:
   388                     cls.exception('while loading cube %s', cube)
   395                     cls.exception('while loading cube %s', cube)
   389             else:
   396             else:
   390                 cls.warning('no __init__ file in cube %s', cube) 
   397                 cls.warning('no __init__ file in cube %s', cube)
   391 
   398 
   392     @classmethod
   399     @classmethod
   393     def init_available_cubes(cls):
   400     def init_available_cubes(cls):
   394         """cubes may register some sources (svnfile for instance) in their
   401         """cubes may register some sources (svnfile for instance) in their
   395         __init__ file, so they should be loaded early in the startup process
   402         __init__ file, so they should be loaded early in the startup process
   397         for cube in cls.available_cubes():
   404         for cube in cls.available_cubes():
   398             try:
   405             try:
   399                 __import__('cubes.%s' % cube)
   406                 __import__('cubes.%s' % cube)
   400             except Exception, ex:
   407             except Exception, ex:
   401                 cls.warning("can't init cube %s: %s", cube, ex)
   408                 cls.warning("can't init cube %s: %s", cube, ex)
   402         
   409 
   403     cubicweb_vobject_path = set(['entities'])
   410     cubicweb_vobject_path = set(['entities'])
   404     cube_vobject_path = set(['entities'])
   411     cube_vobject_path = set(['entities'])
   405 
   412 
   406     @classmethod
   413     @classmethod
   407     def build_vregistry_path(cls, templpath, evobjpath=None, tvobjpath=None):
   414     def build_vregistry_path(cls, templpath, evobjpath=None, tvobjpath=None):
   445                 if exists(path):
   452                 if exists(path):
   446                     vregpath.append(path)
   453                     vregpath.append(path)
   447                 elif exists(path + '.py'):
   454                 elif exists(path + '.py'):
   448                     vregpath.append(path + '.py')
   455                     vregpath.append(path + '.py')
   449         return vregpath
   456         return vregpath
   450         
   457 
   451     def __init__(self):
   458     def __init__(self):
   452         ConfigurationMixIn.__init__(self)
   459         ConfigurationMixIn.__init__(self)
   453         self.adjust_sys_path()
   460         self.adjust_sys_path()
   454         self.load_defaults()
   461         self.load_defaults()
   455         self.translations = {} 
   462         self.translations = {}
   456 
   463 
   457     def adjust_sys_path(self):
   464     def adjust_sys_path(self):
   458         self.cls_adjust_sys_path()
   465         self.cls_adjust_sys_path()
   459         
   466 
   460     def init_log(self, logthreshold=None, debug=False, 
   467     def init_log(self, logthreshold=None, debug=False,
   461                  logfile=None, syslog=False):
   468                  logfile=None, syslog=False):
   462         """init the log service"""
   469         """init the log service"""
   463         if logthreshold is None:
   470         if logthreshold is None:
   464             if debug:
   471             if debug:
   465                 logthreshold = 'DEBUG'
   472                 logthreshold = 'DEBUG'
   472     def vregistry_path(self):
   479     def vregistry_path(self):
   473         """return a list of files or directories where the registry will look
   480         """return a list of files or directories where the registry will look
   474         for application objects. By default return nothing in NoApp config.
   481         for application objects. By default return nothing in NoApp config.
   475         """
   482         """
   476         return []
   483         return []
   477     
   484 
   478     def eproperty_definitions(self):
   485     def eproperty_definitions(self):
   479         cfg = self.persistent_options_configuration()
   486         cfg = self.persistent_options_configuration()
   480         for section, options in cfg.options_by_section():
   487         for section, options in cfg.options_by_section():
   481             section = section.lower()
   488             section = section.lower()
   482             for optname, optdict, value in options:
   489             for optname, optdict, value in options:
   485                 default = cfg.option_default(optname, optdict)
   492                 default = cfg.option_default(optname, optdict)
   486                 pdef = {'type': type, 'vocabulary': vocab, 'default': default,
   493                 pdef = {'type': type, 'vocabulary': vocab, 'default': default,
   487                         'help': optdict['help'],
   494                         'help': optdict['help'],
   488                         'sitewide': optdict.get('sitewide', False)}
   495                         'sitewide': optdict.get('sitewide', False)}
   489                 yield key, pdef
   496                 yield key, pdef
   490                 
   497 
   491     def map_option(self, optdict):
   498     def map_option(self, optdict):
   492         try:
   499         try:
   493             vocab = optdict['choices']
   500             vocab = optdict['choices']
   494         except KeyError:
   501         except KeyError:
   495             vocab = optdict.get('vocabulary')
   502             vocab = optdict.get('vocabulary')
   496             if isinstance(vocab, Method):
   503             if isinstance(vocab, Method):
   497                 vocab = getattr(self, vocab.method, ())
   504                 vocab = getattr(self, vocab.method, ())
   498         return CFGTYPE2ETYPE_MAP[optdict['type']], vocab
   505         return CFGTYPE2ETYPE_MAP[optdict['type']], vocab
   499 
   506 
   500     
   507 
   501 class CubicWebConfiguration(CubicWebNoAppConfiguration):
   508 class CubicWebConfiguration(CubicWebNoAppConfiguration):
   502     """base class for cubicweb server and web configurations"""
   509     """base class for cubicweb server and web configurations"""
   503     
   510 
   504     INSTANCE_DATA_DIR = None
   511     INSTANCE_DATA_DIR = None
   505     if CubicWebNoAppConfiguration.mode == 'test':
   512     if CubicWebNoAppConfiguration.mode == 'test':
   506         root = os.environ['APYCOT_ROOT']
   513         root = os.environ['APYCOT_ROOT']
   507         REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
   514         REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
   508         RUNTIME_DIR = '/tmp/'
   515         RUNTIME_DIR = '/tmp/'
   521 
   528 
   522     # for some commands (creation...) we don't want to initialize gettext
   529     # for some commands (creation...) we don't want to initialize gettext
   523     set_language = True
   530     set_language = True
   524     # set this to true to avoid false error message while creating an application
   531     # set this to true to avoid false error message while creating an application
   525     creating = False
   532     creating = False
   526     
   533 
   527     options = CubicWebNoAppConfiguration.options + (
   534     options = CubicWebNoAppConfiguration.options + (
   528         ('log-file',
   535         ('log-file',
   529          {'type' : 'string',
   536          {'type' : 'string',
   530           'default': Method('default_log_file'),
   537           'default': Method('default_log_file'),
   531           'help': 'file where output logs should be written',
   538           'help': 'file where output logs should be written',
   544           'help': 'listening port of the SMTP mail server',
   551           'help': 'listening port of the SMTP mail server',
   545           'group': 'email', 'inputlevel': 1,
   552           'group': 'email', 'inputlevel': 1,
   546           }),
   553           }),
   547         ('sender-name',
   554         ('sender-name',
   548          {'type' : 'string',
   555          {'type' : 'string',
   549           'default': Method('default_application_id'), 
   556           'default': Method('default_application_id'),
   550           'help': 'name used as HELO name for outgoing emails from the \
   557           'help': 'name used as HELO name for outgoing emails from the \
   551 repository.',
   558 repository.',
   552           'group': 'email', 'inputlevel': 2,
   559           'group': 'email', 'inputlevel': 2,
   553           }),
   560           }),
   554         ('sender-addr',
   561         ('sender-addr',
   562 
   569 
   563     @classmethod
   570     @classmethod
   564     def runtime_dir(cls):
   571     def runtime_dir(cls):
   565         """run time directory for pid file..."""
   572         """run time directory for pid file..."""
   566         return env_path('CW_RUNTIME', cls.RUNTIME_DIR, 'run time')
   573         return env_path('CW_RUNTIME', cls.RUNTIME_DIR, 'run time')
   567     
   574 
   568     @classmethod
   575     @classmethod
   569     def registry_dir(cls):
   576     def registry_dir(cls):
   570         """return the control directory"""
   577         """return the control directory"""
   571         return env_path('CW_REGISTRY', cls.REGISTRY_DIR, 'registry')
   578         return env_path('CW_REGISTRY', cls.REGISTRY_DIR, 'registry')
   572 
   579 
   574     def instance_data_dir(cls):
   581     def instance_data_dir(cls):
   575         """return the instance data directory"""
   582         """return the instance data directory"""
   576         return env_path('CW_INSTANCE_DATA',
   583         return env_path('CW_INSTANCE_DATA',
   577                         cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
   584                         cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
   578                         'additional data')
   585                         'additional data')
   579         
   586 
   580     @classmethod
   587     @classmethod
   581     def migration_scripts_dir(cls):
   588     def migration_scripts_dir(cls):
   582         """cubicweb migration scripts directory"""
   589         """cubicweb migration scripts directory"""
   583         return env_path('CW_MIGRATION', cls.MIGRATION_DIR, 'migration')
   590         return env_path('CW_MIGRATION', cls.MIGRATION_DIR, 'migration')
   584 
   591 
   587         """return a configuration instance for the given application identifier
   594         """return a configuration instance for the given application identifier
   588         """
   595         """
   589         config = config or guess_configuration(cls.application_home(appid))
   596         config = config or guess_configuration(cls.application_home(appid))
   590         configcls = configuration_cls(config)
   597         configcls = configuration_cls(config)
   591         return configcls(appid)
   598         return configcls(appid)
   592     
   599 
   593     @classmethod
   600     @classmethod
   594     def possible_configurations(cls, appid):
   601     def possible_configurations(cls, appid):
   595         """return the name of possible configurations for the given
   602         """return the name of possible configurations for the given
   596         application id
   603         application id
   597         """
   604         """
   598         home = cls.application_home(appid)
   605         home = cls.application_home(appid)
   599         return possible_configurations(home)
   606         return possible_configurations(home)
   600     
   607 
   601     @classmethod
   608     @classmethod
   602     def application_home(cls, appid):
   609     def application_home(cls, appid):
   603         """return the home directory of the application with the given
   610         """return the home directory of the application with the given
   604         application id
   611         application id
   605         """
   612         """
   614                'twisted'   : ('common', 'web'),}
   621                'twisted'   : ('common', 'web'),}
   615     @classmethod
   622     @classmethod
   616     def accept_mode(cls, mode):
   623     def accept_mode(cls, mode):
   617         #assert mode in cls.MODES, mode
   624         #assert mode in cls.MODES, mode
   618         return mode in cls.MCOMPAT[cls.name]
   625         return mode in cls.MCOMPAT[cls.name]
   619             
   626 
   620     # default configuration methods ###########################################
   627     # default configuration methods ###########################################
   621     
   628 
   622     def default_application_id(self):
   629     def default_application_id(self):
   623         """return the application identifier, useful for option which need this
   630         """return the application identifier, useful for option which need this
   624         as default value
   631         as default value
   625         """
   632         """
   626         return self.appid
   633         return self.appid
   638                 except IOError:
   645                 except IOError:
   639                     path = '%s-%s.log' % (basepath, i)
   646                     path = '%s-%s.log' % (basepath, i)
   640                     i += 1
   647                     i += 1
   641             return path
   648             return path
   642         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
   649         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
   643     
   650 
   644     def default_pid_file(self):
   651     def default_pid_file(self):
   645         """return default path to the pid file of the application'server"""
   652         """return default path to the pid file of the application'server"""
   646         return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name))
   653         return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name))
   647     
   654 
   648     # instance methods used to get application specific resources #############
   655     # instance methods used to get application specific resources #############
   649     
   656 
   650     def __init__(self, appid):
   657     def __init__(self, appid):
   651         self.appid = appid
   658         self.appid = appid
   652         CubicWebNoAppConfiguration.__init__(self)
   659         CubicWebNoAppConfiguration.__init__(self)
   653         self._cubes = None
   660         self._cubes = None
   654         self._site_loaded = set()
   661         self._site_loaded = set()
   662             sys.path.insert(0, self.apphome)
   669             sys.path.insert(0, self.apphome)
   663 
   670 
   664     @property
   671     @property
   665     def apphome(self):
   672     def apphome(self):
   666         return join(self.registry_dir(), self.appid)
   673         return join(self.registry_dir(), self.appid)
   667     
   674 
   668     @property
   675     @property
   669     def appdatahome(self):
   676     def appdatahome(self):
   670         return join(self.instance_data_dir(), self.appid)
   677         return join(self.instance_data_dir(), self.appid)
   671         
   678 
   672     def init_cubes(self, cubes):
   679     def init_cubes(self, cubes):
   673         assert self._cubes is None
   680         assert self._cubes is None, self._cubes
   674         self._cubes = self.reorder_cubes(cubes)
   681         self._cubes = self.reorder_cubes(cubes)
   675         # load cubes'__init__.py file first
   682         # load cubes'__init__.py file first
   676         for cube in cubes:
   683         for cube in cubes:
   677             __import__('cubes.%s' % cube)
   684             __import__('cubes.%s' % cube)
   678         self.load_site_cubicweb()
   685         self.load_site_cubicweb()
   679         # reload config file in cases options are defined in cubes __init__
   686         # reload config file in cases options are defined in cubes __init__
   680         # or site_cubicweb files
   687         # or site_cubicweb files
   681         self.load_file_configuration(self.main_config_file())
   688         self.load_file_configuration(self.main_config_file())
   682         # configuration initialization hook
   689         # configuration initialization hook
   683         self.load_configuration()
   690         self.load_configuration()
   684         
   691 
   685     def cubes(self):
   692     def cubes(self):
   686         """return the list of cubes used by this instance
   693         """return the list of cubes used by this instance
   687 
   694 
   688         result is ordered from the top level cubes to inner dependencies
   695         result is ordered from the top level cubes to inner dependencies
   689         cubes
   696         cubes
   690         """
   697         """
   691         assert self._cubes is not None
   698         assert self._cubes is not None
   692         return self._cubes
   699         return self._cubes
   693         
   700 
   694     def cubes_path(self):
   701     def cubes_path(self):
   695         """return the list of path to cubes used by this instance, from outer
   702         """return the list of path to cubes used by this instance, from outer
   696         most to inner most cubes
   703         most to inner most cubes
   697         """
   704         """
   698         return [self.cube_dir(p) for p in self.cubes()]
   705         return [self.cube_dir(p) for p in self.cubes()]
   700     def add_cubes(self, cubes):
   707     def add_cubes(self, cubes):
   701         """add given cubes to the list of used cubes"""
   708         """add given cubes to the list of used cubes"""
   702         if not isinstance(cubes, list):
   709         if not isinstance(cubes, list):
   703             cubes = list(cubes)
   710             cubes = list(cubes)
   704         self._cubes = self.reorder_cubes(list(self._cubes) + cubes)
   711         self._cubes = self.reorder_cubes(list(self._cubes) + cubes)
   705         
   712 
   706     def main_config_file(self):
   713     def main_config_file(self):
   707         """return application's control configuration file"""
   714         """return application's control configuration file"""
   708         return join(self.apphome, '%s.conf' % self.name)
   715         return join(self.apphome, '%s.conf' % self.name)
   709             
   716 
   710     def save(self):
   717     def save(self):
   711         """write down current configuration"""
   718         """write down current configuration"""
   712         self.generate_config(open(self.main_config_file(), 'w'))
   719         self.generate_config(open(self.main_config_file(), 'w'))
   713 
   720 
   714     @cached
   721     @cached
   717         infos = []
   724         infos = []
   718         for pkg in self.cubes():
   725         for pkg in self.cubes():
   719             version = self.cube_version(pkg)
   726             version = self.cube_version(pkg)
   720             infos.append('%s-%s' % (pkg, version))
   727             infos.append('%s-%s' % (pkg, version))
   721         return md5.new(';'.join(infos)).hexdigest()
   728         return md5.new(';'.join(infos)).hexdigest()
   722                 
   729 
   723     def load_site_cubicweb(self):
   730     def load_site_cubicweb(self):
   724         """load (web?) application's specific site_cubicweb file"""
   731         """load (web?) application's specific site_cubicweb file"""
   725         for path in reversed([self.apphome] + self.cubes_path()):
   732         for path in reversed([self.apphome] + self.cubes_path()):
   726             sitefile = join(path, 'site_cubicweb.py')
   733             sitefile = join(path, 'site_cubicweb.py')
   727             if exists(sitefile) and not sitefile in self._site_loaded:
   734             if exists(sitefile) and not sitefile in self._site_loaded:
   731                 sitefile = join(path, 'site_erudi.py')
   738                 sitefile = join(path, 'site_erudi.py')
   732                 if exists(sitefile) and not sitefile in self._site_loaded:
   739                 if exists(sitefile) and not sitefile in self._site_loaded:
   733                     self._load_site_cubicweb(sitefile)
   740                     self._load_site_cubicweb(sitefile)
   734                     self._site_loaded.add(sitefile)
   741                     self._site_loaded.add(sitefile)
   735                     self.warning('site_erudi.py is deprecated, should be renamed to site_cubicweb.py')
   742                     self.warning('site_erudi.py is deprecated, should be renamed to site_cubicweb.py')
   736                 
   743 
   737     def _load_site_cubicweb(self, sitefile):
   744     def _load_site_cubicweb(self, sitefile):
   738         context = {}
   745         context = {}
   739         execfile(sitefile, context, context)
   746         execfile(sitefile, context, context)
   740         self.info('%s loaded', sitefile)
   747         self.info('%s loaded', sitefile)
   741         # cube specific options
   748         # cube specific options
   742         if context.get('options'):
   749         if context.get('options'):
   743             self.register_options(context['options'])
   750             self.register_options(context['options'])
   744             self.load_defaults()
   751             self.load_defaults()
   745                 
   752 
   746     def load_configuration(self):
   753     def load_configuration(self):
   747         """load application's configuration files"""
   754         """load application's configuration files"""
   748         super(CubicWebConfiguration, self).load_configuration()
   755         super(CubicWebConfiguration, self).load_configuration()
   749         if self.apphome and self.set_language:
   756         if self.apphome and self.set_language:
   750             # init gettext
   757             # init gettext
   751             self._set_language()
   758             self._set_language()
   752             
   759 
   753     def init_log(self, logthreshold=None, debug=False, force=False):
   760     def init_log(self, logthreshold=None, debug=False, force=False):
   754         """init the log service"""
   761         """init the log service"""
   755         if not force and hasattr(self, '_logging_initialized'):
   762         if not force and hasattr(self, '_logging_initialized'):
   756             return
   763             return
   757         self._logging_initialized = True
   764         self._logging_initialized = True
   773         for path in glob(join(self.apphome, 'i18n',
   780         for path in glob(join(self.apphome, 'i18n',
   774                               '*', 'LC_MESSAGES', 'cubicweb.mo')):
   781                               '*', 'LC_MESSAGES', 'cubicweb.mo')):
   775             lang = path.split(os.sep)[-3]
   782             lang = path.split(os.sep)[-3]
   776             if lang != 'en':
   783             if lang != 'en':
   777                 yield lang
   784                 yield lang
   778         
   785 
   779     def _set_language(self):
   786     def _set_language(self):
   780         """set language for gettext"""
   787         """set language for gettext"""
   781         from gettext import translation
   788         from gettext import translation
   782         path = join(self.apphome, 'i18n')
   789         path = join(self.apphome, 'i18n')
   783         for language in self.available_languages():
   790         for language in self.available_languages():
   785             try:
   792             try:
   786                 tr = translation('cubicweb', path, languages=[language])
   793                 tr = translation('cubicweb', path, languages=[language])
   787                 self.translations[language] = tr.ugettext
   794                 self.translations[language] = tr.ugettext
   788             except (ImportError, AttributeError, IOError):
   795             except (ImportError, AttributeError, IOError):
   789                 self.exception('localisation support error for language %s',
   796                 self.exception('localisation support error for language %s',
   790                                language)            
   797                                language)
   791     
   798 
   792     def vregistry_path(self):
   799     def vregistry_path(self):
   793         """return a list of files or directories where the registry will look
   800         """return a list of files or directories where the registry will look
   794         for application objects
   801         for application objects
   795         """
   802         """
   796         templpath = list(reversed(self.cubes_path()))
   803         templpath = list(reversed(self.cubes_path()))
   800 
   807 
   801     def set_sources_mode(self, sources):
   808     def set_sources_mode(self, sources):
   802         if not 'all' in sources:
   809         if not 'all' in sources:
   803             print 'warning: ignoring specified sources, requires a repository '\
   810             print 'warning: ignoring specified sources, requires a repository '\
   804                   'configuration'
   811                   'configuration'
   805         
   812 
   806     def migration_handler(self):
   813     def migration_handler(self):
   807         """return a migration handler instance"""
   814         """return a migration handler instance"""
   808         from cubicweb.common.migration import MigrationHelper
   815         from cubicweb.common.migration import MigrationHelper
   809         return MigrationHelper(self, verbosity=self.verbosity)
   816         return MigrationHelper(self, verbosity=self.verbosity)
   810 
   817 
   818         sourcedirs = [join(path, 'i18n') for path in self.cubes_path()]
   825         sourcedirs = [join(path, 'i18n') for path in self.cubes_path()]
   819         sourcedirs.append(self.i18n_lib_dir())
   826         sourcedirs.append(self.i18n_lib_dir())
   820         return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
   827         return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
   821 
   828 
   822 set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration'))
   829 set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration'))
   823         
   830 
   824 # alias to get a configuration instance from an application id
   831 # alias to get a configuration instance from an application id
   825 application_configuration = CubicWebConfiguration.config_for        
   832 application_configuration = CubicWebConfiguration.config_for
   826 
   833