web/webconfig.py
brancholdstable
changeset 6665 90f2f20367bc
parent 6424 f443a2b8a5c7
child 6425 8d7c2fd2ac66
equal deleted inserted replaced
6018:f4d1d5d9ccbb 6665:90f2f20367bc
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """common web configuration for twisted/modpython instances
    18 """web ui configuration for cubicweb instances"""
    19 
    19 
    20 """
       
    21 __docformat__ = "restructuredtext en"
    20 __docformat__ = "restructuredtext en"
    22 _ = unicode
    21 _ = unicode
    23 
    22 
    24 import os
    23 import os
    25 from os.path import join, exists, split
    24 from os.path import join, exists, split
       
    25 from warnings import warn
    26 
    26 
    27 from logilab.common.decorators import cached
    27 from logilab.common.decorators import cached
       
    28 from logilab.common.deprecation import deprecated
    28 
    29 
    29 from cubicweb.toolsutils import read_config
    30 from cubicweb.toolsutils import read_config
    30 from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options, merge_options
    31 from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options, merge_options
    31 
    32 
    32 
    33 
    75     """the WebConfiguration is a singleton object handling instance's
    76     """the WebConfiguration is a singleton object handling instance's
    76     configuration and preferences
    77     configuration and preferences
    77     """
    78     """
    78     cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set([join('web', 'views')])
    79     cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set([join('web', 'views')])
    79     cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['views'])
    80     cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['views'])
       
    81     uiprops = {'FCKEDITOR_PATH': ''}
    80 
    82 
    81     options = merge_options(CubicWebConfiguration.options + (
    83     options = merge_options(CubicWebConfiguration.options + (
    82         ('anonymous-user',
    84         ('anonymous-user',
    83          {'type' : 'string',
    85          {'type' : 'string',
    84           'default': None,
    86           'default': None,
   184           }),
   186           }),
   185 
   187 
   186         ('print-traceback',
   188         ('print-traceback',
   187          {'type' : 'yn',
   189          {'type' : 'yn',
   188           'default': CubicWebConfiguration.mode != 'system',
   190           'default': CubicWebConfiguration.mode != 'system',
   189           'help': 'print the traceback on the error page when an error occured',
   191           'help': 'print the traceback on the error page when an error occurred',
   190           'group': 'web', 'level': 2,
   192           'group': 'web', 'level': 2,
   191           }),
   193           }),
   192 
   194 
   193         ('captcha-font-file',
   195         ('captcha-font-file',
   194          {'type' : 'string',
   196          {'type' : 'string',
   203           'help': 'Font size to use for captcha image generation (you must \
   205           'help': 'Font size to use for captcha image generation (you must \
   204 have the python imaging library installed to use captcha)',
   206 have the python imaging library installed to use captcha)',
   205           'group': 'web', 'level': 3,
   207           'group': 'web', 'level': 3,
   206           }),
   208           }),
   207 
   209 
       
   210         ('use-old-css',
       
   211          {'type' : 'yn',
       
   212           'default': True,
       
   213           'help': 'use cubicweb.old.css instead of 3.9 cubicweb.css',
       
   214           'group': 'web', 'level': 2,
       
   215           }),
   208         ))
   216         ))
   209 
   217 
   210     def fckeditor_installed(self):
   218     def fckeditor_installed(self):
   211         return exists(self.ext_resources['FCKEDITOR_PATH'])
   219         return exists(self.uiprops['FCKEDITOR_PATH'])
   212 
   220 
   213     def eproperty_definitions(self):
   221     def eproperty_definitions(self):
   214         for key, pdef in super(WebConfiguration, self).eproperty_definitions():
   222         for key, pdef in super(WebConfiguration, self).eproperty_definitions():
   215             if key == 'ui.fckeditor' and not self.fckeditor_installed():
   223             if key == 'ui.fckeditor' and not self.fckeditor_installed():
   216                 continue
   224                 continue
   226         """return the instance's repository object"""
   234         """return the instance's repository object"""
   227         try:
   235         try:
   228             return self.__repo
   236             return self.__repo
   229         except AttributeError:
   237         except AttributeError:
   230             from cubicweb.dbapi import get_repository
   238             from cubicweb.dbapi import get_repository
   231             if self.repo_method == 'inmemory':
   239             repo = get_repository(self.repo_method, vreg=vreg, config=self)
   232                 repo = get_repository('inmemory', vreg=vreg, config=self)
       
   233             else:
       
   234                 repo = get_repository('pyro', self['pyro-instance-id'],
       
   235                                       config=self)
       
   236             self.__repo = repo
   240             self.__repo = repo
   237             return repo
   241             return repo
   238 
   242 
   239     def vc_config(self):
   243     def vc_config(self):
   240         return self.repository().get_versions()
   244         return self.repository().get_versions()
   241 
       
   242     # mapping to external resources (id -> path) (`external_resources` file) ##
       
   243     ext_resources = {
       
   244         'FAVICON':  'DATADIR/favicon.ico',
       
   245         'LOGO':     'DATADIR/logo.png',
       
   246         'RSS_LOGO': 'DATADIR/rss.png',
       
   247         'HELP':     'DATADIR/help.png',
       
   248         'CALENDAR_ICON': 'DATADIR/calendar.gif',
       
   249         'SEARCH_GO':'DATADIR/go.png',
       
   250 
       
   251         'FCKEDITOR_PATH':  '/usr/share/fckeditor/',
       
   252 
       
   253         'IE_STYLESHEETS':    ['DATADIR/cubicweb.ie.css'],
       
   254         'STYLESHEETS':       ['DATADIR/cubicweb.css'],
       
   255         'STYLESHEETS_PRINT': ['DATADIR/cubicweb.print.css'],
       
   256 
       
   257         'JAVASCRIPTS':       ['DATADIR/jquery.js',
       
   258                               'DATADIR/jquery.corner.js',
       
   259                               'DATADIR/jquery.json.js',
       
   260                               'DATADIR/cubicweb.compat.js',
       
   261                               'DATADIR/cubicweb.python.js',
       
   262                               'DATADIR/cubicweb.htmlhelpers.js'],
       
   263         }
       
   264 
       
   265 
   245 
   266     def anonymous_user(self):
   246     def anonymous_user(self):
   267         """return a login and password to use for anonymous users. None
   247         """return a login and password to use for anonymous users. None
   268         may be returned for both if anonymous connections are not allowed
   248         may be returned for both if anonymous connections are not allowed
   269         """
   249         """
   274             user, passwd = None, None
   254             user, passwd = None, None
   275         if user is not None:
   255         if user is not None:
   276             user = unicode(user)
   256             user = unicode(user)
   277         return user, passwd
   257         return user, passwd
   278 
   258 
   279     def has_resource(self, rid):
       
   280         """return true if an external resource is defined"""
       
   281         return bool(self.ext_resources.get(rid))
       
   282 
       
   283     @cached
       
   284     def locate_resource(self, rid):
   259     def locate_resource(self, rid):
   285         """return the directory where the given resource may be found"""
   260         """return the (directory, filename) where the given resource
       
   261         may be found
       
   262         """
   286         return self._fs_locate(rid, 'data')
   263         return self._fs_locate(rid, 'data')
   287 
   264 
   288     @cached
       
   289     def locate_doc_file(self, fname):
   265     def locate_doc_file(self, fname):
   290         """return the directory where the given resource may be found"""
   266         """return the directory where the given resource may be found"""
   291         return self._fs_locate(fname, 'wdoc')
   267         return self._fs_locate(fname, 'wdoc')[0]
   292 
   268 
   293     def _fs_locate(self, rid, rdirectory):
   269     @cached
       
   270     def _fs_path_locate(self, rid, rdirectory):
   294         """return the directory where the given resource may be found"""
   271         """return the directory where the given resource may be found"""
   295         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
   272         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
   296         for directory in path:
   273         for directory in path:
   297             if exists(join(directory, rdirectory, rid)):
   274             if exists(join(directory, rdirectory, rid)):
   298                 return join(directory, rdirectory)
   275                 return directory
       
   276 
       
   277     def _fs_locate(self, rid, rdirectory):
       
   278         """return the (directory, filename) where the given resource
       
   279         may be found
       
   280         """
       
   281         directory = self._fs_path_locate(rid, rdirectory)
       
   282         if directory is None:
       
   283             return None, None
       
   284         if rdirectory == 'data' and rid.endswith('.css'):
       
   285             if self['use-old-css'] and rid == 'cubicweb.css':
       
   286                 # @import('cubicweb.css') in css
       
   287                 rid = 'cubicweb.old.css'
       
   288             return self.uiprops.process_resource(join(directory, rdirectory), rid), rid
       
   289         return join(directory, rdirectory), rid
   299 
   290 
   300     def locate_all_files(self, rid, rdirectory='wdoc'):
   291     def locate_all_files(self, rid, rdirectory='wdoc'):
   301         """return all files corresponding to the given resource"""
   292         """return all files corresponding to the given resource"""
   302         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
   293         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
   303         for directory in path:
   294         for directory in path:
   307 
   298 
   308     def load_configuration(self):
   299     def load_configuration(self):
   309         """load instance's configuration files"""
   300         """load instance's configuration files"""
   310         super(WebConfiguration, self).load_configuration()
   301         super(WebConfiguration, self).load_configuration()
   311         # load external resources definition
   302         # load external resources definition
   312         self._build_ext_resources()
       
   313         self._init_base_url()
   303         self._init_base_url()
       
   304         self._build_ui_properties()
   314 
   305 
   315     def _init_base_url(self):
   306     def _init_base_url(self):
   316         # normalize base url(s)
   307         # normalize base url(s)
   317         baseurl = self['base-url'] or self.default_base_url()
   308         baseurl = self['base-url'] or self.default_base_url()
   318         if baseurl and baseurl[-1] != '/':
   309         if baseurl and baseurl[-1] != '/':
   319             baseurl += '/'
   310             baseurl += '/'
   320         if not self.repairing:
   311         if not self.repairing:
   321             self.global_set_option('base-url', baseurl)
   312             self.global_set_option('base-url', baseurl)
   322         httpsurl = self['https-url']
   313         httpsurl = self['https-url']
   323         if httpsurl and httpsurl[-1] != '/':
   314         if httpsurl:
   324             httpsurl += '/'
   315             if httpsurl[-1] != '/':
   325             if not self.repairing:
   316                 httpsurl += '/'
   326                 self.global_set_option('https-url', httpsurl)
   317                 if not self.repairing:
   327 
   318                     self.global_set_option('https-url', httpsurl)
   328     def _build_ext_resources(self):
   319             if self.debugmode:
   329         libresourcesfile = join(self.shared_dir(), 'data', 'external_resources')
   320                 self.https_datadir_url = httpsurl + 'data/'
   330         self.ext_resources.update(read_config(libresourcesfile))
   321             else:
       
   322                 self.https_datadir_url = httpsurl + 'data%s/' % self.instance_md5_version()
       
   323         if self.debugmode:
       
   324             self.datadir_url = baseurl + 'data/'
       
   325         else:
       
   326             self.datadir_url = baseurl + 'data%s/' % self.instance_md5_version()
       
   327 
       
   328     def _build_ui_properties(self):
       
   329         # self.datadir_url[:-1] to remove trailing /
       
   330         from cubicweb.web.propertysheet import PropertySheet
       
   331         cachedir = join(self.appdatahome, 'uicache')
       
   332         self.check_writeable_uid_directory(cachedir)
       
   333         self.uiprops = PropertySheet(
       
   334             cachedir,
       
   335             data=lambda x: self.datadir_url + x,
       
   336             datadir_url=self.datadir_url[:-1])
       
   337         self._init_uiprops(self.uiprops)
       
   338         if self['https-url']:
       
   339             cachedir = join(self.appdatahome, 'uicachehttps')
       
   340             self.check_writeable_uid_directory(cachedir)
       
   341             self.https_uiprops = PropertySheet(
       
   342                 cachedir,
       
   343                 data=lambda x: self.https_datadir_url + x,
       
   344                 datadir_url=self.https_datadir_url[:-1])
       
   345             self._init_uiprops(self.https_uiprops)
       
   346 
       
   347     def _init_uiprops(self, uiprops):
       
   348         libuiprops = join(self.shared_dir(), 'data', 'uiprops.py')
       
   349         uiprops.load(libuiprops)
   331         for path in reversed([self.apphome] + self.cubes_path()):
   350         for path in reversed([self.apphome] + self.cubes_path()):
   332             resourcesfile = join(path, 'data', 'external_resources')
   351             self._load_ui_properties_file(uiprops, path)
   333             if exists(resourcesfile):
   352         self._load_ui_properties_file(uiprops, self.apphome)
   334                 self.debug('loading %s', resourcesfile)
   353         datadir_url = uiprops.context['datadir_url']
   335                 self.ext_resources.update(read_config(resourcesfile))
   354         # XXX pre 3.9 css compat
   336         resourcesfile = join(self.apphome, 'external_resources')
   355         if self['use-old-css']:
       
   356             if (datadir_url+'/cubicweb.css') in uiprops['STYLESHEETS']:
       
   357                 idx = uiprops['STYLESHEETS'].index(datadir_url+'/cubicweb.css')
       
   358                 uiprops['STYLESHEETS'][idx] = datadir_url+'/cubicweb.old.css'
       
   359             if datadir_url+'/cubicweb.reset.css' in uiprops['STYLESHEETS']:
       
   360                 uiprops['STYLESHEETS'].remove(datadir_url+'/cubicweb.reset.css')
       
   361         cubicweb_js_url = datadir_url + '/cubicweb.js'
       
   362         if cubicweb_js_url not in uiprops['JAVASCRIPTS']:
       
   363             uiprops['JAVASCRIPTS'].insert(0, cubicweb_js_url)
       
   364 
       
   365     def _load_ui_properties_file(self, uiprops, path):
       
   366         resourcesfile = join(path, 'data', 'external_resources')
   337         if exists(resourcesfile):
   367         if exists(resourcesfile):
   338             self.debug('loading %s', resourcesfile)
   368             warn('[3.9] %s file is deprecated, use an uiprops.py file'
   339             self.ext_resources.update(read_config(resourcesfile))
   369                  % resourcesfile, DeprecationWarning)
   340         for resource in ('STYLESHEETS', 'STYLESHEETS_PRINT',
   370             datadir_url = uiprops.context['datadir_url']
   341                          'IE_STYLESHEETS', 'JAVASCRIPTS'):
   371             for rid, val in read_config(resourcesfile).iteritems():
   342             val = self.ext_resources[resource]
   372                 if rid in ('STYLESHEETS', 'STYLESHEETS_PRINT',
   343             if isinstance(val, str):
   373                            'IE_STYLESHEETS', 'JAVASCRIPTS'):
   344                 files = [w.strip() for w in val.split(',') if w.strip()]
   374                     val = [w.strip().replace('DATADIR', datadir_url)
   345                 self.ext_resources[resource] = files
   375                            for w in val.split(',') if w.strip()]
       
   376                     if rid == 'IE_STYLESHEETS':
       
   377                         rid = 'STYLESHEETS_IE'
       
   378                 else:
       
   379                     val = val.strip().replace('DATADIR', datadir_url)
       
   380                 uiprops[rid] = val
       
   381         uipropsfile = join(path, 'uiprops.py')
       
   382         if exists(uipropsfile):
       
   383             self.debug('loading %s', uipropsfile)
       
   384             uiprops.load(uipropsfile)
   346 
   385 
   347     # static files handling ###################################################
   386     # static files handling ###################################################
   348 
   387 
   349     @property
   388     @property
   350     def static_directory(self):
   389     def static_directory(self):
   367         stream.close()
   406         stream.close()
   368 
   407 
   369     def static_file_del(self, rpath):
   408     def static_file_del(self, rpath):
   370         if self.static_file_exists(rpath):
   409         if self.static_file_exists(rpath):
   371             os.remove(join(self.static_directory, rpath))
   410             os.remove(join(self.static_directory, rpath))
       
   411 
       
   412     @deprecated('[3.9] use _cw.uiprops.get(rid)')
       
   413     def has_resource(self, rid):
       
   414         """return true if an external resource is defined"""
       
   415         return bool(self.uiprops.get(rid))