1 # copyright 2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
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/>. |
|
18 """property sheets allowing configuration of the web ui""" |
|
19 |
|
20 __docformat__ = "restructuredtext en" |
|
21 |
|
22 import re |
|
23 import os |
|
24 import os.path as osp |
|
25 import tempfile |
|
26 |
|
27 |
|
28 TYPE_CHECKS = [('STYLESHEETS', list), ('JAVASCRIPTS', list), |
|
29 ('STYLESHEETS_IE', list), ('STYLESHEETS_PRINT', list), |
|
30 ] |
|
31 |
|
32 class lazystr(object): |
|
33 def __init__(self, string, context): |
|
34 self.string = string |
|
35 self.context = context |
|
36 def __str__(self): |
|
37 return self.string % self.context |
|
38 |
|
39 |
|
40 class PropertySheet(dict): |
|
41 def __init__(self, cache_directory, **context): |
|
42 self._cache_directory = cache_directory |
|
43 self.context = context |
|
44 self.reset() |
|
45 context['sheet'] = self |
|
46 context['lazystr'] = self.lazystr |
|
47 self._percent_rgx = re.compile('%(?!\()') |
|
48 |
|
49 def lazystr(self, str): |
|
50 return lazystr(str, self) |
|
51 |
|
52 def reset(self): |
|
53 self.clear() |
|
54 self._ordered_propfiles = [] |
|
55 self._propfile_mtime = {} |
|
56 |
|
57 def load(self, fpath): |
|
58 scriptglobals = self.context.copy() |
|
59 scriptglobals['__file__'] = fpath |
|
60 with open(fpath, 'rb') as fobj: |
|
61 code = compile(fobj.read(), fpath, 'exec') |
|
62 exec(code, scriptglobals, self) |
|
63 for name, type in TYPE_CHECKS: |
|
64 if name in self: |
|
65 if not isinstance(self[name], type): |
|
66 msg = "Configuration error: %s.%s should be a %s" % (fpath, name, type) |
|
67 raise Exception(msg) |
|
68 self._propfile_mtime[fpath] = os.stat(fpath).st_mtime |
|
69 self._ordered_propfiles.append(fpath) |
|
70 |
|
71 def need_reload(self): |
|
72 for fpath, mtime in self._propfile_mtime.items(): |
|
73 if os.stat(fpath).st_mtime > mtime: |
|
74 return True |
|
75 return False |
|
76 |
|
77 def reload(self): |
|
78 ordered_files = self._ordered_propfiles |
|
79 self.reset() |
|
80 for fpath in ordered_files: |
|
81 self.load(fpath) |
|
82 |
|
83 def reload_if_needed(self): |
|
84 if self.need_reload(): |
|
85 self.reload() |
|
86 |
|
87 def process_resource(self, rdirectory, rid): |
|
88 cachefile = osp.join(self._cache_directory, rid) |
|
89 self.debug('processing %s/%s into %s', |
|
90 rdirectory, rid, cachefile) |
|
91 rcachedir = osp.dirname(cachefile) |
|
92 if not osp.exists(rcachedir): |
|
93 os.makedirs(rcachedir) |
|
94 sourcefile = osp.join(rdirectory, rid) |
|
95 with open(sourcefile) as f: |
|
96 content = f.read() |
|
97 # XXX replace % not followed by a paren by %% to avoid having to do |
|
98 # this in the source css file ? |
|
99 try: |
|
100 content = self.compile(content) |
|
101 except ValueError as ex: |
|
102 self.error("can't process %s/%s: %s", rdirectory, rid, ex) |
|
103 adirectory = rdirectory |
|
104 else: |
|
105 tmpfd, tmpfile = tempfile.mkstemp(dir=rcachedir, prefix=osp.basename(cachefile)) |
|
106 with os.fdopen(tmpfd, 'w') as stream: |
|
107 stream.write(content) |
|
108 os.rename(tmpfile, cachefile) |
|
109 adirectory = self._cache_directory |
|
110 return adirectory |
|
111 |
|
112 def compile(self, content): |
|
113 return self._percent_rgx.sub('%%', content) % self |
|
114 |
|
115 # these are overridden by set_log_methods below |
|
116 # only defining here to prevent pylint from complaining |
|
117 info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None |
|
118 |
|
119 from cubicweb.web import LOGGER |
|
120 from logilab.common.logging_ext import set_log_methods |
|
121 set_log_methods(PropertySheet, LOGGER) |
|