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())
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
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
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"
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]
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'
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)
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)
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)