17 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA |
18 |
__version__='$Revision$'[11:-2] |
__version__='$Revision$'[11:-2] |
19 |
|
|
20 |
|
# ugh, this import list needs to be cleaned up |
21 |
import OpenTAL, pax, PTmetal_handler, pax.paxtransform |
import OpenTAL, pax, PTmetal_handler, pax.paxtransform |
22 |
from pax.backwards_compatibility import * |
from pax.backwards_compatibility import * |
23 |
from ZPythonExpr import PythonExpr, SecureModuleImporter |
from ZPythonExpr import PythonExpr, SecureModuleImporter |
24 |
import cPickle as pickle |
import cPickle as pickle |
25 |
#import pickle |
#import pickle |
|
|
|
26 |
import AccessControl, Acquisition, sys |
import AccessControl, Acquisition, sys |
27 |
from Acquisition import aq_base |
from Acquisition import aq_base |
28 |
from Globals import DTMLFile, ImageFile, MessageDialog, package_home |
from Globals import DTMLFile, ImageFile, MessageDialog, package_home |
29 |
from zLOG import LOG, ERROR, INFO |
from zLOG import LOG, PROBLEM, ERROR, INFO |
30 |
from OFS.SimpleItem import SimpleItem |
from OFS.SimpleItem import SimpleItem |
31 |
from DateTime.DateTime import DateTime |
from DateTime.DateTime import DateTime |
32 |
from string import join, strip, rstrip, split, replace, lower |
from string import join, strip, rstrip, split, replace, lower |
47 |
from ZODB.PersistentMapping import PersistentMapping |
from ZODB.PersistentMapping import PersistentMapping |
48 |
from OFS.SimpleItem import SimpleItem |
from OFS.SimpleItem import SimpleItem |
49 |
import Globals |
import Globals |
50 |
|
from encodings.aliases import aliases as encoding_aliases |
51 |
from DocumentTemplate.DT_Util import TemplateDict, InstanceDict |
from DocumentTemplate.DT_Util import TemplateDict, InstanceDict |
52 |
from AccessControl.DTML import RestrictedDTML |
from AccessControl.DTML import RestrictedDTML |
53 |
class Rtd(RestrictedDTML, TemplateDict): |
class Rtd(RestrictedDTML, TemplateDict): |
144 |
if not self._securityManager.validate(accessed, container, name, value, *a): |
if not self._securityManager.validate(accessed, container, name, value, *a): |
145 |
raise Unauthorized, name |
raise Unauthorized, name |
146 |
|
|
147 |
|
class EncodingNegotiator(Acquisition.Explicit): |
148 |
|
security = AccessControl.ClassSecurityInfo() |
149 |
|
security.declareObjectPublic() |
150 |
|
fallback = 'utf_8' # always acceptable |
151 |
|
forced = None |
152 |
|
|
153 |
|
def log(self, message, severity=INFO): |
154 |
|
LOG('EncodingNegotiator', severity, message) |
155 |
|
|
156 |
|
def __init__(self, preferred=None): |
157 |
|
if preferred is None: |
158 |
|
self.preferred = ('utf_8', 'utf_16') |
159 |
|
else: |
160 |
|
self.preferred = self.purify(preferred) |
161 |
|
self.log('Initializing with %s' % self.preferred) |
162 |
|
self.acceptable = sets.Set(self.preferred) |
163 |
|
self.clients = [] |
164 |
|
|
165 |
|
def purify_name(self, name): |
166 |
|
"find if encoding is an alias and return the pythonic name" |
167 |
|
name = name.replace('-', '_').replace(' ', '_').lower() |
168 |
|
return encoding_aliases.get(name, name) |
169 |
|
|
170 |
|
def purify(self, encodings): |
171 |
|
"find all encodings that are aliases and replace them by the pythonic name" |
172 |
|
return [self.purify_name(name) for name in encodings] |
173 |
|
|
174 |
|
security.declarePublic('restrict') |
175 |
|
def restrict(self, client, encodings): |
176 |
|
"restrict the set of acceptable encodings by intersecting it with <encodings>" |
177 |
|
client_id = id(client) |
178 |
|
if client_id not in self.clients: |
179 |
|
self.clients.append(client_id) |
180 |
|
encodings = self.purify(encodings) |
181 |
|
self.log('restricting with %s due to %s' % (encodings, client)) |
182 |
|
self.acceptable.intersection_update(sets.Set(encodings)) |
183 |
|
self.log('now: %s' % self.acceptable) |
184 |
|
else: |
185 |
|
self.log('client %s already cached' % client) |
186 |
|
|
187 |
|
security.declarePublic('force') |
188 |
|
def force(self, encoding): |
189 |
|
"force one encoding. If it's called multiple times, bad luck, last one wins." |
190 |
|
self.log('forcing %s' % encoding) |
191 |
|
self.forced = encoding |
192 |
|
|
193 |
|
def encode(self, text): |
194 |
|
if self.forced is not None: |
195 |
|
try: |
196 |
|
return text.encode(self.forced), self.forced |
197 |
|
except UnicodeError: |
198 |
|
self.log('Was told to force %s encoding but it failed' % self.forced, PROBLEM) |
199 |
|
for enc in self.preferred: |
200 |
|
if enc in self.acceptable: |
201 |
|
try: |
202 |
|
return text.encode(enc), enc |
203 |
|
except UnicodeError: |
204 |
|
pass |
205 |
|
self.log('negotiation failed, falling back to %s' % self.fallback) |
206 |
|
return text.encode(self.fallback), self.fallback # should never fail! |
207 |
|
Globals.InitializeClass(EncodingNegotiator) |
208 |
|
|
209 |
|
|
210 |
# paxtransform handlers |
# paxtransform handlers |
211 |
handler_registry = [OpenTAL.metal_handler, OpenTAL.tal_handler] |
handler_registry = [OpenTAL.metal_handler, OpenTAL.tal_handler] |
212 |
# paxtransform handlers |
# paxtransform handlers |
259 |
source_type = 'HTML' |
source_type = 'HTML' |
260 |
handlers = ('tal', 'metal') |
handlers = ('tal', 'metal') |
261 |
encoding = 'utf-8' |
encoding = 'utf-8' |
262 |
|
acceptable_encodings = ('utf_8', 'utf_16') |
263 |
_errors = () |
_errors = () |
264 |
_warnings = () |
_warnings = () |
265 |
_text = '<opental />' |
_text = '<opental />' |
496 |
context = self._context_class(bound_names) |
context = self._context_class(bound_names) |
497 |
try: |
try: |
498 |
context.encoding = self.default_zope_data_encoding |
context.encoding = self.default_zope_data_encoding |
499 |
except: |
except AttributeError: |
500 |
context.encoding = self.encoding |
context.encoding = self.encoding |
501 |
context.root_xmlns_map = root_ns_map |
context.root_xmlns_map = root_ns_map |
502 |
tr_engine.initialize(context) |
tr_engine.initialize(context) |
503 |
|
encoding_neg = EncodingNegotiator(self.acceptable_encodings) |
504 |
|
# we need the acquisition wrapper or AccessControl will squeal |
505 |
|
self.REQUEST.pt_output_encoding = encoding_neg.__of__(self) |
506 |
|
# now run the template |
507 |
#result = pax.XML(tr_engine.transform(self.paxtree(), context), |
#result = pax.XML(tr_engine.transform(self.paxtree(), context), |
508 |
# root_ns_map, self.encoding) |
# root_ns_map, self.encoding) |
509 |
result = tr_engine.transform(self.paxtree(), context).text |
result = tr_engine.transform(self.paxtree(), context).text |
510 |
|
result, encoding = encoding_neg.encode(result) |
511 |
|
# now this is hairy: how to get the official (IANA) http name |
512 |
|
# of the encoding based on the python name? |
513 |
|
# the hack below catches most usual cases, but far from all. |
514 |
|
encoding = encoding.replace('_', '-') |
515 |
|
if encoding.startswith('latin_'): |
516 |
|
encoding = 'latin' + encoding[6:] |
517 |
if keyset is not None: |
if keyset is not None: |
518 |
# Store the result in the cache. |
# Store the result in the cache. |
519 |
self.ZCacheable_set(result, keywords=keyset) |
self.ZCacheable_set(result, keywords=keyset) |
520 |
try: |
self.REQUEST.RESPONSE.setHeader('content-type', '%s; charset=%s' % ( |
|
self.REQUEST.RESPONSE.setHeader('content-type', |
|
|
'%s; charset=%s' % ( |
|
521 |
self.content_type, |
self.content_type, |
522 |
context.encoding)) |
encoding)) |
523 |
except AttributeError: |
print >>sys.stderr, self.REQUEST.RESPONSE.headers |
|
pass |
|
524 |
return result |
return result |
525 |
finally: |
finally: |
526 |
security.removeContext(self) |
security.removeContext(self) |