class MollieIdealMultiplePayments(object):
    implements(IMollieIdealMultiplePayments)
    adapts(IAttributeAnnotatable)

    def __init__(self, context):
        self.ideal_wrapper = getUtility(IMollieIdeal)
        annotations = IAnnotations(context)
        self._metadata = annotations.get(
            IDEAL_MULTIPLE_PAYMENTS_ANNOTATION_KEY, None)
        if self._metadata is None:
            self._metadata = PersistentMapping()
            annotations[IDEAL_MULTIPLE_PAYMENTS_ANNOTATION_KEY] = \
                self._metadata

    # Methods

    def get_banks(self):
        return self.ideal_wrapper.get_banks()

    def get_payment_url(self, partner_id, bank_id, amount, message,
                        report_url, return_url, profile_key=None):
        transaction_id, url = self.ideal_wrapper.request_payment(
            partner_id, bank_id, amount, message, report_url,
            return_url, profile_key)

        self._metadata[transaction_id] = {
            'partner_id': partner_id,
            'profile_key': profile_key,
            'amount': amount,
            'last_update': DateTime(),
            'curreny': None,
            'status': None,
            'paid': None,
            'consumer': {},
            'last_status': None,
            }
        return transaction_id, url

    def get_transaction(self, transaction_id):
        transaction = self._metadata.get(transaction_id)
        if transaction is None:
            raise UnknownTransactionError
        return transaction

    def get_payment_status(self, transaction_id):
        transaction = self.get_transaction(transaction_id)
        order_info = self.ideal_wrapper.check_payment(
            transaction['partner_id'], transaction_id)
        if order_info['status'] != 'CheckedBefore':
            # Only store the main info the first time.
            transaction['currency'] = order_info['currency']
            transaction['paid'] = order_info['paid']
            transaction['consumer'] = order_info.get('consumer')
            transaction['status'] = order_info['status']
        transaction['last_status'] = order_info['status']
        transaction['last_update'] = DateTime()
        return transaction['last_status']
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping
        m = PersistentMapping({'x': 1}, a=2, b=3)
        m['name'] = 'bob'
        self.assertEqual(m['name'], "bob")
        self.assertEqual(m.get('name', 42), "bob")
        self.assert_('name' in m)

        try:
            m['fred']
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_('fred' not in m)
        self.assertEqual(m.get('fred'), None)
        self.assertEqual(m.get('fred', 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = m.items()
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])
Exemple #3
0
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping
        m = PersistentMapping({'x': 1}, a=2, b=3)
        m['name'] = 'bob'
        self.assertEqual(m['name'], "bob")
        self.assertEqual(m.get('name', 42), "bob")
        self.assert_('name' in m)

        try:
            m['fred']
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_('fred' not in m)
        self.assertEqual(m.get('fred'), None)
        self.assertEqual(m.get('fred', 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = m.items()
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ['a', 'b', 'name', 'x'])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, 'bob'])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items,
                         [('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)])
    def checkBasicOps(self):
        from persistent.mapping import PersistentMapping

        m = PersistentMapping({"x": 1}, a=2, b=3)
        m["name"] = "bob"
        self.assertEqual(m["name"], "bob")
        self.assertEqual(m.get("name", 42), "bob")
        self.assert_("name" in m)

        try:
            m["fred"]
        except KeyError:
            pass
        else:
            self.fail("expected KeyError")
        self.assert_("fred" not in m)
        self.assertEqual(m.get("fred"), None)
        self.assertEqual(m.get("fred", 42), 42)

        keys = m.keys()
        keys.sort()
        self.assertEqual(keys, ["a", "b", "name", "x"])

        values = m.values()
        values.sort()
        self.assertEqual(values, [1, 2, 3, "bob"])

        items = m.items()
        items.sort()
        self.assertEqual(items, [("a", 2), ("b", 3), ("name", "bob"), ("x", 1)])

        keys = list(m.iterkeys())
        keys.sort()
        self.assertEqual(keys, ["a", "b", "name", "x"])

        values = list(m.itervalues())
        values.sort()
        self.assertEqual(values, [1, 2, 3, "bob"])

        items = list(m.iteritems())
        items.sort()
        self.assertEqual(items, [("a", 2), ("b", 3), ("name", "bob"), ("x", 1)])
Exemple #5
0
 def _data(self):
     """Return dictionary to store data."""
     sheets_data = getattr(self.context, '_sheets', None)
     if sheets_data is None:
         sheets_data = PersistentMapping()
         setattr(self.context, '_sheets', sheets_data)
     data = sheets_data.get(self._data_key, None)
     if data is None:
         data = PersistentMapping()
         sheets_data[self._data_key] = data
     return data
class Player(OOBTree):
    """Player container, which holds details and game sessions for a single
    player

    """
    implements(interfaces.IPlayer)

    def __init__(self, name, details={}):
        super(Player, self).__init__()
        self.name = name
        self.details = PersistentMapping(details.items())

    @property
    def duration(self):
        total = datetime.timedelta(0)
        for session in self.values():
            total += getattr(session, "duration", datetime.timedelta(0))
        return total

    def session(self, games):
        """Create a new Session for today if needed. Always returns a session
        with games shuffled in random order.

        """
        today = str(datetime.datetime.utcnow().date())
        if not today in self:
            all_games = games.items()
            random.shuffle(all_games)
            selected_games = dict(all_games[:8])
            if self.details.get("assisted", False):
                self[today] = Session(selected_games, 0.30)
            else:
                self[today] = Session(selected_games)
        return self[today]

    def get_sessions(self):
        return map(lambda x: self[x], sorted(self.keys()))
class Player(OOBTree):
    """Player container, which holds details and game sessions for a single
    player

    """
    implements(interfaces.IPlayer)

    def __init__(self, name, details={}):
        super(Player, self).__init__()
        self.name = name
        self.details = PersistentMapping(details.items())

    @property
    def duration(self):
        total = datetime.timedelta(0)
        for session in self.values():
            total += getattr(session, "duration", datetime.timedelta(0))
        return total

    def session(self, games):
        """Create a new Session for today if needed. Always returns a session
        with games shuffled in random order.

        """
        today = str(datetime.datetime.utcnow().date())
        if not today in self:
            all_games = games.items()
            random.shuffle(all_games)
            selected_games = dict(all_games[:8])
            if self.details.get("assisted", False):
                self[today] = Session(selected_games, 0.30)
            else:
                self[today] = Session(selected_games)
        return self[today]

    def get_sessions(self):
        return map(lambda x: self[x], sorted(self.keys()))
Exemple #8
0
class WorkflowVersions(Folder):
    """Workflow versions adapter"""
    def __init__(self):
        super().__init__()
        self.last_version_id = 0
        self.state_by_version = PersistentMapping()
        self.versions_by_state = PersistentMapping()
        self.deleted = PersistentMapping()

    def get_version(self, version_id):
        """Get version with given ID"""
        if version_id is None:
            version_id = self.last_version_id
        try:
            return self[str(version_id)]
        except KeyError as ex:  # pylint: disable=invalid-name
            raise VersionError(
                "Missing given version ID {0}".format(version_id)) from ex

    def get_versions(self, states=None, sort=False, reverse=False):
        """Get all versions, or those in given state"""
        if states:
            if isinstance(states, str):
                states = (states, )
            versions = set()
            for state in states:
                if state is None:
                    state = NONE_STATE
                for version in self.versions_by_state.get(state, ()):
                    versions.add(self[str(version)])
            if sort:
                versions = sorted(versions,
                                  key=lambda x: int(x.__name__),
                                  reverse=reverse)
            return versions
        return (v for k, v in sorted(self.items(), key=lambda x: int(x[0])))

    def get_last_versions(self, count=1):
        """Last version(s) getter"""
        result = list(
            (v for k, v in sorted(self.items(), key=lambda x: -int(x[0]))))
        if count:
            result = result[:count]
        return result

    def add_version(self, content, state, principal=None):
        """Add new version to versions list"""
        self.last_version_id += 1
        version_id = self.last_version_id
        # init version state
        alsoProvides(content, IWorkflowVersion)
        wf_state = IWorkflowState(content)
        wf_state.version_id = version_id
        wf_state.state = state
        if principal is not None:
            wf_state.state_principal = principal
        # store new version
        if state is None:
            state = NONE_STATE
        self[str(version_id)] = content
        self.state_by_version[version_id] = state
        versions = self.versions_by_state.get(state, [])
        versions.append(version_id)
        self.versions_by_state[state] = versions
        return version_id

    def set_state(self, version_id, state, principal=None):
        """Set state of given version"""
        if str(version_id) not in self:
            return
        # update version state
        version = self[str(version_id)]
        wf_state = IWorkflowState(version)
        wf_state.version_id = version_id
        wf_state.state = state
        if principal is not None:
            wf_state.state_principal = principal
        # update versions/states mapping
        if state is None:
            state = NONE_STATE
        old_state = self.state_by_version[version_id]
        versions = self.versions_by_state[old_state]
        if version_id in versions:
            versions.remove(version_id)
            if versions:
                self.versions_by_state[old_state] = versions
            else:
                del self.versions_by_state[old_state]
        self.state_by_version[version_id] = state
        versions = self.versions_by_state.get(state, [])
        versions.append(version_id)
        self.versions_by_state[state] = versions

    def has_version(self, states):
        """Test for existing version(s) in given state(s)"""
        if states is None:
            states = NONE_STATE
        if not isinstance(states, (list, tuple, set)):
            states = {states}
        for state in states:
            if bool(self.versions_by_state.get(state, ())):
                return True
        return False

    def remove_version(self,
                       version_id,
                       state='deleted',
                       comment=None,
                       principal=None,
                       request=None):
        # pylint: disable=too-many-arguments
        """Remove version with given ID"""
        if str(version_id) not in self:
            return
        # update version state
        version = self[str(version_id)]
        wf_state = IWorkflowState(version)
        if comment:
            if request is None:
                request = check_request()
            translate = request.localizer.translate
            workflow = get_utility(
                IWorkflow,
                name=get_parent(self, IWorkflowManagedContent).workflow_name)
            item = WorkflowHistoryItem(
                date=datetime.utcnow(),
                source_version=wf_state.version_id,
                source_state=translate(
                    workflow.states.getTerm(wf_state.state).title),
                target_state=translate(workflow.states.getTerm(state).title),
                principal=request.principal.id,
                comment=comment)
            wf_state.history.append(item)  # pylint: disable=no-member
        wf_state.state = state
        if principal is not None:
            wf_state.state_principal = principal
        # remove given version
        state = self.state_by_version[version_id]
        versions = self.versions_by_state[state]
        versions.remove(version_id)
        if versions:
            self.versions_by_state[state] = versions
        else:
            del self.versions_by_state[state]
        del self.state_by_version[version_id]
        self.deleted[version_id] = self[str(version_id)]
        del self[str(version_id)]
Exemple #9
0
class Room( Persistent, RoomBase, Fossilizable ):
    """
    ZODB specific implementation.

    For documentation of methods see base class.
    """

    fossilizes(IRoomMapFossil, IRoomCalendarFossil)

    __dalManager = Factory.getDALManager()
    vcList = []

    def __init__(self):
        RoomBase.__init__( self )
        self.customAtts = PersistentMapping()
        self.avaibleVC = []
        self._nonBookableDates = []

    def getNonBookableDates(self):
        try:
            if self._nonBookableDates:
                pass
        except AttributeError:
            self._nonBookableDates = []
            self._p_changed = 1
        return self._nonBookableDates

    def addNonBookableDate(self, udate):
        self._nonBookableDates.append(udate)
        self._p_changed = 1

    def addNonBookableDateFromParams(self, params):
        nbd = NonBookableDate(params["startDate"], params["endDate"])
        self._nonBookableDates.append(nbd)
        self._p_changed = 1

    def clearNonBookableDates(self):
        self._nonBookableDates = []
        self._p_changed = 1

    def isNonBookableDay(self, day):
        for nbd in self.getNonBookableDates():
            if nbd.doesDayOverlap(day):
                return True
        return False

    def getBlockedDay(self, day):
        blockings = Factory.newRoomBlocking().getByDate(day)
        for bl in blockings:
            rbl = bl.getBlockedRoom(self)
            if rbl and rbl.active is not False:
                return rbl
        return None

    def setAvailableVC(self, avc):
        self.avaibleVC = avc

    def getAvailableVC(self):
        try:
            return self.avaibleVC
        except:
            self.avaibleVC = []
        return self.avaibleVC

    @staticmethod
    def getRoot():
        return Room.__dalManager.getRoot(_ROOMS)

    def getAllManagers(self):
        managers = set([self.getResponsible()])
        if self.customAtts.get('Simba List'):
            groups = GroupHolder().match({'name': self.customAtts['Simba List']},
                                         exact=True, forceWithoutExtAuth=True)
            if not groups:
                groups = GroupHolder().match({'name': self.customAtts['Simba List']}, exact=True)
            if groups and len(groups) == 1:
                managers |= set(groups[0].getMemberList())
        return list(managers)

    def insert( self ):
        """ Documentation in base class. """
        RoomBase.insert( self )
        roomsBTree = Room.getRoot()
        # Ensure ID
        if self.id == None:
            # Maximum ID + 1
            if len( roomsBTree ) > 0:
                self.id = roomsBTree.maxKey() + 1
            else:
                self.id = 1 # Can not use maxKey for 1st record in a tree
        # Add self to the BTree
        roomsBTree[self.id] = self
        Catalog.getIdx('user_room').index_obj(self.guid)

    def update( self ):
        """ Documentation in base class. """
        RoomBase.update( self )

        # Check Simba mailing list
        listName = self.customAtts.get( 'Simba List' )
        if listName:
            from MaKaC.user import GroupHolder
            groups = GroupHolder().match( { 'name': listName }, forceWithoutExtAuth = True )
            if not groups:
                groups = GroupHolder().match( { 'name': listName } )
            if not groups:
                self.customAtts['Simba List'] = 'Error: unknown mailing list'

        # reindex - needed due to possible manager changes
        # super slow, though...
        Catalog.getIdx('user_room').unindex_obj(self.guid)
        Catalog.getIdx('user_room').index_obj(self.guid)

        self._p_changed = True

    def remove( self ):
        """ Documentation in base class. """
        RoomBase.remove( self )
        roomsBTree = Room.getRoot()
        del roomsBTree[self.id]
        if Catalog.getIdx('user_room').has_obj(self.guid):
            Catalog.getIdx('user_room').unindex_obj(self.guid)

    @classmethod
    def isAvatarResponsibleForRooms(cls, avatar):
        return Catalog.getIdx('user_room').get(avatar.getId()) is not None

    @classmethod
    def getUserRooms(cls, avatar):
        return Catalog.getIdx('user_room').get(avatar.getId())

    # Typical actions
    @staticmethod
    def getRooms( *args, **kwargs ):
        """ Documentation in base class. """

        roomsBTree = Room.getRoot()
        location = kwargs.get( 'location' )

        if kwargs.get( 'allFast' ) == True:
            return [ room for room in roomsBTree.values() if room.isActive and (not location or room.locationName == location) ]

        if kwargs.get( 'reallyAllFast' ) == True:
            return [ room for room in roomsBTree.values() if (not location or room.locationName == location) ]

        if len( kwargs ) == 0:
            ret_lst = []
            for room in roomsBTree.values():
                ret_lst.append( room )

        roomID = kwargs.get( 'roomID' )
        roomName = kwargs.get( 'roomName' )
        roomEx = kwargs.get( 'roomExample' )
        resvEx = kwargs.get( 'resvExample' )
        freeText = kwargs.get( 'freeText' )
        available = kwargs.get( 'available' )
        countOnly = kwargs.get( 'countOnly' )
        minCapacity = kwargs.get( 'minCapacity' )
        location = kwargs.get( 'location' )
        ownedBy = kwargs.get( 'ownedBy' )
        customAtts = kwargs.get( 'customAtts' )
#        responsibleID = kwargs.get( 'responsibleID' )
        pendingBlockings = kwargs.get( 'pendingBlockings' )

        ret_lst = []
        counter = 0
        if roomID != None:
            return roomsBTree.get( roomID )
        if roomName != None:
            for room in roomsBTree.itervalues():
                if room.name == roomName:
                    if location == None or room.locationName == location:
                        return room
            return None

        for room in roomsBTree.itervalues():
            # Apply all conditions =========
            if location != None:
                if room.locationName != location:
                    continue
            if roomEx != None:
                if not qbeMatch( roomEx, room, Room.__attrSpecialEqual, minCapacity = minCapacity ):
                    continue
                if not room.__hasEquipment( roomEx.getEquipment() ):
                    continue
            if freeText != None:
                if not room.__hasFreeText( freeText.split() ):
                    continue
            if resvEx != None:
                resvEx.room = room
                aval = room.isAvailable( resvEx )
                if aval != available:
                    continue
                blockState = resvEx.getBlockingConflictState(ContextManager.get('currentUser'))
                if blockState == 'active':
                    continue
                elif blockState == 'pending' and pendingBlockings:
                    continue
            if ownedBy != None:
                if not room.isOwnedBy( ownedBy ):
                    continue
            if customAtts is not None:
                if not hasattr(room, "customAtts"):
                    continue
                discard = False
                for condition in customAtts:
                    attName = condition["name"]
                    allowEmpty = condition.get("allowEmpty", False)
                    filter = condition.get("filter", None)
                    if not attName in room.customAtts:
                        discard = True
                        break
                    elif not allowEmpty and str(room.customAtts[attName]).strip() == "":
                        discard = True
                        break
                    elif not filter(room.customAtts[attName]):
                        discard = True
                        break
                if discard:
                    continue

            # All conditions are met: add room to the results
            counter += 1
            if not countOnly:
                ret_lst.append( room )

        #print "Found %d rooms." % counter
        if countOnly:
            return counter
        else:
            return ret_lst

    # Statistics ====================================

    @staticmethod
    def countRooms( *args, **kwargs ):
        """ Documentation in base class. """
        kwargs['countOnly'] = True
        return Room.getRooms( **kwargs )

    @staticmethod
    def getNumberOfRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        return Room.countRooms( location = location )

    @staticmethod
    def getNumberOfActiveRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )

    @staticmethod
    def getNumberOfReservableRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isReservable = True
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )

    def getLocationName( self ):
        #from MaKaC.plugins.RoomBooking.default.factory import Factory
        #return Factory.locationName
        return self._locationName

    def setLocationName( self, locationName ):
        self._locationName = locationName

    def savePhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()

    def saveSmallPhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomSmallPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()


    # ==== Private ===================================================

    def _getSafeLocationName( self ):
        if self.locationName == None:
            return None
        s = ""
        for i in xrange( 0, len( self.locationName ) ):
            code = ord( self.locationName[i] )
            if ( code in xrange( ord( 'a' ), ord( 'z' ) + 1 ) ) or \
               ( code in xrange( ord( 'A' ), ord( 'Z' ) + 1 ) ) or \
               ( code in xrange( ord( '0' ), ord( '9' ) + 1 ) ):
                # Valid
                s += self.locationName[i]
            else:
                s += '_' # Replace all other characters with underscore
        return s

    def _doGetPhotoId( self, force = False ):
        photoId = "%s-%s-%s-%s" % ( str( self._getSafeLocationName() ), str( self.building ).strip(), str( self.floor ).strip(), str( self.roomNr ).strip() )

        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = photoId + ".jpg"
        fullPath = os.path.join( filePath, fileName )
        from os.path import exists
        if exists( fullPath ) or force:
            return photoId
        else:
            return None

    def _doSetPhotoId( self ):
        """
        For this plugin, photoId is always composed of location-building-floor-room.jpg
        """
        pass

    def __hasFreeText( self, freeTextList ):
        # OR
        for freeText in freeTextList:
            freeText = freeText.lower()
            if self.__hasOneFreeText( freeText ):
                return True
        return False

    def __hasOneFreeText( self, freeText ):
        # Look for freeText in all string and int attributes
        for attrName in dir( self ):
            if attrName[0] == '_':
                continue
            attrType = eval( 'self.' + attrName + '.__class__.__name__' )
            if attrType == 'str':
                attrVal = eval( 'self.' + attrName )
                if attrVal.lower().find( freeText ) != -1:
                    return True

        # Look for freeText in equipment
        if self.__hasEquipment( [ freeText ] ):
            return True

        # Look for freeText in responsible
        if self.responsibleId != None:
            user = self.getResponsible();
            if freeText in user.getFullName().lower()  or  freeText in user.getEmail().lower():
                return True

        # Look for freeText in custom attributes
        for value in self.customAtts.itervalues():
            if value and ( freeText in value.lower() ):
                return True

        # Not found
        return False

    @staticmethod
    def __goodCapacity( val1, val2, minCapacity = None ):
        # Difference in capacity less than 20%
        if val1 < 1: val1 = 1

        if not minCapacity:
            return abs( val1 - val2 ) / float( val1 ) <= 0.2
        else:
            return val2 > val1

    @classmethod
    def __attrSpecialEqual( cls, attrName, exampleVal, candidateVal, **kwargs ):
        if attrName in ( 'guid', 'locationName', 'name', 'photoId', 'needsAVCSetup' ):
            return True # Skip by stating they match
        if attrName in ( 'responsibleId', 'responsibleID' ):
            return exampleVal == candidateVal # Just exact string matching
        if attrName[0:7] == 'verbose':
            return True
        if attrName.find( 'capacity' ) != -1:
            minCapacity = kwargs.get( 'minCapacity' )
            return cls.__goodCapacity( exampleVal, candidateVal, minCapacity )
        if attrName == 'customAtts':
            # Check if all values in exampleVal are contained
            # in corresponding values of candidateVal
            for k, v in exampleVal.iteritems():
                if v: # If value is specified
                    if candidateVal.get( k ) == None:
                        # Candidate does not have the attribute
                        return False
                    if not ( v in candidateVal[k] ):
                        # Candidate's attribute value does not match example
                        return False
            return True
        return None

    def __hasEquipment( self, requiredEquipmentList ):
        iHave = self.getEquipment()
        for reqEq in requiredEquipmentList:
            have = False
            for myEq in iHave:
                if myEq.lower().find( reqEq.lower() ) != -1:
                    have = True
                    break
            if not have:
                return False
        return True

    def getBookingUrl(self):
        """ Room booking URL """
        return str(urlHandlers.UHRoomBookingBookingForm.getURL(target=self))

    def getDetailsUrl(self):
        """ Room details URL """
        return str(urlHandlers.UHRoomBookingRoomDetails.getURL(target=self))

    def getMarkerDescription(self):
        """ Room description for the map marker """
        infos = []
        if self.capacity:
            infos.append("%s %s" % (self.capacity , _("people")))
        if self.isReservable:
            infos.append(_("public"))
        else:
            infos.append(_("private"))
        if self.resvsNeedConfirmation:
            infos.append(_("needs confirmation"))
        else:
            infos.append(_("auto-confirmation"))
        if self.needsAVCSetup:
            infos.append(_("video conference"))
        return ", ".join(infos)

    def getTipPhotoURL(self):
        """ URL of the tip photo of the room """
        from MaKaC.webinterface.urlHandlers import UHRoomPhoto
        photoId = self._doGetPhotoId()
        if not photoId:
            photoId = "NoPhoto"
        return str(UHRoomPhoto.getURL(photoId))

    def getIsAutoConfirm(self):
        """ Has the room auto-confirmation of schedule? """
        return not self.resvsNeedConfirmation

    locationName = property( getLocationName, setLocationName )
class NotificationTool(UniqueObject, SimpleItem, PropertyManager):
    """Main notification tool."""
    id = ID
    title = TITLE
    meta_type = META_TYPE

    manage_options = (PropertyManager.manage_options
                      + SimpleItem.manage_options)

    ## Extra subscriptions
    extra_subscriptions_enabled = False
    extra_subscriptions_recursive = True

    ## Debug mode
    debug_mode = False

    ## Ignore rules
    ignore_rules = DEFAULT_IGNORE_RULES

    ## Item creation
    item_creation_notification_enabled = False
    on_item_creation_users = []
    on_item_creation_mail_template = []
    ## Item modification
    item_modification_notification_enabled = False
    on_item_modification_users = []
    on_item_modification_mail_template = []
    ## Item removal
    item_removal_notification_enabled = False
    on_item_removal_users = []
    on_item_removal_mail_template = []
    ## Workflow transition
    wf_transition_notification_enabled = False
    on_wf_transition_users = []
    on_wf_transition_mail_template = []
    ## Member registration
    member_registration_notification_enabled = False
    on_member_registration_users = []
    on_member_registration_mail_template = []
    ## Member modification
    member_modification_notification_enabled = False
    on_member_modification_users = []
    on_member_modification_mail_template = []
    ## Discussion item creation
    discussion_item_creation_notification_enabled = False
    on_discussion_item_creation_users = []
    on_discussion_item_creation_mail_template = []

    _properties = ({'id': 'extra_subscriptions_enabled',
                    'label': 'Enable extra subscriptions',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'extra_subscriptions_recursive',
                    'label': 'Toggle recursive mode for extra subscriptions',
                    'mode': 'w',
                    'type': 'boolean'},

                   {'id': 'debug_mode',
                    'label': 'Toggle debug mode',
                    'mode': 'w',
                    'type': 'boolean'},

                   {'id': 'ignore_rules',
                    'label': 'Rules (ignore)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'item_creation_notification_enabled',
                    'label': 'Enable item creation notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_creation_users',
                    'label': 'Rules on item creation (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_creation_mail_template',
                    'label': 'Rules on item creation (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'item_modification_notification_enabled',
                    'label': 'Enable item modification notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_modification_users',
                    'label': 'Rules on item modification (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_modification_mail_template',
                    'label': 'Rules on item modification (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'item_removal_notification_enabled',
                    'label': 'Enable item removal notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_removal_users',
                    'label': 'Rules on item removal (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_removal_mail_template',
                    'label': 'Rules on item removal (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'wf_transition_notification_enabled',
                    'label': 'Enable workflow transition notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_wf_transition_users',
                    'label': 'Rules on workflow transition (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_wf_transition_mail_template',
                    'label': 'Rules on workflow transition (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'member_registration_notification_enabled',
                    'label': 'Enable member registration notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_member_registration_users',
                    'label': 'Rules on member registration (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_member_registration_mail_template',
                    'label': 'Rules on member registration (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'member_modification_notification_enabled',
                    'label': 'Enable member modification notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_member_modification_users',
                    'label': 'Rules on member modification (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_member_modification_mail_template',
                    'label': 'Rules on member modification (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'discussion_item_creation_notification_enabled',
                    'label': 'Enable discussion item creation notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_discussion_item_creation_users',
                    'label': 'Rules on discussion item creation (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_discussion_item_creation_mail_template',
                    'label': 'Rules on discussion item creation (mail template)',
                    'mode': 'w',
                    'type': 'lines'},
                   )

    security = ClassSecurityInfo()
    decPrivate = security.declarePrivate
    decProtected = security.declareProtected
    decPublic = security.declarePublic


    def __init__(self, *args, **kwargs):
        self._uid_to_path = PersistentMapping()
        self._subscriptions = PersistentMapping()


    #################################################################
    ## Notification handlers
    ########################
    decPrivate('onItemCreation')
    def onItemCreation(self, obj):
        """Handler called when an item is created.

        It returns the number of mails which have been sent.

        **Warning:** this handler is not called when a discussion item
        is added. In this case, ``onDiscussionItemCreation()`` is
        called instead.
        """
        if not self.getProperty('item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        return self._handlerHelper(obj, 'item_creation',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onItemModification')
    def onItemModification(self, obj):
        """Handler called when an item is modified.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('item_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'current': obj,
                               'previous': getPreviousVersion(obj)})
        return self._handlerHelper(obj, 'item_modification',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)

    decPrivate('onItemRemoval')
    def onItemRemoval(self, obj):
        """Handler called when an item is removed.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('item_removal_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        return self._handlerHelper(obj, 'item_removal',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onWorkflowTransition')
    def onWorkflowTransition(self, obj, action):
        """Handler called when a workflow transition is triggered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('wf_transition_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        wtool = getToolByName(self, 'portal_workflow')
        comments = wtool.getInfoFor(obj, 'comments')
        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'transition': action,
                               'comments': comments,
                               'previous_state': getPreviousWorkflowState(obj)})
        return self._handlerHelper(obj, 'wf_transition',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onMemberRegistration')
    def onMemberRegistration(self, member, properties):
        """Handler called when a new portal member has been
        registered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('member_registration_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        if properties is None:
            properties = {} ## FIXME: How could it be? (Damien)

        current_user = getSecurityManager().getUser()
        extra_bindings = getBasicBindings(member)
        extra_bindings.update({'current_user': current_user,
                               'member': member,
                               'properties': properties,
                               'event': 'registration'})
        return self._handlerHelper(member, 'member_registration',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onMemberModification')
    def onMemberModification(self, member):
        """Handler called when a member changes his/her properties.

        It returns the number of mails which have been sent.
        """
        ## FIXME: this should go away when we rely on the appropriate
        ## event.
        ## This method can also be called when the member is
        ## registered. We have to check that.
        stack = inspect.stack()
        ## 1st item is ourself
        ## 2nd item is 'CMFCore.MemberDataTool.notifyMemberModified()'
        ## 3rd item is 'CMFCore.MemberDataTool.setMemberProperties()'
        ## 4th item is what we want to check: it is either 'addMember'
        ## or 'setProperties()'
        caller = stack[3][3]
        if caller != 'setProperties':
            return 0

        if not self.getProperty('member_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        ## FIXME: what is the purpose of the following lines? (Damien)
        memberdata = getToolByName(self, 'portal_memberdata')
        properties = {}
        for key, value in memberdata.propertyItems():
            properties[key] = value

        current_user = getSecurityManager().getUser()
        extra_bindings = getBasicBindings(None)
        extra_bindings.update({'current_user': current_user,
                               'member': member,
                               'properties': properties,
                               'event': 'modification'})
        return self._handlerHelper(member, 'member_modification',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onDiscussionItemCreation')
    def onDiscussionItemCreation(self, discussion_item):
        """Handler called when a discussion item is created.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('discussion_item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(discussion_item):
            return 0

        ## We add two bindings to disambiguate the meaning of 'here'
        ## in the mail template and the rules: 'discussion_item' and
        ## 'discussed_item'.
        discussed_item = discussion_item
        while discussed_item.meta_type == discussion_item.meta_type:
            discussed_item = discussed_item.aq_inner.aq_parent.aq_parent
        extra_bindings = getBasicBindings(discussed_item)
        extra_bindings.update({'discussion_item': discussion_item,
                               'discussed_item': discussed_item})
        return self._handlerHelper(discussion_item,
                                   'discussion_item_creation',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    def _handlerHelper(self, obj, what,
                       get_users_extra_bindings,
                       mail_template_extra_bindings,
                       mail_template_options):
        """An helper method for ``on*()`` handlers.

        It returns the number of mails which have been sent.
        """
        self._updateSubscriptionMapping(obj)
        users_by_label = self.getUsersToNotify(obj, what,
                                               get_users_extra_bindings)
        if self.isExtraSubscriptionsEnabled():
            users = users_by_label.get('', [])
            users.extend(self.getExtraSubscribersOf(self._getPath(obj)).items())
            users_by_label[''] = users

        n_sent = 0
        for label, users in users_by_label.items():
            users = self.removeUnAuthorizedSubscribers(users, obj)
            mail_template_extra_bindings['label'] = label
            for user, how in users:
                # Fetch the delivery utilities the user requested, then use
                # that to notify
                for h in how:
                    try:
                        delivery = getUtility(INotificationDelivery, h)
                    except:
                        # The method is not known, or the third party
                        # product that provided it was uninstalled
                        LOG.warning("Could not look up INotificationDelivery "\
                            "utility named '%s'", h)
                        continue
                    n_sent += delivery.notify(obj, user, what, label,
                        get_users_extra_bindings, mail_template_extra_bindings,
                        mail_template_options)

        return n_sent
    #################################################################


    #################################################################
    ## Utility methods
    ###############################
    decPrivate('ignoreNotification')
    def ignoreNotification(self, obj):
        """Return whether notification have been set to be ignored for
        ``obj``.
        """
        ec = getExpressionContext(obj)
        for match_expr in self.getProperty('ignore_rules', ()):
            try:
                if self._match(match_expr, ec):
                    return True
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'ignore_rules' rule "\
                          "('%s') for '%s'",
                          match_expr, obj.absolute_url(1),
                          exc_info=True)
        return False


    decPrivate('getUsersToNotify')
    def getUsersToNotify(self, obj, what, ec_bindings=None):
        """Return a mapping from label to a list of user/how tuples,
        based on the passed ``what`` and ``ob``. ``what`` is one of the
        implemented notifications (*item_modification*, *wf_transition*,
        etc.). ``how`` is a string that says which delivery method to use.

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_users' % what, None)
        if rules is None:
            raise NotImplementedError, \
                "Notification on '%s' is not implemented." % what

        ec = getExpressionContext(obj, ec_bindings)
        users_by_label = {}
        ignore_next_rules = False
        for rule in rules:
            try:
                match_expr, users_expr = rule.split(RULE_DELIMITER, 1)
                parts = users_expr.split(RULE_DELIMITER)
                label = ''
                how = ('mail',)
                if len(parts) > 1:
                    users_expr, label = parts[:2]
                if len(parts) > 2:
                    how = tuple([p.strip() for p in parts[2:]])
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_users' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            users_expr = users_expr.strip()
            label = label.strip()
            users = users_by_label.get(label, [])
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_users' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if users_expr == '*':
                users.extend([(u, how) for u in self.getAllUsers()])
                ignore_next_rules = True
            else:
                try:
                    users.extend([(u, how) for u in Expression(users_expr)(ec)])
                except ConflictError:
                    raise
                except:
                    LOG.error("Error in 'on_%s_users' rule "\
                              "('%s') for '%s'",
                              what, users_expr, obj.absolute_url(1),
                              exc_info=True)
            users_by_label[label] = users
            if ignore_next_rules:
                break
        return users_by_label


    decPrivate('getTemplate')
    def getTemplate(self, obj, what, ec_bindings=None):
        """Return the template to notify for the ``what`` of an object
        ``obj``, ``what`` being one of the implemented notification
        ("*item_modification*", "*wf_transition*", etc.), or ``None``
        if none could be found.

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_mail_template' % what, None)
        if rules is None:
            raise NotImplementedError, \
                'Notification on "%s" is not implemented.'

        ec = getExpressionContext(obj, ec_bindings)
        template = None
        for rule in rules:
            try:
                match_expr, template_expr = rule.split(RULE_DELIMITER)
                match_expr, template_expr = match_expr.strip(), template_expr.strip()
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_mail_template' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            template_expr = template_expr.strip()
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            try:
                template = Expression(template_expr)(ec)
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, template_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if type(template) == StringType:
                template = obj.restrictedTraverse(template, None)
            if template is not None:
                break
        return template


    decPrivate('getAllUsers')
    def getAllUsers(self):
        """Return a list of all user ids of the portal.

        **Warning:** this method may be costly if you rely on an
        external (non ZODB) user source. Use it at your own risk.
        """
        mtool = getToolByName(self, 'portal_membership')
        return mtool.listMemberIds()


    decPrivate('removeUnAuthorizedSubscribers')
    def removeUnAuthorizedSubscribers(self, subscribers, obj):
        """Return users from ``subscribers`` who are authorized to
        view ``obj``.
        """
        portal = getToolByName(self, 'portal_url').getPortalObject()
        mtool = getToolByName(self, 'portal_membership')
        filtered_subscribers = []
        for subscriber, notifymethod in subscribers:
            if self._anonymousShouldBeNotified(obj):
                filtered_subscribers.append((subscriber, notifymethod))
            else:
                ## We use '_huntUser()' and not 'mtool.getMemberById()'
                ## because the latter would provide a wrapped user
                ## object, with a specific context where the user is
                ## not allowed to view 'obj'.
                member = mtool._huntUser(str(subscriber), portal)
                if member is not None:
                    if member.has_permission('View', obj):
                        filtered_subscribers.append((subscriber, notifymethod))

        return filtered_subscribers


    def _match(self, expr, ec):
        """Return ``True`` if ``expr`` returns something which can be
        evaluated to ``True`` in the expression context (``ec``) or if
        ``expr`` is "*".
        """
        if expr == '*':
            return True
        expr = Expression(expr)
        return bool(expr(ec))


    def _getPath(self, obj):
        """Return path of ``obj``.

        A slash (``/``) is appended to the path if the object is
        folderish. The returned path is relative to the portal object.
        """
        utool = getToolByName(self, 'portal_url')
        path = utool.getRelativeContentURL(obj)
        path = '/' + path
        if not getattr(obj.aq_base, 'isPrincipiaFolderish', False):
            return path
        if path[-1] != '/':
            path += '/'
        return path


    def _getParents(self, path):
        """Get the parents of the item corresponding to ``path`` and
        return their respective path.

        Parents are returned from ``path`` to the portal root object.
        """
        if path == '/':
            return []
        if path[-1] == '/':
            path = path[:-1]
        parent = path[:path.rfind('/') + 1]
        parents = [parent]
        parents.extend(self._getParents(parent))
        return tuple(parents)


    def _getUID(self, obj):
        """Return UID of the object."""
        if not IATContentType.providedBy(obj):
            return None

        portal_uidhandler = getToolByName(self, 'portal_uidhandler')
        uid = portal_uidhandler.queryUid(obj, None)
        if uid is None: ## Not yet registered
            uid = portal_uidhandler.register(obj)
        return uid


    def _anonymousShouldBeNotified(self, obj):
        """Return whether anonymous users should be notified, i.e.
        whether anonymous users can view ``obj``.
        """
        return 'Anonymous' in rolesForPermissionOn('View', obj)
    #################################################################


    #################################################################
    ## Extra subscriptions settings
    ###############################
    decProtected('View', 'isExtraSubscriptionsEnabled')
    def isExtraSubscriptionsEnabled(self):
        """Return whether extra subscriptions are enabled."""
        return self.getProperty('extra_subscriptions_enabled')


    decProtected('View', 'isExtraSubscriptionsRecursive')
    def isExtraSubscriptionsRecursive(self):
        """Return whether extra subscriptions are recursive.

        Note that this method does not check whether extra
        subscriptions are enabled or not.
        """
        return self.getProperty('extra_subscriptions_recursive')
    #################################################################


    #################################################################
    ## Extra subscriptions logic
    ############################
    def _updateSubscriptionMapping(self, obj):
        """Update subscription mapping."""
        uid = self._getUID(obj)
        if not uid:
            return

        path = self._getPath(obj)
        known_path = self._uid_to_path.get(uid)
        if known_path != path:
            self._uid_to_path[uid] = path
            if known_path is not None:
                ## We have old informations for this object
                for key, value in self._subscriptions.items():
                    if key.startswith(known_path):
                        new_key = path + key[len(known_path) : ]
                        self._subscriptions[new_key] = value
                        del self._subscriptions[key]


    decPublic('currentUserHasSubscribePermission')
    def currentUserHasSubscribePermissionOn(self, obj):
        """Return whether the current user is allowed to subscribe to
        or unsubscribe from ``obj``.
        """
        if not IATContentType.providedBy(obj) and not \
                IPloneSiteRoot.providedBy(obj):
            return False

        mtool = getToolByName(self, 'portal_membership')
        return mtool.checkPermission(SUBSCRIBE_PERMISSION, obj)


    decPublic('subscribeTo')
    def subscribeTo(self, obj, email=None, how=['mail']):
        """Subscribe ``email`` (or the current user if ``email`` is
        None) to ``obj``. You can pass the methods by which the user should
        be notified as a tuple using the ``how`` keyword argument.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to subscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            user = getSecurityManager().getUser().getId()
            subscribers[user] = tuple(how)
            self._subscriptions[path] = subscribers


    decPublic('unSubscribeFrom')
    def unSubscribeFrom(self, obj, email=None):
        """Unsubscribe ``email`` (or the current user if ``email`` is
        ``None``) from ``obj``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            user = getSecurityManager().getUser().getId()
            try:
                del subscribers[user]
                self._subscriptions[path] = subscribers
            except KeyError:
                pass ## User was not subscribed.


    decPublic('unSubscribeFromObjectAbove')
    def unSubscribeFromObjectAbove(self, obj, email=None):
        """Find folderish items above ``obj`` and unsubscribe
        ``email`` (or the current user if ``email`` is ``None``) from
        the first one (s)he is subscribed to.

        If ``user`` is subscribed to ``obj``, this method is
        equivalent to ``unSubscribeFrom(obj, user)``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            utool = getToolByName(obj, 'portal_url')
            portal = utool.getPortalObject()
            portal_container = portal.aq_inner.aq_parent
            while obj != portal_container:
                if self.isSubscribedTo(obj, as_if_not_recursive=True):
                    self.unSubscribeFrom(obj)
                    break
                obj = obj.aq_parent


    decPublic('isSubscribedTo')
    def isSubscribedTo(self, obj, email=None,
                       as_if_not_recursive=False):
        """Return whether ``email`` (or the current user if ``email``
        is ``None``) is subscribed to ``obj``.

        If ``as_if_not_recursive`` is ``True``, this method acts as if
        the recursive mode was off.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is None:
            ## Yes, 'email' is actually the id of the current user.
            email = getSecurityManager().getUser().getId()
        self._updateSubscriptionMapping(obj)
        path = self._getPath(obj)
        subscribers = self.getExtraSubscribersOf(path,
                                                 as_if_not_recursive)
        return subscribers.has_key(email)


    decPrivate('getExtraSubscribersOf')
    def getExtraSubscribersOf(self, path, as_if_not_recursive=False):
        """Return users or email addresses which are subscribed to
        the given path.

        This method returns a mapping of users to tuples of notification
        methods, that is, each user can subscribe to be notified in more
        than one way.  If ``as_if_not_recursive`` is ``True``, this
        method acts as if the recursive mode was off.
        """
        subscribers = self._subscriptions.get(path, {}).copy()
        if self.isExtraSubscriptionsRecursive() and \
                not as_if_not_recursive:
            if path[-1] == '/':
                path = path[:-1]
            i = path.rfind('/')
            if i != -1:
                parent = path[:i + 1]
                subscribers.update(self.getExtraSubscribersOf(parent))
        return subscribers
class PSession( base.Session, Persistent ):
    """
    Keys which are already used in the data dictionary of each session:
        - menuStatus: it is used for knowing if the conference display menu is closed or opened.
        - accessKeys: contains all the access keys entered by the user in this session
        - modifKeys: contains all the modification keys entered by the user in this session
    """

    def __init__(self, request, id):
        base.Session.__init__(self, request, id)
        self.user = None
        minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        self.datadict = PersistentMapping()
        base.Session.__init__(self, request, id)
        self._lang = minfo.getLang()
        self.datadict["ActiveTimezone"] = "LOCAL"

    @property
    def csrf_token(self):
        try:
            return self._csrf_token
        except AttributeError:
            self._csrf_token = str(uuid.uuid4())
            return self._csrf_token

    def reset_csrf_token(self):
        if hasattr(self, '_csrf_token'):
            del self._csrf_token

    @property
    def csrf_protected(self):
        """Does the session need CSRF protection?"""
        return self.user is not None

    def has_info (self):
        """has_info() -> boolean

        Return true if this session contains any information that must
        be saved.
        """
        # This flag will indicate when to commit a session
        return getattr(self, '_v_modified', False)

    def setUser( self, newUser ):
        if newUser:
            self._lang = newUser.getLang()
        self.user = newUser
        self._v_modified = True

    def getUser( self ):
        return self.user

    def getId( self ):
        return self.id

    def setVar(self, key, value):
        try:
            self.datadict[key] = value
        except AttributeError:
            self.datadict = PersistentMapping()
            self.datadict[key] = value
        self._v_modified = True

    def getVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        return self.datadict.get(key,None)

    def removeVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        if self.datadict.has_key(key):
            del self.datadict[key]
            self._v_modified = True

    def getLang(self):
        try:
            if self._lang is None:
                raise Exception("no language")
        except:
            try:
                lang=self.user.getLang()
            except:
                lang="en_GB"
                Logger.get('i18n').debug('No user language defined. Using %s as default.' % lang)
            self._lang = lang

        return self._lang

    def setLang(self, lang):
        self._lang = lang
        self._v_modified = True

    def _p_resolveConflict(self, oldState, savedState, newState):
        """
        ZODB Conflict resolution
        Basically, all the atributes are taken from the conflicting
        transaction ("this one"), except for the creation time, which
        is max(T1,T2)
        """

        # Language, user and address are overwritten
        savedState['_lang'] = newState.get('_lang', None)
        savedState['user'] = newState.get('user', None)
        savedState['__remote_address'] = newState.get('__remote_address', None)

        # access time will be the latest

        savedState['__creation_time'] = max(newState.get('__creation_time', 0),
                                            savedState.get('__creation_time', 0))

        return oldState
Exemple #12
0
class CoreFilter(Persistent):
    """Core persistent record filter implementation"""

    implements(IRecordFilter)

    def __init__(self, *args, **kwargs):
        super(CoreFilter, self).__init__(*args, **kwargs)
        self._uid = str(uuid.uuid4())
        self.reset(**kwargs)

    def reset(self, **kwargs):
        self.operator = kwargs.get('operator', 'AND')
        self._queries = PersistentMapping()
        self._order = PersistentList()

    def validate(self, schema):
        for q in self._queries.values():
            if not q.validate(schema):
                raise ValidationError(q.fieldname)

    def build(self, schema):
        self.validate(schema)
        return filter_query(self, schema)

    def add(self, query=None, **kwargs):
        if query is None:
            ## attempt to make query from kwargs given either
            ## field/comparator/value or fieldname/comparator/value
            field = kwargs.get('field', None)
            fieldname = kwargs.get('fieldname', None)
            if not (field or fieldname):
                raise ValueError('Field missing for query construction')
            if fieldname is None and field:
                fieldname = field.__name__
            comparator = kwargs.get('comparator', None)
            value = kwargs.get('value', None)
            if not (value and comparator):
                raise ValueError('Missing value or comparator')
            query = FieldQuery(fieldname, comparator, value)
        fieldname = query.fieldname
        self._queries[fieldname] = query
        self._order.append(fieldname)

    def remove(self, query):
        if IFieldQuery.providedBy(query):
            query = query.fieldname
        if query not in self._queries:
            raise KeyError('Query not found (fieldname: %s)' % query)
        del(self._queries[query])
        self._order.remove(query)

    ## RO mapping interface
    def get(self, name, default=None):
        return self._queries.get(name, default)

    def __len__(self):
        return len(self._order)

    def __getitem__(self, name):
        v = self.get(name, None)
        if v is None:
            raise KeyError(name)  # fieldname not found
        return v

    def __contains__(self, name):
        if IFieldQuery.providedBy(name):
            name = name.field.__name__
        return name in self._order

    def keys(self):
        return list(self._order)

    def iterkeys(self):
        return self._order.__iter__()

    def itervalues(self):
        return itertools.imap(lambda k: self.get(k), self.iterkeys())

    def iteritems(self):
        return itertools.imap(lambda k: (k, self.get(k)), self.iterkeys())

    def values(self):
        return list(self.itervalues())

    def items(self):
        return list(self.iteritems())
Exemple #13
0
class BaseHistoryRecord(object):
    """Basic implementation of a history record.

    Contains basic set of required data and abstract implementation. Can
    re-populate iself from data by invoking the __new__ method directly and
    then re-populating the attributes on the instance instead of calling
    __init__.

    Each record must have a unique `history_type` from which it can be built
    with IHistory.append_record.

    If `needs_syncing` is `True` a records that is created on the
    `SubmittedProposal` side is automatically added to its corresponding
    `Proposal`.
    """

    history_type = None
    needs_syncing = False

    @classmethod
    def re_populate(cls, context, timestamp, data):
        record = cls.__new__(cls)
        record.context = context
        record.timestamp = timestamp
        record.data = data
        return record

    def __init__(self, context, timestamp=None, uuid=None):
        timestamp = timestamp or utcnow_tz_aware()
        if uuid is None:
            uuid = uuid4()
        elif isinstance(uuid, basestring):
            uuid = UUID(uuid)

        self.context = context
        self.timestamp = timestamp
        self.data = PersistentMapping(created=timestamp,
                                      userid=unicode(
                                          api.user.get_current().getId()),
                                      history_type=self.history_type,
                                      uuid=uuid)

    def append_to(self, history):
        if self.timestamp in history:
            raise ValueError('Timestamp {} already in use'.format(
                self.timestamp))

        history[self.timestamp] = self.data

    def message(self):
        raise NotImplementedError

    @property
    def css_class(self):
        return self.history_type

    @property
    def created(self):
        return self.data['created']

    @property
    def text(self):
        return self.data.get('text')

    @property
    def uuid(self):
        return self.data.get('uuid')

    def get_actor_link(self):
        return Actor.lookup(self.data['userid']).get_link()
Exemple #14
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = "link"

    def __init__(
        self,
        firstname="",
        lastname="",
        email="",
        phone="",
        extension="",
        fax="",
        department="",
        position="",
        organization="",
        location="",
        country="",
        websites=None,
        languages="",
        office="",
        room_no="",
        biography="",
        data=None,
        home_path=None,
        preferred_communities=None,
        dob=None,
        gender="",
    ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.fax = fax
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.location = location
        if country not in countries.as_dict:
            country = "XX"
        self.country = country
        self.websites = websites or ()
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = PersistentList()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None
        self.preferred_communities = preferred_communities
        self.last_login_time = None
        # states are
        # 1. inactive - user has become inactive rather than deleted from the system.
        # 2. active   - registered with a invite email which creates the profile
        self.security_state = "active"
        self.dob = dob
        self.gender = gender

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        title = [self.firstname.strip(), self.lastname.strip()]
        if getattr(self, "security_state", None) == "inactive":
            title += ["(Inactive)"]
        return unicode(" ".join(title))

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name, IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (IProfile.ALERT_IMMEDIATELY, IProfile.ALERT_DIGEST, IProfile.ALERT_NEVER):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference

    def thumb_url(self, request, size=(46, 46)):
        if "photo" in self:
            return thumb_url(self["photo"], request, size)
        else:
            return request.api.static_url + "/img/default_user.png"

    @property
    def fullname(self):
        return self.firstname + " " + self.lastname
class CustomField(Folder):
    """
    A CustomField is an object that becomes automatically included
    as part of the Add Issue page. The ID of the custom field becomes the
    name of the input. So if the ID is 'foo' the input rendered becomes
    <input name="foo">
    
    This class defines:
        
    Type of input
    ---------------------------------------------------------------------------
    
      You can select one of the following:
          text, textarea, password, hidden, select, checkbox, radio or file
          
      Depending on which one you select you'll specify parameters such as 
      'cols' (for type 'textarea' of course) or size. By having first selected
      a type, the field will autogenerate some default parameters that you can
      later modify.
    
    
    Default value
    ---------------------------------------------------------------------------
    
      The default value can be either a simple string inputted or it can be a
      reference to something else callable that will get the default value and
      this is done with a TALES expression. 
    
    Being mandatory or optional
    ---------------------------------------------------------------------------
    
      By default every field is optional but by making it mandatory, you'll 
      most likely going to have to specify a validation because sometimes it's 
      not as simple as checking that a value is boolean or not (e.g. bool(''))
    
    Validation
    ---------------------------------------------------------------------------
    
      This is where you specify either a reference to a script or a TALES 
      expression that will work out if a particular value is valid or not. 
    
    Javascript events hooks (onchange, onclick, onfocus, onblur)
    ---------------------------------------------------------------------------
    
      You'll be responsible for what you write in the values for these. The 
      values must be available javascript functions.
    
    Setting persistent values on issues
    ---------------------------------------------------------------------------
    
      (This is actually implemented in IssueTrackerProduct/IssueTracker.py)
    
      When saving the issue, we'll add an attribute to the issue like this::
          
          <id of custom field>: <value at input>
          
      This will pass through the validation a second time but unlike the first
      time, if the validation fails this time a hard error is raised. The type
      of the value is by default a unicode string or what else is appropriate 
      based on the input type. 
      You can specify an expression that will massage the input before it's 
      saved. So, suppose you want to save it as a floating point number you
      enter this expression::
          
          python:float(value)
          
    Getting persistent values on issues
    ---------------------------------------------------------------------------
    
      (This is actually implemented in IssueTrackerProduct/IssueTracker.py)

      You can ask the issuetracker for the value of a custom field simply by
      specifying the ID of the custom field and an optional default value. 
      Quite possibly you'll have an issuetracker where issues were added before
      the creation of the custom field so it'll be important to supply a 
      default value.
    
    Additionally loaded Javascript and CSS
    ---------------------------------------------------------------------------
    
      You get an area for entering the Javascript and the CSS and this is 
      automatically loaded on the Add Issue page. If you in your input of this
      (on the first line) enter a name of a file or DTML Method/Document that
      exists, that is instead rendered.
      The input can also be a valid URL if it looks relative and valid.
    
    """

    meta_type = CUSTOMFIELD_METATYPE

    manage_options = (
        {"label": "Manage", "action": "manage_field"},
        {"label": "Validation", "action": "manage_validation"},
    ) + Folder.manage_options

    _properties = (
        {"id": "title", "type": "ustring", "mode": "w"},
        {"id": "disabled", "type": "boolean", "mode": "w"},
        {"id": "python_type", "type": "selection", "mode": "w", "select_variable": "getOKPythonTypes"},
        {"id": "include_in_filter_options", "type": "boolean", "mode": "w"},
    )

    security = ClassSecurityInfo()

    def __init__(
        self,
        id,
        title=u"",
        input_type="text",
        python_type="ustring",
        extra_js=u"",
        extra_css=u"",
        mandatory=False,
        options=[],
        options_expression="",
        visibility_expression="",
        include_in_filter_options=False,
    ):
        self.id = str(id)
        self.title = title
        self.input_type = input_type
        self.python_type = python_type
        self.attributes = PersistentMapping()
        self.extra_css = extra_css
        self.extra_js = extra_js
        self.mandatory = mandatory
        self.options = options
        self.options_expression = options_expression
        self.disabled = False
        self.visibility_expression = visibility_expression
        self.include_in_filter_options = include_in_filter_options

    ##
    ## Attributes of the object
    ##

    def getId(self):
        return self.id

    def getTitle(self):
        return self.title

    def isMandatory(self):
        return self.mandatory

    def isDisabled(self):
        return self.disabled

    def getOptions(self):
        return self.options

    def getInputType(self):
        return self.input_type

    def getPythonType(self):
        return self.python_type

    security.declareProtected(VMS, "getOptionsFlat")

    def getOptionsFlat(self):
        """ return the list of options with a | pipe sign to split tuples """
        return list_to_flat(self.getOptions())

    def getOptionsExpression(self):
        """ true if it looks like a TALES expression """
        return self.options_expression

    def getVisibilityExpression(self):
        return self.visibility_expression

    def includeInFilterOptions(self):
        return self.include_in_filter_options

    ##
    ## Special Zope magic
    ##

    def getOKPythonTypes(self):
        return OK_python_types

    ##
    ## Special massaging on the class attributes
    ##

    def _prepareByType(self):
        """ set all the appropriate default bits and pieces by the 
        input_type. For example, if the input type is 'textarea' set a
        default cols and rows.
        """
        if self.input_type == "textarea":
            self.attributes["cols"] = DEFAULT_TEXTAREA_COLS
            self.attributes["rows"] = DEFAULT_TEXTAREA_ROWS

        elif self.input_type == "checkbox":
            pass
        # if 'value' in self.attributes:
        #     del self.attributes['value']

        elif self.input_type == "radio":
            if "value" in self.attributes:
                del self.attributes["value"]

        elif self.input_type == "file":
            if "value" in self.attributes:
                del self.attributes["value"]

    ##
    ## Rendering stuff
    ##

    def render(self, *value, **extra_attributes):
        """ return the tag (e.g. <textarea>) and any other accompanying
        HTML stuff. 
        """

        # if someone passes None as the first and only parameter to render()
        # the value of variable 'value' will be (None,)
        # This should be considered as if nothing is set
        if value == (None,):
            value = []

        if value and isinstance(value[0], InstanceType) and value[0].__class__.__name__ == "HTTPRequest":
            # this method has been called with REQUEST as the value parameter.
            # Note that it's still a list or tuple but convert it to the actual value.
            value = value[0].form.get(self.getId(), None)
            if value is None:
                value = ()
            else:
                value = (value,)  # make sure it's a tuple

        out = []
        if DevelopmentMode:
            out.append(u"<!--CustomField: %s -->" % self.getId())

            if self.isDisabled():
                logger.warn("A disabled custom field (%s) is rendered" % self.absolute_url_path())

        # take out some extra keywords from the extra_attributes
        skip_extra_css = extra_attributes.pop("skip_extra_css", False)
        skip_extra_js = extra_attributes.pop("skip_extra_js", False)

        if self.extra_css and not skip_extra_css:
            out.append(self.render_extra_css())
        if self.extra_js and not skip_extra_js:
            out.append(self.render_extra_js())

        out.append(self.render_tag(*value, **extra_attributes))

        return "\n".join(out)

    def render_tag(self, *value, **extra_attributes):
        """ return a piece of unicode HTML that """

        assert len(value) <= 1, "Can't pass more than one argument as value"

        inner = []
        attributes = {}
        # notice the order of these update() calls! It matters

        name_prefix = extra_attributes.pop("name_prefix", "")

        # It's an option to called render_tag() with in_filter=True which tells
        # us that this tag is rendered as a filter, in the filter options.
        # This is something that can be done on-the-fly and it means that
        # certain things should work differently. For example, a 'select' type
        # input get's an added 'size' and 'multiple' attribute when used as a
        # filter.
        in_filter = extra_attributes.pop("in_filter", False)

        # core attributes
        dom_id = self.attributes.get("dom_id", "id_%s" % self.getId())
        attributes.update({"name": self._wrapPythonTypeName(name_prefix), "id": dom_id, "title": self.getTitle()})

        # saved attributes
        attributes.update(dict(self.attributes))

        # extra on rendering attributes
        attributes.update(extra_attributes)

        # Now, "hack" the attributes if this is used in a filter
        if in_filter:
            if self.input_type == "select":
                attributes["multiple"] = "multiple"
                if "size" not in attributes:
                    attributes["size"] = min(5, len(list(self.getOptionsIterable())))

        # filler is a dict that we will use to render the template
        filler = {}

        if self.input_type == "textarea":
            template = u"<textarea %(inner)s>%(value)s</textarea>"

            v = None
            if value:
                v = value[0]  # from the argument
            elif "value" in attributes:
                v = attributes.pop("value")

            if v:
                filler["value"] = Utils.safe_html_quote(v)
            else:
                filler["value"] = u""

        elif self.input_type == "select":
            template = u"<select %(inner)s>\n%(all_options)s\n</select>"
            all_options = []

            v = []
            if value:
                v = value[0]
                # makes sure the value doesn't become a nested list
                if isinstance(v, list):
                    v = Utils.flatten_lines(v)

            elif "value" in attributes:
                v = attributes.pop("value")

            if not isinstance(v, (tuple, list)):
                v = [v]

            # if the value passed to render this select contains
            # items that are not in the list of options, don't
            # use the list of options.
            _values_selected = []
            for option in self.getOptionsIterable():
                if isinstance(option, (tuple, list)):
                    value, label = option
                else:
                    value, label = option, option

                if self.getPythonType() == "int":
                    try:
                        value = int(value)
                    except ValueError:
                        pass
                elif self.getPythonType() == "float":
                    try:
                        value = float(value)
                    except ValueError:
                        pass

                if value in v:
                    tmpl = u'<option value="%s" selected="selected">%s</option>'
                    _values_selected.append(value)
                else:
                    tmpl = u'<option value="%s">%s</option>'

                all_options.append(tmpl % (value, label))

            if Set(v) - Set(_values_selected):
                # there were values that weren't in the list of options!
                _values_not_in_options = list(Set(v) - Set(_values_selected))

                # if nothing was matched in the list of options,
                # reset the whole all_options list.
                if not _values_selected and all_options:
                    all_options = []

                for value in _values_not_in_options:
                    label = value
                    tmpl = u'<option value="%s" selected="selected">%s</option>'
                    all_options.append(tmpl % (value, label))

            filler["all_options"] = "\n".join(all_options)

        elif self.input_type == "radio":
            # special case
            if not self.getOptionsIterable():
                template = u"ERROR: No options"
            else:
                template = u"%(all_inputs)s"
                all_inputs = []

                v = None
                if value:
                    v = value[0]  # from the argument

                elif "value" in attributes:
                    v = attributes.pop("value")

                special_attributes = ""
                inner = []
                for k, v2 in attributes.items():
                    if k in ("id",):
                        continue
                    inner.append('%s="%s"' % (k, v2))
                if inner:
                    special_attributes = " " + " ".join(inner)

                for option in self.getOptions():
                    if isinstance(option, (tuple, list)):
                        value, label = option
                    else:
                        value, label = option, option

                    if value == v:
                        tmpl = u'<input type="radio" value="%s" checked="checked"%s /> %s<br />'
                    else:
                        tmpl = u'<input type="radio" value="%s"%s/> %s<br />'
                    all_inputs.append(tmpl % (value, special_attributes, label))

                filler["all_inputs"] = "\n".join(all_inputs)

        elif self.input_type == "checkbox":
            # another special case

            v = None
            if value:
                v = value[0]  # from the argument
            elif "value" in attributes:
                v = attributes.pop("value")

            # If there are no options you can work this like a normal text input
            if not self.getOptions():
                # but what if it should be a boolean and it's true, then the
                # tag needs to contain checked="checked"
                if v:
                    template = u'<input type="checkbox" checked="checked" %(inner)s />'
                else:
                    template = u'<input type="checkbox" %(inner)s />'

            else:
                # crap!
                template = u"%(all_inputs)s"
                all_inputs = []

                special_attributes = ""
                inner = []
                for k, v2 in attributes.items():
                    if k in ("id",):
                        continue
                    inner.append('%s="%s"' % (k, v2))
                if inner:
                    special_attributes = " " + " ".join(inner)

                for option in self.getOptions():
                    if isinstance(option, (tuple, list)):
                        value, label = option
                    else:
                        value, label = option, option

                    if value == v:
                        tmpl = u'<input type="checkbox" value="%s" checked="checked"%s /> %s<br />'
                    else:
                        tmpl = u'<input type="checkbox" value="%s"%s/> %s<br />'
                    all_inputs.append(tmpl % (value, special_attributes, label))

                filler["all_inputs"] = "\n".join(all_inputs)

        elif self.input_type == "password":

            template = u'<input type="password" %(inner)s />'

        elif self.input_type == "file":
            template = u'<input type="file" %(inner)s />'

        else:  # type text
            template = u"<input %(inner)s />"

        if not (self.input_type == "radio" or (self.input_type == "checkbox" and self.getOptions())):

            if value and self.input_type not in ("select",):
                if value and value[0]:
                    # This overrides the default value
                    attributes["value"] = value[0]

            for key, val in sorted(attributes.items()):
                inner.append('%s="%s"' % (key, val))

            filler["inner"] = " ".join(inner)

        return template % filler

    def __str__(self):
        return str(self.render())

    def _wrapPythonTypeName(self, prefix=""):
        """ if name is 'age' and python_type is 'int' then return
        'age:int'.
        If the type is unicode type, add the encoding
        """
        name, python_type = self.getId(), self.python_type

        # add the prefix
        name = "%s%s" % (prefix, name)

        if self.input_type == "file":
            # exception
            return name

        if python_type in ("ustring", "ulines"):
            return "%s:%s:%s" % (name, UNICODE_ENCODING, python_type)
        elif python_type == "string":
            return name
        else:
            return "%s:%s" % (name, python_type)

    def render_extra_css(self):
        """ return a piece of HTML that loads the CSS.
        If it looks like the attribute self.extra_css is a URI,
        return a <link rel="stylesheet"> tag instead.
        """
        css = self.extra_css
        if len(css.splitlines()) == 1 and (css.startswith("http") or css.startswith("/") or css.endswith(".css")):
            return u'<link rel="stylesheet" type="text/css" href="%s" />' % css
        elif css:
            return u'<style type="text/css">\n%s\n</style>' % css
        else:
            return u""

    def render_extra_js(self):
        """ return a piece of HTML that loads the javascript.
        If it looks like the attribute self.extra_js is a URI,
        return a <script src="..."> tag instead.
        """
        js = self.extra_js
        if len(js.splitlines()) == 1 and (js.startswith("http") or js.startswith("/") or js.endswith(".js")):
            return u'<script type="text/javascript" src="%s"></script>' % js
        elif js:
            return u'<script type="text/javascript">\n%s\n</script>' % js
        else:
            return u""

    security.declareProtected(VMS, "preview_render")

    def preview_render(self, *value, **extra_attributes):
        """ wrapper on render() that is able to cut out some of the verbose stuff
        from the render output.
        """
        html = self.render(*value, **extra_attributes)
        return html

    ##
    ## TALES expression for options
    ##

    def getOptionsIterable(self):
        """ return a list of options """
        if self.getOptionsExpression():
            ec = self._getExprContext(self)
            ex = Expression(self.options_expression)
            return list(ex(ec))
        else:
            return self.getOptions()

    def _getExprContext(self, object, extra_namespaces={}):
        return getExprContext(self, object, extra_namespaces=extra_namespaces)

    def _valid_options_expression(self):
        """ return true if self.options_expression is valid 
        otherwise raise an error. 
        """
        ec = self._getExprContext(self)
        ex = Expression(self.options_expression)
        iterable = ex(ec)

        if isinstance(iterable, (list, tuple)):
            # each item should be unicodeable and
            # every item must something
            for item in iterable:
                if isinstance(item, (tuple, list)):
                    key, value = item
                    if key and not value:
                        value = key
                else:
                    key, value = item, item
                    if not item:
                        return False

            # an iterable we can't find anything wrong with
            return True

        # default is not to pass
        return False

    ##
    ## Validation
    ##

    def getValidationExpressions(self):
        return self.objectValues(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)

    security.declarePrivate("testValidValue")

    def testValidValue(self, value):
        """ return a tuple of (valid or not [bool], message [unicode]) if the value
        passes all the validation expressions (assuming the field has any)
        """
        # check the python type
        if self.python_type == "ustring":
            # should be possible to do this
            try:
                unicode(value)
            except TypeError:
                return False, u"Not a unicode string"
        elif self.python_type == "int":
            try:
                int(value)
            except ValueError:
                return False, u"Not an integer number"
        elif self.python_type == "float":
            try:
                float(value)
            except ValueError:
                return False, u"Not a floating point number"
        elif self.python_type == "long":
            try:
                long(value)
            except ValueError:
                return False, u"Not a long integer number"
        elif self.python_type == "date":
            try:
                if isinstance(value, basestring):
                    DateTime(value)
            except DateError:
                return False, u"Not a valid date"
        elif self.python_type == "ulines":
            if isinstance(value, basestring):
                try:
                    [unicode(x) for x in value.splitlines()]
                except ValueError:
                    return False, u"Not a list of unicode strings"
            elif value is not None:
                value = Utils.flatten_lines(value)
                try:
                    [unicode(x) for x in value]
                except ValueError:
                    return False, u"Not a list of unicode strings"
        elif self.python_type == "lines":
            if isinstance(value, basestring):
                try:
                    [str(x) for x in value.splitlines()]
                except ValueError:
                    return False, u"Not a list of strings"
            elif value is not None:
                value = Utils.flatten_lines(value)
                try:
                    [str(x) for x in value]
                except ValueError:
                    return False, u"Not a list of strings"

        # check each TALES expression
        for ve in self.getValidationExpressions():
            ec = self._getExprContext(self, extra_namespaces=dict(value=value))
            ex = Expression(ve.expression)
            if not bool(ex(ec)):
                return False, ve.message

        # by default no validation expression made it invalid
        return True, None

    ##
    ## Working with the persistent attributes
    ##

    def getCoreAttribute(self, *key_and_default):
        """ return the value of this attribute. If len(@key_and_default) = 2 is
        the second one is a default value. If not don't fall back on a default.
        """
        if not len(key_and_default) in (1, 2):
            raise ValueError, "Call getCoreAttribute(key [,default])"

        if len(key_and_default) == 1:
            return self.attributes[key_and_default[0]]
        else:
            return self.attributes.get(key_and_default[0], key_and_default[1])

    security.declareProtected(VMS, "getCoreAttributeKeys")

    def getCoreAttributeKeys(self):
        return list(self.attributes.keys())

    security.declareProtected(VMS, "getCoreAttributeKeyLabel")

    def getCoreAttributeKeyLabel(self, key, html_ok=False):
        """ return a string that explains what the key is.
        The resturn string can contain HTML.
        """
        if key == "dom_id":
            if html_ok:
                return u'<abbr title="DOM element ID, not Zope object ID">DOM ID</abbr>'
            else:
                return u"DOM ID"

        if key.startswith("on") and re.findall("on\w+", key):
            return u"on" + key[2:].capitalize()

        if key in ("rows", "cols"):
            return u"Textarea %s" % key

        return key.title()

    def getCoreAttributeKeySuggestions(self):
        """ return a list of suggestions of attribute keys you might want to add """
        suggestions = ["style", "size", "dom_id", "onchange", "onkeypress", "onclick", "onfocus", "onblur", "value"]
        # add more
        if self.input_type == "textarea":
            suggestions.append("cols")
            suggestions.append("rows")
        elif self.input_type == "select":
            suggestions.append("multiple")

        # reduce already used ones
        suggestions = [x for x in suggestions if x not in self.attributes]

        # sort them by their labels
        suggestions = [(self.getCoreAttributeKeyLabel(x), x) for x in suggestions]
        suggestions.sort()

        # return just the keys
        return [x[1] for x in suggestions]

    def getCoreAttributeKeyName(self, key):
        """ return what the suitable name for the key should be a input tag
        """
        return u"%s:ustring" % key

    def getDeleteableAttributeKeys(self):
        """ return a list of keys of attributes you can delete """
        all = Set(list(self.attributes.keys()))
        not_ = Set(CORE_ATTRIBUTES)
        return list(all - not_)

    ##
    ## Modifying the custom field
    ##

    security.declareProtected(VMS, "manage_saveFieldProperties")

    def manage_saveFieldProperties(
        self,
        input_type=None,
        python_type=None,
        title=None,
        mandatory=False,
        extra_css=None,
        extra_js=None,
        options=None,
        options_expression=None,
        visibility_expression=None,
        include_in_filter_options=False,
        REQUEST=None,
        **settings
    ):
        """ saving changes via the web """
        if input_type is not None:
            different = input_type != self.input_type

            if not input_type in OK_input_types:
                raise ValueError, "invalid input_type"
            self.input_type = input_type
            if different:
                self._prepareByType()

        if python_type is not None:
            assert python_type in OK_python_types, "Invalid Python type (%r)" % python_type
            self.python_type = python_type

        if title is not None:
            self.title = unicode(title)

        self.mandatory = bool(mandatory)
        self.include_in_filter_options = bool(include_in_filter_options)

        if extra_css is not None:
            self.extra_css = unicode(extra_css).strip()

        if extra_js is not None:
            self.extra_js = unicode(extra_js).strip()

        if options_expression is not None:
            self.options_expression = str(options_expression).strip()

            if self.options_expression:
                assert self._valid_options_expression(), "Invalid expression"

        if visibility_expression is not None:
            self.visibility_expression = visibility_expression

        if options is not None:
            self.options = flat_to_list(options)

        if not settings and REQUEST is not None:
            settings = self.REQUEST.form

        # I don't like the pattern but it'll have to do for now
        for key, value in settings.items():
            if key not in CORE_ATTRIBUTES:
                self.attributes[key] = value

        if REQUEST is not None:
            msg = "Changes saved"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_addFieldProperty")

    def manage_addFieldProperty(self, key=None, new_key=None, REQUEST=None):
        """ add a new attribute property """
        if not key and not new_key:
            raise ValueError, "must pass 'key' OR 'new_key'"

        if new_key:
            key = new_key.strip()

        key = str(key)

        self.attributes[key] = u""

        if REQUEST is not None:
            msg = "Field added"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            url += "#field-%s" % key
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_deleteFieldProperty")

    def manage_deleteFieldProperty(self, key, REQUEST=None):
        """ delete a field property """
        del self.attributes[key]

        if REQUEST is not None:
            msg = "Attribute deleted"
            url = self.absolute_url() + "/manage_field"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_addValidationExpression")

    def manage_addValidationExpression(self, expression, message=u"", REQUEST=None):
        """ add a new validation expression """
        # check that it's not complete rubbish
        expression = str(expression).strip()
        message = unicode(message).strip()
        if not expression:
            raise ValueError, "Expression can't be empty"

        # XXX: Got to figure out a better way to test the expression without a
        # arbitrary value like this
        ## test it
        # ec = self._getExprContext(self, extra_namespaces=dict(value='123'))
        # ex = Expression(expression)
        # try:
        #    ex(ec)
        # except Exception, m:
        #    raise ValueError, m

        c = len(self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)) + 1
        oid = "validation_%s" % c
        while base_hasattr(self, oid):
            c += 1
            oid = "validation_%s" % c

        instance = ValidationExpression(oid, expression, message)
        self._setObject(oid, instance)

        if REQUEST is not None:
            msg = "Expression added"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_deleteValidationExpression")

    def manage_deleteValidationExpression(self, id, REQUEST=None):
        """ delete a validation expression """
        assert id in self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)

        self.manage_delObjects([id])

        if REQUEST is not None:
            msg = "Expression delete"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)

    security.declareProtected(VMS, "manage_editValidationExpression")

    def manage_editValidationExpression(self, id, expression, message, delete=False, REQUEST=None):
        """ change a validation expression object """
        assert id in self.objectIds(CUSTOMFIELD_VALIDATION_EXPRESSION_METATYPE)
        obj = getattr(self, id)

        if delete:
            return self.manage_deleteValidationExpression(id, REQUEST=REQUEST)

        expression = str(expression).strip()
        message = unicode(message).strip()
        if not expression:
            raise ValueError, "Expression can't be empty"

        # test it
        ec = self._getExprContext(self, extra_namespaces=dict(value="123"))
        ex = Expression(expression)
        try:
            ex(ec)
        except Exception, m:
            raise ValueError, m

        obj.expression = expression
        obj.message = message

        if REQUEST is not None:
            msg = "Expression changed"
            url = self.absolute_url() + "/manage_validation"
            url += "?manage_tabs_message=%s" % Utils.url_quote_plus(msg)
            REQUEST.RESPONSE.redirect(url)
class BaseHistoryRecord(object):
    """Basic implementation of a history record.

    Contains basic set of required data and abstract implementation. Can
    re-populate iself from data by invoking the __new__ method directly and
    then re-populating the attributes on the instance instead of calling
    __init__.

    Each record must have a unique `history_type` from which it can be built
    with IHistory.append_record.

    If `needs_syncing` is `True` a records that is created on the
    `SubmittedProposal` side is automatically added to its corresponding
    `Proposal`.
    """

    history_type = None
    needs_syncing = False

    @classmethod
    def re_populate(cls, context, timestamp, data):
        record = cls.__new__(cls)
        record.context = context
        record.timestamp = timestamp
        record.data = data
        return record

    def __init__(self, context, timestamp=None, uuid=None):
        timestamp = timestamp or utcnow_tz_aware()
        if uuid is None:
            uuid = uuid4()
        elif isinstance(uuid, basestring):
            uuid = UUID(uuid)

        self.context = context
        self.timestamp = timestamp
        self.data = PersistentMapping(
            created=timestamp,
            userid=unicode(api.user.get_current().getId()),
            history_type=self.history_type,
            uuid=uuid)

    def append_to(self, history):
        if self.timestamp in history:
            raise ValueError('Timestamp {} already in use'.format(
                self.timestamp))

        history[self.timestamp] = self.data

    def message(self):
        raise NotImplementedError

    @property
    def css_class(self):
        return self.history_type

    @property
    def created(self):
        return self.data['created']

    @property
    def text(self):
        return self.data.get('text')

    @property
    def uuid(self):
        return self.data.get('uuid')

    def get_actor_link(self):
        return Actor.lookup(self.data['userid']).get_link()
class ACL(Persistent):

    """ Access control hub. The ACL can be instantiated and added to, say
    the root object, with attribute name acl. """

    def __init__(self, settings):

        self.users = PersistentMapping()
        self.groups = PersistentMapping()
        self.activation = PersistentMapping()

        admin, pwd = settings.get('pycms.admin_user',
                                  "admin:admin").split(":")

        self.users['admin'] = User(admin, "Administrator", "", pwd)
        self.groups['admin'] = Group('admin', users=PersistentList(['admin']))
        self.groups['viewers'] = Group('viewers')
        self.groups['editors'] = Group('editors')

    def generate_user_invite_key(self, user_id):

        """ Generate unique registration key for user and set on user. """

        if not user_id in self.users:
            return None

        t1 = time.time()
        time.sleep(random.random())
        t2 = time.time()
        base = hashlib.md5(str(t1 + t2))
        key = base.hexdigest()[:KEY_LENGTH]

        self.activation[key] = self.users[user_id]

        return key

    def get_user_for_activation(self, key):

        return self.activation.get(key, None)

    def unset_activation_key(self, key):

        if key in self.activation:
            del self.activation[key]

    def list_users(self):

        """ Return a dict of users, using the id as key """

        return self.users.keys()

    def list_groups(self):

        return self.groups.keys()

    def update_user(self, **data):

        self.users[data['email']].name = data['name']
        if data.get('pwd', None):
            self.users[data['email']].set_pwd(data['pwd'])

    def set_user_groups(self, user_id, groups=[]):

        """ Remove user from all groups, and then reset..."""

        for group_id in self.groups.keys():
            self.rm_user_from_group(group_id, user_id)

        for group_id in groups:
            self.add_user_to_group(group_id, user_id)

    def rm_user_from_group(self, group_id, user_id):

        if user_id in self.groups[group_id].users:
            idx = self.groups[group_id].users.index(user_id)
            del self.groups[group_id].users[idx]

    def add_user_to_group(self, group_id, user_id):

        if not user_id in self.groups[group_id].users:
            self.groups[group_id].users.append(user_id)

    def create_user(self, email, pwd=None, name='', profile=None):

        if email in self.users:
            return None

        self.users[email] = User(email, name or email,
                                 email, pwd,
                                 profile=profile)

        return self.users[email]

    def remove_user(self, user_id):

        if user_id in self.users:
            del self.users[user_id]
Exemple #18
0
class Room( Persistent, RoomBase ):
    """ 
    ZODB specific implementation. 
    
    For documentation of methods see base class.
    """

    __dalManager = Factory.getDALManager()
    vcList = []

    def __init__(self):
        RoomBase.__init__( self )
        self.customAtts = PersistentMapping()
        self.avaibleVC = []
    
    def setAvailableVC(self, avc):
        self.avaibleVC = avc
    
    def getAvailableVC(self):
        try:
            return self.avaibleVC
        except:
            self.avaibleVC = []
        return self.avaibleVC

    @staticmethod
    def getRoot():
        return Room.__dalManager.getRoot(_ROOMS)
    
    def insert( self ):
        """ Documentation in base class. """
        RoomBase.insert( self )
        roomsBTree = Room.getRoot()
        # Ensure ID
        if self.id == None:
            # Maximum ID + 1
            if len( roomsBTree ) > 0:
                self.id = roomsBTree.maxKey() + 1
            else:
                self.id = 1 # Can not use maxKey for 1st record in a tree
        # Add self to the BTree
        roomsBTree[self.id] = self 
    
    def update( self ):
        """ Documentation in base class. """
        RoomBase.update( self )
        
        # Check Simba mailing list
        listName = self.customAtts.get( 'Simba List' )
        if listName:
            from MaKaC.user import GroupHolder
            groups = GroupHolder().match( { 'name': listName }, forceWithoutExtAuth = True )
            if not groups:
                groups = GroupHolder().match( { 'name': listName } )
            if not groups:
                self.customAtts['Simba List'] = 'Error: unknown mailing list'
        
        self._p_changed = True
    
    def remove( self ):
        """ Documentation in base class. """
        RoomBase.remove( self )
        roomsBTree = Room.getRoot()
        del roomsBTree[self.id]
        from MaKaC.user import AvatarHolder
        AvatarHolder().invalidateRoomManagerIdList()
    
    # Typical actions
    @staticmethod
    def getRooms( *args, **kwargs ): 
        """ Documentation in base class. """

        roomsBTree = Room.getRoot()
        location = kwargs.get( 'location' )

        if kwargs.get( 'allFast' ) == True:
            return [ room for room in roomsBTree.values() if room.isActive and (not location or room.locationName == location) ]

        if kwargs.get( 'reallyAllFast' ) == True:
            return [ room for room in roomsBTree.values() if (not location or room.locationName == location) ]
        
        if len( kwargs ) == 0:
            ret_lst = []
            for room in roomsBTree.values():
                ret_lst.append( room )

        roomID = kwargs.get( 'roomID' )
        roomName = kwargs.get( 'roomName' )
        roomEx = kwargs.get( 'roomExample' )
        resvEx = kwargs.get( 'resvExample' )
        freeText = kwargs.get( 'freeText' )
        available = kwargs.get( 'available' )
        countOnly = kwargs.get( 'countOnly' )
        minCapacity = kwargs.get( 'minCapacity' )
        location = kwargs.get( 'location' )
        ownedBy = kwargs.get( 'ownedBy' )
        customAtts = kwargs.get( 'customAtts' )
#        responsibleID = kwargs.get( 'responsibleID' )
        
        ret_lst = []
        counter = 0
        if roomID != None:
            return roomsBTree.get( roomID )
        if roomName != None:
            for room in roomsBTree.itervalues():
                if room.name == roomName:
                    if location == None or room.locationName == location:
                        return room
            return None
        
        for room in roomsBTree.itervalues():
            # Apply all conditions =========
            if location != None:
                if room.locationName != location:
                    continue            
            if roomEx != None:
                if not qbeMatch( roomEx, room, Room.__attrSpecialEqual, minCapacity = minCapacity ):
                    continue
                if not room.__hasEquipment( roomEx.getEquipment() ):
                    continue
            if freeText != None:
                if not room.__hasFreeText( freeText.split() ):
                    continue
            if resvEx != None:
                resvEx.room = room
                aval = room.isAvailable( resvEx )
                if aval != available:
                    continue
            if ownedBy != None:
                if not room.isOwnedBy( ownedBy ):
                    continue
            if customAtts is not None:
                if not hasattr(room, "customAtts"):
                    continue
                discard = False
                for condition in customAtts:
                    attName = condition["name"]
                    allowEmpty = condition.get("allowEmpty", False)
                    filter = condition.get("filter", None)
                    if not attName in room.customAtts:
                        discard = True
                        break
                    elif not allowEmpty and str(room.customAtts[attName]).strip() == "":
                        discard = True
                        break
                    elif not filter(room.customAtts[attName]):
                        discard = True
                        break
                if discard:
                    continue
            
            # All conditions are met: add room to the results
            counter += 1
            if not countOnly:
                ret_lst.append( room )

        #print "Found %d rooms." % counter
        if countOnly:
            return counter
        else:
            return ret_lst

    # Statistics ====================================

    @staticmethod
    def countRooms( *args, **kwargs ):
        """ Documentation in base class. """
        kwargs['countOnly'] = True
        return Room.getRooms( **kwargs )

    @staticmethod
    def getNumberOfRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        return Room.countRooms( location = location )
    
    @staticmethod
    def getNumberOfActiveRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )

    @staticmethod
    def getNumberOfReservableRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isReservable = True
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )
    
    def getLocationName( self ):
        #from MaKaC.plugins.RoomBooking.default.factory import Factory
        #return Factory.locationName
        return self._locationName
    
    def setLocationName( self, locationName ):
        self._locationName = locationName

    def savePhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()
    
    def saveSmallPhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomSmallPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()

    
    # ==== Private ===================================================

    def _getSafeLocationName( self ):
        if self.locationName == None:
            return None
        s = ""
        for i in xrange( 0, len( self.locationName ) ):
            code = ord( self.locationName[i] )
            if ( code in xrange( ord( 'a' ), ord( 'z' ) + 1 ) ) or \
               ( code in xrange( ord( 'A' ), ord( 'Z' ) + 1 ) ) or \
               ( code in xrange( ord( '0' ), ord( '9' ) + 1 ) ):
                # Valid
                s += self.locationName[i]
            else:
                s += '_' # Replace all other characters with underscore
        return s

    def _doGetPhotoId( self, force = False ):
        photoId = "%s-%s-%s-%s" % ( str( self._getSafeLocationName() ), str( self.building ).strip(), str( self.floor ).strip(), str( self.roomNr ).strip() )
        
        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = photoId + ".jpg"
        fullPath = os.path.join( filePath, fileName )
        from os.path import exists
        if exists( fullPath ) or force:
            return photoId
        else:
            return None

    def _doSetPhotoId( self ):
        """
        For this plugin, photoId is always composed of location-building-floor-room.jpg
        """
        pass

    def __hasFreeText( self, freeTextList ):
        # OR
        for freeText in freeTextList:
            freeText = freeText.lower()
            if self.__hasOneFreeText( freeText ):
                return True
        return False
    
    def __hasOneFreeText( self, freeText ):
        # Look for freeText in all string and int attributes
        for attrName in dir( self ):
            if attrName[0] == '_': 
                continue 
            attrType = eval( 'self.' + attrName + '.__class__.__name__' )
            if attrType == 'str': 
                attrVal = eval( 'self.' + attrName )
                if attrVal.lower().find( freeText ) != -1:
                    return True

        # Look for freeText in equipment
        if self.__hasEquipment( [ freeText ] ):
            return True
        
        # Look for freeText in responsible
        if self.responsibleId != None:
            user = self.getResponsible();
            if freeText in user.getFullName().lower()  or  freeText in user.getEmail().lower():
                return True
        
        # Look for freeText in custom attributes
        for value in self.customAtts.itervalues():
            if value and ( freeText in value.lower() ):
                return True
        
        # Not found
        return False
        
    @staticmethod
    def __goodCapacity( val1, val2, minCapacity = None ):
        # Difference in capacity less than 20%
        if val1 < 1: val1 = 1
        
        if not minCapacity:
            return abs( val1 - val2 ) / float( val1 ) <= 0.2
        else:
            return val2 > val1

    @classmethod
    def __attrSpecialEqual( cls, attrName, exampleVal, candidateVal, **kwargs ):
        if attrName in ( 'guid', 'locationName', 'name', 'photoId', 'needsAVCSetup' ):
            return True # Skip by stating they match
        if attrName in ( 'responsibleId', 'responsibleID' ):
            return exampleVal == candidateVal # Just exact string matching
        if attrName[0:7] == 'verbose':
            return True
        if attrName.find( 'capacity' ) != -1:
            minCapacity = kwargs.get( 'minCapacity' )
            return cls.__goodCapacity( exampleVal, candidateVal, minCapacity )
        if attrName == 'customAtts':
            # Check if all values in exampleVal are contained 
            # in corresponding values of candidateVal
            for k, v in exampleVal.iteritems():
                if v: # If value is specified
                    if candidateVal.get( k ) == None:
                        # Candidate does not have the attribute
                        return False
                    if not ( v in candidateVal[k] ):
                        # Candidate's attribute value does not match example
                        return False
            return True
        return None
        
    def __hasEquipment( self, requiredEquipmentList ):
        iHave = self.getEquipment()
        for reqEq in requiredEquipmentList:
            have = False
            for myEq in iHave:
                if myEq.lower().find( reqEq.lower() ) != -1:
                    have = True
                    break
            if not have:
                return False
        return True
class ContentGroupManager:
    """ """

    def __init__(self):
        self.__groups_collection = PersistentMapping()

    def __add_group_item(self, id, name, filter, pattern):
        #create a new item
        item = ContentGroup(id, name, filter, pattern)
        self.__groups_collection[id] = item

    def __update_group_item(self, id, name, filter, pattern):
        #modify an item
        item = self.__groups_collection.get(id)
        if item is not None:
            item.name =     name
            item.filter =   filter
            item.pattern =  pattern
            self.__groups_collection[id] = item

    def __delete_group_item(self, id):
        #delete an item
        try:    del(self.__groups_collection[id])
        except: pass


    #################
    #   BASIC API   #
    #################
    def get_groups_collection(self):
        #return the groups collection
        return self.__groups_collection

    def get_groups_ids(self):
        #get the groups ids
        return self.__groups_collection.keys()

    def get_groups_list(self):
        #get a list with all items
        return utils.utSortObjsListByAttr(self.__groups_collection.values(), 'name')

    def get_group_item(self, id):
        #get an item
        try:    return self.__groups_collection[id]
        except: return None

    def get_group_item_data(self, id):
        #get an item data
        item = self.get_group_item(id)
        if item is not None: 
            return ['update',
                    item.id,
                    item.name,
                    item.filter,
                    item.pattern,
                    item.start,
                    item.maxResults,
                    item.g_filter,
                    item.restrict,
                    item.safeSearch,
                    item.language,
                    item.inputencoding,
                    item.outputencoding,
                    item.http_proxy,
                    item.search_type]
        else:
            return ['add', '', '', '', '']

    def add_group_item(self, id, name, filter, pattern):
        #create a new item
        self.__add_group_item(id, name, filter, pattern)

    def update_group_item(self, id, name, filter, pattern):
        #modify an item
        self.__update_group_item(id, name, filter, pattern)

    def update_google_props(self, id, start, maxResults, filter, restrict, safeSearch,
                            language, inputencoding, outputencoding, http_proxy):
        #update the Google search properties
        msg = 0
        item = self.__groups_collection.get(id)
        if item is not None:
            #set data
            try:
                #check if integer values
                start =            int(start)
                maxResults =       int(maxResults)
                filter =           int(filter)
                safeSearch =       int(safeSearch)
            except:
                msg = 1
            if not msg:
                #set data
                item.start =            start
                item.maxResults =       maxResults
                item.g_filter =         filter
                item.safeSearch =       safeSearch
                item.restrict =         restrict
                item.language =         language
                item.inputencoding =    inputencoding
                item.outputencoding =   outputencoding
                item.http_proxy =       http_proxy
                self.__groups_collection[id] = item
        return msg

    def update_search_type(self, id, search_type):
        #update the Google search type
        item = self.__groups_collection.get(id)
        item.search_type = search_type
        self.__groups_collection[id] = item

    def delete_group_item(self, ids):
        #delete 1 or more items
        map(self.__delete_group_item, ids)

    security = ClassSecurityInfo()
    security.setDefaultAccess("allow")
Exemple #20
0
class DecaObject(Persistent):
	"""A generic DECA object"""
	def __init__(self, id = None):
		Persistent.__init__(self)
		if id is not None:
			self.ID = id
		else:
			self.ID = uuid.uuid4()
		self.TemplateName = ""
		self.Attributes = PersistentMapping()
		self.TitleAttr = None
		self.Graphics = None
		self.IsReflection = False
		self.Tag = None

	def copy(self, newID):
		obj = DecaObject(newID)
		obj.TemplateName = self.TemplateName
		obj.TitleAttr = self.TitleAttr
		for k,v in self.Attributes.items() :
			obj.Attributes[k] = v
		obj.Graphics = self.Graphics
		obj.IsReflection = self.IsReflection
		obj.Tag = self.Tag
		return obj

	def GetTitle(self):
		"""Returns the title of the object. If the Title Attribute defined, it's value will be returned.
		Else if Tag defined, it's value will be returned. Else the object's ID will be returned. """
		if self.TitleAttr is not None:
			return self.Attributes[self.TitleAttr]
		if self.Tag is not None and str(self.Tag) != '':
			return str(self.Tag)
		return str(self.ID)

	def SetAttr(self, name, value, location=LOCAL):
		"""Sets the attribute value in desired location.

		**Note:** if the object isn't the reflect, attribute will be stored locally"""
		if location == LOCAL :
			self.Attributes[name] = value
		# todo: set attribute to the base object if we are reflection

	def GetAttr(self, name, default=None, priority=LOCAL):
		"""Gets the attribute value. For the reflects the priority may points that the base value must be given.
		If value absent in desired location other locations will be scanned. Finnaly, the default value will be returned"""
		result = default
		if priority == LOCAL :
			result = self.Attributes.get(name, default)
		else:
			# todo: read base object's property if we are reflection
			pass
		return result

	def GetShape(self):
		# todo: get shape description. Is it necessary?
		return 

	def GetPropList(self, holder = None) :
		self.propsGrid = holder
		props = OrderedDict([
			('Identity', self.ID),
			('Template', self.TemplateName),
			('Title Attribute', self.TitleAttr),
			('Is Reflection', self.IsReflection),
		])
		attrs = OrderedDict([])
		for k,v in self.Attributes.items():
			attrs.update([(k, v)])
		result = OrderedDict([
			(_("Object's properties"), props),
			(_("Local object's attributes"), attrs)
		])
		return result

	def ReflectTo(self, dstLayer):
		if not isinstance(dstLayer, Deca.Layer):
			dstLayer = Deca.world.GetLayer(str(dstLayer))
		if not self.ID in dstLayer.storage.objects.keys():
			nwo = self.copy(self.ID)
			nwo.IsReflection = True
			dstLayer.AddObject(nwo)
			return nwo
		return None

	def GetEngines(self):
		def genList(base, dirlist):
			res = []
			for ent in dirlist:
				if os.path.isdir(os.path.join(base, ent)):
					# process subdir
					nb = os.path.join(base, ent)
					res.append( (ent, genList(nb, os.listdir(nb))) )
				else:
					ft = os.path.splitext(ent)
					if ft[1].lower() == '.py' and not ft[0].startswith('_'):
						res.append(ft[0])
			return res
		# main function
		pl = []
		if self.TemplateName and self.TemplateName != '' :
			epath = os.path.join(Deca.world.EnginesPath, self.TemplateName)
			if os.path.isdir(epath):
				pl = genList(epath, os.listdir(epath))
			# if Object's engines
		# if template given
		return pl

	def ExecuteEngine(self, name, layer, shape = None, dict = None):
		item = os.path.join(Deca.world.EnginesPath, self.TemplateName, name + '.py')
		fl = None
		if not dict:
			dict = globals()
		try:
			fl = open(item, 'r')
			dict['ActiveDecaLayer'] = layer
			dict['ActiveShape'] = shape
			dict['ActiveObject'] = self.ID
			exec fl in dict
		finally:
			if fl : fl.close()
Exemple #21
0
class CoreFilter(Persistent):
    """Core persistent record filter implementation"""

    implements(IRecordFilter)

    def __init__(self, *args, **kwargs):
        super(CoreFilter, self).__init__(*args, **kwargs)
        self._uid = str(uuid.uuid4())
        self.reset(**kwargs)

    def reset(self, **kwargs):
        self.operator = kwargs.get('operator', 'AND')
        self._queries = PersistentMapping()
        self._order = PersistentList()

    def validate(self, schema):
        for q in self._queries.values():
            if not q.validate(schema):
                raise ValidationError(q.fieldname)

    def build(self, schema):
        self.validate(schema)
        return filter_query(self, schema)

    def add(self, query=None, **kwargs):
        if query is None:
            ## attempt to make query from kwargs given either
            ## field/comparator/value or fieldname/comparator/value
            field = kwargs.get('field', None)
            fieldname = kwargs.get('fieldname', None)
            if not (field or fieldname):
                raise ValueError('Field missing for query construction')
            if fieldname is None and field:
                fieldname = field.__name__
            comparator = kwargs.get('comparator', None)
            value = kwargs.get('value', None)
            if not (value and comparator):
                raise ValueError('Missing value or comparator')
            query = FieldQuery(fieldname, comparator, value)
        fieldname = query.fieldname
        self._queries[fieldname] = query
        self._order.append(fieldname)

    def remove(self, query):
        if IFieldQuery.providedBy(query):
            query = query.fieldname
        if query not in self._queries:
            raise KeyError('Query not found (fieldname: %s)' % query)
        del (self._queries[query])
        self._order.remove(query)

    ## RO mapping interface
    def get(self, name, default=None):
        return self._queries.get(name, default)

    def __len__(self):
        return len(self._order)

    def __getitem__(self, name):
        v = self.get(name, None)
        if v is None:
            raise KeyError(name)  # fieldname not found
        return v

    def __contains__(self, name):
        if IFieldQuery.providedBy(name):
            name = name.field.__name__
        return name in self._order

    def keys(self):
        return list(self._order)

    def iterkeys(self):
        return self._order.__iter__()

    def itervalues(self):
        return itertools.imap(lambda k: self.get(k), self.iterkeys())

    def iteritems(self):
        return itertools.imap(lambda k: (k, self.get(k)), self.iterkeys())

    def values(self):
        return list(self.itervalues())

    def items(self):
        return list(self.iteritems())
class BaseQuestion(BaseContent):
    """Base class for survey questions"""
    immediate_view = "base_edit"
    global_allow = 0
    filter_content_types = 1
    allowed_content_types = ()
    include_default_actions = 1
    _at_rename_after_creation = True

    def __init__(self, oid, **kwargs):
        self.reset()
        BaseContent.__init__(self, oid, **kwargs)

    security = ClassSecurityInfo()

    security.declareProtected(CMFCorePermissions.View, 'getAbstract')
    def getAbstract(self, **kw):
        return self.Description()

    security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'setAbstract')
    def setAbstract(self, val, **kw):
        self.setDescription(val)

    security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'reset')
    def reset(self):
        """Remove answers for all users."""
        if USE_BTREE:
            self.answers = OOBTree()
        else:
            self.answers = PersistentMapping()

    security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'resetForUser')
    def resetForUser(self, userid):
        """Remove answer for a single user"""
        if self.answers.has_key(userid):
            del self.answers[userid]

    security.declareProtected(CMFCorePermissions.View, 'addAnswer')
    def addAnswer(self, value, comments=""):
        """Add an answer and optional comments for a user.
        This method protects _addAnswer from anonymous users specifying a
        userid when they vote, and thus apparently voting as another user
        of their choice.
        """
        # Get hold of the parent survey
        survey = None
        ob = self
        while survey is None:
            ob = ob.aq_parent
            if ob.meta_type == 'Survey':
                survey = ob
            elif getattr(ob, '_isPortalRoot', False):
                raise Exception("Could not find a parent Survey.")
        portal_membership = getToolByName(self, 'portal_membership')
        if portal_membership.isAnonymousUser() and not survey.getAllowAnonymous():
            raise Unauthorized, ("This survey is not available to anonymous users.")
        # Use the survey to get hold of the appropriate userid
        userid = survey.getSurveyId()
        # Call the real method for storing the answer for this user.
        return self._addAnswer(userid, value, comments)

    def _addAnswer(self, userid, value, comments=""):
        """Add an answer and optional comments for a user."""
        # We don't let users over-write answers that they've already made.
        # Their first answer must be explicitly 'reset' before another
        # answer can be supplied.
        # XXX this causes problem when survey fails validation
        # will also cause problem with save function
##        if self.answers.has_key(userid):
##            # XXX Should this get raised?  If so, a more appropriate
##            # exception is probably in order.
##            msg = "User '%s' has already answered this question. Reset the original response to supply a new answer."
##            raise Exception(msg % userid)
##        else:
        self.answers[userid] = PersistentMapping(value=value,
                                                 comments=comments)
        if not isinstance(self.answers, (PersistentMapping, OOBTree)):
            # It must be a standard dictionary from an old install, so
            # we need to inform the ZODB about the change manually.
            self.answers._p_changed = 1

    security.declareProtected(CMFCorePermissions.View, 'getAnswerFor')
    def getAnswerFor(self, userid):
        """Get a specific user's answer"""
        answer = self.answers.get(userid, {}).get('value', None)
        if self.getInputType() in ['multipleSelect', 'checkbox']:
            if type(answer) == 'NoneType':
                return []
        return answer

    security.declareProtected(CMFCorePermissions.View, 'getCommentsFor')
    def getCommentsFor(self, userid):
        """Get a specific user's comments"""
        return self.answers.get(userid, {}).get('comments', None)

    security.declareProtected(CMFCorePermissions.View, 'getComments')
    def getComments(self):
        """Return a userid, comments mapping"""
        mlist = []
        for k, v in self.answers.items():
            mapping = {}
            mapping['userid'] = k
            mapping['comments'] = v.get('comments', '')
            mlist.append(mapping)
        return mlist
class MollieIdealPayment(object):
    implements(IMollieIdealPayment)
    adapts(IAttributeAnnotatable)

    def __init__(self, context):
        self.ideal_wrapper = getUtility(IMollieIdeal)
        annotations = IAnnotations(context)
        self._metadata = annotations.get(IDEAL_PAYMENT_ANNOTATION_KEY, None)
        if self._metadata is None:
            self._metadata = PersistentMapping()
            annotations[IDEAL_PAYMENT_ANNOTATION_KEY] = self._metadata

    # Properties

    def _getter(self, key):
        return self._metadata.get(key)

    def _setter(self, key, value):
        self._metadata[key] = value

    _partner_id = property(lambda self: self._getter('partner_id'),
        lambda self, value: self._setter('partner_id', value))

    _profile_key = property(lambda self: self._getter('profile_key'),
        lambda self, value: self._setter('profile_key', value))

    last_update = property(lambda self: self._getter('last_update'),
        lambda self, value: self._setter('last_update', value))

    transaction_id = property(lambda self: self._getter('transaction_id'),
        lambda self, value: self._setter('transaction_id', value))

    amount = property(lambda self: self._getter('amount'),
        lambda self, value: self._setter('amount', value))

    currency = property(lambda self: self._getter('currency'),
        lambda self, value: self._setter('currency', value))

    paid = property(lambda self: self._getter('paid'),
        lambda self, value: self._setter('paid', value))

    consumer = property(lambda self: self._getter('consumer'),
        lambda self, value: self._setter('consumer', value))

    status = property(lambda self: self._getter('status'),
        lambda self, value: self._setter('status', value))

    last_status = property(lambda self: self._getter('last_status'),
        lambda self, value: self._setter('last_status', value))

    # Methods

    def get_banks(self):
        return self.ideal_wrapper.get_banks()

    def get_payment_url(self, partner_id, bank_id, amount, message,
                        report_url, return_url, profile_key=None):
        transaction_id, url = self.ideal_wrapper.request_payment(
            partner_id, bank_id, amount, message, report_url,
            return_url, profile_key)

        self.transaction_id = transaction_id
        self._partner_id = partner_id
        self._profile_key = profile_key
        self.amount = amount
        self.last_update = DateTime()
        return url

    def get_payment_status(self):
        order_info = self.ideal_wrapper.check_payment(
            self._partner_id, self.transaction_id)
        if order_info['status'] != 'CheckedBefore':
            # Only store the main info the first time.
            self.currency = order_info['currency']
            self.paid = order_info['paid']
            self.consumer = order_info.get('consumer')
            self.status = order_info['status']
        self.last_status = order_info['status']
        self.last_update = DateTime()
        return self.last_status
Exemple #24
0
    def testTheWorld(self):
        # Test constructors
        u = PersistentMapping()
        u0 = PersistentMapping(l0)
        u1 = PersistentMapping(l1)
        u2 = PersistentMapping(l2)

        uu = PersistentMapping(u)
        uu0 = PersistentMapping(u0)
        uu1 = PersistentMapping(u1)
        uu2 = PersistentMapping(u2)

        class OtherMapping:
            def __init__(self, initmapping):
                self.__data = initmapping

            def items(self):
                return self.__data.items()

        v0 = PersistentMapping(OtherMapping(u0))
        vv = PersistentMapping([(0, 0), (1, 1)])

        # Test __repr__
        eq = self.assertEqual

        eq(str(u0), str(l0), "str(u0) == str(l0)")
        eq(repr(u1), repr(l1), "repr(u1) == repr(l1)")
        eq( ` u2 `, ` l2 `, "`u2` == `l2`")

        # Test __cmp__ and __len__

        def mycmp(a, b):
            r = cmp(a, b)
            if r < 0: return -1
            if r > 0: return 1
            return r

        all = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2]
        for a in all:
            for b in all:
                eq(mycmp(a, b), mycmp(len(a), len(b)),
                   "mycmp(a, b) == mycmp(len(a), len(b))")

        # Test __getitem__

        for i in range(len(u2)):
            eq(u2[i], i, "u2[i] == i")

        # Test get

        for i in range(len(u2)):
            eq(u2.get(i), i, "u2.get(i) == i")
            eq(u2.get(i, 5), i, "u2.get(i, 5) == i")

        for i in min(u2) - 1, max(u2) + 1:
            eq(u2.get(i), None, "u2.get(i) == None")
            eq(u2.get(i, 5), 5, "u2.get(i, 5) == 5")

        # Test __setitem__

        uu2[0] = 0
        uu2[1] = 100
        uu2[2] = 200

        # Test __delitem__

        del uu2[1]
        del uu2[0]
        try:
            del uu2[0]
        except KeyError:
            pass
        else:
            raise TestFailed("uu2[0] shouldn't be deletable")

        # Test __contains__
        for i in u2:
            self.failUnless(i in u2, "i in u2")
        for i in min(u2) - 1, max(u2) + 1:
            self.failUnless(i not in u2, "i not in u2")

        # Test update

        l = {"a": "b"}
        u = PersistentMapping(l)
        u.update(u2)
        for i in u:
            self.failUnless(i in l or i in u2, "i in l or i in u2")
        for i in l:
            self.failUnless(i in u, "i in u")
        for i in u2:
            self.failUnless(i in u, "i in u")

        # Test setdefault

        x = u2.setdefault(0, 5)
        eq(x, 0, "u2.setdefault(0, 5) == 0")

        x = u2.setdefault(5, 5)
        eq(x, 5, "u2.setdefault(5, 5) == 5")
        self.failUnless(5 in u2, "5 in u2")

        # Test pop

        x = u2.pop(1)
        eq(x, 1, "u2.pop(1) == 1")
        self.failUnless(1 not in u2, "1 not in u2")

        try:
            u2.pop(1)
        except KeyError:
            pass
        else:
            raise TestFailed("1 should not be poppable from u2")

        x = u2.pop(1, 7)
        eq(x, 7, "u2.pop(1, 7) == 7")

        # Test popitem

        items = u2.items()
        key, value = u2.popitem()
        self.failUnless((key, value) in items, "key, value in items")
        self.failUnless(key not in u2, "key not in u2")

        # Test clear

        u2.clear()
        eq(u2, {}, "u2 == {}")
Exemple #25
0
class PSession(base.Session, Persistent):
    """
    Keys which are already used in the data dictionary of each session:
        - menuStatus: it is used for knowing if the conference display menu is closed or opened.
        - accessKeys: contains all the access keys entered by the user in this session
        - modifKeys: contains all the modification keys entered by the user in this session
    """
    def __init__(self, request, id):
        base.Session.__init__(self, request, id)
        self.user = None
        minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        self.datadict = PersistentMapping()
        base.Session.__init__(self, request, id)
        self._lang = minfo.getLang()
        self.datadict["ActiveTimezone"] = "LOCAL"

    @property
    def csrf_token(self):
        try:
            return self._csrf_token
        except AttributeError:
            self._csrf_token = str(uuid.uuid4())
            return self._csrf_token

    def reset_csrf_token(self):
        if hasattr(self, '_csrf_token'):
            del self._csrf_token

    def has_info(self):
        """has_info() -> boolean

        Return true if this session contains any information that must
        be saved.
        """
        # This flag will indicate when to commit a session
        return getattr(self, '_v_modified', False)

    def setUser(self, newUser):
        if newUser:
            self._lang = newUser.getLang()
        self.user = newUser
        self.reset_csrf_token()
        self._v_modified = True

    def getUser(self):
        return self.user

    def getId(self):
        return self.id

    def setVar(self, key, value):
        try:
            self.datadict[key] = value
        except AttributeError:
            self.datadict = PersistentMapping()
            self.datadict[key] = value
        self._v_modified = True

    def getVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        return self.datadict.get(key, None)

    def removeVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        if self.datadict.has_key(key):
            del self.datadict[key]
            self._v_modified = True

    def getLang(self):
        try:
            if self._lang is None:
                raise Exception("no language")
        except:
            try:
                lang = self.user.getLang()
            except:
                lang = "en_GB"
                Logger.get('i18n').debug(
                    'No user language defined. Using %s as default.' % lang)
            self._lang = lang

        return self._lang

    def setLang(self, lang):
        self._lang = lang
        self._v_modified = True

    def _p_resolveConflict(self, oldState, savedState, newState):
        """
        ZODB Conflict resolution
        Basically, all the atributes are taken from the conflicting
        transaction ("this one"), except for the creation time, which
        is max(T1,T2)
        """

        # Language, user and address are overwritten
        savedState['_lang'] = newState.get('_lang', None)
        savedState['user'] = newState.get('user', None)
        savedState['__remote_address'] = newState.get('__remote_address', None)

        # access time will be the latest

        savedState['__creation_time'] = max(
            newState.get('__creation_time', 0),
            savedState.get('__creation_time', 0))

        return oldState
class PSession( base.Session, Persistent ):
    """
    Keys which are already used in the data dictionary of each session: 
        - currentCategoryId: it is used for knowing which is the category that contains the current conference.
        - menuStatus: it is used for knowing if the conference display menu is closed or opened.
        - accessKeys: contains all the access keys entered by the user in this session
        - modifKeys: contains all the modification keys entered by the user in this session
    """
    
    def __init__(self, request, id):
        base.Session.__init__(self, request, id)
        self.user = None
        minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        self.datadict = PersistentMapping() 
        base.Session.__init__(self, request, id) 
        #minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        #self.setVar("ActiveTimezone",minfo.getTimezone())
        self._lang = minfo.getLang()        
        self.setVar("ActiveTimezone","LOCAL")

    def setUser( self, newUser ):
        if newUser:
            self._lang = newUser.getLang()
        self.user = newUser
        #get_transaction().commit()

    def getUser( self ):
        return self.user

    def getId( self ):
        return self.id
        
    def setVar(self, key, value):
        try:
            self.datadict[key] = value
        except AttributeError:
            self.datadict = PersistentMapping()
            self.datadict[key] = value
            
    def getVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        return self.datadict.get(key,None)

    def removeVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        if self.datadict.has_key(key):
            del self.datadict[key]

    def getLang(self):
        try:
            if self._lang is None:
                raise Exception("no language")
        except:
            try:                
                lang=self.user.getLang()                
            except:
                lang="en_US"
                Logger.get('i18n').debug('No user language defined. Using %s as default.' % lang)
            self._lang = lang
            
        return self._lang
         
    def setLang(self, lang):
        self._lang = lang
Exemple #27
0
class DecaObject(Persistent):
    """A generic DECA object"""

    def __init__(self, id=None):
        Persistent.__init__(self)
        if id is not None:
            self.ID = id
        else:
            self.ID = uuid.uuid4()
        self.TemplateName = ""
        self.Attributes = PersistentMapping()
        self.TitleAttr = None
        self.Graphics = None
        self.IsReflection = False
        self.Tag = None

    def copy(self, newID):
        obj = DecaObject(newID)
        obj.TemplateName = self.TemplateName
        obj.TitleAttr = self.TitleAttr
        for k, v in self.Attributes.items():
            obj.Attributes[k] = v
        obj.Graphics = self.Graphics
        obj.IsReflection = self.IsReflection
        obj.Tag = self.Tag
        return obj

    def GetTitle(self):
        """Returns the title of the object. If the Title Attribute defined, it's value will be returned.
		Else if Tag defined, it's value will be returned. Else the object's ID will be returned. """
        if self.TitleAttr is not None:
            return self.Attributes[self.TitleAttr]
        if self.Tag is not None and str(self.Tag) != "":
            return str(self.Tag)
        return str(self.ID)

    def SetAttr(self, name, value, location=LOCAL):
        """Sets the attribute value in desired location.

		**Note:** if the object isn't the reflect, attribute will be stored locally"""
        if location == LOCAL:
            self.Attributes[name] = value
            # todo: set attribute to the base object if we are reflection

    def GetAttr(self, name, default=None, priority=LOCAL):
        """Gets the attribute value. For the reflects the priority may points that the base value must be given.
		If value absent in desired location other locations will be scanned. Finnaly, the default value will be returned"""
        result = default
        if priority == LOCAL:
            result = self.Attributes.get(name, default)
        else:
            # todo: read base object's property if we are reflection
            pass
        return result

    def GetShape(self):
        # todo: get shape description. Is it necessary?
        return

    def GetPropList(self, holder=None):
        self.propsGrid = holder
        props = OrderedDict(
            [
                ("Identity", self.ID),
                ("Template", self.TemplateName),
                ("Title Attribute", self.TitleAttr),
                ("Is Reflection", self.IsReflection),
            ]
        )
        attrs = OrderedDict([])
        for k, v in self.Attributes.items():
            attrs.update([(k, v)])
        result = OrderedDict([(_("Object's properties"), props), (_("Local object's attributes"), attrs)])
        return result

    def ReflectTo(self, dstLayer):
        if not isinstance(dstLayer, Deca.Layer):
            dstLayer = Deca.world.GetLayer(str(dstLayer))
        if not self.ID in dstLayer.storage.objects.keys():
            nwo = self.copy(self.ID)
            nwo.IsReflection = True
            dstLayer.AddObject(nwo)
            return nwo
        return None

    def GetEngines(self):
        def genList(base, dirlist):
            res = []
            for ent in dirlist:
                if os.path.isdir(os.path.join(base, ent)):
                    # process subdir
                    nb = os.path.join(base, ent)
                    res.append((ent, genList(nb, os.listdir(nb))))
                else:
                    ft = os.path.splitext(ent)
                    if ft[1].lower() == ".py" and not ft[0].startswith("_"):
                        res.append(ft[0])
            return res
            # main function

        pl = []
        if self.TemplateName and self.TemplateName != "":
            epath = os.path.join(Deca.world.EnginesPath, self.TemplateName)
            if os.path.isdir(epath):
                pl = genList(epath, os.listdir(epath))
                # if Object's engines
                # if template given
        return pl

    def ExecuteEngine(self, name, layer, shape=None, dict=None):
        item = os.path.join(Deca.world.EnginesPath, self.TemplateName, name + ".py")
        fl = None
        if not dict:
            dict = globals()
        try:
            fl = open(item, "r")
            dict["ActiveDecaLayer"] = layer
            dict["ActiveShape"] = shape
            dict["ActiveObject"] = self.ID
            exec fl in dict
        finally:
            if fl:
                fl.close()
Exemple #28
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = 'link'

    def __init__(self,
                 firstname = '',
                 lastname = '',
                 email = '',
                 phone = '',
                 extension = '',
                 department = '',
                 position = '',
                 organization = '',
                 location = '',
                 country = '',
                 website = '',
                 languages = '',
                 office='',
                 room_no='',
                 biography='',
                 data=None,
                 home_path=None,
                ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.location = location
        self.country = country
        self.website = website
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = PersistentList()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        return unicode(
            '%s %s' % (self.firstname.strip(), self.lastname.strip())
            )

    def get_photo(self):
        for ext in image_extensions.values():
            name = "photo." + ext
            if name in self:
                return self[name]
        return None

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name,
                                     IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (
            IProfile.ALERT_IMMEDIATELY,
            IProfile.ALERT_DIGEST,
            IProfile.ALERT_NEVER):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference
class PersistentOrderedDict(persistent.Persistent):
    '''
    This class implements the same interface as the `collections.OrderedDict`
    class from the standard library, but uses `persistent` data types for ZODB
    support.
    '''
    def __init__(self, items=None):
        self.key_index = PersistentList()
        self.data = PersistentMapping()
        if items:
            for k, v in items:
                self[k] = v

    def keys(self):
        return self.key_index[:]

    def __setitem__(self, k, v):
        if k not in self.data:
            self.key_index.append(k)
        self.data[k] = v

    def items(self):
        return [(k, v) for k, v in self.iteritems()]

    def iteritems(self):
        for k in self.key_index:
            yield k, self.data[k]

    def values(self):
        return [v for k, v in self.iteritems()]

    def get(self, key):
        return self.data.get(key)

    def __delitem__(self, key):
        del self.data[key]
        i = self.key_index.index(key)
        del self.key_index[i]

    def __getitem__(self, key):
        return self.data[key]

    def setdefault(self, key, default_value):
        if key not in self:
            self[key] = default_value
        return self[key]

    def move_to_end(self, key, last=True):
        assert(key in self)
        items = []
        for k, v in self.items():
            if k != key:
                items.append((k, v))
                del self[k]
        if last:
            items.append((key, self[key]))
            del self[key]
        for k, v in items:
            self[k] = v

    def __contains__(self, key):
        return key in self.data
class Box(Persistent):
    implements(IDepositBox)

    def __init__(self, max_age=config.MAX_AGE, purge_days=config.PURGE_DAYS):
        self.data = PersistentMapping()
        self._last_purge = int(time.time())
        self.max_age = max_age
        self.purge_days = purge_days

    def _generate_new_id(self):
        """Generate new id.
        """
        new_id = id_generator()
        while new_id in self.data.keys():
            new_id = id_generator()
        return new_id

    def put(self, value, token=None):
        """Put value in box, with optional token, and return generated id.

        Calling this method also does a purge once a day (well, when
        the last purge was at least 24 hours ago).  The frequency can
        be controlled with the purge_days attribute.
        """
        cutoff = int(time.time()) - (self.purge_days * 86400)
        if self._last_purge < cutoff:
            self.purge()

        if value is None:
            raise ValueError
        id = self._generate_new_id()
        self.data[id] = BoxItem(token, value, confirmed=False)
        return id

    def edit(self, secret, value, token=None):
        """Edit value in the box, when secret and optional token match.
        """
        if value is None:
            raise ValueError
        stored = self.get(secret, token=token)
        if value == stored:
            # No change
            return
        self.data[secret] = BoxItem(token, value, confirmed=True)

    def get(self, secret, token=None):
        stored = self.data.get(secret)
        if stored is None:
            return None
        if stored.token != token:
            # raise Exception
            return None
        if not stored.confirmed:
            # Purge this item when it is expired:
            cutoff = int(time.time()) - self.max_age * 86400
            if stored.timestamp < cutoff:
                del self.data[secret]
                return None
            if token:
                # When there is a token, the item must be confirmed
                # before we return the value.  Main use case: email
                # confirmation.
                return None
        return stored.value

    def confirm(self, secret, token=None):
        """Confirm the item/token and return whether this succeeded or not.
        """
        stored = self.data.get(secret)
        if stored is None:
            return None
        if stored.token != token:
            # raise Exception?
            return None
        if not stored.confirmed:
            # First check if the confirmation comes too late.
            cutoff = int(time.time()) - self.max_age * 86400
            if stored.timestamp < cutoff:
                del self.data[secret]
                # Report back that we have failed, in case anyone
                # wants to know.
                return False
        stored.confirmed = True
        return True

    def pop(self, secret, token=None):
        stored = self.get(secret, token=token)
        if stored is None:
            return None
        self.data.pop(secret)
        return stored

    def get_all_confirmed(self):
        for key, stored in self.data.items():
            if stored.confirmed:
                yield stored.value

    def purge(self):
        """Purge items that have expired.

        Confirmed items are not purged.
        """
        cutoff = int(time.time()) - self.max_age * 86400
        logger.info("Started purging data.")
        for key, stored in self.data.items():
            if not stored.confirmed and stored.timestamp < cutoff:
                logger.info("Purged data with secret %r", key)
                del self.data[key]
        self._last_purge = int(time.time())
        logger.info("Finished purging data.")
Exemple #31
0
class Parcel(Persistent):
    def __init__(self, warehouse, name):
        self._warehouse = warehouse
        self.name = name
        self.metadata = PersistentMapping()
        self.history = PersistentList()

    @property
    def uploading(self):
        return "upload_time" not in self.metadata

    @property
    def file_uploading(self):
        from parcel import _get_stages_for_parcel

        DELIVERY_STAGES, _ = _get_stages_for_parcel(self)
        stage = DELIVERY_STAGES.get(self.metadata.get("stage"), {})
        return stage.get("file_uploading", False)

    def save_metadata(self, new_metadata):
        self._warehouse.logger.info("Metadata update for %r: %r (user %s)", self.name, new_metadata, _current_user())
        for key, value in new_metadata.iteritems():
            if key == "prev_parcel_list":
                self.metadata[_ensure_unicode(key)] = [_ensure_unicode(v) for v in value]
            else:
                self.metadata[_ensure_unicode(key)] = _ensure_unicode(value)

    def get_path(self):
        return self._warehouse.parcels_path / self.name

    def get_files(self):
        for f in self.get_path().listdir():
            if not f.name.startswith(".") and not f.isdir():
                yield f

    def finalize(self):
        self._warehouse.logger.info("Finalizing %r (user %s)", self.name, _current_user())
        self.checksum = checksum(self.get_path())
        self.save_metadata({"upload_time": datetime.utcnow().isoformat()})

    def link_in_tree(self):
        symlink_path = self._warehouse.tree_path
        if self.metadata["delivery_type"] == COUNTRY:
            filtered_metadata = tuple(set(METADATA) ^ set(COUNTRY_EXCLUDE_METADATA))
        elif self.metadata["delivery_type"] == STREAM:
            filtered_metadata = tuple(set(METADATA) ^ set(STREAM_EXCLUDE_METADATA))
        else:
            filtered_metadata = METADATA

        for name in filtered_metadata:
            if name in self.metadata:
                symlink_path = symlink_path / self.metadata[name]
        symlink_path.makedirs_p()
        target_path = self.get_path()
        for c in xrange(1, 101):
            symlink_path_c = symlink_path / str(c)
            if not symlink_path_c.islink():
                target_path.symlink(symlink_path_c)
                return symlink_path_c
            else:
                if symlink_path_c.readlink() == target_path:
                    return
        else:
            raise RuntimeError("Unable to create symlink, tried 100 numbers")

    def add_history_item(self, title, time, actor, description_html):
        item = ParcelHistoryItem(self, title, time, actor, description_html)
        item.id_ = len(self.history) + 1
        self.history.append(item)
        return item

    @property
    def last_modified(self):
        return self.history[-1].time
Exemple #32
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = 'link'
    fax = ''  # BBB
    _websites = ()
    last_login_time = None  # BBB
    last_chatter_query = None  # BBB
    date_format = None  # BBB

    def _get_website(self):
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return old_ws
        return self._websites and self._websites[0] or ''

    website = property(_get_website, )

    def _get_websites(self):
        self._p_activate()
        if '_websites' in self.__dict__:
            return self._websites
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return (old_ws, )
        return ()

    def _set_websites(self, value):
        self._websites = value  # coerce / normalize?
        if 'website' in self.__dict__:
            del self.__dict__['website']

    websites = property(_get_websites, _set_websites)

    def __init__(
        self,
        firstname='',
        lastname='',
        email='',
        phone='',
        extension='',
        fax='',
        department='',
        position='',
        organization='',
        location='',
        country='',
        websites=None,
        languages='',
        office='',
        room_no='',
        biography='',
        date_format=None,
        data=None,
        home_path=None,
        preferred_communities=None,
    ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.fax = fax
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.location = location
        if country not in countries.as_dict:
            country = 'XX'
        self.country = country
        if websites is not None:
            self.websites = websites
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        if date_format not in cultures.as_dict:
            date_format = None
        self.date_format = date_format
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = PersistentList()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None
        self.preferred_communities = preferred_communities
        self.last_login_time = None
        self.last_chatter_query = None

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        title = [self.firstname.strip(), self.lastname.strip()]
        if getattr(self, 'security_state', None) == 'inactive':
            title += [
                '(Inactive)',
            ]
        return unicode(' '.join(title))

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name,
                                     IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (IProfile.ALERT_IMMEDIATELY,
                              IProfile.ALERT_DIGEST, IProfile.ALERT_NEVER):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference
Exemple #33
0
class NotificationTool(UniqueObject, SimpleItem, PropertyManager):
    """Main notification tool."""
    id = ID
    title = TITLE
    meta_type = META_TYPE

    manage_options = (PropertyManager.manage_options +
                      SimpleItem.manage_options)

    ## Extra subscriptions
    extra_subscriptions_enabled = False
    extra_subscriptions_recursive = True

    ## Debug mode
    debug_mode = False

    ## Ignore rules
    ignore_rules = DEFAULT_IGNORE_RULES

    ## Item creation
    item_creation_notification_enabled = False
    on_item_creation_users = []
    on_item_creation_mail_template = []
    ## Item modification
    item_modification_notification_enabled = False
    on_item_modification_users = []
    on_item_modification_mail_template = []
    ## Workflow transition
    wf_transition_notification_enabled = False
    on_wf_transition_users = []
    on_wf_transition_mail_template = []
    ## Member registration
    member_registration_notification_enabled = False
    on_member_registration_users = []
    on_member_registration_mail_template = []
    ## Member modification
    member_modification_notification_enabled = False
    on_member_modification_users = []
    on_member_modification_mail_template = []
    ## Discussion item creation
    discussion_item_creation_notification_enabled = False
    on_discussion_item_creation_users = []
    on_discussion_item_creation_mail_template = []

    _properties = (
        {
            'id': 'extra_subscriptions_enabled',
            'label': 'Enable extra subscriptions',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'extra_subscriptions_recursive',
            'label': 'Toggle recursive mode for extra subscriptions',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'debug_mode',
            'label': 'Toggle debug mode',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'ignore_rules',
            'label': 'Rules (ignore)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'item_creation_notification_enabled',
            'label': 'Enable item creation notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_item_creation_users',
            'label': 'Rules on item creation (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_item_creation_mail_template',
            'label': 'Rules on item creation (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'item_modification_notification_enabled',
            'label': 'Enable item modification notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_item_modification_users',
            'label': 'Rules on item modification (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_item_modification_mail_template',
            'label': 'Rules on item modification (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'wf_transition_notification_enabled',
            'label': 'Enable workflow transition notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_wf_transition_users',
            'label': 'Rules on workflow transition (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_wf_transition_mail_template',
            'label': 'Rules on workflow transition (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'member_registration_notification_enabled',
            'label': 'Enable member registration notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_member_registration_users',
            'label': 'Rules on member registration (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_member_registration_mail_template',
            'label': 'Rules on member registration (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'member_modification_notification_enabled',
            'label': 'Enable member modification notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_member_modification_users',
            'label': 'Rules on member modification (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_member_modification_mail_template',
            'label': 'Rules on member modification (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'discussion_item_creation_notification_enabled',
            'label': 'Enable discussion item creation notification',
            'mode': 'w',
            'type': 'boolean'
        },
        {
            'id': 'on_discussion_item_creation_users',
            'label': 'Rules on discussion item creation (users)',
            'mode': 'w',
            'type': 'lines'
        },
        {
            'id': 'on_discussion_item_creation_mail_template',
            'label': 'Rules on discussion item creation (mail template)',
            'mode': 'w',
            'type': 'lines'
        },
    )

    security = ClassSecurityInfo()
    decPrivate = security.declarePrivate
    decProtected = security.declareProtected
    decPublic = security.declarePublic

    def __init__(self, *args, **kwargs):
        self._uid_to_path = PersistentMapping()
        self._subscriptions = PersistentMapping()

    #################################################################
    ## Notification handlers
    ########################
    decPrivate('onItemCreation')

    def onItemCreation(self, obj):
        """Handler called when an item is created.

        It returns the number of mails which have been sent.

        **Warning:** this handler is not called when a discussion item
        is added. In this case, ``onDiscussionItemCreation()`` is
        called instead.
        """
        if not self.getProperty('item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        return self._handlerHelper(obj, 'item_creation', extra_bindings,
                                   extra_bindings, extra_bindings)

    decPrivate('onItemModification')

    def onItemModification(self, obj):
        """Handler called when an item is modified.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('item_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({
            'current': obj,
            'previous': getPreviousVersion(obj)
        })
        return self._handlerHelper(obj, 'item_modification', extra_bindings,
                                   extra_bindings, extra_bindings)

    decPrivate('onWorkflowTransition')

    def onWorkflowTransition(self, obj, action):
        """Handler called when a workflow transition is triggered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('wf_transition_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        wtool = getToolByName(self, 'portal_workflow')
        comments = wtool.getInfoFor(obj, 'comments')
        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({
            'transition': action,
            'comments': comments,
            'previous_state': getPreviousWorkflowState(obj)
        })

        current_state_display = extra_bindings['current_state']
        previous_state_display = extra_bindings['previous_state']

        try:
            wf_def = wtool.getWorkflowsFor(obj)
            if len(wf_def) > 0:
                curr_wf = wf_def[0]
                wf_states = curr_wf.states
                current_state_display = wf_states[
                    extra_bindings['current_state']].title
                if extra_bindings['previous_state'] <> None:
                    previous_state_display = wf_states[
                        extra_bindings['previous_state']].title
                else:
                    previous_state_display = ""
        except AttributeError:
            pass

        extra_bindings.update({
            'current_state_title': current_state_display,
            'previous_state_title': previous_state_display,
        })

        return self._handlerHelper(obj, 'wf_transition', extra_bindings,
                                   extra_bindings, extra_bindings)

    decPrivate('onMemberRegistration')

    def onMemberRegistration(self, member, properties):
        """Handler called when a new portal member has been
        registered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('member_registration_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        if properties is None:
            properties = {}  ## FIXME: How could it be? (Damien)

        current_user = getSecurityManager().getUser()
        extra_bindings = getBasicBindings(member)
        extra_bindings.update({
            'current_user': current_user,
            'member': member,
            'properties': properties,
            'event': 'registration'
        })
        return self._handlerHelper(member, 'member_registration',
                                   extra_bindings, extra_bindings,
                                   extra_bindings)

    decPrivate('onMemberModification')

    def onMemberModification(self, member):
        """Handler called when a member changes his/her properties.

        It returns the number of mails which have been sent.
        """
        ## FIXME: this should go away when we rely on the appropriate
        ## event.
        ## This method can also be called when the member is
        ## registered. We have to check that.
        stack = inspect.stack()
        ## 1st item is ourself
        ## 2nd item is 'CMFCore.MemberDataTool.notifyMemberModified()'
        ## 3rd item is 'CMFCore.MemberDataTool.setMemberProperties()'
        ## 4th item is what we want to check: it is either 'addMember'
        ## or 'setProperties()'
        caller = stack[3][3]
        if caller != 'setProperties':
            return 0

        if not self.getProperty('member_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        ## FIXME: what is the purpose of the following lines? (Damien)
        memberdata = getToolByName(self, 'portal_memberdata')
        properties = {}
        for key, value in memberdata.propertyItems():
            properties[key] = value

        current_user = getSecurityManager().getUser()
        extra_bindings = getBasicBindings(None)
        extra_bindings.update({
            'current_user': current_user,
            'member': member,
            'properties': properties,
            'event': 'modification'
        })
        return self._handlerHelper(member, 'member_modification',
                                   extra_bindings, extra_bindings,
                                   extra_bindings)

    decPrivate('onDiscussionItemCreation')

    def onDiscussionItemCreation(self, discussion_item):
        """Handler called when a discussion item is created.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty(
                'discussion_item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(discussion_item):
            return 0

        ## We add two bindings to disambiguate the meaning of 'here'
        ## in the mail template and the rules: 'discussion_item' and
        ## 'discussed_item'.
        discussed_item = discussion_item
        while discussed_item.meta_type == discussion_item.meta_type:
            discussed_item = discussed_item.aq_inner.aq_parent.aq_parent
        extra_bindings = getBasicBindings(discussed_item)
        extra_bindings.update({
            'discussion_item': discussion_item,
            'discussed_item': discussed_item
        })
        return self._handlerHelper(discussion_item, 'discussion_item_creation',
                                   extra_bindings, extra_bindings,
                                   extra_bindings)

    def _handlerHelper(self, obj, what, get_users_extra_bindings,
                       mail_template_extra_bindings, mail_template_options):
        """An helper method for ``on*()`` handlers.

        It returns the number of mails which have been sent.
        """
        #import pdb;pdb.set_trace()
        self._updateSubscriptionMapping(obj)
        users_by_label = self.getUsersToNotify(obj, what,
                                               get_users_extra_bindings)
        if self.isExtraSubscriptionsEnabled():
            users = users_by_label.get('', [])
            users.extend(self.getExtraSubscribersOf(self._getPath(obj)))
            users_by_label[''] = users

        n_sent = 0
        for label, users in users_by_label.items():
            users = self.removeUnAuthorizedSubscribers(users, obj)

            #remove user who has initiated the action.
            author = mail_template_options['author']
            users = removeActionInitiatorFromUsers(users, author)

            addresses = self.getEmailAddresses(users)

            if not addresses:
                LOG.warning("No addresses for label '%s' for '%s' "\
                            "notification of '%s'",
                            label, what, obj.absolute_url(1))
                continue
            mail_template_extra_bindings['label'] = label
            template = self.getMailTemplate(obj, what,
                                            mail_template_extra_bindings)
            if template is None:
                LOG.warning("No mail template for label '%s' for "\
                            "'%s' notification of '%s'",
                            label, what, obj.absolute_url(1))
                continue

            try:
                message = template(**mail_template_options)
            except ConflictError:
                raise
            except:
                LOG.error("Cannot evaluate mail template '%s' on '%s' "\
                          "for '%s' for label '%s'",
                          template.absolute_url(1),
                          obj.absolute_url(1), what, label,
                          exc_info=True)
                continue
            n_sent += self.sendNotification(addresses, message)

        return n_sent

    #################################################################

    #################################################################
    ## Utility methods
    ###############################
    decPrivate('ignoreNotification')

    def ignoreNotification(self, obj):
        """Return whether notification have been set to be ignored for
        ``obj``.
        """
        ec = getExpressionContext(obj)
        users = []
        for match_expr in self.getProperty('ignore_rules', ()):
            try:
                if self._match(match_expr, ec):
                    return True
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'ignore_rules' rule "\
                          "('%s') for '%s'",
                          match_expr, obj.absolute_url(1),
                          exc_info=True)
        return False

    decPrivate('getUsersToNotify')

    def getUsersToNotify(self, obj, what, ec_bindings=None):
        """Return a mapping of list of users to notify by label for
        the ``what`` of ``obj``, ``what`` being one of the
        implemented notification (*item_modification*,
        *wf_transition*, etc.).

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_users' % what, None)
        if rules is None:
            raise NotImplementedError, \
                "Notification on '%s' is not implemented." % what

        ec = getExpressionContext(obj, ec_bindings)
        users_by_label = {}
        ignore_next_rules = False
        for rule in rules:
            try:
                match_expr, users_expr = rule.split(RULE_DELIMITER, 1)
                if RULE_DELIMITER in users_expr:
                    users_expr, label = users_expr.split(RULE_DELIMITER)
                else:
                    label = ''
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_users' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            users_expr = users_expr.strip()
            label = label.strip()
            users = users_by_label.get(label, [])
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_users' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if users_expr == '*':
                users.extend(self.getAllUsers())
                ignore_next_rules = True
            else:
                try:
                    users.extend(Expression(users_expr)(ec))
                except ConflictError:
                    raise
                except:
                    LOG.error("Error in 'on_%s_users' rule "\
                              "('%s') for '%s'",
                              what, users_expr, obj.absolute_url(1),
                              exc_info=True)
            users_by_label[label] = users
            if ignore_next_rules:
                break
        return users_by_label

    decPrivate('getMailTemplate')

    def getMailTemplate(self, obj, what, ec_bindings=None):
        """Return the template to notify for the ``what`` of an object
        ``obj``, ``what`` being one of the implemented notification
        ("*item_modification*", "*wf_transition*", etc.), or ``None``
        if none could be found.

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_mail_template' % what, None)
        if rules is None:
            raise NotImplementedError, \
                'Notification on "%s" is not implemented.'

        ec = getExpressionContext(obj, ec_bindings)
        template = None
        for rule in rules:
            try:
                match_expr, template_expr = rule.split(RULE_DELIMITER)
                match_expr, template_expr = match_expr.strip(
                ), template_expr.strip()
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_mail_template' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            template_expr = template_expr.strip()
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            try:
                template = Expression(template_expr)(ec)
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, template_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if type(template) == StringType:
                template = obj.restrictedTraverse(template, None)
            if template is not None:
                break
        return template

    decPrivate('getAllUsers')

    def getAllUsers(self):
        """Return a list of all user ids of the portal.

        **Warning:** this method may be costly if you rely on an
        external (non ZODB) user source. Use it at your own risk.
        """
        mtool = getToolByName(self, 'portal_membership')
        return mtool.listMemberIds()

    decPrivate('removeUnAuthorizedSubscribers')

    def removeUnAuthorizedSubscribers(self, subscribers, obj):
        """Return users from ``subscribers`` who are authorized to
        view ``obj``.
        """
        portal = getToolByName(self, 'portal_url').getPortalObject()
        mtool = getToolByName(self, 'portal_membership')
        filtered_subscribers = []
        for subscriber in subscribers:
            if self._anonymousShouldBeNotified(obj):
                filtered_subscribers.append(subscriber)
            else:
                ## We use '_huntUser()' and not 'mtool.getMemberById()'
                ## because the latter would provide a wrapped user
                ## object, with a specific context where the user is
                ## not allowed to view 'obj'.
                member = mtool._huntUser(str(subscriber), portal)
                if member is not None:
                    if member.has_permission('View', obj):
                        filtered_subscribers.append(subscriber)

        return filtered_subscribers

    decPrivate('getEmailAddresses')

    def getEmailAddresses(self, users):
        """Return email addresses of ``users``.

        For each value in ``users``:

        - if the value is not an e-mail, suppose it is an user id and
        try to get the ``email`` property of this user;

        - remove duplicates;

        - remove bogus e-mail addresses.
        """
        mtool = getToolByName(self, 'portal_membership')
        addresses = {}
        for user in users:
            member = mtool.getMemberById(str(user))
            if member is not None:
                user = member.getProperty('email', '')
            if user is None:
                continue
            if EMAIL_REGEXP.match(user):
                addresses[user] = 1
        return addresses.keys()

    decPrivate('sendNotification')

    def sendNotification(self, addresses, message):
        """Send ``message`` to all ``addresses``."""
        mailhosts = self.superValues(MAIL_HOST_META_TYPES)
        if not mailhosts:
            raise MailHostNotFound

        from Products.MaildropHost import MaildropHost
        bfound = False
        for mh in mailhosts:
            if isinstance(mh, MaildropHost):
                mailhost = mh
                bfound = True
            if bfound == True:
                break

        if bfound == False:
            mailhost = mailhosts[0]

        ptool = getToolByName(self, 'portal_properties').site_properties
        encoding = ptool.getProperty('default_charset', 'utf-8')
        message = encodeMailHeaders(message, encoding)

        if self.getProperty('debug_mode'):
            LOG.info('About to send this message to %s: \n%s', addresses,
                     message)

        n_messages_sent = 0
        for address in addresses:
            this_message = ('To: %s\n' % address) + message
            this_message = this_message.encode(encoding)
            try:
                mailhost.send(this_message)
                n_messages_sent += 1
            except ConflictError:
                raise
            except:
                LOG.error('Error while sending '\
                          'notification: \n%s' % this_message,
                          exc_info=True)
        return n_messages_sent

    def _match(self, expr, ec):
        """Return ``True`` if ``expr`` returns something which can be
        evaluated to ``True`` in the expression context (``ec``) or if
        ``expr`` is "*".
        """
        if expr == '*':
            return True
        expr = Expression(expr)
        return bool(expr(ec))

    def _getPath(self, obj):
        """Return path of ``obj``.

        A slash (``/``) is appended to the path if the object is
        folderish. The returned path is relative to the portal object.
        """
        utool = getToolByName(self, 'portal_url')
        path = utool.getRelativeContentURL(obj)
        path = '/' + path
        if not getattr(obj.aq_base, 'isPrincipiaFolderish', False):
            return path
        if path[-1] != '/':
            path += '/'
        return path

    def _getParents(self, path):
        """Get the parents of the item corresponding to ``path`` and
        return their respective path.

        Parents are returned from ``path`` to the portal root object.
        """
        if path == '/':
            return []
        if path[-1] == '/':
            path = path[:-1]
        parent = path[:path.rfind('/') + 1]
        parents = [parent]
        parents.extend(self._getParents(parent))
        return tuple(parents)

    def _getUID(self, obj):
        """Return UID of the object."""
        portal_uidhandler = getToolByName(self, 'portal_uidhandler')
        uid = portal_uidhandler.queryUid(obj, None)
        if uid is None:  ## Not yet registered
            uid = portal_uidhandler.register(obj)
        return uid

    def _anonymousShouldBeNotified(self, obj):
        """Return whether anonymous users should be notified, i.e.
        whether anonymous users can view ``obj``.
        """
        return 'Anonymous' in rolesForPermissionOn('View', obj)

    #################################################################

    #################################################################
    ## Extra subscriptions settings
    ###############################
    decProtected('View', 'isExtraSubscriptionsEnabled')

    def isExtraSubscriptionsEnabled(self):
        """Return whether extra subscriptions are enabled."""
        return self.getProperty('extra_subscriptions_enabled')

    decProtected('View', 'isExtraSubscriptionsRecursive')

    def isExtraSubscriptionsRecursive(self):
        """Return whether extra subscriptions are recursive.

        Note that this method does not check whether extra
        subscriptions are enabled or not.
        """
        return self.getProperty('extra_subscriptions_recursive')

    #################################################################

    #################################################################
    ## Extra subscriptions logic
    ############################
    def _updateSubscriptionMapping(self, obj):
        """Update subscription mapping."""
        uid = self._getUID(obj)
        path = self._getPath(obj)
        known_path = self._uid_to_path.get(uid)
        if known_path != path:
            self._uid_to_path[uid] = path
            if known_path is not None:
                ## We have old informations for this object
                for key, value in self._subscriptions.items():
                    if key.startswith(known_path):
                        new_key = path + key[len(known_path):]
                        self._subscriptions[new_key] = value
                        del self._subscriptions[key]

    decPublic('currentUserHasSubscribePermission')

    def currentUserHasSubscribePermissionOn(self, obj):
        """Return whether the current user is allowed to subscribe to
        or unsubscribe from ``obj``.
        """
        if not IATContentType.providedBy(obj) and not \
                IPloneSiteRoot.providedBy(obj):
            return False

        mtool = getToolByName(self, 'portal_membership')
        return mtool.checkPermission(SUBSCRIBE_PERMISSION, obj)

    decPublic('subscribeTo')

    def subscribeTo(self, obj, email=None):
        """Subscribe ``email`` (or the current user if ``email`` is
        None) to ``obj``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to subscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            user = getSecurityManager().getUser().getId()
            subscribers[user] = 1
            self._subscriptions[path] = subscribers

    decPublic('unSubscribeFrom')

    def unSubscribeFrom(self, obj, email=None):
        """Unsubscribe ``email`` (or the current user if ``email`` is
        ``None``) from ``obj``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            user = getSecurityManager().getUser().getId()

            try:
                del subscribers[user]
                self._subscriptions[path] = subscribers
            except KeyError:
                pass  ## User was not subscribed.

    decPublic('unSubscribeFromObjectAbove')

    def unSubscribeFromObjectAbove(self, obj, email=None):
        """Find folderish items above ``obj`` and unsubscribe
        ``email`` (or the current user if ``email`` is ``None``) from
        the first one (s)he is subscribed to.

        If ``user`` is subscribed to ``obj``, this method is
        equivalent to ``unSubscribeFrom(obj, user)``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        elif email is not None:
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception.
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)
            utool = getToolByName(obj, 'portal_url')
            portal = utool.getPortalObject()
            portal_container = portal.aq_inner.aq_parent
            while obj != portal_container:
                if self.isSubscribedTo(obj, as_if_not_recursive=True):
                    self.unSubscribeFrom(obj)
                    break
                obj = obj.aq_parent

    decPublic('isSubscribedTo')

    def isSubscribedTo(self, obj, email=None, as_if_not_recursive=False):
        """Return whether ``email`` (or the current user if ``email``
        is ``None``) is subscribed to ``obj``.

        If ``as_if_not_recursive`` is ``True``, this method acts as if
        the recursive mode was off.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is None:
            ## Yes, 'email' is actually the id of the current user.
            email = getSecurityManager().getUser().getId()
        self._updateSubscriptionMapping(obj)
        path = self._getPath(obj)
        subscribers = self.getExtraSubscribersOf(path, as_if_not_recursive)
        return subscribers.has_key(email)

    decPrivate('getExtraSubscribersOf')

    def getExtraSubscribersOf(self, path, as_if_not_recursive=False):
        """Return users or email addresses which are subscribed to
        the given path.

        This method returns a mapping whose keys are the users or
        email addresses. If ``as_if_not_recursive`` is ``True``, this
        method acts as if the recursive mode was off.
        """
        subscribers = self._subscriptions.get(path, {}).copy()
        if path.endswith('/'):
            j = path.rfind('/')
            if j != -1:
                newpath = path[:j]
                subscribers1 = self._subscriptions.get(newpath, {}).copy()
                subscribers.update(subscribers1)
        if self.isExtraSubscriptionsRecursive() and \
                not as_if_not_recursive:
            if path[-1] == '/':
                path = path[:-1]
            i = path.rfind('/')
            if i != -1:
                parent = path[:i + 1]
                subscribers.update(self.getExtraSubscribersOf(parent))
        return subscribers
Exemple #34
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = 'link'
    fax = '' # BBB
    _websites = ()
    last_login_time = None # BBB

    def _get_website(self):
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return old_ws
        return self._websites and self._websites[0] or ''

    website = property(_get_website,)

    def _get_websites(self):
        self._p_activate()
        if '_websites' in self.__dict__:
            return self._websites
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return (old_ws,)
        return ()

    def _set_websites(self, value):
        self._websites = value # coerce / normalize?
        if 'website' in self.__dict__:
            del self.__dict__['website']

    websites = property(_get_websites, _set_websites)

    def __init__(self,
                 firstname = '',
                 lastname = '',
                 email = '',
                 phone = '',
                 extension = '',
                 fax = '',
                 department = '',
                 position = '',
                 organization = '',
                 location = '',
                 country = '',
                 websites = None,
                 languages = '',
                 office='',
                 room_no='',
                 biography='',
                 data=None,
                 home_path=None,
                 preferred_communities = None,
                ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.fax = fax
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.location = location
        if country not in countries.as_dict:
            country = 'XX'
        self.country = country
        if websites is not None:
            self.websites = websites
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = PersistentList()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None
        self.preferred_communities = preferred_communities
        self.last_login_time = None

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        title = [self.firstname.strip(), self.lastname.strip()]
        if getattr(self, 'security_state', None) == 'inactive':
            title += ['(Inactive)',]
        return unicode(' '.join(title))

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name,
                                     IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (
            IProfile.ALERT_IMMEDIATELY,
            IProfile.ALERT_DIGEST,
            IProfile.ALERT_NEVER):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference
Exemple #35
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = 'link'
    fax = ''  # BBB
    two_factor_phone = ''  # BBB
    two_factor_verified = False
    _two_factor_verify_code = ''  # BBB
    _websites = ()
    last_login_time = None  # BBB
    date_format = None  # BBB
    current_auth_code = None
    current_auth_code_time_stamp = datetime.utcnow()
    additional_fields = ('phone', 'extension', 'fax', 'department', 'position',
                         'organization', 'industry', 'location', 'country',
                         'websites', 'languages', 'office', 'room_no',
                         'biography', 'date_format', 'home_path')

    def _get_website(self):
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return old_ws
        return self._websites and self._websites[0] or ''

    website = property(_get_website, )

    def _get_websites(self):
        self._p_activate()
        if '_websites' in self.__dict__:
            return self._websites
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return (old_ws, )
        return ()

    def _set_websites(self, value):
        self._websites = value  # coerce / normalize?
        if 'website' in self.__dict__:
            del self.__dict__['website']

    websites = property(_get_websites, _set_websites)

    def __init__(
        self,
        firstname='',
        lastname='',
        email='',
        phone='',
        extension='',
        fax='',
        department='',
        position='',
        organization='',
        industry='',
        location='',
        country='US',
        websites=None,
        languages='',
        office='',
        room_no='',
        biography='',
        date_format='en-US',
        data=None,
        home_path=None,
        preferred_communities=None,
        two_factor_phone='',
        two_factor_verified=False,
    ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.fax = fax
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.industry = industry
        self.location = location
        if country not in countries.as_dict:
            country = 'XX'
        self.country = country
        if websites is not None:
            self.websites = websites
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        if date_format not in cultures.as_dict:
            date_format = None
        self.date_format = date_format
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = Accumulator()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None
        self.preferred_communities = preferred_communities
        self.last_login_time = None
        self.two_factor_phone = two_factor_phone
        self.two_factor_verified = two_factor_verified

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        title = [self.firstname.strip(), self.lastname.strip()]
        if getattr(self, 'security_state', None) == 'inactive':
            title += ['(Inactive)']
        return unicode(' '.join(title))

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name,
                                     IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (IProfile.ALERT_IMMEDIATELY,
                              IProfile.ALERT_DAILY_DIGEST,
                              IProfile.ALERT_NEVER,
                              IProfile.ALERT_WEEKLY_DIGEST,
                              IProfile.ALERT_BIWEEKLY_DIGEST):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference
Exemple #36
0
class PasswordResetTool(SimpleItem):
    meta_type = 'Eionet Password Reset Tool'
    security = ClassSecurityInfo()
    icon = '++resource++eea.ldapadmin-www/eionet_password_reset_tool.gif'
    session_messages = SESSION_MESSAGES

    manage_options = (
        {'label': 'Configure', 'action': 'manage_edit'},
        {'label': 'View', 'action': ''},
    ) + SimpleItem.manage_options

    _render_template = TemplateRenderer(CommonTemplateLogic)

    def __init__(self, config={}):
        super(PasswordResetTool, self).__init__()
        self._config = PersistentMapping(config)
        self._tokens = PersistentMapping()

    security.declareProtected(view_management_screens, 'get_config')

    def get_config(self):
        return dict(self._config)

    security.declareProtected(view_management_screens, 'manage_edit')
    manage_edit = PageTemplateFile('zpt/pwreset_manage_edit', globals())
    manage_edit.ldap_config_edit_macro = ldap_config.edit_macro

    security.declareProtected(view_management_screens, 'manage_edit_save')

    def manage_edit_save(self, REQUEST):
        """ save changes to configuration """
        form = REQUEST.form
        new_config = ldap_config.read_form(form, edit=True)

        new_config['legacy_ldap_server'] = form.get('legacy_ldap_server', '')
        new_config['legacy_admin_dn'] = form.get('legacy_admin_dn', '')
        new_config['legacy_admin_pw'] = form.get('legacy_admin_pw', '')
        if not new_config['legacy_admin_pw']:
            del new_config['legacy_admin_pw']  # don't overwrite

        self._config.update(new_config)
        REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_edit')

    def _get_ldap_agent(self, bind=True):
        return ldap_config.ldap_agent_with_config(self._config, bind)

    def _predefined_filters(self):
        return sorted(self.objectValues([query.Query.meta_type]),
                      key=lambda ob: ob.getId())

    security.declareProtected(view, 'index_html')

    def index_html(self, REQUEST):
        """ view """
        email = REQUEST.get('email', '')
        options = {'email': email}
        return self._render_template('zpt/pwreset_index.zpt', **options)

    def _new_token(self, user_id):
        token = random_token()
        self._tokens[token] = TokenData(user_id, datetime.utcnow())
        return token

    def _send_token_email(self, addr_to, token, user_info):
        addr_from = "*****@*****.**"
        email_template = load_template('zpt/pwreset_token_email.zpt')
        expiration_time = datetime.utcnow() + timedelta(days=1)
        options = {
            'token_url': self.absolute_url() + "/confirm_email?token=" + token,
            'user_info': user_info,
            'context': self,
            'network_name': NETWORK_NAME,
            'expiration_time': expiration_time.strftime("%Y-%m-%d %H:%M:%S")
        }
        print options['token_url']
        message = MIMEText(email_template(**options).encode('utf-8'),
                           _charset='utf-8')
        message['From'] = addr_from
        message['To'] = addr_to
        message['Subject'] = "%s account password recovery" % NETWORK_NAME

        try:
            mailer = getUtility(IMailDelivery, name="Mail")
            mailer.send(addr_from, [addr_to], message.as_string())
        except ComponentLookupError:
            mailer = getUtility(IMailDelivery, name="naaya-mail-delivery")
            try:
                mailer.send(addr_from, [addr_to], message.as_string())
            except AssertionError:
                mailer.send(addr_from, [addr_to], message)

    security.declareProtected(view, 'ask_for_password_reset')

    def ask_for_password_reset(self, REQUEST=None, email=None):
        """ view """
        if REQUEST is None:
            REQUEST = self.REQUEST
        if not email:
            email = REQUEST.form['email']

        agent = self._get_ldap_agent()
        users = agent.search_user_by_email(email)   # , no_disabled=True)

        if users:
            # some people have multiple accounts; send mail for each account.
            for user_info in users:
                if user_info['status'] == 'disabled':
                    msg = "This email: %s belongs to a disabled account" % \
                        user_info['email']
                    _set_session_message(REQUEST, 'error', msg)
                    location = (
                        self.absolute_url() +
                        '/messages_html?msg=email-disabled')
                else:
                    token = self._new_token(user_info['id'])
                    log.info(
                        "Sending password recovery email to user %r at %r.",
                        user_info['id'], email)
                    self._send_token_email(email, token, user_info)

                    location = (self.absolute_url() +
                                '/messages_html?msg=email-sent')

        else:
            log.info("Requested password recovery with invalid email %r.",
                     email)
            msg = "Email address not found in database."
            _set_session_message(REQUEST, 'error', msg)
            location = self.absolute_url() + '/'

        if REQUEST:
            REQUEST.RESPONSE.redirect(location)

    security.declareProtected(view, 'messages_html')

    def messages_html(self, REQUEST):
        """ view """
        options = {
            'message-name': REQUEST.form['msg'],
        }
        return self._render_template('zpt/pwreset_message.zpt', **options)

    def _say_token_expired(self, REQUEST):
        msg = ("Password reset link is invalid, perhaps it has "
               "expired. Please try again.")
        _set_session_message(REQUEST, 'error', msg)
        location = self.absolute_url() + '/'
        REQUEST.RESPONSE.redirect(location)

    def _expire_tokens(self):
        expired = []
        cutoff_time = datetime.utcnow() - timedelta(days=1)
        for token, token_data in self._tokens.iteritems():
            if token_data.timestamp < cutoff_time:
                expired.append(token)
        for token in expired:
            log.info('Token %r expired.', token)
            del self._tokens[token]

    security.declareProtected(view, 'confirm_email')

    def confirm_email(self, REQUEST):
        """ view """

        token = REQUEST.form['token']
        self._expire_tokens()
        token_data = self._tokens.get(token, None)

        if token_data is None:
            return self._say_token_expired(REQUEST)

        options = {
            'token': token,
            'user_id': token_data.user_id,
        }
        return self._render_template('zpt/pwreset_new_password.zpt', **options)

    def reset_password(self, REQUEST):
        """ view """

        token = REQUEST.form['token']
        self._expire_tokens()
        token_data = self._tokens.get(token, None)

        if token_data is None:
            return self._say_token_expired(REQUEST)

        new_password = REQUEST.form['password']
        if new_password != REQUEST.form['password-confirm']:
            _set_session_message(REQUEST, 'error', "Passwords do not match.")
            location = self.absolute_url() + '/confirm_email?token=' + token

        else:
            log.info("Restting password for user %r with token %r",
                     token_data.user_id, token)
            agent = self._get_ldap_agent(bind=True)
            agent.set_user_password(token_data.user_id, None, new_password)
            del self._tokens[token]
            location = (self.absolute_url() +
                        '/messages_html?msg=password-reset')

        REQUEST.RESPONSE.redirect(location)

    security.declareProtected(view, 'can_edit_users')

    def can_edit_users(self):
        user = self.REQUEST.AUTHENTICATED_USER
        return bool(user.has_permission(eionet_edit_users, self))
Exemple #37
0
class PasswordResetTool(SimpleItem):
    meta_type = 'Eionet Password Reset Tool'
    security = ClassSecurityInfo()
    icon = '++resource++eea.ldapadmin-www/eionet_password_reset_tool.gif'
    session_messages = SESSION_MESSAGES

    manage_options = (
        {
            'label': 'Configure',
            'action': 'manage_edit'
        },
        {
            'label': 'View',
            'action': ''
        },
    ) + SimpleItem.manage_options

    _render_template = TemplateRenderer(CommonTemplateLogic)

    def __init__(self, config={}):
        super(PasswordResetTool, self).__init__()
        self._config = PersistentMapping(config)
        self._tokens = PersistentMapping()

    security.declareProtected(view_management_screens, 'get_config')

    def get_config(self):
        return dict(self._config)

    security.declareProtected(view_management_screens, 'manage_edit')
    manage_edit = PageTemplateFile('zpt/pwreset_manage_edit', globals())
    manage_edit.ldap_config_edit_macro = ldap_config.edit_macro

    security.declareProtected(view_management_screens, 'manage_edit_save')

    def manage_edit_save(self, REQUEST):
        """ save changes to configuration """
        form = REQUEST.form
        new_config = ldap_config.read_form(form, edit=True)

        new_config['legacy_ldap_server'] = form.get('legacy_ldap_server', '')
        new_config['legacy_admin_dn'] = form.get('legacy_admin_dn', '')
        new_config['legacy_admin_pw'] = form.get('legacy_admin_pw', '')
        if not new_config['legacy_admin_pw']:
            del new_config['legacy_admin_pw']  # don't overwrite

        self._config.update(new_config)
        REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_edit')

    def _get_ldap_agent(self, bind=True):
        return ldap_config.ldap_agent_with_config(self._config, bind)

    def _predefined_filters(self):
        return sorted(self.objectValues([query.Query.meta_type]),
                      key=lambda ob: ob.getId())

    security.declareProtected(view, 'index_html')

    def index_html(self, REQUEST):
        """ view """
        email = REQUEST.get('email', '')
        options = {'email': email}
        return self._render_template('zpt/pwreset_index.zpt', **options)

    def _new_token(self, user_id):
        token = random_token()
        self._tokens[token] = TokenData(user_id, datetime.utcnow())
        return token

    def _send_token_email(self, addr_to, token, user_info):
        addr_from = "*****@*****.**"
        email_template = load_template('zpt/pwreset_token_email.zpt')
        expiration_time = datetime.utcnow() + timedelta(days=1)
        options = {
            'token_url': self.absolute_url() + "/confirm_email?token=" + token,
            'user_info': user_info,
            'context': self,
            'network_name': NETWORK_NAME,
            'expiration_time': expiration_time.strftime("%Y-%m-%d %H:%M:%S")
        }
        print options['token_url']
        message = MIMEText(email_template(**options).encode('utf-8'),
                           _charset='utf-8')
        message['From'] = addr_from
        message['To'] = addr_to
        message['Subject'] = "%s account password recovery" % NETWORK_NAME

        try:
            mailer = getUtility(IMailDelivery, name="Mail")
            mailer.send(addr_from, [addr_to], message.as_string())
        except ComponentLookupError:
            mailer = getUtility(IMailDelivery, name="naaya-mail-delivery")
            try:
                mailer.send(addr_from, [addr_to], message.as_string())
            except AssertionError:
                mailer.send(addr_from, [addr_to], message)

    security.declareProtected(view, 'ask_for_password_reset')

    def ask_for_password_reset(self,
                               REQUEST=None,
                               email=None,
                               on_create=False):
        """ view """
        if REQUEST is None:
            REQUEST = self.REQUEST
        if not email:
            email = REQUEST.form['email']

        agent = self._get_ldap_agent()
        users = agent.search_user_by_email(email)  # , no_disabled=True)

        if users:
            # some people have multiple accounts; send mail for each account.
            for user_info in users:
                if user_info['status'] == 'disabled':
                    msg = "This email: %s belongs to a disabled account" % \
                        user_info['email']
                    _set_session_message(REQUEST, 'error', msg)
                    location = (self.absolute_url() +
                                '/messages_html?msg=email-disabled')
                else:
                    token = self._new_token(user_info['id'])
                    log.info(
                        "Sending password recovery email to user %r at %r.",
                        user_info['id'], email)
                    self._send_token_email(email, token, user_info)

                    location = (self.absolute_url() +
                                '/messages_html?msg=email-sent')

        else:
            log.info("Requested password recovery with invalid email %r.",
                     email)
            msg = "Email address not found in database."
            _set_session_message(REQUEST, 'error', msg)
            location = self.absolute_url() + '/'

        if REQUEST and not on_create:
            REQUEST.RESPONSE.redirect(location)

    security.declareProtected(view, 'messages_html')

    def messages_html(self, REQUEST):
        """ view """
        options = {
            'message-name': REQUEST.form['msg'],
        }
        return self._render_template('zpt/pwreset_message.zpt', **options)

    def _say_token_expired(self, REQUEST):
        msg = ("Password reset link is invalid, perhaps it has "
               "expired. Please try again.")
        _set_session_message(REQUEST, 'error', msg)
        location = self.absolute_url() + '/'
        REQUEST.RESPONSE.redirect(location)

    def _expire_tokens(self):
        expired = []
        cutoff_time = datetime.utcnow() - timedelta(days=1)
        for token, token_data in self._tokens.iteritems():
            if token_data.timestamp < cutoff_time:
                expired.append(token)
        for token in expired:
            log.info('Token %r expired.', token)
            del self._tokens[token]

    security.declareProtected(view, 'confirm_email')

    def confirm_email(self, REQUEST):
        """ view """

        token = REQUEST.form['token']
        self._expire_tokens()
        token_data = self._tokens.get(token, None)

        if token_data is None:
            return self._say_token_expired(REQUEST)

        options = {
            'token': token,
            'user_id': token_data.user_id,
        }
        return self._render_template('zpt/pwreset_new_password.zpt', **options)

    def reset_password(self, REQUEST):
        """ view """

        token = REQUEST.form['token']
        self._expire_tokens()
        token_data = self._tokens.get(token, None)

        if token_data is None:
            return self._say_token_expired(REQUEST)

        new_password = REQUEST.form['password']
        if new_password != REQUEST.form['password-confirm']:
            _set_session_message(REQUEST, 'error', "Passwords do not match.")
            location = self.absolute_url() + '/confirm_email?token=' + token

        else:
            log.info("Restting password for user %r with token %r",
                     token_data.user_id, token)
            agent = self._get_ldap_agent(bind=True)
            try:
                agent.set_user_password(token_data.user_id, None, new_password)
            except CONSTRAINT_VIOLATION, e:
                if e.message['info'] in [
                        'Password fails quality checking policy'
                ]:
                    try:
                        defaultppolicy = agent.conn.search_s(
                            'cn=defaultppolicy,ou=pwpolicies,o=EIONET,'
                            'l=Europe', SCOPE_BASE)
                        p_length = defaultppolicy[0][1]['pwdMinLength'][0]
                        message = '%s (min. %s characters)' % (
                            e.message['info'], p_length)
                    except NO_SUCH_OBJECT:
                        message = e.message['info']
                else:
                    message = e.message['info']
                _set_session_message(REQUEST, 'error', message)
                location = (self.absolute_url() + '/confirm_email?token=' +
                            token)
            else:
Exemple #38
0
class Room( Persistent, RoomBase, Fossilizable ):
    """
    ZODB specific implementation.

    For documentation of methods see base class.
    """

    fossilizes(IRoomMapFossil, IRoomCalendarFossil)

    __dalManager = Factory.getDALManager()
    vcList = []

    def __init__(self):
        RoomBase.__init__( self )
        self.customAtts = PersistentMapping()
        self.avaibleVC = []
        self._nonBookableDates = []

    def getNonBookableDates(self):
        try:
            if self._nonBookableDates:
                pass
        except AttributeError:
            self._nonBookableDates = []
            self._p_changed = 1
        return self._nonBookableDates

    def addNonBookableDate(self, udate):
        self._nonBookableDates.append(udate)
        self._p_changed = 1

    def addNonBookableDateFromParams(self, params):
        nbd = NonBookableDate(params["startDate"], params["endDate"])
        self._nonBookableDates.append(nbd)
        self._p_changed = 1

    def clearNonBookableDates(self):
        self._nonBookableDates = []
        self._p_changed = 1

    def isNonBookableDay(self, day):
        for nbd in self.getNonBookableDates():
            if nbd.doesDayOverlap(day):
                return True
        return False

    def getBlockedDay(self, day):
        blockings = Factory.newRoomBlocking().getByDate(day)
        for bl in blockings:
            rbl = bl.getBlockedRoom(self)
            if rbl and rbl.active is not False:
                return rbl
        return None

    def setAvailableVC(self, avc):
        self.avaibleVC = avc

    def getAvailableVC(self):
        try:
            return self.avaibleVC
        except:
            self.avaibleVC = []
        return self.avaibleVC

    @staticmethod
    def getRoot():
        return Room.__dalManager.getRoot(_ROOMS)

    def getAllManagers(self):
        managers = set([self.getResponsible()])
        if self.customAtts.get('Simba List'):
            groups = GroupHolder().match({'name': self.customAtts['Simba List']},
                                         exact=True, forceWithoutExtAuth=True)
            if not groups:
                groups = GroupHolder().match({'name': self.customAtts['Simba List']}, exact=True)
            if groups and len(groups) == 1:
                managers |= set(groups[0].getMemberList())
        return list(managers)

    def insert( self ):
        """ Documentation in base class. """
        RoomBase.insert( self )
        roomsBTree = Room.getRoot()
        # Ensure ID
        if self.id == None:
            # Maximum ID + 1
            if len( roomsBTree ) > 0:
                self.id = roomsBTree.maxKey() + 1
            else:
                self.id = 1 # Can not use maxKey for 1st record in a tree
        # Add self to the BTree
        roomsBTree[self.id] = self
        Catalog.getIdx('user_room').index_obj(self.guid)

    def update( self ):
        """ Documentation in base class. """
        RoomBase.update( self )

        # Check Simba mailing list
        listName = self.customAtts.get( 'Simba List' )
        if listName:
            from MaKaC.user import GroupHolder
            groups = GroupHolder().match( { 'name': listName }, forceWithoutExtAuth = True )
            if not groups:
                groups = GroupHolder().match( { 'name': listName } )
            if not groups:
                self.customAtts['Simba List'] = 'Error: unknown mailing list'

        # reindex - needed due to possible manager changes
        # super slow, though...
        Catalog.getIdx('user_room').unindex_obj(self.guid)
        Catalog.getIdx('user_room').index_obj(self.guid)

        self._p_changed = True

    def remove( self ):
        """ Documentation in base class. """
        RoomBase.remove( self )
        roomsBTree = Room.getRoot()
        del roomsBTree[self.id]
        Catalog.getIdx('user_room').unindex_obj(self.guid)

    @classmethod
    def isAvatarResponsibleForRooms(cls, avatar):
        return Catalog.getIdx('user_room').get(avatar.getId()) is not None

    @classmethod
    def getUserRooms(cls, avatar):
        return Catalog.getIdx('user_room').get(avatar.getId())

    # Typical actions
    @staticmethod
    def getRooms( *args, **kwargs ):
        """ Documentation in base class. """

        roomsBTree = Room.getRoot()
        location = kwargs.get( 'location' )

        if kwargs.get( 'allFast' ) == True:
            return [ room for room in roomsBTree.values() if room.isActive and (not location or room.locationName == location) ]

        if kwargs.get( 'reallyAllFast' ) == True:
            return [ room for room in roomsBTree.values() if (not location or room.locationName == location) ]

        if len( kwargs ) == 0:
            ret_lst = []
            for room in roomsBTree.values():
                ret_lst.append( room )

        roomID = kwargs.get( 'roomID' )
        roomName = kwargs.get( 'roomName' )
        roomEx = kwargs.get( 'roomExample' )
        resvEx = kwargs.get( 'resvExample' )
        freeText = kwargs.get( 'freeText' )
        available = kwargs.get( 'available' )
        countOnly = kwargs.get( 'countOnly' )
        minCapacity = kwargs.get( 'minCapacity' )
        location = kwargs.get( 'location' )
        ownedBy = kwargs.get( 'ownedBy' )
        customAtts = kwargs.get( 'customAtts' )
#        responsibleID = kwargs.get( 'responsibleID' )
        pendingBlockings = kwargs.get( 'pendingBlockings' )

        ret_lst = []
        counter = 0
        if roomID != None:
            return roomsBTree.get( roomID )
        if roomName != None:
            for room in roomsBTree.itervalues():
                if room.name == roomName:
                    if location == None or room.locationName == location:
                        return room
            return None

        for room in roomsBTree.itervalues():
            # Apply all conditions =========
            if location != None:
                if room.locationName != location:
                    continue
            if roomEx != None:
                if not qbeMatch( roomEx, room, Room.__attrSpecialEqual, minCapacity = minCapacity ):
                    continue
                if not room.__hasEquipment( roomEx.getEquipment() ):
                    continue
            if freeText != None:
                if not room.__hasFreeText( freeText.split() ):
                    continue
            if resvEx != None:
                resvEx.room = room
                aval = room.isAvailable( resvEx )
                if aval != available:
                    continue
                blockState = resvEx.getBlockingConflictState(ContextManager.get('currentUser'))
                if blockState == 'active':
                    continue
                elif blockState == 'pending' and pendingBlockings:
                    continue
            if ownedBy != None:
                if not room.isOwnedBy( ownedBy ):
                    continue
            if customAtts is not None:
                if not hasattr(room, "customAtts"):
                    continue
                discard = False
                for condition in customAtts:
                    attName = condition["name"]
                    allowEmpty = condition.get("allowEmpty", False)
                    filter = condition.get("filter", None)
                    if not attName in room.customAtts:
                        discard = True
                        break
                    elif not allowEmpty and str(room.customAtts[attName]).strip() == "":
                        discard = True
                        break
                    elif not filter(room.customAtts[attName]):
                        discard = True
                        break
                if discard:
                    continue

            # All conditions are met: add room to the results
            counter += 1
            if not countOnly:
                ret_lst.append( room )

        #print "Found %d rooms." % counter
        if countOnly:
            return counter
        else:
            return ret_lst

    # Statistics ====================================

    @staticmethod
    def countRooms( *args, **kwargs ):
        """ Documentation in base class. """
        kwargs['countOnly'] = True
        return Room.getRooms( **kwargs )

    @staticmethod
    def getNumberOfRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        return Room.countRooms( location = location )

    @staticmethod
    def getNumberOfActiveRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )

    @staticmethod
    def getNumberOfReservableRooms( *args, **kwargs ):
        """ Documentation in base class. """
        location = kwargs.get( 'location', Location.getDefaultLocation().friendlyName )
        room = Factory.newRoom()
        room.isReservable = True
        room.isActive = True
        return Room.countRooms( roomExample = room, location = location )

    def getLocationName( self ):
        #from MaKaC.plugins.RoomBooking.default.factory import Factory
        #return Factory.locationName
        return self._locationName

    def setLocationName( self, locationName ):
        self._locationName = locationName

    def savePhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()

    def saveSmallPhoto( self, photoPath ):
        filePath = Config.getInstance().getRoomSmallPhotosDir()
        fileName = self._doGetPhotoId( force = True ) + ".jpg"
        try: os.makedirs( filePath )
        except: pass
        fullPath = os.path.join( filePath, fileName )

        f = open( fullPath, "wb" )
        f.write( photoPath.file.read() )
        f.close()


    # ==== Private ===================================================

    def _getSafeLocationName( self ):
        if self.locationName == None:
            return None
        s = ""
        for i in xrange( 0, len( self.locationName ) ):
            code = ord( self.locationName[i] )
            if ( code in xrange( ord( 'a' ), ord( 'z' ) + 1 ) ) or \
               ( code in xrange( ord( 'A' ), ord( 'Z' ) + 1 ) ) or \
               ( code in xrange( ord( '0' ), ord( '9' ) + 1 ) ):
                # Valid
                s += self.locationName[i]
            else:
                s += '_' # Replace all other characters with underscore
        return s

    def _doGetPhotoId( self, force = False ):
        photoId = "%s-%s-%s-%s" % ( str( self._getSafeLocationName() ), str( self.building ).strip(), str( self.floor ).strip(), str( self.roomNr ).strip() )

        filePath = Config.getInstance().getRoomPhotosDir()
        fileName = photoId + ".jpg"
        fullPath = os.path.join( filePath, fileName )
        from os.path import exists
        if exists( fullPath ) or force:
            return photoId
        else:
            return None

    def _doSetPhotoId( self ):
        """
        For this plugin, photoId is always composed of location-building-floor-room.jpg
        """
        pass

    def __hasFreeText( self, freeTextList ):
        # OR
        for freeText in freeTextList:
            freeText = freeText.lower()
            if self.__hasOneFreeText( freeText ):
                return True
        return False

    def __hasOneFreeText( self, freeText ):
        # Look for freeText in all string and int attributes
        for attrName in dir( self ):
            if attrName[0] == '_':
                continue
            attrType = eval( 'self.' + attrName + '.__class__.__name__' )
            if attrType == 'str':
                attrVal = eval( 'self.' + attrName )
                if attrVal.lower().find( freeText ) != -1:
                    return True

        # Look for freeText in equipment
        if self.__hasEquipment( [ freeText ] ):
            return True

        # Look for freeText in responsible
        if self.responsibleId != None:
            user = self.getResponsible();
            if freeText in user.getFullName().lower()  or  freeText in user.getEmail().lower():
                return True

        # Look for freeText in custom attributes
        for value in self.customAtts.itervalues():
            if value and ( freeText in value.lower() ):
                return True

        # Not found
        return False

    @staticmethod
    def __goodCapacity( val1, val2, minCapacity = None ):
        # Difference in capacity less than 20%
        if val1 < 1: val1 = 1

        if not minCapacity:
            return abs( val1 - val2 ) / float( val1 ) <= 0.2
        else:
            return val2 > val1

    @classmethod
    def __attrSpecialEqual( cls, attrName, exampleVal, candidateVal, **kwargs ):
        if attrName in ( 'guid', 'locationName', 'name', 'photoId', 'needsAVCSetup' ):
            return True # Skip by stating they match
        if attrName in ( 'responsibleId', 'responsibleID' ):
            return exampleVal == candidateVal # Just exact string matching
        if attrName[0:7] == 'verbose':
            return True
        if attrName.find( 'capacity' ) != -1:
            minCapacity = kwargs.get( 'minCapacity' )
            return cls.__goodCapacity( exampleVal, candidateVal, minCapacity )
        if attrName == 'customAtts':
            # Check if all values in exampleVal are contained
            # in corresponding values of candidateVal
            for k, v in exampleVal.iteritems():
                if v: # If value is specified
                    if candidateVal.get( k ) == None:
                        # Candidate does not have the attribute
                        return False
                    if not ( v in candidateVal[k] ):
                        # Candidate's attribute value does not match example
                        return False
            return True
        return None

    def __hasEquipment( self, requiredEquipmentList ):
        iHave = self.getEquipment()
        for reqEq in requiredEquipmentList:
            have = False
            for myEq in iHave:
                if myEq.lower().find( reqEq.lower() ) != -1:
                    have = True
                    break
            if not have:
                return False
        return True

    def getBookingUrl(self):
        """ Room booking URL """
        return str(urlHandlers.UHRoomBookingBookingForm.getURL(target=self))

    def getDetailsUrl(self):
        """ Room details URL """
        return str(urlHandlers.UHRoomBookingRoomDetails.getURL(target=self))

    def getMarkerDescription(self):
        """ Room description for the map marker """
        infos = []
        if self.capacity:
            infos.append("%s %s" % (self.capacity , _("people")))
        if self.isReservable:
            infos.append(_("public"))
        else:
            infos.append(_("private"))
        if self.resvsNeedConfirmation:
            infos.append(_("needs confirmation"))
        else:
            infos.append(_("auto-confirmation"))
        if self.needsAVCSetup:
            infos.append(_("video conference"))
        return ", ".join(infos)

    def getTipPhotoURL(self):
        """ URL of the tip photo of the room """
        from MaKaC.webinterface.urlHandlers import UHRoomPhoto
        photoId = self._doGetPhotoId()
        if not photoId:
            photoId = "NoPhoto"
        return str(UHRoomPhoto.getURL(photoId))

    def getIsAutoConfirm(self):
        """ Has the room auto-confirmation of schedule? """
        return not self.resvsNeedConfirmation

    locationName = property( getLocationName, setLocationName )
class NounPhraseStorage(Persistent):
    """A storage utility to keep noun-phrases in the ZODB.
    """
    
    implements(INounPhraseStorage)
    
    def __init__(self):
        """
        """
        self.rankedNouns = PersistentMapping()
        self.rankedNPs = PersistentMapping()
        self.extractor = getUtility(ITermExtractor)
        self.friendlyTypes = PersistentList()
    
    def _scoresToRanks(self,rankdict):
        scored_items = sorted(rankdict.items(),key=itemgetter(1),reverse=True)
        ranked_items = [
            ranked_item
            for ranked_item in
            ranks_from_scores(scored_items)]
        return ranked_items
    
    def addDocument(self,doc_id,text):
        """
        """
        (noun_scores,noun_phrase_scores) = self.extractor.extract(text)
        if noun_scores:
            ranked_nouns = self._scoresToRanks(noun_scores)
            self.rankedNouns[doc_id] = ranked_nouns
        if noun_phrase_scores:
            ranked_nps = self._scoresToRanks(noun_phrase_scores)
            self.rankedNPs[doc_id] = ranked_nps
    
    def _derankTerms(self,rankedTerms):
        return [term for (term,rank) in rankedTerms]
    
    def getRankedTerms(self,doc_id,ranksToKeep=0):
        """
        """
        ranked_nouns = self.rankedNouns.get(doc_id,[])
        ranked_nps = self.rankedNPs.get(doc_id,[])
        if ranksToKeep:
            ranked_nouns = [
                (noun,score)
                for (noun,score) in ranked_nouns
                if score < ranksToKeep]
            ranked_nps = [
                (np,score)
                for (np,score) in ranked_nps
                if score < ranksToKeep]
        
        return (ranked_nouns,ranked_nps)
    
    def getTerms(self,doc_id,ranksToKeep=0):
        (ranked_nouns,ranked_nps) = self.getRankedTerms(doc_id,ranksToKeep)
        ranked_nouns = self._derankTerms(ranked_nouns)
        ranked_nps = self._derankTerms(ranked_nps)
        return (ranked_nouns,ranked_nps)
    
    
    def getRankedNounTerms(self,doc_id,ranksToKeep=0):
        """
        """
        ranked_nouns = self.rankedNouns.get(doc_id,[])
        if ranksToKeep:
            ranked_nouns = [
                (noun,score)
                for (noun,score) in ranked_nouns
                if score < ranksToKeep]
        return ranked_nouns
    
    def getRankedNPTerms(self,doc_id,ranksToKeep=0):
        """
        """
        ranked_nps = self.rankedNPs.get(doc_id,[])
        if ranksToKeep:
            ranked_nps = [
                (np,score)
                for (np,score) in ranked_nps
                if score < ranksToKeep]
        
        return ranked_nps
    
    def getNounTerms(self,doc_id,ranksToKeep=0):
        ranked_nouns = self.getRankedTerms(doc_id,ranksToKeep)[0]
        ranked_nouns = self._derankTerms(ranked_nouns)
        return ranked_nouns
    
    def getNPTerms(self,doc_id,ranksToKeep=0):
        ranked_nps = self.getRankedTerms(doc_id,ranksToKeep)[1]
        ranked_nps = self._derankTerms(ranked_nps)
        return ranked_nps
    
    def clear(self):
        """Wipes the storage
        """
        self.rankedNouns.clear()
        self.rankedNPs.clear()
Exemple #40
0
class PSession(base.Session, Persistent):
    """
    Keys which are already used in the data dictionary of each session: 
        - currentCategoryId: it is used for knowing which is the category that contains the current conference.
        - menuStatus: it is used for knowing if the conference display menu is closed or opened.
        - accessKeys: contains all the access keys entered by the user in this session
        - modifKeys: contains all the modification keys entered by the user in this session
    """
    def __init__(self, request, id):
        base.Session.__init__(self, request, id)
        self.user = None
        minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        self.datadict = PersistentMapping()
        base.Session.__init__(self, request, id)
        #minfo = info.HelperMaKaCInfo.getMaKaCInfoInstance()
        #self.setVar("ActiveTimezone",minfo.getTimezone())
        self._lang = minfo.getLang()
        self.setVar("ActiveTimezone", "LOCAL")

    def setUser(self, newUser):
        if newUser:
            self._lang = newUser.getLang()
        self.user = newUser
        #get_transaction().commit()

    def getUser(self):
        return self.user

    def getId(self):
        return self.id

    def setVar(self, key, value):
        try:
            self.datadict[key] = value
        except AttributeError:
            self.datadict = PersistentMapping()
            self.datadict[key] = value

    def getVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        return self.datadict.get(key, None)

    def removeVar(self, key):
        try:
            if self.datadict:
                pass
        except AttributeError:
            self.datadict = PersistentMapping()
            return None
        if self.datadict.has_key(key):
            del self.datadict[key]

    def getLang(self):
        try:
            if self._lang is None:
                raise Exception("no language")
        except:
            try:
                lang = self.user.getLang()
            except:
                lang = "en_US"
                Logger.get('i18n').debug(
                    'No user language defined. Using %s as default.' % lang)
            self._lang = lang

        return self._lang

    def setLang(self, lang):
        self._lang = lang
Exemple #41
0
class NotificationTool(UniqueObject, SimpleItem, PropertyManager):
    """Main notification tool."""
    id = ID
    title = TITLE
    meta_type = META_TYPE

    manage_options = (PropertyManager.manage_options
                      + SimpleItem.manage_options)

    ## Extra subscriptions
    extra_subscriptions_enabled = False
    ## FIXME: rename this to something like
    ## 'restrict_subscriptions_to_authenticated_users'
    extra_subscriptions_for_authenticated_only = True
    extra_subscriptions_recursive = True

    ## Debug settings
    debug_log_addresses = False

    ## Ignore rules
    ignore_rules = DEFAULT_IGNORE_RULES

    ## Item creation
    item_creation_notification_enabled = True
    on_item_creation_users = []
    on_item_creation_mail_template = []
    ## Item modification
    item_modification_notification_enabled = True
    on_item_modification_users = []
    on_item_modification_mail_template = []
    ## Item deletion
    item_deletion_notification_enabled = True
    on_item_deletion_users = []
    on_item_deletion_mail_template = []
    ## Workflow transition
    wf_transition_notification_enabled = True
    on_wf_transition_users = []
    on_wf_transition_mail_template = []
    ## Member registration
    member_registration_notification_enabled = True
    on_member_registration_users = []
    on_member_registration_mail_template = []
    ## Member modification
    member_modification_notification_enabled = True
    on_member_modification_users = []
    on_member_modification_mail_template = []
    ## Discussion item creation
    discussion_item_creation_notification_enabled = True
    on_discussion_item_creation_users = []
    on_discussion_item_creation_mail_template = []

    _properties = ({'id': 'extra_subscriptions_enabled',
                    'label': 'Enable extra subscriptions',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'extra_subscriptions_for_authenticated_only',
                    'label': 'Enable extra subscriptions only for authenticated users',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'extra_subscriptions_recursive',
                    'label': 'Toggle recursive mode for extra subscriptions',
                    'mode': 'w',
                    'type': 'boolean'},

                   {'id': 'debug_log_addresses',
                    'label': 'Toggle debug mode: log addresses',
                    'mode': 'w',
                    'type': 'boolean'},

                   {'id': 'ignore_rules',
                    'label': 'Rules (ignore)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'item_creation_notification_enabled',
                    'label': 'Enable item creation notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_creation_users',
                    'label': 'Rules on item creation (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_creation_mail_template',
                    'label': 'Rules on item creation (mail template)',
                    'mode': 'w',
                    'type': 'lines'},
 
                   {'id': 'item_modification_notification_enabled',
                    'label': 'Enable item modification notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_modification_users',
                    'label': 'Rules on item modification (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_modification_mail_template',
                    'label': 'Rules on item modification (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'item_deletion_notification_enabled',
                    'label': 'Enable item deletion notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_item_deletion_users',
                    'label': 'Rules on item deletion (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_item_deletion_mail_template',
                    'label': 'Rules on item deletion (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'wf_transition_notification_enabled',
                    'label': 'Enable workflow transition notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_wf_transition_users',
                    'label': 'Rules on workflow transition (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_wf_transition_mail_template',
                    'label': 'Rules on workflow transition (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'member_registration_notification_enabled',
                    'label': 'Enable member registration notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_member_registration_users',
                    'label': 'Rules on member registration (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_member_registration_mail_template',
                    'label': 'Rules on member registration (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'member_modification_notification_enabled',
                    'label': 'Enable member modification notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_member_modification_users',
                    'label': 'Rules on member modification (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_member_modification_mail_template',
                    'label': 'Rules on member modification (mail template)',
                    'mode': 'w',
                    'type': 'lines'},

                   {'id': 'discussion_item_creation_notification_enabled',
                    'label': 'Enable discussion item creation notification',
                    'mode': 'w',
                    'type': 'boolean'},
                   {'id': 'on_discussion_item_creation_users',
                    'label': 'Rules on discussion item creation (users)',
                    'mode': 'w',
                    'type': 'lines'},
                   {'id': 'on_discussion_item_creation_mail_template',
                    'label': 'Rules on discussion item creation (mail template)',
                    'mode': 'w',
                    'type': 'lines'},
                   )

    security = ClassSecurityInfo()
    decPrivate = security.declarePrivate
    decProtected = security.declareProtected
    decPublic = security.declarePublic


    def __init__(self, *args, **kwargs):
        self._uid_to_path = PersistentMapping()
        self._subscriptions = PersistentMapping()


    #################################################################
    ## Notification handlers
    ########################
    decPrivate('onItemCreation')
    def onItemCreation(self, obj):
        """Handler called when an item is created.

        It returns the number of mails which have been sent.

        **Warning:** this handler is not called when a discussion item
        is added. In this case, ``onDiscussionItemCreation()`` is
        called instead.
        """
        if not self.getProperty('item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'current': obj,
                               'previous': None})
        return self._handlerHelper(obj, 'item_creation',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onItemModification')
    def onItemModification(self, obj):
        """Handler called when an item is modified.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('item_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'current': obj,
                               'previous': self.getPreviousVersion(obj)})
        return self._handlerHelper(obj, 'item_modification',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)
                                   
    decPrivate('onItemDeletion')
    def onItemDeletion(self, obj, container):
        """Handler called when an item is deleted.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('item_deletion_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'current': None,
                               'previous': obj,
                               'container': container})
        return self._handlerHelper(obj, 'item_deletion',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onWorkflowTransition')
    def onWorkflowTransition(self, workflow, obj, action, result):
        """Handler called when a workflow transition is triggered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('wf_transition_notification_enabled'):
            return 0
        if self.ignoreNotification(obj):
            return 0

        wtool = getToolByName(self, 'portal_workflow')
        current_state = wtool.getInfoFor(obj, 'review_state')
        comments = wtool.getInfoFor(obj, 'comments')
        extra_bindings = getBasicBindings(obj)
        extra_bindings.update({'transition': action,
                               'comments': comments})
        return self._handlerHelper(obj, 'wf_transition',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onMemberRegistration')
    def onMemberRegistration(self, member, properties):
        """Handler called when a new portal member has been registered.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('member_registration_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        if properties is None:
            properties = {} ## FIXME: How could it be? (Damien)

        current_user = getSecurityManager().getUser()
        extra_bindings = {'current_user': current_user,
                          'member': member,
                          'properties': properties,
                          'event': 'registration'}
        return self._handlerHelper(member, 'member_registration',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onMemberModification')
    def onMemberModification(self, member):
        """Handler called when a member changes his/her properties.

        It returns the number of mails which have been sent.
        """
        ## This method can also be called when the member is
        ## registered. We have to check that.
        stack = inspect.stack()
        ## 1st item is ourself
        ## 2nd item is 'CMFCore.MemberDataTool.notifyMemberModified()'
        ## 3rd item is 'CMFCore.MemberDataTool.setMemberProperties()'
        ## 4th item is what we want to check: it is either 'addMember'
        ## or 'setProperties()'
        caller = stack[3][3]
        if caller != 'setProperties':
            return 0

        if not self.getProperty('member_modification_notification_enabled'):
            return 0
        if self.ignoreNotification(member):
            return 0

        ## FIXME: what is the purpose of the following lines? (Damien)
        memberdata = getToolByName(self, 'portal_memberdata')
        properties = {}
        for key, value in memberdata.propertyItems():
            properties[key] = value

        current_user = getSecurityManager().getUser()
        extra_bindings = {'current_user': current_user,
                          'member': member,
                          'properties': properties,
                          'event': 'modification'}
        return self._handlerHelper(member, 'member_modification',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    decPrivate('onDiscussionItemCreation')
    def onDiscussionItemCreation(self, discussion_item):
        """Handler called when a discussion item is created.

        It returns the number of mails which have been sent.
        """
        if not self.getProperty('discussion_item_creation_notification_enabled'):
            return 0
        if self.ignoreNotification(discussion_item):
            return 0

        ## We add two bindings to disambiguate the meaning of 'here'
        ## in the mail template and the rules: 'discussion_item' and
        ## 'discussed_item'.
        discussed_item = discussion_item
        while discussed_item.meta_type == discussion_item.meta_type:
            discussed_item = discussed_item.aq_inner.aq_parent.aq_parent
        extra_bindings = getBasicBindings(discussion_item)
        extra_bindings = {'discussion_item': discussion_item,
                          'discussed_item': discussed_item}
        return self._handlerHelper(discussion_item,
                                   'discussion_item_creation',
                                   extra_bindings,
                                   extra_bindings,
                                   extra_bindings)


    def _handlerHelper(self, obj, what,
                       get_users_extra_bindings,
                       mail_template_extra_bindings,
                       mail_template_options):
        """An helper method for ``on*()`` handlers.

        It returns the number of mails which have been sent.
        """
        self._updateSubscriptionMapping(obj)
        users = self.getUsersToNotify(obj, what,
                                      get_users_extra_bindings)
        if self.isExtraSubscriptionsEnabled():
            users.extend(self.getExtraSubscribersOf(self._getPath(obj)))
        users = self.removeUnAuthorizedSubscribers(users, obj)

        addresses = self.getEmailAddresses(users)
        if not addresses:
            LOG.warning("No addresses for '%s' notification of %s",
                        what, obj.absolute_url(1))
            return 0

        template = self.getMailTemplate(obj, what,
                                        mail_template_extra_bindings)
        if template is None:
            LOG.warning("No mail template for '%s' notification of %s",
                        what, obj.absolute_url(1))
            return 0

        try:
            message = template(**mail_template_options)
        except ConflictError:
            raise
        except:
            LOG.error("Cannot evaluate mail template '%s' on '%s' "\
                      "for '%s'",
                      template.absolute_url(1),
                      obj.absolute_url(1), what,
                      exc_info=True)
            return 0

        return self.sendNotification(addresses, message)
    #################################################################


    #################################################################
    ## Utility methods
    ###############################
    decPrivate('ignoreNotification')
    def ignoreNotification(self, obj):
        """Return ``True`` iff notification have been set to be ignored for
        ``obj``.
        """
        ec = getExpressionContext(obj)
        users = []
        for match_expr in self.getProperty('ignore_rules', ()):
            try:
                if self._match(match_expr, ec):
                    return True
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'ignore_rules' rule "\
                          "('%s') for '%s'",
                          match_expr, obj.absolute_url(1),
                          exc_info=True)
        return False


    decPrivate('getUsersToNotify')
    def getUsersToNotify(self, obj, what, ec_bindings=None):
        """Return a list of users to notify for the ``what`` of an object
        ``obj``, ``what`` being one of the implemented notification
        ("*item_modification*", "*wf_transition*", etc.).

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_users' % what, None)
        if rules is None:
            raise NotImplementedError, \
                'Notification on "%s" is not implemented.' % what

        ec = getExpressionContext(obj, ec_bindings)
        users = []
        for rule in rules:
            try:
                match_expr, users_expr = rule.split(RULE_DELIMITER)
                match_expr, users_expr = match_expr.strip(), users_expr.strip()
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_users' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            users_expr = users_expr.strip()
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_users' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if users_expr == '*':
                users.extend(self.getAllUsers())
                break
            else:
                try:
                    users.extend(Expression(users_expr)(ec))
                except ConflictError:
                    raise
                except:
                    LOG.error("Error in 'on_%s_users' rule "\
                              "('%s') for '%s'",
                              what, users_expr, obj.absolute_url(1),
                              exc_info=True)
        return users


    decPrivate('getMailTemplate')
    def getMailTemplate(self, obj, what, ec_bindings=None):
        """Return the template to notify for the ``what`` of an object
        ``obj``, ``what`` being one of the implemented notification
        ("*item_modification*", "*wf_transition*", etc.), or ``None``
        if none could be found.

        ``ec_bindings`` is a mapping which is injected into the
        expression context of the expression of the rules.
        """
        rules = self.getProperty('on_%s_mail_template' % what, None)
        if rules is None:
            raise NotImplementedError, \
                'Notification on "%s" is not implemented.'

        ec = getExpressionContext(obj, ec_bindings)
        template = None
        for rule in rules:
            try:
                match_expr, template_expr = rule.split(RULE_DELIMITER)
                match_expr, template_expr = match_expr.strip(), template_expr.strip()
            except ValueError:
                LOG.error("'%s' is not a valid rule "\
                          "('on_%s_mail_template' on '%s')",
                          rule, what, obj.absolute_url(1))
                continue
            match_expr = match_expr.strip()
            template_expr = template_expr.strip()
            try:
                if not self._match(match_expr, ec):
                    continue
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, match_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            try:
                template = Expression(template_expr)(ec)
            except ConflictError:
                raise
            except:
                LOG.error("Error in 'on_%s_mail_template' rule "\
                          "('%s') for '%s'",
                          what, template_expr, obj.absolute_url(1),
                          exc_info=True)
                continue
            if type(template) == StringType:
                template = getattr(obj, template, None)
            if template is not None:
                break
        return template


    decPrivate('getAllUsers')
    def getAllUsers(self):
        """Return a list of all user ids of the portal.

        **Warning:** this method may be costly if you rely on an
        external (non ZODB) user source. Use it at your own risk.
        """
        mtool = getToolByName(self, 'portal_membership')
        return mtool.listMemberIds()


    decPrivate('removeUnAuthorizedSubscribers')
    def removeUnAuthorizedSubscribers(self, subscribers, obj):
        """Return users from ``subscribers`` who are authorized to view
        ``obj``.
        """
        portal = getToolByName(self, 'portal_url').getPortalObject()
        mtool = getToolByName(self, 'portal_membership')
        filtered_subscribers = []
        for subscriber in subscribers:
            ## We use '_huntUser()' because 'mtool.getMemberById()'
            ## would have provided a wrapped user object, with a
            ## specific context where the user is not allowed to view
            ## 'obj'. (Damien)
            member = mtool._huntUser(str(subscriber), portal)
            if member is not None:
                if member.has_permission('View', obj):
                    filtered_subscribers.append(subscriber)
            elif self._anonymousShouldBeNotified(obj):
                filtered_subscribers.append(subscriber)
        return filtered_subscribers


    decPrivate('getPreviousVersion')
    def getPreviousVersion(self, obj):
        """Return the previous version of the object, or ``None`` if none
        could be found.

        FIXME: various implementations have been tried, without
        luck. See previous revisions of this file in the SVN
        repository. (Damien)

        Update (06/03/2006): Interesting related informations might be
        found at https://dev.plone.org/archetypes/ticket/648
        """
        return None


    decPrivate('getEmailAddresses')
    def getEmailAddresses(self, users):
        """Return email addresses of ``users``.

        For each value in ``users``:

        - if the value is not an e-mail, suppose it is an user id and
        try to get the ``email`` property of this user;

        - remove duplicates;

        - remove bogus e-mail addresses.
        """
        mtool = getToolByName(self, 'portal_membership')
        addresses = {}
        for user in users:
            member = mtool.getMemberById(str(user))
            if member is not None:
                user = member.getProperty('email', '')
            if EMAIL_REGEXP.match(user):
                addresses[user] = 1
        return addresses.keys()


    decPrivate('sendNotification')
    def sendNotification(self, addresses, message):
        """Send ``message`` to all ``addresses``."""
        mailhosts = self.superValues(MAIL_HOST_META_TYPES)
        if not mailhosts:
            raise MailHostNotFound
        mailhost = mailhosts[0]

        ptool = getToolByName(self, 'portal_properties')
        encoding = ptool.site_properties.getProperty('default_charset')
        message = encodeMailHeaders(message, encoding)

        if self.getProperty('debug_log_addresses'):
            LOG.info('About to send notifications to %s', addresses)

        n_messages_sent = 0
        ## FIXME: This may not be very efficient. Or maybe it
        ## is... Would be good to take a look at other products which
        ## provide a similar feature.
        for address in addresses:
            this_message = ('To: %s\n' % address) + message
            try:
                ## FIXME: SecureMailHost, which is shipped with Plone,
                ## has declared 'send' deprecated and says
                ## 'secureSend' should be used instead. It seems to me
                ## that using '_send()' would be easier. (Damien)
                mailhost.send(this_message)
                n_messages_sent += 1
            except ConflictError:
                raise
            except:
                LOG.error('Error while sending '\
                          'notification: \n%s' % this_message,
                          exc_info=True)
                pass
        return n_messages_sent


    def _match(self, expr, ec):
        """Return ``True`` if ``expr`` returns something which can be
        evaluated to ``True`` in the expression context (``ec``) or if
        ``expr`` is "*".
        """
        if expr == '*':
            return True
        expr = Expression(expr)
        return bool(expr(ec))


    def _getPath(self, obj):
        """Return path of ``obj``.

        A slash ('/') is appended to the path if the object is
        folderish. The returned path is relative to the portal object.
        """
        utool = getToolByName(self, 'portal_url')
        path = utool.getRelativeContentURL(obj)
        path = '/' + path
        if not getattr(obj.aq_base, 'isPrincipiaFolderish', False):
            return path
        if path[-1] != '/':
            path += '/'
        return path


    def _getParents(self, path):
        """Get the parents of the item corresponding to ``path`` and return
        their respective path.

        Parents are returned from ``path`` to the portal root object.
        """
        if path == '/':
            return []
        if path[-1] == '/':
            path = path[:-1]
        parent = path[:path.rfind('/') + 1]
        parents = [parent]
        parents.extend(self._getParents(parent))
        return tuple(parents)


    def _getUID(self, obj):
        """Return UID of the object."""
        portal_uidhandler = getToolByName(self, 'portal_uidhandler')
        uid = portal_uidhandler.queryUid(obj, None)
        return uid


    def _anonymousShouldBeNotified(self, obj):
        """Return ``True`` iff anonymous users should be notified.

        It returns ``True`` iff anonymous users have the ``View``
        permission on ``obj``.
        """
        return 'Anonymous' in rolesForPermissionOn('View', obj)
    #################################################################


    #################################################################
    ## Extra subscriptions settings
    ###############################
    decProtected('View', 'isExtraSubscriptionsEnabled')
    def isExtraSubscriptionsEnabled(self):
        """Return ``True`` iff extra subscriptions are enabled."""
        return self.getProperty('extra_subscriptions_enabled')


    decProtected('View', 'isExtraSubscriptionsRecursive')
    def isExtraSubscriptionsRecursive(self):
        """Return ``True`` iff extra subscriptions are recursive.

        Note that this method does not check whether extra
        subscriptions are enabled or not.
        """
        return self.getProperty('extra_subscriptions_recursive')


    decProtected('View', 'isExtraSubscriptionsForAuthenticatedOnly')
    def isExtraSubscriptionsForAuthenticatedOnly(self):
        """Return ``True iff extra subscriptions are restricted to
        authenticated users.

        Note that this method does not check whether extra
        subscriptions are enabled or not.
        """
        return self.getProperty('extra_subscriptions_for_authenticated_only')
    #################################################################


    #################################################################
    ## Extra subscriptions logic
    ############################
    def _updateSubscriptionMapping(self, obj):
        """Update subscription mapping."""
        uid = self._getUID(obj)
        if uid is not None:
            path = self._getPath(obj)
            known_path = self._uid_to_path.get(uid)
            if known_path != path:
                self._uid_to_path[uid] = path
                if known_path is not None:
                    ## We have old informations for this object
                    for key, value in self._subscriptions.items():
                        if key.startswith(known_path):
                            new_key = path + key[len(known_path) : ]
                            self._subscriptions[new_key] = value
                            del self._subscriptions[key]


    decPublic('currentUserHasSubscribePermission')
    def currentUserHasSubscribePermissionOn(self, obj):
        """Return ``True`` iff the current user has ``SUBSCRIBE_PERMISSION``
        on ``obj``.
        """
        mtool = getToolByName(self, 'portal_membership')
        return mtool.checkPermission(SUBSCRIBE_PERMISSION, obj)


    decPublic('subscriptionToParentIsAllowed')
    def isSubscriptionToParentAllowed(self, obj):
        """Return ``True`` iff subscription to the parent of ``obj`` (i.e. the
        first folderish item above ``obj``) is allowed.

        This method uses Plone specific scripts.
        """
        if self.isExtraSubscriptionsRecursive() \
                and not obj.is_folderish() \
                and obj.isDefaultPageInFolder():
            try:
                parent = obj.aq_parent
            except ConflictError:
                raise
            except:
                return False
            return self.currentUserHasSubscribePermissionOn(parent)
        return False


    decPublic('showSubscriptionPortlet')
    def showSubscriptionPortlet(self, obj):
        """Return ``True`` iff the subscription portlet should be shown while
        viewing ``obj``.
        """
        mtool = getToolByName(self, 'portal_membership')
        anonymous = mtool.isAnonymousUser()
        return self.isExtraSubscriptionsEnabled() \
            and not (self.isExtraSubscriptionsForAuthenticatedOnly() \
                     and anonymous) \
                     and self.currentUserHasSubscribePermissionOn(obj)


    decPublic('subscribeTo')
    def subscribeTo(self, obj, email=None):
        """Subscribe ``email`` (or the current user if ``email`` is None) to
        ``obj``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is not None:
            if not self.isExtraSubscriptionsForAuthenticatedOnly():
                raise DisabledFeature
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            allowed_roles = rolesForPermissionOn(SUBSCRIBE_PERMISSION,
                                                 obj)
            if 'Anonymous' not in allowed_roles:
                raise Unauthorized
            ## FIXME: We would like to send an email to ask the user
            ## to confirm its subscription. Since this has not yet
            ## been implemented, we raise an error. (Damien)
            raise NotImplementedError
        elif not self.currentUserHasSubscribePermissionOn(obj):
            raise Unauthorized
        else:
            user = getSecurityManager().getUser().getId()
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            subscribers[user] = 1
            self._subscriptions[path] = subscribers


    decPublic('unSubscribeFrom')
    def unSubscribeFrom(self, obj, email=None):
        """Unsubscribe ``email`` (or the current user if ``email`` is
        ``None``) from ``obj``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is not None:
            if not self.isExtraSubscriptionsForAuthenticatedOnly():
                raise DisabledFeature
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception. (Damien)
            raise NotImplementedError
        else:
            user = getSecurityManager().getUser().getId()
            self._updateSubscriptionMapping(obj)
            path = self._getPath(obj)
            subscribers = self._subscriptions.get(path, {})
            try:
                del subscribers[user]
                self._subscriptions[path] = subscribers
            except KeyError:
                pass ## User was not subscribed.


    decPublic('unSubscribeFromObjectAbove')
    def unSubscribeFromObjectAbove(self, obj, email=None):
        """Find folderish items above ``obj`` and unsubscribe ``email`` (or
        the current user if ``email`` is ``None``) from the first one
        (s)he is subscribed to.

        If ``user`` is subscribed to ``obj``, this method is equivalent to
        ``unSubscribeFrom(obj, user)``.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is not None:
            if not self.isExtraSubscriptionsForAuthenticatedOnly():
                raise DisabledFeature
            if not EMAIL_REGEXP.match(email):
                raise InvalidEmailAddress
            ## FIXME: an anonymous user would like to unsubscribe
            ## his/her address. This has not yet been implemented, so
            ## we raise an exception. (Damien)
            raise NotImplementedError
        else:
            self._updateSubscriptionMapping(obj)

            utool = getToolByName(obj, 'portal_url')
            portal = utool.getPortalObject()
            portal_container = portal.aq_inner.aq_parent
            while obj != portal_container:
                if self.isSubscribedTo(obj, as_if_not_recursive=True):
                    self.unSubscribeFrom(obj)
                    break
                obj = obj.aq_parent


    decPublic('isSubscribedTo')
    def isSubscribedTo(self, obj, email=None,
                       as_if_not_recursive=False):
        """Return ``True`` iff ``email`` (or the current user if ``email`` is
        ``None``) is subscribed to ``obj``.

        If ``as_if_not_recursive`` is ``True``, this method acts as if
        the recursive mode was off.
        """
        if not self.isExtraSubscriptionsEnabled():
            raise DisabledFeature

        if email is None:
            ## Yes, 'email' is actually the id of the current user.
            email = getSecurityManager().getUser().getId()
        self._updateSubscriptionMapping(obj)
        path = self._getPath(obj)
        subscribers = self.getExtraSubscribersOf(path,
                                                 as_if_not_recursive)
        return subscribers.has_key(email)


    decPrivate('getExtraSubscribersOf')
    def getExtraSubscribersOf(self, path, as_if_not_recursive=False):
        """Return users or email addresses which are subscribed to the given
        path.

        This method returns a mapping whose keys are the users or
        email addresses. If ``as_if_not_recursive`` is ``True``, this
        method acts as if the recursive mode was off.
        """
        subscribers = self._subscriptions.get(path, {}).copy()
        if self.isExtraSubscriptionsRecursive() and \
                not as_if_not_recursive:
            if path[-1] == '/':
                path = path[:-1]
            i = path.rfind('/')
            if i != -1:
                parent = path[:i + 1]
                subscribers.update(self.getExtraSubscribersOf(parent))
        return subscribers
    #################################################################



    decProtected(SUBSCRIBE_PERMISSION, '__useless')
    def __useless(self): pass
Exemple #42
0
class Profile(Folder):

    implements(IProfile)

    alert_attachments = 'link'
    fax = ''  # BBB
    two_factor_phone = ''  # BBB
    two_factor_verified = False
    _two_factor_verify_code = ''  # BBB
    _websites = ()
    last_login_time = None  # BBB
    date_format = None  # BBB
    current_auth_code = None
    current_auth_code_time_stamp = datetime.utcnow()
    additional_fields = ('phone', 'extension', 'fax', 'department', 'position',
                         'organization', 'industry', 'location', 'country',
                         'websites', 'languages', 'office', 'room_no',
                         'biography', 'date_format', 'home_path')

    def _get_website(self):
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return old_ws
        return self._websites and self._websites[0] or ''

    website = property(_get_website,)

    def _get_websites(self):
        self._p_activate()
        if '_websites' in self.__dict__:
            return self._websites
        old_ws = self.__dict__.get('website')
        if old_ws is not None:
            return (old_ws,)
        return ()

    def _set_websites(self, value):
        self._websites = value  # coerce / normalize?
        if 'website' in self.__dict__:
            del self.__dict__['website']

    websites = property(_get_websites, _set_websites)

    def __init__(self,
                 firstname='', lastname='', email='', phone='', extension='',
                 fax='', department='', position='', organization='',
                 industry='', location='', country='US', websites=None,
                 languages='', office='', room_no='', biography='',
                 date_format='en-US', data=None, home_path=None,
                 preferred_communities=None, two_factor_phone='',
                 two_factor_verified=False,
                 ):
        super(Profile, self).__init__(data)
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.phone = phone
        self.fax = fax
        self.extension = extension
        self.department = department
        self.position = position
        self.organization = organization
        self.industry = industry
        self.location = location
        if country not in countries.as_dict:
            country = 'XX'
        self.country = country
        if websites is not None:
            self.websites = websites
        self.languages = languages
        self.office = office
        self.room_no = room_no
        self.biography = biography
        if date_format not in cultures.as_dict:
            date_format = None
        self.date_format = date_format
        self.home_path = home_path
        self._alert_prefs = PersistentMapping()
        self._pending_alerts = Accumulator()
        self.categories = PersistentMapping()
        self.password_reset_key = None
        self.password_reset_time = None
        self.preferred_communities = preferred_communities
        self.last_login_time = None
        self.two_factor_phone = two_factor_phone
        self.two_factor_verified = two_factor_verified

    @property
    def creator(self):
        return self.__name__

    @property
    def title(self):
        title = [self.firstname.strip(), self.lastname.strip()]
        if getattr(self, 'security_state', None) == 'inactive':
            title += ['(Inactive)']
        return unicode(' '.join(title))

    def get_alerts_preference(self, community_name):
        return self._alert_prefs.get(community_name,
                                     IProfile.ALERT_IMMEDIATELY)

    def set_alerts_preference(self, community_name, preference):
        if preference not in (
                IProfile.ALERT_IMMEDIATELY,
                IProfile.ALERT_DAILY_DIGEST,
                IProfile.ALERT_NEVER,
                IProfile.ALERT_WEEKLY_DIGEST,
                IProfile.ALERT_BIWEEKLY_DIGEST):
            raise ValueError("Invalid preference.")

        self._alert_prefs[community_name] = preference
Exemple #43
0
class StopWordManager:
    """ StopWordManager contains the stopwords list """

    def __init__(self):
        self.__stopwords_collection = PersistentMapping()

    def __add_stopword_item(self, id, stopword):
        #create a new item
        item = StopWord(id, stopword)
        self.__stopwords_collection[id] = item

    def __update_stopword_item(self, id, stopword):
        #modify an item
        item = self.__stopwords_collection.get(id)
        if item is not None:
            item.stopword = stopword
            self.__stopwords_collection[id] = item

    def __delete_stopword_item(self, id):
        #delete an item
        try:    del(self.__stopwords_collection[id])
        except: pass

    def __write_stopwords(self):
        #save stopwords to 'aliss_stopwords.txt'
        if not os.path.isdir(ALISS_STOPWORDS_PATH):
            os.mkdir(ALISS_STOPWORDS_PATH)

        sw_file = open(ALISS_STOPWORDS_FILE, 'w')
        f_write = sw_file.write
        f_write("# language = english\n")
        for w in self.get_stopwords_list():
            f_write("%s\n" % w.stopword)
        sw_file.close()


    #################
    #   BASIC API   #
    #################
    def add_stopword_item(self, id, stopword):
        #create a new item
        self.__add_stopword_item(id, stopword)
        self.__write_stopwords()

    def update_stopword_item(self, id, stopword):
        #modify an item
        self.__update_stopword_item(id, stopword)
        self.__write_stopwords()

    def delete_stopword_item(self, ids):
        #delete 1 or more items
        map(self.__delete_stopword_item, ids)
        self.__write_stopwords()


    #################
    #   GETTERS     #
    #################
    def get_stopwords_collection(self):
        #get the collection
        return self.__stopwords_collection

    def get_stopwords_list(self):
        #get a list with all items
        return utils.utSortObjsListByAttr(self.__stopwords_collection.values(), 'stopword')

    def get_stopword_item(self, id):
        #get an item
        try:    return self.__stopwords_collection[id]
        except: return None

    def get_stopword_item_data(self, id):
        #get an item data
        item = self.get_stopword_item(id)
        if item is not None: 
            return ['update', item.id, item.stopword]
        else:
            return ['add', '', '']

    def check_duplicate(self, word):
        #return True if a duplicate is found
        l_word = utils.formatString(word)
        for k in self.get_stopwords_list():
            if l_word == utils.formatString(k.stopword):
                return 1
        return 0


    ##############
    #   EXPORT   #
    ##############
    def export_stopwords(self, orig_url):
        #create exported stopwords XML
        r = []
        ra = r.append

        #XLIFF header
        ra('<?xml version="1.0" encoding="UTF-8"?>')
        ra('<!DOCTYPE xliff SYSTEM "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">')
        ra('<!-- XLIFF Format Copyright © OASIS Open 2001-2003 -->')
        ra('<xliff version="1.0">')
        ra('<file original="%s"' % orig_url)
        ra(' product-name="ALiSS"')
        ra(' product-version="0.9.0"')
        ra(' datatype="plaintext"')
        ra(' source-language="English"')
        ra(' target-language="English"')
        ra(' date="%s">' % utils.getCurrentDate().HTML4())
        ra('<body>')

        #XLIFF content
        for sword in self.get_stopwords_list():
            ra('<trans-unit id="%s">' % sword.id)
            ra('<source>%s</source>' % utils.utXmlEncode(sword.stopword))
            ra('<target>%s</target>' % utils.utXmlEncode(sword.stopword))
            ra('</trans-unit>')

        #XLIFF footer
        ra('</body>')
        ra('</file>')
        ra('</xliff>')
        return ''.join(r)


    ##############
    #   IMPORT   #
    ##############
    def xliff_import(self, file, add_type):
        """ XLIFF is the XML Localization Interchange File Format
            designed by a group of software providers.
            It is specified by www.oasis-open.org
        """

        msg_err = ''
        parser = xliff_parser()

        #parse the xliff information
        chandler = parser.parseHeader(file)

        if chandler is None:
            msg_err = 'Unable to parse XLIFF file'

        if not msg_err:
            #delete old stopwords
            if add_type == 'forced_add':
                self.__delete_stopword_item(self.get_stopwords_collection().keys())

            header_info = chandler.getFileTag()
            #get the target language
            target_language = [x for x in header_info if x[0]=='target-language'][0][1]

            body_info = chandler.getBody() #return a dictionary {id: (source, target)}

            for sword_id, sword in body_info.items():
                l_data = sword['target']
                if not self.check_duplicate(l_data):
                    if not sword_id: sword_id = utils.utGenRandomId()
                    self.__add_stopword_item(sword_id, sword['target'])
            self.__write_stopwords()

        return msg_err