Example #1
0
def unsubscribe(request):
    token = request.REQUEST.get('token', '')
    email = request.REQUEST.get('email')

    unsubscribe_all_link = 'http://{}/unsubscribe?'.format(settings.DOMAIN) + urlencode({
        'action': 'ALL',
        'token': util.token(email),
        'email': email,
    })

    ctx = {
        'token': token,
        'email': email,

        'unsubscribed': False,
        'unsubscribed_on_get': False,
        'unsubscribed_settings': None,
        'user': None,
        'error': False,
        'action': request.REQUEST.get('action'),
        'unsubscribe_all_link': unsubscribe_all_link,
    }

    template_name = 'unsubscribe.html'

    if email and util.token(email) == token:
        # No user_id associated with the sent email, unsubscribe this email address from all email
        find_user = User.objects.filter(email=email)
        # If there is one and only one user with that email address, then pick them, otherwise we'll fall back to just an email address
        user = ctx['unsubscribing_user'] = find_user[0] if find_user.count() == 1 else None
    else:
        ctx['error'] = True
        return r2r_jinja(template_name, ctx, request)

    all_actions = EmailChannel.all_handled_actions()

    if user:
        # Support for unsubscribe headers.
        # We support passing in 'actions'
        action = request.REQUEST.get('action')
        if action and (action in EmailChannel.all_handled_actions() or action.upper() == 'ALL'):
            ctx['unsubscribed_on_get'] = ctx['unsubscribed'] = True
            user.kv.subscriptions.unsubscribe(action)
            Metrics.unsubscribe_action.record(request, action=action, method=request.method)

        if request.method == 'POST':
            # Handle the 'ALL' case separately because the semantics for it are inverted.
            # ie, if ALL is checked, it means to DISABLE. While if REMIXED is checked, it means ENABLE.
            handle_unsubscribe_post(user, request.REQUEST, request)

        # We use this dictionary to render the checkboxes in the html.
        ctx['unsubscribed'] = ctx['unsubscribed'] or get_unsubscriptions(user, all_actions)

        ctx['unsubscribed_settings'] = get_unsubscriptions(user)
        template_name = 'unsubscribe_for_user.html'
    else:
        ctx['error'] = True
        return r2r_jinja(template_name, ctx, request)

    return r2r_jinja(template_name, ctx, request)
Example #2
0
    def test_no_reply_notifications_for_inactive_users(self):
        author = create_user()

        comment = create_comment(author=author)

        action_func_list = [Actions.remixed,
                            Actions.replied,
                            Actions.thread_replied]

        # First make sure that the user can receive all possible "comment"
        # related notifications, like replied, remixed ... etc
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertTrue(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))

        author.is_active = False

        author.save()
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertFalse(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))
    def test_no_reply_notifications_for_inactive_users(self):
        author = create_user()

        comment = create_comment(author=author)

        action_func_list = [Actions.remixed,
                            Actions.replied,
                            Actions.thread_replied]

        # First make sure that the user can receive all possible "comment"
        # related notifications, like replied, remixed ... etc
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertTrue(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))

        author.is_active = False

        author.save()
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertFalse(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))
Example #4
0
    def test_24h_digest_email(self):
        user = create_user(email="*****@*****.**")
        pn = Actions.digest(user)
        notification = Notification.from_pending_notification(pn, user, "EmailChannel")

        email_message = EmailChannel().make_message(notification, force=True)
        message = EmailChannel().make_email_backend_message(email_message)
        email_message.record_sent(notification.action)
Example #5
0
    def test_24h_digest_email(self):
        user = create_user(email="*****@*****.**")
        pn = Actions.digest(user)
        notification = Notification.from_pending_notification(pn, user, "EmailChannel")

        email_message = EmailChannel().make_message(notification, force=True)
        message = EmailChannel().make_email_backend_message(email_message)
        email_message.record_sent(notification.action)
Example #6
0
def unsubscribe(request):
    token = request.REQUEST.get('token', '')
    email = request.REQUEST.get('email')

    ctx = {
        'token': token,
        'email': email,
        'unsubscribed': False,
        'unsubscribed_on_get': False,
        'unsubscribed_settings': None,
        'user': None,
        'error': False,
    }

    template_name = 'unsubscribe.html'

    if email and util.token(email) == token:
        # No user_id associated with the sent email, unsubscribe this email address from all email
        find_user = User.objects.filter(email=email)
        # If there is one and only one user with that email address, then pick them, otherwise we'll fall back to just an email address
        user = ctx['user'] = find_user[0] if find_user.count() == 1 else None
    else:
        ctx['error'] = True
        return r2r_jinja(template_name, ctx, request)

    all_actions = EmailChannel.all_handled_actions()

    if user:
        # Support for unsubscribe headers.
        # We support passing in 'actions'
        action = request.REQUEST.get('action')
        if action and (action in EmailChannel.all_handled_actions()
                       or action.upper() == 'ALL'):
            ctx['unsubscribed_on_get'] = ctx['unsubscribed'] = True
            user.kv.subscriptions.unsubscribe(action)
            Metrics.unsubscribe_action.record(request,
                                              action=action,
                                              method=request.method)

        if request.method == 'POST':
            # Handle the 'ALL' case separately because the semantics for it are inverted.
            # ie, if ALL is checked, it means to DISABLE. While if REMIXED is checked, it means ENABLE.
            handle_unsubscribe_post(user, request.REQUEST, request)

        # We use this dictionary to render the checkboxes in the html.
        ctx['unsubscribed'] = ctx['unsubscribed'] or get_unsubscriptions(
            user, all_actions)

        ctx['unsubscribed_settings'] = get_unsubscriptions(user)
        template_name = 'unsubscribe_for_user.html'
    else:
        unsubscribe_newsletter(email)
        ctx['unsubscribed'] = True
        Metrics.unsubscribe_email_address.record(request)

    return r2r_jinja(template_name, ctx, request)
Example #7
0
    def test_not_enabled_if_user_has_no_email(self):
        user = create_user(email="*****@*****.**")
        for action in EmailChannel.recipient_actions:
            assert EmailChannel.enabled_for_recipient_action(action, user)
        for action in EmailChannel.actor_actions:
            assert EmailChannel.enabled_for_actor_action(action, user)

        user.email = ""
        user.save()

        for action in EmailChannel.recipient_actions:
            assert not EmailChannel.enabled_for_recipient_action(action, user)
        for action in EmailChannel.actor_actions:
            assert not EmailChannel.enabled_for_actor_action(action, user)
Example #8
0
    def test_delivering_email_records_email_sent_metric(self):
        with override_service('experiment_placer', FakeExperimentPlacer, kwargs={'email_notifications': 'experimental'}):
            user = create_user(email="*****@*****.**")

            comment = create_comment()
            comment2 = create_comment(author=user, replied_comment=comment)

            pn = Actions.replied(user, comment2)
            notification = Notification.from_pending_notification(pn, user, "EmailChannel")
            channel = EmailChannel()

            with override_service('metrics', FakeMetrics):
                channel.deliver(notification)
                self.assertEqual(1, len(Services.metrics.email_sent.records))
    def test_delivering_email_records_email_sent_metric(self):
        with override_service('experiment_placer', FakeExperimentPlacer, kwargs={'email_notifications': 'experimental'}):
            user = create_user(email="*****@*****.**")

            comment = create_comment()
            comment2 = create_comment(author=user, replied_comment=comment)

            pn = Actions.replied(user, comment2)
            notification = Notification.from_pending_notification(pn, user, "EmailChannel")
            channel = EmailChannel()

            with override_service('metrics', FakeMetrics):
                channel.deliver(notification)
                self.assertEqual(1, len(Services.metrics.email_sent.records))
Example #10
0
    def test_not_enabled_if_user_has_no_email(self):
        user = create_user(email="*****@*****.**")
        for action in EmailChannel.recipient_actions:
            assert EmailChannel.enabled_for_recipient_action(action, user)
        for action in EmailChannel.actor_actions:
            assert EmailChannel.enabled_for_actor_action(action, user)

        user.email = ""
        user.save()

        for action in EmailChannel.recipient_actions:
            assert not EmailChannel.enabled_for_recipient_action(action, user)
        for action in EmailChannel.actor_actions:
            assert not EmailChannel.enabled_for_actor_action(action, user)
 def test_unsubscribe_from_all_semantics(self):
     user = create_user()
     subs = user.kv.subscriptions
     assert subs.can_receive("ALL")
     assert subs.can_receive("ALL")
     
     subs.unsubscribe_from_all()
     assert not subs.can_receive("ALL")
     for a in EmailChannel.all_handled_actions():
         assert not subs.can_receive(a)
     
     subs.subscribe("ALL")
     assert subs.can_receive("ALL")
     assert subs.can_receive("ALL")
     for a in EmailChannel.all_handled_actions():
         assert subs.can_receive(a)
Example #12
0
    def test_unsubscribe_from_all_semantics(self):
        user = create_user()
        subs = user.kv.subscriptions
        assert subs.can_receive("ALL")
        assert subs.can_receive("ALL")

        subs.unsubscribe_from_all()
        assert not subs.can_receive("ALL")
        for a in EmailChannel.all_handled_actions():
            assert not subs.can_receive(a)

        subs.subscribe("ALL")
        assert subs.can_receive("ALL")
        assert subs.can_receive("ALL")
        for a in EmailChannel.all_handled_actions():
            assert subs.can_receive(a)
Example #13
0
def get_unsubscriptions(user, action_list=EmailChannel.all_handled_actions()):
    d = {}
    for action in action_list:
        if not user.kv.subscriptions.can_receive(action):
            d[action] = True
        else:
            d[action] = False

    d['ALL'] = not user.kv.subscriptions.can_receive('ALL')
    return d
    def test_granular_unsubscribe(self):
        all_actions = EmailChannel.all_handled_actions()

        for action in all_actions:
            if action == 'newsletter':
                continue
            u = create_user()
            assert u.kv.subscriptions.can_receive(action)
            actions_dict = {}
            actions_dict = {action: "on"}
            self.validate_unsubscript(actions_dict, u)
            assert u.kv.subscriptions.can_receive(action)
Example #15
0
    def test_granular_unsubscribe(self):
        all_actions = EmailChannel.all_handled_actions()

        for action in all_actions:
            if action == 'newsletter':
                continue
            u = create_user()
            assert u.kv.subscriptions.can_receive(action)
            actions_dict = {}
            actions_dict = {action: "on"}
            self.validate_unsubscript(actions_dict, u)
            assert u.kv.subscriptions.can_receive(action)
Example #16
0
    def test_user_can_unsubscribe_through_header_link(self):
        user = create_user(email="*****@*****.**")
        comment = create_comment()
        comment2 = create_comment(author=user, replied_comment=comment)

        pn = Actions.replied(user, comment2)
        notification = Notification.from_pending_notification(pn, user, "EmailChannel")

        email_message = EmailChannel().make_message(notification, force=True)
        message = EmailChannel().make_email_backend_message(email_message)
        email_message.record_sent(notification.action)

        assert message.extra_headers
        unsubscribe_link = message.extra_headers.get("List-Unsubscribe")
        assert unsubscribe_link

        # Use should be able to receive notifications ...
        self.assertTrue(user.kv.subscriptions.can_receive("replied"))
        # Now, 'curl' the unsubscribe link
        self.assertStatus(200, unsubscribe_link)
        # Now, this action should be disabled in the notifications subscriptions.
        self.assertFalse(user.kv.subscriptions.can_receive("replied"))
Example #17
0
    def test_user_can_unsubscribe_through_header_link(self):
        user = create_user(email="*****@*****.**")
        comment = create_comment()
        comment2 = create_comment(author=user, replied_comment=comment)

        pn = Actions.replied(user, comment2)
        notification = Notification.from_pending_notification(pn, user, "EmailChannel")

        email_message = EmailChannel().make_message(notification, force=True)
        message = EmailChannel().make_email_backend_message(email_message)
        email_message.record_sent(notification.action)

        assert message.extra_headers
        unsubscribe_link = message.extra_headers.get("List-Unsubscribe")
        assert unsubscribe_link

        # Use should be able to receive notifications ...
        self.assertTrue(user.kv.subscriptions.can_receive('replied'))
        # Now, 'curl' the unsubscribe link
        self.assertStatus(200, unsubscribe_link)
        # Now, this action should be disabled in the notifications subscriptions.
        self.assertFalse(user.kv.subscriptions.can_receive('replied'))
    def validate_unsubscript(self, actions_dict, canvas_user=None, all_actions=None):
        if not canvas_user:
            canvas_user = create_user()
        if not all_actions:
            all_actions = EmailChannel.all_handled_actions()

        request = FakeRequest()
        views.handle_unsubscribe_post(canvas_user, actions_dict, request)
        unsubscriptions = views.get_unsubscriptions(canvas_user, all_actions)
        for action in all_actions:
            if action == 'newsletter':
                continue
            value = action
            if action == "ALL":
                value = not action
            if actions_dict.get(action) == "on":
                assert not unsubscriptions.get(value)
            else:
                assert unsubscriptions.get(value)
        return unsubscriptions
    def test_granualr_unsubscribe_blanket_ban(self):
        all_actions = EmailChannel.all_handled_actions()
        # ALL has inverted semantics ... make sure it works.
        all_actions.append("ALL")
        # Reuse the same user
        canvas_user = create_user()
        action = "ALL"
        actions_dict = {action: "on"}
        unsubscriptions = self.validate_unsubscript(actions_dict, canvas_user, all_actions)
        for action in all_actions:
            # Ensure that we unsubscribed from all of them!
            assert unsubscriptions.get(action)

        action = "ALL"
        # Remove blanket subscription
        actions_dict = {}
        request = FakeRequest()
        views.handle_unsubscribe_post(canvas_user, actions_dict, request)
        unsubscriptions = views.get_unsubscriptions(canvas_user, all_actions)
        for action in all_actions:
            # Ensure that the user is now subscribed for everything, which is the default without the blanket ban.
            assert not unsubscriptions.get(action)
Example #20
0
def handle_unsubscribe_post(user, actions_dict, request):
    subscriptions = user.kv.subscriptions
    all_actions = EmailChannel.all_handled_actions()

    unsubscribe_from_all = actions_dict.get('ALL', False)
    if unsubscribe_from_all:
        subscriptions.unsubscribe_from_all()
        Metrics.unsubscribe_all.record(request)
        return
    elif not subscriptions.can_receive('ALL'):
        # Remove the blanket ban, and honor individual preferences.
        subscriptions.subscribe('ALL')
        return

    # Handle the rest of the actions.
    for action in all_actions:
        if bool(actions_dict.get(action, False)):
            subscriptions.subscribe(action)
        else:
            # It was unchecked. So unsubscribe.
            subscriptions.unsubscribe(action)
            Metrics.unsubscribe_action.record(request, action=action, method=request.method)
Example #21
0
    def validate_unsubscript(self,
                             actions_dict,
                             canvas_user=None,
                             all_actions=None):
        if not canvas_user:
            canvas_user = create_user()
        if not all_actions:
            all_actions = EmailChannel.all_handled_actions()

        request = FakeRequest()
        views.handle_unsubscribe_post(canvas_user, actions_dict, request)
        unsubscriptions = views.get_unsubscriptions(canvas_user, all_actions)
        for action in all_actions:
            if action == 'newsletter':
                continue
            value = action
            if action == "ALL":
                value = not action
            if actions_dict.get(action) == "on":
                assert not unsubscriptions.get(value)
            else:
                assert unsubscriptions.get(value)
        return unsubscriptions
Example #22
0
    def test_granualr_unsubscribe_blanket_ban(self):
        all_actions = EmailChannel.all_handled_actions()
        # ALL has inverted semantics ... make sure it works.
        all_actions.append("ALL")
        # Reuse the same user
        canvas_user = create_user()
        action = "ALL"
        actions_dict = {action: "on"}
        unsubscriptions = self.validate_unsubscript(actions_dict, canvas_user,
                                                    all_actions)
        for action in all_actions:
            # Ensure that we unsubscribed from all of them!
            assert unsubscriptions.get(action)

        action = "ALL"
        # Remove blanket subscription
        actions_dict = {}
        request = FakeRequest()
        views.handle_unsubscribe_post(canvas_user, actions_dict, request)
        unsubscriptions = views.get_unsubscriptions(canvas_user, all_actions)
        for action in all_actions:
            # Ensure that the user is now subscribed for everything, which is the default without the blanket ban.
            assert not unsubscriptions.get(action)
Example #23
0
def unsubscribe(request):
    token = request.REQUEST.get('token', '')
    email = request.REQUEST.get('email')
    user_id = request.REQUEST.get('user_id')

    token_user = User.objects.get_or_none(id=user_id) if user_id else None

    unsubscribed = False
    unsubscribed_on_get = False

    if user_id and util.token(user_id) == token and token_user:
        user = token_user

    elif email and util.token(email) == token:
        # No user_id associated with the sent email, unsubscribe this email address from all email
        find_user = User.objects.filter(email=email)
        # If there is one and only one user with that email address, then pick them, otherwise we'll fall back to just an email address
        user = find_user[0] if find_user.count() == 1 else None

    elif request.user.is_authenticated():
        # Token mismatch, but we have a logged in user.
        user = request.user

    else:
        error = True
        return r2r('unsubscribe.django.html', locals())

    all_actions = EmailChannel.all_handled_actions()

    if user:
        subscriptions = user.kv.subscriptions

        # We need to handle any posts that are passed in the URL
        comment_id = request.GET.get('post')
        if comment_id:
            try:
                unsubscribed_post = Comment.objects.get(pk=int(comment_id))
            except ObjectDoesNotExist:
                pass
            else:
                user.redis.mute_thread(unsubscribed_post)
                unsubscribed_from_thread = unsubscribed_on_get = unsubscribed = True
                Metrics.mute_thread.record(request)

        # Support for unsubscribe headers.
        # We support passing in 'actions'
        action = request.REQUEST.get('action')
        if action and action in EmailChannel.all_handled_actions():
            unsubscribed_on_get = unsubscribed = True
            user.kv.subscriptions.unsubscribe(action)
            Metrics.unsubscribe_action.record(request, action=action, method=request.method)

        if request.method == 'POST':
            # Handle the 'ALL' case separately because the semantics for it are inverted.
            # ie, if ALL is checked, it means to DISABLE. While if REMIXED is checked, it means ENABLE.
            handle_unsubscribe_post(user, request.REQUEST, request)

        # We use this dictionary to render the checkboxes in the html.
        unsubscribed = unsubscribed or get_unsubscriptions(user, all_actions)

        unsubscribed_settings = get_unsubscriptions(user)
    else:
        unsubscribe_newsletter(email)
        unsubscribed = True
        Metrics.unsubscribe_email_address.record(request)

    return r2r('unsubscribe.django.html', locals())
Example #24
0
            def get_email_message():
                user = create_user()
                pn = Actions.digest(user)
                notification = Notification.from_pending_notification(pn, user, "EmailChannel")

                return EmailChannel().make_message(notification, force=True)