Example #1
0
 def test_newsletter_languages(self):
     # newsletter_languages() returns the set of languages
     # of the newsletters
     # (Note that newsletter_languages() is not part of the external
     # API, but is used internally)
     models.Newsletter.objects.create(
         slug='slug',
         title='title',
         active=False,
         languages='en-US',
         vendor_id='VENDOR1',
     )
     models.Newsletter.objects.create(
         slug='slug2',
         title='title',
         active=False,
         languages='fr, de ',
         vendor_id='VENDOR2',
     )
     models.Newsletter.objects.create(
         slug='slug3',
         title='title',
         active=False,
         languages='en-US, fr',
         vendor_id='VENDOR3',
     )
     expect = set(['en-US', 'fr', 'de'])
     self.assertEqual(expect, newsletter_languages())
Example #2
0
def get_accept_languages(header_value):
    """
    Parse the user's Accept-Language HTTP header and return a list of languages
    """
    # adapted from bedrock: http://j.mp/1o3pWo5
    languages = []
    pattern = re.compile(r'^([A-Za-z]{2,3})(?:-([A-Za-z]{2})(?:-[A-Za-z0-9]+)?)?$')

    # bug 1102652
    header_value = header_value.replace('_', '-')

    try:
        parsed = parse_accept_lang_header(header_value)
    except ValueError:  # see https://code.djangoproject.com/ticket/21078
        return languages

    for lang, priority in parsed:
        m = pattern.match(lang)

        if not m:
            continue

        lang = m.group(1).lower()

        # Check if the shorter code is supported. This covers obsolete long
        # codes like fr-FR (should match fr) or ja-JP (should match ja)
        if m.group(2) and lang not in newsletter_languages():
            lang += '-' + m.group(2).upper()

        if lang not in languages:
            languages.append(lang)

    return languages
Example #3
0
def get_accept_languages(header_value):
    """
    Parse the user's Accept-Language HTTP header and return a list of languages
    """
    # adapted from bedrock: http://j.mp/1o3pWo5
    if not header_value:
        return []
    languages = []
    pattern = re.compile(r"^([A-Za-z]{2,3})(?:-([A-Za-z]{2})(?:-[A-Za-z0-9]+)?)?$")

    # bug 1102652
    header_value = header_value.replace("_", "-")

    try:
        parsed = parse_accept_lang_header(header_value)
    except ValueError:  # see https://code.djangoproject.com/ticket/21078
        return languages

    for lang, _priority in parsed:
        m = pattern.match(lang)

        if not m:
            continue

        lang = m.group(1).lower()

        # Check if the shorter code is supported. This covers obsolete long
        # codes like fr-FR (should match fr) or ja-JP (should match ja)
        if m.group(2) and lang not in newsletter_languages():
            lang += "-" + m.group(2).upper()

        if lang not in languages:
            languages.append(lang)

    return languages
Example #4
0
 def test_newsletter_languages(self):
     # newsletter_languages() returns the set of languages
     # of the newsletters
     # (Note that newsletter_languages() is not part of the external
     # API, but is used internally)
     models.Newsletter.objects.create(
         slug='slug',
         title='title',
         active=False,
         languages='en-US',
         vendor_id='VENDOR1',
     )
     models.Newsletter.objects.create(
         slug='slug2',
         title='title',
         active=False,
         languages='fr, de ',
         vendor_id='VENDOR2',
     )
     models.Newsletter.objects.create(
         slug='slug3',
         title='title',
         active=False,
         languages='en-US, fr',
         vendor_id='VENDOR3',
     )
     expect = set(['en-US', 'fr', 'de'])
     self.assertEqual(expect, newsletter_languages())
Example #5
0
def get_best_supported_lang(code):
    """
    Take whatever language code we get from the user and return the best one
    we support for newsletters.
    """
    code = str(code).lower()
    all_langs = [lang.lower() for lang in newsletter_languages()]
    if code in all_langs:
        return _fix_supported_lang(code)

    all_langs_2char = {c[:2]: c for c in all_langs}
    code2 = code[:2]
    if code2 in all_langs_2char:
        return _fix_supported_lang(all_langs_2char[code2])

    return "en"
Example #6
0
def get_best_language(languages):
    """
    Return the best language for use with our newsletters. If none match, return first in list.

    @param languages: list of language codes.
    @return: a single language code
    """
    if not languages:
        return None

    # try again with 2 letter languages
    languages_2l = [lang[:2] for lang in languages]
    supported_langs = newsletter_languages()

    for lang in chain(languages, languages_2l):
        if lang in supported_langs:
            return lang

    return languages[0]
Example #7
0
def get_best_supported_lang(code):
    """
    Take whatever language code we get from the user and return the best one
    we support for newsletters.
    """
    code = str(code).lower()
    all_langs = [l.lower() for l in newsletter_languages()]
    if code in all_langs:
        return _fix_supported_lang(code)

    all_langs_2char = {c[:2]: c for c in all_langs}
    if len(code) > 2:
        code2 = code[:2]
        if code2 in all_langs:
            return _fix_supported_lang(code2)
        if code2 in all_langs_2char:
            return _fix_supported_lang(all_langs_2char[code2])

    return 'en'
Example #8
0
def get_best_language(languages):
    """
    Return the best language for use with our newsletters. If none match, return first in list.

    @param languages: list of language codes.
    @return: a single language code
    """
    if not languages:
        return None

    # try again with 2 letter languages
    languages_2l = [lang[:2] for lang in languages]
    supported_langs = newsletter_languages()

    for lang in chain(languages, languages_2l):
        if lang in supported_langs:
            return lang

    return languages[0]
Example #9
0
def fxa_verified(data):
    """Add new FxA users to SFDC"""
    # if we're not using the sandbox ignore testing domains
    if email_is_testing(data["email"]):
        return

    lang = get_best_language(get_accept_languages(data.get("locale")))
    if not lang or lang not in newsletter_languages():
        lang = "other"

    email = data["email"]
    fxa_id = data["uid"]
    create_date = data.get("createDate", data.get("ts"))
    newsletters = data.get("newsletters")
    metrics = data.get("metricsContext", {})
    new_data = {
        "email": email,
        "source_url": fxa_source_url(metrics),
        "country": data.get("countryCode", ""),
        "fxa_lang": data.get("locale"),
        "fxa_service": data.get("service", ""),
        "fxa_id": fxa_id,
        "optin": True,
        "format": "H",
    }
    if create_date:
        new_data["fxa_create_date"] = iso_format_unix_timestamp(create_date)

    newsletters = newsletters or []
    newsletters.append(settings.FXA_REGISTER_NEWSLETTER)
    new_data["newsletters"] = newsletters

    user_data = get_fxa_user_data(fxa_id, email)
    # don't overwrite the user's language if already set
    if not (user_data and user_data.get("lang")):
        new_data["lang"] = lang

    upsert_contact(SUBSCRIBE, new_data, user_data)
Example #10
0
def fxa_callback(request):
    # remove state from session to prevent multiple attempts
    error_url = f"https://{settings.FXA_EMAIL_PREFS_DOMAIN}/newsletter/fxa-error/"
    sess_state = request.session.pop("fxa_state", None)
    if sess_state is None:
        statsd.incr("news.views.fxa_callback.error.no_state")
        return HttpResponseRedirect(error_url)

    code = request.GET.get("code")
    state = request.GET.get("state")
    if not (code and state):
        statsd.incr("news.views.fxa_callback.error.no_code_state")
        return HttpResponseRedirect(error_url)

    if sess_state != state:
        statsd.incr("news.views.fxa_callback.error.no_state_match")
        return HttpResponseRedirect(error_url)

    fxa_oauth, fxa_profile = get_fxa_clients()
    try:
        access_token = fxa_oauth.trade_code(
            code, ttl=settings.FXA_OAUTH_TOKEN_TTL)["access_token"]
        user_profile = fxa_profile.get_profile(access_token)
    except Exception:
        statsd.incr("news.views.fxa_callback.error.fxa_comm")
        sentry_sdk.capture_exception()
        return HttpResponseRedirect(error_url)

    email = user_profile["email"]
    try:
        user_data = get_user_data(email=email)
    except SalesforceError:
        statsd.incr("news.views.fxa_callback.error.get_user_data")
        sentry_sdk.capture_exception()
        return HttpResponseRedirect(error_url)

    if user_data:
        token = user_data["token"]
    else:
        new_user_data = {
            "email":
            email,
            "optin":
            True,
            "format":
            "H",
            "newsletters": [settings.FXA_REGISTER_NEWSLETTER],
            "source_url":
            f"{settings.FXA_REGISTER_SOURCE_URL}?utm_source=basket-fxa-oauth",
        }
        locale = user_profile.get("locale")
        if locale:
            new_user_data["fxa_lang"] = locale
            lang = get_best_language(get_accept_languages(locale))
            if lang not in newsletter_languages():
                lang = "other"

            new_user_data["lang"] = lang

        try:
            token = upsert_contact(SUBSCRIBE, new_user_data, None)[0]
        except SalesforceError:
            statsd.incr("news.views.fxa_callback.error.upsert_contact")
            sentry_sdk.capture_exception()
            return HttpResponseRedirect(error_url)

    redirect_to = (
        f"https://{settings.FXA_EMAIL_PREFS_DOMAIN}/newsletter/existing/{token}/?fxa=1"
    )
    return HttpResponseRedirect(redirect_to)
Example #11
0
def fxa_callback(request):
    # remove state from session to prevent multiple attempts
    error_url = f'https://{settings.FXA_EMAIL_PREFS_DOMAIN}/newsletter/fxa-error/'
    sess_state = request.session.pop('fxa_state', None)
    if sess_state is None:
        statsd.incr('news.views.fxa_callback.error.no_state')
        return HttpResponseRedirect(error_url)

    code = request.GET.get('code')
    state = request.GET.get('state')
    if not (code and state):
        statsd.incr('news.views.fxa_callback.error.no_code_state')
        return HttpResponseRedirect(error_url)

    if sess_state != state:
        statsd.incr('news.views.fxa_callback.error.no_state_match')
        return HttpResponseRedirect(error_url)

    fxa_oauth, fxa_profile = get_fxa_clients()
    try:
        access_token = fxa_oauth.trade_code(
            code, ttl=settings.FXA_OAUTH_TOKEN_TTL)['access_token']
        user_profile = fxa_profile.get_profile(access_token)
    except Exception:
        statsd.incr('news.views.fxa_callback.error.fxa_comm')
        sentry_sdk.capture_exception()
        return HttpResponseRedirect(error_url)

    email = user_profile['email']
    try:
        user_data = get_user_data(email=email)
    except SalesforceError:
        statsd.incr('news.views.fxa_callback.error.get_user_data')
        sentry_sdk.capture_exception()
        return HttpResponseRedirect(error_url)

    if user_data:
        token = user_data['token']
    else:
        new_user_data = {
            'email':
            email,
            'optin':
            True,
            'format':
            'H',
            'newsletters': [settings.FXA_REGISTER_NEWSLETTER],
            'source_url':
            f'{settings.FXA_REGISTER_SOURCE_URL}?utm_source=basket-fxa-oauth',
        }
        locale = user_profile.get('locale')
        if locale:
            new_user_data['fxa_lang'] = locale
            lang = get_best_language(get_accept_languages(locale))
            if lang not in newsletter_languages():
                lang = 'other'

            new_user_data['lang'] = lang

        try:
            token = upsert_contact(SUBSCRIBE, new_user_data, None)[0]
        except SalesforceError:
            statsd.incr('news.views.fxa_callback.error.upsert_contact')
            sentry_sdk.capture_exception()
            return HttpResponseRedirect(error_url)

    redirect_to = f'https://{settings.FXA_EMAIL_PREFS_DOMAIN}/newsletter/existing/{token}/?fxa=1'
    return HttpResponseRedirect(redirect_to)