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)
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)
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)
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)
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)
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)
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())