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 |
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): |