# local role components including a PAS local role manager plugin
#  See http://goo.gl/ymRxG for (slightly outdated) documentation of the
#   basic idea here, or look at source for borg.localrole.workspace module..

from Acquisition import aq_inner
from AccessControl.class_init import InitializeClass
from AccessControl import ClassSecurityInfo
from borg.localrole.workspace import WorkspaceLocalRoleManager as BasePlugin
from borg.localrole.workspace import clra_cache_key, store_on_request
from plone.memoize.volatile import cache
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PlonePAS.interfaces.plugins import ILocalRolesPlugin
from zope.annotation.interfaces import IAnnotations
from zope.interface import implements
from zope.globalrequest import getRequest

from collective.teamwork.interfaces import IWorkspaceContext
from config import APP_ROLES


BLOCKROLES = tuple(r.get('id') for r in APP_ROLES)

filter_roles = lambda s: filter(lambda r: r not in BLOCKROLES, s)


def clear_cached_localroles(userid):
    """Given user id (not login name), clear cached localroles"""
    prefix = 'collective.teamwork.user.localrole.checkLocalRolesAllowed'
    request = getRequest()
    anno = IAnnotations(request)
    relevant = [k for k in anno.keys() if k.startswith(prefix) and userid in k]
    for key in relevant:
        del anno[key]


manage_addEnhancedWorkspaceLRMForm = PageTemplateFile(
    "zmi/WorkspaceLocalRoleManagerForm.pt", globals(),
    __name__="manage_addWorkspaceRoleManagerForm")


def manage_addEnhancedWorkspaceLRM(dispatcher, id, title=None, REQUEST=None):
    plugin = WorkspaceLocalRoleManager(id, title)
    dispatcher._setObject(plugin.getId(), plugin)
    if REQUEST is not None:
        REQUEST.RESPONSE.redirect(
            '%s/manage_workspace?manage_tabs_message='
            'WorkspaceLocalRoleManager+added.' %
            dispatcher.absolute_url(),
            )


class WorkspaceLocalRoleManager(BasePlugin):
    """
    PAS local roles manager plugin for a workspaces site; acts
    like the base plugin, except:

        *   Roles defined in collective.teamwork.user.interfaces.APP_ROLES
            are not inherited via getRolesInContext() method if the context
            itself is a workspace.  This allows for this role to be
            inherited in contained folders, but such implied access is
            not assumed for nested workspaces.

        *   All other roles have stock Plone behavior.

    Some method code borrowed as useful from superclass
    borg.localrole.workspace.WorkspaceLocalRoleManager.
    """

    meta_type = 'Enhanced workspaces roles manager'

    security = ClassSecurityInfo()

    implements(ILocalRolesPlugin)

    def _user_info(self, user):
        """Return tuple of user id and list of principal ids"""
        uf = self._get_userfolder(user)
        if uf is not None:
            user = aq_inner(user).__of__(uf)  # re-wrap, if we have uf
        principal_ids = self._get_principal_ids(user)  # user id, group names
        return (user, principal_ids)

    security.declarePrivate("getRolesInContext")

    def getRolesInContext(self, user, object):
        roles = set()
        workspace = None
        user, principal_ids = self._user_info(user)
        for obj in self._parent_chain(object):
            if user._check_context(obj):
                for provider in self._getAdapters(obj):
                    for principal_id in principal_ids:
                        context_roles = list(provider.getRoles(principal_id))
                        if workspace:
                            # once you have seen a previous workspace, there
                            # are certain roles you DO NOT want to inherit
                            # from a higher-level workspace in the containment
                            # hierarchy.  We filter those roles from the list
                            # for all but the first seen workspace walking up
                            # the hierarchy.
                            context_roles = filter_roles(context_roles)
                        roles.update(context_roles)
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return list(roles)

    security.declarePrivate("checkLocalRolesAllowed")

    @cache(get_key=clra_cache_key, get_cache=store_on_request)
    def checkLocalRolesAllowed(self, user, object, object_roles):
        user, principal_ids = self._user_info(user)
        workspace = None
        check_roles = set(object_roles)
        for obj in self._parent_chain(object):
            for provider in self._getAdapters(obj):
                for principal_id in principal_ids:
                    roles = list(provider.getRoles(principal_id))
                    if workspace:
                        # seen previous workspace, don't inherit some roles
                        roles = filter_roles(roles)
                    if check_roles.intersection(roles):
                        if user._check_context(obj):
                            return 1
                        else:
                            return 0
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return None

    security.declarePrivate("getAllLocalRolesInContext")

    def getAllLocalRolesInContext(self, object):
        rolemap = {}
        workspace = None
        for obj in self._parent_chain(object):
            for provider in self._getAdapters(obj):
                iter_roles = provider.getAllRoles()
                for principal, roles in iter_roles:
                    if workspace:
                        # seen previous workspace, don't inherit some roles
                        roles = filter_roles(roles)
                    rolemap.setdefault(principal, set()).update(roles)
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return rolemap


InitializeClass(WorkspaceLocalRoleManager)  # set up traversal security!


# local role components including a PAS local role manager plugin
#  See http://goo.gl/ymRxG for (slightly outdated) documentation of the
#   basic idea here, or look at source for borg.localrole.workspace module..

from Acquisition import aq_inner
from AccessControl.class_init import InitializeClass
from AccessControl import ClassSecurityInfo
from borg.localrole.workspace import WorkspaceLocalRoleManager as BasePlugin
from borg.localrole.workspace import clra_cache_key, store_on_request
from plone.memoize.volatile import cache
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PlonePAS.interfaces.plugins import ILocalRolesPlugin
from zope.annotation.interfaces import IAnnotations
from zope.interface import implements
from zope.globalrequest import getRequest

from collective.teamwork.interfaces import IWorkspaceContext
from config import APP_ROLES


BLOCKROLES = tuple(r.get('id') for r in APP_ROLES)

filter_roles = lambda s: filter(lambda r: r not in BLOCKROLES, s)


def clear_cached_localroles(userid):
    """Given user id (not login name), clear cached localroles"""
    prefix = 'collective.teamwork.user.localrole.checkLocalRolesAllowed'
    request = getRequest()
    anno = IAnnotations(request)
    relevant = [k for k in anno.keys() if k.startswith(prefix) and userid in k]
    for key in relevant:
        del anno[key]


manage_addEnhancedWorkspaceLRMForm = PageTemplateFile(
    "zmi/WorkspaceLocalRoleManagerForm.pt", globals(),
    __name__="manage_addWorkspaceRoleManagerForm")


def manage_addEnhancedWorkspaceLRM(dispatcher, id, title=None, REQUEST=None):
    plugin = WorkspaceLocalRoleManager(id, title)
    dispatcher._setObject(plugin.getId(), plugin)
    if REQUEST is not None:
        REQUEST.RESPONSE.redirect(
            '%s/manage_workspace?manage_tabs_message='
            'WorkspaceLocalRoleManager+added.' %
            dispatcher.absolute_url(),
            )


class WorkspaceLocalRoleManager(BasePlugin):
    """
    PAS local roles manager plugin for a workspaces site; acts
    like the base plugin, except:

        *   Roles defined in collective.teamwork.user.interfaces.APP_ROLES
            are not inherited via getRolesInContext() method if the context
            itself is a workspace.  This allows for this role to be
            inherited in contained folders, but such implied access is
            not assumed for nested workspaces.

        *   All other roles have stock Plone behavior.

    Some method code borrowed as useful from superclass
    borg.localrole.workspace.WorkspaceLocalRoleManager.
    """

    meta_type = 'Enhanced workspaces roles manager'

    security = ClassSecurityInfo()

    implements(ILocalRolesPlugin)

    def _user_info(self, user):
        """Return tuple of user id and list of principal ids"""
        uf = self._get_userfolder(user)
        if uf is not None:
            user = aq_inner(user).__of__(uf)  # re-wrap, if we have uf
        principal_ids = self._get_principal_ids(user)  # user id, group names
        return (user, principal_ids)

    security.declarePrivate("getRolesInContext")

    def getRolesInContext(self, user, object):
        roles = set()
        workspace = None
        user, principal_ids = self._user_info(user)
        for obj in self._parent_chain(object):
            if user._check_context(obj):
                for provider in self._getAdapters(obj):
                    for principal_id in principal_ids:
                        context_roles = list(provider.getRoles(principal_id))
                        if workspace:
                            # once you have seen a previous workspace, there
                            # are certain roles you DO NOT want to inherit
                            # from a higher-level workspace in the containment
                            # hierarchy.  We filter those roles from the list
                            # for all but the first seen workspace walking up
                            # the hierarchy.
                            context_roles = filter_roles(context_roles)
                        roles.update(context_roles)
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return list(roles)

    security.declarePrivate("checkLocalRolesAllowed")

    @cache(get_key=clra_cache_key, get_cache=store_on_request)
    def checkLocalRolesAllowed(self, user, object, object_roles):
        user, principal_ids = self._user_info(user)
        workspace = None
        check_roles = set(object_roles)
        for obj in self._parent_chain(object):
            for provider in self._getAdapters(obj):
                for principal_id in principal_ids:
                    roles = list(provider.getRoles(principal_id))
                    if workspace:
                        # seen previous workspace, don't inherit some roles
                        roles = filter_roles(roles)
                    if check_roles.intersection(roles):
                        if user._check_context(obj):
                            return 1
                        else:
                            return 0
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return None

    security.declarePrivate("getAllLocalRolesInContext")

    def getAllLocalRolesInContext(self, object):
        rolemap = {}
        workspace = None
        for obj in self._parent_chain(object):
            for provider in self._getAdapters(obj):
                iter_roles = provider.getAllRoles()
                for principal, roles in iter_roles:
                    if workspace:
                        # seen previous workspace, don't inherit some roles
                        roles = filter_roles(roles)
                    rolemap.setdefault(principal, set()).update(roles)
            if IWorkspaceContext.providedBy(obj):
                workspace = obj  # mark ws as seen before looking at parents
        return rolemap


InitializeClass(WorkspaceLocalRoleManager)  # set up traversal security!


# local role components including a PAS local role manager plugin #  See http://goo.gl/ymRxG for (slightly outdated) documentation of the #   basic idea here, or look at source for borg.localrole.workspace module.. Given user id (not login name), clear cached localroles PAS local roles manager plugin for a workspaces site; acts     like the base plugin, except:          *   Roles defined in collective.teamwork.user.interfaces.APP_ROLES             are not inherited via getRolesInContext() method if the context             itself is a workspace.  This allows for this role to be             inherited in contained folders, but such implied access is             not assumed for nested workspaces.          *   All other roles have stock Plone behavior.      Some method code borrowed as useful from superclass     borg.localrole.workspace.WorkspaceLocalRoleManager. Return tuple of user id and list of principal ids # re-wrap, if we have uf # user id, group names # once you have seen a previous workspace, there # are certain roles you DO NOT want to inherit # from a higher-level workspace in the containment # hierarchy.  We filter those roles from the list # for all but the first seen workspace walking up # the hierarchy. # mark ws as seen before looking at parents # seen previous workspace, don't inherit some roles # mark ws as seen before looking at parents # seen previous workspace, don't inherit some roles # mark ws as seen before looking at parents # set up traversal security!