Пример #1
0
def remove_from_basket_task(instance_id):
    """Remove from Basket Task.

    This task unsubscribes a user to Basket. The task retries on
    failure at most BASKET_TASK_MAX_RETRIES times and if it finally
    doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    from models import UserProfile
    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED:
        return

    try:
        basket.unsubscribe(instance.basket_token, instance.user.email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException), exception:
        try:
            remove_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('subscribe', instance.user.email,
                                   exception.message)
Пример #2
0
def unsubscribe_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user from the Mozillians newsletter.
    The task retries on failure at most BASKET_TASK_MAX_RETRIES times
    and if it finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # IMPLEMENTATION NOTE:
    #
    # This task might run AFTER the User has been deleted, so it can't
    # look anything up about the user locally. It has to make do
    # with the email and token passed in.

    if not BASKET_ENABLED:
        return

    try:
        if not basket_token:
            # We don't have this user's token yet, and we need it to
            # unsubscribe.  Ask basket for it
            basket_token = basket.lookup_user(email=email)['token']

        basket.unsubscribe(basket_token,
                           email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            unsubscribe_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('unsubscribe', email, exception.message)
Пример #3
0
def newsletter_subscribe(locale, email, cleaned_data):
    subscription_details = get_subscription_details(email)
    subscribed = subscribed_to_newsletter(subscription_details)

    if cleaned_data['newsletter'] and not subscribed:
        if not cleaned_data['agree']:
            raise forms.ValidationError(PRIVACY_REQUIRED)
        optin = 'N'
        if locale == 'en-US':
            optin = 'Y'
        for i in range(constance.config.BASKET_RETRIES):
            try:
                result = basket.subscribe(
                        email=email,
                        newsletters=settings.BASKET_APPS_NEWSLETTER,
                        country=cleaned_data['country'],
                        format=cleaned_data['format'],
                        lang=locale,
                        optin=optin)
                if result.get('status') != 'error':
                    break
            except BasketException:
                if i == constance.config.BASKET_RETRIES:
                    return HttpResponseServerError()
                else:
                    time.sleep(constance.config.BASKET_RETRY_WAIT * i)
    elif subscription_details:
        basket.unsubscribe(subscription_details['token'], email,
                           newsletters=settings.BASKET_APPS_NEWSLETTER)
Пример #4
0
 def subscribe(self, request, email):
     subscription_details = self.get_subscription_details(email)
     if 'subscribe_needed' in self.cleaned_data:
         optin = 'N'
         if request.locale == 'en-US':
             optin = 'Y'
         basket_data = {
             'email': email,
             'newsletters': settings.BASKET_APPS_NEWSLETTER,
             'country': self.cleaned_data['country'],
             'format': self.cleaned_data['format'],
             'lang': request.locale,
             'optin': optin,
             'source_url': request.build_absolute_uri()
         }
         for retry in range(config.BASKET_RETRIES):
             try:
                 result = basket.subscribe(**basket_data)
                 if result.get('status') != 'error':
                     break
             except BasketException:
                 if retry == config.BASKET_RETRIES:
                     return HttpResponseServerError()
                 else:
                     time.sleep(config.BASKET_RETRY_WAIT * retry)
     elif subscription_details:
         basket.unsubscribe(subscription_details['token'],
                            email,
                            newsletters=settings.BASKET_APPS_NEWSLETTER)
Пример #5
0
def remove_from_basket_task(instance_id):
    """Remove from Basket Task.

    This task unsubscribes a user to Basket. The task retries on
    failure at most BASKET_TASK_MAX_RETRIES times and if it finally
    doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    from models import UserProfile
    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED:
        return

    try:
        basket.unsubscribe(instance.basket_token, instance.user.email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException), exception:
        try:
            remove_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('subscribe', instance.user.email,
                                   exception.message)
Пример #6
0
 def subscribe(self, request, email):
     subscription_details = self.get_subscription_details(email)
     if 'subscribe_needed' in self.cleaned_data:
         optin = 'N'
         if request.locale == 'en-US':
             optin = 'Y'
         basket_data = {
             'email': email,
             'newsletters': settings.BASKET_APPS_NEWSLETTER,
             'country': self.cleaned_data['country'],
             'format': self.cleaned_data['format'],
             'lang': request.locale,
             'optin': optin,
             'source_url': request.build_absolute_uri()
         }
         for retry in range(config.BASKET_RETRIES):
             try:
                 result = basket.subscribe(**basket_data)
                 if result.get('status') != 'error':
                     break
             except BasketException:
                 if retry == config.BASKET_RETRIES:
                     return HttpResponseServerError()
                 else:
                     time.sleep(config.BASKET_RETRY_WAIT * retry)
     elif subscription_details:
         basket.unsubscribe(subscription_details['token'], email,
                            newsletters=settings.BASKET_APPS_NEWSLETTER)
Пример #7
0
def unsubscribe_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user from the Mozillians newsletter.
    The task retries on failure at most BASKET_TASK_MAX_RETRIES times
    and if it finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # IMPLEMENTATION NOTE:
    #
    # This task might run AFTER the User has been deleted, so it can't
    # look anything up about the user locally. It has to make do
    # with the email and token passed in.

    if not BASKET_ENABLED:
        return

    try:
        if not basket_token:
            # We don't have this user's token yet, and we need it to
            # unsubscribe.  Ask basket for it
            basket_token = basket.lookup_user(email=email)['token']

        basket.unsubscribe(basket_token, email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            unsubscribe_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('unsubscribe', email, exception.message)
Пример #8
0
def newsletter_subscribe(request, email, cleaned_data):
    subscription_details = get_subscription_details(email)
    subscribed = subscribed_to_newsletter(subscription_details)

    if cleaned_data['newsletter'] and not subscribed:
        if not cleaned_data['agree']:
            raise forms.ValidationError(PRIVACY_REQUIRED)
        optin = 'N'
        if request.locale == 'en-US':
            optin = 'Y'
        for i in range(constance.config.BASKET_RETRIES):
            try:
                result = basket.subscribe(
                    email=email,
                    newsletters=settings.BASKET_APPS_NEWSLETTER,
                    country=cleaned_data['country'],
                    format=cleaned_data['format'],
                    lang=request.locale,
                    optin=optin,
                    source_url=request.build_absolute_uri())
                if result.get('status') != 'error':
                    break
            except BasketException:
                if i == constance.config.BASKET_RETRIES:
                    return HttpResponseServerError()
                else:
                    time.sleep(constance.config.BASKET_RETRY_WAIT * i)
    elif subscription_details:
        basket.unsubscribe(subscription_details['token'],
                           email,
                           newsletters=settings.BASKET_APPS_NEWSLETTER)
Пример #9
0
 def subscribe(self, request, email):
     subscription_details = self.get_subscription_details(email)
     if "subscribe_needed" in self.cleaned_data:
         optin = "N"
         if request.locale == "en-US":
             optin = "Y"
         basket_data = {
             "email": email,
             "newsletters": settings.BASKET_APPS_NEWSLETTER,
             "country": self.cleaned_data["country"],
             "format": self.cleaned_data["format"],
             "lang": request.locale,
             "optin": optin,
             "source_url": request.build_absolute_uri(),
         }
         for retry in range(config.BASKET_RETRIES):
             try:
                 result = basket.subscribe(**basket_data)
                 if result.get("status") != "error":
                     break
             except BasketException:
                 if retry == config.BASKET_RETRIES:
                     return HttpResponseServerError()
                 else:
                     time.sleep(config.BASKET_RETRY_WAIT * retry)
     elif subscription_details:
         basket.unsubscribe(subscription_details["token"], email, newsletters=settings.BASKET_APPS_NEWSLETTER)
Пример #10
0
 def test_unsubscribe_bad_args(self):
     """
     unsubscribe must be passed newsletters or optout, or it raises
     BasketException
     """
     email = "*****@*****.**"
     newsletters = None
     token = "TOKEN"
     optout = None
     with patch('basket.base.request', autospec=True):
         with self.assertRaises(BasketException):
             unsubscribe(token, email, newsletters, optout)
Пример #11
0
 def test_unsubscribe_bad_args(self):
     """
     unsubscribe must be passed newsletters or optout, or it raises
     BasketException
     """
     email = "*****@*****.**"
     newsletters = None
     token = "TOKEN"
     optout = None
     with patch('basket.base.request', autospec=True):
         with self.assertRaises(BasketException):
             unsubscribe(token, email, newsletters, optout)
Пример #12
0
def unsubscribe_user_task(self, result, newsletters=[], optout=False):
    """Removes a user from the Basket subscription."""

    if not result:
        return None

    newsletters_to_unsubscribe = []
    if result.get('status') == 'ok':
        email = result.get('email')
        token = result.get('token')

        # only unsubscribe from our newsletters
        if newsletters:
            newsletters_to_unsubscribe = list(set(newsletters).intersection(result['newsletters']))
        else:
            newsletters_to_unsubscribe = list(set(MOZILLIANS_NEWSLETTERS)
                                              .intersection(result['newsletters']))

    # Unsubscribe the calculated newsletters
    if newsletters_to_unsubscribe:
        try:
            unsubscribe_result = basket.unsubscribe(token=token,
                                                    email=email,
                                                    newsletters=newsletters_to_unsubscribe,
                                                    optout=optout)
        except MaxRetriesExceededError as exc:
            raise exc
        except basket.BasketException as exc:
            raise self.retry(exc=exc)
        return unsubscribe_result
    return None
def unsubscribe_user_task(self, result, newsletters=[], optout=False):
    """Removes a user from the Basket subscription."""

    if not result:
        return None

    newsletters_to_unsubscribe = []
    if result.get('status') == 'ok':
        email = result.get('email')
        token = result.get('token')

        # only unsubscribe from our newsletters
        if newsletters:
            newsletters_to_unsubscribe = list(
                set(newsletters).intersection(result['newsletters']))
        else:
            newsletters_to_unsubscribe = list(
                set(MOZILLIANS_NEWSLETTERS).intersection(
                    result['newsletters']))

    # Unsubscribe the calculated newsletters
    if newsletters_to_unsubscribe:
        try:
            unsubscribe_result = basket.unsubscribe(
                token=token,
                email=email,
                newsletters=newsletters_to_unsubscribe,
                optout=optout)
        except MaxRetriesExceededError as exc:
            raise exc
        except basket.BasketException as exc:
            raise self.retry(exc=exc)
        return unsubscribe_result
    return None
Пример #14
0
def remove_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user to Basket. The task retries on
    failure at most BASKET_TASK_MAX_RETRIES times and if it finally
    doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    if not BASKET_ENABLED:
        return

    if not basket_token:
        # We don't have this user's token yet, ask basket for it
        user = User.objects.get(email=email)
        try:
            basket_token = user.userprofile.lookup_basket_token()
        except basket.BasketException as exception:
            try:
                remove_from_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', email, exception.message)
            return
        if not basket_token:
            logger.error('No user found with email %s' % email)
            return

        # Remember the token
        # Since UserProfile.save() will trigger another update_basket_task
        # via a post-save signal, avoid calling save()
        from models import UserProfile
        UserProfile.objects.filter(user__email=email).update(
            basket_token=basket_token)

    try:
        basket.unsubscribe(basket_token,
                           email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException), exception:
        try:
            remove_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('subscribe', email, exception.message)
Пример #15
0
def remove_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user to Basket. The task retries on
    failure at most BASKET_TASK_MAX_RETRIES times and if it finally
    doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    if not BASKET_ENABLED:
        return

    if not basket_token:
        # We don't have this user's token yet, ask basket for it
        user = User.objects.get(email=email)
        try:
            basket_token = user.userprofile.lookup_basket_token()
        except basket.BasketException as exception:
            try:
                remove_from_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', email, exception.message)
            return
        if not basket_token:
            logger.error('No user found with email %s' % email)
            return

        # Remember the token
        # Since UserProfile.save() will trigger another update_basket_task
        # via a post-save signal, avoid calling save()
        from models import UserProfile
        UserProfile.objects.filter(user__email=email).update(basket_token=basket_token)

    try:
        basket.unsubscribe(basket_token, email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException), exception:
        try:
            remove_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('subscribe', email, exception.message)
Пример #16
0
def remove_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user to Basket. The task retries on
    failure at most BASKET_TASK_MAX_RETRIES times and if it finally
    doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    if not BASKET_ENABLED or not basket_token:
        return

    try:
        basket.unsubscribe(basket_token, email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException), exception:
        try:
            remove_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('subscribe', email, exception.message)
Пример #17
0
def unsubscribe_newsletter(user_profile, basket_id):
    # Security check, the basket token will be set by
    # `fetch_subscribed_newsletters` but since we shouldn't simply error
    # we just fetch it in case something went wrong.
    if not user_profile.basket_token:
        sync_user_with_basket(user_profile)

    # If we still don't have a basket token we can't unsubscribe.
    # This usually means the user doesn't exist in basket yet, which
    # is more or less identical with not being subscribed to any
    # newsletters.
    if user_profile.basket_token:
        response = basket.unsubscribe(
            user_profile.basket_token, user_profile.email, basket_id)
        return response['status'] == 'ok'
    return False
Пример #18
0
def unsubscribe_newsletter(user_profile, basket_id):
    # Security check, the basket token will be set by
    # `fetch_subscribed_newsletters` but since we shouldn't simply error
    # we just fetch it in case something went wrong.
    if not user_profile.basket_token:
        sync_user_with_basket(user_profile)

    # If we still don't have a basket token we can't unsubscribe.
    # This usually means the user doesn't exist in basket yet, which
    # is more or less identical with not being subscribed to any
    # newsletters.
    if user_profile.basket_token:
        response = basket.unsubscribe(
            user_profile.basket_token, user_profile.email, basket_id)
        return response['status'] == 'ok'
    return False
Пример #19
0
    def test_unsubscribe_optout(self):
        """
        unsubscribe calls request with the expected parms and returns the
        result. optout is passed if true, instead of newsletters.
        """
        email = "*****@*****.**"
        newsletters = ['news1', 'news2']
        token = "TOKEN"
        optout = True
        expected_data = {'email': email, 'optout': 'Y'}
        with patch('basket.base.request', autospec=True) as request_call:
            result = unsubscribe(token, email, newsletters, optout)

        request_call.assert_called_with('post',
                                        'unsubscribe',
                                        data=expected_data,
                                        token=token)
        self.assertEqual(request_call.return_value, result)
Пример #20
0
    def test_unsubscribe(self):
        """
        unsubscribe calls request with the expected parms, returns the result
        """
        email = "*****@*****.**"
        newsletters = ['news1', 'news2']
        token = "TOKEN"
        optout = False
        expected_data = {
            'email': email,
            'newsletters': newsletters,
        }
        with patch('basket.base.request', autospec=True) as request_call:
            result = unsubscribe(token, email, newsletters, optout)

        request_call.assert_called_with('post', 'unsubscribe',
                                        data=expected_data,
                                        token=token)
        self.assertEqual(request_call.return_value, result)
Пример #21
0
def existing(request, token=None):
    """Manage subscriptions.  If token is provided, user can manage their
    existing subscriptions, to subscribe, unsubscribe, change email or
    language preferences, etc.  If no token is provided, user can
    fill in their email and language preferences and sign up for
    newsletters.

    @param HTTPRequest request: Django request object
    @param string token: A UUID that identifies this user to the backend. It's
    sent to users in each newsletter as part of a link to this page, so they
    can manage their subscriptions without needing an account somewhere with
    userid & password.
    """
    locale = getattr(request, 'locale', 'en-US')

    if not token:
        return redirect(reverse('newsletter.recovery'))

    if not UUID_REGEX.match(token):
        # Bad token
        messages.add_message(request, messages.ERROR, bad_token)
        # Redirect to the recovery page
        return redirect(reverse('newsletter.recovery'))

    unsub_parm = None

    # Example user:
    #
    # {u'lang': u'en',
    #  u'format': u'H',
    #  u'country': u'us',
    #  u'newsletters': [u'firefox-tips', u'mobile'],
    #  u'created-date': u'1/30/2013 12:46:05 PM',
    #  u'token': u'some-uuid',
    #  u'email': u'*****@*****.**'
    # }

    user_exists = False
    if token:
        try:
            user = basket.user(token)
        except basket.BasketNetworkException:
            # Something wrong with basket backend, no point in continuing,
            # we'd probably fail to subscribe them anyway.
            log.exception("Basket timeout")
            messages.add_message(request, messages.ERROR, general_error)
            return l10n_utils.render(request, 'newsletter/existing.html')
        except basket.BasketException as e:
            log.exception("FAILED to get user from token (%s)", e.desc)
        else:
            user_exists = True

    if not user_exists:
        # Bad or no token
        messages.add_message(request, messages.ERROR, bad_token)
        # Redirect to the recovery page
        return redirect(reverse('newsletter.recovery'))

    # Get the newsletter data - it's a dictionary of dictionaries
    newsletter_data = utils.get_newsletters()

    # Figure out which newsletters to display, and whether to show them
    # as already subscribed.
    initial = []
    for newsletter, data in newsletter_data.iteritems():
        # Only show a newsletter if it has ['active'] == True and
        # ['show'] == True or the user is already subscribed
        if not data.get('active', False):
            continue
        if data.get('show', False) or newsletter in user['newsletters']:
            langs = data['languages']
            nstrings = NEWSLETTER_STRINGS.get(newsletter)
            if nstrings:
                title = nstrings['title']
                description = nstrings.get('description', u'')
            else:
                # Firefox Marketplace for Desktop/Android/Firefox OS should be
                # shorten in the titles
                title = _(data['title'].replace('Firefox Marketplace for ', ''))
                description = _(data['description'])

            form_data = {
                'title': Markup(title),
                'subscribed_radio': newsletter in user['newsletters'],
                'subscribed_check': newsletter in user['newsletters'],
                'newsletter': newsletter,
                'description': Markup(description),
                'english_only': len(langs) == 1 and langs[0].startswith('en'),
            }
            if 'order' in data:
                form_data['order'] = data['order']
            initial.append(form_data)

    # Sort by 'order' field if we were given it; otherwise, by title
    if initial:
        keyfield = 'order' if 'order' in initial[0] else 'title'
        initial.sort(key=itemgetter(keyfield))

    NewsletterFormSet = formset_factory(NewsletterForm, extra=0,
                                        max_num=len(initial))

    if request.method == 'POST':
        form_kwargs = {}

        # Temporary form so we can see if they checked 'remove_all'.  If
        # they did, no point in validating the newsletters formset and it would
        # look dumb to complain about it.
        form = ManageSubscriptionsForm(locale, data=request.POST, initial=user)
        remove_all = form.is_valid() and form.cleaned_data['remove_all']

        formset_is_valid = False

        if remove_all:
            # We don't care about the newsletter formset
            formset_is_valid = True
            # Make an initialized one in case we fall through to the bottom
            formset = NewsletterFormSet(initial=initial)
        else:
            # We do need to validate the newsletter formset
            formset = NewsletterFormSet(request.POST, initial=initial)
            # Set `newsletters` to the list of newsletters they want.
            # After this, we don't need the formset anymore.
            newsletters = None
            if formset.is_valid():
                formset_is_valid = True
                # What newsletters do they say they want to be subscribed to?
                newsletters = set([subform.cleaned_data['newsletter']
                                   for subform in formset
                                   if (subform.cleaned_data['subscribed_radio'] or
                                       subform.cleaned_data['subscribed_check'])])
                form_kwargs['newsletters'] = newsletters

        form = ManageSubscriptionsForm(locale, data=request.POST, initial=user,
                                       **form_kwargs)

        if formset_is_valid and form.is_valid():

            data = form.cleaned_data

            # Update their format and locale information, if it has changed.
            # Also pass their updated list of newsletters they want to be
            # subscribed to, for basket to implement.
            kwargs = {}
            for k in ['lang', 'format', 'country']:
                if user[k] != data[k]:
                    kwargs[k] = data[k]
            if not remove_all:
                kwargs['newsletters'] = ",".join(newsletters)
            if kwargs:
                try:
                    basket.update_user(token, **kwargs)
                except basket.BasketException:
                    log.exception("Error updating user in basket")
                    messages.add_message(
                        request, messages.ERROR, general_error
                    )
                    return l10n_utils.render(request,
                                             'newsletter/existing.html')

            # If they chose to remove all, tell basket that they've opted out
            if remove_all:
                try:
                    basket.unsubscribe(token, user['email'], optout=True)
                except (basket.BasketException, requests.Timeout):
                    log.exception("Error updating subscriptions in basket")
                    messages.add_message(
                        request, messages.ERROR, general_error
                    )
                    return l10n_utils.render(request,
                                             'newsletter/existing.html')
                # We need to pass their token to the next view
                url = reverse('newsletter.updated') \
                    + "?unsub=%s&token=%s" % (UNSUB_UNSUBSCRIBED_ALL, token)
                return redirect(url)

            # We're going to redirect, so the only way to tell the next
            # view that we should display the welcome message in the
            # template is to modify the URL
            url = reverse('newsletter.updated')
            if unsub_parm:
                url += "?unsub=%s" % unsub_parm
            return redirect(url)

        # FALL THROUGH so page displays errors
    else:
        form = ManageSubscriptionsForm(
            locale, initial=user
        )
        formset = NewsletterFormSet(initial=initial)

    # For the template, we want a dictionary whose keys are language codes
    # and each value is the list of newsletter keys that are available in
    # that language code.
    newsletter_languages = defaultdict(list)
    for newsletter, data in newsletter_data.iteritems():
        for lang in data['languages']:
            newsletter_languages[lang].append(newsletter)
    newsletter_languages = mark_safe(json.dumps(newsletter_languages))

    # We also want a list of the newsletters the user is already subscribed
    # to
    already_subscribed = mark_safe(json.dumps(user['newsletters']))

    context = {
        'form': form,
        'formset': formset,
        'newsletter_languages': newsletter_languages,
        'newsletters_subscribed': already_subscribed,
        'email': user['email'],
    }
    return l10n_utils.render(request,
                             'newsletter/existing.html',
                             context)
Пример #22
0
def existing(request, token=None):
    """Manage subscriptions.  If token is provided, user can manage their
    existing subscriptions, to subscribe, unsubscribe, change email or
    language preferences, etc.  If no token is provided, user can
    fill in their email and language preferences and sign up for
    newsletters.

    @param HTTPRequest request: Django request object
    @param string token: A UUID that identifies this user to the backend. It's
    sent to users in each newsletter as part of a link to this page, so they
    can manage their subscriptions without needing an account somewhere with
    userid & password.
    """
    locale = getattr(request, "locale", "en-US")

    unsub_parm = None

    # Example user:
    #
    # {u'lang': u'en',
    #  u'format': u'H',
    #  u'country': u'us',
    #  u'newsletters': [u'firefox-tips', u'mobile'],
    #  u'created-date': u'1/30/2013 12:46:05 PM',
    #  u'token': u'some-uuid',
    #  u'email': u'*****@*****.**'
    # }

    user_exists = False
    if token:
        try:
            user = basket.user(token)
        except basket.BasketException:
            log.exception("FAILED to get user from token")
        except requests.Timeout:
            # Something wrong with basket backend, no point in continuing,
            # we'd probably fail to subscribe them anyway.
            log.exception("Basket timeout")
            messages.add_message(request, messages.ERROR, general_error)
            return l10n_utils.render(request, "newsletter/existing.html")
        else:
            user_exists = True

    if not user_exists:
        # Bad or no token
        messages.add_message(request, messages.ERROR, bad_token)
        # If we don't pass the template a formset, most of the page won't
        # be displayed. Our message still will be displayed.
        return l10n_utils.render(request, "newsletter/existing.html", {})

    # Get the newsletter data - it's a dictionary of dictionaries
    newsletter_data = utils.get_newsletters()

    # Figure out which newsletters to display, and whether to show them
    # as already subscribed.
    initial = []
    for newsletter, data in newsletter_data.iteritems():
        # Only show a newsletter if it has ['show'] == True or the
        # user is already subscribed
        if data.get("show", False) or newsletter in user["newsletters"]:
            langs = data["languages"]
            initial.append(
                {
                    "title": data["title"],
                    "subscribed": newsletter in user["newsletters"],
                    "newsletter": newsletter,
                    "description": data["description"],
                    "english_only": len(langs) == 1 and langs[0].startswith("en"),
                    "order": data["order"],
                }
            )

    # Sort by 'order' field if we were given it; otherwise, by title
    if initial:
        keyfield = "order" if "order" in initial[0] else "title"
        initial.sort(key=itemgetter(keyfield))

    NewsletterFormSet = formset_factory(NewsletterForm, extra=0, max_num=len(initial))

    if request.method == "POST":
        form_kwargs = {}

        # Temporary form so we can see if they checked 'remove_all'.  If
        # they did, no point in validating the newsletters formset and it would
        # look dumb to complain about it.
        form = ManageSubscriptionsForm(locale, data=request.POST, initial=user)
        remove_all = form.is_valid() and form.cleaned_data["remove_all"]

        formset_is_valid = False

        if remove_all:
            # We don't care about the newsletter formset
            formset_is_valid = True
            # Make an initialized one in case we fall through to the bottom
            formset = NewsletterFormSet(initial=initial)
        else:
            # We do need to validate the newsletter formset
            formset = NewsletterFormSet(request.POST, initial=initial)
            # Set `newsletters` to the list of newsletters they want.
            # After this, we don't need the formset anymore.
            newsletters = None
            if formset.is_valid():
                formset_is_valid = True
                # What newsletters do they say they want to be subscribed to?
                newsletters = set(
                    [subform.cleaned_data["newsletter"] for subform in formset if subform.cleaned_data["subscribed"]]
                )
                form_kwargs["newsletters"] = newsletters

        form = ManageSubscriptionsForm(locale, data=request.POST, initial=user, **form_kwargs)

        if formset_is_valid and form.is_valid():

            data = form.cleaned_data

            # Update their format and locale information, if it has changed.
            # Also pass their updated list of newsletters they want to be
            # subscribed to, for basket to implement.
            kwargs = {}
            for k in ["lang", "format", "country"]:
                if user[k] != data[k]:
                    kwargs[k] = data[k]
            if not remove_all:
                kwargs["newsletters"] = ",".join(newsletters)
            if kwargs:
                try:
                    basket.update_user(token, **kwargs)
                except basket.BasketException:
                    log.exception("Error updating user in basket")
                    messages.add_message(request, messages.ERROR, general_error)
                    return l10n_utils.render(request, "newsletter/existing.html")
                messages.add_message(request, messages.INFO, thank_you)

            # If they chose to remove all, tell basket that they've opted out
            if remove_all:
                try:
                    basket.unsubscribe(token, user["email"], optout=True)
                except (basket.BasketException, requests.Timeout):
                    log.exception("Error updating subscriptions in basket")
                    messages.add_message(request, messages.ERROR, general_error)
                    return l10n_utils.render(request, "newsletter/existing.html")
                if not kwargs:
                    # We haven't told them thank you yet
                    messages.add_message(request, messages.INFO, thank_you)
                # We need to pass their token to the next view
                url = reverse("newsletter.updated") + "?unsub=%s&token=%s" % (UNSUB_UNSUBSCRIBED_ALL, token)
                return redirect(url)

            # We're going to redirect, so the only way to tell the next
            # view that we should display the welcome message in the
            # template is to modify the URL
            url = reverse("newsletter.updated")
            if unsub_parm:
                url += "?unsub=%s" % unsub_parm
            return redirect(url)

        # FALL THROUGH so page displays errors
    else:
        form = ManageSubscriptionsForm(locale, initial=user)
        formset = NewsletterFormSet(initial=initial)

    # For the template, we want a dictionary whose keys are language codes
    # and each value is the list of newsletter keys that are available in
    # that language code.
    newsletter_languages = defaultdict(list)
    for newsletter, data in newsletter_data.iteritems():
        for lang in data["languages"]:
            newsletter_languages[lang].append(newsletter)
    newsletter_languages = mark_safe(json.dumps(newsletter_languages))

    # We also want a list of the newsletters the user is already subscribed
    # to
    already_subscribed = mark_safe(json.dumps(user["newsletters"]))

    context = {
        "form": form,
        "formset": formset,
        "newsletter_languages": newsletter_languages,
        "newsletters_subscribed": already_subscribed,
        "email": user["email"],
    }
    return l10n_utils.render(request, "newsletter/existing.html", context)
Пример #23
0
def update_basket_task(instance_id, newsletters=[]):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if (not BASKET_ENABLED or not instance or not newsletters
            or not waffle.switch_is_active('BASKET_SWITCH_ENABLED')):
        return

    email = instance.user.email
    token = instance.basket_token
    newsletters_to_subscribe = []

    if token:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return

        # Check if the users needs to be subscribed to additional newsletters.
        newsletters_to_subscribe = [
            nl for nl in newsletters if nl not in result['newsletters']
        ]

        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    result['newsletters'],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsubscribe all from the old token
                basket.unsubscribe(token=token, email=old_email, optout=True)
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email,
                                           exception.message)
                return

            # That was all successful. Update the token.
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(
                basket_token=token)

    if not token or newsletters_to_subscribe:
        # There is no token or the user is missing newsletters,
        # try to subscribe the user to basket.
        subscribe_to = newsletters_to_subscribe or newsletters
        try:
            retval = basket.subscribe(email,
                                      subscribe_to,
                                      sync='Y',
                                      trigger_welcome='N')
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return

        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(
            basket_token=retval['token'])
Пример #24
0
def update_basket_task(instance_id, newsletters=[]):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if (not BASKET_ENABLED or not instance or not newsletters or
            not waffle.switch_is_active('BASKET_SWITCH_ENABLED')):
        return

    email = instance.user.email
    token = instance.basket_token
    newsletters_to_subscribe = []

    if token:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return

        # Check if the users needs to be subscribed to additional newsletters.
        newsletters_to_subscribe = [nl for nl in newsletters if nl not in result['newsletters']]

        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    result['newsletters'],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsubscribe all from the old token
                basket.unsubscribe(token=token, email=old_email, optout=True)
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email, exception.message)
                return

            # That was all successful. Update the token.
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)

    if not token or newsletters_to_subscribe:
        # There is no token or the user is missing newsletters,
        # try to subscribe the user to basket.
        subscribe_to = newsletters_to_subscribe or newsletters
        try:
            retval = basket.subscribe(
                email,
                subscribe_to,
                sync='Y',
                trigger_welcome='N'
            )
        except (requests.exceptions.RequestException, basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email, exception.message)
            return

        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=retval['token'])
Пример #25
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if not BASKET_ENABLED or not instance or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(
                email,
                [settings.BASKET_NEWSLETTER],
                sync='Y',
                trigger_welcome='N'
            )
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return
        # Remember the token
        instance.basket_token = retval['token']
        token = retval['token']
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return
        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    [settings.BASKET_NEWSLETTER],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsub all from the old token
                basket.unsubscribe(token=token, email=old_email,
                                   newsletters=[settings.BASKET_NEWSLETTER], optout='Y')
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email, exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result['token']
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
Пример #26
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(email, [settings.BASKET_NEWSLETTER],
                                      sync='Y',
                                      trigger_welcome='N')
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return
        # Remember the token
        instance.basket_token = retval['token']
        token = retval['token']
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return
        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    [settings.BASKET_NEWSLETTER],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsub all from the old token
                basket.unsubscribe(token=token,
                                   email=old_email,
                                   newsletters=[settings.BASKET_NEWSLETTER],
                                   optout='Y')
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email,
                                           exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result['token']
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(
                basket_token=token)

    GroupMembership = get_model('groups', 'GroupMembership')
    Group = get_model('groups', 'Group')
    data = {}
    # What groups is the user in?
    user_group_pks = (instance.groups.filter(
        groupmembership__status=GroupMembership.MEMBER).values_list('pk',
                                                                    flat=True))
    for group in Group.objects.filter(functional_area=True):
        name = group.name.upper().replace(' ', '_')
        data[name] = 'Y' if group.id in user_group_pks else 'N'

    # User location if known
    if instance.geo_country:
        data['country'] = instance.geo_country.code
    if instance.geo_city:
        data['city'] = instance.geo_city.name

    # We have a token, proceed with the update
    try:
        basket.request('post',
                       'custom_update_phonebook',
                       token=token,
                       data=data)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            update_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('update_phonebook', email,
                                   exception.message)
Пример #27
0
def existing(request, token=None):
    """Manage subscriptions.  If token is provided, user can manage their
    existing subscriptions, to subscribe, unsubscribe, change email or
    language preferences, etc.  If no token is provided, user can
    fill in their email and language preferences and sign up for
    newsletters.

    @param HTTPRequest request: Django request object
    @param string token: A UUID that identifies this user to the backend. It's
    sent to users in each newsletter as part of a link to this page, so they
    can manage their subscriptions without needing an account somewhere with
    userid & password.
    """
    locale = l10n_utils.get_locale(request)

    if not token:
        return redirect(reverse('newsletter.recovery'))

    if not UUID_REGEX.match(token):
        # Bad token
        messages.add_message(request, messages.ERROR, bad_token)
        # Redirect to the recovery page
        return redirect(reverse('newsletter.recovery'))

    if waffle.switch('newsletter-maintenance-mode'):
        return l10n_utils.render(request, 'newsletter/existing.html')

    unsub_parm = None

    # Example user:
    #
    # {u'lang': u'en',
    #  u'format': u'H',
    #  u'country': u'us',
    #  u'newsletters': [u'firefox-tips', u'mobile'],
    #  u'created-date': u'1/30/2013 12:46:05 PM',
    #  u'token': u'some-uuid',
    #  u'email': u'*****@*****.**'
    # }

    has_fxa = 'fxa' in request.GET
    user = None
    if token:
        try:
            # ask for fxa status if not passed in the URL
            params = None if has_fxa else {'fxa': 1}
            user = basket.request('get', 'user', token=token, params=params)
        except basket.BasketNetworkException:
            # Something wrong with basket backend, no point in continuing,
            # we'd probably fail to subscribe them anyway.
            log.exception("Basket timeout")
            messages.add_message(request, messages.ERROR, general_error)
            return l10n_utils.render(request, 'newsletter/existing.html')
        except basket.BasketException as e:
            log.exception("FAILED to get user from token (%s)", e.desc)

    if not user:
        # Bad or no token
        messages.add_message(request, messages.ERROR, bad_token)
        # Redirect to the recovery page
        return redirect(reverse('newsletter.recovery'))

    # if `has_fxa` not returned from basket, set it from the URL
    user.setdefault('has_fxa', has_fxa)
    # Get the newsletter data - it's a dictionary of dictionaries
    newsletter_data = utils.get_newsletters()

    # Figure out which newsletters to display, and whether to show them
    # as already subscribed.
    initial = []
    for newsletter, data in newsletter_data.items():
        # Only show a newsletter if it has ['active'] == True and
        # ['show'] == True or the user is already subscribed
        if not data.get('active', False):
            continue

        if (data.get('show', False) or newsletter in user['newsletters']
                or (user['has_fxa'] and newsletter in settings.FXA_NEWSLETTERS
                    and any(
                        locale.startswith(l)
                        for l in settings.FXA_NEWSLETTERS_LOCALES))):
            langs = data['languages']
            nstrings = NEWSLETTER_STRINGS.get(newsletter)
            if nstrings:
                if newsletter == 'firefox-accounts-journey' and locale.startswith(
                        'en'):
                    # alternate english title
                    title = u'Firefox Account Tips'
                else:
                    title = nstrings['title']
                description = nstrings.get('description', u'')
            else:
                # Firefox Marketplace for Desktop/Android/Firefox OS should be
                # shorten in the titles
                title = _(data['title'].replace('Firefox Marketplace for ',
                                                ''))
                description = _(data['description'])

            form_data = {
                'title': Markup(title),
                'subscribed_radio': newsletter in user['newsletters'],
                'subscribed_check': newsletter in user['newsletters'],
                'newsletter': newsletter,
                'description': Markup(description),
                'english_only': len(langs) == 1 and langs[0].startswith('en'),
                'indented': data.get('indent', False),
            }
            if 'order' in data:
                form_data['order'] = data['order']
            initial.append(form_data)

    # Sort by 'order' field if we were given it; otherwise, by title
    if initial:
        keyfield = 'order' if 'order' in initial[0] else 'title'
        initial.sort(key=itemgetter(keyfield))

    NewsletterFormSet = formset_factory(NewsletterForm,
                                        extra=0,
                                        max_num=len(initial))

    if request.method == 'POST':
        form_kwargs = {}

        # Temporary form so we can see if they checked 'remove_all'.  If
        # they did, no point in validating the newsletters formset and it would
        # look dumb to complain about it.
        form = ManageSubscriptionsForm(locale, data=request.POST, initial=user)
        remove_all = form.is_valid() and form.cleaned_data['remove_all']

        formset_is_valid = False

        if remove_all:
            # We don't care about the newsletter formset
            formset_is_valid = True
            # Make an initialized one in case we fall through to the bottom
            formset = NewsletterFormSet(initial=initial)
        else:
            # We do need to validate the newsletter formset
            formset = NewsletterFormSet(request.POST, initial=initial)
            # Set `newsletters` to the list of newsletters they want.
            # After this, we don't need the formset anymore.
            newsletters = None
            if formset.is_valid():
                formset_is_valid = True
                # What newsletters do they say they want to be subscribed to?
                newsletters = set([
                    subform.cleaned_data['newsletter'] for subform in formset
                    if (subform.cleaned_data['subscribed_radio']
                        or subform.cleaned_data['subscribed_check'])
                ])
                form_kwargs['newsletters'] = newsletters

        form = ManageSubscriptionsForm(locale,
                                       data=request.POST,
                                       initial=user,
                                       **form_kwargs)

        if formset_is_valid and form.is_valid():

            data = form.cleaned_data

            # Update their format and locale information, if it has changed.
            # Also pass their updated list of newsletters they want to be
            # subscribed to, for basket to implement.
            kwargs = {}
            if settings.BASKET_API_KEY:
                kwargs['api_key'] = settings.BASKET_API_KEY
            for k in ['lang', 'format', 'country']:
                if user[k] != data[k]:
                    kwargs[k] = data[k]
            if not remove_all:
                kwargs['newsletters'] = ",".join(newsletters)
            if kwargs:
                # always send lang so basket doesn't try to guess
                kwargs['lang'] = data['lang']
                try:
                    basket.update_user(token, **kwargs)
                except basket.BasketException:
                    log.exception("Error updating user in basket")
                    messages.add_message(request, messages.ERROR,
                                         general_error)
                    return l10n_utils.render(request,
                                             'newsletter/existing.html')

            # If they chose to remove all, tell basket that they've opted out
            if remove_all:
                try:
                    basket.unsubscribe(token, user['email'], optout=True)
                except (basket.BasketException, requests.Timeout):
                    log.exception("Error updating subscriptions in basket")
                    messages.add_message(request, messages.ERROR,
                                         general_error)
                    return l10n_utils.render(request,
                                             'newsletter/existing.html')
                # We need to pass their token to the next view
                url = reverse('newsletter.updated') \
                    + "?unsub=%s&token=%s" % (UNSUB_UNSUBSCRIBED_ALL, token)
                return redirect(url)

            # We're going to redirect, so the only way to tell the next
            # view that we should display the welcome message in the
            # template is to modify the URL
            url = reverse('newsletter.updated')
            if unsub_parm:
                url += "?unsub=%s" % unsub_parm
            return redirect(url)

        # FALL THROUGH so page displays errors
    else:
        form = ManageSubscriptionsForm(locale, initial=user)
        formset = NewsletterFormSet(initial=initial)

    # For the template, we want a dictionary whose keys are language codes
    # and each value is the list of newsletter keys that are available in
    # that language code.
    newsletter_languages = defaultdict(list)
    for newsletter, data in newsletter_data.items():
        for lang in data['languages']:
            newsletter_languages[lang].append(newsletter)
    newsletter_languages = mark_safe(json.dumps(newsletter_languages))

    # We also want a list of the newsletters the user is already subscribed to
    already_subscribed = mark_safe(json.dumps(user['newsletters']))

    context = {
        'did_confirm': request.GET.get('confirm', None) == '1',
        'form': form,
        'formset': formset,
        'newsletter_languages': newsletter_languages,
        'newsletters_subscribed': already_subscribed,
        'email': user['email'],
    }

    return l10n_utils.render(request, 'newsletter/existing.html', context)
Пример #28
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile

    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(email, [settings.BASKET_NEWSLETTER], sync="Y", trigger_welcome="N")
        except (requests.exceptions.RequestException, basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers("subscribe", instance.user.email, exception.message)
            return
        # Remember the token
        instance.basket_token = retval["token"]
        token = retval["token"]
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers("update_phonebook", token, msg)
            return
        old_email = result["email"]
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(email, [settings.BASKET_NEWSLETTER], sync="Y", trigger_welcome="N")
                # unsub all from the old token
                basket.unsubscribe(token=token, email=old_email, newsletters=[settings.BASKET_NEWSLETTER], optout="Y")
            except (requests.exceptions.RequestException, basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers("subscribe", email, exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result["token"]
            token = subscribe_result["token"]
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)

    GroupMembership = get_model("groups", "GroupMembership")
    Group = get_model("groups", "Group")
    data = {}
    # What groups is the user in?
    user_group_pks = instance.groups.filter(groupmembership__status=GroupMembership.MEMBER).values_list("pk", flat=True)
    for group in Group.objects.filter(functional_area=True):
        name = group.name.upper().replace(" ", "_")
        data[name] = "Y" if group.id in user_group_pks else "N"

    # User location if known
    if instance.geo_country:
        data["country"] = instance.geo_country.code
    if instance.geo_city:
        data["city"] = instance.geo_city.name

    # We have a token, proceed with the update
    try:
        basket.request("post", "custom_update_phonebook", token=token, data=data)
    except (requests.exceptions.RequestException, basket.BasketException) as exception:
        try:
            update_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers("update_phonebook", email, exception.message)