cubicweb/web/webconfig.py
changeset 12528 f9b46b63393e
parent 12503 b01dd0ef43aa
child 12529 7276f1c89ddd
equal deleted inserted replaced
12527:bcb895aa2684 12528:f9b46b63393e
    30 
    30 
    31 from logilab.common.decorators import cached, cachedproperty
    31 from logilab.common.decorators import cached, cachedproperty
    32 from logilab.common.configuration import merge_options
    32 from logilab.common.configuration import merge_options
    33 
    33 
    34 from cubicweb import ConfigurationError
    34 from cubicweb import ConfigurationError
    35 from cubicweb.toolsutils import read_config
       
    36 from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options
    35 from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options
    37 
    36 
    38 
    37 
    39 _DATA_DIR = join(dirname(__file__), 'data')
    38 _DATA_DIR = join(dirname(__file__), 'data')
    40 
    39 
    41 
    40 
    42 register_persistent_options( (
    41 register_persistent_options((
    43     # site-wide only web ui configuration
    42     # site-wide only web ui configuration
    44     ('site-title',
    43     ('site-title',
    45      {'type' : 'string', 'default': 'unset title',
    44      {'type': 'string', 'default': 'unset title',
    46       'help': _('site title'),
    45       'help': _('site title'),
    47       'sitewide': True, 'group': 'ui',
    46       'sitewide': True, 'group': 'ui',
    48       }),
    47       }),
    49     ('main-template',
    48     ('main-template',
    50      {'type' : 'string', 'default': 'main-template',
    49      {'type': 'string', 'default': 'main-template',
    51       'help': _('id of main template used to render pages'),
    50       'help': _('id of main template used to render pages'),
    52       'sitewide': True, 'group': 'ui',
    51       'sitewide': True, 'group': 'ui',
    53       }),
    52       }),
    54     # user web ui configuration
    53     # user web ui configuration
    55     ('fckeditor',
    54     ('fckeditor',
    56      {'type' : 'yn', 'default': False,
    55      {'type': 'yn', 'default': False,
    57       'help': _('should html fields being edited using fckeditor (a HTML '
    56       'help': _('should html fields being edited using fckeditor (a HTML '
    58                 'WYSIWYG editor).  You should also select text/html as default '
    57                 'WYSIWYG editor).  You should also select text/html as default '
    59                 'text format to actually get fckeditor.'),
    58                 'text format to actually get fckeditor.'),
    60       'group': 'ui',
    59       'group': 'ui',
    61       }),
    60       }),
    62     # navigation configuration
    61     # navigation configuration
    63     ('page-size',
    62     ('page-size',
    64      {'type' : 'int', 'default': 40,
    63      {'type': 'int', 'default': 40,
    65       'help': _('maximum number of objects displayed by page of results'),
    64       'help': _('maximum number of objects displayed by page of results'),
    66       'group': 'navigation',
    65       'group': 'navigation',
    67       }),
    66       }),
    68     ('related-limit',
    67     ('related-limit',
    69      {'type' : 'int', 'default': 8,
    68      {'type': 'int', 'default': 8,
    70       'help': _('maximum number of related entities to display in the primary '
    69       'help': _('maximum number of related entities to display in the primary '
    71                 'view'),
    70                 'view'),
    72       'group': 'navigation',
    71       'group': 'navigation',
    73       }),
    72       }),
    74     ('combobox-limit',
    73     ('combobox-limit',
    75      {'type' : 'int', 'default': 20,
    74      {'type': 'int', 'default': 20,
    76       'help': _('maximum number of entities to display in related combo box'),
    75       'help': _('maximum number of entities to display in related combo box'),
    77       'group': 'navigation',
    76       'group': 'navigation',
    78       }),
    77       }),
    79 
    78 
    80     ))
    79 ))
    81 
    80 
    82 
    81 
    83 class BaseWebConfiguration(CubicWebConfiguration):
    82 class BaseWebConfiguration(CubicWebConfiguration):
    84     """Base class for web configurations"""
    83     """Base class for web configurations"""
    85     cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['web.views'])
    84     cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['web.views'])
    86     cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['views'])
    85     cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['views'])
    87 
    86 
    88     options = merge_options(CubicWebConfiguration.options + (
    87     options = merge_options(CubicWebConfiguration.options + (
    89         ('repository-uri',
    88         ('repository-uri',
    90          {'type' : 'string',
    89          {'type': 'string',
    91           'default': 'inmemory://',
    90           'default': 'inmemory://',
    92           'help': 'see `cubicweb.dbapi.connect` documentation for possible value',
    91           'help': 'see `cubicweb.dbapi.connect` documentation for possible value',
    93           'group': 'web', 'level': 2,
    92           'group': 'web', 'level': 2,
    94           }),
    93           }),
    95         ('use-uicache',
    94         ('use-uicache',
    96          {'type': 'yn', 'default': True,
    95          {'type': 'yn', 'default': True,
    97           'help': _('should css be compiled and store in uicache'),
    96           'help': _('should css be compiled and store in uicache'),
    98           'group': 'ui', 'level': 2,
    97           'group': 'ui', 'level': 2,
    99           }),
    98           }),
   100         ('anonymous-user',
    99         ('anonymous-user',
   101          {'type' : 'string',
   100          {'type': 'string',
   102           'default': None,
   101           'default': None,
   103           'help': 'login of the CubicWeb user account to use for anonymous user (if you want to allow anonymous)',
   102           'help': ('login of the CubicWeb user account to use for anonymous '
       
   103                    'user (if you want to allow anonymous)'),
   104           'group': 'web', 'level': 1,
   104           'group': 'web', 'level': 1,
   105           }),
   105           }),
   106         ('anonymous-password',
   106         ('anonymous-password',
   107          {'type' : 'string',
   107          {'type': 'string',
   108           'default': None,
   108           'default': None,
   109           'help': 'password of the CubicWeb user account to use for anonymous user, '
   109           'help': 'password of the CubicWeb user account to use for anonymous user, '
   110           'if anonymous-user is set',
   110           'if anonymous-user is set',
   111           'group': 'web', 'level': 1,
   111           'group': 'web', 'level': 1,
   112           }),
   112           }),
   113         ('query-log-file',
   113         ('query-log-file',
   114          {'type' : 'string',
   114          {'type': 'string',
   115           'default': None,
   115           'default': None,
   116           'help': 'web instance query log file',
   116           'help': 'web instance query log file',
   117           'group': 'web', 'level': 3,
   117           'group': 'web', 'level': 3,
   118           }),
   118           }),
   119         ('cleanup-anonymous-session-time',
   119         ('cleanup-anonymous-session-time',
   120          {'type' : 'time',
   120          {'type': 'time',
   121           'default': '5min',
   121           'default': '5min',
   122           'help': 'Same as cleanup-session-time but specific to anonymous '
   122           'help': 'Same as cleanup-session-time but specific to anonymous '
   123           'sessions. You can have a much smaller timeout here since it will be '
   123           'sessions. You can have a much smaller timeout here since it will be '
   124           'transparent to the user. Default to 5min.',
   124           'transparent to the user. Default to 5min.',
   125           'group': 'web', 'level': 3,
   125           'group': 'web', 'level': 3,
   131 
   131 
   132         None may be returned for both if anonymous connection is not
   132         None may be returned for both if anonymous connection is not
   133         allowed or if an empty login is used in configuration
   133         allowed or if an empty login is used in configuration
   134         """
   134         """
   135         try:
   135         try:
   136             user   = self['anonymous-user'] or None
   136             user = self['anonymous-user'] or None
   137             passwd = self['anonymous-password']
   137             passwd = self['anonymous-password']
   138             if user:
   138             if user:
   139                 user = text_type(user)
   139                 user = text_type(user)
   140         except KeyError:
   140         except KeyError:
   141             user, passwd = None, None
   141             user, passwd = None, None
   142         except UnicodeDecodeError:
   142         except UnicodeDecodeError:
   143             raise ConfigurationError("anonymous information should only contains ascii")
   143             raise ConfigurationError("anonymous information should only contains ascii")
   144         return user, passwd
   144         return user, passwd
   145 
       
   146 
   145 
   147 
   146 
   148 class WebConfiguration(BaseWebConfiguration):
   147 class WebConfiguration(BaseWebConfiguration):
   149     """the WebConfiguration is a singleton object handling instance's
   148     """the WebConfiguration is a singleton object handling instance's
   150     configuration and preferences
   149     configuration and preferences
   157                    'If served from a different domain, that domain should allow '
   156                    'If served from a different domain, that domain should allow '
   158                    'cross-origin requests.'),
   157                    'cross-origin requests.'),
   159           'group': 'web',
   158           'group': 'web',
   160           }),
   159           }),
   161         ('auth-mode',
   160         ('auth-mode',
   162          {'type' : 'choice',
   161          {'type': 'choice',
   163           'choices' : ('cookie', 'http'),
   162           'choices': ('cookie', 'http'),
   164           'default': 'cookie',
   163           'default': 'cookie',
   165           'help': 'authentication mode (cookie / http)',
   164           'help': 'authentication mode (cookie / http)',
   166           'group': 'web', 'level': 3,
   165           'group': 'web', 'level': 3,
   167           }),
   166           }),
   168         ('realm',
   167         ('realm',
   169          {'type' : 'string',
   168          {'type': 'string',
   170           'default': 'cubicweb',
   169           'default': 'cubicweb',
   171           'help': 'realm to use on HTTP authentication mode',
   170           'help': 'realm to use on HTTP authentication mode',
   172           'group': 'web', 'level': 3,
   171           'group': 'web', 'level': 3,
   173           }),
   172           }),
   174         ('http-session-time',
   173         ('http-session-time',
   175          {'type' : 'time',
   174          {'type': 'time',
   176           'default': 0,
   175           'default': 0,
   177           'help': "duration of the cookie used to store session identifier. "
   176           'help': "duration of the cookie used to store session identifier. "
   178           "If 0, the cookie will expire when the user exist its browser. "
   177           "If 0, the cookie will expire when the user exist its browser. "
   179           "Should be 0 or greater than repository\'s session-time.",
   178           "Should be 0 or greater than repository\'s session-time.",
   180           'group': 'web', 'level': 2,
   179           'group': 'web', 'level': 2,
   181           }),
   180           }),
   182         ('submit-mail',
   181         ('submit-mail',
   183          {'type' : 'string',
   182          {'type': 'string',
   184           'default': None,
   183           'default': None,
   185           'help': ('Mail used as recipient to report bug in this instance, '
   184           'help': ('Mail used as recipient to report bug in this instance, '
   186                    'if you want this feature on'),
   185                    'if you want this feature on'),
   187           'group': 'web', 'level': 2,
   186           'group': 'web', 'level': 2,
   188           }),
   187           }),
   189 
   188 
   190         ('language-mode',
   189         ('language-mode',
   191          {'type' : 'choice',
   190          {'type': 'choice',
   192           'choices': ('http-negotiation', 'url-prefix', ''),
   191           'choices': ('http-negotiation', 'url-prefix', ''),
   193           'default': 'http-negotiation',
   192           'default': 'http-negotiation',
   194           'help': ('source for interface\'s language detection. '
   193           'help': ('source for interface\'s language detection. '
   195                    'If set to "http-negotiation" the Accept-Language HTTP header will be used,'
   194                    'If set to "http-negotiation" the Accept-Language HTTP header will be used,'
   196                    ' if set to "url-prefix", the URL will be inspected for a short language prefix.'),
   195                    ' if set to "url-prefix", the URL will be inspected for a'
       
   196                    ' short language prefix.'),
   197           'group': 'web', 'level': 2,
   197           'group': 'web', 'level': 2,
   198           }),
   198           }),
   199 
   199 
   200         ('print-traceback',
   200         ('print-traceback',
   201          {'type' : 'yn',
   201          {'type': 'yn',
   202           'default': CubicWebConfiguration.mode != 'system',
   202           'default': CubicWebConfiguration.mode != 'system',
   203           'help': 'print the traceback on the error page when an error occurred',
   203           'help': 'print the traceback on the error page when an error occurred',
   204           'group': 'web', 'level': 2,
   204           'group': 'web', 'level': 2,
   205           }),
   205           }),
   206 
   206 
   207         ('captcha-font-file',
   207         ('captcha-font-file',
   208          {'type' : 'string',
   208          {'type': 'string',
   209           'default': join(_DATA_DIR, 'porkys.ttf'),
   209           'default': join(_DATA_DIR, 'porkys.ttf'),
   210           'help': 'True type font to use for captcha image generation (you \
   210           'help': 'True type font to use for captcha image generation (you \
   211 must have the python imaging library installed to use captcha)',
   211 must have the python imaging library installed to use captcha)',
   212           'group': 'web', 'level': 3,
   212           'group': 'web', 'level': 3,
   213           }),
   213           }),
   214         ('captcha-font-size',
   214         ('captcha-font-size',
   215          {'type' : 'int',
   215          {'type': 'int',
   216           'default': 25,
   216           'default': 25,
   217           'help': 'Font size to use for captcha image generation (you must \
   217           'help': 'Font size to use for captcha image generation (you must \
   218 have the python imaging library installed to use captcha)',
   218 have the python imaging library installed to use captcha)',
   219           'group': 'web', 'level': 3,
   219           'group': 'web', 'level': 3,
   220           }),
   220           }),
   221 
   221 
   222         ('concat-resources',
   222         ('concat-resources',
   223          {'type' : 'yn',
   223          {'type': 'yn',
   224           'default': False,
   224           'default': False,
   225           'help': 'use modconcat-like URLS to concat and serve JS / CSS files',
   225           'help': 'use modconcat-like URLS to concat and serve JS / CSS files',
   226           'group': 'web', 'level': 2,
   226           'group': 'web', 'level': 2,
   227           }),
   227           }),
   228         ('anonymize-jsonp-queries',
   228         ('anonymize-jsonp-queries',
   242           'default': None,
   242           'default': None,
   243           'help': 'The static data resource directory path.',
   243           'help': 'The static data resource directory path.',
   244           'group': 'web', 'level': 2,
   244           'group': 'web', 'level': 2,
   245           }),
   245           }),
   246         ('access-control-allow-origin',
   246         ('access-control-allow-origin',
   247          {'type' : 'csv',
   247          {'type': 'csv',
   248           'default': (),
   248           'default': (),
   249           'help':('comma-separated list of allowed origin domains or "*" for any domain'),
   249           'help': ('comma-separated list of allowed origin domains or "*" for any domain'),
   250           'group': 'web', 'level': 2,
   250           'group': 'web', 'level': 2,
   251           }),
   251           }),
   252         ('access-control-allow-methods',
   252         ('access-control-allow-methods',
   253          {'type' : 'csv',
   253          {'type': 'csv',
   254           'default': (),
   254           'default': (),
   255           'help': ('comma-separated list of allowed HTTP methods'),
   255           'help': ('comma-separated list of allowed HTTP methods'),
   256           'group': 'web', 'level': 2,
   256           'group': 'web', 'level': 2,
   257           }),
   257           }),
   258         ('access-control-max-age',
   258         ('access-control-max-age',
   259          {'type' : 'int',
   259          {'type': 'int',
   260           'default': None,
   260           'default': None,
   261           'help': ('maximum age of cross-origin resource sharing (in seconds)'),
   261           'help': ('maximum age of cross-origin resource sharing (in seconds)'),
   262           'group': 'web', 'level': 2,
   262           'group': 'web', 'level': 2,
   263           }),
   263           }),
   264         ('access-control-expose-headers',
   264         ('access-control-expose-headers',
   265          {'type' : 'csv',
   265          {'type': 'csv',
   266           'default': (),
   266           'default': (),
   267           'help':('comma-separated list of HTTP headers the application declare in response to a preflight request'),
   267           'help': ('comma-separated list of HTTP headers the application '
       
   268                    'declare in response to a preflight request'),
   268           'group': 'web', 'level': 2,
   269           'group': 'web', 'level': 2,
   269           }),
   270           }),
   270         ('access-control-allow-headers',
   271         ('access-control-allow-headers',
   271          {'type' : 'csv',
   272          {'type': 'csv',
   272           'default': (),
   273           'default': (),
   273           'help':('comma-separated list of HTTP headers the application may set in the response'),
   274           'help': ('comma-separated list of HTTP headers the application may set in the response'),
   274           'group': 'web', 'level': 2,
   275           'group': 'web', 'level': 2,
   275           }),
   276           }),
   276         ))
   277     ))
   277 
   278 
   278     def __init__(self, *args, **kwargs):
   279     def __init__(self, *args, **kwargs):
   279         super(WebConfiguration, self).__init__(*args, **kwargs)
   280         super(WebConfiguration, self).__init__(*args, **kwargs)
   280         self.uiprops = None
   281         self.uiprops = None
   281         self.datadir_url = None
   282         self.datadir_url = None
   342                 # @import('cubicweb.css') in css
   343                 # @import('cubicweb.css') in css
   343                 warn('[3.20] cubicweb.old.css has been renamed back to cubicweb.css',
   344                 warn('[3.20] cubicweb.old.css has been renamed back to cubicweb.css',
   344                      DeprecationWarning)
   345                      DeprecationWarning)
   345                 rid = 'cubicweb.css'
   346                 rid = 'cubicweb.css'
   346             return self.ensure_uid_directory(
   347             return self.ensure_uid_directory(
   347                         self.uiprops.process_resource(
   348                 self.uiprops.process_resource(
   348                              join(directory, rdirectory), rid)), rid
   349                     join(directory, rdirectory), rid)), rid
   349         return join(directory, rdirectory), rid
   350         return join(directory, rdirectory), rid
   350 
   351 
   351     def locate_all_files(self, rid, rdirectory='wdoc'):
   352     def locate_all_files(self, rid, rdirectory='wdoc'):
   352         """return all files corresponding to the given resource"""
   353         """return all files corresponding to the given resource"""
   353         path = [self.apphome] + self.cubes_path() + [dirname(__file__)]
   354         path = [self.apphome] + self.cubes_path() + [dirname(__file__)]
   401         uiprops.load(libuiprops)
   402         uiprops.load(libuiprops)
   402         for path in reversed([self.apphome] + self.cubes_path()):
   403         for path in reversed([self.apphome] + self.cubes_path()):
   403             self._load_ui_properties_file(uiprops, path)
   404             self._load_ui_properties_file(uiprops, path)
   404         self._load_ui_properties_file(uiprops, self.apphome)
   405         self._load_ui_properties_file(uiprops, self.apphome)
   405         datadir_url = uiprops.context['datadir_url']
   406         datadir_url = uiprops.context['datadir_url']
   406         if (datadir_url+'/cubicweb.old.css') in uiprops['STYLESHEETS']:
   407         if (datadir_url + '/cubicweb.old.css') in uiprops['STYLESHEETS']:
   407             warn('[3.20] cubicweb.old.css has been renamed back to cubicweb.css',
   408             warn('[3.20] cubicweb.old.css has been renamed back to cubicweb.css',
   408                  DeprecationWarning)
   409                  DeprecationWarning)
   409             idx = uiprops['STYLESHEETS'].index(datadir_url+'/cubicweb.old.css')
   410             idx = uiprops['STYLESHEETS'].index(datadir_url + '/cubicweb.old.css')
   410             uiprops['STYLESHEETS'][idx] = datadir_url+'/cubicweb.css'
   411             uiprops['STYLESHEETS'][idx] = datadir_url + '/cubicweb.css'
   411         if datadir_url+'/cubicweb.reset.css' in uiprops['STYLESHEETS']:
   412         if datadir_url + '/cubicweb.reset.css' in uiprops['STYLESHEETS']:
   412             warn('[3.20] cubicweb.reset.css is obsolete', DeprecationWarning)
   413             warn('[3.20] cubicweb.reset.css is obsolete', DeprecationWarning)
   413             uiprops['STYLESHEETS'].remove(datadir_url+'/cubicweb.reset.css')
   414             uiprops['STYLESHEETS'].remove(datadir_url + '/cubicweb.reset.css')
   414         cubicweb_js_url = datadir_url + '/cubicweb.js'
   415         cubicweb_js_url = datadir_url + '/cubicweb.js'
   415         if cubicweb_js_url not in uiprops['JAVASCRIPTS']:
   416         if cubicweb_js_url not in uiprops['JAVASCRIPTS']:
   416             uiprops['JAVASCRIPTS'].insert(0, cubicweb_js_url)
   417             uiprops['JAVASCRIPTS'].insert(0, cubicweb_js_url)
   417 
   418 
   418     def _load_ui_properties_file(self, uiprops, path):
   419     def _load_ui_properties_file(self, uiprops, path):