typo--
[Plinn.git] / Products / Plinn / utils.py
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright (C) 2005-2007 BenoƮt PIN <benoit.pin@ensmp.fr> #
5 # #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ Plinn public utilities
21
22
23 """
24
25 import string
26 import re
27 from random import randrange
28 from Acquisition import aq_base
29 from quopri import encodestring
30 from json import dumps as json_dumps
31 from zope.globalrequest import getRequest
32 from AccessControl.PermissionRole import rolesForPermissionOn
33 from AccessControl import ModuleSecurityInfo
34 from AccessControl import getSecurityManager
35 from AccessControl.User import UnrestrictedUser
36 from OFS.CopySupport import _cb_decode, _cb_encode, cookie_path
37 from Products.CMFCore.utils import getToolByName, getUtilityByInterfaceName
38 from Products.CMFCore.exceptions import BadRequest
39 from Products.Utf8Splitter.Utf8Splitter import Utf8Utils
40 from Globals import REPLACEABLE, NOT_REPLACEABLE, UNIQUE
41 from zope.i18n import translate as i18ntranslate
42 from zope.i18n.interfaces import IUserPreferredLanguages
43 from zope.i18nmessageid import MessageFactory
44 from zope.component.interfaces import ComponentLookupError
45 from zope.dottedname.resolve import resolve as resolve_dotted_name
46 from zope.component import queryAdapter
47
48 _marker = []
49
50 security = ModuleSecurityInfo( 'Products.Plinn.utils' )
51
52 security.declarePublic('json_dumps')
53
54 security.declarePublic('thisObjectComeFromPortalSkin')
55 def thisObjectComeFromPortalSkin(ob, portal=None):
56 """ check if ob comes from portal_skins """
57 if not portal :
58 portal = getToolByName(ob, 'portal_url')
59 portal = portal.getPortalObject()
60
61 if ob.aq_self == portal.aq_self :
62 return False
63
64 obId = ob.id
65 if callable(obId) :
66 obId = obId()
67
68 sob = getattr(portal, obId, None)
69
70 if sob is None :
71 return False
72 elif not(sob.aq_inner.aq_self is ob.aq_inner.aq_self) :
73 return False
74 else :
75 try :
76 portal._checkId(obId)
77 return True
78 except BadRequest :
79 return False
80
81 security.declarePublic('listWorkflowActions')
82 def listWorkflowActions(context) :
83 wftool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IWorkflowTool')
84 return wftool.listActions(object=context)
85
86 def capitalizeCompoundGivenName(givenName) :
87 givenName = givenName.strip()
88 givenNames = ' '.join(givenName.split('-')).split()
89 givenNameCapitalized = '-'.join(map(string.capitalize, givenNames))
90 return givenNameCapitalized
91
92
93 def formatFullName(memberName, memberGivenName, memberId, nameBefore=1) :
94 memberName = memberName.decode('utf-8')
95 memberGivenName = memberGivenName.decode('utf-8')
96 memberFullName = u''
97 if memberName and memberGivenName :
98 if nameBefore :
99 memberFullName = memberName.capitalize() + ' ' + capitalizeCompoundGivenName(memberGivenName)
100 else :
101 memberFullName = capitalizeCompoundGivenName(memberGivenName) + ' ' + memberName.capitalize()
102
103 elif memberName and not memberGivenName :
104 memberFullName = memberName.capitalize()
105
106 elif not memberName and memberGivenName :
107 memberFullName = capitalizeCompoundGivenName(memberGivenName)
108
109 else :
110 memberFullName = memberId
111
112 return memberFullName.encode('utf-8')
113
114 # from OFS.ObjectManager #63
115 bad_url_chars = re.compile(r'[^a-zA-Z0-9-_~,.$\(\)@]')
116
117 security.declarePublic('makeValidId')
118 def makeValidId(self, id, allow_dup=0):
119 id = Utf8Utils.desacc(id)
120 id = bad_url_chars.sub('-', id)
121 # If allow_dup is false, an error will be raised if an object
122 # with the given id already exists. If allow_dup is true,
123 # only check that the id string contains no illegal chars;
124 # check_valid_id() will be called again later with allow_dup
125 # set to false before the object is added.
126
127 makeRandomId = False
128 if id in ('.', '..'):
129 makeRandomId = True
130 if id.startswith('_'):
131 id = id.lstrip('_')
132 if id.startswith('aq_'):
133 id = id[3:]
134
135 while id.endswith('__') :
136 id = id[:-1]
137 if not allow_dup:
138 obj = getattr(self, id, None)
139 if obj is not None:
140 # An object by the given id exists either in this
141 # ObjectManager or in the acquisition path.
142 flags = getattr(obj, '__replaceable__', NOT_REPLACEABLE)
143 if hasattr(aq_base(self), id):
144 # The object is located in this ObjectManager.
145 if not flags & REPLACEABLE:
146 makeRandomId = True
147 # else the object is replaceable even if the UNIQUE
148 # flag is set.
149 elif flags & UNIQUE:
150 makeRandomId = True
151 if id == 'REQUEST':
152 makeRandomId = True
153
154 if makeRandomId is True :
155 id = str(randrange(2,10000)) + id
156 return id
157
158
159
160 def _checkMemberPermission(userid, permission, obj, StringType = type('')):
161 user = obj.aq_inner.acl_users.getUser(userid)
162 roles = rolesForPermissionOn(permission, obj)
163 if type(roles) is StringType:
164 roles=[roles]
165 if user.allowed( obj, roles ):
166 return 1
167 return 0
168
169 def getCPInfo(self) :
170 if self.REQUEST.RESPONSE.cookies.has_key('__cp') :
171 cp = self.REQUEST.RESPONSE.cookies['__cp']['value']
172 else :
173 cp = self.REQUEST.get('__cp')
174 try: cp = _cb_decode(cp)
175 except: return None
176 return cp
177
178
179 def popCP(self, indexes=None) :
180 try: cp = _cb_decode(self.REQUEST['__cp'])
181 except: return
182
183 paths = list(cp[1])
184 if indexes is not None :
185 indexes = list(indexes)
186 indexes.sort()
187 indexes.reverse()
188 for index in indexes :
189 paths.pop(index)
190 else :
191 paths.pop()
192
193 if not paths :
194 self.REQUEST.RESPONSE.expireCookie('__cp', path=self.REQUEST['BASEPATH1'] or "/")
195 else :
196 paths = tuple(paths)
197 cp = _cb_encode( (cp[0], paths) )
198 resp = self.REQUEST['RESPONSE']
199 resp.setCookie('__cp', cp, path='%s' % cookie_path(self.REQUEST))
200
201 security.declarePublic('Message')
202 Message = MessageFactory('plinn')
203
204 security.declarePublic('translate')
205 def translate(message, context=None):
206 """ Translate i18n message.
207 """
208 if isinstance(message, Exception):
209 try:
210 message = message[0]
211 except (TypeError, IndexError):
212 pass
213 if not context :
214 request = getRequest()
215 else :
216 request = context.REQUEST
217 return i18ntranslate(message, domain='plinn', context=request)
218
219 security.declarePublic('desacc')
220 desacc = Utf8Utils.desacc
221
222 security.declarePublic('getPreferredLanguages')
223 def getPreferredLanguages(context):
224 """ returns browser prefered languages"""
225 request = getattr(context, 'REQUEST', None)
226 if request is not None :
227 adapter = IUserPreferredLanguages(request, None)
228 if adapter is not None :
229 return adapter.getPreferredLanguages()
230 return []
231
232 security.declarePublic('getBestTranslationLanguage')
233 def getBestTranslationLanguage(langs, context):
234 """ returns best translation language according
235 to available languages (param langs)
236 and user preferences (retrieves by context)
237 """
238 request = getattr(context, 'REQUEST', None)
239 if request :
240 negociator = getUtilityByInterfaceName('zope.i18n.interfaces.INegotiator')
241 return negociator.getLanguage(langs, request) or langs[0]
242 else :
243 return langs[0]
244
245 security.declarePublic('getAdapterByInterface')
246 def getAdapterByInterface(ob, dotted_name, default=_marker) :
247 """ Get the adapter which provides the interface on the given object.
248 """
249 try:
250 iface = resolve_dotted_name(dotted_name)
251 except ImportError:
252 if default is _marker:
253 raise ComponentLookupError, dotted_name
254 return default
255
256 adapter = queryAdapter(ob, iface, default=default)
257 if adapter is _marker :
258 raise ComponentLookupError, "no adapter providing %r found on %r" % (dotted_name, ob)
259
260 # the adapter must be wrapped to allow security mahinery to work.
261 if adapter != default :
262 return adapter.__of__(ob)
263 else :
264 return default
265
266 security.declarePublic('encodeQuopriEmail')
267 def encodeQuopriEmail(name, email) :
268 qpName = encodestring(name).replace('=\n', '')
269 return '''"=?utf-8?q?%s?=" <%s>''' % (qpName, email)
270
271 security.declarePublic('encodeMailHeader')
272 def encodeMailHeader(content) :
273 s = encodestring(content).replace('=\n', '')
274 s = s.replace('_', '=5F')
275 s = s.replace(' ', '_')
276
277 lines = []
278 STEP = 50
279 start = 0
280 stop = STEP
281 part = s[start:stop]
282 lines.append(part)
283
284 while len(part) == STEP:
285 start = start + STEP
286 stop = stop + STEP
287 part = s[start:stop]
288 lines.append(part)
289
290 lines = [' =?utf-8?Q?%s?=' % part for part in lines]
291 s = '\n'.join(lines)
292 s = s.strip()
293 return s
294
295
296 def _sudo(func, userid=None) :
297 """
298 execute func or any callable object
299 without restriction. Used to switch off
300 security assertions (eg. checkPermission) encountered
301 during the execution.
302 """
303
304 sm = getSecurityManager()
305 restrictedUser = sm.getUser()
306
307 if not userid :
308 userid = restrictedUser.getId()
309
310 sm._context.user = UnrestrictedUser(userid, '', (), ())
311
312 deferedEx = None
313 try :
314 ret = func()
315 except Exception, e :
316 deferedEx = e
317
318 sm._context.user = restrictedUser
319
320 if deferedEx is not None :
321 raise e
322
323 return ret
324
325 security.declarePublic('searchContentsWithLocalRolesForAuthenticatedUser')
326 def searchContentsWithLocalRolesForAuthenticatedUser(**kw):
327 mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool')
328 ctool = getUtilityByInterfaceName('Products.CMFCore.interfaces.ICatalogTool')
329 member = mtool.getAuthenticatedMember()
330 userid = member.getId()
331 userAndGroups = ['user:%s' % userid]
332
333 getGroups = getattr(member, 'getGroups', None)
334 if getGroups is not None :
335 for group in getGroups():
336 userAndGroups.append('user:'+group)
337
338 kw[ 'allowedRolesAndUsers' ] = userAndGroups
339
340 return ctool.unrestrictedSearchResults(**kw)