##############################################################################
# Copyright (c) 2004-2005 Black Rock City, LLC
# Copyright (c) 2004-2005 ObjectRealms, LLC
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
##############################################################################
"""
Description:
Created: March 31, 2004
$Id:
"""

from AccessControl import ClassSecurityInfo
from Interface import Interface

from Products.Archetypes.public import BaseBTreeFolder, registerType

from Products.CMFCore import CMFCorePermissions
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.ActionInformation import ActionInformation
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.utils import UniqueObject

from Products.TeamSpace.permissions import ManageTeam
from Products.TeamSpace.relations import TeamMembershipRelation
from Products.TeamSpace.relations import TeamSpaceTeamRelation
from Products.TeamSpace.relations import MemberTeamRelation
from Products.TeamSpace.team import Team
from Products.TeamSpace.membership import TeamMembership
from Products.TeamSpace.zimport import *

from App.Common import package_home
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from OFS.Folder import Folder
try:
    set = set
except NameError:
    from sets import Set as set
import os

from interfaces.team import ITeam
from interfaces.membership import ITeamMembership

_zmi = os.path.join(os.path.dirname(__file__), 'zmi')

class TeamsTool(UniqueObject, BaseBTreeFolder, ActionProviderBase):
    """ Stores teams objects """

    _default_roles = None

    id = 'portal_teams'
    meta_type = 'Teams Tool'
    title = 'Teams'

    manage_options=(
        {'label':'Policy', 'action':'manage_globalTeamPolicyForm'},
        ) + ActionProviderBase.manage_options + Folder.manage_options

    manage_globalTeamPolicyForm=PageTemplateFile('global_team_policy', _zmi)

    security = ClassSecurityInfo()

    # Action Provider Actions
    _actions = (
       ActionInformation(
        id='myteamspaces',
        title='My Teams',
        action='python:portal.portal_membership.getHomeUrl()+"/ts_my_teamspaces"',
        permissions=(CMFCorePermissions.View,),
        category='user',
        condition='python: member and portal.portal_membership.getHomeFolder()',
        visible=1,
        ),
    )
    
    allowed_content_types = (Team.portal_type,)
    allowed_membership_types =  (TeamMembership.portal_type,)
    content_icon = 'teams_tool.png'
    global_allow = 0

    _default_membership_type = TeamMembership.portal_type
    
    def __init__(self, id=''):
        BaseBTreeFolder.__init__(self, self.id)
        self.title = TeamsTool.title
        # Used at team creation time
        self._default_roles = ()
        self._default_allowed_roles = ()
        # We hardcode these settings for now
        self._default_active_states = ('active',)
        self._default_teamactive_states = ('published',)
        # make _actions an instance value, the action object will behave
        # more predictably
        self._actions = self._actions

    security.declarePrivate('getRefTargetBrains')
    def getRefTargetBrains(self, obj, relationship=None):
        """
        Performs reference_ and portal_catalog queries to return the
        catalog brains from the portal_catalog for all objects that
        the object has references to w/ the specified relationship.

        Never actually touches the referenced objects, faster than using
        the getRefs API.
        """
        rcat = getToolByName(self, 'reference_catalog')
        cat = getToolByName(self, 'portal_catalog')
        objuid = obj.UID()
        refquery = {'sourceUID': objuid}
        if relationship is not None:
            refquery['relationship'] = relationship
        refbrains = rcat(**refquery)
        targetuids = [b.targetUID for b in refbrains]
        targetbrains = cat(UID=targetuids)
        return targetbrains

    security.declarePrivate('getRefSourceBrains')
    def getRefSourceBrains(self, obj, relationship=None):
        """
        Performs reference_ and portal_catalog queries to return the
        catalog brains from the portal_catalog for all objects that
        the object has back-references to w/ the specified
        relationship.

        Never actually touches the referenced objects, faster than
        using the getRefs API.
        """
        rcat = getToolByName(self, 'reference_catalog')
        cat = getToolByName(self, 'portal_catalog')
        objuid = obj.UID()
        refquery = {'targetUID': objuid}
        if relationship is not None:
            refquery['relationship'] = relationship
        refbrains = rcat(**refquery)
        sourceuids = [b.sourceUID for b in refbrains]
        sourcebrains = cat(UID=sourceuids)
        return sourcebrains

    def getTeamById(self, team_id):
        """
        returns specified team object
        """
        team = None
        if self.has_key(team_id):
            team = self._getOb( team_id )
        if team != None:
            assert team.meta_type in self.getAllowedTeamTypes(), \
                   "Invalid Team Type"
        return team

    def _conditionalGetMember(self, member_id=None):
        """
        returns the member specified by member_id, or authenticated
        member if member_id is None
        """
        mtool = getToolByName(self, 'portal_membership')
        if member_id is None:
            return mtool.getAuthenticatedMember()
        else:
            return mtool.getMemberById(member_id)

    def getTeamsForMember(self, member, roles=[], active=None):
        """
        returns the team objects for the provided member object
        """
        member_id = member.getId()
        memuid = member.UID()
        relationship = MemberTeamRelation.relationship
        teambrains = self.getRefTargetBrains(member, relationship)
        teams = [b._unrestrictedGetObject() for b in teambrains]
        if not roles and not active:
            return teams

        memberships = []
        for team in teams:
            mship = team.getMembershipByMemberId(member_id,
                                                 active_only=active)
            if mship:
                memberships.append(mship)

        # Filter for roles
        if roles:
            def checkroles(mb):
                return set(roles) <= set(mb.getTeamRoles())
            memberships = filter(checkroles, memberships)
        return [m.getTeam() for m in memberships]    
        
    def getTeamsByMemberId(self, member_id=None, roles=[], active=None):
        """
        get teams, filter by roles. roles must be a subset of
        membership roles to return. Setting active to a True value
        returns only teams where member has an active membership
        """
        member = self._conditionalGetMember(member_id)
        if member is None or not hasattr(member, 'UID'):
            return []

        return self.getTeamsForMember(member, roles, active)

    def getTeamSpacesFor(self, member_id=None, active=None):
        """
        Return a list of all teamspaces of which this team is a member
        """
        teams=self.getTeamMembershipsFor(member_id, active)
        if not teams:
            return []
        spaces=reduce(lambda x,y:x+y, [t.getTeamSpaces() for t in teams])
        return list(set(spaces))

    def getTeamMembershipsFor(self, member_id=None, active=None):
        """
        Setting active to a non None value returns only active memberships
        """
        member = self._conditionalGetMember(member_id)
        if member is None or not hasattr(member, 'UID'):
            return []

        memuid = member.UID()
        relationship = TeamMembershipRelation.relationship
        mshipbrains = self.getRefTargetBrains(member, relationship)

        if not active:
            return [b.getObject() for b in mshipbrains]

        memberships = []
        for b in mshipbrains:
            mship = b.getObject()
            if b.review_state in mship.getTeam().getActiveStates():
                memberships.append(mship)

        return memberships

    def getTeams(self):
        cat = getToolByName(self, 'portal_catalog')
        teams = cat(portal_type = self.getAllowedTeamTypes() )
        return [ t.getObject() for t in teams ]

    def getMembershipsByTeamId(self, team_id):
        """ Returns all team memberships for a team_id """
        return self.getTeamById(team_id).getMemberships()

    def getMembersByTeamId(self, team_id):
        """ Returns all memberdata objects for a team_id """
        return self.getTeamById( team_id).getMembers()

    ## ZMI Utility Methods 
    security.declareProtected(ManageTeam, 'setDefaultRoles')
    def setDefaultRoles(self, default_roles=None, REQUEST=None):
        """ Set the default role assigned when membership is created inside a team """
        if default_roles is None and REQUEST:
            default_roles = REQUEST.get('default_roles')
        self._default_roles = default_roles

        if REQUEST:
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    def getDefaultRoles(self):
        """ Returns the default role assigned when membership is created inside a team """
        return self._default_roles

    security.declareProtected(ManageTeam, 'setDefaultAllowedRoles')
    def setDefaultAllowedRoles(self, allowed_roles=None, REQUEST=None):
        """
        Set the default allowed role assigned when membership is created inside a team
        """
        if allowed_roles is None and REQUEST:
            allowed_roles = REQUEST.get('allowed_roles')

        self._default_allowed_roles = allowed_roles

        if REQUEST:
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    def getDefaultAllowedRoles(self):
        """
        return the allowed roles passed to initialize teams
        """
        return self._default_allowed_roles


    security.declareProtected(ManageTeam, 'setDefaultActiveStates')
    def setDefaultActiveStates(self, default_active_states=None, REQUEST=None):
        """ Sets the default workflow states that define a team
        membership as active """
        if not default_active_states:
            default_active_states = ()
            if REQUEST:
                default_active_states = REQUEST.get('default_active_states', ())

        if type('') == type(default_active_states):
            default_active_states = (default_active_states, )

        self._default_active_states = default_active_states
        if REQUEST:
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    def getDefaultActiveStates(self):
        """ Returns the default workflow states that define a team membership as active """
        return self._default_active_states

    security.declareProtected(ManageTeam, 'setAllowedTeamTypes')
    def setAllowedTeamTypes(self, allowed_types):
        """
        set what teams are allowed in the team tool
        """
        t_tool = getToolByName(self, 'portal_types')
        type_info = t_tool.getTypeInfo( self )
        all_types = t_tool.listContentTypes()

        act = set(type_info.allowed_content_types)
        # remove the other team types
        act.difference_update( self.getITeamTypes() )
        
        for team_type in allowed_types:
            assert team_type in all_types, "Invalid Team Type"
            act.add(team_type)

        types = tuple(act)
        self.allowed_content_types = types
        type_info.allowed_content_types = types

    security.declareProtected(ManageTeam, 'getAllowedTeamTypes')
    def getAllowedTeamTypes(self):
        t_tool = getToolByName(self, 'portal_types')
        type_info = t_tool.getTypeInfo( self )
        content_types = set( type_info.allowed_content_types )
        return tuple( content_types.intersection( self.getITeamTypes() ) )

    security.declareProtected(ManageTeam, 'setAllowedMembershipTypes')
    def setAllowedMembershipTypes(self, allowed_types):
        """
        set what memberships are allowed in the teams
        """
        self.allowed_membership_types=tuple(allowed_types)

    security.declareProtected(ManageTeam, 'getAllowedMembershipTypes')
    def getAllowedMembershipTypes(self):
        """
        get the set of allowed team membership types
        """
        return self.allowed_membership_types
        
    security.declareProtected(ManageTeam, 'setDefaultMembershipType')
    def setDefaultMembershipType(self, mship_type):
        """
        set the default team membership type
        """
        if mship_type not in self.getAllowedMembershipTypes():
            raise ValueError, "Unsupported team membership type: %s" % mship_type
        self._default_membership_type = mship_type

    security.declareProtected(ManageTeam, 'getDefaultMembershipType')
    def getDefaultMembershipType(self):
        """
        return the default team membership type
        """
        return self._default_membership_type

    security.declareProtected(ManageTeam, 'getITeamTypes')
    def getITeamTypes(self):
        at_tool = getToolByName(self, 'archetype_tool')
        return [ klass.portal_type for klass in at_tool.listTypes()\
                 if ITeam.implementedBy(klass) ]
    
    security.declareProtected(ManageTeam, 'getITeamMembershipTypes')
    def getITeamMembershipTypes(self):
        at_tool = getToolByName(self, 'archetype_tool')
        return [ klass.portal_type for klass in at_tool.listTypes()\
                 if ITeamMembership.implementedBy(klass) ]
        
    security.declareProtected(ManageTeam, 'manage_globalTeamPolicy')
    def manage_globalTeamPolicy( self,
                                 allowed_roles=[],
                                 default_roles=[],
                                 default_active_states=[],
                                 allowed_types=[],
                                 allowed_membership_types=[],
                                 default_membership_type=[],
                                 REQUEST=None ):
        
        """Form processing management method"""
        
        self.setDefaultAllowedRoles(allowed_roles)
        self.setDefaultRoles(default_roles)
        self.setDefaultActiveStates(default_active_states)
        self.setAllowedTeamTypes(allowed_types)
        self.setAllowedMembershipTypes(allowed_membership_types)
        self.setDefaultMembershipType(default_membership_type)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        BaseBTreeFolder.manage_afterAdd(self, item, container)
        # Set up the default allowed roles
        mt = getToolByName(self, 'portal_membership')
        self._default_allowed_roles = mt.getPortalRoles()

registerType( TeamsTool )


