1 """
2 Utilities, adapters, markers... de todo un poco para
3 soportar contenido multi-lenguaje
4
5 @author: Juan Pablo Gimenez
6 @contact: jpg@rcom.com.ar
7 """
8 __author__ = """Juan Pablo Gimenez <jpg@rcom.com.ar>"""
9 __docformat__ = 'plaintext'
10
11 import sys
12 from types import FunctionType as function
13
14 from zope.interface import implements, implementedBy, \
15 classProvides
16 from zope.app.component.hooks import getSite
17 from zope.component.interfaces import IFactory
18 from zope.component import queryUtility
19 from zope.i18n.interfaces import IUserPreferredLanguages
20
21 from Products.Archetypes.utils import capitalize
22 from Products.ATContentTypes.interfaces import IATContentType
23 from Products.CMFCore.utils import getToolByName
24 from Products.CMFPlone.utils import classImplements, \
25 classDoesNotImplement
26
27 from platecom.utils.interfaces import IContentTypesMultilingualPatcher, \
28 IMultilingualContentMarker, \
29 IMultilingualGettersMarker, \
30 IFieldEmptiness
31
32 NOT_FALLBACK_FIELDS = ['id',
33 'language',]
34 FALLBACK_TYPES = ['string',
35 'text',
36 'lines',]
37
39 """ Utility para aplicar los parches de soporte multilenguaje
40 a un ContentType en particular
41 """
42 implements(IContentTypesMultilingualPatcher)
43
44 - def patch(self, klass, replace_accessors=False):
45 """ Incorpora los metodos con fallback multi-lenguaje
46
47 >>> import string
48 >>> from Products.Archetypes.atapi import *
49 >>> from Products.ATContentTypes.content.base import ATCTContent
50 >>> from icsemantic.core.content.multilingual import ContentTypesMultilingualPatcher
51 >>> from icsemantic.core.interfaces import IMultilingualContentMarker, IMultilingualGettersMarker
52
53 >>> class MyContent(ATCTContent):
54 ... portal_type = meta_type = 'MyContent'
55 ... schema = Schema((
56 ... StringField('some_field', storage=AnnotationStorage()),
57 ... StringField('_other_field'),
58 ... ))
59
60 >>> registerType(MyContent, 'Archetypes')
61
62 >>> hasattr(MyContent, 'getSome_field')
63 True
64 >>> hasattr(MyContent, 'getMultilingualSome_field')
65 False
66
67 >>> original_getter = getattr(MyContent, 'getSome_field')
68 >>> ccpatcher = ContentTypesMultilingualPatcher()
69 >>> ccpatcher.patch(MyContent)
70
71 >>> IMultilingualContentMarker.implementedBy(MyContent)
72 True
73 >>> IMultilingualGettersMarker.implementedBy(MyContent)
74 False
75 >>> hasattr(MyContent, 'getSome_field')
76 True
77 >>> hasattr(MyContent, 'getMultilingualSome_field')
78 True
79 >>> original_getter == getattr(MyContent, 'getSome_field')
80 True
81
82 >>> ccpatcher.unpatch(MyContent)
83 >>> IMultilingualContentMarker.implementedBy(MyContent)
84 False
85 >>> IMultilingualGettersMarker.implementedBy(MyContent)
86 False
87 >>> ccpatcher.patch(MyContent, replace_accessors=True)
88
89 >>> IMultilingualContentMarker.implementedBy(MyContent)
90 True
91 >>> IMultilingualGettersMarker.implementedBy(MyContent)
92 True
93 >>> hasattr(MyContent, 'getSome_field')
94 True
95 >>> hasattr(MyContent, 'getMultilingualSome_field')
96 True
97 >>> original_getter == getattr(MyContent, 'getSome_field')
98 False
99 >>> getattr(MyContent, 'getSome_field') == getattr(MyContent, 'getMultilingualSome_field')
100 True
101 >>> original_getter == getattr(MyContent, '_old_getSome_field')
102 True
103
104 Y ahora pruebo con basura...
105 >>> ccpatcher.patch('pepe')
106 Traceback (most recent call last):
107 ...
108 AttributeError...
109
110 """
111 if not IATContentType.isImplementedByInstancesOf(klass):
112
113 portal = getSite()
114 archetype_tool = getToolByName(portal, 'archetype_tool')
115 for type in archetype_tool.listRegisteredTypes():
116 if type['meta_type'] == klass:
117 klass = type['klass']
118 assert IATContentType.isImplementedByInstancesOf(klass)
119
120 if IMultilingualContentMarker.implementedBy(klass):
121 return
122
123 def getMultilingualField(self, field_name, fallback=True):
124 """
125 """
126 field = self.getField(field_name)
127 if field is None:
128 raise KeyError("Cannot find field with name %s" % field_name)
129
130 value = field.get(self)
131 if not IMultilingualContentMarker.providedBy(self) or \
132 field.isLanguageIndependent(self) or \
133 not fallback or \
134 field_name in NOT_FALLBACK_FIELDS:
135
136
137
138
139 return value
140
141 translations = self.getTranslations()
142 current_lang = self.getLanguage()
143 if fallback and current_lang and len(translations) > 1:
144 try:
145 platecom_lang_util = queryUtility(IUserPreferredLanguages,
146 name='platecom_preferred_languages')
147 request=getattr(self, 'REQUEST', None)
148 langs = platecom_lang_util.getPreferredLanguages(request=request)
149 langs += [current_lang]
150 for lang in langs:
151 if translations.has_key(lang):
152 inst = translations[lang][0]
153 field = inst.getField(field_name)
154 if field:
155 value = field.get(inst)
156 if not IFieldEmptiness(field)(inst):
157 break
158 except:
159 pass
160 return value
161
162 if not hasattr(klass, 'getMultilingualField'):
163 setattr(klass, 'getMultilingualField', getMultilingualField)
164
165 fields = klass.schema.fields()
166 for field in fields:
167 name = field.getName()
168
169 if field.type in FALLBACK_TYPES and \
170 name not in NOT_FALLBACK_FIELDS and \
171 not field.isLanguageIndependent(field):
172 makeMethod(klass, field)
173 def getMultilingualAccessor(self, field):
174 """Return the accessor method for getting data out of this
175 field"""
176 if field.multilingual_accessor:
177 accessor = getattr(self, field.multilingual_accessor, None)
178 if not accessor and field.accessor:
179 accessor = getattr(self, field.accessor, None)
180 return accessor
181 setattr(klass, 'getMultilingualAccessor', getMultilingualAccessor)
182 if replace_accessors:
183
184 getName = field.accessor
185 getMultilingualName = "getMultilingual%s" % capitalize(name)
186 if hasattr(klass, getName) and \
187 hasattr(klass, getMultilingualName):
188 getMethod = getattr(klass, getName)
189 getMultilingualMethod = getattr(klass, getMultilingualName)
190 setattr(klass, '_old_%s' % getName, getMethod)
191 setattr(klass, getName, getMultilingualMethod)
192 classImplements(klass, IMultilingualGettersMarker)
193 classImplements(klass, IMultilingualContentMarker)
194
195 - def unpatch(self, klass):
196 """ Remueve los metodos con fallback multi-lenguaje
197
198 >>> import string
199 >>> from Products.Archetypes.atapi import *
200 >>> from Products.ATContentTypes.content.base import ATCTContent
201 >>> from icsemantic.core.content.multilingual import ContentTypesMultilingualPatcher
202
203 >>> class MyContent(ATCTContent):
204 ... portal_type = meta_type = 'MyContent'
205 ... schema = Schema((
206 ... StringField('some_field', storage=AnnotationStorage()),
207 ... StringField('_other_field'),
208 ... ))
209
210 >>> registerType(MyContent, 'Archetypes')
211
212 >>> hasattr(MyContent, 'getSome_field')
213 True
214 >>> hasattr(MyContent, 'getMultilingualSome_field')
215 False
216
217 >>> ccpatcher = ContentTypesMultilingualPatcher()
218 >>> ccpatcher.patch(MyContent)
219
220 >>> hasattr(MyContent, 'getSome_field')
221 True
222 >>> hasattr(MyContent, 'getMultilingualSome_field')
223 True
224
225 >>> ccpatcher = ContentTypesMultilingualPatcher()
226 >>> ccpatcher.unpatch(MyContent)
227
228 >>> hasattr(MyContent, 'getSome_field')
229 True
230 >>> hasattr(MyContent, 'getMultilingualSome_field')
231 False
232
233 """
234 if not IATContentType.isImplementedByInstancesOf(klass):
235
236 portal = getSite()
237 archetype_tool = getToolByName(portal, 'archetype_tool')
238 for type in archetype_tool.listRegisteredTypes():
239 if type['meta_type'] == klass:
240 klass = type['klass']
241 assert IATContentType.isImplementedByInstancesOf(klass)
242
243 if IMultilingualContentMarker.implementedBy(klass):
244 fields = klass.schema.fields()
245 for field in fields:
246 methodName = "getMultilingual%s" % capitalize(field.getName())
247 if hasattr(klass, methodName):
248 try:
249 delattr(klass, methodName)
250 except:
251 pass
252 if IMultilingualGettersMarker.implementedBy(klass):
253 name = field.getName()
254 getName = field.accessor
255 getOldName = "_old_%s" % getName
256 if hasattr(klass, getName) and hasattr(klass, getOldName):
257 getOldMethod = getattr(klass, getOldName)
258 setattr(klass, getName, getOldMethod)
259 try:
260 delattr(klass, getOldName)
261 except:
262 pass
263
264 if hasattr(klass, 'getMultilingualField'):
265 try:
266 delattr(klass, 'getMultilingualField')
267 except:
268 pass
269
270 classDoesNotImplement( klass, IMultilingualContentMarker)
271 classDoesNotImplement( klass, IMultilingualGettersMarker)
272
274 name = field.getName()
275 def generatedMultilingualAccessor(self, fallback = True):
276 def who_called_me():
277 try:
278 1/0
279 except ZeroDivisionError:
280 return sys.exc_info()[2].tb_frame.f_back.f_back.f_code.co_name
281
282 who=who_called_me()
283
284 if who == '_get_object_datum':
285 fallback = False
286
287 value = self.getMultilingualField(name, fallback)
288
289
290 return value
291 method = generatedMultilingualAccessor
292 methodName = "getMultilingual%s" % capitalize(name)
293 method = function(method.func_code,
294 method.func_globals,
295 methodName,
296 method.func_defaults,
297 method.func_closure,
298 )
299 if not hasattr(klass, methodName):
300 setattr(klass, methodName, method)
301 field.multilingual_accessor = methodName
302