source: enpraxis.educommons/trunk/enpraxis/educommons/browser/sharing.py @ 384

Revision 384, 17.6 KB checked in by david, 4 years ago (diff)

updating licensing information

Line 
1##################################################################################
2#    Copyright (c) 2004-2009 Utah State University, All rights reserved.
3#    Portions copyright 2009 Massachusetts Institute of Technology, All rights reserved.
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; if not, write to the Free Software                 
17#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   
18#                                                                                 
19##################################################################################
20
21__author__  = '''Brent Lambert, David Ray, Jon Thomas'''
22__version__   = '$ Revision 0.0 $'[11:-2]
23
24from zope.component import getUtilitiesFor, queryUtility, getMultiAdapter
25
26from Products.Five.browser import BrowserView
27from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
28
29from Acquisition import aq_inner, aq_parent, aq_base
30from AccessControl import Unauthorized
31from zExceptions import Forbidden
32
33from Products.CMFCore.utils import getToolByName
34from Products.CMFCore import permissions
35from Products.CMFPlone import PloneMessageFactory as _
36
37from plone.memoize.instance import memoize, clearafter
38
39from plone.app.workflow.interfaces import ISharingPageRole
40
41from enpraxis.educommons.browser.interfaces import IeduCommonsSharingPageRole
42
43AUTH_GROUP = 'AuthenticatedUsers'
44STICKY = (AUTH_GROUP,)
45
46class SharingView(BrowserView):
47   
48    # Actions
49    template = ViewPageTemplateFile('sharing.pt')
50   
51    def __call__(self):
52        """Perform the update and redirect if necessary, or render the page
53        """
54       
55        postback = True
56       
57        form = self.request.form
58        submitted = form.get('form.submitted', False)
59   
60        save_button = form.get('form.button.Save', None) is not None
61        cancel_button = form.get('form.button.Cancel', None) is not None
62   
63        if submitted and not cancel_button:
64
65            if not self.request.get('REQUEST_METHOD','GET') == 'POST':
66                raise Forbidden
67           
68            # Update the acquire-roles setting
69            inherit = bool(form.get('inherit', False))
70            self.update_inherit(inherit)
71
72            # Update settings for users and groups
73            entries = form.get('entries', [])
74            roles = [r['id'] for r in self.roles()]
75            settings = []
76            for entry in entries:
77                settings.append(
78                    dict(id = entry['id'],
79                         type = entry['type'],
80                         roles = [r for r in roles if entry.get('role_%s' % r, False)]))
81            if settings:
82                self.update_role_settings(settings)
83           
84        # Other buttons return to the sharing page
85        if cancel_button:
86            postback = False
87       
88        if postback:
89            return self.template()
90        else:
91            context_state = self.context.restrictedTraverse("@@plone_context_state")
92            url = context_state.view_url()
93            self.request.response.redirect(url)
94           
95    # View
96   
97    @memoize
98    def roles(self):
99        """Get a list of roles that can be managed.
100       
101        Returns a list of dics with keys:
102       
103            - id
104            - title
105        """
106        context = aq_inner(self.context)
107        portal_membership = getToolByName(context, 'portal_membership')
108       
109        pairs = []
110
111        #check to see if OpenOCW installed
112        portal_setup = self.aq_parent.portal_setup
113        if 'openOCW-final' in portal_setup.getImportStepRegistry().listSteps():
114            #user default sharing page roles
115            for name, utility in getUtilitiesFor(ISharingPageRole):
116                permission = utility.required_permission
117                if permission is None or portal_membership.checkPermission(permission, context):
118                    pairs.append(dict(id = name, title = utility.title))
119        else:
120            #use eduCommons sharing page roles
121            for name, utility in getUtilitiesFor(IeduCommonsSharingPageRole):
122                permission = utility.required_permission
123                if permission is None or portal_membership.checkPermission(permission, context):
124                    pairs.append(dict(id = name, title = utility.title))
125
126        pairs.sort(lambda x, y: cmp(x['id'], y['id']))
127        return pairs
128       
129    @memoize
130    def role_settings(self):
131        """Get current settings for users and groups for which settings have been made.
132       
133        Returns a list of dicts with keys:
134       
135         - id
136         - title
137         - type (one of 'group' or 'user')
138         - roles
139         
140        'roles' is a dict of settings, with keys of role ids as returned by
141        roles(), and values True if the role is explicitly set, False
142        if the role is explicitly disabled and None if the role is inherited.
143        """
144       
145        existing_settings = self.existing_role_settings()
146        user_results = self.user_search_results()
147        group_results = self.group_search_results()
148
149        return existing_settings + user_results + group_results
150       
151    def inherited(self, context=None):
152        """Return True if local roles are inherited here.
153        """
154        if context is None:
155            context = self.context
156        if getattr(aq_base(context), '__ac_local_roles_block__', None):
157            return False
158        return True
159       
160    # helper functions
161   
162    @memoize
163    def existing_role_settings(self):
164        """Get current settings for users and groups that have already got
165        at least one of the managed local roles.
166
167        Returns a list of dicts as per role_settings()
168        """
169        context = aq_inner(self.context)
170       
171        portal_membership = getToolByName(aq_inner(self.context), 'portal_membership')
172        portal_groups = getToolByName(aq_inner(self.context), 'portal_groups')
173        portal = getToolByName(aq_inner(self.context), 'portal_url').getPortalObject()
174        acl_users = getattr(portal, 'acl_users')
175       
176        info = []
177       
178        # This logic is adapted from computeRoleMap.py
179       
180        local_roles = acl_users.getLocalRolesForDisplay(context)
181        acquired_roles = self._inherited_roles()
182        available_roles = [r['id'] for r in self.roles()]
183
184        # first process acquired roles
185        items = {}
186        for name, roles, rtype, rid in acquired_roles:
187            items[rid] = dict(id       = rid,
188                              name     = name,
189                              type     = rtype,
190                              sitewide = [],
191                              acquired = roles,
192                              local    = [],)
193                               
194        # second process local roles
195        for name, roles, rtype, rid in local_roles:
196            if items.has_key(rid):
197                items[rid]['local'] = roles
198            else:
199                items[rid] = dict(id       = rid,
200                                  name     = name,
201                                  type     = rtype,
202                                  sitewide = [],
203                                  acquired = [],
204                                  local    = roles,)
205
206        # Make sure we always get the authenticated users virtual group
207        if AUTH_GROUP not in items:
208            items[AUTH_GROUP] = dict(id = AUTH_GROUP,
209                                     name = _(u'Logged-in users'),
210                                     type  = 'group',
211                                     sitewide = [],
212                                     acquired = [],
213                                     local = [],)
214
215        # Sort the list: first the authenticated users virtual group, then
216        # all other groups and then all users, alphabetically
217
218        dec_users = [( a['id'] not in STICKY,
219                       a['type'],
220                       a['name'],
221                       a) for a in items.values()]
222        dec_users.sort()
223       
224        # Add the items to the info dict, assigning full name if possible.
225        # Also, recut roles in the format specified in the docstring
226       
227        for d in dec_users:
228            item = d[-1]
229            name = item['name']
230            rid = item['id']
231            global_roles = set()
232           
233            if item['type'] == 'user':
234                member = acl_users.getUserById(rid)
235                if member is not None:
236                    name = member.getProperty('fullname') or member.getId() or name
237                    global_roles = set(member.getRoles())
238            elif item['type'] == 'group':
239                g = portal_groups.getGroupById(rid)
240                name = g.getGroupTitleOrName()
241                global_roles = set(g.getRoles())
242               
243                # This isn't a proper group, so it needs special treatment :(
244                if rid == AUTH_GROUP:
245                    name = _(u'Logged-in users')
246           
247            info_item = dict(id    = item['id'],
248                             type  = item['type'],
249                             title = name,
250                             roles = {})
251                             
252            # Record role settings
253            have_roles = False
254            for r in available_roles:
255                if r in global_roles:
256                    info_item['roles'][r] = 'global'
257                elif r in item['acquired']:
258                    info_item['roles'][r] = 'acquired'
259                    have_roles = True # we want to show acquired roles
260                elif r in item['local']:
261                    info_item['roles'][r] = True
262                    have_roles = True # at least one role is set
263                else:
264                    info_item['roles'][r] = False
265                   
266            if have_roles or rid in STICKY:
267                info.append(info_item)
268           
269        return info
270       
271    def user_search_results(self):
272        """Return search results for a query to add new users
273       
274        Returns a list of dicts, as per role_settings()
275        """
276        context = aq_inner(self.context)
277        acl_users = getToolByName(context, 'acl_users')
278       
279        search_term = self.request.form.get('search_term', None)
280        if not search_term:
281            return []
282           
283        existing_users = set([u['id'] for u in self.existing_role_settings()
284                                if u['type'] == 'user'])
285        empty_roles = dict([(r['id'], False) for r in self.roles()])
286        info = []
287       
288        hunter = getMultiAdapter((context, self.request), name='pas_search')
289        for userinfo in hunter.searchUsers(fullname=search_term):
290            userid = userinfo['userid']
291            if userid not in existing_users:
292                user = acl_users.getUserById(userid)
293                roles = empty_roles.copy()
294                for r in user.getRoles():
295                    if r in roles:
296                        roles[r] = 'global'
297                info.append(dict(id    = userid,
298                                 title = user.getProperty('fullname') or user.getId() or userid,
299                                 type  = 'user',
300                                 roles = roles))
301        return info
302       
303    def group_search_results(self):
304        """Return search results for a query to add new groups
305       
306        Returns a list of dicts, as per role_settings()
307        """
308        context = aq_inner(self.context)
309        portal_groups = getToolByName(context, 'portal_groups')
310       
311        search_term = self.request.form.get('search_term', None)
312        if not search_term:
313            return []
314           
315        existing_groups = set([g['id'] for g in self.existing_role_settings()
316                                if g['type'] == 'group'])
317        empty_roles = dict([(r['id'], False) for r in self.roles()])
318        info = []
319       
320        hunter = getMultiAdapter((context, self.request), name='pas_search')
321        for groupinfo in hunter.searchGroups(id=search_term):
322            groupid = groupinfo['groupid']
323            if groupid not in existing_groups:
324                group = portal_groups.getGroupById(groupid)
325                roles = empty_roles.copy()
326                for r in group.getRoles():
327                    if r in roles:
328                        roles[r] = 'global'               
329                info.append(dict(id    = groupid,
330                                 title = group.getGroupTitleOrName(),
331                                 type  = 'group',
332                                 roles = roles))
333        return info
334       
335    def _inherited_roles(self):
336        """Returns a tuple with the acquired local roles."""
337        context = aq_inner(self.context)
338       
339        if not self.inherited(context):
340            return []
341       
342        portal = getToolByName(context, 'portal_url').getPortalObject()
343        result = []
344        cont = True
345        if portal != context:
346            parent = aq_parent(context)
347            while cont:
348                if not getattr(parent, 'acl_users', False):
349                    break
350                userroles = parent.acl_users._getLocalRolesForDisplay(parent)
351                for user, roles, role_type, name in userroles:
352                    # Find user in result
353                    found = 0
354                    for user2, roles2, type2, name2 in result:
355                        if user2 == user:
356                            # Check which roles must be added to roles2
357                            for role in roles:
358                                if not role in roles2:
359                                    roles2.append(role)
360                            found = 1
361                            break
362                    if found == 0:
363                        # Add it to result and make sure roles is a list so
364                        # we may append and not overwrite the loop variable
365                        result.append([user, list(roles), role_type, name])
366                if parent == portal:
367                    cont = False
368                elif not self.inherited(parent):
369                    # Role acquired check here
370                    cont = False
371                else:
372                    parent = aq_parent(parent)
373
374        # Tuplize all inner roles
375        for pos in range(len(result)-1,-1,-1):
376            result[pos][1] = tuple(result[pos][1])
377            result[pos] = tuple(result[pos])
378
379        return tuple(result)
380       
381    def update_inherit(self, status=True):
382        """Enable or disable local role acquisition on the context.
383        """
384        context = aq_inner(self.context)
385        portal_membership = getToolByName(context, 'portal_membership')
386       
387        if not portal_membership.checkPermission(permissions.ModifyPortalContent, context):
388            raise Unauthorized
389
390        if not status:
391            context.__ac_local_roles_block__ = True
392        else:
393            if getattr(aq_base(context), '__ac_local_roles_block__', None):
394                context.__ac_local_roles_block__ = None
395
396        context.reindexObjectSecurity()
397       
398    @clearafter
399    def update_role_settings(self, new_settings):
400        """Update local role settings and reindex object security if necessary.
401       
402        new_settings is a list of dicts with keys id, for the user/group id;
403        type, being either 'user' or 'group'; and roles, containing the list
404        of role ids that are set.
405        """
406       
407        reindex = False
408        context = aq_inner(self.context)
409           
410        managed_roles = frozenset([r['id'] for r in self.roles()])
411        member_ids_to_clear = []
412        for s in new_settings:
413            user_id = s['id']
414           
415            existing_roles = frozenset(context.get_local_roles_for_userid(userid=user_id))
416            selected_roles = frozenset(s['roles'])
417           
418            # We will remove those roles that we are managing and which set
419            # on the context, but which were not selected
420            to_remove = (managed_roles & existing_roles) - selected_roles
421           
422            # Leaving us with the selected roles, less any roles that we
423            # want to remove
424            new_roles = (selected_roles | existing_roles) - to_remove
425           
426            # take away roles that we are managing, that were not selected
427            # and which were part of the existing roles
428           
429            if new_roles:
430                context.manage_setLocalRoles(user_id, list(new_roles))
431                reindex = True
432            elif existing_roles:
433                member_ids_to_clear.append(user_id)
434               
435        if member_ids_to_clear:
436            context.manage_delLocalRoles(userids=member_ids_to_clear)
437            reindex = True
438       
439        if reindex:
440            self.context.reindexObjectSecurity()
Note: See TracBrowser for help on using the repository browser.