def test_subscribed_as_user_and_address(self): # Anne subscribes to a mailing list twice, once as a user and once # with an explicit address. She has two memberships. self._ant.subscribe(self._anne) self._ant.subscribe(self._anne.preferred_address) self.assertEqual(self._anne.memberships.member_count, 2) self.assertEqual(self._ant.members.member_count, 2) self.assertEqual( [member.address.email for member in self._ant.members.members], ['*****@*****.**', '*****@*****.**']) # get_member() is defined to return the explicit address. member = self._ant.members.get_member('*****@*****.**') subscriber = member.subscriber self.assertTrue(IAddress.providedBy(subscriber)) self.assertFalse(IUser.providedBy(subscriber)) # get_memberships() returns them all. memberships = self._ant.members.get_memberships('*****@*****.**') self.assertEqual(len(memberships), 2) as_address = (memberships[0] if IAddress.providedBy(memberships[0].subscriber) else memberships[1]) as_user = (memberships[1] if IUser.providedBy(memberships[1].subscriber) else memberships[0]) self.assertEqual(as_address.subscriber, self._anne.preferred_address) self.assertEqual(as_user.subscriber, self._anne) # All the email addresses match. self.assertEqual( [record.address.email for record in memberships], ['*****@*****.**', '*****@*****.**'])
def test_subscribed_as_user_and_address(self): # Anne subscribes to a mailing list twice, once as a user and once # with an explicit address. She has two memberships. self._ant.subscribe(self._anne) self._ant.subscribe(self._anne.preferred_address) self.assertEqual(self._anne.memberships.member_count, 2) self.assertEqual(self._ant.members.member_count, 2) self.assertEqual( [member.address.email for member in self._ant.members.members], ['*****@*****.**', '*****@*****.**']) # get_member() is defined to return the explicit address. member = self._ant.members.get_member('*****@*****.**') subscriber = member.subscriber self.assertTrue(IAddress.providedBy(subscriber)) self.assertFalse(IUser.providedBy(subscriber)) # get_memberships() returns them all. memberships = self._ant.members.get_memberships('*****@*****.**') self.assertEqual(len(memberships), 2) as_address = (memberships[0] if IAddress.providedBy( memberships[0].subscriber) else memberships[1]) as_user = (memberships[1] if IUser.providedBy( memberships[1].subscriber) else memberships[0]) self.assertEqual(as_address.subscriber, self._anne.preferred_address) self.assertEqual(as_user.subscriber, self._anne) # All the email addresses match. self.assertEqual([record.address.email for record in memberships], ['*****@*****.**', '*****@*****.**'])
def subscribe(self, store, subscriber, role=MemberRole.member): """See `IMailingList`.""" if IAddress.providedBy(subscriber): member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._address == subscriber).first() if member: raise AlreadySubscribedError( self.fqdn_listname, subscriber.email, role) elif IUser.providedBy(subscriber): if subscriber.preferred_address is None: raise MissingPreferredAddressError(subscriber) member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._user == subscriber).first() if member: raise AlreadySubscribedError( self.fqdn_listname, subscriber.preferred_address.email, role) else: raise ValueError('subscriber must be an address or user') member = Member(role=role, list_id=self._list_id, subscriber=subscriber) member.preferences = Preferences() store.add(member) notify(SubscriptionEvent(self, member)) return member
def subscribe(self, store, subscriber, role=MemberRole.member): """See `IMailingList`.""" if IAddress.providedBy(subscriber): member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._address == subscriber).first() if member: raise AlreadySubscribedError(self.fqdn_listname, subscriber.email, role) elif IUser.providedBy(subscriber): if subscriber.preferred_address is None: raise MissingPreferredAddressError(subscriber) member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._user == subscriber).first() if member: raise AlreadySubscribedError( self.fqdn_listname, subscriber.preferred_address.email, role) else: raise ValueError('subscriber must be an address or user') member = Member(role=role, list_id=self._list_id, subscriber=subscriber) member.preferences = Preferences() store.add(member) notify(SubscriptionEvent(self, member)) return member
def _get_subscriber(self, store, subscriber, role): """Get some information about a user/address. Returns a 2-tuple of (member, email) for the given subscriber. If the subscriber is is not an ``IAddress`` or ``IUser``, then a 2-tuple of (None, None) is returned. If the subscriber is not already subscribed, then (None, email) is returned. If the subscriber is an ``IUser`` and does not have a preferred address, (member, None) is returned. """ member = None email = None if IAddress.providedBy(subscriber): member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._address == subscriber).first() email = subscriber.email elif IUser.providedBy(subscriber): if subscriber.preferred_address is None: raise MissingPreferredAddressError(subscriber) email = subscriber.preferred_address.email member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._user == subscriber).first() return member, email
def __init__(self, mlist, subscriber=None, *, pre_verified=False, pre_confirmed=False, pre_approved=False): super().__init__() self.mlist = mlist self.address = None self.user = None self.which = None self.member = None self._set_token(TokenOwner.no_one) # The subscriber must be either an IUser or IAddress. if IAddress.providedBy(subscriber): self.address = subscriber self.user = self.address.user self.which = WhichSubscriber.address elif IUser.providedBy(subscriber): self.address = subscriber.preferred_address self.user = subscriber self.which = WhichSubscriber.user self.subscriber = subscriber self.pre_verified = pre_verified self.pre_confirmed = pre_confirmed self.pre_approved = pre_approved
def handle_SubscriptionEvent(event): if not isinstance(event, SubscriptionEvent): return member = event.member # Only send notifications if a member (as opposed to a moderator, # non-member, or owner) is being subscribed. if member.role is not MemberRole.member: return mlist = member.mailing_list # Maybe send the list administrators a notification. if mlist.admin_notify_mchanges: subscriber = member.subscriber if IAddress.providedBy(subscriber): address = subscriber.email display_name = subscriber.display_name else: assert IUser.providedBy(subscriber) address = subscriber.preferred_address.email display_name = subscriber.display_name send_admin_subscription_notice(mlist, address, display_name) # Maybe send a welcome message to the new member. The event's flag # overrides the mailinglist's configuration, iff it is non-None. if ((event.send_welcome_message is None and mlist.send_welcome_message) or event.send_welcome_message): send_welcome_message(mlist, member, member.preferred_language)
def subscribe(self, subscriber, role=MemberRole.member): """See `IMailingList`.""" store = Store.of(self) if IAddress.providedBy(subscriber): member = store.find( Member, Member.role == role, Member.mailing_list == self.fqdn_listname, Member._address == subscriber).one() if member: raise AlreadySubscribedError( self.fqdn_listname, subscriber.email, role) elif IUser.providedBy(subscriber): if subscriber.preferred_address is None: raise MissingPreferredAddressError(subscriber) member = store.find( Member, Member.role == role, Member.mailing_list == self.fqdn_listname, Member._user == subscriber).one() if member: raise AlreadySubscribedError( self.fqdn_listname, subscriber, role) else: raise ValueError('subscriber must be an address or user') member = Member(role=role, mailing_list=self.fqdn_listname, subscriber=subscriber) member.preferences = Preferences() store.add(member) return member
def __init__(self, mlist, subscriber=None, *, pre_approved=False, pre_confirmed=False): super().__init__(mlist, subscriber) if IAddress.providedBy(subscriber) or IUser.providedBy(subscriber): self.member = self.mlist.regular_members.get_member( self.address.email) self.pre_confirmed = pre_confirmed self.pre_approved = pre_approved
def __init__(self, role, list_id, subscriber): self._member_id = uid_factory.new() self.role = role self.list_id = list_id if IAddress.providedBy(subscriber): self._address = subscriber # Look this up dynamically. self._user = None elif IUser.providedBy(subscriber): self._user = subscriber # Look this up dynamically. self._address = None else: raise ValueError('subscriber must be a user or address') if role in (MemberRole.owner, MemberRole.moderator): self.moderation_action = Action.accept else: assert role in (MemberRole.member, MemberRole.nonmember), ( 'Invalid MemberRole: {}'.format(role)) self.moderation_action = None
def __init__(self, role, list_id, subscriber): self._member_id = uid_factory.new_uid() self.role = role self.list_id = list_id if IAddress.providedBy(subscriber): self._address = subscriber # Look this up dynamically. self._user = None elif IUser.providedBy(subscriber): self._user = subscriber # Look this up dynamically. self._address = None else: raise ValueError("subscriber must be a user or address") if role in (MemberRole.owner, MemberRole.moderator): self.moderation_action = Action.accept elif role is MemberRole.member: self.moderation_action = getUtility(IListManager).get_by_list_id(list_id).default_member_action else: assert role is MemberRole.nonmember, "Invalid MemberRole: {0}".format(role) self.moderation_action = getUtility(IListManager).get_by_list_id(list_id).default_nonmember_action
def handle_SubscriptionEvent(event): if not isinstance(event, SubscriptionEvent): return member = event.member # Only send notifications if a member (as opposed to a moderator, # non-member, or owner) is being subscribed. if member.role is not MemberRole.member: return mlist = member.mailing_list # Maybe send the list administrators a notification. if mlist.admin_notify_mchanges: subscriber = member.subscriber if IAddress.providedBy(subscriber): address = subscriber.email display_name = subscriber.display_name else: assert IUser.providedBy(subscriber) address = subscriber.preferred_address.email display_name = subscriber.display_name send_admin_subscription_notice(mlist, address, display_name) # Maybe send a welcome message to the new member. if mlist.send_welcome_message: send_welcome_message(mlist, member, member.preferred_language)
def on_post(self, request, response): """Create a new member.""" try: validator = Validator( list_id=str, subscriber=subscriber_validator(self.api), display_name=str, delivery_mode=enum_validator(DeliveryMode), role=enum_validator(MemberRole), pre_verified=bool, pre_confirmed=bool, pre_approved=bool, _optional=('delivery_mode', 'display_name', 'role', 'pre_verified', 'pre_confirmed', 'pre_approved')) arguments = validator(request) except ValueError as error: bad_request(response, str(error)) return # Dig the mailing list out of the arguments. list_id = arguments.pop('list_id') mlist = getUtility(IListManager).get_by_list_id(list_id) if mlist is None: bad_request(response, b'No such list') return # Figure out what kind of subscriber is being registered. Either it's # a user via their preferred email address or it's an explicit address. # If it's a UUID, then it must be associated with an existing user. subscriber = arguments.pop('subscriber') user_manager = getUtility(IUserManager) # We use the display name if there is one. display_name = arguments.pop('display_name', '') if isinstance(subscriber, UUID): user = user_manager.get_user_by_id(subscriber) if user is None: bad_request(response, b'No such user') return subscriber = user else: # This must be an email address. See if there's an existing # address object associated with this email. address = user_manager.get_address(subscriber) if address is None: # Create a new address, which of course will not be validated. address = user_manager.create_address( subscriber, display_name) subscriber = address # What role are we subscribing? Regular members go through the # subscription policy workflow while owners, moderators, and # nonmembers go through the legacy API for now. role = arguments.pop('role', MemberRole.member) if role is MemberRole.member: # Get the pre_ flags for the subscription workflow. pre_verified = arguments.pop('pre_verified', False) pre_confirmed = arguments.pop('pre_confirmed', False) pre_approved = arguments.pop('pre_approved', False) # Now we can run the registration process until either the # subscriber is subscribed, or the workflow is paused for # verification, confirmation, or approval. registrar = IRegistrar(mlist) try: token, token_owner, member = registrar.register( subscriber, pre_verified=pre_verified, pre_confirmed=pre_confirmed, pre_approved=pre_approved) except AlreadySubscribedError: conflict(response, b'Member already subscribed') return except MissingPreferredAddressError: bad_request(response, b'User has no preferred address') return except MembershipIsBannedError: bad_request(response, b'Membership is banned') return except SubscriptionPendingError: conflict(response, b'Subscription request already pending') return if token is None: assert token_owner is TokenOwner.no_one, token_owner # The subscription completed. Let's get the resulting member # and return the location to the new member. Member ids are # UUIDs and need to be converted to URLs because JSON doesn't # directly support UUIDs. member_id = self.api.from_uuid(member.member_id) location = self.api.path_to('members/{}'.format(member_id)) created(response, location) return # The member could not be directly subscribed because there are # some out-of-band steps that need to be completed. E.g. the user # must confirm their subscription or the moderator must approve # it. In this case, an HTTP 202 Accepted is exactly the code that # we should use, and we'll return both the confirmation token and # the "token owner" so the client knows who should confirm it. assert token is not None, token assert token_owner is not TokenOwner.no_one, token_owner assert member is None, member content = dict(token=token, token_owner=token_owner.name) accepted(response, etag(content)) return # 2015-04-15 BAW: We're subscribing some role other than a regular # member. Use the legacy API for this for now. assert role in (MemberRole.owner, MemberRole.moderator, MemberRole.nonmember) # 2015-04-15 BAW: We're limited to using an email address with this # legacy API, so if the subscriber is a user, the user must have a # preferred address, which we'll use, even though it will subscribe # the explicit address. It is an error if the user does not have a # preferred address. # # If the subscriber is an address object, just use that. if IUser.providedBy(subscriber): if subscriber.preferred_address is None: bad_request(response, b'User without preferred address') return email = subscriber.preferred_address.email else: assert IAddress.providedBy(subscriber) email = subscriber.email delivery_mode = arguments.pop('delivery_mode', DeliveryMode.regular) record = RequestRecord(email, display_name, delivery_mode) try: member = add_member(mlist, record, role) except MembershipIsBannedError: bad_request(response, b'Membership is banned') return except AlreadySubscribedError: bad_request(response, '{} is already an {} of {}'.format( email, role.name, mlist.fqdn_listname)) return # The subscription completed. Let's get the resulting member # and return the location to the new member. Member ids are # UUIDs and need to be converted to URLs because JSON doesn't # directly support UUIDs. member_id = self.api.from_uuid(member.member_id) location = self.api.path_to('members/{}'.format(member_id)) created(response, location)
def on_post(self, request, response): """Create a new member.""" try: validator = Validator(list_id=str, subscriber=subscriber_validator(self.api), display_name=str, delivery_mode=enum_validator(DeliveryMode), role=enum_validator(MemberRole), pre_verified=bool, pre_confirmed=bool, pre_approved=bool, _optional=('delivery_mode', 'display_name', 'role', 'pre_verified', 'pre_confirmed', 'pre_approved')) arguments = validator(request) except ValueError as error: bad_request(response, str(error)) return # Dig the mailing list out of the arguments. list_id = arguments.pop('list_id') mlist = getUtility(IListManager).get_by_list_id(list_id) if mlist is None: bad_request(response, b'No such list') return # Figure out what kind of subscriber is being registered. Either it's # a user via their preferred email address or it's an explicit address. # If it's a UUID, then it must be associated with an existing user. subscriber = arguments.pop('subscriber') user_manager = getUtility(IUserManager) # We use the display name if there is one. display_name = arguments.pop('display_name', '') if isinstance(subscriber, UUID): user = user_manager.get_user_by_id(subscriber) if user is None: bad_request(response, b'No such user') return subscriber = user else: # This must be an email address. See if there's an existing # address object associated with this email. address = user_manager.get_address(subscriber) if address is None: # Create a new address, which of course will not be validated. address = user_manager.create_address(subscriber, display_name) subscriber = address # What role are we subscribing? Regular members go through the # subscription policy workflow while owners, moderators, and # nonmembers go through the legacy API for now. role = arguments.pop('role', MemberRole.member) if role is MemberRole.member: # Get the pre_ flags for the subscription workflow. pre_verified = arguments.pop('pre_verified', False) pre_confirmed = arguments.pop('pre_confirmed', False) pre_approved = arguments.pop('pre_approved', False) # Now we can run the registration process until either the # subscriber is subscribed, or the workflow is paused for # verification, confirmation, or approval. registrar = ISubscriptionManager(mlist) try: token, token_owner, member = registrar.register( subscriber, pre_verified=pre_verified, pre_confirmed=pre_confirmed, pre_approved=pre_approved) except AlreadySubscribedError: conflict(response, b'Member already subscribed') return except MissingPreferredAddressError: bad_request(response, b'User has no preferred address') return except MembershipIsBannedError: bad_request(response, b'Membership is banned') return except SubscriptionPendingError: conflict(response, b'Subscription request already pending') return if token is None: assert token_owner is TokenOwner.no_one, token_owner # The subscription completed. Let's get the resulting member # and return the location to the new member. Member ids are # UUIDs and need to be converted to URLs because JSON doesn't # directly support UUIDs. member_id = self.api.from_uuid(member.member_id) location = self.api.path_to('members/{}'.format(member_id)) created(response, location) return # The member could not be directly subscribed because there are # some out-of-band steps that need to be completed. E.g. the user # must confirm their subscription or the moderator must approve # it. In this case, an HTTP 202 Accepted is exactly the code that # we should use, and we'll return both the confirmation token and # the "token owner" so the client knows who should confirm it. assert token is not None, token assert token_owner is not TokenOwner.no_one, token_owner assert member is None, member content = dict(token=token, token_owner=token_owner.name) accepted(response, etag(content)) return # 2015-04-15 BAW: We're subscribing some role other than a regular # member. Use the legacy API for this for now. assert role in (MemberRole.owner, MemberRole.moderator, MemberRole.nonmember) # 2015-04-15 BAW: We're limited to using an email address with this # legacy API, so if the subscriber is a user, the user must have a # preferred address, which we'll use, even though it will subscribe # the explicit address. It is an error if the user does not have a # preferred address. # # If the subscriber is an address object, just use that. if IUser.providedBy(subscriber): if subscriber.preferred_address is None: bad_request(response, b'User without preferred address') return email = subscriber.preferred_address.email else: assert IAddress.providedBy(subscriber) email = subscriber.email delivery_mode = arguments.pop('delivery_mode', DeliveryMode.regular) record = RequestRecord(email, display_name, delivery_mode) try: member = add_member(mlist, record, role) except MembershipIsBannedError: bad_request(response, b'Membership is banned') return except AlreadySubscribedError: bad_request( response, '{} is already an {} of {}'.format(email, role.name, mlist.fqdn_listname)) return # The subscription completed. Let's get the resulting member # and return the location to the new member. Member ids are # UUIDs and need to be converted to URLs because JSON doesn't # directly support UUIDs. member_id = self.api.from_uuid(member.member_id) location = self.api.path_to('members/{}'.format(member_id)) created(response, location)