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)])
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 _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 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)]
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
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 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 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]
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")
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()
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
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 == {}")
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
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()
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.")
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
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
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
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
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
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))
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:
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()
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
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
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
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