#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-


# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
#     Romain Chantereau <rchantereau@entrouvert.com>
#     Nicolas Clapiès <nclapies@easter-eggs.org>
#     Pierre-Antoine Dejace <padejace@entrouvert.be>
#     Thierry Dulieu <tdulieu@easter-eggs.com>
#     Florent Monnier <monnier@codelutin.com>
#     Cédric Musso <cmusso@easter-eggs.org>
#     Frédéric Péters <fpeters@entrouvert.be>
#     Benjamin Poussin <poussin@codelutin.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#     Sébastien Régnier <regnier@codelutin.com>
#     Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
#     Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
#     Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
#     Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
#     Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


__doc__ = """Glasnost Groups Server"""

__version__ = '$Revision$'[11:-2]


import sys

glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
sys.path.insert(0, glasnostPythonDir)

import glasnost.common.context as context
import glasnost.common.GroupsCommon as commonGroups
import glasnost.common.faults as faults
import glasnost.common.tools_new as commonTools

import glasnost.server.ObjectsServer as objects
from glasnost.server.tools import *

from glasnost.proxy.CacheProxy import invalidateValue
from glasnost.proxy.DispatcherProxy import getApplicationId
from glasnost.proxy.tools import getProxy


applicationName = 'GroupsServer'
applicationRole = 'groups'
dispatcher = None


class AdminGroups(objects.AdminServerMixin, commonGroups.AdminGroups):
    pass
objects.register(AdminGroups)


class GroupMixin(objects.ObjectServerMixin):
    def getGroup(self, groupId):
        if commonTools.extractDispatcherId(groupId) == context.getVar(
                'dispatcherId'):
            return self.getServer().getInternalGroup(groupId)
        else:
            print '### External group: %s' % groupId
            return getProxy(groupId).getObject(groupId)


class GroupAll(GroupMixin, commonGroups.GroupAll):
    pass
objects.register(GroupAll)


class GroupDelta(GroupMixin, commonGroups.GroupDelta):
    pass
objects.register(GroupDelta)


class GroupIntersection(GroupMixin, commonGroups.GroupIntersection):
    pass
objects.register(GroupIntersection)


class GroupRole(GroupMixin, commonGroups.GroupRole):
    pass
objects.register(GroupRole)


class GroupUnion(GroupMixin, commonGroups.GroupUnion):
    pass
objects.register(GroupUnion)


# Compatibility with Glasnost < 0.7 (TODO: remove someday)
Group = GroupUnion


class GroupsServer(commonGroups.GroupsCommonMixin, objects.ObjectsServer):
    def addObjectMember(self, objectId, memberId):
        """Add a member to an union group."""

        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        if not self.canModifyObject(objectId):
            clientToken = context.getVar('clientToken')
            clientId = getApplicationId(clientToken)
            clientRole = commonTools.extractRole(clientId)
            if clientRole not in ('accountrequests', 'identities'):
                raise faults.UserAccessDenied()
        object = virtualServer.loadObjectCore(objectId)
        if not isinstance(object, GroupUnion):
            raise faults.NotAGroupUnion(object)
        object.acquireNonCore()
        try:
            if not object.canBeModified():
                raise faults.ReadOnlyObject()
            object.version += 1
            if object.membersSet is None:
                object.membersSet = []
            object.membersSet.append(memberId)
            object.saveNonCore()
        finally:
            object.releaseNonCore()
        virtualServer.markObjectAsDirty(object)
        invalidateValue(objectId)
        return object.version

    #def canGetObject(self, objectId):
    #    """An object is always readable by everybody."""
    #    return 1

    def deleteObjectMember(self, objectId, memberId):
        """Remove a member from an union group."""
        
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        if not self.canModifyObject(objectId):
            clientToken = context.getVar('clientToken')
            clientId = getApplicationId(clientToken)
            clientRole = commonTools.extractRole(clientId)
            if clientRole != 'identities':
                raise faults.UserAccessDenied()
        object = virtualServer.loadObjectCore(objectId)
        if not isinstance(object, GroupUnion):
            raise faults.NotAGroupUnion(object)
        object.acquireNonCore()
        try:
            if not object.canBeModified():
                raise faults.ReadOnlyObject()
            object.version += 1
            if object.membersSet is None:
                object.membersSet = []
            if memberId in object.membersSet:
                object.membersSet.remove(memberId)
            else:
                # trying to remove non-existent object;
                # should it raise an exception ?
                pass
            object.saveNonCore()
        finally:
            object.releaseNonCore()
        virtualServer.markObjectAsDirty(object)
        invalidateValue(objectId)
        return object.version

    def getInternalGroup(self, groupId):
        virtualServerId = self.computeVirtualServerId(groupId)
        virtualServer = self.getVirtualServer(virtualServerId)
        group = virtualServer.loadObjectCore(groupId)
        return group

    def getObjectIdsWithContent(self, objectId, indirect = 0):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        result = []
        for object in virtualServer.objects.values():
            if object.contains(objectId, indirect = indirect):
                result.append(object.id)
        return result

    def objectContains(self, objectId, containedId):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        object = virtualServer.loadObjectCore(objectId)
        result = object.contains(containedId)
        return result

    def registerPublicMethods(self):
        objects.ObjectsServer.registerPublicMethods(self)
        self.registerPublicMethod('addObjectMember')
        self.registerPublicMethod('deleteObjectMember')
        self.registerPublicMethod('getObjectIdsWithContent')
        self.registerPublicMethod('objectContains')
        self.registerPublicMethod('setContains')

    def repairVirtualServer(self, virtualServer, version):
        changed = 0
        if version < 4000:
            changed = virtualServer.admin.repair(4000) or changed
            for id, object in virtualServer.objects.items():
                newId = repairId(id)
                if newId:
                    changed = 1
                    del virtualServer.objects[id]
                    virtualServer.objects[newId] = object
                changed = object.repair(4000) or changed
                if not object.__dict__.has_key('language'):
                    changed = 1
                    object.language = 'fr'
        if version < 5004:
            changed = virtualServer.admin.repair(5004) or changed
            for id, object in virtualServer.objects.items():
                changed = object.repair(5004) or changed
        if version <= 1013000:
            admin = virtualServer.admin
            if admin.id is None:
                changed = 1
                admin.id = '%s/__admin__' % virtualServer.virtualServerId
        if version <= 1054000:
            changed = 1
            admin = virtualServer.admin
            for object in virtualServer.objects.values():
                object.writersSet = admin.writersSet
        if version <= 1055000:
            for object in virtualServer.objects.values():
                if hasattr(object, 'readersSet'):
                    changed = 1
                    del object.readersSet
        if version <= 1056000:
            # Class Group has been renamed GroupUnion.
            changed = 1
        if changed:
            virtualServer.markAllAsDirtyFIXME()

    def setContains(self, set, objectId):
        group = GroupUnion()
        group.membersSet = set
        return group.contains(objectId)

    def upgradeVirtualServer_0001_0029(self, virtualServer):
        # Transform the groups of people into groups of identities.
        for object in virtualServer.objects.values():
            dirty = 0
            if object.acceptedRoles is not None \
                    and 'people' in object.acceptedRoles \
                    and not 'identities' in object.acceptedRoles:
                dirty = 1
                object.acceptedRoles[object.acceptedRoles.index('people')
                                     ] = 'identities'
            if hasattr(object, 'membersSet') and object.membersSet is not None:
                for i, memberId in zip(range(len(object.membersSet)),
                                       object.membersSet):
                    if commonTools.extractRole(memberId) == 'people':
                        dirty = 1
                        object.membersSet[i] = '%s/%s' % (
                                commonTools.makeApplicationId(memberId,
                                                              'identities'),
                                commonTools.extractLocalId(memberId))
            if dirty:
                virtualServer.markObjectAsDirty(object)

groupsServer = GroupsServer()


if __name__ == "__main__":
    groupsServer.launch(applicationName, applicationRole)
