0cf9854d0cd6782a95cff3f9e70295d13eb0424c
[GroupUserFolder.git] / PloneFeaturePreview.py
1 # -*- coding: utf-8 -*-
2 ## GroupUserFolder
3 ## Copyright (C)2006 Ingeniweb
4
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 2 of the License, or
8 ## (at your option) any later version.
9
10 ## This program is distributed in the hope that it will be useful,
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ## GNU General Public License for more details.
14
15 ## You should have received a copy of the GNU General Public License
16 ## along with this program; see the file COPYING. If not, write to the
17 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 """
19
20 GRUF3 Feature-preview stuff.
21
22 This code shouldn't be here but allow people to preview advanced GRUF3
23 features (eg. flexible LDAP searching in 'sharing' tab, ...) in Plone 2,
24 without having to upgrade to Plone 2.1.
25
26 Methods here are monkey-patched by now but will be provided directly by
27 Plone 2.1.
28 Please forgive this 'uglyness' but some users really want to have full
29 LDAP support without switching to the latest Plone version ! ;)
30
31
32 BY DEFAULT, this thing IS enabled with Plone 2.0.x
33 """
34 __version__ = "$Revision: $"
35 # $Source: $
36 # $Id: PloneFeaturePreview.py 587 2008-07-31 09:20:06Z pin $
37 __docformat__ = 'restructuredtext'
38
39 from Products.CMFCore.utils import UniqueObject
40 from Products.CMFCore.utils import getToolByName
41 from OFS.SimpleItem import SimpleItem
42 from OFS.Image import Image
43 from Globals import InitializeClass, DTMLFile, MessageDialog
44 from Acquisition import aq_base
45 from AccessControl.User import nobody
46 from AccessControl import ClassSecurityInfo
47 from Products.CMFCore.ActionProviderBase import ActionProviderBase
48 from interfaces.portal_groups import portal_groups as IGroupsTool
49 from global_symbols import *
50
51
52 # This is "stollen" from MembershipTool.py
53 # this should probably be in MemberDataTool.py
54 def searchForMembers( self, REQUEST=None, **kw ):
55 """
56 searchForMembers(self, REQUEST=None, **kw) => normal or fast search method.
57
58 The following properties can be provided:
59 - name
60 - email
61 - last_login_time
62 - roles
63
64 This is an 'AND' request.
65
66 If name is provided, then a _fast_ search is performed with GRUF's
67 searchUsersByName() method. This will improve performance.
68
69 In any other case, a regular (possibly _slow_) search is performed.
70 As it uses the listMembers() method, which is itself based on gruf.getUsers(),
71 this can return partial results. This may change in the future.
72 """
73 md = self.portal_memberdata
74 mt = self.portal_membership
75 if REQUEST:
76 dict = REQUEST
77 else:
78 dict = kw
79
80 # Attributes retreiving & mangling
81 name = dict.get('name', None)
82 email = dict.get('email', None)
83 roles = dict.get('roles', None)
84 last_login_time = dict.get('last_login_time', None)
85 is_manager = mt.checkPermission('Manage portal', self)
86 if name:
87 name = name.strip().lower()
88 if email:
89 email = email.strip().lower()
90
91
92 # We want 'name' request to be handled properly with large user folders.
93 # So we have to check both the fullname and loginname, without scanning all
94 # possible users.
95 md_users = None
96 uf_users = None
97 if name:
98 # We first find in MemberDataTool users whose _full_ name match what we want.
99 lst = md.searchMemberDataContents('fullname', name)
100 md_users = [ x['username'] for x in lst ]
101
102 # Fast search management if the underlying acl_users support it.
103 # This will allow us to retreive users by their _id_ (not name).
104 acl_users = self.acl_users
105 meth = getattr(acl_users, "searchUsersByName", None)
106 if meth:
107 uf_users = meth(name) # gruf search
108
109 # Now we have to merge both lists to get a nice users set.
110 # This is possible only if both lists are filled (or we may miss users else).
111 Log(LOG_DEBUG, md_users, uf_users, )
112 members = []
113 if md_users is not None and uf_users is not None:
114 names_checked = 1
115 wrap = mt.wrapUser
116 getUser = acl_users.getUser
117 for userid in md_users:
118 members.append(wrap(getUser(userid)))
119 for userid in uf_users:
120 if userid in md_users:
121 continue # Kill dupes
122 usr = getUser(userid)
123 if usr is not None:
124 members.append(wrap(usr))
125
126 # Optimization trick
127 if not email and \
128 not roles and \
129 not last_login_time:
130 return members
131 else:
132 # If the lists are not available, we just stupidly get the members list
133 members = self.listMembers()
134 names_checked = 0
135
136 # Now perform individual checks on each user
137 res = []
138 portal = self.portal_url.getPortalObject()
139
140 for member in members:
141 #user = md.wrapUser(u)
142 u = member.getUser()
143 if not (member.listed or is_manager):
144 continue
145 if name and not names_checked:
146 if (u.getUserName().lower().find(name) == -1 and
147 member.getProperty('fullname').lower().find(name) == -1):
148 continue
149 if email:
150 if member.getProperty('email').lower().find(email) == -1:
151 continue
152 if roles:
153 user_roles = member.getRoles()
154 found = 0
155 for r in roles:
156 if r in user_roles:
157 found = 1
158 break
159 if not found:
160 continue
161 if last_login_time:
162 if member.last_login_time < last_login_time:
163 continue
164 res.append(member)
165 Log(LOG_DEBUG, res)
166 return res
167
168
169 def listAllowedMembers(self,):
170 """listAllowedMembers => list only members which belong
171 to the same groups/roles as the calling user.
172 """
173 user = self.REQUEST['AUTHENTICATED_USER']
174 caller_roles = user.getRoles() # Have to provide a hook for admins
175 current_members = self.listMembers()
176 allowed_members =[]
177 for member in current_members:
178 for role in caller_roles:
179 if role in member.getRoles():
180 allowed_members.append(member)
181 break
182 return allowed_members
183
184
185 def _getPortrait(self, member_id):
186 """
187 return member_id's portrait if you can.
188 If it's not possible, just try to fetch a 'portait' property from the underlying user source,
189 then create a portrait from it.
190 """
191 # fetch the 'portrait' property
192 Log(LOG_DEBUG, "trying to fetch the portrait for the given member id")
193 portrait = self._former_getPortrait(member_id)
194 if portrait:
195 Log(LOG_DEBUG, "Returning the old-style portrait:", portrait, "for", member_id)
196 return portrait
197
198 # Try to find a portrait in the user source
199 member = self.portal_membership.getMemberById(member_id)
200 portrait = member.getUser().getProperty('portrait', None)
201 if not portrait:
202 Log(LOG_DEBUG, "No portrait available in the user source for", member_id)
203 return None
204
205 # Convert the user-source portrait into a plone-complyant one
206 Log(LOG_DEBUG, "Converting the portrait", type(portrait))
207 portrait = Image(id=member_id, file=portrait, title='')
208 membertool = self.portal_memberdata
209 membertool._setPortrait(portrait, member_id)
210
211 # Re-call ourself to retreive the real portrait
212 Log(LOG_DEBUG, "Returning the real portrait")
213 return self._former_getPortrait(member_id)
214
215
216 def setLocalRoles( self, obj, member_ids, member_role, reindex=1 ):
217 """ Set local roles on an item """
218 member = self.getAuthenticatedMember()
219 gruf = self.acl_users
220 my_roles = member.getRolesInContext( obj )
221
222 if 'Manager' in my_roles or member_role in my_roles:
223 for member_id in member_ids:
224 u = gruf.getUserById(member_id) or gruf.getGroupByName(member_id)
225 if not u:
226 continue
227 member_id = u.getUserId()
228 roles = list(obj.get_local_roles_for_userid( userid=member_id ))
229
230 if member_role not in roles:
231 roles.append( member_role )
232 obj.manage_setLocalRoles( member_id, roles )
233
234 if reindex:
235 # It is assumed that all objects have the method
236 # reindexObjectSecurity, which is in CMFCatalogAware and
237 # thus PortalContent and PortalFolder.
238 obj.reindexObjectSecurity()
239
240 def deleteLocalRoles( self, obj, member_ids, reindex=1 ):
241 """ Delete local roles for members member_ids """
242 member = self.getAuthenticatedMember()
243 my_roles = member.getRolesInContext( obj )
244 gruf = self.acl_users
245 member_ids = [
246 u.getUserId() for u in [
247 gruf.getUserById(u) or gruf.getGroupByName(u) for u in member_ids
248 ] if u
249 ]
250
251 if 'Manager' in my_roles or 'Owner' in my_roles:
252 obj.manage_delLocalRoles( userids=member_ids )
253
254 if reindex:
255 obj.reindexObjectSecurity()
256
257 # Monkeypatch it !
258 if PREVIEW_PLONE21_IN_PLONE20_:
259 from Products.CMFCore import MembershipTool as CMFCoreMembershipTool
260 CMFCoreMembershipTool.MembershipTool.setLocalRoles = setLocalRoles
261 CMFCoreMembershipTool.MembershipTool.deleteLocalRoles = deleteLocalRoles
262 from Products.CMFPlone import MemberDataTool
263 from Products.CMFPlone import MembershipTool
264 MembershipTool.MembershipTool.searchForMembers = searchForMembers
265 MembershipTool.MembershipTool.listAllowedMembers = listAllowedMembers
266 MemberDataTool.MemberDataTool._former_getPortrait = MemberDataTool.MemberDataTool._getPortrait
267 MemberDataTool.MemberDataTool._getPortrait = _getPortrait
268 Log(LOG_NOTICE, "Applied GRUF's monkeypatch over Plone 2.0.x. Enjoy!")
269
270
271