зеркало из https://github.com/mozilla/bedrock.git
Black format all Python files
This commit is contained in:
Родитель
e8325a7944
Коммит
31943fea75
|
@ -7,6 +7,7 @@ class SimpleDictCache(LocMemCache):
|
|||
Only for use with simple immutable data structures that can be
|
||||
inserted into a dict.
|
||||
"""
|
||||
|
||||
def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
||||
key = self.make_key(key, version=version)
|
||||
self.validate_key(key)
|
||||
|
|
|
@ -5,9 +5,11 @@ from everett.manager import (
|
|||
)
|
||||
|
||||
|
||||
config = ConfigManager([
|
||||
# first check for environment variables
|
||||
ConfigOSEnv(),
|
||||
# then look in the .env file
|
||||
ConfigEnvFileEnv('.env'),
|
||||
])
|
||||
config = ConfigManager(
|
||||
[
|
||||
# first check for environment variables
|
||||
ConfigOSEnv(),
|
||||
# then look in the .env file
|
||||
ConfigEnvFileEnv(".env"),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -5,20 +5,19 @@ from lib.l10n_utils import translation
|
|||
|
||||
|
||||
def geo(request):
|
||||
return {'country_code': get_country_from_request(request)}
|
||||
return {"country_code": get_country_from_request(request)}
|
||||
|
||||
|
||||
def i18n(request):
|
||||
return {
|
||||
'LANGUAGES': settings.LANGUAGES,
|
||||
'LANG': (settings.LANGUAGE_URL_MAP.get(
|
||||
translation.get_language()) or translation.get_language()),
|
||||
'DIR': 'rtl' if translation.get_language_bidi() else 'ltr',
|
||||
"LANGUAGES": settings.LANGUAGES,
|
||||
"LANG": (settings.LANGUAGE_URL_MAP.get(translation.get_language()) or translation.get_language()),
|
||||
"DIR": "rtl" if translation.get_language_bidi() else "ltr",
|
||||
}
|
||||
|
||||
|
||||
def globals(request):
|
||||
return {
|
||||
'request': request,
|
||||
'settings': settings,
|
||||
"request": request,
|
||||
"settings": settings,
|
||||
}
|
||||
|
|
|
@ -8,20 +8,20 @@ from product_details import product_details
|
|||
|
||||
|
||||
def valid_country_code(country):
|
||||
codes = product_details.get_regions('en-US').keys()
|
||||
codes = product_details.get_regions("en-US").keys()
|
||||
if country and country.lower() in codes:
|
||||
return country.upper()
|
||||
|
||||
|
||||
def get_country_from_param(request):
|
||||
is_prod = request.get_host() == 'www.mozilla.org'
|
||||
country_code = valid_country_code(request.GET.get('geo'))
|
||||
is_prod = request.get_host() == "www.mozilla.org"
|
||||
country_code = valid_country_code(request.GET.get("geo"))
|
||||
return country_code if not is_prod else None
|
||||
|
||||
|
||||
def get_country_from_header(request):
|
||||
"""Return an uppercase 2 letter country code retrieved from the request header."""
|
||||
country_code = valid_country_code(request.META.get('HTTP_CF_IPCOUNTRY'))
|
||||
country_code = valid_country_code(request.META.get("HTTP_CF_IPCOUNTRY"))
|
||||
if not country_code and settings.DEV:
|
||||
country_code = settings.DEV_GEO_COUNTRY_CODE
|
||||
|
||||
|
|
|
@ -14,89 +14,86 @@ class NullHandler(logging.Handler):
|
|||
pass
|
||||
|
||||
|
||||
base_fmt = ('%(name)s:%(levelname)s %(message)s '
|
||||
':%(pathname)s:%(lineno)s')
|
||||
base_fmt = "%(name)s:%(levelname)s %(message)s :%(pathname)s:%(lineno)s"
|
||||
use_syslog = settings.HAS_SYSLOG and not settings.DEBUG
|
||||
|
||||
if use_syslog:
|
||||
hostname = socket.gethostname()
|
||||
else:
|
||||
hostname = 'localhost'
|
||||
hostname = "localhost"
|
||||
|
||||
cfg = {
|
||||
'version': 1,
|
||||
'filters': {},
|
||||
'formatters': {
|
||||
'debug': {
|
||||
'()': commonware.log.Formatter,
|
||||
'datefmt': '%H:%M:%s',
|
||||
'format': '%(asctime)s ' + base_fmt,
|
||||
"version": 1,
|
||||
"filters": {},
|
||||
"formatters": {
|
||||
"debug": {
|
||||
"()": commonware.log.Formatter,
|
||||
"datefmt": "%H:%M:%s",
|
||||
"format": "%(asctime)s " + base_fmt,
|
||||
},
|
||||
'prod': {
|
||||
'()': commonware.log.Formatter,
|
||||
'datefmt': '%H:%M:%s',
|
||||
'format': '%s %s: [%%(REMOTE_ADDR)s] %s' % (hostname,
|
||||
settings.SYSLOG_TAG,
|
||||
base_fmt),
|
||||
"prod": {
|
||||
"()": commonware.log.Formatter,
|
||||
"datefmt": "%H:%M:%s",
|
||||
"format": "%s %s: [%%(REMOTE_ADDR)s] %s" % (hostname, settings.SYSLOG_TAG, base_fmt),
|
||||
},
|
||||
'cef': {
|
||||
'()': cef.SysLogFormatter,
|
||||
'datefmt': '%H:%M:%s',
|
||||
"cef": {
|
||||
"()": cef.SysLogFormatter,
|
||||
"datefmt": "%H:%M:%s",
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'()': logging.StreamHandler,
|
||||
'formatter': 'debug',
|
||||
"handlers": {
|
||||
"console": {
|
||||
"()": logging.StreamHandler,
|
||||
"formatter": "debug",
|
||||
},
|
||||
'syslog': {
|
||||
'()': logging.handlers.SysLogHandler,
|
||||
'facility': logging.handlers.SysLogHandler.LOG_LOCAL7,
|
||||
'formatter': 'prod',
|
||||
"syslog": {
|
||||
"()": logging.handlers.SysLogHandler,
|
||||
"facility": logging.handlers.SysLogHandler.LOG_LOCAL7,
|
||||
"formatter": "prod",
|
||||
},
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'class': 'django.utils.log.AdminEmailHandler',
|
||||
"mail_admins": {
|
||||
"level": "ERROR",
|
||||
"class": "django.utils.log.AdminEmailHandler",
|
||||
},
|
||||
'cef_syslog': {
|
||||
'()': logging.handlers.SysLogHandler,
|
||||
'facility': logging.handlers.SysLogHandler.LOG_LOCAL4,
|
||||
'formatter': 'cef',
|
||||
"cef_syslog": {
|
||||
"()": logging.handlers.SysLogHandler,
|
||||
"facility": logging.handlers.SysLogHandler.LOG_LOCAL4,
|
||||
"formatter": "cef",
|
||||
},
|
||||
'cef_console': {
|
||||
'()': logging.StreamHandler,
|
||||
'formatter': 'cef',
|
||||
"cef_console": {
|
||||
"()": logging.StreamHandler,
|
||||
"formatter": "cef",
|
||||
},
|
||||
"null": {
|
||||
"()": NullHandler,
|
||||
},
|
||||
'null': {
|
||||
'()': NullHandler,
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': False,
|
||||
"loggers": {
|
||||
"django.request": {
|
||||
"handlers": ["mail_admins"],
|
||||
"level": "ERROR",
|
||||
"propagate": False,
|
||||
},
|
||||
"cef": {
|
||||
"handlers": ["cef_syslog" if use_syslog else "cef_console"],
|
||||
},
|
||||
'cef': {
|
||||
'handlers': ['cef_syslog' if use_syslog else 'cef_console'],
|
||||
}
|
||||
},
|
||||
'root': {},
|
||||
"root": {},
|
||||
}
|
||||
|
||||
for key, value in settings.LOGGING.items():
|
||||
if hasattr(cfg[key], 'update'):
|
||||
if hasattr(cfg[key], "update"):
|
||||
cfg[key].update(value)
|
||||
else:
|
||||
cfg[key] = value
|
||||
|
||||
# Set the level and handlers for all loggers.
|
||||
for logger in list(cfg['loggers'].values()) + [cfg['root']]:
|
||||
if 'handlers' not in logger:
|
||||
logger['handlers'] = ['syslog' if use_syslog else 'console']
|
||||
if 'level' not in logger:
|
||||
logger['level'] = settings.LOG_LEVEL
|
||||
if logger is not cfg['root'] and 'propagate' not in logger:
|
||||
logger['propagate'] = False
|
||||
for logger in list(cfg["loggers"].values()) + [cfg["root"]]:
|
||||
if "handlers" not in logger:
|
||||
logger["handlers"] = ["syslog" if use_syslog else "console"]
|
||||
if "level" not in logger:
|
||||
logger["level"] = settings.LOG_LEVEL
|
||||
if logger is not cfg["root"] and "propagate" not in logger:
|
||||
logger["propagate"] = False
|
||||
|
||||
dictconfig.dictConfig(cfg)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -11,8 +10,8 @@ from bedrock.utils.git import GitRepo
|
|||
|
||||
|
||||
def get_config_file_name(app_name=None):
|
||||
app_name = app_name or settings.APP_NAME or 'bedrock-dev'
|
||||
return os.path.join(settings.WWW_CONFIG_PATH, 'waffle_configs', '%s.env' % app_name)
|
||||
app_name = app_name or settings.APP_NAME or "bedrock-dev"
|
||||
return os.path.join(settings.WWW_CONFIG_PATH, "waffle_configs", "%s.env" % app_name)
|
||||
|
||||
|
||||
def get_config_values():
|
||||
|
@ -36,34 +35,31 @@ def refresh_db_values():
|
|||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from git.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git."),
|
||||
|
||||
def output(self, msg):
|
||||
if not self.quiet:
|
||||
print(msg)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.quiet = options['quiet']
|
||||
repo = GitRepo(settings.WWW_CONFIG_PATH, settings.WWW_CONFIG_REPO,
|
||||
branch_name=settings.WWW_CONFIG_BRANCH, name='WWW Config')
|
||||
self.output('Updating git repo')
|
||||
self.quiet = options["quiet"]
|
||||
repo = GitRepo(settings.WWW_CONFIG_PATH, settings.WWW_CONFIG_REPO, branch_name=settings.WWW_CONFIG_BRANCH, name="WWW Config")
|
||||
self.output("Updating git repo")
|
||||
repo.update()
|
||||
if not (options['force'] or repo.has_changes()):
|
||||
self.output('No config updates')
|
||||
if not (options["force"] or repo.has_changes()):
|
||||
self.output("No config updates")
|
||||
return
|
||||
|
||||
self.output('Loading configs into database')
|
||||
self.output("Loading configs into database")
|
||||
count = refresh_db_values()
|
||||
|
||||
if count:
|
||||
self.output('%s configs successfully loaded' % count)
|
||||
self.output("%s configs successfully loaded" % count)
|
||||
else:
|
||||
self.output('No configs found. Please try again later.')
|
||||
self.output("No configs found. Please try again later.")
|
||||
|
||||
repo.set_db_latest()
|
||||
|
||||
self.output('Saved latest git repo state to database')
|
||||
self.output('Done!')
|
||||
self.output("Saved latest git repo state to database")
|
||||
self.output("Done!")
|
||||
|
|
|
@ -29,9 +29,11 @@ class LocaleURLMiddleware:
|
|||
|
||||
def __init__(self, get_response=None):
|
||||
if not settings.USE_I18N or not settings.USE_L10N:
|
||||
warn("USE_I18N or USE_L10N is False but LocaleURLMiddleware is "
|
||||
"loaded. Consider removing bedrock.base.middleware."
|
||||
"LocaleURLMiddleware from your MIDDLEWARE setting.")
|
||||
warn(
|
||||
"USE_I18N or USE_L10N is False but LocaleURLMiddleware is "
|
||||
"loaded. Consider removing bedrock.base.middleware."
|
||||
"LocaleURLMiddleware from your MIDDLEWARE setting."
|
||||
)
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
|
@ -46,12 +48,11 @@ class LocaleURLMiddleware:
|
|||
full_path = prefixer.fix(prefixer.shortened_path)
|
||||
|
||||
if not (request.path in settings.SUPPORTED_LOCALE_IGNORE or full_path == request.path):
|
||||
query_string = request.META.get('QUERY_STRING', '')
|
||||
full_path = urllib.parse.quote(full_path.encode('utf-8'))
|
||||
query_string = request.META.get("QUERY_STRING", "")
|
||||
full_path = urllib.parse.quote(full_path.encode("utf-8"))
|
||||
|
||||
if query_string:
|
||||
full_path = '?'.join(
|
||||
[full_path, unquote(query_string, errors='ignore')])
|
||||
full_path = "?".join([full_path, unquote(query_string, errors="ignore")])
|
||||
|
||||
response = HttpResponsePermanentRedirect(full_path)
|
||||
|
||||
|
@ -59,11 +60,11 @@ class LocaleURLMiddleware:
|
|||
old_locale = prefixer.locale
|
||||
new_locale, _ = urlresolvers.split_path(full_path)
|
||||
if old_locale != new_locale:
|
||||
response['Vary'] = 'Accept-Language'
|
||||
response["Vary"] = "Accept-Language"
|
||||
|
||||
return response
|
||||
|
||||
request.path_info = '/' + prefixer.shortened_path
|
||||
request.path_info = "/" + prefixer.shortened_path
|
||||
request.locale = prefixer.locale
|
||||
translation.activate(prefixer.locale or settings.LANGUAGE_CODE)
|
||||
|
||||
|
@ -73,6 +74,7 @@ class BasicAuthMiddleware:
|
|||
Middleware to protect the entire site with a single basic-auth username and password.
|
||||
Set the BASIC_AUTH_CREDS environment variable to enable.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response=None):
|
||||
if not settings.BASIC_AUTH_CREDS:
|
||||
raise MiddlewareNotUsed
|
||||
|
@ -87,8 +89,8 @@ class BasicAuthMiddleware:
|
|||
def process_request(self, request):
|
||||
required_auth = settings.BASIC_AUTH_CREDS
|
||||
if required_auth:
|
||||
if 'HTTP_AUTHORIZATION' in request.META:
|
||||
auth = request.META['HTTP_AUTHORIZATION'].split()
|
||||
if "HTTP_AUTHORIZATION" in request.META:
|
||||
auth = request.META["HTTP_AUTHORIZATION"].split()
|
||||
if len(auth) == 2:
|
||||
if auth[0].lower() == "basic":
|
||||
provided_auth = base64.b64decode(auth[1])
|
||||
|
@ -96,11 +98,9 @@ class BasicAuthMiddleware:
|
|||
# we're good. continue on.
|
||||
return None
|
||||
|
||||
response = HttpResponse(status=401,
|
||||
content='<h1>Unauthorized. '
|
||||
'This site is in private demo mode.</h1>')
|
||||
realm = settings.APP_NAME or 'bedrock-demo'
|
||||
response['WWW-Authenticate'] = 'Basic realm="{}"'.format(realm)
|
||||
response = HttpResponse(status=401, content="<h1>Unauthorized. This site is in private demo mode.</h1>")
|
||||
realm = settings.APP_NAME or "bedrock-demo"
|
||||
response["WWW-Authenticate"] = 'Basic realm="{}"'.format(realm)
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ class ConfigValue(models.Model):
|
|||
value = models.CharField(max_length=200)
|
||||
|
||||
class Meta:
|
||||
app_label = 'base'
|
||||
app_label = "base"
|
||||
|
||||
def __str__(self):
|
||||
return '%s=%s' % (self.name, self.value)
|
||||
return "%s=%s" % (self.name, self.value)
|
||||
|
||||
|
||||
def get_config_dict():
|
||||
|
|
|
@ -40,7 +40,7 @@ def switch(cxt, name, locales=None):
|
|||
which is a list of locales for a prefix (e.g. "en" expands to "en-US, en-GB").
|
||||
"""
|
||||
if locales:
|
||||
if cxt['LANG'] not in expand_locale_groups(locales):
|
||||
if cxt["LANG"] not in expand_locale_groups(locales):
|
||||
return False
|
||||
|
||||
return waffle.switch(name)
|
||||
|
@ -73,10 +73,8 @@ def urlparams(url_, hash=None, **query):
|
|||
query_dict = dict(urllib.parse.parse_qsl(smart_str(q))) if q else {}
|
||||
query_dict.update((k, v) for k, v in query.items())
|
||||
|
||||
query_string = _urlencode([(k, v) for k, v in query_dict.items()
|
||||
if v is not None])
|
||||
new = urllib.parse.ParseResult(
|
||||
url.scheme, url.netloc, url.path, url.params, query_string, fragment)
|
||||
query_string = _urlencode([(k, v) for k, v in query_dict.items() if v is not None])
|
||||
new = urllib.parse.ParseResult(url.scheme, url.netloc, url.path, url.params, query_string, fragment)
|
||||
return new.geturl()
|
||||
|
||||
|
||||
|
@ -92,7 +90,7 @@ def _urlencode(items):
|
|||
def mailtoencode(txt):
|
||||
"""Url encode a string using %20 for spaces."""
|
||||
if isinstance(txt, str):
|
||||
txt = txt.encode('utf-8')
|
||||
txt = txt.encode("utf-8")
|
||||
return urllib.parse.quote(txt)
|
||||
|
||||
|
||||
|
@ -100,14 +98,14 @@ def mailtoencode(txt):
|
|||
def urlencode(txt):
|
||||
"""Url encode a string using + for spaces."""
|
||||
if isinstance(txt, str):
|
||||
txt = txt.encode('utf-8')
|
||||
txt = txt.encode("utf-8")
|
||||
return urllib.parse.quote_plus(txt)
|
||||
|
||||
|
||||
@library.global_function
|
||||
def static(path):
|
||||
if settings.DEBUG and path.startswith('/'):
|
||||
raise ValueError('Static paths must not begin with a slash')
|
||||
if settings.DEBUG and path.startswith("/"):
|
||||
raise ValueError("Static paths must not begin with a slash")
|
||||
|
||||
try:
|
||||
return staticfiles_storage.url(path)
|
||||
|
@ -122,7 +120,7 @@ def js_bundle(name):
|
|||
|
||||
Bundles are defined in the "media/static-bundles.json" file.
|
||||
"""
|
||||
path = 'js/{}.js'.format(name)
|
||||
path = "js/{}.js".format(name)
|
||||
path = staticfiles_storage.url(path)
|
||||
return jinja2.Markup(JS_TEMPLATE % path)
|
||||
|
||||
|
@ -133,7 +131,7 @@ def css_bundle(name):
|
|||
|
||||
Bundles are defined in the "media/static-bundles.json" file.
|
||||
"""
|
||||
path = 'css/{}.css'.format(name)
|
||||
path = "css/{}.css".format(name)
|
||||
path = staticfiles_storage.url(path)
|
||||
return jinja2.Markup(CSS_TEMPLATE % path)
|
||||
|
||||
|
@ -141,7 +139,7 @@ def css_bundle(name):
|
|||
@library.global_function
|
||||
def alternate_url(path, locale):
|
||||
alt_paths = settings.ALT_CANONICAL_PATHS
|
||||
path = path.lstrip('/')
|
||||
path = path.lstrip("/")
|
||||
if path in alt_paths and locale in alt_paths[path]:
|
||||
return alt_paths[path][locale]
|
||||
|
||||
|
@ -157,10 +155,9 @@ def get_donate_params(ctx):
|
|||
:returns: dictionary of donation values, including list of amount presets
|
||||
"""
|
||||
|
||||
donate_params = settings.DONATE_PARAMS.get(
|
||||
ctx['LANG'], settings.DONATE_PARAMS['en-US'])
|
||||
donate_params = settings.DONATE_PARAMS.get(ctx["LANG"], settings.DONATE_PARAMS["en-US"])
|
||||
|
||||
# presets are stored as a string but we need a list for views
|
||||
donate_params['preset_list'] = donate_params['presets'].split(',')
|
||||
donate_params["preset_list"] = donate_params["presets"].split(",")
|
||||
|
||||
return donate_params
|
||||
|
|
|
@ -15,8 +15,9 @@ class AcceptedLocalesTest(TestCase):
|
|||
DEV_LANGUAGES or PROD_LANGUAGES should be used.
|
||||
|
||||
"""
|
||||
locale = data_path('www-l10n')
|
||||
locale_bkp = data_path('www-l10n_bkp')
|
||||
|
||||
locale = data_path("www-l10n")
|
||||
locale_bkp = data_path("www-l10n_bkp")
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
|
@ -34,20 +35,22 @@ class AcceptedLocalesTest(TestCase):
|
|||
|
||||
"""
|
||||
if os.path.exists(cls.locale_bkp):
|
||||
raise Exception('A backup of locale/ exists at %s which might '
|
||||
'mean that previous tests didn\'t end cleanly. '
|
||||
'Skipping the test suite.' % cls.locale_bkp)
|
||||
raise Exception(
|
||||
"A backup of locale/ exists at %s which might "
|
||||
"mean that previous tests didn't end cleanly. "
|
||||
"Skipping the test suite." % cls.locale_bkp
|
||||
)
|
||||
cls.DEV = settings.DEV
|
||||
cls.PROD_LANGUAGES = settings.PROD_LANGUAGES
|
||||
cls.DEV_LANGUAGES = settings.DEV_LANGUAGES
|
||||
settings.PROD_LANGUAGES = ('en-US',)
|
||||
settings.PROD_LANGUAGES = ("en-US",)
|
||||
if os.path.exists(cls.locale):
|
||||
shutil.move(cls.locale, cls.locale_bkp)
|
||||
else:
|
||||
cls.locale_bkp = None
|
||||
for loc in ('en-US', 'fr', 'metadata'):
|
||||
for loc in ("en-US", "fr", "metadata"):
|
||||
os.makedirs(os.path.join(cls.locale, loc))
|
||||
open(os.path.join(cls.locale, 'empty_file'), 'w').close()
|
||||
open(os.path.join(cls.locale, "empty_file"), "w").close()
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
|
@ -69,8 +72,7 @@ class AcceptedLocalesTest(TestCase):
|
|||
"""
|
||||
settings.DEV = True
|
||||
langs = get_dev_languages()
|
||||
assert langs == ['en-US', 'fr'] or langs == ['fr', 'en-US'], (
|
||||
'DEV_LANGUAGES do not correspond to the contents of locale/.')
|
||||
assert langs == ["en-US", "fr"] or langs == ["fr", "en-US"], "DEV_LANGUAGES do not correspond to the contents of locale/."
|
||||
|
||||
def test_dev_languages(self):
|
||||
"""Test the accepted locales on dev instances.
|
||||
|
@ -81,10 +83,11 @@ class AcceptedLocalesTest(TestCase):
|
|||
settings.DEV = True
|
||||
# simulate the successful result of the DEV_LANGUAGES list
|
||||
# comprehension defined in settings.
|
||||
settings.DEV_LANGUAGES = ['en-US', 'fr']
|
||||
assert settings.LANGUAGE_URL_MAP == {'en-us': 'en-US', 'fr': 'fr'}, (
|
||||
'DEV is True, but DEV_LANGUAGES are not used to define the '
|
||||
'allowed locales.')
|
||||
settings.DEV_LANGUAGES = ["en-US", "fr"]
|
||||
assert settings.LANGUAGE_URL_MAP == {
|
||||
"en-us": "en-US",
|
||||
"fr": "fr",
|
||||
}, "DEV is True, but DEV_LANGUAGES are not used to define the allowed locales."
|
||||
|
||||
def test_prod_languages(self):
|
||||
"""Test the accepted locales on prod instances.
|
||||
|
@ -93,6 +96,4 @@ class AcceptedLocalesTest(TestCase):
|
|||
|
||||
"""
|
||||
settings.DEV = False
|
||||
assert settings.LANGUAGE_URL_MAP == {'en-us': 'en-US'}, (
|
||||
'DEV is False, but PROD_LANGUAGES are not used to define the '
|
||||
'allowed locales.')
|
||||
assert settings.LANGUAGE_URL_MAP == {"en-us": "en-US"}, "DEV is False, but PROD_LANGUAGES are not used to define the allowed locales."
|
||||
|
|
|
@ -7,63 +7,60 @@ from lib.l10n_utils import translation
|
|||
|
||||
|
||||
class TestContext(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
translation.activate('en-US')
|
||||
translation.activate("en-US")
|
||||
self.factory = RequestFactory()
|
||||
translation.activate('en-US')
|
||||
translation.activate("en-US")
|
||||
|
||||
def render(self, content, request=None):
|
||||
if not request:
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
tpl = jinja2.Template(content)
|
||||
return render_to_string(tpl, request=request)
|
||||
|
||||
def test_request(self):
|
||||
assert self.render('{{ request.path }}') == '/'
|
||||
assert self.render("{{ request.path }}") == "/"
|
||||
|
||||
def test_settings(self):
|
||||
assert self.render('{{ settings.LANGUAGE_CODE }}') == 'en-US'
|
||||
assert self.render("{{ settings.LANGUAGE_CODE }}") == "en-US"
|
||||
|
||||
def test_languages(self):
|
||||
assert self.render("{{ LANGUAGES['en-us'] }}") == 'English (US)'
|
||||
assert self.render("{{ LANGUAGES['en-us'] }}") == "English (US)"
|
||||
|
||||
def test_lang_setting(self):
|
||||
assert self.render("{{ LANG }}") == 'en-US'
|
||||
assert self.render("{{ LANG }}") == "en-US"
|
||||
|
||||
def test_lang_dir(self):
|
||||
assert self.render("{{ DIR }}") == 'ltr'
|
||||
assert self.render("{{ DIR }}") == "ltr"
|
||||
|
||||
def test_geo_header(self):
|
||||
"""Country code from request header should work"""
|
||||
req = self.factory.get('/', HTTP_CF_IPCOUNTRY='de')
|
||||
assert self.render('{{ country_code }}', req) == 'DE'
|
||||
req = self.factory.get("/", HTTP_CF_IPCOUNTRY="de")
|
||||
assert self.render("{{ country_code }}", req) == "DE"
|
||||
|
||||
@override_settings(DEV=False)
|
||||
def test_geo_no_header(self):
|
||||
"""Country code when header absent should be None"""
|
||||
req = self.factory.get('/')
|
||||
assert self.render('{{ country_code }}', req) == 'None'
|
||||
req = self.factory.get("/")
|
||||
assert self.render("{{ country_code }}", req) == "None"
|
||||
|
||||
def test_geo_param(self):
|
||||
"""Country code from header should be overridden by query param
|
||||
for pre-prod domains."""
|
||||
req = self.factory.get('/', data={'geo': 'fr'}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert self.render('{{ country_code }}', req) == 'FR'
|
||||
for pre-prod domains."""
|
||||
req = self.factory.get("/", data={"geo": "fr"}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert self.render("{{ country_code }}", req) == "FR"
|
||||
|
||||
# should use header if at prod domain
|
||||
req = self.factory.get('/', data={'geo': 'fr'},
|
||||
HTTP_CF_IPCOUNTRY='de',
|
||||
HTTP_HOST='www.mozilla.org')
|
||||
assert self.render('{{ country_code }}', req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": "fr"}, HTTP_CF_IPCOUNTRY="de", HTTP_HOST="www.mozilla.org")
|
||||
assert self.render("{{ country_code }}", req) == "DE"
|
||||
|
||||
@override_settings(DEV=False)
|
||||
def test_invalid_geo_param(self):
|
||||
req = self.factory.get('/', data={'geo': 'france'}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert self.render('{{ country_code }}', req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": "france"}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert self.render("{{ country_code }}", req) == "DE"
|
||||
|
||||
req = self.factory.get('/', data={'geo': ''}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert self.render('{{ country_code }}', req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": ""}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert self.render("{{ country_code }}", req) == "DE"
|
||||
|
||||
req = self.factory.get('/', data={'geo': 'france'})
|
||||
assert self.render('{{ country_code }}', req) == 'None'
|
||||
req = self.factory.get("/", data={"geo": "france"})
|
||||
assert self.render("{{ country_code }}", req) == "None"
|
||||
|
|
|
@ -9,34 +9,32 @@ class TestGeo(TestCase):
|
|||
|
||||
def test_geo_header(self):
|
||||
"""Country code from request header should work"""
|
||||
req = self.factory.get('/', HTTP_CF_IPCOUNTRY='de')
|
||||
assert get_country_from_request(req) == 'DE'
|
||||
req = self.factory.get("/", HTTP_CF_IPCOUNTRY="de")
|
||||
assert get_country_from_request(req) == "DE"
|
||||
|
||||
@override_settings(DEV=False)
|
||||
def test_geo_no_header(self):
|
||||
"""Country code when header absent should be None"""
|
||||
req = self.factory.get('/')
|
||||
req = self.factory.get("/")
|
||||
assert get_country_from_request(req) is None
|
||||
|
||||
def test_geo_param(self):
|
||||
"""Country code from header should be overridden by query param
|
||||
for pre-prod domains."""
|
||||
req = self.factory.get('/', data={'geo': 'fr'}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert get_country_from_request(req) == 'FR'
|
||||
for pre-prod domains."""
|
||||
req = self.factory.get("/", data={"geo": "fr"}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert get_country_from_request(req) == "FR"
|
||||
|
||||
# should use header if at prod domain
|
||||
req = self.factory.get('/', data={'geo': 'fr'},
|
||||
HTTP_CF_IPCOUNTRY='de',
|
||||
HTTP_HOST='www.mozilla.org')
|
||||
assert get_country_from_request(req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": "fr"}, HTTP_CF_IPCOUNTRY="de", HTTP_HOST="www.mozilla.org")
|
||||
assert get_country_from_request(req) == "DE"
|
||||
|
||||
@override_settings(DEV=False)
|
||||
def test_invalid_geo_param(self):
|
||||
req = self.factory.get('/', data={'geo': 'france'}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert get_country_from_request(req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": "france"}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert get_country_from_request(req) == "DE"
|
||||
|
||||
req = self.factory.get('/', data={'geo': ''}, HTTP_CF_IPCOUNTRY='de')
|
||||
assert get_country_from_request(req) == 'DE'
|
||||
req = self.factory.get("/", data={"geo": ""}, HTTP_CF_IPCOUNTRY="de")
|
||||
assert get_country_from_request(req) == "DE"
|
||||
|
||||
req = self.factory.get('/', data={'geo': 'france'})
|
||||
req = self.factory.get("/", data={"geo": "france"})
|
||||
assert get_country_from_request(req) is None
|
||||
|
|
|
@ -9,11 +9,11 @@ from bedrock.base.templatetags import helpers
|
|||
|
||||
jinja_env = Jinja2.get_default()
|
||||
SEND_TO_DEVICE_MESSAGE_SETS = {
|
||||
'default': {
|
||||
'email': {
|
||||
'android': 'download-firefox-android',
|
||||
'ios': 'download-firefox-ios',
|
||||
'all': 'download-firefox-mobile',
|
||||
"default": {
|
||||
"email": {
|
||||
"android": "download-firefox-android",
|
||||
"ios": "download-firefox-ios",
|
||||
"all": "download-firefox-mobile",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,61 +27,60 @@ def render(s, context={}):
|
|||
class HelpersTests(TestCase):
|
||||
def test_urlencode_with_unicode(self):
|
||||
template = '<a href="?var={{ key|urlencode }}">'
|
||||
context = {'key': '?& /()'}
|
||||
context = {"key": "?& /()"}
|
||||
assert render(template, context) == '<a href="?var=%3F%26+%2F%28%29">'
|
||||
# non-ascii
|
||||
context = {'key': u'\xe4'}
|
||||
context = {"key": "\xe4"}
|
||||
assert render(template, context) == '<a href="?var=%C3%A4">'
|
||||
|
||||
def test_mailtoencode_with_unicode(self):
|
||||
template = '<a href="?var={{ key|mailtoencode }}">'
|
||||
context = {'key': '?& /()'}
|
||||
context = {"key": "?& /()"}
|
||||
assert render(template, context) == '<a href="?var=%3F%26%20/%28%29">'
|
||||
# non-ascii
|
||||
context = {'key': u'\xe4'}
|
||||
context = {"key": "\xe4"}
|
||||
assert render(template, context) == '<a href="?var=%C3%A4">'
|
||||
|
||||
|
||||
@override_settings(LANG_GROUPS={'en': ['en-US', 'en-GB']})
|
||||
@override_settings(LANG_GROUPS={"en": ["en-US", "en-GB"]})
|
||||
def test_switch():
|
||||
with patch.object(helpers, 'waffle') as waffle:
|
||||
ret = helpers.switch({'LANG': 'de'}, 'dude', ['fr', 'de'])
|
||||
with patch.object(helpers, "waffle") as waffle:
|
||||
ret = helpers.switch({"LANG": "de"}, "dude", ["fr", "de"])
|
||||
|
||||
assert ret is waffle.switch.return_value
|
||||
waffle.switch.assert_called_with('dude')
|
||||
waffle.switch.assert_called_with("dude")
|
||||
|
||||
with patch.object(helpers, 'waffle') as waffle:
|
||||
assert not helpers.switch({'LANG': 'de'}, 'dude', ['fr', 'en'])
|
||||
with patch.object(helpers, "waffle") as waffle:
|
||||
assert not helpers.switch({"LANG": "de"}, "dude", ["fr", "en"])
|
||||
|
||||
waffle.switch.assert_not_called()
|
||||
|
||||
with patch.object(helpers, 'waffle') as waffle:
|
||||
ret = helpers.switch({'LANG': 'de'}, 'dude')
|
||||
with patch.object(helpers, "waffle") as waffle:
|
||||
ret = helpers.switch({"LANG": "de"}, "dude")
|
||||
|
||||
assert ret is waffle.switch.return_value
|
||||
waffle.switch.assert_called_with('dude')
|
||||
waffle.switch.assert_called_with("dude")
|
||||
|
||||
with patch.object(helpers, 'waffle') as waffle:
|
||||
ret = helpers.switch({'LANG': 'en-GB'}, 'dude', ['de', 'en'])
|
||||
with patch.object(helpers, "waffle") as waffle:
|
||||
ret = helpers.switch({"LANG": "en-GB"}, "dude", ["de", "en"])
|
||||
|
||||
assert ret is waffle.switch.return_value
|
||||
waffle.switch.assert_called_with('dude')
|
||||
waffle.switch.assert_called_with("dude")
|
||||
|
||||
|
||||
@override_settings(DONATE_PARAMS={'en-US': {'presets': '50,30,20,10'},
|
||||
'id': {'presets': '270000,140000,70000,40000'}})
|
||||
@override_settings(DONATE_PARAMS={"en-US": {"presets": "50,30,20,10"}, "id": {"presets": "270000,140000,70000,40000"}})
|
||||
class TestGetDonateParams(TestCase):
|
||||
def test_en_us(self):
|
||||
ctx = {'LANG': 'en-US'}
|
||||
ctx = {"LANG": "en-US"}
|
||||
params = helpers.get_donate_params(ctx)
|
||||
assert params['preset_list'] == '50,30,20,10'.split(',')
|
||||
assert params["preset_list"] == "50,30,20,10".split(",")
|
||||
|
||||
def test_id(self):
|
||||
ctx = {'LANG': 'id'}
|
||||
ctx = {"LANG": "id"}
|
||||
params = helpers.get_donate_params(ctx)
|
||||
assert params['preset_list'] == '270000,140000,70000,40000'.split(',')
|
||||
assert params["preset_list"] == "270000,140000,70000,40000".split(",")
|
||||
|
||||
def test_undefined_locale(self):
|
||||
ctx = {'LANG': 'de'}
|
||||
ctx = {"LANG": "de"}
|
||||
params = helpers.get_donate_params(ctx)
|
||||
assert params['preset_list'] == '50,30,20,10'.split(',')
|
||||
assert params["preset_list"] == "50,30,20,10".split(",")
|
||||
|
|
|
@ -12,32 +12,28 @@ class TestLocaleURLMiddleware(TestCase):
|
|||
self.rf = RequestFactory()
|
||||
self.middleware = LocaleURLMiddleware()
|
||||
|
||||
@override_settings(DEV_LANGUAGES=('de', 'fr'))
|
||||
@override_settings(DEV_LANGUAGES=("de", "fr"))
|
||||
def test_redirects_to_correct_language(self):
|
||||
"""Should redirect to lang prefixed url."""
|
||||
path = '/the/dude/'
|
||||
req = self.rf.get(path, HTTP_ACCEPT_LANGUAGE='de')
|
||||
path = "/the/dude/"
|
||||
req = self.rf.get(path, HTTP_ACCEPT_LANGUAGE="de")
|
||||
resp = self.middleware.process_request(req)
|
||||
self.assertEqual(resp['Location'], '/de' + path)
|
||||
self.assertEqual(resp["Location"], "/de" + path)
|
||||
|
||||
@override_settings(DEV_LANGUAGES=('es', 'fr'),
|
||||
LANGUAGE_CODE='en-US')
|
||||
@override_settings(DEV_LANGUAGES=("es", "fr"), LANGUAGE_CODE="en-US")
|
||||
def test_redirects_to_default_language(self):
|
||||
"""Should redirect to default lang if not in settings."""
|
||||
path = '/the/dude/'
|
||||
req = self.rf.get(path, HTTP_ACCEPT_LANGUAGE='de')
|
||||
path = "/the/dude/"
|
||||
req = self.rf.get(path, HTTP_ACCEPT_LANGUAGE="de")
|
||||
resp = self.middleware.process_request(req)
|
||||
self.assertEqual(resp['Location'], '/en-US' + path)
|
||||
self.assertEqual(resp["Location"], "/en-US" + path)
|
||||
|
||||
@override_settings(DEV_LANGUAGES=('de', 'fr'))
|
||||
@override_settings(DEV_LANGUAGES=("de", "fr"))
|
||||
def test_redirects_to_correct_language_despite_unicode_errors(self):
|
||||
"""Should redirect to lang prefixed url, stripping invalid chars."""
|
||||
path = '/the/dude/'
|
||||
corrupt_querystring = '?' + urlencode(
|
||||
{b'a\xa4\x91b\xa4\x91i\xc0de': 's'})
|
||||
corrected_querystring = '?abide=s'
|
||||
req = self.rf.get(path + corrupt_querystring,
|
||||
HTTP_ACCEPT_LANGUAGE='de')
|
||||
path = "/the/dude/"
|
||||
corrupt_querystring = "?" + urlencode({b"a\xa4\x91b\xa4\x91i\xc0de": "s"})
|
||||
corrected_querystring = "?abide=s"
|
||||
req = self.rf.get(path + corrupt_querystring, HTTP_ACCEPT_LANGUAGE="de")
|
||||
resp = self.middleware.process_request(req)
|
||||
self.assertEqual(resp['Location'],
|
||||
'/de' + path + corrected_querystring)
|
||||
self.assertEqual(resp["Location"], "/de" + path + corrected_querystring)
|
||||
|
|
|
@ -4,11 +4,10 @@ from django.conf import settings
|
|||
from django.test import override_settings
|
||||
|
||||
|
||||
@override_settings(DEV=False, PROD_LANGUAGES=('de', 'fr', 'nb-NO', 'ja', 'ja-JP-mac',
|
||||
'en-US', 'en-GB'))
|
||||
@override_settings(DEV=False, PROD_LANGUAGES=("de", "fr", "nb-NO", "ja", "ja-JP-mac", "en-US", "en-GB"))
|
||||
def test_lang_groups():
|
||||
# should not contain 'nb' and 'ja' group should contain 'ja'
|
||||
assert dict(settings.LANG_GROUPS) == {
|
||||
'ja': ['ja-JP-mac', 'ja'],
|
||||
'en': ['en-US', 'en-GB'],
|
||||
"ja": ["ja-JP-mac", "ja"],
|
||||
"en": ["en-US", "en-GB"],
|
||||
}
|
||||
|
|
|
@ -24,17 +24,17 @@ class C:
|
|||
|
||||
def custom_key_func(key, key_prefix, version):
|
||||
"A customized cache key function"
|
||||
return 'CUSTOM-' + '-'.join([key_prefix, str(version), key])
|
||||
return "CUSTOM-" + "-".join([key_prefix, str(version), key])
|
||||
|
||||
|
||||
_caches_setting_base = {
|
||||
'default': {},
|
||||
'prefix': {'KEY_PREFIX': 'cacheprefix{}'.format(os.getpid())},
|
||||
'v2': {'VERSION': 2},
|
||||
'custom_key': {'KEY_FUNCTION': custom_key_func},
|
||||
'custom_key2': {'KEY_FUNCTION': 'bedrock.base.tests.test_simple_dict_cache.custom_key_func'},
|
||||
'cull': {'OPTIONS': {'MAX_ENTRIES': 30}},
|
||||
'zero_cull': {'OPTIONS': {'CULL_FREQUENCY': 0, 'MAX_ENTRIES': 30}},
|
||||
"default": {},
|
||||
"prefix": {"KEY_PREFIX": "cacheprefix{}".format(os.getpid())},
|
||||
"v2": {"VERSION": 2},
|
||||
"custom_key": {"KEY_FUNCTION": custom_key_func},
|
||||
"custom_key2": {"KEY_FUNCTION": "bedrock.base.tests.test_simple_dict_cache.custom_key_func"},
|
||||
"cull": {"OPTIONS": {"MAX_ENTRIES": 30}},
|
||||
"zero_cull": {"OPTIONS": {"CULL_FREQUENCY": 0, "MAX_ENTRIES": 30}},
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,9 +52,11 @@ def caches_setting_for_tests(base=None, **params):
|
|||
return setting
|
||||
|
||||
|
||||
@override_settings(CACHES=caches_setting_for_tests(
|
||||
BACKEND='bedrock.base.cache.SimpleDictCache',
|
||||
))
|
||||
@override_settings(
|
||||
CACHES=caches_setting_for_tests(
|
||||
BACKEND="bedrock.base.cache.SimpleDictCache",
|
||||
)
|
||||
)
|
||||
class SimpleDictCacheTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
|
@ -76,15 +78,15 @@ class SimpleDictCacheTests(TestCase):
|
|||
|
||||
def test_prefix(self):
|
||||
# Test for same cache key conflicts between shared backend
|
||||
cache.set('somekey', 'value')
|
||||
cache.set("somekey", "value")
|
||||
|
||||
# should not be set in the prefixed cache
|
||||
self.assertFalse('somekey' in caches['prefix']) # noqa
|
||||
self.assertFalse("somekey" in caches["prefix"]) # noqa
|
||||
|
||||
caches['prefix'].set('somekey', 'value2')
|
||||
caches["prefix"].set("somekey", "value2")
|
||||
|
||||
self.assertEqual(cache.get('somekey'), 'value')
|
||||
self.assertEqual(caches['prefix'].get('somekey'), 'value2')
|
||||
self.assertEqual(cache.get("somekey"), "value")
|
||||
self.assertEqual(caches["prefix"].get("somekey"), "value2")
|
||||
|
||||
def test_non_existent(self):
|
||||
# Non-existent cache keys return as None/default
|
||||
|
@ -94,17 +96,17 @@ class SimpleDictCacheTests(TestCase):
|
|||
|
||||
def test_non_none_default(self):
|
||||
# Should cache None values if default is not None
|
||||
cache.set('is_none', None)
|
||||
self.assertIsNone(cache.get('is_none', 'bang!'))
|
||||
cache.set("is_none", None)
|
||||
self.assertIsNone(cache.get("is_none", "bang!"))
|
||||
|
||||
def test_get_many(self):
|
||||
# Multiple cache keys can be returned using get_many
|
||||
cache.set('a', 'a')
|
||||
cache.set('b', 'b')
|
||||
cache.set('c', 'c')
|
||||
cache.set('d', 'd')
|
||||
self.assertEqual(cache.get_many(['a', 'c', 'd']), {'a': 'a', 'c': 'c', 'd': 'd'})
|
||||
self.assertEqual(cache.get_many(['a', 'b', 'e']), {'a': 'a', 'b': 'b'})
|
||||
cache.set("a", "a")
|
||||
cache.set("b", "b")
|
||||
cache.set("c", "c")
|
||||
cache.set("d", "d")
|
||||
self.assertEqual(cache.get_many(["a", "c", "d"]), {"a": "a", "c": "c", "d": "d"})
|
||||
self.assertEqual(cache.get_many(["a", "b", "e"]), {"a": "a", "b": "b"})
|
||||
|
||||
def test_delete(self):
|
||||
# Cache keys can be deleted
|
||||
|
@ -131,47 +133,47 @@ class SimpleDictCacheTests(TestCase):
|
|||
|
||||
def test_incr(self):
|
||||
# Cache values can be incremented
|
||||
cache.set('answer', 41)
|
||||
self.assertEqual(cache.incr('answer'), 42)
|
||||
self.assertEqual(cache.get('answer'), 42)
|
||||
self.assertEqual(cache.incr('answer', 10), 52)
|
||||
self.assertEqual(cache.get('answer'), 52)
|
||||
self.assertEqual(cache.incr('answer', -10), 42)
|
||||
self.assertRaises(ValueError, cache.incr, 'does_not_exist')
|
||||
cache.set("answer", 41)
|
||||
self.assertEqual(cache.incr("answer"), 42)
|
||||
self.assertEqual(cache.get("answer"), 42)
|
||||
self.assertEqual(cache.incr("answer", 10), 52)
|
||||
self.assertEqual(cache.get("answer"), 52)
|
||||
self.assertEqual(cache.incr("answer", -10), 42)
|
||||
self.assertRaises(ValueError, cache.incr, "does_not_exist")
|
||||
|
||||
def test_decr(self):
|
||||
# Cache values can be decremented
|
||||
cache.set('answer', 43)
|
||||
self.assertEqual(cache.decr('answer'), 42)
|
||||
self.assertEqual(cache.get('answer'), 42)
|
||||
self.assertEqual(cache.decr('answer', 10), 32)
|
||||
self.assertEqual(cache.get('answer'), 32)
|
||||
self.assertEqual(cache.decr('answer', -10), 42)
|
||||
self.assertRaises(ValueError, cache.decr, 'does_not_exist')
|
||||
cache.set("answer", 43)
|
||||
self.assertEqual(cache.decr("answer"), 42)
|
||||
self.assertEqual(cache.get("answer"), 42)
|
||||
self.assertEqual(cache.decr("answer", 10), 32)
|
||||
self.assertEqual(cache.get("answer"), 32)
|
||||
self.assertEqual(cache.decr("answer", -10), 42)
|
||||
self.assertRaises(ValueError, cache.decr, "does_not_exist")
|
||||
|
||||
def test_close(self):
|
||||
self.assertTrue(hasattr(cache, 'close'))
|
||||
self.assertTrue(hasattr(cache, "close"))
|
||||
cache.close()
|
||||
|
||||
def test_data_types(self):
|
||||
# Many different data types can be cached
|
||||
stuff = {
|
||||
'string': 'this is a string',
|
||||
'int': 42,
|
||||
'list': [1, 2, 3, 4],
|
||||
'tuple': (1, 2, 3, 4),
|
||||
'dict': {'A': 1, 'B': 2},
|
||||
'function': f,
|
||||
'class': C,
|
||||
"string": "this is a string",
|
||||
"int": 42,
|
||||
"list": [1, 2, 3, 4],
|
||||
"tuple": (1, 2, 3, 4),
|
||||
"dict": {"A": 1, "B": 2},
|
||||
"function": f,
|
||||
"class": C,
|
||||
}
|
||||
cache.set("stuff", stuff)
|
||||
self.assertEqual(cache.get("stuff"), stuff)
|
||||
|
||||
def test_expiration(self):
|
||||
# Cache values can be set to expire
|
||||
cache.set('expire1', 'very quickly', 1)
|
||||
cache.set('expire2', 'very quickly', 1)
|
||||
cache.set('expire3', 'very quickly', 1)
|
||||
cache.set("expire1", "very quickly", 1)
|
||||
cache.set("expire2", "very quickly", 1)
|
||||
cache.set("expire3", "very quickly", 1)
|
||||
|
||||
time.sleep(2)
|
||||
self.assertEqual(cache.get("expire1"), None)
|
||||
|
@ -183,10 +185,10 @@ class SimpleDictCacheTests(TestCase):
|
|||
def test_unicode(self):
|
||||
# Unicode values can be cached
|
||||
stuff = {
|
||||
'ascii': 'ascii_value',
|
||||
'unicode_ascii': 'Iñtërnâtiônàlizætiøn1',
|
||||
'Iñtërnâtiônàlizætiøn': 'Iñtërnâtiônàlizætiøn2',
|
||||
'ascii2': {'x': 1}
|
||||
"ascii": "ascii_value",
|
||||
"unicode_ascii": "Iñtërnâtiônàlizætiøn1",
|
||||
"Iñtërnâtiônàlizætiøn": "Iñtërnâtiônàlizætiøn2",
|
||||
"ascii2": {"x": 1},
|
||||
}
|
||||
# Test `set`
|
||||
for key, value in stuff.items():
|
||||
|
@ -210,24 +212,24 @@ class SimpleDictCacheTests(TestCase):
|
|||
# Binary strings should be cacheable
|
||||
from zlib import compress, decompress
|
||||
|
||||
value = 'value_to_be_compressed'
|
||||
value = "value_to_be_compressed"
|
||||
compressed_value = compress(value.encode())
|
||||
|
||||
# Test set
|
||||
cache.set('binary1', compressed_value)
|
||||
compressed_result = cache.get('binary1')
|
||||
cache.set("binary1", compressed_value)
|
||||
compressed_result = cache.get("binary1")
|
||||
self.assertEqual(compressed_value, compressed_result)
|
||||
self.assertEqual(value, decompress(compressed_result).decode())
|
||||
|
||||
# Test add
|
||||
cache.add('binary1-add', compressed_value)
|
||||
compressed_result = cache.get('binary1-add')
|
||||
cache.add("binary1-add", compressed_value)
|
||||
compressed_result = cache.get("binary1-add")
|
||||
self.assertEqual(compressed_value, compressed_result)
|
||||
self.assertEqual(value, decompress(compressed_result).decode())
|
||||
|
||||
# Test set_many
|
||||
cache.set_many({'binary1-set_many': compressed_value})
|
||||
compressed_result = cache.get('binary1-set_many')
|
||||
cache.set_many({"binary1-set_many": compressed_value})
|
||||
compressed_result = cache.get("binary1-set_many")
|
||||
self.assertEqual(compressed_value, compressed_result)
|
||||
self.assertEqual(value, decompress(compressed_result).decode())
|
||||
|
||||
|
@ -263,51 +265,51 @@ class SimpleDictCacheTests(TestCase):
|
|||
self.assertEqual(cache.get("key2"), None)
|
||||
|
||||
def test_long_timeout(self):
|
||||
'''
|
||||
"""
|
||||
Using a timeout greater than 30 days makes memcached think
|
||||
it is an absolute expiration timestamp instead of a relative
|
||||
offset. Test that we honour this convention. Refs #12399.
|
||||
'''
|
||||
cache.set('key1', 'eggs', 60 * 60 * 24 * 30 + 1) # 30 days + 1 second
|
||||
self.assertEqual(cache.get('key1'), 'eggs')
|
||||
"""
|
||||
cache.set("key1", "eggs", 60 * 60 * 24 * 30 + 1) # 30 days + 1 second
|
||||
self.assertEqual(cache.get("key1"), "eggs")
|
||||
|
||||
cache.add('key2', 'ham', 60 * 60 * 24 * 30 + 1)
|
||||
self.assertEqual(cache.get('key2'), 'ham')
|
||||
cache.add("key2", "ham", 60 * 60 * 24 * 30 + 1)
|
||||
self.assertEqual(cache.get("key2"), "ham")
|
||||
|
||||
cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, 60 * 60 * 24 * 30 + 1)
|
||||
self.assertEqual(cache.get('key3'), 'sausage')
|
||||
self.assertEqual(cache.get('key4'), 'lobster bisque')
|
||||
cache.set_many({"key3": "sausage", "key4": "lobster bisque"}, 60 * 60 * 24 * 30 + 1)
|
||||
self.assertEqual(cache.get("key3"), "sausage")
|
||||
self.assertEqual(cache.get("key4"), "lobster bisque")
|
||||
|
||||
def test_forever_timeout(self):
|
||||
'''
|
||||
"""
|
||||
Passing in None into timeout results in a value that is cached forever
|
||||
'''
|
||||
cache.set('key1', 'eggs', None)
|
||||
self.assertEqual(cache.get('key1'), 'eggs')
|
||||
"""
|
||||
cache.set("key1", "eggs", None)
|
||||
self.assertEqual(cache.get("key1"), "eggs")
|
||||
|
||||
cache.add('key2', 'ham', None)
|
||||
self.assertEqual(cache.get('key2'), 'ham')
|
||||
added = cache.add('key1', 'new eggs', None)
|
||||
cache.add("key2", "ham", None)
|
||||
self.assertEqual(cache.get("key2"), "ham")
|
||||
added = cache.add("key1", "new eggs", None)
|
||||
self.assertEqual(added, False)
|
||||
self.assertEqual(cache.get('key1'), 'eggs')
|
||||
self.assertEqual(cache.get("key1"), "eggs")
|
||||
|
||||
cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, None)
|
||||
self.assertEqual(cache.get('key3'), 'sausage')
|
||||
self.assertEqual(cache.get('key4'), 'lobster bisque')
|
||||
cache.set_many({"key3": "sausage", "key4": "lobster bisque"}, None)
|
||||
self.assertEqual(cache.get("key3"), "sausage")
|
||||
self.assertEqual(cache.get("key4"), "lobster bisque")
|
||||
|
||||
def test_zero_timeout(self):
|
||||
'''
|
||||
"""
|
||||
Passing in zero into timeout results in a value that is not cached
|
||||
'''
|
||||
cache.set('key1', 'eggs', 0)
|
||||
self.assertEqual(cache.get('key1'), None)
|
||||
"""
|
||||
cache.set("key1", "eggs", 0)
|
||||
self.assertEqual(cache.get("key1"), None)
|
||||
|
||||
cache.add('key2', 'ham', 0)
|
||||
self.assertEqual(cache.get('key2'), None)
|
||||
cache.add("key2", "ham", 0)
|
||||
self.assertEqual(cache.get("key2"), None)
|
||||
|
||||
cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, 0)
|
||||
self.assertEqual(cache.get('key3'), None)
|
||||
self.assertEqual(cache.get('key4'), None)
|
||||
cache.set_many({"key3": "sausage", "key4": "lobster bisque"}, 0)
|
||||
self.assertEqual(cache.get("key3"), None)
|
||||
self.assertEqual(cache.get("key4"), None)
|
||||
|
||||
def test_float_timeout(self):
|
||||
# Make sure a timeout given as a float doesn't crash anything.
|
||||
|
@ -318,19 +320,19 @@ class SimpleDictCacheTests(TestCase):
|
|||
# Create initial cache key entries. This will overflow the cache,
|
||||
# causing a cull.
|
||||
for i in range(1, initial_count):
|
||||
cull_cache.set('cull%d' % i, 'value', 1000)
|
||||
cull_cache.set("cull%d" % i, "value", 1000)
|
||||
count = 0
|
||||
# Count how many keys are left in the cache.
|
||||
for i in range(1, initial_count):
|
||||
if 'cull%d' % i in cull_cache: # noqa
|
||||
if "cull%d" % i in cull_cache: # noqa
|
||||
count = count + 1
|
||||
self.assertEqual(count, final_count)
|
||||
|
||||
def test_cull(self):
|
||||
self._perform_cull_test(caches['cull'], 50, 29)
|
||||
self._perform_cull_test(caches["cull"], 50, 29)
|
||||
|
||||
def test_zero_cull(self):
|
||||
self._perform_cull_test(caches['zero_cull'], 50, 19)
|
||||
self._perform_cull_test(caches["zero_cull"], 50, 19)
|
||||
|
||||
def test_invalid_keys(self):
|
||||
"""
|
||||
|
@ -351,13 +353,13 @@ class SimpleDictCacheTests(TestCase):
|
|||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
# memcached does not allow whitespace or control characters in keys
|
||||
cache.set('key with spaces', 'value')
|
||||
cache.set("key with spaces", "value")
|
||||
self.assertTrue(w)
|
||||
self.assertIsInstance(w[0].message, CacheKeyWarning)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
# memcached limits key length to 250
|
||||
cache.set('a' * 251, 'value')
|
||||
cache.set("a" * 251, "value")
|
||||
self.assertEqual(len(w), 1)
|
||||
self.assertIsInstance(w[0].message, CacheKeyWarning)
|
||||
finally:
|
||||
|
@ -365,291 +367,278 @@ class SimpleDictCacheTests(TestCase):
|
|||
|
||||
def test_cache_versioning_get_set(self):
|
||||
# set, using default version = 1
|
||||
cache.set('answer1', 42)
|
||||
self.assertEqual(cache.get('answer1'), 42)
|
||||
self.assertEqual(cache.get('answer1', version=1), 42)
|
||||
self.assertEqual(cache.get('answer1', version=2), None)
|
||||
cache.set("answer1", 42)
|
||||
self.assertEqual(cache.get("answer1"), 42)
|
||||
self.assertEqual(cache.get("answer1", version=1), 42)
|
||||
self.assertEqual(cache.get("answer1", version=2), None)
|
||||
|
||||
self.assertEqual(caches['v2'].get('answer1'), None)
|
||||
self.assertEqual(caches['v2'].get('answer1', version=1), 42)
|
||||
self.assertEqual(caches['v2'].get('answer1', version=2), None)
|
||||
self.assertEqual(caches["v2"].get("answer1"), None)
|
||||
self.assertEqual(caches["v2"].get("answer1", version=1), 42)
|
||||
self.assertEqual(caches["v2"].get("answer1", version=2), None)
|
||||
|
||||
# set, default version = 1, but manually override version = 2
|
||||
cache.set('answer2', 42, version=2)
|
||||
self.assertEqual(cache.get('answer2'), None)
|
||||
self.assertEqual(cache.get('answer2', version=1), None)
|
||||
self.assertEqual(cache.get('answer2', version=2), 42)
|
||||
cache.set("answer2", 42, version=2)
|
||||
self.assertEqual(cache.get("answer2"), None)
|
||||
self.assertEqual(cache.get("answer2", version=1), None)
|
||||
self.assertEqual(cache.get("answer2", version=2), 42)
|
||||
|
||||
self.assertEqual(caches['v2'].get('answer2'), 42)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=1), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=2), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2"), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=1), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=2), 42)
|
||||
|
||||
# v2 set, using default version = 2
|
||||
caches['v2'].set('answer3', 42)
|
||||
self.assertEqual(cache.get('answer3'), None)
|
||||
self.assertEqual(cache.get('answer3', version=1), None)
|
||||
self.assertEqual(cache.get('answer3', version=2), 42)
|
||||
caches["v2"].set("answer3", 42)
|
||||
self.assertEqual(cache.get("answer3"), None)
|
||||
self.assertEqual(cache.get("answer3", version=1), None)
|
||||
self.assertEqual(cache.get("answer3", version=2), 42)
|
||||
|
||||
self.assertEqual(caches['v2'].get('answer3'), 42)
|
||||
self.assertEqual(caches['v2'].get('answer3', version=1), None)
|
||||
self.assertEqual(caches['v2'].get('answer3', version=2), 42)
|
||||
self.assertEqual(caches["v2"].get("answer3"), 42)
|
||||
self.assertEqual(caches["v2"].get("answer3", version=1), None)
|
||||
self.assertEqual(caches["v2"].get("answer3", version=2), 42)
|
||||
|
||||
# v2 set, default version = 2, but manually override version = 1
|
||||
caches['v2'].set('answer4', 42, version=1)
|
||||
self.assertEqual(cache.get('answer4'), 42)
|
||||
self.assertEqual(cache.get('answer4', version=1), 42)
|
||||
self.assertEqual(cache.get('answer4', version=2), None)
|
||||
caches["v2"].set("answer4", 42, version=1)
|
||||
self.assertEqual(cache.get("answer4"), 42)
|
||||
self.assertEqual(cache.get("answer4", version=1), 42)
|
||||
self.assertEqual(cache.get("answer4", version=2), None)
|
||||
|
||||
self.assertEqual(caches['v2'].get('answer4'), None)
|
||||
self.assertEqual(caches['v2'].get('answer4', version=1), 42)
|
||||
self.assertEqual(caches['v2'].get('answer4', version=2), None)
|
||||
self.assertEqual(caches["v2"].get("answer4"), None)
|
||||
self.assertEqual(caches["v2"].get("answer4", version=1), 42)
|
||||
self.assertEqual(caches["v2"].get("answer4", version=2), None)
|
||||
|
||||
def test_cache_versioning_add(self):
|
||||
|
||||
# add, default version = 1, but manually override version = 2
|
||||
cache.add('answer1', 42, version=2)
|
||||
self.assertEqual(cache.get('answer1', version=1), None)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.add("answer1", 42, version=2)
|
||||
self.assertEqual(cache.get("answer1", version=1), None)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
|
||||
cache.add('answer1', 37, version=2)
|
||||
self.assertEqual(cache.get('answer1', version=1), None)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.add("answer1", 37, version=2)
|
||||
self.assertEqual(cache.get("answer1", version=1), None)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
|
||||
cache.add('answer1', 37, version=1)
|
||||
self.assertEqual(cache.get('answer1', version=1), 37)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.add("answer1", 37, version=1)
|
||||
self.assertEqual(cache.get("answer1", version=1), 37)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
|
||||
# v2 add, using default version = 2
|
||||
caches['v2'].add('answer2', 42)
|
||||
self.assertEqual(cache.get('answer2', version=1), None)
|
||||
self.assertEqual(cache.get('answer2', version=2), 42)
|
||||
caches["v2"].add("answer2", 42)
|
||||
self.assertEqual(cache.get("answer2", version=1), None)
|
||||
self.assertEqual(cache.get("answer2", version=2), 42)
|
||||
|
||||
caches['v2'].add('answer2', 37)
|
||||
self.assertEqual(cache.get('answer2', version=1), None)
|
||||
self.assertEqual(cache.get('answer2', version=2), 42)
|
||||
caches["v2"].add("answer2", 37)
|
||||
self.assertEqual(cache.get("answer2", version=1), None)
|
||||
self.assertEqual(cache.get("answer2", version=2), 42)
|
||||
|
||||
caches['v2'].add('answer2', 37, version=1)
|
||||
self.assertEqual(cache.get('answer2', version=1), 37)
|
||||
self.assertEqual(cache.get('answer2', version=2), 42)
|
||||
caches["v2"].add("answer2", 37, version=1)
|
||||
self.assertEqual(cache.get("answer2", version=1), 37)
|
||||
self.assertEqual(cache.get("answer2", version=2), 42)
|
||||
|
||||
# v2 add, default version = 2, but manually override version = 1
|
||||
caches['v2'].add('answer3', 42, version=1)
|
||||
self.assertEqual(cache.get('answer3', version=1), 42)
|
||||
self.assertEqual(cache.get('answer3', version=2), None)
|
||||
caches["v2"].add("answer3", 42, version=1)
|
||||
self.assertEqual(cache.get("answer3", version=1), 42)
|
||||
self.assertEqual(cache.get("answer3", version=2), None)
|
||||
|
||||
caches['v2'].add('answer3', 37, version=1)
|
||||
self.assertEqual(cache.get('answer3', version=1), 42)
|
||||
self.assertEqual(cache.get('answer3', version=2), None)
|
||||
caches["v2"].add("answer3", 37, version=1)
|
||||
self.assertEqual(cache.get("answer3", version=1), 42)
|
||||
self.assertEqual(cache.get("answer3", version=2), None)
|
||||
|
||||
caches['v2'].add('answer3', 37)
|
||||
self.assertEqual(cache.get('answer3', version=1), 42)
|
||||
self.assertEqual(cache.get('answer3', version=2), 37)
|
||||
caches["v2"].add("answer3", 37)
|
||||
self.assertEqual(cache.get("answer3", version=1), 42)
|
||||
self.assertEqual(cache.get("answer3", version=2), 37)
|
||||
|
||||
def test_cache_versioning_has_key(self):
|
||||
cache.set('answer1', 42)
|
||||
cache.set("answer1", 42)
|
||||
|
||||
# has_key
|
||||
self.assertTrue('answer1' in cache) # noqa
|
||||
self.assertTrue(cache.has_key('answer1', version=1)) # noqa
|
||||
self.assertFalse(cache.has_key('answer1', version=2)) # noqa
|
||||
self.assertTrue("answer1" in cache) # noqa
|
||||
self.assertTrue(cache.has_key("answer1", version=1)) # noqa
|
||||
self.assertFalse(cache.has_key("answer1", version=2)) # noqa
|
||||
|
||||
self.assertFalse('answer1' in caches['v2']) # noqa
|
||||
self.assertTrue(caches['v2'].has_key('answer1', version=1)) # noqa
|
||||
self.assertFalse(caches['v2'].has_key('answer1', version=2)) # noqa
|
||||
self.assertFalse("answer1" in caches["v2"]) # noqa
|
||||
self.assertTrue(caches["v2"].has_key("answer1", version=1)) # noqa
|
||||
self.assertFalse(caches["v2"].has_key("answer1", version=2)) # noqa
|
||||
|
||||
def test_cache_versioning_delete(self):
|
||||
cache.set('answer1', 37, version=1)
|
||||
cache.set('answer1', 42, version=2)
|
||||
cache.delete('answer1')
|
||||
self.assertEqual(cache.get('answer1', version=1), None)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.set("answer1", 37, version=1)
|
||||
cache.set("answer1", 42, version=2)
|
||||
cache.delete("answer1")
|
||||
self.assertEqual(cache.get("answer1", version=1), None)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
|
||||
cache.set('answer2', 37, version=1)
|
||||
cache.set('answer2', 42, version=2)
|
||||
cache.delete('answer2', version=2)
|
||||
self.assertEqual(cache.get('answer2', version=1), 37)
|
||||
self.assertEqual(cache.get('answer2', version=2), None)
|
||||
cache.set("answer2", 37, version=1)
|
||||
cache.set("answer2", 42, version=2)
|
||||
cache.delete("answer2", version=2)
|
||||
self.assertEqual(cache.get("answer2", version=1), 37)
|
||||
self.assertEqual(cache.get("answer2", version=2), None)
|
||||
|
||||
cache.set('answer3', 37, version=1)
|
||||
cache.set('answer3', 42, version=2)
|
||||
caches['v2'].delete('answer3')
|
||||
self.assertEqual(cache.get('answer3', version=1), 37)
|
||||
self.assertEqual(cache.get('answer3', version=2), None)
|
||||
cache.set("answer3", 37, version=1)
|
||||
cache.set("answer3", 42, version=2)
|
||||
caches["v2"].delete("answer3")
|
||||
self.assertEqual(cache.get("answer3", version=1), 37)
|
||||
self.assertEqual(cache.get("answer3", version=2), None)
|
||||
|
||||
cache.set('answer4', 37, version=1)
|
||||
cache.set('answer4', 42, version=2)
|
||||
caches['v2'].delete('answer4', version=1)
|
||||
self.assertEqual(cache.get('answer4', version=1), None)
|
||||
self.assertEqual(cache.get('answer4', version=2), 42)
|
||||
cache.set("answer4", 37, version=1)
|
||||
cache.set("answer4", 42, version=2)
|
||||
caches["v2"].delete("answer4", version=1)
|
||||
self.assertEqual(cache.get("answer4", version=1), None)
|
||||
self.assertEqual(cache.get("answer4", version=2), 42)
|
||||
|
||||
def test_cache_versioning_incr_decr(self):
|
||||
cache.set('answer1', 37, version=1)
|
||||
cache.set('answer1', 42, version=2)
|
||||
cache.incr('answer1')
|
||||
self.assertEqual(cache.get('answer1', version=1), 38)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.decr('answer1')
|
||||
self.assertEqual(cache.get('answer1', version=1), 37)
|
||||
self.assertEqual(cache.get('answer1', version=2), 42)
|
||||
cache.set("answer1", 37, version=1)
|
||||
cache.set("answer1", 42, version=2)
|
||||
cache.incr("answer1")
|
||||
self.assertEqual(cache.get("answer1", version=1), 38)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
cache.decr("answer1")
|
||||
self.assertEqual(cache.get("answer1", version=1), 37)
|
||||
self.assertEqual(cache.get("answer1", version=2), 42)
|
||||
|
||||
cache.set('answer2', 37, version=1)
|
||||
cache.set('answer2', 42, version=2)
|
||||
cache.incr('answer2', version=2)
|
||||
self.assertEqual(cache.get('answer2', version=1), 37)
|
||||
self.assertEqual(cache.get('answer2', version=2), 43)
|
||||
cache.decr('answer2', version=2)
|
||||
self.assertEqual(cache.get('answer2', version=1), 37)
|
||||
self.assertEqual(cache.get('answer2', version=2), 42)
|
||||
cache.set("answer2", 37, version=1)
|
||||
cache.set("answer2", 42, version=2)
|
||||
cache.incr("answer2", version=2)
|
||||
self.assertEqual(cache.get("answer2", version=1), 37)
|
||||
self.assertEqual(cache.get("answer2", version=2), 43)
|
||||
cache.decr("answer2", version=2)
|
||||
self.assertEqual(cache.get("answer2", version=1), 37)
|
||||
self.assertEqual(cache.get("answer2", version=2), 42)
|
||||
|
||||
cache.set('answer3', 37, version=1)
|
||||
cache.set('answer3', 42, version=2)
|
||||
caches['v2'].incr('answer3')
|
||||
self.assertEqual(cache.get('answer3', version=1), 37)
|
||||
self.assertEqual(cache.get('answer3', version=2), 43)
|
||||
caches['v2'].decr('answer3')
|
||||
self.assertEqual(cache.get('answer3', version=1), 37)
|
||||
self.assertEqual(cache.get('answer3', version=2), 42)
|
||||
cache.set("answer3", 37, version=1)
|
||||
cache.set("answer3", 42, version=2)
|
||||
caches["v2"].incr("answer3")
|
||||
self.assertEqual(cache.get("answer3", version=1), 37)
|
||||
self.assertEqual(cache.get("answer3", version=2), 43)
|
||||
caches["v2"].decr("answer3")
|
||||
self.assertEqual(cache.get("answer3", version=1), 37)
|
||||
self.assertEqual(cache.get("answer3", version=2), 42)
|
||||
|
||||
cache.set('answer4', 37, version=1)
|
||||
cache.set('answer4', 42, version=2)
|
||||
caches['v2'].incr('answer4', version=1)
|
||||
self.assertEqual(cache.get('answer4', version=1), 38)
|
||||
self.assertEqual(cache.get('answer4', version=2), 42)
|
||||
caches['v2'].decr('answer4', version=1)
|
||||
self.assertEqual(cache.get('answer4', version=1), 37)
|
||||
self.assertEqual(cache.get('answer4', version=2), 42)
|
||||
cache.set("answer4", 37, version=1)
|
||||
cache.set("answer4", 42, version=2)
|
||||
caches["v2"].incr("answer4", version=1)
|
||||
self.assertEqual(cache.get("answer4", version=1), 38)
|
||||
self.assertEqual(cache.get("answer4", version=2), 42)
|
||||
caches["v2"].decr("answer4", version=1)
|
||||
self.assertEqual(cache.get("answer4", version=1), 37)
|
||||
self.assertEqual(cache.get("answer4", version=2), 42)
|
||||
|
||||
def test_cache_versioning_get_set_many(self):
|
||||
# set, using default version = 1
|
||||
cache.set_many({'ford1': 37, 'arthur1': 42})
|
||||
self.assertEqual(cache.get_many(['ford1', 'arthur1']),
|
||||
{'ford1': 37, 'arthur1': 42})
|
||||
self.assertEqual(cache.get_many(['ford1', 'arthur1'], version=1),
|
||||
{'ford1': 37, 'arthur1': 42})
|
||||
self.assertEqual(cache.get_many(['ford1', 'arthur1'], version=2), {})
|
||||
cache.set_many({"ford1": 37, "arthur1": 42})
|
||||
self.assertEqual(cache.get_many(["ford1", "arthur1"]), {"ford1": 37, "arthur1": 42})
|
||||
self.assertEqual(cache.get_many(["ford1", "arthur1"], version=1), {"ford1": 37, "arthur1": 42})
|
||||
self.assertEqual(cache.get_many(["ford1", "arthur1"], version=2), {})
|
||||
|
||||
self.assertEqual(caches['v2'].get_many(['ford1', 'arthur1']), {})
|
||||
self.assertEqual(caches['v2'].get_many(['ford1', 'arthur1'], version=1),
|
||||
{'ford1': 37, 'arthur1': 42})
|
||||
self.assertEqual(caches['v2'].get_many(['ford1', 'arthur1'], version=2), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford1", "arthur1"]), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford1", "arthur1"], version=1), {"ford1": 37, "arthur1": 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford1", "arthur1"], version=2), {})
|
||||
|
||||
# set, default version = 1, but manually override version = 2
|
||||
cache.set_many({'ford2': 37, 'arthur2': 42}, version=2)
|
||||
self.assertEqual(cache.get_many(['ford2', 'arthur2']), {})
|
||||
self.assertEqual(cache.get_many(['ford2', 'arthur2'], version=1), {})
|
||||
self.assertEqual(cache.get_many(['ford2', 'arthur2'], version=2),
|
||||
{'ford2': 37, 'arthur2': 42})
|
||||
cache.set_many({"ford2": 37, "arthur2": 42}, version=2)
|
||||
self.assertEqual(cache.get_many(["ford2", "arthur2"]), {})
|
||||
self.assertEqual(cache.get_many(["ford2", "arthur2"], version=1), {})
|
||||
self.assertEqual(cache.get_many(["ford2", "arthur2"], version=2), {"ford2": 37, "arthur2": 42})
|
||||
|
||||
self.assertEqual(caches['v2'].get_many(['ford2', 'arthur2']),
|
||||
{'ford2': 37, 'arthur2': 42})
|
||||
self.assertEqual(caches['v2'].get_many(['ford2', 'arthur2'], version=1), {})
|
||||
self.assertEqual(caches['v2'].get_many(['ford2', 'arthur2'], version=2),
|
||||
{'ford2': 37, 'arthur2': 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford2", "arthur2"]), {"ford2": 37, "arthur2": 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford2", "arthur2"], version=1), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford2", "arthur2"], version=2), {"ford2": 37, "arthur2": 42})
|
||||
|
||||
# v2 set, using default version = 2
|
||||
caches['v2'].set_many({'ford3': 37, 'arthur3': 42})
|
||||
self.assertEqual(cache.get_many(['ford3', 'arthur3']), {})
|
||||
self.assertEqual(cache.get_many(['ford3', 'arthur3'], version=1), {})
|
||||
self.assertEqual(cache.get_many(['ford3', 'arthur3'], version=2),
|
||||
{'ford3': 37, 'arthur3': 42})
|
||||
caches["v2"].set_many({"ford3": 37, "arthur3": 42})
|
||||
self.assertEqual(cache.get_many(["ford3", "arthur3"]), {})
|
||||
self.assertEqual(cache.get_many(["ford3", "arthur3"], version=1), {})
|
||||
self.assertEqual(cache.get_many(["ford3", "arthur3"], version=2), {"ford3": 37, "arthur3": 42})
|
||||
|
||||
self.assertEqual(caches['v2'].get_many(['ford3', 'arthur3']),
|
||||
{'ford3': 37, 'arthur3': 42})
|
||||
self.assertEqual(caches['v2'].get_many(['ford3', 'arthur3'], version=1), {})
|
||||
self.assertEqual(caches['v2'].get_many(['ford3', 'arthur3'], version=2),
|
||||
{'ford3': 37, 'arthur3': 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford3", "arthur3"]), {"ford3": 37, "arthur3": 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford3", "arthur3"], version=1), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford3", "arthur3"], version=2), {"ford3": 37, "arthur3": 42})
|
||||
|
||||
# v2 set, default version = 2, but manually override version = 1
|
||||
caches['v2'].set_many({'ford4': 37, 'arthur4': 42}, version=1)
|
||||
self.assertEqual(cache.get_many(['ford4', 'arthur4']),
|
||||
{'ford4': 37, 'arthur4': 42})
|
||||
self.assertEqual(cache.get_many(['ford4', 'arthur4'], version=1),
|
||||
{'ford4': 37, 'arthur4': 42})
|
||||
self.assertEqual(cache.get_many(['ford4', 'arthur4'], version=2), {})
|
||||
caches["v2"].set_many({"ford4": 37, "arthur4": 42}, version=1)
|
||||
self.assertEqual(cache.get_many(["ford4", "arthur4"]), {"ford4": 37, "arthur4": 42})
|
||||
self.assertEqual(cache.get_many(["ford4", "arthur4"], version=1), {"ford4": 37, "arthur4": 42})
|
||||
self.assertEqual(cache.get_many(["ford4", "arthur4"], version=2), {})
|
||||
|
||||
self.assertEqual(caches['v2'].get_many(['ford4', 'arthur4']), {})
|
||||
self.assertEqual(caches['v2'].get_many(['ford4', 'arthur4'], version=1),
|
||||
{'ford4': 37, 'arthur4': 42})
|
||||
self.assertEqual(caches['v2'].get_many(['ford4', 'arthur4'], version=2), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford4", "arthur4"]), {})
|
||||
self.assertEqual(caches["v2"].get_many(["ford4", "arthur4"], version=1), {"ford4": 37, "arthur4": 42})
|
||||
self.assertEqual(caches["v2"].get_many(["ford4", "arthur4"], version=2), {})
|
||||
|
||||
def test_incr_version(self):
|
||||
cache.set('answer', 42, version=2)
|
||||
self.assertEqual(cache.get('answer'), None)
|
||||
self.assertEqual(cache.get('answer', version=1), None)
|
||||
self.assertEqual(cache.get('answer', version=2), 42)
|
||||
self.assertEqual(cache.get('answer', version=3), None)
|
||||
cache.set("answer", 42, version=2)
|
||||
self.assertEqual(cache.get("answer"), None)
|
||||
self.assertEqual(cache.get("answer", version=1), None)
|
||||
self.assertEqual(cache.get("answer", version=2), 42)
|
||||
self.assertEqual(cache.get("answer", version=3), None)
|
||||
|
||||
self.assertEqual(cache.incr_version('answer', version=2), 3)
|
||||
self.assertEqual(cache.get('answer'), None)
|
||||
self.assertEqual(cache.get('answer', version=1), None)
|
||||
self.assertEqual(cache.get('answer', version=2), None)
|
||||
self.assertEqual(cache.get('answer', version=3), 42)
|
||||
self.assertEqual(cache.incr_version("answer", version=2), 3)
|
||||
self.assertEqual(cache.get("answer"), None)
|
||||
self.assertEqual(cache.get("answer", version=1), None)
|
||||
self.assertEqual(cache.get("answer", version=2), None)
|
||||
self.assertEqual(cache.get("answer", version=3), 42)
|
||||
|
||||
caches['v2'].set('answer2', 42)
|
||||
self.assertEqual(caches['v2'].get('answer2'), 42)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=1), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=2), 42)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=3), None)
|
||||
caches["v2"].set("answer2", 42)
|
||||
self.assertEqual(caches["v2"].get("answer2"), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=1), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=2), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=3), None)
|
||||
|
||||
self.assertEqual(caches['v2'].incr_version('answer2'), 3)
|
||||
self.assertEqual(caches['v2'].get('answer2'), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=1), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=2), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=3), 42)
|
||||
self.assertEqual(caches["v2"].incr_version("answer2"), 3)
|
||||
self.assertEqual(caches["v2"].get("answer2"), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=1), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=2), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=3), 42)
|
||||
|
||||
self.assertRaises(ValueError, cache.incr_version, 'does_not_exist')
|
||||
self.assertRaises(ValueError, cache.incr_version, "does_not_exist")
|
||||
|
||||
def test_decr_version(self):
|
||||
cache.set('answer', 42, version=2)
|
||||
self.assertEqual(cache.get('answer'), None)
|
||||
self.assertEqual(cache.get('answer', version=1), None)
|
||||
self.assertEqual(cache.get('answer', version=2), 42)
|
||||
cache.set("answer", 42, version=2)
|
||||
self.assertEqual(cache.get("answer"), None)
|
||||
self.assertEqual(cache.get("answer", version=1), None)
|
||||
self.assertEqual(cache.get("answer", version=2), 42)
|
||||
|
||||
self.assertEqual(cache.decr_version('answer', version=2), 1)
|
||||
self.assertEqual(cache.get('answer'), 42)
|
||||
self.assertEqual(cache.get('answer', version=1), 42)
|
||||
self.assertEqual(cache.get('answer', version=2), None)
|
||||
self.assertEqual(cache.decr_version("answer", version=2), 1)
|
||||
self.assertEqual(cache.get("answer"), 42)
|
||||
self.assertEqual(cache.get("answer", version=1), 42)
|
||||
self.assertEqual(cache.get("answer", version=2), None)
|
||||
|
||||
caches['v2'].set('answer2', 42)
|
||||
self.assertEqual(caches['v2'].get('answer2'), 42)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=1), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=2), 42)
|
||||
caches["v2"].set("answer2", 42)
|
||||
self.assertEqual(caches["v2"].get("answer2"), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=1), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=2), 42)
|
||||
|
||||
self.assertEqual(caches['v2'].decr_version('answer2'), 1)
|
||||
self.assertEqual(caches['v2'].get('answer2'), None)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=1), 42)
|
||||
self.assertEqual(caches['v2'].get('answer2', version=2), None)
|
||||
self.assertEqual(caches["v2"].decr_version("answer2"), 1)
|
||||
self.assertEqual(caches["v2"].get("answer2"), None)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=1), 42)
|
||||
self.assertEqual(caches["v2"].get("answer2", version=2), None)
|
||||
|
||||
self.assertRaises(ValueError, cache.decr_version, 'does_not_exist', version=2)
|
||||
self.assertRaises(ValueError, cache.decr_version, "does_not_exist", version=2)
|
||||
|
||||
def test_custom_key_func(self):
|
||||
# Two caches with different key functions aren't visible to each other
|
||||
cache.set('answer1', 42)
|
||||
self.assertEqual(cache.get('answer1'), 42)
|
||||
self.assertEqual(caches['custom_key'].get('answer1'), None)
|
||||
self.assertEqual(caches['custom_key2'].get('answer1'), None)
|
||||
cache.set("answer1", 42)
|
||||
self.assertEqual(cache.get("answer1"), 42)
|
||||
self.assertEqual(caches["custom_key"].get("answer1"), None)
|
||||
self.assertEqual(caches["custom_key2"].get("answer1"), None)
|
||||
|
||||
caches['custom_key'].set('answer2', 42)
|
||||
self.assertEqual(cache.get('answer2'), None)
|
||||
self.assertEqual(caches['custom_key'].get('answer2'), 42)
|
||||
self.assertEqual(caches['custom_key2'].get('answer2'), 42)
|
||||
caches["custom_key"].set("answer2", 42)
|
||||
self.assertEqual(cache.get("answer2"), None)
|
||||
self.assertEqual(caches["custom_key"].get("answer2"), 42)
|
||||
self.assertEqual(caches["custom_key2"].get("answer2"), 42)
|
||||
|
||||
@override_settings(CACHES={
|
||||
'default': {'BACKEND': 'bedrock.base.cache.SimpleDictCache'},
|
||||
'other': {
|
||||
'BACKEND': 'bedrock.base.cache.SimpleDictCache',
|
||||
'LOCATION': 'other'
|
||||
},
|
||||
})
|
||||
@override_settings(
|
||||
CACHES={
|
||||
"default": {"BACKEND": "bedrock.base.cache.SimpleDictCache"},
|
||||
"other": {"BACKEND": "bedrock.base.cache.SimpleDictCache", "LOCATION": "other"},
|
||||
}
|
||||
)
|
||||
def test_multiple_caches(self):
|
||||
"""Check that multiple locmem caches are isolated"""
|
||||
cache.set('value', 42)
|
||||
self.assertEqual(caches['default'].get('value'), 42)
|
||||
self.assertEqual(caches['other'].get('value'), None)
|
||||
cache.set("value", 42)
|
||||
self.assertEqual(caches["default"].get("value"), 42)
|
||||
self.assertEqual(caches["other"].get("value"), None)
|
||||
|
||||
def test_incr_decr_timeout(self):
|
||||
"""incr/decr does not modify expiry time (matches memcached behavior)"""
|
||||
key = 'value'
|
||||
key = "value"
|
||||
_key = cache.make_key(key)
|
||||
cache.set(key, 1, timeout=cache.default_timeout * 10)
|
||||
expire = cache._expire_info[_key]
|
||||
|
|
|
@ -9,25 +9,26 @@ from bedrock.base.urlresolvers import find_supported, reverse, split_path, Prefi
|
|||
from mock import patch, Mock
|
||||
|
||||
|
||||
@pytest.mark.parametrize('path, result', [
|
||||
# Basic
|
||||
('en-US/some/action', ('en-US', 'some/action')),
|
||||
# First slash doesn't matter
|
||||
('/en-US/some/action', ('en-US', 'some/action')),
|
||||
# Nor does capitalization
|
||||
('En-uS/some/action', ('en-US', 'some/action')),
|
||||
# Unsupported languages return a blank language
|
||||
('unsupported/some/action', ('', 'unsupported/some/action')),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"path, result",
|
||||
[
|
||||
# Basic
|
||||
("en-US/some/action", ("en-US", "some/action")),
|
||||
# First slash doesn't matter
|
||||
("/en-US/some/action", ("en-US", "some/action")),
|
||||
# Nor does capitalization
|
||||
("En-uS/some/action", ("en-US", "some/action")),
|
||||
# Unsupported languages return a blank language
|
||||
("unsupported/some/action", ("", "unsupported/some/action")),
|
||||
],
|
||||
)
|
||||
def test_split_path(path, result):
|
||||
res = split_path(path)
|
||||
assert res == result
|
||||
|
||||
|
||||
# Test urlpatterns
|
||||
urlpatterns = [
|
||||
re_path(r'^test/$', lambda r: None, name='test.view')
|
||||
]
|
||||
urlpatterns = [re_path(r"^test/$", lambda r: None, name="test.view")]
|
||||
|
||||
|
||||
class FakePrefixer:
|
||||
|
@ -35,17 +36,17 @@ class FakePrefixer:
|
|||
self.fix = fix
|
||||
|
||||
|
||||
@patch('bedrock.base.urlresolvers.get_url_prefix')
|
||||
@override_settings(ROOT_URLCONF='bedrock.base.tests.test_urlresolvers')
|
||||
@patch("bedrock.base.urlresolvers.get_url_prefix")
|
||||
@override_settings(ROOT_URLCONF="bedrock.base.tests.test_urlresolvers")
|
||||
class TestReverse(TestCase):
|
||||
def test_unicode_url(self, get_url_prefix):
|
||||
# If the prefixer returns a unicode URL it should be escaped and cast
|
||||
# as a str object.
|
||||
get_url_prefix.return_value = FakePrefixer(lambda p: u'/Françoi%s' % p)
|
||||
result = reverse('test.view')
|
||||
get_url_prefix.return_value = FakePrefixer(lambda p: "/Françoi%s" % p)
|
||||
result = reverse("test.view")
|
||||
|
||||
# Ensure that UTF-8 characters are escaped properly.
|
||||
self.assertEqual(result, '/Fran%C3%A7oi/test/')
|
||||
self.assertEqual(result, "/Fran%C3%A7oi/test/")
|
||||
self.assertEqual(type(result), str)
|
||||
|
||||
|
||||
|
@ -53,109 +54,106 @@ class TestPrefixer(TestCase):
|
|||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
|
||||
@override_settings(LANGUAGE_CODE='en-US')
|
||||
@override_settings(LANGUAGE_CODE="en-US")
|
||||
def test_get_language_default_language_code(self):
|
||||
"""
|
||||
Should return default set by settings.LANGUAGE_CODE if no 'lang'
|
||||
url parameter and no Accept-Language header
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
self.assertFalse('lang' in request.GET)
|
||||
self.assertFalse(request.META.get('HTTP_ACCEPT_LANGUAGE'))
|
||||
request = self.factory.get("/")
|
||||
self.assertFalse("lang" in request.GET)
|
||||
self.assertFalse(request.META.get("HTTP_ACCEPT_LANGUAGE"))
|
||||
prefixer = Prefixer(request)
|
||||
assert prefixer.get_language() == 'en-US'
|
||||
assert prefixer.get_language() == "en-US"
|
||||
|
||||
def test_get_language_returns_best(self):
|
||||
"""
|
||||
Should pass Accept-Language header value to get_best_language
|
||||
and return result
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
request.META['HTTP_ACCEPT_LANGUAGE'] = 'de, es'
|
||||
request = self.factory.get("/")
|
||||
request.META["HTTP_ACCEPT_LANGUAGE"] = "de, es"
|
||||
prefixer = Prefixer(request)
|
||||
prefixer.get_best_language = Mock(return_value='de')
|
||||
assert prefixer.get_language() == 'de'
|
||||
prefixer.get_best_language.assert_called_once_with('de, es')
|
||||
prefixer.get_best_language = Mock(return_value="de")
|
||||
assert prefixer.get_language() == "de"
|
||||
prefixer.get_best_language.assert_called_once_with("de, es")
|
||||
|
||||
@override_settings(LANGUAGE_CODE='en-US')
|
||||
@override_settings(LANGUAGE_CODE="en-US")
|
||||
def test_get_language_no_best(self):
|
||||
"""
|
||||
Should return default set by settings.LANGUAGE_CODE if
|
||||
get_best_language return value is None
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
request.META['HTTP_ACCEPT_LANGUAGE'] = 'de, es'
|
||||
request = self.factory.get("/")
|
||||
request.META["HTTP_ACCEPT_LANGUAGE"] = "de, es"
|
||||
prefixer = Prefixer(request)
|
||||
prefixer.get_best_language = Mock(return_value=None)
|
||||
assert prefixer.get_language() == 'en-US'
|
||||
prefixer.get_best_language.assert_called_once_with('de, es')
|
||||
assert prefixer.get_language() == "en-US"
|
||||
prefixer.get_best_language.assert_called_once_with("de, es")
|
||||
|
||||
@override_settings(LANGUAGE_URL_MAP={'en-us': 'en-US', 'de': 'de'})
|
||||
@override_settings(LANGUAGE_URL_MAP={"en-us": "en-US", "de": "de"})
|
||||
def test_get_best_language_exact_match(self):
|
||||
"""
|
||||
Should return exact match if it is in settings.LANGUAGE_URL_MAP
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
prefixer = Prefixer(request)
|
||||
assert prefixer.get_best_language('de, es') == 'de'
|
||||
assert prefixer.get_best_language("de, es") == "de"
|
||||
|
||||
@override_settings(LANGUAGE_URL_MAP={'en-gb': 'en-GB', 'en-us': 'en-US', 'es-ar': 'es-AR'},
|
||||
CANONICAL_LOCALES={'es': 'es-ES', 'en': 'en-US'})
|
||||
@override_settings(LANGUAGE_URL_MAP={"en-gb": "en-GB", "en-us": "en-US", "es-ar": "es-AR"}, CANONICAL_LOCALES={"es": "es-ES", "en": "en-US"})
|
||||
def test_get_best_language_prefix_match(self):
|
||||
"""
|
||||
Should return a language with a matching prefix from
|
||||
settings.LANGUAGE_URL_MAP + settings.CANONICAL_LOCALES if it exists but
|
||||
no exact match does
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
prefixer = Prefixer(request)
|
||||
assert prefixer.get_best_language('en') == 'en-US'
|
||||
assert prefixer.get_best_language('en-CA') == 'en-US'
|
||||
assert prefixer.get_best_language('en-GB') == 'en-GB'
|
||||
assert prefixer.get_best_language('en-US') == 'en-US'
|
||||
assert prefixer.get_best_language('es') == 'es-ES'
|
||||
assert prefixer.get_best_language('es-AR') == 'es-AR'
|
||||
assert prefixer.get_best_language('es-CL') == 'es-ES'
|
||||
assert prefixer.get_best_language('es-MX') == 'es-ES'
|
||||
assert prefixer.get_best_language("en") == "en-US"
|
||||
assert prefixer.get_best_language("en-CA") == "en-US"
|
||||
assert prefixer.get_best_language("en-GB") == "en-GB"
|
||||
assert prefixer.get_best_language("en-US") == "en-US"
|
||||
assert prefixer.get_best_language("es") == "es-ES"
|
||||
assert prefixer.get_best_language("es-AR") == "es-AR"
|
||||
assert prefixer.get_best_language("es-CL") == "es-ES"
|
||||
assert prefixer.get_best_language("es-MX") == "es-ES"
|
||||
|
||||
@override_settings(LANGUAGE_URL_MAP={'en-us': 'en-US'})
|
||||
@override_settings(LANGUAGE_URL_MAP={"en-us": "en-US"})
|
||||
def test_get_best_language_no_match(self):
|
||||
"""
|
||||
Should return None if there is no exact match or matching
|
||||
prefix
|
||||
"""
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
prefixer = Prefixer(request)
|
||||
assert prefixer.get_best_language('de') is None
|
||||
assert prefixer.get_best_language("de") is None
|
||||
|
||||
@override_settings(LANGUAGE_URL_MAP={'en-ar': 'en-AR', 'en-gb': 'en-GB', 'en-us': 'en-US'},
|
||||
CANONICAL_LOCALES={'en': 'en-US'})
|
||||
@override_settings(LANGUAGE_URL_MAP={"en-ar": "en-AR", "en-gb": "en-GB", "en-us": "en-US"}, CANONICAL_LOCALES={"en": "en-US"})
|
||||
def test_prefixer_with_non_supported_locale(self):
|
||||
"""
|
||||
Should set prefixer.locale to a supported locale that repects CANONICAL_LOCALES
|
||||
when given a URL with a non-supported locale.
|
||||
"""
|
||||
request = self.factory.get('/en-CA/')
|
||||
request = self.factory.get("/en-CA/")
|
||||
prefixer = Prefixer(request)
|
||||
assert prefixer.locale == 'en-US'
|
||||
assert prefixer.locale == "en-US"
|
||||
|
||||
|
||||
@override_settings(LANGUAGE_URL_MAP={'es-ar': 'es-AR', 'en-gb': 'en-GB', 'es-us': 'es-US'},
|
||||
CANONICAL_LOCALES={'es': 'es-ES', 'en': 'en-US'})
|
||||
@override_settings(LANGUAGE_URL_MAP={"es-ar": "es-AR", "en-gb": "en-GB", "es-us": "es-US"}, CANONICAL_LOCALES={"es": "es-ES", "en": "en-US"})
|
||||
class TestFindSupported(TestCase):
|
||||
def test_find_supported(self):
|
||||
assert find_supported('en-CA') == 'en-US'
|
||||
assert find_supported('en-US') == 'en-US'
|
||||
assert find_supported('en-GB') == 'en-GB'
|
||||
assert find_supported('en') == 'en-US'
|
||||
assert find_supported('es-MX') == 'es-ES'
|
||||
assert find_supported('es-AR') == 'es-AR'
|
||||
assert find_supported('es') == 'es-ES'
|
||||
assert find_supported("en-CA") == "en-US"
|
||||
assert find_supported("en-US") == "en-US"
|
||||
assert find_supported("en-GB") == "en-GB"
|
||||
assert find_supported("en") == "en-US"
|
||||
assert find_supported("es-MX") == "es-ES"
|
||||
assert find_supported("es-AR") == "es-AR"
|
||||
assert find_supported("es") == "es-ES"
|
||||
|
||||
def test_find_supported_none(self):
|
||||
"""
|
||||
Should return None if it can't find any supported locale.
|
||||
"""
|
||||
assert find_supported('de') is None
|
||||
assert find_supported('fr') is None
|
||||
assert find_supported('dude') is None
|
||||
assert find_supported("de") is None
|
||||
assert find_supported("fr") is None
|
||||
assert find_supported("dude") is None
|
||||
|
|
|
@ -9,102 +9,107 @@ from bedrock.base.views import geolocate, GeoRedirectView, GeoTemplateView
|
|||
|
||||
class TestGeolocate(TestCase):
|
||||
def get_country(self, country):
|
||||
with patch('bedrock.base.views.get_country_from_request') as geo_mock:
|
||||
with patch("bedrock.base.views.get_country_from_request") as geo_mock:
|
||||
geo_mock.return_value = country
|
||||
rf = RequestFactory()
|
||||
req = rf.get('/')
|
||||
req = rf.get("/")
|
||||
resp = geolocate(req)
|
||||
return json.loads(resp.content)
|
||||
|
||||
def test_geo_returns(self):
|
||||
self.assertDictEqual(self.get_country('US'), {'country_code': 'US'})
|
||||
self.assertDictEqual(self.get_country('FR'), {'country_code': 'FR'})
|
||||
self.assertDictEqual(self.get_country(None), {
|
||||
"error": {
|
||||
"errors": [{
|
||||
"domain": "geolocation",
|
||||
"reason": "notFound",
|
||||
self.assertDictEqual(self.get_country("US"), {"country_code": "US"})
|
||||
self.assertDictEqual(self.get_country("FR"), {"country_code": "FR"})
|
||||
self.assertDictEqual(
|
||||
self.get_country(None),
|
||||
{
|
||||
"error": {
|
||||
"errors": [
|
||||
{
|
||||
"domain": "geolocation",
|
||||
"reason": "notFound",
|
||||
"message": "Not found",
|
||||
}
|
||||
],
|
||||
"code": 404,
|
||||
"message": "Not found",
|
||||
}],
|
||||
"code": 404,
|
||||
"message": "Not found",
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
geo_view = GeoRedirectView.as_view(
|
||||
geo_urls={
|
||||
'CA': 'firefox.new',
|
||||
'US': 'firefox',
|
||||
"CA": "firefox.new",
|
||||
"US": "firefox",
|
||||
},
|
||||
default_url='https://abide.dude'
|
||||
default_url="https://abide.dude",
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DEV=False)
|
||||
class TestGeoRedirectView(TestCase):
|
||||
def get_response(self, country):
|
||||
with patch('bedrock.base.views.get_country_from_request') as geo_mock:
|
||||
with patch("bedrock.base.views.get_country_from_request") as geo_mock:
|
||||
geo_mock.return_value = country
|
||||
rf = RequestFactory()
|
||||
req = rf.get('/')
|
||||
req = rf.get("/")
|
||||
return geo_view(req)
|
||||
|
||||
def test_special_country(self):
|
||||
resp = self.get_response('CA')
|
||||
resp = self.get_response("CA")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == '/firefox/new/'
|
||||
assert resp["location"] == "/firefox/new/"
|
||||
|
||||
resp = self.get_response('US')
|
||||
resp = self.get_response("US")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == '/firefox/'
|
||||
assert resp["location"] == "/firefox/"
|
||||
|
||||
def test_other_country(self):
|
||||
resp = self.get_response('DE')
|
||||
resp = self.get_response("DE")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == 'https://abide.dude'
|
||||
assert resp["location"] == "https://abide.dude"
|
||||
|
||||
resp = self.get_response('JA')
|
||||
resp = self.get_response("JA")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == 'https://abide.dude'
|
||||
assert resp["location"] == "https://abide.dude"
|
||||
|
||||
def test_invalid_country(self):
|
||||
resp = self.get_response('dude')
|
||||
resp = self.get_response("dude")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == 'https://abide.dude'
|
||||
assert resp["location"] == "https://abide.dude"
|
||||
|
||||
resp = self.get_response('42')
|
||||
resp = self.get_response("42")
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'] == 'https://abide.dude'
|
||||
assert resp["location"] == "https://abide.dude"
|
||||
|
||||
|
||||
geo_template_view = GeoTemplateView.as_view(
|
||||
geo_template_names={
|
||||
'DE': 'firefox-klar.html',
|
||||
'GB': 'firefox-focus.html',
|
||||
"DE": "firefox-klar.html",
|
||||
"GB": "firefox-focus.html",
|
||||
},
|
||||
template_name='firefox-mobile.html',
|
||||
template_name="firefox-mobile.html",
|
||||
)
|
||||
|
||||
|
||||
class TestGeoTemplateView(TestCase):
|
||||
def get_template(self, country):
|
||||
with patch('bedrock.firefox.views.l10n_utils.render') as render_mock:
|
||||
with patch('bedrock.base.views.get_country_from_request') as geo_mock:
|
||||
with patch("bedrock.firefox.views.l10n_utils.render") as render_mock:
|
||||
with patch("bedrock.base.views.get_country_from_request") as geo_mock:
|
||||
geo_mock.return_value = country
|
||||
rf = RequestFactory()
|
||||
req = rf.get('/')
|
||||
req = rf.get("/")
|
||||
geo_template_view(req)
|
||||
return render_mock.call_args[0][1][0]
|
||||
|
||||
def test_country_template(self):
|
||||
template = self.get_template('DE')
|
||||
assert template == 'firefox-klar.html'
|
||||
template = self.get_template("DE")
|
||||
assert template == "firefox-klar.html"
|
||||
|
||||
def test_default_template(self):
|
||||
template = self.get_template('US')
|
||||
assert template == 'firefox-mobile.html'
|
||||
template = self.get_template("US")
|
||||
assert template == "firefox-mobile.html"
|
||||
|
||||
def test_no_country(self):
|
||||
template = self.get_template(None)
|
||||
assert template == 'firefox-mobile.html'
|
||||
assert template == "firefox-mobile.html"
|
||||
|
|
|
@ -5,7 +5,7 @@ from mock import patch
|
|||
from bedrock.base import waffle
|
||||
|
||||
|
||||
@patch('bedrock.base.waffle.config')
|
||||
@patch("bedrock.base.waffle.config")
|
||||
def test_switch_helper(config_mock):
|
||||
waffle.switch('dude-and-walter')
|
||||
config_mock.assert_called_with('DUDE_AND_WALTER', namespace='SWITCH', default=str(settings.DEV), parser=bool)
|
||||
waffle.switch("dude-and-walter")
|
||||
config_mock.assert_called_with("DUDE_AND_WALTER", namespace="SWITCH", default=str(settings.DEV), parser=bool)
|
||||
|
|
|
@ -6,74 +6,73 @@ from bedrock.mozorg.tests import TestCase
|
|||
|
||||
|
||||
GOOD_CONFIG = {
|
||||
'THE_DUDE': 'abides',
|
||||
'BOWLING': 'true',
|
||||
"THE_DUDE": "abides",
|
||||
"BOWLING": "true",
|
||||
}
|
||||
|
||||
|
||||
@patch.object(waffle_config, 'get_config_dict',
|
||||
return_value=GOOD_CONFIG)
|
||||
@patch.object(waffle_config, "get_config_dict", return_value=GOOD_CONFIG)
|
||||
class TestConfigDBEnv(TestCase):
|
||||
def setUp(self):
|
||||
self.cdbe = waffle_config.ConfigDBEnv()
|
||||
self.config = ConfigManager([self.cdbe])
|
||||
|
||||
def test_db_config(self, gcd_mock):
|
||||
self.assertEqual('abides', self.config('the_dude'))
|
||||
self.assertEqual('abides', self.config('dude', namespace='the'))
|
||||
self.assertTrue(self.config('bowling', parser=bool))
|
||||
self.assertTrue(self.config('BOWLING', parser=bool))
|
||||
self.assertEqual("abides", self.config("the_dude"))
|
||||
self.assertEqual("abides", self.config("dude", namespace="the"))
|
||||
self.assertTrue(self.config("bowling", parser=bool))
|
||||
self.assertTrue(self.config("BOWLING", parser=bool))
|
||||
with self.assertRaises(ConfigurationMissingError):
|
||||
self.config('donnie')
|
||||
self.config("donnie")
|
||||
|
||||
def test_db_config_cache(self, gcd_mock):
|
||||
self.assertEqual('abides', self.config('the_dude'))
|
||||
self.assertEqual('abides', self.config('the_dude'))
|
||||
self.assertEqual("abides", self.config("the_dude"))
|
||||
self.assertEqual("abides", self.config("the_dude"))
|
||||
self.assertEqual(gcd_mock.call_count, 1)
|
||||
self.cdbe.last_update -= self.cdbe.timeout + 1
|
||||
self.assertEqual('abides', self.config('the_dude'))
|
||||
self.assertEqual('abides', self.config('the_dude'))
|
||||
self.assertEqual("abides", self.config("the_dude"))
|
||||
self.assertEqual("abides", self.config("the_dude"))
|
||||
self.assertEqual(gcd_mock.call_count, 2)
|
||||
|
||||
|
||||
class TestDictOf(TestCase):
|
||||
def test_dict_of(self):
|
||||
parser = waffle_config.DictOf(int)
|
||||
assert parser('dude:1,walter:2,donnie:3') == {
|
||||
'dude': 1,
|
||||
'walter': 2,
|
||||
'donnie': 3,
|
||||
assert parser("dude:1,walter:2,donnie:3") == {
|
||||
"dude": 1,
|
||||
"walter": 2,
|
||||
"donnie": 3,
|
||||
}
|
||||
|
||||
def test_dict_of_whitespace(self):
|
||||
parser = waffle_config.DictOf(int)
|
||||
assert parser(' dude:1, walter: 2 , donnie : 3 ') == {
|
||||
'dude': 1,
|
||||
'walter': 2,
|
||||
'donnie': 3,
|
||||
assert parser(" dude:1, walter: 2 , donnie : 3 ") == {
|
||||
"dude": 1,
|
||||
"walter": 2,
|
||||
"donnie": 3,
|
||||
}
|
||||
|
||||
def test_dict_of_floats(self):
|
||||
parser = waffle_config.DictOf(float)
|
||||
assert parser('dude:1,walter:2,donnie:3.3') == {
|
||||
'dude': 1.0,
|
||||
'walter': 2.0,
|
||||
'donnie': 3.3,
|
||||
assert parser("dude:1,walter:2,donnie:3.3") == {
|
||||
"dude": 1.0,
|
||||
"walter": 2.0,
|
||||
"donnie": 3.3,
|
||||
}
|
||||
|
||||
def test_dict_of_strings(self):
|
||||
parser = waffle_config.DictOf(str)
|
||||
assert parser('dude:abides,walter:rages,donnie:bowls') == {
|
||||
'dude': 'abides',
|
||||
'walter': 'rages',
|
||||
'donnie': 'bowls',
|
||||
assert parser("dude:abides,walter:rages,donnie:bowls") == {
|
||||
"dude": "abides",
|
||||
"walter": "rages",
|
||||
"donnie": "bowls",
|
||||
}
|
||||
|
||||
def test_wrong_type(self):
|
||||
parser = waffle_config.DictOf(int)
|
||||
with self.assertRaises(ValueError):
|
||||
parser('dude:abides,walter:2,donnie:3')
|
||||
parser("dude:abides,walter:2,donnie:3")
|
||||
|
||||
def test_empty(self):
|
||||
parser = waffle_config.DictOf(int)
|
||||
assert parser('') == {}
|
||||
assert parser("") == {}
|
||||
|
|
|
@ -18,7 +18,7 @@ def set_url_prefix(prefix):
|
|||
|
||||
def get_url_prefix():
|
||||
"""Get the prefix for the current thread, or None."""
|
||||
return getattr(_local, 'prefix', None)
|
||||
return getattr(_local, "prefix", None)
|
||||
|
||||
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
|
||||
|
@ -26,7 +26,7 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
|
|||
prefixer = get_url_prefix()
|
||||
|
||||
if prefixer:
|
||||
prefix = prefix or '/'
|
||||
prefix = prefix or "/"
|
||||
url = django_reverse(viewname, urlconf, args, kwargs, prefix)
|
||||
if prefixer:
|
||||
url = prefixer.fix(url)
|
||||
|
@ -50,8 +50,7 @@ def _get_language_map():
|
|||
# en to en-GB (not en-US), es to es-AR (not es-ES), etc. in alphabetical
|
||||
# order. To override this behavior, explicitly define a preferred locale
|
||||
# map with the CANONICAL_LOCALES setting.
|
||||
langs.update((k.split('-')[0], v) for k, v in LUM.items() if
|
||||
k.split('-')[0] not in langs)
|
||||
langs.update((k.split("-")[0], v) for k, v in LUM.items() if k.split("-")[0] not in langs)
|
||||
return langs
|
||||
|
||||
|
||||
|
@ -63,7 +62,7 @@ def find_supported(lang):
|
|||
lang = lang.lower()
|
||||
if lang in FULL_LANGUAGE_MAP:
|
||||
return FULL_LANGUAGE_MAP[lang]
|
||||
pre = lang.split('-')[0]
|
||||
pre = lang.split("-")[0]
|
||||
if pre in FULL_LANGUAGE_MAP:
|
||||
return FULL_LANGUAGE_MAP[pre]
|
||||
|
||||
|
@ -74,16 +73,16 @@ def split_path(path_):
|
|||
|
||||
locale will be empty if it isn't found.
|
||||
"""
|
||||
path = path_.lstrip('/')
|
||||
path = path_.lstrip("/")
|
||||
|
||||
# Use partition instead of split since it always returns 3 parts
|
||||
first, _, rest = path.partition('/')
|
||||
first, _, rest = path.partition("/")
|
||||
|
||||
supported = find_supported(first)
|
||||
if supported:
|
||||
return supported, rest
|
||||
else:
|
||||
return '', path
|
||||
return "", path
|
||||
|
||||
|
||||
class Prefixer:
|
||||
|
@ -98,7 +97,7 @@ class Prefixer:
|
|||
user's Accept-Language header to determine which is best. This
|
||||
mostly follows the RFCs but read bug 439568 for details.
|
||||
"""
|
||||
accept_lang = self.request.META.get('HTTP_ACCEPT_LANGUAGE')
|
||||
accept_lang = self.request.META.get("HTTP_ACCEPT_LANGUAGE")
|
||||
if accept_lang:
|
||||
best = self.get_best_language(accept_lang)
|
||||
if best:
|
||||
|
@ -115,13 +114,13 @@ class Prefixer:
|
|||
return supported
|
||||
|
||||
def fix(self, path):
|
||||
path = path.lstrip('/')
|
||||
url_parts = [self.request.META['SCRIPT_NAME']]
|
||||
path = path.lstrip("/")
|
||||
url_parts = [self.request.META["SCRIPT_NAME"]]
|
||||
|
||||
if path.partition('/')[0] not in settings.SUPPORTED_NONLOCALES:
|
||||
if path.partition("/")[0] not in settings.SUPPORTED_NONLOCALES:
|
||||
locale = self.locale if self.locale else self.get_language()
|
||||
url_parts.append(locale)
|
||||
|
||||
url_parts.append(path)
|
||||
|
||||
return '/'.join(url_parts)
|
||||
return "/".join(url_parts)
|
||||
|
|
|
@ -26,6 +26,7 @@ class GeoTemplateView(l10n_utils.L10nTemplateView):
|
|||
If the requesting country isn't in the list it falls back to the `template_name`
|
||||
setting like the normal TemplateView class.
|
||||
"""
|
||||
|
||||
# dict of country codes to template names
|
||||
geo_template_names = None
|
||||
|
||||
|
@ -49,7 +50,7 @@ class GeoRedirectView(RedirectView):
|
|||
def get_redirect_url(self, *args, **kwargs):
|
||||
country_code = get_country_from_request(self.request)
|
||||
url = self.geo_urls.get(country_code, self.default_url)
|
||||
if re.match(r'https?://', url, re.I):
|
||||
if re.match(r"https?://", url, re.I):
|
||||
self.url = url
|
||||
else:
|
||||
self.pattern_name = url
|
||||
|
@ -69,33 +70,40 @@ def geolocate(request):
|
|||
"""
|
||||
country_code = get_country_from_request(request)
|
||||
if country_code is None:
|
||||
return JsonResponse({
|
||||
"error": {
|
||||
"errors": [{
|
||||
"domain": "geolocation",
|
||||
"reason": "notFound",
|
||||
return JsonResponse(
|
||||
{
|
||||
"error": {
|
||||
"errors": [
|
||||
{
|
||||
"domain": "geolocation",
|
||||
"reason": "notFound",
|
||||
"message": "Not found",
|
||||
}
|
||||
],
|
||||
"code": 404,
|
||||
"message": "Not found",
|
||||
}],
|
||||
"code": 404,
|
||||
"message": "Not found",
|
||||
}
|
||||
}, status=404)
|
||||
}
|
||||
},
|
||||
status=404,
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'country_code': country_code,
|
||||
})
|
||||
return JsonResponse(
|
||||
{
|
||||
"country_code": country_code,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# file names and max seconds since last run
|
||||
HEALTH_FILES = (
|
||||
('download_database', 600),
|
||||
('update_locales', 600),
|
||||
("download_database", 600),
|
||||
("update_locales", 600),
|
||||
)
|
||||
DB_INFO_FILE = getenv('AWS_DB_JSON_DATA_FILE', f'{settings.DATA_PATH}/bedrock_db_info.json')
|
||||
GIT_SHA = getenv('GIT_SHA')
|
||||
BUCKET_NAME = getenv('AWS_DB_S3_BUCKET', 'bedrock-db-dev')
|
||||
REGION_NAME = os.getenv('AWS_DB_REGION', 'us-west-2')
|
||||
S3_BASE_URL = 'https://s3-{}.amazonaws.com/{}'.format(
|
||||
DB_INFO_FILE = getenv("AWS_DB_JSON_DATA_FILE", f"{settings.DATA_PATH}/bedrock_db_info.json")
|
||||
GIT_SHA = getenv("GIT_SHA")
|
||||
BUCKET_NAME = getenv("AWS_DB_S3_BUCKET", "bedrock-db-dev")
|
||||
REGION_NAME = os.getenv("AWS_DB_REGION", "us-west-2")
|
||||
S3_BASE_URL = "https://s3-{}.amazonaws.com/{}".format(
|
||||
REGION_NAME,
|
||||
BUCKET_NAME,
|
||||
)
|
||||
|
@ -104,38 +112,41 @@ S3_BASE_URL = 'https://s3-{}.amazonaws.com/{}'.format(
|
|||
def get_l10n_repo_info():
|
||||
repo = git.GitRepo(settings.LOCALES_PATH, settings.LOCALES_REPO)
|
||||
fluent_repo = git.GitRepo(settings.FLUENT_REPO_PATH, settings.FLUENT_REPO_URL)
|
||||
return ({
|
||||
'latest_ref': repo.current_hash,
|
||||
'last_updated': repo.last_updated,
|
||||
'repo_url': repo.clean_remote_url,
|
||||
}, {
|
||||
'latest_ref': fluent_repo.current_hash,
|
||||
'last_updated': fluent_repo.last_updated,
|
||||
'repo_url': fluent_repo.clean_remote_url,
|
||||
})
|
||||
return (
|
||||
{
|
||||
"latest_ref": repo.current_hash,
|
||||
"last_updated": repo.last_updated,
|
||||
"repo_url": repo.clean_remote_url,
|
||||
},
|
||||
{
|
||||
"latest_ref": fluent_repo.current_hash,
|
||||
"last_updated": fluent_repo.last_updated,
|
||||
"repo_url": fluent_repo.clean_remote_url,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_db_file_url(filename):
|
||||
return '/'.join([S3_BASE_URL, filename])
|
||||
return "/".join([S3_BASE_URL, filename])
|
||||
|
||||
|
||||
def get_extra_server_info():
|
||||
server_name = [getattr(settings, x) for x in ['HOSTNAME', 'CLUSTER_NAME']]
|
||||
server_name = '.'.join(x for x in server_name if x)
|
||||
server_name = [getattr(settings, x) for x in ["HOSTNAME", "CLUSTER_NAME"]]
|
||||
server_name = ".".join(x for x in server_name if x)
|
||||
server_info = {
|
||||
'name': server_name,
|
||||
'git_sha': GIT_SHA,
|
||||
"name": server_name,
|
||||
"git_sha": GIT_SHA,
|
||||
}
|
||||
try:
|
||||
with open(DB_INFO_FILE, 'r') as fp:
|
||||
with open(DB_INFO_FILE, "r") as fp:
|
||||
db_info = json.load(fp)
|
||||
except (IOError, ValueError):
|
||||
pass
|
||||
else:
|
||||
db_info['last_update'] = timeago.format(datetime.fromtimestamp(db_info['updated']))
|
||||
db_info['file_url'] = get_db_file_url(db_info['file_name'])
|
||||
db_info["last_update"] = timeago.format(datetime.fromtimestamp(db_info["updated"]))
|
||||
db_info["file_url"] = get_db_file_url(db_info["file_name"])
|
||||
for key, value in db_info.items():
|
||||
server_info['db_%s' % key] = value
|
||||
server_info["db_%s" % key] = value
|
||||
|
||||
return server_info
|
||||
|
||||
|
@ -146,12 +157,12 @@ def cron_health_check(request):
|
|||
results = []
|
||||
check_pass = True
|
||||
for fname, max_time in HEALTH_FILES:
|
||||
fpath = f'{settings.DATA_PATH}/last-run-{fname}'
|
||||
fpath = f"{settings.DATA_PATH}/last-run-{fname}"
|
||||
try:
|
||||
last_check = os.path.getmtime(fpath)
|
||||
except OSError:
|
||||
check_pass = False
|
||||
results.append((fname, max_time, 'None', False))
|
||||
results.append((fname, max_time, "None", False))
|
||||
continue
|
||||
|
||||
time_since = int(time() - last_check)
|
||||
|
@ -163,7 +174,7 @@ def cron_health_check(request):
|
|||
|
||||
results.append((fname, max_time, time_since, task_pass))
|
||||
|
||||
git_repos = git.GitRepoState.objects.exclude(repo_name='').order_by('repo_name', '-latest_ref_timestamp')
|
||||
git_repos = git.GitRepoState.objects.exclude(repo_name="").order_by("repo_name", "-latest_ref_timestamp")
|
||||
unique_repos = {}
|
||||
for repo in git_repos:
|
||||
if repo.repo_name in unique_repos:
|
||||
|
@ -171,21 +182,26 @@ def cron_health_check(request):
|
|||
unique_repos[repo.repo_name] = repo
|
||||
|
||||
l10n_repo, fluent_repo = get_l10n_repo_info()
|
||||
return render(request, 'cron-health-check.html', {
|
||||
'results': results,
|
||||
'server_info': get_extra_server_info(),
|
||||
'success': check_pass,
|
||||
'git_repos': unique_repos.values(),
|
||||
'l10n_repo': l10n_repo,
|
||||
'fluent_repo': fluent_repo,
|
||||
}, status=200 if check_pass else 500)
|
||||
return render(
|
||||
request,
|
||||
"cron-health-check.html",
|
||||
{
|
||||
"results": results,
|
||||
"server_info": get_extra_server_info(),
|
||||
"success": check_pass,
|
||||
"git_repos": unique_repos.values(),
|
||||
"l10n_repo": l10n_repo,
|
||||
"fluent_repo": fluent_repo,
|
||||
},
|
||||
status=200 if check_pass else 500,
|
||||
)
|
||||
|
||||
|
||||
def server_error_view(request, template_name='500.html'):
|
||||
def server_error_view(request, template_name="500.html"):
|
||||
"""500 error handler that runs context processors."""
|
||||
return l10n_utils.render(request, template_name, ftl_files=['500'], status=500)
|
||||
return l10n_utils.render(request, template_name, ftl_files=["500"], status=500)
|
||||
|
||||
|
||||
def page_not_found_view(request, exception=None, template_name='404.html'):
|
||||
def page_not_found_view(request, exception=None, template_name="404.html"):
|
||||
"""404 error handler that runs context processors."""
|
||||
return l10n_utils.render(request, template_name, ftl_files=['404', '500'], status=404)
|
||||
return l10n_utils.render(request, template_name, ftl_files=["404", "500"], status=404)
|
||||
|
|
|
@ -20,5 +20,5 @@ def switch(name):
|
|||
would check for an environment variable called `SWITCH_DUDE_AND_WALTER`. The string from the
|
||||
`switch()` call is converted to uppercase and dashes replaced with underscores.
|
||||
"""
|
||||
env_name = name.upper().replace('-', '_')
|
||||
return config(env_name, default=str(settings.DEV), parser=bool, namespace='SWITCH')
|
||||
env_name = name.upper().replace("-", "_")
|
||||
return config(env_name, default=str(settings.DEV), parser=bool, namespace="SWITCH")
|
||||
|
|
|
@ -18,6 +18,7 @@ class DictOf:
|
|||
>>> parser('en:10,de:20')
|
||||
{'en': 10, 'de': 20}
|
||||
"""
|
||||
|
||||
def __init__(self, val_parser):
|
||||
self.val_parser = val_parser
|
||||
|
||||
|
@ -28,8 +29,8 @@ class DictOf:
|
|||
if not val:
|
||||
return out
|
||||
|
||||
for part in val.split(','):
|
||||
k, v = part.split(':')
|
||||
for part in val.split(","):
|
||||
k, v = part.split(":")
|
||||
out[k.strip()] = val_parser(v.strip())
|
||||
return out
|
||||
|
||||
|
@ -64,8 +65,10 @@ class ConfigDBEnv(ConfigDictEnv):
|
|||
return configs
|
||||
|
||||
|
||||
config = ConfigManager([
|
||||
ConfigOSEnv(),
|
||||
ConfigEnvFileEnv('.env'),
|
||||
ConfigDBEnv(),
|
||||
])
|
||||
config = ConfigManager(
|
||||
[
|
||||
ConfigOSEnv(),
|
||||
ConfigEnvFileEnv(".env"),
|
||||
ConfigDBEnv(),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -7,31 +7,28 @@ from bedrock.utils.git import GitRepo
|
|||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from git.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git."),
|
||||
|
||||
def output(self, msg):
|
||||
if not self.quiet:
|
||||
print(msg)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.quiet = options['quiet']
|
||||
repo = GitRepo(settings.CONTENT_CARDS_PATH, settings.CONTENT_CARDS_REPO,
|
||||
branch_name=settings.CONTENT_CARDS_BRANCH, name='Content Cards')
|
||||
self.output('Updating git repo')
|
||||
self.quiet = options["quiet"]
|
||||
repo = GitRepo(settings.CONTENT_CARDS_PATH, settings.CONTENT_CARDS_REPO, branch_name=settings.CONTENT_CARDS_BRANCH, name="Content Cards")
|
||||
self.output("Updating git repo")
|
||||
repo.update()
|
||||
if not (options['force'] or repo.has_changes()):
|
||||
self.output('No content card updates')
|
||||
if not (options["force"] or repo.has_changes()):
|
||||
self.output("No content card updates")
|
||||
return
|
||||
|
||||
self.output('Loading content cards into database')
|
||||
self.output("Loading content cards into database")
|
||||
count = ContentCard.objects.refresh()
|
||||
|
||||
self.output('%s content cards successfully loaded' % count)
|
||||
self.output("%s content cards successfully loaded" % count)
|
||||
|
||||
repo.set_db_latest()
|
||||
|
||||
self.output('Saved latest git repo state to database')
|
||||
self.output('Done!')
|
||||
self.output("Saved latest git repo state to database")
|
||||
self.output("Done!")
|
||||
|
|
|
@ -11,7 +11,7 @@ from pathlib import Path
|
|||
from bedrock.base.urlresolvers import reverse
|
||||
|
||||
|
||||
URL_RE = re.compile(r'^https?://', re.I)
|
||||
URL_RE = re.compile(r"^https?://", re.I)
|
||||
|
||||
|
||||
def get_page_content_cards(page_name, locale):
|
||||
|
@ -19,45 +19,47 @@ def get_page_content_cards(page_name, locale):
|
|||
|
||||
|
||||
def get_data_from_file_path(file_path):
|
||||
card_name, locale = file_path.stem.split('.')
|
||||
card_name, locale = file_path.stem.split(".")
|
||||
page_name = file_path.parts[-2]
|
||||
page_id = '{}-{}-{}'.format(page_name, locale, card_name)
|
||||
page_id = "{}-{}-{}".format(page_name, locale, card_name)
|
||||
return {
|
||||
'locale': locale,
|
||||
'card_name': card_name,
|
||||
'page_name': page_name,
|
||||
'page_id': page_id,
|
||||
"locale": locale,
|
||||
"card_name": card_name,
|
||||
"page_name": page_name,
|
||||
"page_id": page_id,
|
||||
}
|
||||
|
||||
|
||||
class ContentCardManager(models.Manager):
|
||||
def get_card(self, page_name, name, locale='en-US'):
|
||||
card_id = '{}-{}-{}'.format(page_name, locale, name)
|
||||
def get_card(self, page_name, name, locale="en-US"):
|
||||
card_id = "{}-{}-{}".format(page_name, locale, name)
|
||||
return self.get(id=card_id)
|
||||
|
||||
def get_page_cards(self, page_name, locale='en-US'):
|
||||
def get_page_cards(self, page_name, locale="en-US"):
|
||||
cards = self.filter(page_name=page_name, locale=locale)
|
||||
return {c.card_name: c.card_data for c in cards}
|
||||
|
||||
def refresh(self):
|
||||
card_objs = []
|
||||
cc_path = Path(settings.CONTENT_CARDS_PATH, 'content')
|
||||
cc_path = Path(settings.CONTENT_CARDS_PATH, "content")
|
||||
with transaction.atomic(using=self.db):
|
||||
self.all().delete()
|
||||
cc_files = cc_path.glob('*/*.json')
|
||||
cc_files = cc_path.glob("*/*.json")
|
||||
for ccf in cc_files:
|
||||
path_data = get_data_from_file_path(ccf)
|
||||
with ccf.open(encoding='utf-8') as ccfo:
|
||||
with ccf.open(encoding="utf-8") as ccfo:
|
||||
data = json.load(ccfo)
|
||||
|
||||
card_objs.append(ContentCard(
|
||||
id=path_data['page_id'],
|
||||
card_name=path_data['card_name'],
|
||||
page_name=path_data['page_name'],
|
||||
locale=path_data['locale'],
|
||||
content=data.pop('html_content', ''),
|
||||
data=data,
|
||||
))
|
||||
card_objs.append(
|
||||
ContentCard(
|
||||
id=path_data["page_id"],
|
||||
card_name=path_data["card_name"],
|
||||
page_name=path_data["page_name"],
|
||||
locale=path_data["locale"],
|
||||
content=data.pop("html_content", ""),
|
||||
data=data,
|
||||
)
|
||||
)
|
||||
self.bulk_create(card_objs)
|
||||
|
||||
return len(card_objs)
|
||||
|
@ -74,10 +76,10 @@ class ContentCard(models.Model):
|
|||
objects = ContentCardManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ('id',)
|
||||
ordering = ("id",)
|
||||
|
||||
def __str__(self):
|
||||
return '{} ({})'.format(self.card_name, self.locale)
|
||||
return "{} ({})".format(self.card_name, self.locale)
|
||||
|
||||
@property
|
||||
def html(self):
|
||||
|
@ -88,30 +90,28 @@ class ContentCard(models.Model):
|
|||
"""Return a dict appropriate for calling the card() macro"""
|
||||
data = {}
|
||||
data.update(self.data)
|
||||
if 'image' in data:
|
||||
data['image_url'] = '%scontentcards/img/%s' % (settings.CONTENT_CARDS_URL,
|
||||
data['image'])
|
||||
del data['image']
|
||||
if "image" in data:
|
||||
data["image_url"] = "%scontentcards/img/%s" % (settings.CONTENT_CARDS_URL, data["image"])
|
||||
del data["image"]
|
||||
|
||||
if 'highres_image' in data:
|
||||
data['highres_image_url'] = '%scontentcards/img/%s' % (settings.CONTENT_CARDS_URL,
|
||||
data['highres_image'])
|
||||
del data['highres_image']
|
||||
if "highres_image" in data:
|
||||
data["highres_image_url"] = "%scontentcards/img/%s" % (settings.CONTENT_CARDS_URL, data["highres_image"])
|
||||
del data["highres_image"]
|
||||
|
||||
if 'ga_title' not in data:
|
||||
data['ga_title'] = data['title']
|
||||
if "ga_title" not in data:
|
||||
data["ga_title"] = data["title"]
|
||||
|
||||
if 'media_icon' in data:
|
||||
data['media_icon'] = 'mzp-has-%s' % data['media_icon']
|
||||
if "media_icon" in data:
|
||||
data["media_icon"] = "mzp-has-%s" % data["media_icon"]
|
||||
|
||||
if 'aspect_ratio' in data:
|
||||
data['aspect_ratio'] = 'mzp-has-aspect-%s' % data['aspect_ratio']
|
||||
if "aspect_ratio" in data:
|
||||
data["aspect_ratio"] = "mzp-has-aspect-%s" % data["aspect_ratio"]
|
||||
|
||||
if 'size' in data:
|
||||
data['class'] = 'mzp-c-card-%s' % data['size']
|
||||
del data['size']
|
||||
if "size" in data:
|
||||
data["class"] = "mzp-c-card-%s" % data["size"]
|
||||
del data["size"]
|
||||
|
||||
if 'link_url' in data and not URL_RE.match(data['link_url']):
|
||||
data['link_url'] = reverse(data['link_url'])
|
||||
if "link_url" in data and not URL_RE.match(data["link_url"]):
|
||||
data["link_url"] = reverse(data["link_url"])
|
||||
|
||||
return data
|
||||
|
|
|
@ -8,35 +8,35 @@ from bedrock.contentcards import models
|
|||
from bedrock.mozorg.tests import TestCase
|
||||
|
||||
|
||||
DATA_PATH = Path(__file__).parent.joinpath('test_data')
|
||||
DATA_PATH = Path(__file__).parent.joinpath("test_data")
|
||||
|
||||
|
||||
class TestGetDataFromFilePath(TestCase):
|
||||
def test_the_func(self):
|
||||
self.assertDictEqual(
|
||||
models.get_data_from_file_path(Path('content/home/dude.en-US.json')),
|
||||
models.get_data_from_file_path(Path("content/home/dude.en-US.json")),
|
||||
{
|
||||
'locale': 'en-US',
|
||||
'card_name': 'dude',
|
||||
'page_name': 'home',
|
||||
'page_id': 'home-en-US-dude',
|
||||
}
|
||||
"locale": "en-US",
|
||||
"card_name": "dude",
|
||||
"page_name": "home",
|
||||
"page_id": "home-en-US-dude",
|
||||
},
|
||||
)
|
||||
self.assertDictEqual(
|
||||
models.get_data_from_file_path(Path('content/the-dude/10th.de.json')),
|
||||
models.get_data_from_file_path(Path("content/the-dude/10th.de.json")),
|
||||
{
|
||||
'locale': 'de',
|
||||
'card_name': '10th',
|
||||
'page_name': 'the-dude',
|
||||
'page_id': 'the-dude-de-10th',
|
||||
}
|
||||
"locale": "de",
|
||||
"card_name": "10th",
|
||||
"page_name": "the-dude",
|
||||
"page_id": "the-dude-de-10th",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@override_settings(
|
||||
CONTENT_CARDS_PATH=str(DATA_PATH),
|
||||
CONTENT_CARDS_URL='/media/',
|
||||
STATIC_URL='/media/',
|
||||
CONTENT_CARDS_URL="/media/",
|
||||
STATIC_URL="/media/",
|
||||
)
|
||||
class TestContentCardModel(TestCase):
|
||||
def setUp(self):
|
||||
|
@ -44,62 +44,60 @@ class TestContentCardModel(TestCase):
|
|||
|
||||
def test_get_card_missing(self):
|
||||
with self.assertRaises(models.ContentCard.DoesNotExist):
|
||||
models.ContentCard.objects.get_card('home', 'card_10')
|
||||
models.ContentCard.objects.get_card("home", "card_10")
|
||||
|
||||
def test_card_data(self):
|
||||
card1 = models.ContentCard.objects.get_card('home', 'card_1')
|
||||
self.assertEqual(card1.id, 'home-en-US-card_1')
|
||||
self.assertEqual(card1.page_name, 'home')
|
||||
self.assertEqual(card1.card_name, 'card_1')
|
||||
self.assertEqual(card1.locale, 'en-US')
|
||||
with self.activate('de'):
|
||||
card1 = models.ContentCard.objects.get_card("home", "card_1")
|
||||
self.assertEqual(card1.id, "home-en-US-card_1")
|
||||
self.assertEqual(card1.page_name, "home")
|
||||
self.assertEqual(card1.card_name, "card_1")
|
||||
self.assertEqual(card1.locale, "en-US")
|
||||
with self.activate("de"):
|
||||
card_data = card1.card_data
|
||||
|
||||
self.assertDictEqual(
|
||||
card_data,
|
||||
{
|
||||
'title': 'We keep your data safe, never sold.',
|
||||
'ga_title': 'We keep your data safe, never sold.',
|
||||
'highres_image_url': '/media/contentcards/img/home/ffyr-high-res.191bff93b820.png',
|
||||
'media_icon': 'mzp-has-video',
|
||||
'class': 'mzp-c-card-large',
|
||||
'image_url': '/media/contentcards/img/home/ffyr.75c74c6ba409.png',
|
||||
'youtube_id': 'rZAQ6vgt8nE',
|
||||
'aspect_ratio': 'mzp-has-aspect-16-9',
|
||||
'desc': u'You have the right to your own life \u2014 and your own data. '
|
||||
u'Everything we make and do fights for you.',
|
||||
'link_url': '/de/firefox/',
|
||||
'tag_label': 'Video',
|
||||
}
|
||||
"title": "We keep your data safe, never sold.",
|
||||
"ga_title": "We keep your data safe, never sold.",
|
||||
"highres_image_url": "/media/contentcards/img/home/ffyr-high-res.191bff93b820.png",
|
||||
"media_icon": "mzp-has-video",
|
||||
"class": "mzp-c-card-large",
|
||||
"image_url": "/media/contentcards/img/home/ffyr.75c74c6ba409.png",
|
||||
"youtube_id": "rZAQ6vgt8nE",
|
||||
"aspect_ratio": "mzp-has-aspect-16-9",
|
||||
"desc": "You have the right to your own life \u2014 and your own data. Everything we make and do fights for you.",
|
||||
"link_url": "/de/firefox/",
|
||||
"tag_label": "Video",
|
||||
},
|
||||
)
|
||||
|
||||
def test_get_page_cards(self):
|
||||
cards = models.ContentCard.objects.get_page_cards('home')
|
||||
self.assertTrue(all(name in cards for name in
|
||||
['card_%d' % i for i in range(1, 6)]))
|
||||
cards = models.ContentCard.objects.get_page_cards("home")
|
||||
self.assertTrue(all(name in cards for name in ["card_%d" % i for i in range(1, 6)]))
|
||||
self.assertDictEqual(
|
||||
cards['card_2'],
|
||||
cards["card_2"],
|
||||
{
|
||||
'aspect_ratio': 'mzp-has-aspect-1-1',
|
||||
'desc': 'Microsoft is giving up on an independent shared platform for the internet, '
|
||||
'and in doing so, hands over control of more of online life to Google.',
|
||||
'highres_image_url': '/media/contentcards/img/home/edge-high-res.e3fd47cab8f0.png',
|
||||
'image_url': '/media/contentcards/img/home/edge.72822d0ff717.png',
|
||||
'link_url': 'https://blog.mozilla.org/blog/2018/12/06/goodbye-edge/?utm_source='
|
||||
'www.mozilla.org&utm_medium=referral&utm_campaign=homepage&utm_content=card',
|
||||
'class': 'mzp-c-card-small',
|
||||
'tag_label': 'Internet Health',
|
||||
'title': 'Goodbye, EdgeHTML',
|
||||
'ga_title': 'Goodbye, EdgeHTML',
|
||||
}
|
||||
"aspect_ratio": "mzp-has-aspect-1-1",
|
||||
"desc": "Microsoft is giving up on an independent shared platform for the internet, "
|
||||
"and in doing so, hands over control of more of online life to Google.",
|
||||
"highres_image_url": "/media/contentcards/img/home/edge-high-res.e3fd47cab8f0.png",
|
||||
"image_url": "/media/contentcards/img/home/edge.72822d0ff717.png",
|
||||
"link_url": "https://blog.mozilla.org/blog/2018/12/06/goodbye-edge/?utm_source="
|
||||
"www.mozilla.org&utm_medium=referral&utm_campaign=homepage&utm_content=card",
|
||||
"class": "mzp-c-card-small",
|
||||
"tag_label": "Internet Health",
|
||||
"title": "Goodbye, EdgeHTML",
|
||||
"ga_title": "Goodbye, EdgeHTML",
|
||||
},
|
||||
)
|
||||
|
||||
def test_get_page_cards_empty(self):
|
||||
cards = models.ContentCard.objects.get_page_cards('home', 'fr')
|
||||
cards = models.ContentCard.objects.get_page_cards("home", "fr")
|
||||
self.assertEqual(cards, {})
|
||||
|
||||
def test_html_content(self):
|
||||
card2 = models.ContentCard.objects.get_card('home', 'card_2')
|
||||
self.assertFalse('html_content' in card2.data)
|
||||
self.assertEqual(card2.content, '<p>This is the converted <em>Markdown</em></p>')
|
||||
self.assertEqual(card2.html, Markup('<p>This is the converted <em>Markdown</em></p>'))
|
||||
card2 = models.ContentCard.objects.get_card("home", "card_2")
|
||||
self.assertFalse("html_content" in card2.data)
|
||||
self.assertEqual(card2.content, "<p>This is the converted <em>Markdown</em></p>")
|
||||
self.assertEqual(card2.html, Markup("<p>This is the converted <em>Markdown</em></p>"))
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -11,31 +11,31 @@ from bedrock.contentful.models import ContentfulEntry
|
|||
|
||||
def data_hash(data):
|
||||
str_data = json.dumps(data, sort_keys=True)
|
||||
return sha256(str_data.encode('utf8')).hexdigest()
|
||||
return sha256(str_data.encode("utf8")).hexdigest()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
rf = RequestFactory()
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from Contentful.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument(
|
||||
"-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from Contentful."
|
||||
),
|
||||
|
||||
def log(self, msg):
|
||||
if not self.quiet:
|
||||
print(msg)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.quiet = options['quiet']
|
||||
self.force = options['force']
|
||||
self.quiet = options["quiet"]
|
||||
self.force = options["force"]
|
||||
if settings.CONTENTFUL_SPACE_ID and settings.CONTENTFUL_SPACE_KEY:
|
||||
self.log('Updating Contentful Data')
|
||||
self.log("Updating Contentful Data")
|
||||
added, updated = self.refresh()
|
||||
self.log(f'Done. Added: {added}. Updated: {updated}')
|
||||
self.log(f"Done. Added: {added}. Updated: {updated}")
|
||||
else:
|
||||
print('Contentful credentials not configured')
|
||||
print("Contentful credentials not configured")
|
||||
return
|
||||
|
||||
def refresh(self):
|
||||
|
@ -43,15 +43,15 @@ class Command(BaseCommand):
|
|||
added_count = 0
|
||||
content_ids = []
|
||||
for ctype in settings.CONTENTFUL_CONTENT_TYPES:
|
||||
for entry in ContentfulPage.client.entries({'content_type': ctype, 'include': 0}).items:
|
||||
content_ids.append((ctype, entry.sys['id']))
|
||||
for entry in ContentfulPage.client.entries({"content_type": ctype, "include": 0}).items:
|
||||
content_ids.append((ctype, entry.sys["id"]))
|
||||
|
||||
for ctype, page_id in content_ids:
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'en-US'
|
||||
request = self.rf.get("/")
|
||||
request.locale = "en-US"
|
||||
page = ContentfulPage(request, page_id)
|
||||
page_data = page.get_content()
|
||||
language = page_data['info']['lang']
|
||||
language = page_data["info"]["lang"]
|
||||
hash = data_hash(page_data)
|
||||
|
||||
try:
|
||||
|
|
|
@ -12,7 +12,7 @@ class ContentfulEntryManager(models.Manager):
|
|||
return self.get(content_type=content_type, language=lang).data
|
||||
|
||||
def get_homepage(self, lang):
|
||||
return self.get(content_type='connectHomepage', language=lang).data
|
||||
return self.get(content_type="connectHomepage", language=lang).data
|
||||
|
||||
|
||||
class ContentfulEntry(models.Model):
|
||||
|
|
|
@ -5,41 +5,41 @@ from django_jinja import library
|
|||
|
||||
# based on bleach.sanitizer.ALLOWED_TAGS
|
||||
ALLOWED_TAGS = [
|
||||
'a',
|
||||
'abbr',
|
||||
'acronym',
|
||||
'b',
|
||||
'blockquote',
|
||||
'button',
|
||||
'code',
|
||||
'div',
|
||||
'em',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'i',
|
||||
'img',
|
||||
'li',
|
||||
'ol',
|
||||
'p',
|
||||
'small',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'ul',
|
||||
"a",
|
||||
"abbr",
|
||||
"acronym",
|
||||
"b",
|
||||
"blockquote",
|
||||
"button",
|
||||
"code",
|
||||
"div",
|
||||
"em",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"i",
|
||||
"img",
|
||||
"li",
|
||||
"ol",
|
||||
"p",
|
||||
"small",
|
||||
"span",
|
||||
"strike",
|
||||
"strong",
|
||||
"ul",
|
||||
]
|
||||
ALLOWED_ATTRS = [
|
||||
'alt',
|
||||
'class',
|
||||
'href',
|
||||
'id',
|
||||
'src',
|
||||
'srcset',
|
||||
'rel',
|
||||
'title',
|
||||
"alt",
|
||||
"class",
|
||||
"href",
|
||||
"id",
|
||||
"src",
|
||||
"srcset",
|
||||
"rel",
|
||||
"title",
|
||||
]
|
||||
|
||||
|
||||
|
@ -47,7 +47,7 @@ def _allowed_attrs(tag, name, value):
|
|||
if name in ALLOWED_ATTRS:
|
||||
return True
|
||||
|
||||
if name.startswith('data-'):
|
||||
if name.startswith("data-"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
from bedrock.redirects.util import redirect
|
||||
|
||||
redirectpatterns = (
|
||||
|
||||
redirect(r'^exp/firefox/new/nav/?$', 'firefox.new'),
|
||||
|
||||
)
|
||||
redirectpatterns = (redirect(r"^exp/firefox/new/nav/?$", "firefox.new"),)
|
||||
|
|
|
@ -13,12 +13,10 @@ from bedrock.mozorg.tests import TestCase
|
|||
|
||||
|
||||
@override_settings(DEV=False)
|
||||
@patch('bedrock.exp.views.l10n_utils.render')
|
||||
@patch("bedrock.exp.views.l10n_utils.render")
|
||||
class TestExpFirefoxNew(TestCase):
|
||||
def test_download_template(self, render_mock):
|
||||
req = RequestFactory().get('/exp/firefox/new/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/exp/firefox/new/")
|
||||
req.locale = "en-US"
|
||||
views.new(req)
|
||||
render_mock.assert_called_once_with(
|
||||
req, 'exp/firefox/new/download.html', ANY, ftl_files='firefox/new/desktop'
|
||||
)
|
||||
render_mock.assert_called_once_with(req, "exp/firefox/new/download.html", ANY, ftl_files="firefox/new/desktop")
|
||||
|
|
|
@ -9,13 +9,13 @@ from bedrock.releasenotes import version_re
|
|||
from bedrock.exp import views
|
||||
|
||||
|
||||
latest_re = r'^firefox(?:/(?P<version>%s))?/%s/$'
|
||||
whatsnew_re_all = latest_re % (version_re, 'whatsnew/all')
|
||||
latest_re = r"^firefox(?:/(?P<version>%s))?/%s/$"
|
||||
whatsnew_re_all = latest_re % (version_re, "whatsnew/all")
|
||||
|
||||
urlpatterns = (
|
||||
page('opt-out', 'exp/opt-out.html'),
|
||||
page('firefox', 'exp/firefox/index.html', ftl_files=['firefox/home']),
|
||||
url(r'^firefox/new/$', views.new, name='exp.firefox.new'),
|
||||
url(r'^$', views.home_view, name='exp.mozorg.home'),
|
||||
page('firefox/accounts', 'exp/firefox/accounts.html', ftl_files=['firefox/accounts']),
|
||||
page("opt-out", "exp/opt-out.html"),
|
||||
page("firefox", "exp/firefox/index.html", ftl_files=["firefox/home"]),
|
||||
url(r"^firefox/new/$", views.new, name="exp.firefox.new"),
|
||||
url(r"^$", views.home_view, name="exp.mozorg.home"),
|
||||
page("firefox/accounts", "exp/firefox/accounts.html", ftl_files=["firefox/accounts"]),
|
||||
)
|
||||
|
|
|
@ -12,50 +12,48 @@ from bedrock.pocketfeed.models import PocketArticle
|
|||
def new(request):
|
||||
|
||||
# note: v and xv params only allow a-z, A-Z, 0-9, -, and _ characters
|
||||
experience = request.GET.get('xv', None)
|
||||
variant = request.GET.get('v', None)
|
||||
experience = request.GET.get("xv", None)
|
||||
variant = request.GET.get("v", None)
|
||||
|
||||
# ensure experience matches pre-defined value
|
||||
if experience not in ['']: # place expected ?xv= values in this list
|
||||
if experience not in [""]: # place expected ?xv= values in this list
|
||||
experience = None
|
||||
|
||||
# ensure variant matches pre-defined value
|
||||
if variant not in ['']: # place expected ?v= values in this list
|
||||
if variant not in [""]: # place expected ?v= values in this list
|
||||
variant = None
|
||||
|
||||
template_name = 'exp/firefox/new/download.html'
|
||||
template_name = "exp/firefox/new/download.html"
|
||||
|
||||
# no harm done by passing 'v' to template, even when no experiment is running
|
||||
# (also makes tests easier to maintain by always sending a context)
|
||||
context = {'experience': experience, 'v': variant}
|
||||
context = {"experience": experience, "v": variant}
|
||||
|
||||
return l10n_utils.render(request, template_name, context,
|
||||
ftl_files='firefox/new/desktop')
|
||||
return l10n_utils.render(request, template_name, context, ftl_files="firefox/new/desktop")
|
||||
|
||||
|
||||
def home_view(request):
|
||||
locale = l10n_utils.get_locale(request)
|
||||
donate_params = settings.DONATE_PARAMS.get(
|
||||
locale, settings.DONATE_PARAMS['en-US'])
|
||||
donate_params = settings.DONATE_PARAMS.get(locale, settings.DONATE_PARAMS["en-US"])
|
||||
|
||||
# presets are stored as a string but, for the home banner
|
||||
# we need it as a list.
|
||||
donate_params['preset_list'] = donate_params['presets'].split(',')
|
||||
donate_params["preset_list"] = donate_params["presets"].split(",")
|
||||
ctx = {
|
||||
'donate_params': donate_params,
|
||||
'pocket_articles': PocketArticle.objects.all()[:4],
|
||||
'ftl_files': ['mozorg/home-mr1-promo'],
|
||||
'active_locales': ['de', 'fr', 'en-US']
|
||||
"donate_params": donate_params,
|
||||
"pocket_articles": PocketArticle.objects.all()[:4],
|
||||
"ftl_files": ["mozorg/home-mr1-promo"],
|
||||
"active_locales": ["de", "fr", "en-US"],
|
||||
}
|
||||
|
||||
if locale == 'de':
|
||||
template_name = 'exp/home/home-de.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-de', 'de')
|
||||
elif locale == 'fr':
|
||||
template_name = 'exp/home/home-fr.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-fr', 'fr')
|
||||
if locale == "de":
|
||||
template_name = "exp/home/home-de.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-de", "de")
|
||||
elif locale == "fr":
|
||||
template_name = "exp/home/home-fr.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-fr", "fr")
|
||||
else:
|
||||
template_name = 'exp/home/home-en.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-2019', 'en-US')
|
||||
template_name = "exp/home/home-en.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-2019", "en-US")
|
||||
|
||||
return l10n_utils.render(request, template_name, ctx)
|
||||
|
|
|
@ -21,12 +21,12 @@ class ExternalFile:
|
|||
try:
|
||||
fileinfo = settings.EXTERNAL_FILES[file_id]
|
||||
except KeyError:
|
||||
raise ValueError('No external file with the {0} ID.'.format(file_id))
|
||||
raise ValueError("No external file with the {0} ID.".format(file_id))
|
||||
|
||||
self._cache = caches['externalfiles']
|
||||
self._cache = caches["externalfiles"]
|
||||
self.file_id = file_id
|
||||
self.name = fileinfo['name']
|
||||
self.cache_key = 'externalfile:{}'.format(self.file_id)
|
||||
self.name = fileinfo["name"]
|
||||
self.cache_key = "externalfile:{}".format(self.file_id)
|
||||
self.file_path = os.path.join(settings.EXTERNAL_FILES_PATH, self.name)
|
||||
|
||||
@property
|
||||
|
@ -75,11 +75,11 @@ class ExternalFile:
|
|||
:return: str or None
|
||||
:raises: ValueError
|
||||
"""
|
||||
with codecs.open(self.file_path, encoding='utf-8') as fp:
|
||||
with codecs.open(self.file_path, encoding="utf-8") as fp:
|
||||
content = fp.read()
|
||||
|
||||
if not content:
|
||||
raise ValueError('%s is empty' % self.name)
|
||||
raise ValueError("%s is empty" % self.name)
|
||||
|
||||
return self.validate_content(content)
|
||||
|
||||
|
@ -92,7 +92,7 @@ class ExternalFile:
|
|||
def update(self):
|
||||
from bedrock.externalfiles.models import ExternalFile as EFModel
|
||||
|
||||
log.info('Updating {0}.'.format(self.name))
|
||||
log.info("Updating {0}.".format(self.name))
|
||||
content = self.validate_file()
|
||||
fo = self.file_object
|
||||
if fo:
|
||||
|
@ -101,7 +101,7 @@ class ExternalFile:
|
|||
else:
|
||||
EFModel.objects.create(name=self.file_id, content=content)
|
||||
|
||||
log.info('Successfully updated {0}.'.format(self.name))
|
||||
log.info("Successfully updated {0}.".format(self.name))
|
||||
return True
|
||||
|
||||
def clear_cache(self):
|
||||
|
|
|
@ -7,38 +7,34 @@ from bedrock.utils.git import GitRepo
|
|||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from git.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git."),
|
||||
|
||||
def output(self, msg):
|
||||
if not self.quiet:
|
||||
print(msg)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.quiet = options['quiet']
|
||||
repo = GitRepo(settings.EXTERNAL_FILES_PATH, settings.EXTERNAL_FILES_REPO,
|
||||
branch_name=settings.EXTERNAL_FILES_BRANCH,
|
||||
name='Community Data')
|
||||
self.output('Updating git repo')
|
||||
self.quiet = options["quiet"]
|
||||
repo = GitRepo(settings.EXTERNAL_FILES_PATH, settings.EXTERNAL_FILES_REPO, branch_name=settings.EXTERNAL_FILES_BRANCH, name="Community Data")
|
||||
self.output("Updating git repo")
|
||||
repo.update()
|
||||
if not (options['force'] or repo.has_changes()):
|
||||
self.output('No community data updates')
|
||||
if not (options["force"] or repo.has_changes()):
|
||||
self.output("No community data updates")
|
||||
return
|
||||
|
||||
self.output('Loading community data into database')
|
||||
self.output("Loading community data into database")
|
||||
|
||||
for fid, finfo in settings.EXTERNAL_FILES.items():
|
||||
klass = import_string(finfo['type'])
|
||||
klass = import_string(finfo["type"])
|
||||
try:
|
||||
klass(fid).update()
|
||||
except ValueError as e:
|
||||
raise CommandError(str(e))
|
||||
|
||||
self.output('Community data successfully loaded')
|
||||
self.output("Community data successfully loaded")
|
||||
|
||||
repo.set_db_latest()
|
||||
|
||||
self.output('Saved latest git repo state to database')
|
||||
self.output('Done!')
|
||||
self.output("Saved latest git repo state to database")
|
||||
self.output("Done!")
|
||||
|
|
|
@ -7,4 +7,4 @@ class ExternalFile(models.Model):
|
|||
last_modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
app_label = 'externalfiles'
|
||||
app_label = "externalfiles"
|
||||
|
|
|
@ -20,17 +20,14 @@ class TestExternalFile(TestCase):
|
|||
timezone.activate(utc)
|
||||
|
||||
def setUp(self):
|
||||
settings.EXTERNAL_FILES['test'] = {
|
||||
'url': 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
|
||||
'name': 'there.is.no.data.xul'
|
||||
}
|
||||
settings.EXTERNAL_FILES["test"] = {"url": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "name": "there.is.no.data.xul"}
|
||||
|
||||
def tearDown(self):
|
||||
externalfiles.ExternalFile('test').clear_cache()
|
||||
del settings.EXTERNAL_FILES['test']
|
||||
externalfiles.ExternalFile("test").clear_cache()
|
||||
del settings.EXTERNAL_FILES["test"]
|
||||
|
||||
def test_last_modified(self):
|
||||
"""Should return the modified timestamp."""
|
||||
EFModel.objects.create(name='test', content='test')
|
||||
efo = EFModel.objects.get(name='test')
|
||||
self.assertEqual(externalfiles.ExternalFile('test').last_modified, efo.last_modified)
|
||||
EFModel.objects.create(name="test", content="test")
|
||||
efo = EFModel.objects.get(name="test")
|
||||
self.assertEqual(externalfiles.ExternalFile("test").last_modified, efo.last_modified)
|
||||
|
|
|
@ -7,6 +7,6 @@ from bedrock.firefox.firefox_details import firefox_desktop
|
|||
|
||||
def latest_firefox_versions(request):
|
||||
return {
|
||||
'latest_firefox_version': firefox_desktop.latest_version(),
|
||||
'esr_firefox_versions': firefox_desktop.esr_minor_versions,
|
||||
"latest_firefox_version": firefox_desktop.latest_version(),
|
||||
"esr_firefox_versions": firefox_desktop.esr_minor_versions,
|
||||
}
|
||||
|
|
|
@ -18,57 +18,60 @@ class _ProductDetails(ProductDetails):
|
|||
bouncer_url = settings.BOUNCER_URL
|
||||
|
||||
def _matches_query(self, info, query):
|
||||
words = re.split(r',|,?\s+', query.strip().lower())
|
||||
return all((word in info['name_en'].lower() or
|
||||
word in info['name_native'].lower()) for word in words)
|
||||
words = re.split(r",|,?\s+", query.strip().lower())
|
||||
return all((word in info["name_en"].lower() or word in info["name_native"].lower()) for word in words)
|
||||
|
||||
|
||||
class FirefoxDesktop(_ProductDetails):
|
||||
download_base_url_transition = '/firefox/download/thanks/'
|
||||
download_base_url_transition = "/firefox/download/thanks/"
|
||||
|
||||
# Human-readable platform names
|
||||
platform_labels = OrderedDict([
|
||||
('win64', 'Windows 64-bit'),
|
||||
('win64-msi', 'Windows 64-bit MSI'),
|
||||
('win64-aarch64', 'Windows ARM64/AArch64'),
|
||||
('win', 'Windows 32-bit'),
|
||||
('win-msi', 'Windows 32-bit MSI'),
|
||||
('osx', 'macOS'),
|
||||
('linux64', 'Linux 64-bit'),
|
||||
('linux', 'Linux 32-bit'),
|
||||
])
|
||||
platform_labels = OrderedDict(
|
||||
[
|
||||
("win64", "Windows 64-bit"),
|
||||
("win64-msi", "Windows 64-bit MSI"),
|
||||
("win64-aarch64", "Windows ARM64/AArch64"),
|
||||
("win", "Windows 32-bit"),
|
||||
("win-msi", "Windows 32-bit MSI"),
|
||||
("osx", "macOS"),
|
||||
("linux64", "Linux 64-bit"),
|
||||
("linux", "Linux 32-bit"),
|
||||
]
|
||||
)
|
||||
|
||||
# Recommended/modern vs traditional/legacy platforms
|
||||
platform_classification = OrderedDict([
|
||||
('recommended', ('win64', 'win64-msi', 'win64-aarch64', 'osx', 'linux64')),
|
||||
('traditional', ('linux', 'win', 'win-msi')),
|
||||
])
|
||||
platform_classification = OrderedDict(
|
||||
[
|
||||
("recommended", ("win64", "win64-msi", "win64-aarch64", "osx", "linux64")),
|
||||
("traditional", ("linux", "win", "win-msi")),
|
||||
]
|
||||
)
|
||||
|
||||
# Human-readable channel names
|
||||
channel_labels = {
|
||||
'nightly': 'Firefox Nightly',
|
||||
'alpha': 'Developer Edition',
|
||||
'devedition': 'Developer Edition',
|
||||
'beta': 'Firefox Beta',
|
||||
'esr': 'Firefox Extended Support Release',
|
||||
'release': 'Firefox',
|
||||
"nightly": "Firefox Nightly",
|
||||
"alpha": "Developer Edition",
|
||||
"devedition": "Developer Edition",
|
||||
"beta": "Firefox Beta",
|
||||
"esr": "Firefox Extended Support Release",
|
||||
"release": "Firefox",
|
||||
}
|
||||
|
||||
# Version property names in product-details
|
||||
version_map = {
|
||||
'nightly': 'FIREFOX_NIGHTLY',
|
||||
'alpha': 'FIREFOX_DEVEDITION',
|
||||
'devedition': 'FIREFOX_DEVEDITION',
|
||||
'beta': 'LATEST_FIREFOX_DEVEL_VERSION',
|
||||
'esr': 'FIREFOX_ESR',
|
||||
'esr_next': 'FIREFOX_ESR_NEXT',
|
||||
'release': 'LATEST_FIREFOX_VERSION',
|
||||
"nightly": "FIREFOX_NIGHTLY",
|
||||
"alpha": "FIREFOX_DEVEDITION",
|
||||
"devedition": "FIREFOX_DEVEDITION",
|
||||
"beta": "LATEST_FIREFOX_DEVEL_VERSION",
|
||||
"esr": "FIREFOX_ESR",
|
||||
"esr_next": "FIREFOX_ESR_NEXT",
|
||||
"release": "LATEST_FIREFOX_VERSION",
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FirefoxDesktop, self).__init__(**kwargs)
|
||||
|
||||
def platforms(self, channel='release', classified=False):
|
||||
def platforms(self, channel="release", classified=False):
|
||||
"""
|
||||
Get the desktop platform dictionary containing slugs and corresponding
|
||||
labels. If the classified option is True, it will be ordered by the
|
||||
|
@ -85,14 +88,14 @@ class FirefoxDesktop(_ProductDetails):
|
|||
|
||||
return list(platforms.items())
|
||||
|
||||
def latest_version(self, channel='release'):
|
||||
version = self.version_map.get(channel, 'LATEST_FIREFOX_VERSION')
|
||||
def latest_version(self, channel="release"):
|
||||
version = self.version_map.get(channel, "LATEST_FIREFOX_VERSION")
|
||||
try:
|
||||
return self.firefox_versions[version]
|
||||
except KeyError:
|
||||
if channel in ['alpha', 'devedition']:
|
||||
if channel in ["alpha", "devedition"]:
|
||||
# beta as a fall-back until all product-details data is updated
|
||||
return self.latest_version('beta')
|
||||
return self.latest_version("beta")
|
||||
|
||||
return None
|
||||
|
||||
|
@ -103,14 +106,14 @@ class FirefoxDesktop(_ProductDetails):
|
|||
return 0
|
||||
|
||||
try:
|
||||
return int(lv.split('.')[0])
|
||||
return int(lv.split(".")[0])
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def esr_major_versions(self):
|
||||
versions = []
|
||||
for channel in ('esr', 'esr_next'):
|
||||
for channel in ("esr", "esr_next"):
|
||||
version_int = self.latest_major_version(channel)
|
||||
if version_int:
|
||||
versions.append(version_int)
|
||||
|
@ -120,33 +123,32 @@ class FirefoxDesktop(_ProductDetails):
|
|||
@property
|
||||
def esr_minor_versions(self):
|
||||
versions = []
|
||||
for channel in ('esr', 'esr_next'):
|
||||
for channel in ("esr", "esr_next"):
|
||||
version = self.latest_version(channel)
|
||||
version_int = self.latest_major_version(channel)
|
||||
if version and version_int:
|
||||
versions.append(str(version).replace('esr', ''))
|
||||
versions.append(str(version).replace("esr", ""))
|
||||
|
||||
return versions
|
||||
|
||||
def latest_builds(self, locale, channel='release'):
|
||||
def latest_builds(self, locale, channel="release"):
|
||||
"""Return build info for a locale and channel.
|
||||
|
||||
:param locale: locale string of the build
|
||||
:param channel: channel of the build: release, beta, or aurora
|
||||
:return: dict or None
|
||||
"""
|
||||
all_builds = (self.firefox_primary_builds,
|
||||
self.firefox_beta_builds)
|
||||
all_builds = (self.firefox_primary_builds, self.firefox_beta_builds)
|
||||
version = self.latest_version(channel)
|
||||
|
||||
for builds in all_builds:
|
||||
if locale in builds and version in builds[locale]:
|
||||
_builds = builds[locale][version]
|
||||
# Append 64-bit builds
|
||||
if 'Windows' in _builds:
|
||||
_builds['Windows 64-bit'] = _builds['Windows']
|
||||
if 'Linux' in _builds:
|
||||
_builds['Linux 64-bit'] = _builds['Linux']
|
||||
if "Windows" in _builds:
|
||||
_builds["Windows 64-bit"] = _builds["Windows"]
|
||||
if "Linux" in _builds:
|
||||
_builds["Linux 64-bit"] = _builds["Linux"]
|
||||
return version, _builds
|
||||
|
||||
def _get_filtered_builds(self, builds, channel, version=None, query=None):
|
||||
|
@ -167,10 +169,10 @@ class FirefoxDesktop(_ProductDetails):
|
|||
continue
|
||||
|
||||
build_info = {
|
||||
'locale': locale,
|
||||
'name_en': self.languages[locale]['English'],
|
||||
'name_native': self.languages[locale]['native'],
|
||||
'platforms': {},
|
||||
"locale": locale,
|
||||
"name_en": self.languages[locale]["English"],
|
||||
"name_native": self.languages[locale]["native"],
|
||||
"platforms": {},
|
||||
}
|
||||
|
||||
# only include builds that match a search query
|
||||
|
@ -178,15 +180,13 @@ class FirefoxDesktop(_ProductDetails):
|
|||
continue
|
||||
|
||||
for platform, label in self.platform_labels.items():
|
||||
build_info['platforms'][platform] = {
|
||||
'download_url': self.get_download_url(channel, version,
|
||||
platform, locale,
|
||||
True, True),
|
||||
build_info["platforms"][platform] = {
|
||||
"download_url": self.get_download_url(channel, version, platform, locale, True, True),
|
||||
}
|
||||
|
||||
f_builds.append(build_info)
|
||||
|
||||
return sorted(f_builds, key=itemgetter('name_en'))
|
||||
return sorted(f_builds, key=itemgetter("name_en"))
|
||||
|
||||
def get_filtered_full_builds(self, channel, version=None, query=None):
|
||||
"""
|
||||
|
@ -196,8 +196,7 @@ class FirefoxDesktop(_ProductDetails):
|
|||
:param query: a string to match against native or english locale name
|
||||
:return: list
|
||||
"""
|
||||
return self._get_filtered_builds(self.firefox_primary_builds,
|
||||
channel, version, query)
|
||||
return self._get_filtered_builds(self.firefox_primary_builds, channel, version, query)
|
||||
|
||||
def get_filtered_test_builds(self, channel, version=None, query=None):
|
||||
"""
|
||||
|
@ -207,13 +206,20 @@ class FirefoxDesktop(_ProductDetails):
|
|||
:param query: a string to match against native or english locale name
|
||||
:return: list
|
||||
"""
|
||||
return self._get_filtered_builds(self.firefox_beta_builds,
|
||||
channel, version, query)
|
||||
return self._get_filtered_builds(self.firefox_beta_builds, channel, version, query)
|
||||
|
||||
def get_download_url(self, channel, version, platform, locale,
|
||||
force_direct=False, force_full_installer=False,
|
||||
force_funnelcake=False, funnelcake_id=None,
|
||||
locale_in_transition=False):
|
||||
def get_download_url(
|
||||
self,
|
||||
channel,
|
||||
version,
|
||||
platform,
|
||||
locale,
|
||||
force_direct=False,
|
||||
force_full_installer=False,
|
||||
force_funnelcake=False,
|
||||
funnelcake_id=None,
|
||||
locale_in_transition=False,
|
||||
):
|
||||
"""
|
||||
Get direct download url for the product.
|
||||
:param channel: one of self.version_map.keys().
|
||||
|
@ -233,23 +239,23 @@ class FirefoxDesktop(_ProductDetails):
|
|||
# no longer used, but still passed in. leaving here for now
|
||||
# as it will likely be used in future.
|
||||
# _version = version
|
||||
_locale = 'ja-JP-mac' if platform == 'osx' and locale == 'ja' else locale
|
||||
channel = 'devedition' if channel == 'alpha' else channel
|
||||
force_direct = True if channel != 'release' else force_direct
|
||||
stub_platforms = ['win', 'win64']
|
||||
esr_channels = ['esr', 'esr_next']
|
||||
_locale = "ja-JP-mac" if platform == "osx" and locale == "ja" else locale
|
||||
channel = "devedition" if channel == "alpha" else channel
|
||||
force_direct = True if channel != "release" else force_direct
|
||||
stub_platforms = ["win", "win64"]
|
||||
esr_channels = ["esr", "esr_next"]
|
||||
include_funnelcake_param = False
|
||||
|
||||
# support optional MSI installer downloads
|
||||
# bug 1493205
|
||||
is_msi = platform.endswith('-msi')
|
||||
is_msi = platform.endswith("-msi")
|
||||
if is_msi:
|
||||
platform = platform[:-4]
|
||||
|
||||
# Bug 1345467 - Only allow specifically configured funnelcake builds
|
||||
if funnelcake_id:
|
||||
fc_platforms = config('FUNNELCAKE_%s_PLATFORMS' % funnelcake_id, default='', parser=ListOf(str))
|
||||
fc_locales = config('FUNNELCAKE_%s_LOCALES' % funnelcake_id, default='', parser=ListOf(str))
|
||||
fc_platforms = config("FUNNELCAKE_%s_PLATFORMS" % funnelcake_id, default="", parser=ListOf(str))
|
||||
fc_locales = config("FUNNELCAKE_%s_LOCALES" % funnelcake_id, default="", parser=ListOf(str))
|
||||
include_funnelcake_param = platform in fc_platforms and _locale in fc_locales
|
||||
|
||||
# Check if direct download link has been requested
|
||||
|
@ -259,55 +265,63 @@ class FirefoxDesktop(_ProductDetails):
|
|||
transition_url = self.download_base_url_transition
|
||||
if funnelcake_id:
|
||||
# include funnelcake in scene 2 URL
|
||||
transition_url += '?f=%s' % funnelcake_id
|
||||
transition_url += "?f=%s" % funnelcake_id
|
||||
|
||||
if locale_in_transition:
|
||||
transition_url = '/%s%s' % (locale, transition_url)
|
||||
transition_url = "/%s%s" % (locale, transition_url)
|
||||
|
||||
return transition_url
|
||||
|
||||
# otherwise build a full download URL
|
||||
prod_name = 'firefox' if channel == 'release' else 'firefox-%s' % channel
|
||||
suffix = 'latest-ssl'
|
||||
prod_name = "firefox" if channel == "release" else "firefox-%s" % channel
|
||||
suffix = "latest-ssl"
|
||||
if is_msi:
|
||||
suffix = 'msi-' + suffix
|
||||
suffix = "msi-" + suffix
|
||||
|
||||
if channel in esr_channels:
|
||||
# nothing special about ESR other than there is no stub.
|
||||
# included in this contitional to avoid the following elif.
|
||||
if channel == 'esr_next':
|
||||
prod_name = 'firefox-esr-next'
|
||||
if channel == "esr_next":
|
||||
prod_name = "firefox-esr-next"
|
||||
elif platform in stub_platforms and not is_msi and not force_full_installer:
|
||||
# Use the stub installer for approved platforms
|
||||
# append funnelcake id to version if we have one
|
||||
if include_funnelcake_param:
|
||||
suffix = 'stub-f%s' % funnelcake_id
|
||||
suffix = "stub-f%s" % funnelcake_id
|
||||
else:
|
||||
suffix = 'stub'
|
||||
elif channel == 'nightly' and locale != 'en-US':
|
||||
suffix = "stub"
|
||||
elif channel == "nightly" and locale != "en-US":
|
||||
# Nightly uses a different product name for localized builds,
|
||||
# and is the only one ಠ_ಠ
|
||||
suffix = 'latest-l10n-ssl'
|
||||
suffix = "latest-l10n-ssl"
|
||||
if is_msi:
|
||||
suffix = 'msi-' + suffix
|
||||
suffix = "msi-" + suffix
|
||||
|
||||
product = '%s-%s' % (prod_name, suffix)
|
||||
product = "%s-%s" % (prod_name, suffix)
|
||||
|
||||
return '?'.join([self.bouncer_url,
|
||||
urlencode([
|
||||
('product', product),
|
||||
('os', platform),
|
||||
# Order matters, lang must be last for bouncer.
|
||||
('lang', _locale),
|
||||
])])
|
||||
return "?".join(
|
||||
[
|
||||
self.bouncer_url,
|
||||
urlencode(
|
||||
[
|
||||
("product", product),
|
||||
("os", platform),
|
||||
# Order matters, lang must be last for bouncer.
|
||||
("lang", _locale),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class FirefoxAndroid(_ProductDetails):
|
||||
# Human-readable architecture names
|
||||
platform_labels = OrderedDict([
|
||||
('arm', 'ARM devices\n(Android %s+)'),
|
||||
('x86', 'Intel devices\n(Android %s+ x86 CPU)'),
|
||||
])
|
||||
platform_labels = OrderedDict(
|
||||
[
|
||||
("arm", "ARM devices\n(Android %s+)"),
|
||||
("x86", "Intel devices\n(Android %s+ x86 CPU)"),
|
||||
]
|
||||
)
|
||||
|
||||
# Recommended/modern vs traditional/legacy platforms
|
||||
# Unused but required to match FirefoxDesktop
|
||||
|
@ -315,57 +329,58 @@ class FirefoxAndroid(_ProductDetails):
|
|||
|
||||
# Human-readable channel names
|
||||
channel_labels = {
|
||||
'nightly': 'Firefox Nightly',
|
||||
'beta': 'Firefox Beta',
|
||||
'release': 'Firefox',
|
||||
"nightly": "Firefox Nightly",
|
||||
"beta": "Firefox Beta",
|
||||
"release": "Firefox",
|
||||
}
|
||||
|
||||
# Version property names in product-details
|
||||
version_map = {
|
||||
'nightly': 'nightly_version',
|
||||
'beta': 'beta_version',
|
||||
'release': 'version',
|
||||
"nightly": "nightly_version",
|
||||
"beta": "beta_version",
|
||||
"release": "version",
|
||||
}
|
||||
|
||||
# Build property names in product-details
|
||||
build_map = {
|
||||
'beta': 'beta_builds',
|
||||
'release': 'builds',
|
||||
"beta": "beta_builds",
|
||||
"release": "builds",
|
||||
}
|
||||
|
||||
# Platform names defined in bouncer
|
||||
platform_map = OrderedDict([
|
||||
('arm', 'android'),
|
||||
('x86', 'android-x86'),
|
||||
])
|
||||
platform_map = OrderedDict(
|
||||
[
|
||||
("arm", "android"),
|
||||
("x86", "android-x86"),
|
||||
]
|
||||
)
|
||||
|
||||
# Product names defined in bouncer
|
||||
product_map = {
|
||||
'nightly': 'fennec-nightly-latest',
|
||||
'beta': 'fennec-beta-latest',
|
||||
'release': 'fennec-latest',
|
||||
"nightly": "fennec-nightly-latest",
|
||||
"beta": "fennec-beta-latest",
|
||||
"release": "fennec-latest",
|
||||
}
|
||||
|
||||
store_url = settings.GOOGLE_PLAY_FIREFOX_LINK_UTMS
|
||||
# Product IDs defined on Google Play
|
||||
# Nightly reuses the Aurora ID to migrate the user base
|
||||
store_product_ids = {
|
||||
'nightly': 'org.mozilla.fenix',
|
||||
'beta': 'org.mozilla.firefox_beta',
|
||||
'release': 'org.mozilla.firefox',
|
||||
"nightly": "org.mozilla.fenix",
|
||||
"beta": "org.mozilla.firefox_beta",
|
||||
"release": "org.mozilla.firefox",
|
||||
}
|
||||
|
||||
archive_url_base = ('https://archive.mozilla.org/pub/mobile/nightly/'
|
||||
'latest-mozilla-%s-android')
|
||||
archive_url_base = "https://archive.mozilla.org/pub/mobile/nightly/latest-mozilla-%s-android"
|
||||
archive_repo = {
|
||||
'nightly': 'central',
|
||||
"nightly": "central",
|
||||
}
|
||||
archive_urls = {
|
||||
'arm': archive_url_base + '-%s/fennec-%s.multi.android-arm.apk',
|
||||
'x86': archive_url_base + '-%s/fennec-%s.multi.android-i386.apk',
|
||||
"arm": archive_url_base + "-%s/fennec-%s.multi.android-arm.apk",
|
||||
"x86": archive_url_base + "-%s/fennec-%s.multi.android-i386.apk",
|
||||
}
|
||||
|
||||
def platforms(self, channel='release', classified=False):
|
||||
def platforms(self, channel="release", classified=False):
|
||||
"""
|
||||
Get the Android platform dictionary containing slugs and corresponding
|
||||
labels. The classified option is unused but required to match the
|
||||
|
@ -375,7 +390,7 @@ class FirefoxAndroid(_ProductDetails):
|
|||
platforms = OrderedDict()
|
||||
|
||||
# Supported Android version has been changed with Firefox 56
|
||||
min_version = '4.1'
|
||||
min_version = "4.1"
|
||||
|
||||
# key is a bouncer platform name, value is the human-readable label
|
||||
for arch, platform in self.platform_map.items():
|
||||
|
@ -384,7 +399,7 @@ class FirefoxAndroid(_ProductDetails):
|
|||
return list(platforms.items())
|
||||
|
||||
def latest_version(self, channel):
|
||||
version = self.version_map.get(channel, 'version')
|
||||
version = self.version_map.get(channel, "version")
|
||||
return self.mobile_details[version]
|
||||
|
||||
def latest_major_version(self, channel):
|
||||
|
@ -394,7 +409,7 @@ class FirefoxAndroid(_ProductDetails):
|
|||
return 0
|
||||
|
||||
try:
|
||||
return int(lv.split('.')[0])
|
||||
return int(lv.split(".")[0])
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
@ -408,28 +423,28 @@ class FirefoxAndroid(_ProductDetails):
|
|||
:param query: a string to match against native or english locale name
|
||||
:return: list
|
||||
"""
|
||||
locales = [build['locale']['code'] for build in builds]
|
||||
locales = [build["locale"]["code"] for build in builds]
|
||||
f_builds = []
|
||||
|
||||
# For now, only list the multi-locale builds because the single-locale
|
||||
# builds are fragile (Bug 1301650)
|
||||
locales = ['multi']
|
||||
locales = ["multi"]
|
||||
|
||||
for locale in locales:
|
||||
if locale == 'multi':
|
||||
name_en = 'Multi-locale'
|
||||
name_native = ''
|
||||
if locale == "multi":
|
||||
name_en = "Multi-locale"
|
||||
name_native = ""
|
||||
elif locale in self.languages:
|
||||
name_en = self.languages[locale]['English']
|
||||
name_native = self.languages[locale]['native']
|
||||
name_en = self.languages[locale]["English"]
|
||||
name_native = self.languages[locale]["native"]
|
||||
else:
|
||||
continue
|
||||
|
||||
build_info = {
|
||||
'locale': locale,
|
||||
'name_en': name_en,
|
||||
'name_native': name_native,
|
||||
'platforms': {},
|
||||
"locale": locale,
|
||||
"name_en": name_en,
|
||||
"name_native": name_native,
|
||||
"platforms": {},
|
||||
}
|
||||
|
||||
# only include builds that match a search query
|
||||
|
@ -438,12 +453,12 @@ class FirefoxAndroid(_ProductDetails):
|
|||
|
||||
for arch, platform in self.platform_map.items():
|
||||
# x86 builds are not localized yet
|
||||
if arch == 'x86' and locale not in ['multi', 'en-US']:
|
||||
if arch == "x86" and locale not in ["multi", "en-US"]:
|
||||
continue
|
||||
|
||||
# Use a direct link instead of Google Play for the /all/ page
|
||||
url = self.get_download_url(channel, arch, locale, True)
|
||||
build_info['platforms'][platform] = {'download_url': url}
|
||||
build_info["platforms"][platform] = {"download_url": url}
|
||||
|
||||
f_builds.append(build_info)
|
||||
|
||||
|
@ -457,7 +472,7 @@ class FirefoxAndroid(_ProductDetails):
|
|||
:param query: a string to match against native or english locale name
|
||||
:return: list
|
||||
"""
|
||||
builds = self.mobile_details[self.build_map.get(channel, 'builds')]
|
||||
builds = self.mobile_details[self.build_map.get(channel, "builds")]
|
||||
|
||||
return self._get_filtered_builds(builds, channel, version, query)
|
||||
|
||||
|
@ -465,8 +480,7 @@ class FirefoxAndroid(_ProductDetails):
|
|||
# We don't have pre-release builds yet
|
||||
return []
|
||||
|
||||
def get_download_url(self, channel='release', arch='arm', locale='multi',
|
||||
force_direct=False):
|
||||
def get_download_url(self, channel="release", arch="arm", locale="multi", force_direct=False):
|
||||
"""
|
||||
Get direct download url for the product.
|
||||
:param channel: one of self.version_map.keys() such as nightly, beta.
|
||||
|
@ -478,16 +492,23 @@ class FirefoxAndroid(_ProductDetails):
|
|||
"""
|
||||
if force_direct:
|
||||
# Use a bouncer link
|
||||
return '?'.join([self.bouncer_url, urlencode([
|
||||
('product', self.product_map.get(channel, 'fennec-latest')),
|
||||
('os', self.platform_map[arch]),
|
||||
# Order matters, lang must be last for bouncer.
|
||||
('lang', locale),
|
||||
])])
|
||||
return "?".join(
|
||||
[
|
||||
self.bouncer_url,
|
||||
urlencode(
|
||||
[
|
||||
("product", self.product_map.get(channel, "fennec-latest")),
|
||||
("os", self.platform_map[arch]),
|
||||
# Order matters, lang must be last for bouncer.
|
||||
("lang", locale),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
if channel != 'release':
|
||||
product_id = self.store_product_ids.get(channel, 'org.mozilla.firefox')
|
||||
return self.store_url.replace(self.store_product_ids['release'], product_id)
|
||||
if channel != "release":
|
||||
product_id = self.store_product_ids.get(channel, "org.mozilla.firefox")
|
||||
return self.store_url.replace(self.store_product_ids["release"], product_id)
|
||||
|
||||
return self.store_url
|
||||
|
||||
|
@ -495,22 +516,22 @@ class FirefoxAndroid(_ProductDetails):
|
|||
class FirefoxIOS(_ProductDetails):
|
||||
# Version property names in product-details
|
||||
version_map = {
|
||||
'beta': 'ios_beta_version',
|
||||
'release': 'ios_version',
|
||||
"beta": "ios_beta_version",
|
||||
"release": "ios_version",
|
||||
}
|
||||
store_url = settings.APPLE_APPSTORE_FIREFOX_LINK
|
||||
|
||||
def latest_version(self, channel):
|
||||
version = self.version_map.get(channel, 'ios_version')
|
||||
version = self.version_map.get(channel, "ios_version")
|
||||
return self.mobile_details[version]
|
||||
|
||||
def get_download_url(self, channel='release', locale='en-US'):
|
||||
def get_download_url(self, channel="release", locale="en-US"):
|
||||
countries = settings.APPLE_APPSTORE_COUNTRY_MAP
|
||||
|
||||
if locale in countries:
|
||||
return self.store_url.format(country=countries[locale])
|
||||
|
||||
return self.store_url.replace('/{country}/', '/')
|
||||
return self.store_url.replace("/{country}/", "/")
|
||||
|
||||
|
||||
firefox_desktop = FirefoxDesktop()
|
||||
|
|
|
@ -7,8 +7,11 @@ from django import forms
|
|||
|
||||
class SendToDeviceWidgetForm(forms.Form):
|
||||
email = forms.EmailField(max_length=100, required=False)
|
||||
platform = forms.ChoiceField(choices=(
|
||||
('ios', 'ios'),
|
||||
('android', 'android'),
|
||||
('all', 'all'),
|
||||
), required=False)
|
||||
platform = forms.ChoiceField(
|
||||
choices=(
|
||||
("ios", "ios"),
|
||||
("android", "android"),
|
||||
("all", "all"),
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,23 +9,31 @@ from bedrock.base.urlresolvers import reverse
|
|||
from lib.l10n_utils import get_locale
|
||||
|
||||
|
||||
def desktop_builds(channel, builds=None, locale=None, force_direct=False,
|
||||
force_full_installer=False, force_funnelcake=False,
|
||||
funnelcake_id=False, locale_in_transition=False, classified=False):
|
||||
def desktop_builds(
|
||||
channel,
|
||||
builds=None,
|
||||
locale=None,
|
||||
force_direct=False,
|
||||
force_full_installer=False,
|
||||
force_funnelcake=False,
|
||||
funnelcake_id=False,
|
||||
locale_in_transition=False,
|
||||
classified=False,
|
||||
):
|
||||
builds = builds or []
|
||||
|
||||
l_version = firefox_desktop.latest_builds(locale, channel)
|
||||
|
||||
# Developer Edition is now based on the Beta channel, so the build list
|
||||
# should be generated from the Beta locales.
|
||||
if channel == 'alpha':
|
||||
l_version = firefox_desktop.latest_builds(locale, 'beta')
|
||||
if channel == "alpha":
|
||||
l_version = firefox_desktop.latest_builds(locale, "beta")
|
||||
|
||||
if l_version:
|
||||
version, platforms = l_version
|
||||
else:
|
||||
locale = 'en-US'
|
||||
version, platforms = firefox_desktop.latest_builds('en-US', channel)
|
||||
locale = "en-US"
|
||||
version, platforms = firefox_desktop.latest_builds("en-US", channel)
|
||||
|
||||
for plat_os, plat_os_pretty in firefox_desktop.platforms(channel, classified):
|
||||
|
||||
|
@ -34,16 +42,19 @@ def desktop_builds(channel, builds=None, locale=None, force_direct=False,
|
|||
# Firefox Nightly: The Windows stub installer is now universal,
|
||||
# automatically detecting a 32-bit and 64-bit desktop, so the
|
||||
# win64-specific entry can be skipped.
|
||||
if channel == 'nightly':
|
||||
if plat_os == 'win':
|
||||
if channel == "nightly":
|
||||
if plat_os == "win":
|
||||
continue
|
||||
if plat_os == 'win64':
|
||||
plat_os = 'win'
|
||||
os_pretty = 'Windows 32/64-bit'
|
||||
if plat_os == "win64":
|
||||
plat_os = "win"
|
||||
os_pretty = "Windows 32/64-bit"
|
||||
|
||||
# And generate all the info
|
||||
download_link = firefox_desktop.get_download_url(
|
||||
channel, version, plat_os, locale,
|
||||
channel,
|
||||
version,
|
||||
plat_os,
|
||||
locale,
|
||||
force_direct=force_direct,
|
||||
force_full_installer=force_full_installer,
|
||||
force_funnelcake=force_funnelcake,
|
||||
|
@ -58,7 +69,10 @@ def desktop_builds(channel, builds=None, locale=None, force_direct=False,
|
|||
download_link_direct = False
|
||||
else:
|
||||
download_link_direct = firefox_desktop.get_download_url(
|
||||
channel, version, plat_os, locale,
|
||||
channel,
|
||||
version,
|
||||
plat_os,
|
||||
locale,
|
||||
force_direct=True,
|
||||
force_full_installer=force_full_installer,
|
||||
force_funnelcake=force_funnelcake,
|
||||
|
@ -67,10 +81,7 @@ def desktop_builds(channel, builds=None, locale=None, force_direct=False,
|
|||
if download_link_direct == download_link:
|
||||
download_link_direct = False
|
||||
|
||||
builds.append({'os': plat_os,
|
||||
'os_pretty': os_pretty,
|
||||
'download_link': download_link,
|
||||
'download_link_direct': download_link_direct})
|
||||
builds.append({"os": plat_os, "os_pretty": os_pretty, "download_link": download_link, "download_link_direct": download_link_direct})
|
||||
|
||||
return builds
|
||||
|
||||
|
@ -78,9 +89,7 @@ def desktop_builds(channel, builds=None, locale=None, force_direct=False,
|
|||
def android_builds(channel, builds=None):
|
||||
builds = builds or []
|
||||
link = firefox_android.get_download_url(channel.lower())
|
||||
builds.append({'os': 'android',
|
||||
'os_pretty': 'Android',
|
||||
'download_link': link})
|
||||
builds.append({"os": "android", "os_pretty": "Android", "download_link": link})
|
||||
|
||||
return builds
|
||||
|
||||
|
@ -88,21 +97,28 @@ def android_builds(channel, builds=None):
|
|||
def ios_builds(channel, builds=None):
|
||||
builds = builds or []
|
||||
link = firefox_ios.get_download_url(channel)
|
||||
builds.append({'os': 'ios',
|
||||
'os_pretty': 'iOS',
|
||||
'download_link': link})
|
||||
builds.append({"os": "ios", "os_pretty": "iOS", "download_link": link})
|
||||
|
||||
return builds
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def download_firefox(ctx, channel='release', platform='all',
|
||||
dom_id=None, locale=None, force_direct=False,
|
||||
force_full_installer=False, force_funnelcake=False,
|
||||
alt_copy=None, button_class='mzp-t-xl',
|
||||
locale_in_transition=False, download_location=None):
|
||||
""" Output a "download firefox" button.
|
||||
def download_firefox(
|
||||
ctx,
|
||||
channel="release",
|
||||
platform="all",
|
||||
dom_id=None,
|
||||
locale=None,
|
||||
force_direct=False,
|
||||
force_full_installer=False,
|
||||
force_funnelcake=False,
|
||||
alt_copy=None,
|
||||
button_class="mzp-t-xl",
|
||||
locale_in_transition=False,
|
||||
download_location=None,
|
||||
):
|
||||
"""Output a "download firefox" button.
|
||||
|
||||
:param ctx: context from calling template.
|
||||
:param channel: name of channel: 'release', 'beta', 'alpha', or 'nightly'.
|
||||
|
@ -120,23 +136,20 @@ def download_firefox(ctx, channel='release', platform='all',
|
|||
:param download_location: Specify the location of download button for
|
||||
GA reporting: 'primary cta', 'nav', 'sub nav', or 'other'.
|
||||
"""
|
||||
show_desktop = platform in ['all', 'desktop']
|
||||
show_android = platform in ['all', 'android']
|
||||
show_ios = platform in ['all', 'ios']
|
||||
alt_channel = '' if channel == 'release' else channel
|
||||
locale = locale or get_locale(ctx['request'])
|
||||
funnelcake_id = ctx.get('funnelcake_id', False)
|
||||
dom_id = dom_id or 'download-button-%s-%s' % (
|
||||
'desktop' if platform == 'all' else platform, channel)
|
||||
show_desktop = platform in ["all", "desktop"]
|
||||
show_android = platform in ["all", "android"]
|
||||
show_ios = platform in ["all", "ios"]
|
||||
alt_channel = "" if channel == "release" else channel
|
||||
locale = locale or get_locale(ctx["request"])
|
||||
funnelcake_id = ctx.get("funnelcake_id", False)
|
||||
dom_id = dom_id or "download-button-%s-%s" % ("desktop" if platform == "all" else platform, channel)
|
||||
|
||||
# Gather data about the build for each platform
|
||||
builds = []
|
||||
|
||||
if show_desktop:
|
||||
version = firefox_desktop.latest_version(channel)
|
||||
builds = desktop_builds(channel, builds, locale, force_direct,
|
||||
force_full_installer, force_funnelcake,
|
||||
funnelcake_id, locale_in_transition)
|
||||
builds = desktop_builds(channel, builds, locale, force_direct, force_full_installer, force_funnelcake, funnelcake_id, locale_in_transition)
|
||||
|
||||
if show_android:
|
||||
version = firefox_android.latest_version(channel)
|
||||
|
@ -144,40 +157,36 @@ def download_firefox(ctx, channel='release', platform='all',
|
|||
|
||||
if show_ios:
|
||||
version = firefox_ios.latest_version(channel)
|
||||
builds.append({'os': 'ios',
|
||||
'os_pretty': 'iOS',
|
||||
'download_link': firefox_ios.get_download_url()})
|
||||
builds.append({"os": "ios", "os_pretty": "iOS", "download_link": firefox_ios.get_download_url()})
|
||||
|
||||
# Get the native name for current locale
|
||||
langs = firefox_desktop.languages
|
||||
locale_name = langs[locale]['native'] if locale in langs else locale
|
||||
locale_name = langs[locale]["native"] if locale in langs else locale
|
||||
|
||||
data = {
|
||||
'locale_name': locale_name,
|
||||
'version': version,
|
||||
'product': 'firefox-%s' % platform,
|
||||
'builds': builds,
|
||||
'id': dom_id,
|
||||
'channel': alt_channel,
|
||||
'show_desktop': show_desktop,
|
||||
'show_android': show_android,
|
||||
'show_ios': show_ios,
|
||||
'alt_copy': alt_copy,
|
||||
'button_class': button_class,
|
||||
'download_location': download_location,
|
||||
'fluent_l10n': ctx['fluent_l10n']
|
||||
"locale_name": locale_name,
|
||||
"version": version,
|
||||
"product": "firefox-%s" % platform,
|
||||
"builds": builds,
|
||||
"id": dom_id,
|
||||
"channel": alt_channel,
|
||||
"show_desktop": show_desktop,
|
||||
"show_android": show_android,
|
||||
"show_ios": show_ios,
|
||||
"alt_copy": alt_copy,
|
||||
"button_class": button_class,
|
||||
"download_location": download_location,
|
||||
"fluent_l10n": ctx["fluent_l10n"],
|
||||
}
|
||||
|
||||
html = render_to_string('firefox/includes/download-button.html', data,
|
||||
request=ctx['request'])
|
||||
html = render_to_string("firefox/includes/download-button.html", data, request=ctx["request"])
|
||||
return jinja2.Markup(html)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button_class=None,
|
||||
locale_in_transition=False, download_location=None):
|
||||
""" Output a simple "download firefox" button that only points to /download/thanks/
|
||||
def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button_class=None, locale_in_transition=False, download_location=None):
|
||||
"""Output a simple "download firefox" button that only points to /download/thanks/
|
||||
|
||||
:param ctx: context from calling template.
|
||||
:param dom_id: Use this string as the id attr on the element.
|
||||
|
@ -189,23 +198,26 @@ def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button
|
|||
GA reporting: 'primary cta', 'nav', 'sub nav', or 'other'.
|
||||
"""
|
||||
|
||||
channel = 'release'
|
||||
locale = locale or get_locale(ctx['request'])
|
||||
funnelcake_id = ctx.get('funnelcake_id', False)
|
||||
dom_id = dom_id or 'download-button-thanks'
|
||||
transition_url = '/firefox/download/thanks/'
|
||||
channel = "release"
|
||||
locale = locale or get_locale(ctx["request"])
|
||||
funnelcake_id = ctx.get("funnelcake_id", False)
|
||||
dom_id = dom_id or "download-button-thanks"
|
||||
transition_url = "/firefox/download/thanks/"
|
||||
version = firefox_desktop.latest_version(channel)
|
||||
|
||||
# if there's a funnelcake param in the page URL e.g. ?f=123
|
||||
if funnelcake_id:
|
||||
# include param in transitional URL e.g. /firefox/download/thanks/?f=123
|
||||
transition_url += '?f=%s' % funnelcake_id
|
||||
transition_url += "?f=%s" % funnelcake_id
|
||||
|
||||
if locale_in_transition:
|
||||
transition_url = '/%s%s' % (locale, transition_url)
|
||||
transition_url = "/%s%s" % (locale, transition_url)
|
||||
|
||||
download_link_direct = firefox_desktop.get_download_url(
|
||||
channel, version, 'win', locale,
|
||||
channel,
|
||||
version,
|
||||
"win",
|
||||
locale,
|
||||
force_direct=True,
|
||||
force_full_installer=False,
|
||||
force_funnelcake=False,
|
||||
|
@ -213,24 +225,22 @@ def download_firefox_thanks(ctx, dom_id=None, locale=None, alt_copy=None, button
|
|||
)
|
||||
|
||||
data = {
|
||||
'id': dom_id,
|
||||
'transition_url': transition_url,
|
||||
'download_link_direct': download_link_direct,
|
||||
'alt_copy': alt_copy,
|
||||
'button_class': button_class,
|
||||
'download_location': download_location,
|
||||
'fluent_l10n': ctx['fluent_l10n']
|
||||
"id": dom_id,
|
||||
"transition_url": transition_url,
|
||||
"download_link_direct": download_link_direct,
|
||||
"alt_copy": alt_copy,
|
||||
"button_class": button_class,
|
||||
"download_location": download_location,
|
||||
"fluent_l10n": ctx["fluent_l10n"],
|
||||
}
|
||||
|
||||
html = render_to_string('firefox/includes/download-button-thanks.html', data,
|
||||
request=ctx['request'])
|
||||
html = render_to_string("firefox/includes/download-button-thanks.html", data, request=ctx["request"])
|
||||
return jinja2.Markup(html)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def download_firefox_desktop_list(ctx, channel='release', dom_id=None, locale=None,
|
||||
force_full_installer=False):
|
||||
def download_firefox_desktop_list(ctx, channel="release", dom_id=None, locale=None, force_full_installer=False):
|
||||
"""
|
||||
Return a HTML list of platform download links for Firefox desktop
|
||||
|
||||
|
@ -241,41 +251,38 @@ def download_firefox_desktop_list(ctx, channel='release', dom_id=None, locale=No
|
|||
the stub installer (for aurora).
|
||||
|
||||
"""
|
||||
dom_id = dom_id or 'download-platform-list-%s' % (channel)
|
||||
locale = locale or get_locale(ctx['request'])
|
||||
dom_id = dom_id or "download-platform-list-%s" % (channel)
|
||||
locale = locale or get_locale(ctx["request"])
|
||||
|
||||
# Make sure funnelcake_id is not passed as builds are often Windows only.
|
||||
builds = desktop_builds(channel, None, locale, True, force_full_installer,
|
||||
False, False, False, True)
|
||||
builds = desktop_builds(channel, None, locale, True, force_full_installer, False, False, False, True)
|
||||
|
||||
recommended_builds = []
|
||||
traditional_builds = []
|
||||
|
||||
for plat in builds:
|
||||
# Add 32-bit label for Windows and Linux builds.
|
||||
if channel != 'nightly':
|
||||
if plat['os'] == 'win':
|
||||
plat['os_pretty'] = 'Windows 32-bit'
|
||||
if channel != "nightly":
|
||||
if plat["os"] == "win":
|
||||
plat["os_pretty"] = "Windows 32-bit"
|
||||
|
||||
if plat['os'] == 'linux':
|
||||
plat['os_pretty'] = 'Linux 32-bit'
|
||||
if plat["os"] == "linux":
|
||||
plat["os_pretty"] = "Linux 32-bit"
|
||||
|
||||
if (plat['os'] in firefox_desktop.platform_classification['recommended'] or
|
||||
channel == 'nightly' and plat['os'] == 'win'):
|
||||
if plat["os"] in firefox_desktop.platform_classification["recommended"] or channel == "nightly" and plat["os"] == "win":
|
||||
recommended_builds.append(plat)
|
||||
else:
|
||||
traditional_builds.append(plat)
|
||||
|
||||
data = {
|
||||
'id': dom_id,
|
||||
'builds': {
|
||||
'recommended': recommended_builds,
|
||||
'traditional': traditional_builds,
|
||||
"id": dom_id,
|
||||
"builds": {
|
||||
"recommended": recommended_builds,
|
||||
"traditional": traditional_builds,
|
||||
},
|
||||
}
|
||||
|
||||
html = render_to_string('firefox/includes/download-list.html', data,
|
||||
request=ctx['request'])
|
||||
html = render_to_string("firefox/includes/download-list.html", data, request=ctx["request"])
|
||||
return jinja2.Markup(html)
|
||||
|
||||
|
||||
|
@ -299,45 +306,45 @@ def firefox_url(platform, page, channel=None):
|
|||
anchor = None
|
||||
|
||||
# Tweak the channel name for the naming URL pattern in urls.py
|
||||
if channel == 'release':
|
||||
if channel == "release":
|
||||
channel = None
|
||||
if channel == 'alpha':
|
||||
if platform == 'desktop':
|
||||
channel = 'developer'
|
||||
if platform == 'android':
|
||||
channel = 'aurora'
|
||||
if channel == 'esr':
|
||||
channel = 'organizations'
|
||||
if channel == "alpha":
|
||||
if platform == "desktop":
|
||||
channel = "developer"
|
||||
if platform == "android":
|
||||
channel = "aurora"
|
||||
if channel == "esr":
|
||||
channel = "organizations"
|
||||
|
||||
# There is now only one /all page URL - issue 8096
|
||||
if page == 'all':
|
||||
if platform == 'desktop':
|
||||
if channel == 'beta':
|
||||
anchor = 'product-desktop-beta'
|
||||
elif channel == 'developer':
|
||||
anchor = 'product-desktop-developer'
|
||||
elif channel == 'nightly':
|
||||
anchor = 'product-desktop-nightly'
|
||||
elif channel == 'organizations':
|
||||
anchor = 'product-desktop-esr'
|
||||
if page == "all":
|
||||
if platform == "desktop":
|
||||
if channel == "beta":
|
||||
anchor = "product-desktop-beta"
|
||||
elif channel == "developer":
|
||||
anchor = "product-desktop-developer"
|
||||
elif channel == "nightly":
|
||||
anchor = "product-desktop-nightly"
|
||||
elif channel == "organizations":
|
||||
anchor = "product-desktop-esr"
|
||||
else:
|
||||
anchor = 'product-desktop-release'
|
||||
elif platform == 'android':
|
||||
if channel == 'beta':
|
||||
anchor = 'product-android-beta'
|
||||
elif channel == 'nightly':
|
||||
anchor = 'product-android-nightly'
|
||||
anchor = "product-desktop-release"
|
||||
elif platform == "android":
|
||||
if channel == "beta":
|
||||
anchor = "product-android-beta"
|
||||
elif channel == "nightly":
|
||||
anchor = "product-android-nightly"
|
||||
else:
|
||||
anchor = 'product-android-release'
|
||||
anchor = "product-android-release"
|
||||
else:
|
||||
if channel:
|
||||
kwargs['channel'] = channel
|
||||
if platform != 'desktop':
|
||||
kwargs['platform'] = platform
|
||||
kwargs["channel"] = channel
|
||||
if platform != "desktop":
|
||||
kwargs["platform"] = platform
|
||||
|
||||
# Firefox for Android and iOS have the system requirements page on SUMO
|
||||
if platform in ['android', 'ios'] and page == 'sysreq':
|
||||
if platform in ["android", "ios"] and page == "sysreq":
|
||||
return settings.FIREFOX_MOBILE_SYSREQ_URL
|
||||
|
||||
anchor = '#' + anchor if anchor else ''
|
||||
return reverse(f'firefox.{page}', kwargs=kwargs) + anchor
|
||||
anchor = "#" + anchor if anchor else ""
|
||||
return reverse(f"firefox.{page}", kwargs=kwargs) + anchor
|
||||
|
|
|
@ -20,20 +20,19 @@ from bedrock.firefox.firefox_details import FirefoxDesktop
|
|||
from bedrock.mozorg.tests import TestCase
|
||||
|
||||
|
||||
TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), 'test_data')
|
||||
PROD_DETAILS_DIR = os.path.join(TEST_DATA_DIR, 'product_details_json')
|
||||
GOOD_PLATS = {'Windows': {}, 'OS X': {}, 'Linux': {}}
|
||||
TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "test_data")
|
||||
PROD_DETAILS_DIR = os.path.join(TEST_DATA_DIR, "product_details_json")
|
||||
GOOD_PLATS = {"Windows": {}, "OS X": {}, "Linux": {}}
|
||||
jinja_env = Jinja2.get_default().env
|
||||
|
||||
|
||||
class TestInstallerHelp(TestCase):
|
||||
def setUp(self):
|
||||
self.button_mock = Mock()
|
||||
self.patcher = patch.dict(jinja_env.globals,
|
||||
download_firefox=self.button_mock)
|
||||
self.patcher = patch.dict(jinja_env.globals, download_firefox=self.button_mock)
|
||||
self.patcher.start()
|
||||
self.view_name = 'firefox.installer-help'
|
||||
with self.activate('en-US'):
|
||||
self.view_name = "firefox.installer-help"
|
||||
with self.activate("en-US"):
|
||||
self.url = reverse(self.view_name)
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -43,78 +42,158 @@ class TestInstallerHelp(TestCase):
|
|||
"""
|
||||
The buttons should use the lang from the query parameter.
|
||||
"""
|
||||
self.client.get(self.url, {
|
||||
'installer_lang': 'fr'
|
||||
})
|
||||
self.button_mock.assert_has_calls([
|
||||
call(alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale='fr'),
|
||||
call('beta', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale='fr'),
|
||||
call('alpha', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale='fr', platform='desktop'),
|
||||
call('nightly', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale='fr', platform='desktop'),
|
||||
])
|
||||
self.client.get(self.url, {"installer_lang": "fr"})
|
||||
self.button_mock.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale="fr",
|
||||
),
|
||||
call(
|
||||
"beta",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale="fr",
|
||||
),
|
||||
call(
|
||||
"alpha",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale="fr",
|
||||
platform="desktop",
|
||||
),
|
||||
call(
|
||||
"nightly",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale="fr",
|
||||
platform="desktop",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def test_buttons_ignore_non_lang(self):
|
||||
"""
|
||||
The buttons should ignore an invalid lang.
|
||||
"""
|
||||
self.client.get(self.url, {
|
||||
'installer_lang': 'not-a-locale'
|
||||
})
|
||||
self.button_mock.assert_has_calls([
|
||||
call(alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None),
|
||||
call('beta', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None),
|
||||
call('alpha', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None, platform='desktop'),
|
||||
call('nightly', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None, platform='desktop'),
|
||||
])
|
||||
self.client.get(self.url, {"installer_lang": "not-a-locale"})
|
||||
self.button_mock.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
),
|
||||
call(
|
||||
"beta",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
),
|
||||
call(
|
||||
"alpha",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
platform="desktop",
|
||||
),
|
||||
call(
|
||||
"nightly",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
platform="desktop",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def test_invalid_channel_specified(self):
|
||||
"""
|
||||
All buttons should show when channel is invalid.
|
||||
"""
|
||||
self.client.get(self.url, {
|
||||
'channel': 'dude',
|
||||
})
|
||||
self.button_mock.assert_has_calls([
|
||||
call(alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None),
|
||||
call('beta', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None),
|
||||
call('alpha', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None, platform='desktop'),
|
||||
call('nightly', alt_copy=Markup('Download Now'), button_class='mzp-t-secondary mzp-t-md', force_direct=True,
|
||||
force_full_installer=True, locale=None, platform='desktop'),
|
||||
])
|
||||
self.client.get(
|
||||
self.url,
|
||||
{
|
||||
"channel": "dude",
|
||||
},
|
||||
)
|
||||
self.button_mock.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
),
|
||||
call(
|
||||
"beta",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
),
|
||||
call(
|
||||
"alpha",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
platform="desktop",
|
||||
),
|
||||
call(
|
||||
"nightly",
|
||||
alt_copy=Markup("Download Now"),
|
||||
button_class="mzp-t-secondary mzp-t-md",
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None,
|
||||
platform="desktop",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def test_one_button_when_channel_specified(self):
|
||||
"""
|
||||
There should be only one button when the channel is given.
|
||||
"""
|
||||
self.client.get(self.url, {
|
||||
'channel': 'beta',
|
||||
})
|
||||
self.button_mock.assert_called_once_with('beta',
|
||||
alt_copy=Markup('Download Now'), button_class='mzp-t-md',
|
||||
force_direct=True,
|
||||
force_full_installer=True,
|
||||
locale=None)
|
||||
self.client.get(
|
||||
self.url,
|
||||
{
|
||||
"channel": "beta",
|
||||
},
|
||||
)
|
||||
self.button_mock.assert_called_once_with(
|
||||
"beta", alt_copy=Markup("Download Now"), button_class="mzp-t-md", force_direct=True, force_full_installer=True, locale=None
|
||||
)
|
||||
|
||||
|
||||
class TestFirefoxAll(TestCase):
|
||||
pd_cache = caches['product-details']
|
||||
pd_cache = caches["product-details"]
|
||||
|
||||
def setUp(self):
|
||||
self.pd_cache.clear()
|
||||
self.firefox_desktop = FirefoxDesktop(json_dir=PROD_DETAILS_DIR)
|
||||
self.patcher = patch.object(
|
||||
fx_views, 'firefox_desktop', self.firefox_desktop)
|
||||
self.patcher = patch.object(fx_views, "firefox_desktop", self.firefox_desktop)
|
||||
self.patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -124,27 +203,27 @@ class TestFirefoxAll(TestCase):
|
|||
"""
|
||||
The unified page should display builds for all products
|
||||
"""
|
||||
resp = self.client.get(reverse('firefox.all'))
|
||||
resp = self.client.get(reverse("firefox.all"))
|
||||
doc = pq(resp.content)
|
||||
assert len(doc('.c-all-downloads-build')) == 9
|
||||
assert len(doc(".c-all-downloads-build")) == 9
|
||||
|
||||
desktop_release_builds = len(self.firefox_desktop.get_filtered_full_builds('release'))
|
||||
desktop_release_builds = len(self.firefox_desktop.get_filtered_full_builds("release"))
|
||||
assert len(doc('.c-locale-list[data-product="desktop_release"] > li')) == desktop_release_builds
|
||||
assert len(doc('.c-locale-list[data-product="desktop_release"] > li[data-language="en-US"] > ul > li > a')) == 8
|
||||
|
||||
desktop_beta_builds = len(self.firefox_desktop.get_filtered_full_builds('beta'))
|
||||
desktop_beta_builds = len(self.firefox_desktop.get_filtered_full_builds("beta"))
|
||||
assert len(doc('.c-locale-list[data-product="desktop_beta"] > li')) == desktop_beta_builds
|
||||
assert len(doc('.c-locale-list[data-product="desktop_beta"] > li[data-language="en-US"] > ul > li > a')) == 8
|
||||
|
||||
desktop_developer_builds = len(self.firefox_desktop.get_filtered_full_builds('alpha'))
|
||||
desktop_developer_builds = len(self.firefox_desktop.get_filtered_full_builds("alpha"))
|
||||
assert len(doc('.c-locale-list[data-product="desktop_developer"] > li')) == desktop_developer_builds
|
||||
assert len(doc('.c-locale-list[data-product="desktop_developer"] > li[data-language="en-US"] > ul > li > a')) == 8
|
||||
|
||||
desktop_nightly_builds = len(self.firefox_desktop.get_filtered_full_builds('nightly'))
|
||||
desktop_nightly_builds = len(self.firefox_desktop.get_filtered_full_builds("nightly"))
|
||||
assert len(doc('.c-locale-list[data-product="desktop_nightly"] > li')) == desktop_nightly_builds
|
||||
assert len(doc('.c-locale-list[data-product="desktop_nightly"] > li[data-language="en-US"] > ul > li > a')) == 8
|
||||
|
||||
desktop_esr_builds = len(self.firefox_desktop.get_filtered_full_builds('esr'))
|
||||
desktop_esr_builds = len(self.firefox_desktop.get_filtered_full_builds("esr"))
|
||||
assert len(doc('.c-locale-list[data-product="desktop_esr"] > li')) == desktop_esr_builds
|
||||
assert len(doc('.c-locale-list[data-product="desktop_esr"] > li[data-language="en-US"] > ul > li > a')) == 8
|
||||
|
||||
|
@ -170,65 +249,69 @@ class TestFirefoxAll(TestCase):
|
|||
locale details are not updated yet, the filtered build list should not
|
||||
include the localized build.
|
||||
"""
|
||||
builds = self.firefox_desktop.get_filtered_full_builds('release')
|
||||
assert 'uz' in self.firefox_desktop.firefox_primary_builds
|
||||
assert 'uz' not in self.firefox_desktop.languages
|
||||
assert len([build for build in builds if build['locale'] == 'uz']) == 0
|
||||
builds = self.firefox_desktop.get_filtered_full_builds("release")
|
||||
assert "uz" in self.firefox_desktop.firefox_primary_builds
|
||||
assert "uz" not in self.firefox_desktop.languages
|
||||
assert len([build for build in builds if build["locale"] == "uz"]) == 0
|
||||
|
||||
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestWhatsNew(TestCase):
|
||||
def setUp(self):
|
||||
self.view = fx_views.WhatsnewView.as_view()
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT='Firefox')
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT="Firefox")
|
||||
|
||||
# begin context variable tests
|
||||
|
||||
@override_settings(DEV=True)
|
||||
@patch.object(fx_views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(fx_views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_context_variables_whatsnew(self, render_mock):
|
||||
"""Should pass the correct context variables"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='70.0')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="70.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
ctx = render_mock.call_args[0][2]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert ctx['version'] == '70.0'
|
||||
assert ctx['analytics_version'] == '70'
|
||||
assert ctx['entrypoint'] == 'mozilla.org-whatsnew70'
|
||||
assert ctx['campaign'] == 'whatsnew70'
|
||||
assert ctx['utm_params'] == ('utm_source=mozilla.org-whatsnew70&utm_medium=referral'
|
||||
'&utm_campaign=whatsnew70&entrypoint=mozilla.org-whatsnew70')
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
assert ctx["version"] == "70.0"
|
||||
assert ctx["analytics_version"] == "70"
|
||||
assert ctx["entrypoint"] == "mozilla.org-whatsnew70"
|
||||
assert ctx["campaign"] == "whatsnew70"
|
||||
assert ctx["utm_params"] == (
|
||||
"utm_source=mozilla.org-whatsnew70&utm_medium=referral&utm_campaign=whatsnew70&entrypoint=mozilla.org-whatsnew70"
|
||||
)
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_context_variables_whatsnew_developer(self, render_mock):
|
||||
"""Should pass the correct context variables for developer channel"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='72.0a2')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="72.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
ctx = render_mock.call_args[0][2]
|
||||
assert template == ['firefox/developer/whatsnew.html']
|
||||
assert ctx['version'] == '72.0a2'
|
||||
assert ctx['analytics_version'] == '72developer'
|
||||
assert ctx['entrypoint'] == 'mozilla.org-whatsnew72developer'
|
||||
assert ctx['campaign'] == 'whatsnew72developer'
|
||||
assert ctx['utm_params'] == ('utm_source=mozilla.org-whatsnew72developer&utm_medium=referral'
|
||||
'&utm_campaign=whatsnew72developer&entrypoint=mozilla.org-whatsnew72developer')
|
||||
assert template == ["firefox/developer/whatsnew.html"]
|
||||
assert ctx["version"] == "72.0a2"
|
||||
assert ctx["analytics_version"] == "72developer"
|
||||
assert ctx["entrypoint"] == "mozilla.org-whatsnew72developer"
|
||||
assert ctx["campaign"] == "whatsnew72developer"
|
||||
assert ctx["utm_params"] == (
|
||||
"utm_source=mozilla.org-whatsnew72developer&utm_medium=referral"
|
||||
"&utm_campaign=whatsnew72developer&entrypoint=mozilla.org-whatsnew72developer"
|
||||
)
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_context_variables_whatsnew_nightly(self, render_mock):
|
||||
"""Should pass the correct context variables for nightly channel"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='72.0a1')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="72.0a1")
|
||||
template = render_mock.call_args[0][1]
|
||||
ctx = render_mock.call_args[0][2]
|
||||
assert template == ['firefox/nightly/whatsnew.html']
|
||||
assert ctx['version'] == '72.0a1'
|
||||
assert ctx['analytics_version'] == '72nightly'
|
||||
assert ctx['entrypoint'] == 'mozilla.org-whatsnew72nightly'
|
||||
assert ctx['campaign'] == 'whatsnew72nightly'
|
||||
assert ctx['utm_params'] == ('utm_source=mozilla.org-whatsnew72nightly&utm_medium=referral'
|
||||
'&utm_campaign=whatsnew72nightly&entrypoint=mozilla.org-whatsnew72nightly')
|
||||
assert template == ["firefox/nightly/whatsnew.html"]
|
||||
assert ctx["version"] == "72.0a1"
|
||||
assert ctx["analytics_version"] == "72nightly"
|
||||
assert ctx["entrypoint"] == "mozilla.org-whatsnew72nightly"
|
||||
assert ctx["campaign"] == "whatsnew72nightly"
|
||||
assert ctx["utm_params"] == (
|
||||
"utm_source=mozilla.org-whatsnew72nightly&utm_medium=referral&utm_campaign=whatsnew72nightly&entrypoint=mozilla.org-whatsnew72nightly"
|
||||
)
|
||||
|
||||
# end context variable tests
|
||||
|
||||
|
@ -237,10 +320,10 @@ class TestWhatsNew(TestCase):
|
|||
@override_settings(DEV=True)
|
||||
def test_fx_nightly_68_0_a1_whatsnew(self, render_mock):
|
||||
"""Should show nightly whatsnew template"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='68.0a1')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="68.0a1")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/nightly/whatsnew.html']
|
||||
assert template == ["firefox/nightly/whatsnew.html"]
|
||||
|
||||
# end nightly whatsnew tests
|
||||
|
||||
|
@ -249,90 +332,90 @@ class TestWhatsNew(TestCase):
|
|||
@override_settings(DEV=True)
|
||||
def test_fx_dev_browser_35_0_a2_whatsnew(self, render_mock):
|
||||
"""Should show default whatsnew template"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='35.0a2')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="35.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index.html']
|
||||
assert template == ["firefox/whatsnew/index.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_fx_dev_browser_57_0_a2_whatsnew(self, render_mock):
|
||||
"""Should show dev browser 57 whatsnew template"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='57.0a2')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="57.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/developer/whatsnew.html']
|
||||
assert template == ["firefox/developer/whatsnew.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
@patch.dict(os.environ, SWITCH_DEV_WHATSNEW_68='False')
|
||||
@patch.dict(os.environ, SWITCH_DEV_WHATSNEW_68="False")
|
||||
def test_fx_dev_browser_68_0_a2_whatsnew_off(self, render_mock):
|
||||
"""Should show regular dev browser whatsnew template"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='68.0a2')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="68.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/developer/whatsnew.html']
|
||||
assert template == ["firefox/developer/whatsnew.html"]
|
||||
|
||||
# end dev edition whatsnew tests
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_rv_prefix(self, render_mock):
|
||||
"""Prefixed oldversion shouldn't impact version sniffing."""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/?oldversion=rv:10.0')
|
||||
self.view(req, version='54.0')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/?oldversion=rv:10.0")
|
||||
self.view(req, version="54.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index.html']
|
||||
assert template == ["firefox/whatsnew/index.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
@patch.object(fx_views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(fx_views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_fx_default_whatsnew_sync(self, render_mock):
|
||||
"""Should use sync template for 60.0"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='60.0')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="60.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
@patch.object(fx_views, 'ftl_file_is_active', lambda *x: False)
|
||||
@patch.object(fx_views, "ftl_file_is_active", lambda *x: False)
|
||||
def test_fx_default_whatsnew_fallback(self, render_mock):
|
||||
"""Should use standard template for 60.0 as fallback"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='60.0')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="60.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index.html']
|
||||
assert template == ["firefox/whatsnew/index.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
@patch.object(fx_views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(fx_views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_fx_default_whatsnew(self, render_mock):
|
||||
"""Should use standard template for 59.0"""
|
||||
req = self.rf.get('/en-US/firefox/whatsnew/')
|
||||
self.view(req, version='59.0')
|
||||
req = self.rf.get("/en-US/firefox/whatsnew/")
|
||||
self.view(req, version="59.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index.html']
|
||||
assert template == ["firefox/whatsnew/index.html"]
|
||||
|
||||
# begin 86.0 whatsnew tests
|
||||
|
||||
def test_fx_86_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx86 template for 86.0 in English"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='86.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="86.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx86-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx86-en.html"]
|
||||
|
||||
def test_fx_86_0_0_de(self, render_mock):
|
||||
"""Should use whatsnew-mobile-qrcode-de template for 86.0 in German"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'de'
|
||||
self.view(req, version='86.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "de"
|
||||
self.view(req, version="86.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-mobile-qrcode-de.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-mobile-qrcode-de.html"]
|
||||
|
||||
def test_fx_86_0_0_locale(self, render_mock):
|
||||
"""Should use standard whatsnew template for 86.0 in other locales"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'es-ES'
|
||||
self.view(req, version='86.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "es-ES"
|
||||
self.view(req, version="86.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
# end 86.0 whatsnew tests
|
||||
|
||||
|
@ -340,19 +423,19 @@ class TestWhatsNew(TestCase):
|
|||
|
||||
def test_fx_87_0_0_en(self, render_mock):
|
||||
"""Should use PiP template for 87.0 in US English"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='87.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="87.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx87-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx87-en.html"]
|
||||
|
||||
def test_fx_87_0_0_locale(self, render_mock):
|
||||
"""Should use standard whatsnew template for 87.0 in other locales"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'es-ES'
|
||||
self.view(req, version='87.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "es-ES"
|
||||
self.view(req, version="87.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
# end 87.0 whatsnew tests
|
||||
|
||||
|
@ -360,19 +443,19 @@ class TestWhatsNew(TestCase):
|
|||
|
||||
def test_fx_88_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx88-en template for 88.0 in English"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='88.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="88.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx88-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx88-en.html"]
|
||||
|
||||
def test_fx_88_0_0_locale(self, render_mock):
|
||||
"""Should use standard whatsnew template for 88.0 in other locales"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'es-ES'
|
||||
self.view(req, version='88.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "es-ES"
|
||||
self.view(req, version="88.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
# end 88.0 whatsnew tests
|
||||
|
||||
|
@ -380,51 +463,51 @@ class TestWhatsNew(TestCase):
|
|||
|
||||
def test_fx_90_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx90-en template for 90.0 in English"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-en.html"]
|
||||
|
||||
def test_fx_90_0_0_de(self, render_mock):
|
||||
"""Should use VPN template for 90.0 in German"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'de'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "de"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-eu.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-eu.html"]
|
||||
|
||||
def test_fx_90_0_0_fr(self, render_mock):
|
||||
"""Should use VPN template for 90.0 in French"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'fr'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "fr"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-eu.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-eu.html"]
|
||||
|
||||
def test_fx_90_0_0_it(self, render_mock):
|
||||
"""Should use VPN template for 90.0 in Italian"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'it'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "it"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-eu.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-eu.html"]
|
||||
|
||||
def test_fx_90_0_0_nl(self, render_mock):
|
||||
"""Should use VPN template for 90.0 in Dutch"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'nl'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "nl"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-eu.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-eu.html"]
|
||||
|
||||
def test_fx_90_0_0_es(self, render_mock):
|
||||
"""Should use VPN template for 90.0 in Spanish"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'es-ES'
|
||||
self.view(req, version='90.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "es-ES"
|
||||
self.view(req, version="90.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx90-eu.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx90-eu.html"]
|
||||
|
||||
# end 90.0 whatsnew tests
|
||||
|
||||
|
@ -432,27 +515,27 @@ class TestWhatsNew(TestCase):
|
|||
|
||||
def test_fx_91_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx91-en template for 91.0 in English"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='91.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="91.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx91-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx91-en.html"]
|
||||
|
||||
def test_fx_91_0_0_de(self, render_mock):
|
||||
"""Should use whatsnew-fx91-de template for 91.0 in German"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'de'
|
||||
self.view(req, version='91.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "de"
|
||||
self.view(req, version="91.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx91-de.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx91-de.html"]
|
||||
|
||||
def test_fx_91_0_0_locale(self, render_mock):
|
||||
"""Should use standard whatsnew template for 91.0 in other locales"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'pl'
|
||||
self.view(req, version='91.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "pl"
|
||||
self.view(req, version="91.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
# end 91.0 whatsnew tests
|
||||
|
||||
|
@ -460,92 +543,92 @@ class TestWhatsNew(TestCase):
|
|||
|
||||
def test_fx_92_0_0_de(self, render_mock):
|
||||
"""Should use whatsnew-fx92-de template for 92.0 in German"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'de'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "de"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-de.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-de.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING='False')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING="False")
|
||||
def test_fx_92_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx92-en template for 92.0 in English when VPN switch is OFF"""
|
||||
req = self.rf.get('/firefox/whatsnew/en/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/en/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-en.html"]
|
||||
|
||||
def test_fx_92_0_0_locale(self, render_mock):
|
||||
"""Should use standard whatsnew template for 92.0 in other locales"""
|
||||
req = self.rf.get('/firefox/whatsnew/')
|
||||
req.locale = 'pl'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/")
|
||||
req.locale = "pl"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/index-account.html']
|
||||
assert template == ["firefox/whatsnew/index-account.html"]
|
||||
|
||||
# end 92.0 whatsnew tests
|
||||
|
||||
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestWhatsNew92English(TestCase):
|
||||
def setUp(self):
|
||||
self.view = fx_views.WhatsNewEnglishView.as_view()
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT='Firefox')
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT="Firefox")
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING='False')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING="False")
|
||||
def test_fx_92_0_0_en(self, render_mock):
|
||||
"""Should use whatsnew-fx92-en template for 92.0 in English when VPN switch is OFF"""
|
||||
req = self.rf.get('/firefox/whatsnew/en/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/en/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-en.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING='True')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING="True")
|
||||
def test_fx_92_0_0_vpn_en(self, render_mock):
|
||||
"""Should use whatsnew-fx92-vpn-en template for 92.0 in English when VPN switch is ON"""
|
||||
req = self.rf.get('/firefox/whatsnew/en/')
|
||||
req.locale = 'en-US'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/en/")
|
||||
req.locale = "en-US"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-vpn-en.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-vpn-en.html"]
|
||||
|
||||
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestWhatsNew92France(TestCase):
|
||||
def setUp(self):
|
||||
self.view = fx_views.WhatsNewFranceView.as_view()
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT='Firefox')
|
||||
self.rf = RequestFactory(HTTP_USER_AGENT="Firefox")
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING_FR='False')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING_FR="False")
|
||||
def test_fx_92_0_0_fr(self, render_mock):
|
||||
"""Should use whatsnew-fx92-fr template for 92.0 in French when VPN switch is OFF"""
|
||||
req = self.rf.get('/firefox/whatsnew/france/')
|
||||
req.locale = 'fr'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/france/")
|
||||
req.locale = "fr"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-fr.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-fr.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING_FR='True')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING_FR="True")
|
||||
def test_fx_92_0_0_vpn_fr(self, render_mock):
|
||||
"""Should use whatsnew-fx92-vpn-fr template for 92.0 in French when VPN switch is ON"""
|
||||
req = self.rf.get('/firefox/whatsnew/france/')
|
||||
req.locale = 'fr'
|
||||
self.view(req, version='92.0')
|
||||
req = self.rf.get("/firefox/whatsnew/france/")
|
||||
req.locale = "fr"
|
||||
self.view(req, version="92.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/whatsnew/whatsnew-fx92-vpn-fr.html']
|
||||
assert template == ["firefox/whatsnew/whatsnew-fx92-vpn-fr.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING='True')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_WHATSNEW_92_VPN_PRICING="True")
|
||||
def test_fx_94_0_a1_fr(self, render_mock):
|
||||
"""Should use Nightly /whatsnew template for 94.0.a1"""
|
||||
req = self.rf.get('/firefox/whatsnew/france/')
|
||||
req.locale = 'fr'
|
||||
self.view(req, version='94.0a1')
|
||||
req = self.rf.get("/firefox/whatsnew/france/")
|
||||
req.locale = "fr"
|
||||
self.view(req, version="94.0a1")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/nightly/whatsnew.html']
|
||||
assert template == ["firefox/nightly/whatsnew.html"]
|
||||
|
||||
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestFirstRun(TestCase):
|
||||
def setUp(self):
|
||||
self.view = fx_views.FirstrunView.as_view()
|
||||
|
@ -554,48 +637,48 @@ class TestFirstRun(TestCase):
|
|||
@override_settings(DEV=True)
|
||||
def test_fx_firstrun_40_0(self, render_mock):
|
||||
"""Should use default firstrun template"""
|
||||
req = self.rf.get('/en-US/firefox/firstrun/')
|
||||
self.view(req, version='40.0')
|
||||
req = self.rf.get("/en-US/firefox/firstrun/")
|
||||
self.view(req, version="40.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/firstrun/firstrun.html']
|
||||
assert template == ["firefox/firstrun/firstrun.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_fx_firstrun_56_0(self, render_mock):
|
||||
"""Should use the default firstrun template"""
|
||||
req = self.rf.get('/en-US/firefox/firstrun/')
|
||||
self.view(req, version='56.0a2')
|
||||
req = self.rf.get("/en-US/firefox/firstrun/")
|
||||
self.view(req, version="56.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/firstrun/firstrun.html']
|
||||
assert template == ["firefox/firstrun/firstrun.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_fxdev_firstrun_57_0(self, render_mock):
|
||||
"""Should use 57 quantum dev edition firstrun template"""
|
||||
req = self.rf.get('/en-US/firefox/firstrun/')
|
||||
self.view(req, version='57.0a2')
|
||||
req = self.rf.get("/en-US/firefox/firstrun/")
|
||||
self.view(req, version="57.0a2")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/developer/firstrun.html']
|
||||
assert template == ["firefox/developer/firstrun.html"]
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_fx_firstrun_57_0(self, render_mock):
|
||||
"""Should use 57 quantum firstrun template"""
|
||||
req = self.rf.get('/en-US/firefox/firstrun/')
|
||||
self.view(req, version='57.0')
|
||||
req = self.rf.get("/en-US/firefox/firstrun/")
|
||||
self.view(req, version="57.0")
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/firstrun/firstrun.html']
|
||||
assert template == ["firefox/firstrun/firstrun.html"]
|
||||
|
||||
# test redirect to /firefox/new/ for legacy /firstrun URLs - Bug 1343823
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_fx_firstrun_legacy_redirect(self, render_mock):
|
||||
req = self.rf.get('/firefox/firstrun/')
|
||||
req.locale = 'en-US'
|
||||
resp = self.view(req, version='39.0')
|
||||
req = self.rf.get("/firefox/firstrun/")
|
||||
req.locale = "en-US"
|
||||
resp = self.view(req, version="39.0")
|
||||
assert resp.status_code == 301
|
||||
assert resp['location'].endswith('/firefox/new/')
|
||||
assert resp["location"].endswith("/firefox/new/")
|
||||
|
||||
def test_fx_firstrun_dev_edition_legacy_redirect(self, render_mock):
|
||||
req = self.rf.get('/firefox/firstrun/')
|
||||
req.locale = 'en-US'
|
||||
resp = self.view(req, version='39.0a2')
|
||||
req = self.rf.get("/firefox/firstrun/")
|
||||
req.locale = "en-US"
|
||||
resp = self.view(req, version="39.0a2")
|
||||
assert resp.status_code == 301
|
||||
assert resp['location'].endswith('/firefox/new/')
|
||||
assert resp["location"].endswith("/firefox/new/")
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -18,42 +18,39 @@ def render(s, context=None):
|
|||
|
||||
|
||||
class TestDownloadButtons(TestCase):
|
||||
|
||||
def latest_version(self):
|
||||
from product_details import product_details
|
||||
return product_details.firefox_versions['LATEST_FIREFOX_VERSION']
|
||||
|
||||
return product_details.firefox_versions["LATEST_FIREFOX_VERSION"]
|
||||
|
||||
def get_l10n(self, locale):
|
||||
return fluent_l10n([locale, 'en'], settings.FLUENT_DEFAULT_FILES)
|
||||
return fluent_l10n([locale, "en"], settings.FLUENT_DEFAULT_FILES)
|
||||
|
||||
def check_desktop_links(self, links):
|
||||
"""Desktop links should have the correct firefox version"""
|
||||
# valid product strings
|
||||
keys = [
|
||||
'firefox-%s' % self.latest_version(),
|
||||
'firefox-stub',
|
||||
'firefox-latest-ssl',
|
||||
'firefox-beta-stub',
|
||||
'firefox-beta-latest-ssl',
|
||||
"firefox-%s" % self.latest_version(),
|
||||
"firefox-stub",
|
||||
"firefox-latest-ssl",
|
||||
"firefox-beta-stub",
|
||||
"firefox-beta-latest-ssl",
|
||||
]
|
||||
|
||||
for link in links:
|
||||
url = pq(link).attr('href')
|
||||
url = pq(link).attr("href")
|
||||
assert any(key in url for key in keys)
|
||||
|
||||
def check_dumb_button(self, doc):
|
||||
# Make sure 5 links are present
|
||||
links = doc('li a')
|
||||
links = doc("li a")
|
||||
assert links.length == 5
|
||||
|
||||
self.check_desktop_links(links[:4])
|
||||
|
||||
# Check that the rest of the links are Android and iOS
|
||||
assert pq(links[4]).attr('href') == settings.GOOGLE_PLAY_FIREFOX_LINK_UTMS
|
||||
assert (
|
||||
pq(links[5]).attr('href') ==
|
||||
settings.APPLE_APPSTORE_FIREFOX_LINK.replace('/{country}/', '/')
|
||||
)
|
||||
assert pq(links[4]).attr("href") == settings.GOOGLE_PLAY_FIREFOX_LINK_UTMS
|
||||
assert pq(links[5]).attr("href") == settings.APPLE_APPSTORE_FIREFOX_LINK.replace("/{country}/", "/")
|
||||
|
||||
def test_button_force_direct(self):
|
||||
"""
|
||||
|
@ -61,85 +58,87 @@ class TestDownloadButtons(TestCase):
|
|||
directly to https://download.mozilla.org.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox(force_direct=true) }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(render("{{ download_firefox(force_direct=true) }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
# Check that the first 5 links are direct.
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links[:5]:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href.startswith('https://download.mozilla.org')
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)['lang'], ['fr'])
|
||||
href = link.attr("href")
|
||||
assert href.startswith("https://download.mozilla.org")
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)["lang"], ["fr"])
|
||||
# direct links should not have the data attr.
|
||||
assert link.attr('data-direct-link') is None
|
||||
assert link.attr("data-direct-link") is None
|
||||
|
||||
def test_button_locale_in_transition(self):
|
||||
"""
|
||||
If the locale_in_transition parameter is True, the link to scene2 should include the locale
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox(locale_in_transition=true) }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(
|
||||
render("{{ download_firefox(locale_in_transition=true) }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)})
|
||||
)
|
||||
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links[1:5]:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href == '/fr/firefox/download/thanks/'
|
||||
href = link.attr("href")
|
||||
assert href == "/fr/firefox/download/thanks/"
|
||||
|
||||
doc = pq(render("{{ download_firefox(locale_in_transition=false) }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
doc = pq(
|
||||
render("{{ download_firefox(locale_in_transition=false) }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)})
|
||||
)
|
||||
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links[1:5]:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href == '/firefox/download/thanks/'
|
||||
href = link.attr("href")
|
||||
assert href == "/firefox/download/thanks/"
|
||||
|
||||
def test_download_location_attribute(self):
|
||||
"""
|
||||
If the download_location parameter is set, it should be included as a data attribute.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox(download_location='primary cta') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(
|
||||
render(
|
||||
"{{ download_firefox(download_location='primary cta') }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}
|
||||
)
|
||||
)
|
||||
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links:
|
||||
link = pq(link)
|
||||
assert link.attr('data-download-location') == 'primary cta'
|
||||
assert link.attr("data-download-location") == "primary cta"
|
||||
|
||||
doc = pq(render("{{ download_firefox() }}", {'request': get_request,
|
||||
'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
doc = pq(render("{{ download_firefox() }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links[1:5]:
|
||||
link = pq(link)
|
||||
assert link.attr('data-download-location') is None
|
||||
assert link.attr("data-download-location") is None
|
||||
|
||||
def test_download_nosnippet_attribute(self):
|
||||
"""
|
||||
Unsupported messaging should be well formed <div>'s with data-nosnippet attribute (issue #8739).
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox() }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(render("{{ download_firefox() }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
unrecognised_message = doc('.unrecognized-download').empty().outerHtml()
|
||||
unrecognised_message = doc(".unrecognized-download").empty().outerHtml()
|
||||
assert unrecognised_message == '<div class="unrecognized-download" data-nosnippet="true"></div>'
|
||||
|
||||
def test_button_has_data_attr_if_not_direct(self):
|
||||
|
@ -148,21 +147,18 @@ class TestDownloadButtons(TestCase):
|
|||
`data-direct-link` attribute that contains the direct url.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox() }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(render("{{ download_firefox() }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
# The first 8 links should be for desktop.
|
||||
links = doc('.download-list a')
|
||||
links = doc(".download-list a")
|
||||
|
||||
for link in links[:8]:
|
||||
assert (
|
||||
pq(link).attr('data-direct-link')
|
||||
.startswith('https://download.mozilla.org'))
|
||||
assert pq(link).attr("data-direct-link").startswith("https://download.mozilla.org")
|
||||
|
||||
# The ninth link is mobile and should not have the attr
|
||||
assert pq(links[8]).attr('data-direct-link') is None
|
||||
assert pq(links[8]).attr("data-direct-link") is None
|
||||
|
||||
def test_nightly_desktop(self):
|
||||
"""
|
||||
|
@ -170,20 +166,23 @@ class TestDownloadButtons(TestCase):
|
|||
instead of the Windows 64-bit build
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox('nightly', platform='desktop') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(
|
||||
render(
|
||||
"{{ download_firefox('nightly', platform='desktop') }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}
|
||||
)
|
||||
)
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 7
|
||||
assert pq(list[0]).attr('class') == 'os_win'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[4]).attr('class') == 'os_osx'
|
||||
assert pq(list[5]).attr('class') == 'os_linux64'
|
||||
assert pq(list[6]).attr('class') == 'os_linux'
|
||||
assert pq(list[0]).attr("class") == "os_win"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_win-msi"
|
||||
assert pq(list[4]).attr("class") == "os_osx"
|
||||
assert pq(list[5]).attr("class") == "os_linux64"
|
||||
assert pq(list[6]).attr("class") == "os_linux"
|
||||
# stub disabled for now for non-en-US locales
|
||||
# bug 1339870
|
||||
# assert 'stub' in pq(pq(list[1]).find('a')[0]).attr('href')
|
||||
|
@ -191,198 +190,197 @@ class TestDownloadButtons(TestCase):
|
|||
def test_aurora_desktop(self):
|
||||
"""The Aurora channel should have Windows 64 build"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox('alpha', platform='desktop') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(
|
||||
render("{{ download_firefox('alpha', platform='desktop') }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)})
|
||||
)
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 8
|
||||
assert pq(list[0]).attr('class') == 'os_win64'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_win'
|
||||
assert pq(list[4]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[5]).attr('class') == 'os_osx'
|
||||
assert pq(list[6]).attr('class') == 'os_linux64'
|
||||
assert pq(list[7]).attr('class') == 'os_linux'
|
||||
assert pq(list[0]).attr("class") == "os_win64"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_win"
|
||||
assert pq(list[4]).attr("class") == "os_win-msi"
|
||||
assert pq(list[5]).attr("class") == "os_osx"
|
||||
assert pq(list[6]).attr("class") == "os_linux64"
|
||||
assert pq(list[7]).attr("class") == "os_linux"
|
||||
|
||||
def test_beta_desktop(self):
|
||||
"""The Beta channel should not have Windows 64 build yet"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox('beta', platform='desktop') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(
|
||||
render("{{ download_firefox('beta', platform='desktop') }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)})
|
||||
)
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 8
|
||||
assert pq(list[0]).attr('class') == 'os_win64'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_win'
|
||||
assert pq(list[4]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[5]).attr('class') == 'os_osx'
|
||||
assert pq(list[6]).attr('class') == 'os_linux64'
|
||||
assert pq(list[7]).attr('class') == 'os_linux'
|
||||
assert pq(list[0]).attr("class") == "os_win64"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_win"
|
||||
assert pq(list[4]).attr("class") == "os_win-msi"
|
||||
assert pq(list[5]).attr("class") == "os_osx"
|
||||
assert pq(list[6]).attr("class") == "os_linux64"
|
||||
assert pq(list[7]).attr("class") == "os_linux"
|
||||
|
||||
def test_firefox_desktop(self):
|
||||
"""The Release channel should not have Windows 64 build yet"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'fr'
|
||||
doc = pq(render("{{ download_firefox(platform='desktop') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "fr"
|
||||
doc = pq(render("{{ download_firefox(platform='desktop') }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 8
|
||||
assert pq(list[0]).attr('class') == 'os_win64'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_win'
|
||||
assert pq(list[4]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[5]).attr('class') == 'os_osx'
|
||||
assert pq(list[6]).attr('class') == 'os_linux64'
|
||||
assert pq(list[7]).attr('class') == 'os_linux'
|
||||
assert pq(list[0]).attr("class") == "os_win64"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_win"
|
||||
assert pq(list[4]).attr("class") == "os_win-msi"
|
||||
assert pq(list[5]).attr("class") == "os_osx"
|
||||
assert pq(list[6]).attr("class") == "os_linux64"
|
||||
assert pq(list[7]).attr("class") == "os_linux"
|
||||
|
||||
def test_latest_nightly_android(self):
|
||||
"""The download button should have a Google Play link"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
doc = pq(render("{{ download_firefox('nightly', platform='android') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n('fr')}))
|
||||
get_request = rf.get("/fake")
|
||||
doc = pq(render("{{ download_firefox('nightly', platform='android') }}", {"request": get_request, "fluent_l10n": self.get_l10n("fr")}))
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 1
|
||||
assert pq(list[0]).attr('class') == 'os_android'
|
||||
assert pq(list[0]).attr("class") == "os_android"
|
||||
|
||||
links = doc('.download-list li a')
|
||||
links = doc(".download-list li a")
|
||||
assert links.length == 1
|
||||
assert pq(links[0]).attr('href').startswith('https://play.google.com')
|
||||
assert pq(links[0]).attr("href").startswith("https://play.google.com")
|
||||
|
||||
list = doc('.download-other .arch')
|
||||
list = doc(".download-other .arch")
|
||||
assert list.length == 0
|
||||
|
||||
def test_beta_mobile(self):
|
||||
"""The download button should have a Google Play link"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
doc = pq(render("{{ download_firefox('beta', platform='android') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n('fr')}))
|
||||
get_request = rf.get("/fake")
|
||||
doc = pq(render("{{ download_firefox('beta', platform='android') }}", {"request": get_request, "fluent_l10n": self.get_l10n("fr")}))
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 1
|
||||
assert pq(list[0]).attr('class') == 'os_android'
|
||||
assert pq(list[0]).attr("class") == "os_android"
|
||||
|
||||
links = doc('.download-list li a')
|
||||
links = doc(".download-list li a")
|
||||
assert links.length == 1
|
||||
assert pq(links[0]).attr('href').startswith('https://play.google.com')
|
||||
assert pq(links[0]).attr("href").startswith("https://play.google.com")
|
||||
|
||||
list = doc('.download-other .arch')
|
||||
list = doc(".download-other .arch")
|
||||
assert list.length == 0
|
||||
|
||||
def test_firefox_mobile(self):
|
||||
"""The download button should have a Google Play link"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
doc = pq(render("{{ download_firefox(platform='android') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n('fr')}))
|
||||
get_request = rf.get("/fake")
|
||||
doc = pq(render("{{ download_firefox(platform='android') }}", {"request": get_request, "fluent_l10n": self.get_l10n("fr")}))
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 1
|
||||
assert pq(list[0]).attr('class') == 'os_android'
|
||||
assert pq(list[0]).attr("class") == "os_android"
|
||||
|
||||
links = doc('.download-list li a')
|
||||
links = doc(".download-list li a")
|
||||
assert links.length == 1
|
||||
assert pq(links[0]).attr('href').startswith('https://play.google.com')
|
||||
assert pq(links[0]).attr("href").startswith("https://play.google.com")
|
||||
|
||||
list = doc('.download-other .arch')
|
||||
list = doc(".download-other .arch")
|
||||
assert list.length == 0
|
||||
|
||||
def test_ios(self):
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
doc = pq(render("{{ download_firefox(platform='ios') }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n('fr')}))
|
||||
get_request = rf.get("/fake")
|
||||
doc = pq(render("{{ download_firefox(platform='ios') }}", {"request": get_request, "fluent_l10n": self.get_l10n("fr")}))
|
||||
|
||||
list = doc('.download-list li')
|
||||
list = doc(".download-list li")
|
||||
assert list.length == 1
|
||||
assert pq(list[0]).attr('class') == 'os_ios'
|
||||
assert pq(list[0]).attr("class") == "os_ios"
|
||||
|
||||
|
||||
class TestDownloadThanksButton(TestCase):
|
||||
|
||||
def get_l10n(self, locale):
|
||||
return fluent_l10n([locale, 'en'], settings.FLUENT_DEFAULT_FILES)
|
||||
return fluent_l10n([locale, "en"], settings.FLUENT_DEFAULT_FILES)
|
||||
|
||||
def test_download_firefox_thanks_button(self):
|
||||
"""
|
||||
Download link should point to /firefox/download/thanks/
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'en-US'
|
||||
doc = pq(render("{{ download_firefox_thanks() }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "en-US"
|
||||
doc = pq(render("{{ download_firefox_thanks() }}", {"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)}))
|
||||
|
||||
button = doc('.c-button-download-thanks')
|
||||
links = doc('.c-button-download-thanks > .download-link')
|
||||
button = doc(".c-button-download-thanks")
|
||||
links = doc(".c-button-download-thanks > .download-link")
|
||||
assert links.length == 1
|
||||
|
||||
link = pq(links)
|
||||
href = link.attr('href')
|
||||
href = link.attr("href")
|
||||
|
||||
assert href == '/firefox/download/thanks/'
|
||||
assert button.attr('id') == 'download-button-thanks'
|
||||
assert link.attr('data-link-type') == 'download'
|
||||
assert href == "/firefox/download/thanks/"
|
||||
assert button.attr("id") == "download-button-thanks"
|
||||
assert link.attr("data-link-type") == "download"
|
||||
|
||||
# Direct attribute for legacy IE browsers should always be win 32bit
|
||||
assert link.attr('data-direct-link') == 'https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US'
|
||||
assert link.attr("data-direct-link") == "https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US"
|
||||
|
||||
def test_download_firefox_thanks_attributes(self):
|
||||
"""
|
||||
Download link should support custom attributes
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'en-US'
|
||||
doc = pq(render("{{ download_firefox_thanks(dom_id='test-download', button_class='test-css-class', "
|
||||
"download_location='primary cta', locale_in_transition=True) }}",
|
||||
{'request': get_request, 'fluent_l10n': self.get_l10n(get_request.locale)}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "en-US"
|
||||
doc = pq(
|
||||
render(
|
||||
"{{ download_firefox_thanks(dom_id='test-download', button_class='test-css-class', "
|
||||
"download_location='primary cta', locale_in_transition=True) }}",
|
||||
{"request": get_request, "fluent_l10n": self.get_l10n(get_request.locale)},
|
||||
)
|
||||
)
|
||||
|
||||
button = doc('.c-button-download-thanks')
|
||||
links = doc('.c-button-download-thanks > .download-link')
|
||||
button = doc(".c-button-download-thanks")
|
||||
links = doc(".c-button-download-thanks > .download-link")
|
||||
assert links.length == 1
|
||||
|
||||
link = pq(links)
|
||||
href = link.attr('href')
|
||||
href = link.attr("href")
|
||||
|
||||
assert href == '/en-US/firefox/download/thanks/'
|
||||
assert button.attr('id') == 'test-download'
|
||||
assert link.attr('data-download-location') == 'primary cta'
|
||||
assert 'test-css-class' in link.attr('class')
|
||||
assert href == "/en-US/firefox/download/thanks/"
|
||||
assert button.attr("id") == "test-download"
|
||||
assert link.attr("data-download-location") == "primary cta"
|
||||
assert "test-css-class" in link.attr("class")
|
||||
|
||||
|
||||
class TestDownloadList(TestCase):
|
||||
|
||||
def latest_version(self):
|
||||
from product_details import product_details
|
||||
return product_details.firefox_versions['LATEST_FIREFOX_VERSION']
|
||||
|
||||
return product_details.firefox_versions["LATEST_FIREFOX_VERSION"]
|
||||
|
||||
def check_desktop_links(self, links):
|
||||
"""Desktop links should have the correct firefox version"""
|
||||
# valid product strings
|
||||
keys = [
|
||||
'firefox-%s' % self.latest_version(),
|
||||
'firefox-stub',
|
||||
'firefox-latest-ssl',
|
||||
'firefox-msi-latest-ssl',
|
||||
'firefox-beta-stub',
|
||||
'firefox-beta-latest-ssl',
|
||||
"firefox-%s" % self.latest_version(),
|
||||
"firefox-stub",
|
||||
"firefox-latest-ssl",
|
||||
"firefox-msi-latest-ssl",
|
||||
"firefox-beta-stub",
|
||||
"firefox-beta-latest-ssl",
|
||||
]
|
||||
|
||||
for link in links:
|
||||
url = pq(link).attr('href')
|
||||
url = pq(link).attr("href")
|
||||
assert any(key in url for key in keys)
|
||||
|
||||
def test_firefox_desktop_list_release(self):
|
||||
|
@ -390,153 +388,138 @@ class TestDownloadList(TestCase):
|
|||
All release download links must be directly to https://download.mozilla.org.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'en-US'
|
||||
doc = pq(render("{{ download_firefox_desktop_list() }}",
|
||||
{'request': get_request}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "en-US"
|
||||
doc = pq(render("{{ download_firefox_desktop_list() }}", {"request": get_request}))
|
||||
|
||||
# Check that links classes are ordered as expected.
|
||||
list = doc('.download-platform-list li')
|
||||
list = doc(".download-platform-list li")
|
||||
assert list.length == 8
|
||||
assert pq(list[0]).attr('class') == 'os_win64'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_osx'
|
||||
assert pq(list[4]).attr('class') == 'os_linux64'
|
||||
assert pq(list[5]).attr('class') == 'os_linux'
|
||||
assert pq(list[6]).attr('class') == 'os_win'
|
||||
assert pq(list[7]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[0]).attr("class") == "os_win64"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_osx"
|
||||
assert pq(list[4]).attr("class") == "os_linux64"
|
||||
assert pq(list[5]).attr("class") == "os_linux"
|
||||
assert pq(list[6]).attr("class") == "os_win"
|
||||
assert pq(list[7]).attr("class") == "os_win-msi"
|
||||
|
||||
links = doc('.download-platform-list a')
|
||||
links = doc(".download-platform-list a")
|
||||
|
||||
# Check desktop links have the correct version
|
||||
self.check_desktop_links(links)
|
||||
|
||||
for link in links:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href.startswith('https://download.mozilla.org')
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)['lang'], ['en-US'])
|
||||
href = link.attr("href")
|
||||
assert href.startswith("https://download.mozilla.org")
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)["lang"], ["en-US"])
|
||||
|
||||
def test_firefox_desktop_list_aurora(self):
|
||||
"""
|
||||
All aurora download links must be directly to https://download.mozilla.org.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'en-US'
|
||||
doc = pq(render("{{ download_firefox_desktop_list(channel='alpha') }}",
|
||||
{'request': get_request}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "en-US"
|
||||
doc = pq(render("{{ download_firefox_desktop_list(channel='alpha') }}", {"request": get_request}))
|
||||
|
||||
# Check that links classes are ordered as expected.
|
||||
list = doc('.download-platform-list li')
|
||||
list = doc(".download-platform-list li")
|
||||
assert list.length == 8
|
||||
assert pq(list[0]).attr('class') == 'os_win64'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_osx'
|
||||
assert pq(list[4]).attr('class') == 'os_linux64'
|
||||
assert pq(list[5]).attr('class') == 'os_linux'
|
||||
assert pq(list[6]).attr('class') == 'os_win'
|
||||
assert pq(list[7]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[0]).attr("class") == "os_win64"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_osx"
|
||||
assert pq(list[4]).attr("class") == "os_linux64"
|
||||
assert pq(list[5]).attr("class") == "os_linux"
|
||||
assert pq(list[6]).attr("class") == "os_win"
|
||||
assert pq(list[7]).attr("class") == "os_win-msi"
|
||||
|
||||
links = doc('.download-platform-list a')
|
||||
links = doc(".download-platform-list a")
|
||||
|
||||
for link in links:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href.startswith('https://download.mozilla.org')
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)['lang'], ['en-US'])
|
||||
href = link.attr("href")
|
||||
assert href.startswith("https://download.mozilla.org")
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)["lang"], ["en-US"])
|
||||
|
||||
def test_firefox_desktop_list_nightly(self):
|
||||
"""
|
||||
All nightly download links must be directly to https://download.mozilla.org.
|
||||
"""
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/fake')
|
||||
get_request.locale = 'en-US'
|
||||
doc = pq(render("{{ download_firefox_desktop_list(channel='nightly') }}",
|
||||
{'request': get_request}))
|
||||
get_request = rf.get("/fake")
|
||||
get_request.locale = "en-US"
|
||||
doc = pq(render("{{ download_firefox_desktop_list(channel='nightly') }}", {"request": get_request}))
|
||||
|
||||
# Check that links classes are ordered as expected.
|
||||
list = doc('.download-platform-list li')
|
||||
list = doc(".download-platform-list li")
|
||||
assert list.length == 7
|
||||
assert pq(list[0]).attr('class') == 'os_win'
|
||||
assert pq(list[1]).attr('class') == 'os_win64-msi'
|
||||
assert pq(list[2]).attr('class') == 'os_win64-aarch64'
|
||||
assert pq(list[3]).attr('class') == 'os_osx'
|
||||
assert pq(list[4]).attr('class') == 'os_linux64'
|
||||
assert pq(list[5]).attr('class') == 'os_linux'
|
||||
assert pq(list[6]).attr('class') == 'os_win-msi'
|
||||
assert pq(list[0]).attr("class") == "os_win"
|
||||
assert pq(list[1]).attr("class") == "os_win64-msi"
|
||||
assert pq(list[2]).attr("class") == "os_win64-aarch64"
|
||||
assert pq(list[3]).attr("class") == "os_osx"
|
||||
assert pq(list[4]).attr("class") == "os_linux64"
|
||||
assert pq(list[5]).attr("class") == "os_linux"
|
||||
assert pq(list[6]).attr("class") == "os_win-msi"
|
||||
|
||||
links = doc('.download-platform-list a')
|
||||
links = doc(".download-platform-list a")
|
||||
|
||||
for link in links:
|
||||
link = pq(link)
|
||||
href = link.attr('href')
|
||||
assert href.startswith('https://download.mozilla.org')
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)['lang'], ['en-US'])
|
||||
href = link.attr("href")
|
||||
assert href.startswith("https://download.mozilla.org")
|
||||
self.assertListEqual(parse_qs(urlparse(href).query)["lang"], ["en-US"])
|
||||
|
||||
|
||||
class TestFirefoxURL(TestCase):
|
||||
rf = RequestFactory()
|
||||
|
||||
def _render(self, platform, page, channel=None):
|
||||
req = self.rf.get('/')
|
||||
req = self.rf.get("/")
|
||||
if channel:
|
||||
tmpl = "{{ firefox_url('%s', '%s', '%s') }}" % (platform, page, channel)
|
||||
else:
|
||||
tmpl = "{{ firefox_url('%s', '%s') }}" % (platform, page)
|
||||
return render(tmpl, {'request': req})
|
||||
return render(tmpl, {"request": req})
|
||||
|
||||
def test_firefox_all(self):
|
||||
"""Should return a reversed path for the Firefox all downloads page"""
|
||||
assert self._render('desktop', 'all').endswith('/firefox/all/#product-desktop-release')
|
||||
assert self._render('desktop', 'all', 'release').endswith('/firefox/all/#product-desktop-release')
|
||||
assert self._render('desktop', 'all', 'beta').endswith('/firefox/all/#product-desktop-beta')
|
||||
assert self._render('desktop', 'all', 'alpha').endswith('/firefox/all/#product-desktop-developer')
|
||||
assert self._render('desktop', 'all', 'developer').endswith('/firefox/all/#product-desktop-developer')
|
||||
assert self._render('desktop', 'all', 'nightly').endswith('/firefox/all/#product-desktop-nightly')
|
||||
assert self._render('desktop', 'all', 'esr').endswith('/firefox/all/#product-desktop-esr')
|
||||
assert self._render('desktop', 'all', 'organizations').endswith('/firefox/all/#product-desktop-esr')
|
||||
assert self._render('android', 'all').endswith('/firefox/all/#product-android-release')
|
||||
assert self._render('android', 'all', 'release').endswith('/firefox/all/#product-android-release')
|
||||
assert self._render('android', 'all', 'beta').endswith('/firefox/all/#product-android-beta')
|
||||
assert self._render('android', 'all', 'nightly').endswith('/firefox/all/#product-android-nightly')
|
||||
assert self._render("desktop", "all").endswith("/firefox/all/#product-desktop-release")
|
||||
assert self._render("desktop", "all", "release").endswith("/firefox/all/#product-desktop-release")
|
||||
assert self._render("desktop", "all", "beta").endswith("/firefox/all/#product-desktop-beta")
|
||||
assert self._render("desktop", "all", "alpha").endswith("/firefox/all/#product-desktop-developer")
|
||||
assert self._render("desktop", "all", "developer").endswith("/firefox/all/#product-desktop-developer")
|
||||
assert self._render("desktop", "all", "nightly").endswith("/firefox/all/#product-desktop-nightly")
|
||||
assert self._render("desktop", "all", "esr").endswith("/firefox/all/#product-desktop-esr")
|
||||
assert self._render("desktop", "all", "organizations").endswith("/firefox/all/#product-desktop-esr")
|
||||
assert self._render("android", "all").endswith("/firefox/all/#product-android-release")
|
||||
assert self._render("android", "all", "release").endswith("/firefox/all/#product-android-release")
|
||||
assert self._render("android", "all", "beta").endswith("/firefox/all/#product-android-beta")
|
||||
assert self._render("android", "all", "nightly").endswith("/firefox/all/#product-android-nightly")
|
||||
|
||||
def test_firefox_sysreq(self):
|
||||
"""Should return a reversed path for the Firefox sysreq page"""
|
||||
assert self._render('desktop', 'sysreq').endswith('/firefox/system-requirements/')
|
||||
assert (
|
||||
self._render('desktop', 'sysreq', 'release')
|
||||
.endswith('/firefox/system-requirements/'))
|
||||
assert (
|
||||
self._render('desktop', 'sysreq', 'beta')
|
||||
.endswith('/firefox/beta/system-requirements/'))
|
||||
assert (
|
||||
self._render('desktop', 'sysreq', 'alpha')
|
||||
.endswith('/firefox/developer/system-requirements/'))
|
||||
assert (
|
||||
self._render('desktop', 'sysreq', 'esr')
|
||||
.endswith('/firefox/organizations/system-requirements/'))
|
||||
assert (
|
||||
self._render('desktop', 'sysreq', 'organizations')
|
||||
.endswith('/firefox/organizations/system-requirements/'))
|
||||
assert self._render("desktop", "sysreq").endswith("/firefox/system-requirements/")
|
||||
assert self._render("desktop", "sysreq", "release").endswith("/firefox/system-requirements/")
|
||||
assert self._render("desktop", "sysreq", "beta").endswith("/firefox/beta/system-requirements/")
|
||||
assert self._render("desktop", "sysreq", "alpha").endswith("/firefox/developer/system-requirements/")
|
||||
assert self._render("desktop", "sysreq", "esr").endswith("/firefox/organizations/system-requirements/")
|
||||
assert self._render("desktop", "sysreq", "organizations").endswith("/firefox/organizations/system-requirements/")
|
||||
|
||||
def test_desktop_notes(self):
|
||||
"""Should return a reversed path for the desktop notes page"""
|
||||
assert self._render('desktop', 'notes').endswith('/firefox/notes/')
|
||||
assert self._render('desktop', 'notes', 'release').endswith('/firefox/notes/')
|
||||
assert self._render('desktop', 'notes', 'beta').endswith('/firefox/beta/notes/')
|
||||
assert self._render('desktop', 'notes', 'alpha').endswith('/firefox/developer/notes/')
|
||||
assert self._render('desktop', 'notes', 'esr').endswith('/firefox/organizations/notes/')
|
||||
assert (
|
||||
self._render('desktop', 'notes', 'organizations')
|
||||
.endswith('/firefox/organizations/notes/'))
|
||||
assert self._render("desktop", "notes").endswith("/firefox/notes/")
|
||||
assert self._render("desktop", "notes", "release").endswith("/firefox/notes/")
|
||||
assert self._render("desktop", "notes", "beta").endswith("/firefox/beta/notes/")
|
||||
assert self._render("desktop", "notes", "alpha").endswith("/firefox/developer/notes/")
|
||||
assert self._render("desktop", "notes", "esr").endswith("/firefox/organizations/notes/")
|
||||
assert self._render("desktop", "notes", "organizations").endswith("/firefox/organizations/notes/")
|
||||
|
||||
def test_android_notes(self):
|
||||
"""Should return a reversed path for the Android notes page"""
|
||||
assert self._render('android', 'notes').endswith('/firefox/android/notes/')
|
||||
assert self._render('android', 'notes', 'release').endswith('/firefox/android/notes/')
|
||||
assert self._render('android', 'notes', 'beta').endswith('/firefox/android/beta/notes/')
|
||||
assert self._render('android', 'notes', 'alpha').endswith('/firefox/android/aurora/notes/')
|
||||
assert self._render("android", "notes").endswith("/firefox/android/notes/")
|
||||
assert self._render("android", "notes", "release").endswith("/firefox/android/notes/")
|
||||
assert self._render("android", "notes", "beta").endswith("/firefox/android/beta/notes/")
|
||||
assert self._render("android", "notes", "alpha").endswith("/firefox/android/aurora/notes/")
|
||||
|
|
|
@ -20,7 +20,7 @@ from bedrock.mozorg.tests import TestCase
|
|||
|
||||
|
||||
@override_settings(
|
||||
STUB_ATTRIBUTION_HMAC_KEY='achievers',
|
||||
STUB_ATTRIBUTION_HMAC_KEY="achievers",
|
||||
STUB_ATTRIBUTION_RATE=1,
|
||||
STUB_ATTRIBUTION_MAX_LEN=600,
|
||||
)
|
||||
|
@ -28,268 +28,254 @@ class TestStubAttributionCode(TestCase):
|
|||
def _get_request(self, params):
|
||||
rf = RequestFactory()
|
||||
return rf.get(
|
||||
'/',
|
||||
"/",
|
||||
params,
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_ACCEPT='application/json',
|
||||
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||
HTTP_ACCEPT="application/json",
|
||||
)
|
||||
|
||||
def test_not_ajax_request(self):
|
||||
req = RequestFactory().get('/', {'source': 'malibu'})
|
||||
req = RequestFactory().get("/", {"source": "malibu"})
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
assert 'cache-control' not in resp
|
||||
assert "cache-control" not in resp
|
||||
data = json.loads(resp.content)
|
||||
self.assertEqual(data['error'], 'Resource only available via XHR')
|
||||
self.assertEqual(data["error"], "Resource only available via XHR")
|
||||
|
||||
def test_no_valid_param_names(self):
|
||||
final_params = {
|
||||
'source': 'www.mozilla.org',
|
||||
'medium': '(none)',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "www.mozilla.org",
|
||||
"medium": "(none)",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request({'dude': 'abides'})
|
||||
req = self._get_request({"dude": "abides"})
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22',
|
||||
data["attribution_sig"],
|
||||
"135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22",
|
||||
)
|
||||
|
||||
def test_no_valid_param_data(self):
|
||||
params = {
|
||||
'utm_source': 'br@ndt',
|
||||
'utm_medium': 'ae<t>her',
|
||||
'experiment': 'dfb</p>s',
|
||||
'variation': 'ef&bvcv',
|
||||
'visit_id': '14</p>4538.1610<t>957',
|
||||
"utm_source": "br@ndt",
|
||||
"utm_medium": "ae<t>her",
|
||||
"experiment": "dfb</p>s",
|
||||
"variation": "ef&bvcv",
|
||||
"visit_id": "14</p>4538.1610<t>957",
|
||||
}
|
||||
final_params = {
|
||||
'source': 'www.mozilla.org',
|
||||
'medium': '(none)',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "www.mozilla.org",
|
||||
"medium": "(none)",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22',
|
||||
data["attribution_sig"],
|
||||
"135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22",
|
||||
)
|
||||
|
||||
def test_some_valid_param_data(self):
|
||||
params = {'utm_source': 'brandt', 'utm_content': 'ae<t>her'}
|
||||
params = {"utm_source": "brandt", "utm_content": "ae<t>her"}
|
||||
final_params = {
|
||||
'source': 'brandt',
|
||||
'medium': '(direct)',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "brandt",
|
||||
"medium": "(direct)",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'b53097f17741b75cdd5b737d3c8ba03349a6093148adeada2ee69adf4fe87322',
|
||||
data["attribution_sig"],
|
||||
"b53097f17741b75cdd5b737d3c8ba03349a6093148adeada2ee69adf4fe87322",
|
||||
)
|
||||
|
||||
def test_campaign_data_too_long(self):
|
||||
"""If the code is too long then the utm_campaign value should be truncated"""
|
||||
params = {
|
||||
'utm_source': 'brandt',
|
||||
'utm_medium': 'aether',
|
||||
'utm_content': 'A144_A000_0000000',
|
||||
'utm_campaign': 'The%7cDude%7cabides%7cI%7cdont%7cknow%7cabout%7cyou%7c'
|
||||
'but%7cI%7ctake%7ccomfort%7cin%7cthat' * 6,
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': 'chrome',
|
||||
'visit_id': '1456954538.1610960957',
|
||||
"utm_source": "brandt",
|
||||
"utm_medium": "aether",
|
||||
"utm_content": "A144_A000_0000000",
|
||||
"utm_campaign": "The%7cDude%7cabides%7cI%7cdont%7cknow%7cabout%7cyou%7cbut%7cI%7ctake%7ccomfort%7cin%7cthat" * 6,
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "chrome",
|
||||
"visit_id": "1456954538.1610960957",
|
||||
}
|
||||
final_params = {
|
||||
'source': 'brandt',
|
||||
'medium': 'aether',
|
||||
'campaign': 'The|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in'
|
||||
'|thatThe|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in|thatThe'
|
||||
'|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in|thatThe|Dude|abides'
|
||||
'|I|dont|know|about|you|but|I|take|comfort|in|thatThe|Dude|abides|I|dont|know'
|
||||
'|about|you|but|I|take|comfort|in|thatT_',
|
||||
'content': 'A144_A000_0000000',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': 'chrome',
|
||||
'visit_id': '1456954538.1610960957',
|
||||
"source": "brandt",
|
||||
"medium": "aether",
|
||||
"campaign": "The|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in"
|
||||
"|thatThe|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in|thatThe"
|
||||
"|Dude|abides|I|dont|know|about|you|but|I|take|comfort|in|thatThe|Dude|abides"
|
||||
"|I|dont|know|about|you|but|I|take|comfort|in|thatThe|Dude|abides|I|dont|know"
|
||||
"|about|you|but|I|take|comfort|in|thatT_",
|
||||
"content": "A144_A000_0000000",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "chrome",
|
||||
"visit_id": "1456954538.1610960957",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
code = querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
code = querystringsafe_base64.decode(data["attribution_code"].encode()).decode()
|
||||
assert len(code) <= 600
|
||||
attrs = parse_qs(code)
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'3c1611db912c51a96418eb7806fbaf1400b8d05fbf6ee4f2f1fb3c0ba74a89f4',
|
||||
data["attribution_sig"],
|
||||
"3c1611db912c51a96418eb7806fbaf1400b8d05fbf6ee4f2f1fb3c0ba74a89f4",
|
||||
)
|
||||
|
||||
def test_other_data_too_long_not_campaign(self):
|
||||
"""If the code is too long but not utm_campaign return error"""
|
||||
params = {
|
||||
'utm_source': 'brandt',
|
||||
'utm_campaign': 'dude',
|
||||
'utm_content': 'A144_A000_0000000',
|
||||
'utm_medium': 'The%7cDude%7cabides%7cI%7cdont%7cknow%7cabout%7cyou%7c'
|
||||
'but%7cI%7ctake%7ccomfort%7cin%7cthat' * 6,
|
||||
"utm_source": "brandt",
|
||||
"utm_campaign": "dude",
|
||||
"utm_content": "A144_A000_0000000",
|
||||
"utm_medium": "The%7cDude%7cabides%7cI%7cdont%7cknow%7cabout%7cyou%7cbut%7cI%7ctake%7ccomfort%7cin%7cthat" * 6,
|
||||
}
|
||||
final_params = {'error': 'Invalid code'}
|
||||
final_params = {"error": "Invalid code"}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
self.assertDictEqual(data, final_params)
|
||||
|
||||
def test_returns_valid_data(self):
|
||||
params = {
|
||||
'utm_source': 'brandt',
|
||||
'utm_medium': 'aether',
|
||||
'experiment': 'firefox-new',
|
||||
'variation': '1',
|
||||
'ua': 'chrome',
|
||||
'visit_id': '1456954538.1610960957',
|
||||
"utm_source": "brandt",
|
||||
"utm_medium": "aether",
|
||||
"experiment": "firefox-new",
|
||||
"variation": "1",
|
||||
"ua": "chrome",
|
||||
"visit_id": "1456954538.1610960957",
|
||||
}
|
||||
final_params = {
|
||||
'source': 'brandt',
|
||||
'medium': 'aether',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': 'firefox-new',
|
||||
'variation': '1',
|
||||
'ua': 'chrome',
|
||||
'visit_id': '1456954538.1610960957'
|
||||
"source": "brandt",
|
||||
"medium": "aether",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "firefox-new",
|
||||
"variation": "1",
|
||||
"ua": "chrome",
|
||||
"visit_id": "1456954538.1610960957",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'b2dc555b2914fdec9f9a1247d244520392e4f888961a6fb57a74a1cdf041261f',
|
||||
data["attribution_sig"],
|
||||
"b2dc555b2914fdec9f9a1247d244520392e4f888961a6fb57a74a1cdf041261f",
|
||||
)
|
||||
|
||||
def test_handles_referrer(self):
|
||||
params = {'utm_source': 'brandt', 'referrer': 'https://duckduckgo.com/privacy'}
|
||||
params = {"utm_source": "brandt", "referrer": "https://duckduckgo.com/privacy"}
|
||||
final_params = {
|
||||
'source': 'brandt',
|
||||
'medium': '(direct)',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "brandt",
|
||||
"medium": "(direct)",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'b53097f17741b75cdd5b737d3c8ba03349a6093148adeada2ee69adf4fe87322',
|
||||
data["attribution_sig"],
|
||||
"b53097f17741b75cdd5b737d3c8ba03349a6093148adeada2ee69adf4fe87322",
|
||||
)
|
||||
|
||||
def test_handles_referrer_no_source(self):
|
||||
params = {
|
||||
'referrer': 'https://example.com:5000/searchin',
|
||||
'utm_medium': 'aether',
|
||||
"referrer": "https://example.com:5000/searchin",
|
||||
"utm_medium": "aether",
|
||||
}
|
||||
final_params = {
|
||||
'source': 'example.com:5000',
|
||||
'medium': 'referral',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "example.com:5000",
|
||||
"medium": "referral",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'd075cbcbae3bcef5bda3650a259863151586e3a4709d53886ab3cc83a6963d00',
|
||||
data["attribution_sig"],
|
||||
"d075cbcbae3bcef5bda3650a259863151586e3a4709d53886ab3cc83a6963d00",
|
||||
)
|
||||
|
||||
def test_handles_referrer_utf8(self):
|
||||
|
@ -299,327 +285,321 @@ class TestStubAttributionCode(TestCase):
|
|||
non-ascii domain names in the referrer. The allowed list for bouncer
|
||||
doesn't include any such domains anyway, so we should just ignore them.
|
||||
"""
|
||||
params = {'referrer': 'http://youtubê.com/sorry/'}
|
||||
params = {"referrer": "http://youtubê.com/sorry/"}
|
||||
final_params = {
|
||||
'source': 'www.mozilla.org',
|
||||
'medium': '(none)',
|
||||
'campaign': '(not set)',
|
||||
'content': '(not set)',
|
||||
'experiment': '(not set)',
|
||||
'variation': '(not set)',
|
||||
'ua': '(not set)',
|
||||
'visit_id': '(not set)',
|
||||
"source": "www.mozilla.org",
|
||||
"medium": "(none)",
|
||||
"campaign": "(not set)",
|
||||
"content": "(not set)",
|
||||
"experiment": "(not set)",
|
||||
"variation": "(not set)",
|
||||
"ua": "(not set)",
|
||||
"visit_id": "(not set)",
|
||||
}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
data = json.loads(resp.content)
|
||||
# will it blend?
|
||||
attrs = parse_qs(
|
||||
querystringsafe_base64.decode(data['attribution_code'].encode()).decode()
|
||||
)
|
||||
attrs = parse_qs(querystringsafe_base64.decode(data["attribution_code"].encode()).decode())
|
||||
# parse_qs returns a dict with lists for values
|
||||
attrs = {k: v[0] for k, v in attrs.items()}
|
||||
self.assertDictEqual(attrs, final_params)
|
||||
self.assertEqual(
|
||||
data['attribution_sig'],
|
||||
'135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22',
|
||||
data["attribution_sig"],
|
||||
"135b2245f6b70978bc8142a91521facdb31d70a1bfbdefdc1bd1dee92ce21a22",
|
||||
)
|
||||
|
||||
@override_settings(STUB_ATTRIBUTION_RATE=0.2)
|
||||
def test_rate_limit(self):
|
||||
params = {'utm_source': 'brandt', 'utm_medium': 'aether'}
|
||||
params = {"utm_source": "brandt", "utm_medium": "aether"}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
|
||||
@override_settings(STUB_ATTRIBUTION_RATE=0)
|
||||
def test_rate_limit_disabled(self):
|
||||
params = {'utm_source': 'brandt', 'utm_medium': 'aether'}
|
||||
params = {"utm_source": "brandt", "utm_medium": "aether"}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 429)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
|
||||
@override_settings(STUB_ATTRIBUTION_HMAC_KEY='')
|
||||
@override_settings(STUB_ATTRIBUTION_HMAC_KEY="")
|
||||
def test_no_hmac_key_set(self):
|
||||
params = {'utm_source': 'brandt', 'utm_medium': 'aether'}
|
||||
params = {"utm_source": "brandt", "utm_medium": "aether"}
|
||||
req = self._get_request(params)
|
||||
resp = views.stub_attribution_code(req)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
assert resp['cache-control'] == 'max-age=300'
|
||||
assert resp["cache-control"] == "max-age=300"
|
||||
|
||||
|
||||
class TestSendToDeviceView(TestCase):
|
||||
def setUp(self):
|
||||
patcher = patch('bedrock.firefox.views.basket.subscribe')
|
||||
patcher = patch("bedrock.firefox.views.basket.subscribe")
|
||||
self.mock_subscribe = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
patcher = patch('bedrock.firefox.views.basket.request')
|
||||
patcher = patch("bedrock.firefox.views.basket.request")
|
||||
self.mock_send_sms = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def _request(self, data, expected_status=200, locale='en-US'):
|
||||
req = RequestFactory().post('/', data)
|
||||
def _request(self, data, expected_status=200, locale="en-US"):
|
||||
req = RequestFactory().post("/", data)
|
||||
req.locale = locale
|
||||
resp = views.send_to_device_ajax(req)
|
||||
assert resp.status_code == expected_status
|
||||
return json.loads(resp.content)
|
||||
|
||||
def test_phone_or_email_required(self):
|
||||
resp_data = self._request({'platform': 'android'})
|
||||
assert not resp_data['success']
|
||||
assert 's2d-email' in resp_data['errors']
|
||||
resp_data = self._request({"platform": "android"})
|
||||
assert not resp_data["success"]
|
||||
assert "s2d-email" in resp_data["errors"]
|
||||
assert not self.mock_send_sms.called
|
||||
assert not self.mock_subscribe.called
|
||||
|
||||
def test_send_android_email(self):
|
||||
resp_data = self._request(
|
||||
{
|
||||
'platform': 'android',
|
||||
's2d-email': 'dude@example.com',
|
||||
'source-url': 'https://nihilism.info',
|
||||
"platform": "android",
|
||||
"s2d-email": "dude@example.com",
|
||||
"source-url": "https://nihilism.info",
|
||||
}
|
||||
)
|
||||
assert resp_data['success']
|
||||
assert resp_data["success"]
|
||||
self.mock_subscribe.assert_called_with(
|
||||
'dude@example.com',
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS['default']['email']['android'],
|
||||
source_url='https://nihilism.info',
|
||||
lang='en-US',
|
||||
"dude@example.com",
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS["default"]["email"]["android"],
|
||||
source_url="https://nihilism.info",
|
||||
lang="en-US",
|
||||
)
|
||||
|
||||
def test_send_android_email_basket_error(self):
|
||||
self.mock_subscribe.side_effect = views.basket.BasketException
|
||||
resp_data = self._request(
|
||||
{
|
||||
'platform': 'android',
|
||||
's2d-email': 'dude@example.com',
|
||||
'source-url': 'https://nihilism.info',
|
||||
"platform": "android",
|
||||
"s2d-email": "dude@example.com",
|
||||
"source-url": "https://nihilism.info",
|
||||
},
|
||||
400,
|
||||
)
|
||||
assert not resp_data['success']
|
||||
assert 'system' in resp_data['errors']
|
||||
assert not resp_data["success"]
|
||||
assert "system" in resp_data["errors"]
|
||||
|
||||
def test_send_android_bad_email(self):
|
||||
resp_data = self._request(
|
||||
{
|
||||
'platform': 'android',
|
||||
's2d-email': '@example.com',
|
||||
'source-url': 'https://nihilism.info',
|
||||
"platform": "android",
|
||||
"s2d-email": "@example.com",
|
||||
"source-url": "https://nihilism.info",
|
||||
}
|
||||
)
|
||||
assert not resp_data['success']
|
||||
assert 'email' in resp_data['errors']
|
||||
assert not resp_data["success"]
|
||||
assert "email" in resp_data["errors"]
|
||||
assert not self.mock_subscribe.called
|
||||
|
||||
# an invalid value for 'message-set' should revert to 'default' message set
|
||||
def test_invalid_message_set(self):
|
||||
resp_data = self._request(
|
||||
{
|
||||
'platform': 'ios',
|
||||
's2d-email': 'dude@example.com',
|
||||
'message-set': 'the-dude-is-not-in',
|
||||
"platform": "ios",
|
||||
"s2d-email": "dude@example.com",
|
||||
"message-set": "the-dude-is-not-in",
|
||||
}
|
||||
)
|
||||
assert resp_data['success']
|
||||
assert resp_data["success"]
|
||||
self.mock_subscribe.assert_called_with(
|
||||
'dude@example.com',
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS['default']['email']['ios'],
|
||||
"dude@example.com",
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS["default"]["email"]["ios"],
|
||||
source_url=None,
|
||||
lang='en-US',
|
||||
lang="en-US",
|
||||
)
|
||||
|
||||
# /firefox/android/ embedded widget (bug 1221328)
|
||||
def test_android_embedded_email(self):
|
||||
resp_data = self._request(
|
||||
{
|
||||
'platform': 'android',
|
||||
's2d-email': 'dude@example.com',
|
||||
'message-set': 'fx-android',
|
||||
"platform": "android",
|
||||
"s2d-email": "dude@example.com",
|
||||
"message-set": "fx-android",
|
||||
}
|
||||
)
|
||||
assert resp_data['success']
|
||||
assert resp_data["success"]
|
||||
self.mock_subscribe.assert_called_with(
|
||||
'dude@example.com',
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS['fx-android']['email']['android'],
|
||||
"dude@example.com",
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS["fx-android"]["email"]["android"],
|
||||
source_url=None,
|
||||
lang='en-US',
|
||||
lang="en-US",
|
||||
)
|
||||
|
||||
# /firefox/mobile-download/desktop
|
||||
def test_fx_mobile_download_desktop_email(self):
|
||||
resp_data = self._request(
|
||||
{
|
||||
's2d-email': 'dude@example.com',
|
||||
'message-set': 'fx-mobile-download-desktop',
|
||||
"s2d-email": "dude@example.com",
|
||||
"message-set": "fx-mobile-download-desktop",
|
||||
}
|
||||
)
|
||||
assert resp_data['success']
|
||||
assert resp_data["success"]
|
||||
self.mock_subscribe.assert_called_with(
|
||||
'dude@example.com',
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS['fx-mobile-download-desktop']['email'][
|
||||
'all'
|
||||
],
|
||||
"dude@example.com",
|
||||
views.SEND_TO_DEVICE_MESSAGE_SETS["fx-mobile-download-desktop"]["email"]["all"],
|
||||
source_url=None,
|
||||
lang='en-US',
|
||||
lang="en-US",
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DEV=False)
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestFirefoxNew(TestCase):
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_download_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "en-US"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/download.html']
|
||||
assert template == ["firefox/new/desktop/download.html"]
|
||||
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_thanks_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/download/thanks/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/download/thanks/")
|
||||
req.locale = "en-US"
|
||||
view = views.DownloadThanksView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/thanks.html']
|
||||
assert template == ["firefox/new/desktop/thanks.html"]
|
||||
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: False)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: False)
|
||||
def test_download_basic_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "de"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/basic/base_download.html']
|
||||
assert template == ["firefox/new/basic/base_download.html"]
|
||||
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: False)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: False)
|
||||
def test_thanks_basic_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/download/thanks/')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/download/thanks/")
|
||||
req.locale = "de"
|
||||
view = views.DownloadThanksView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/basic/thanks.html']
|
||||
assert template == ["firefox/new/basic/thanks.html"]
|
||||
|
||||
def test_thanks_redirect(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/?scene=2&dude=abides')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/new/?scene=2&dude=abides")
|
||||
req.locale = "en-US"
|
||||
view = views.NewView.as_view()
|
||||
resp = view(req)
|
||||
assert resp.status_code == 301
|
||||
assert resp['location'].endswith(
|
||||
'/firefox/download/thanks/?scene=2&dude=abides'
|
||||
)
|
||||
assert resp["location"].endswith("/firefox/download/thanks/?scene=2&dude=abides")
|
||||
|
||||
# begin AMO experiment - issue 10207
|
||||
|
||||
@patch.dict(os.environ, SWITCH_NEW_REDESIGN='True')
|
||||
@patch.dict(os.environ, SWITCH_NEW_REDESIGN="True")
|
||||
def test_thanks_amo_experiment_en(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/download/thanks/?xv=amo')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/download/thanks/?xv=amo")
|
||||
req.locale = "en-US"
|
||||
view = views.DownloadThanksView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/thanks-amo-exp.html']
|
||||
assert template == ["firefox/new/desktop/thanks-amo-exp.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_NEW_REDESIGN='True')
|
||||
@patch.dict(os.environ, SWITCH_NEW_REDESIGN="True")
|
||||
def test_thanks_amo_experiment_locales(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/download/thanks/?xv=amo')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/download/thanks/?xv=amo")
|
||||
req.locale = "de"
|
||||
view = views.DownloadThanksView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/thanks.html']
|
||||
assert template == ["firefox/new/desktop/thanks.html"]
|
||||
|
||||
# begin yandex - issue 5635
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX='True')
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX="True")
|
||||
def test_yandex_download(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'ru'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "ru"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/download_yandex.html']
|
||||
assert template == ["firefox/new/desktop/download_yandex.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX='True')
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX="True")
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_yandex_show_to_ru(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'ru'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "ru"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/download_yandex.html']
|
||||
assert template == ["firefox/new/desktop/download_yandex.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX='True')
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX="True")
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_yandex_hide_not_ru(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "de"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/download.html']
|
||||
assert template == ["firefox/new/desktop/download.html"]
|
||||
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX='False')
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.dict(os.environ, SWITCH_FIREFOX_YANDEX="False")
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_yandex_hide_switch_off(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'ru'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "ru"
|
||||
view = views.NewView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/desktop/download.html']
|
||||
assert template == ["firefox/new/desktop/download.html"]
|
||||
|
||||
# end yandex - issue 5635
|
||||
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW='de:100')
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW="de:100")
|
||||
def test_experiment_redirect(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "de"
|
||||
view = views.NewView.as_view()
|
||||
resp = view(req)
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'].endswith('/exp/firefox/new/')
|
||||
assert resp['cache-control'] == 'max-age=0, no-cache, no-store, must-revalidate'
|
||||
req.locale = 'en-US'
|
||||
assert resp["location"].endswith("/exp/firefox/new/")
|
||||
assert resp["cache-control"] == "max-age=0, no-cache, no-store, must-revalidate"
|
||||
req.locale = "en-US"
|
||||
resp = view(req)
|
||||
assert resp.status_code == 200
|
||||
assert 'cache-control' not in resp
|
||||
assert "cache-control" not in resp
|
||||
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW='de:100')
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW="de:100")
|
||||
def test_experiment_redirect_query(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/?dude=abide&walter=bowl')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/new/?dude=abide&walter=bowl")
|
||||
req.locale = "de"
|
||||
view = views.NewView.as_view()
|
||||
resp = view(req)
|
||||
assert resp.status_code == 302
|
||||
assert resp['location'].endswith('/exp/firefox/new/?dude=abide&walter=bowl')
|
||||
assert resp["location"].endswith("/exp/firefox/new/?dude=abide&walter=bowl")
|
||||
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW='de:100')
|
||||
@patch.dict(os.environ, EXP_CONFIG_FX_NEW="de:100")
|
||||
def test_experiment_redirect_automation_param(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/new/?automation=true')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/firefox/new/?automation=true")
|
||||
req.locale = "de"
|
||||
view = views.NewView.as_view()
|
||||
resp = view(req)
|
||||
assert resp.status_code == 200
|
||||
assert 'cache-control' not in resp
|
||||
assert "cache-control" not in resp
|
||||
|
||||
|
||||
class TestFirefoxNewNoIndex(TestCase):
|
||||
def test_download_noindex(self):
|
||||
# Scene 1 of /firefox/new/ should never contain a noindex tag.
|
||||
req = RequestFactory().get('/firefox/new/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/new/")
|
||||
req.locale = "en-US"
|
||||
view = views.NewView.as_view()
|
||||
response = view(req)
|
||||
doc = pq(response.content)
|
||||
|
@ -628,63 +608,62 @@ class TestFirefoxNewNoIndex(TestCase):
|
|||
|
||||
def test_thanks_canonical(self):
|
||||
# Scene 2 /firefox/download/thanks/ should always contain a noindex tag.
|
||||
req = RequestFactory().get('/firefox/download/thanks/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/download/thanks/")
|
||||
req.locale = "en-US"
|
||||
view = views.DownloadThanksView.as_view()
|
||||
response = view(req)
|
||||
doc = pq(response.content)
|
||||
robots = doc('meta[name="robots"]')
|
||||
assert robots.length == 1
|
||||
assert 'noindex' in robots.attr('content')
|
||||
assert "noindex" in robots.attr("content")
|
||||
|
||||
|
||||
@override_settings(DEV=False)
|
||||
@patch('bedrock.firefox.views.l10n_utils.render', return_value=HttpResponse())
|
||||
@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse())
|
||||
class TestFirefoxPlatform(TestCase):
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_linux_download_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/linux/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/linux/")
|
||||
req.locale = "en-US"
|
||||
view = views.PlatformViewLinux.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/basic/download_linux.html']
|
||||
assert template == ["firefox/new/basic/download_linux.html"]
|
||||
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_mac_download_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/mac/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/mac/")
|
||||
req.locale = "en-US"
|
||||
view = views.PlatformViewMac.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/basic/download_mac.html']
|
||||
assert template == ["firefox/new/basic/download_mac.html"]
|
||||
|
||||
@patch.object(views, 'ftl_file_is_active', lambda *x: True)
|
||||
@patch.object(views, "ftl_file_is_active", lambda *x: True)
|
||||
def test_windows_download_template(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/windows/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/windows/")
|
||||
req.locale = "en-US"
|
||||
view = views.PlatformViewWindows.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/new/basic/download_windows.html']
|
||||
assert template == ["firefox/new/basic/download_windows.html"]
|
||||
|
||||
|
||||
class TestFirefoxHome(TestCase):
|
||||
@patch('bedrock.firefox.views.l10n_utils.render')
|
||||
@patch("bedrock.firefox.views.l10n_utils.render")
|
||||
def test_firefox_home(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/")
|
||||
req.locale = "en-US"
|
||||
view = views.FirefoxHomeView.as_view()
|
||||
view(req)
|
||||
template = render_mock.call_args[0][1]
|
||||
assert template == ['firefox/home/index-master.html']
|
||||
assert template == ["firefox/home/index-master.html"]
|
||||
|
||||
|
||||
class TestFirefoxWelcomePage1(TestCase):
|
||||
@patch('bedrock.firefox.views.l10n_utils.render')
|
||||
@patch("bedrock.firefox.views.l10n_utils.render")
|
||||
def test_firefox_welcome_page1(self, render_mock):
|
||||
req = RequestFactory().get('/firefox/welcome/1/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/firefox/welcome/1/")
|
||||
req.locale = "en-US"
|
||||
views.firefox_welcome_page1(req)
|
||||
render_mock.assert_called_once_with(req, 'firefox/welcome/page1.html', ANY,
|
||||
ftl_files='firefox/welcome/page1')
|
||||
render_mock.assert_called_once_with(req, "firefox/welcome/page1.html", ANY, ftl_files="firefox/welcome/page1")
|
||||
|
|
|
@ -14,202 +14,211 @@ from bedrock.utils import views as utils_views
|
|||
from bedrock.utils.views import VariationTemplateView
|
||||
|
||||
|
||||
latest_re = r'^firefox(?:/(?P<version>%s))?/%s/$'
|
||||
firstrun_re = latest_re % (version_re, 'firstrun')
|
||||
whatsnew_re = latest_re % (version_re, 'whatsnew')
|
||||
whatsnew_re_china = latest_re % (version_re, 'whatsnew/china')
|
||||
whatsnew_re_en = latest_re % (version_re, 'whatsnew/en')
|
||||
whatsnew_re_france = latest_re % (version_re, 'whatsnew/france')
|
||||
whatsnew_re_all = latest_re % (version_re, 'whatsnew/all')
|
||||
platform_re = '(?P<platform>android|ios)'
|
||||
channel_re = '(?P<channel>beta|aurora|developer|nightly|organizations)'
|
||||
releasenotes_re = latest_re % (version_re, r'(aurora|release)notes')
|
||||
android_releasenotes_re = releasenotes_re.replace(r'firefox', 'firefox/android')
|
||||
ios_releasenotes_re = releasenotes_re.replace(r'firefox', 'firefox/ios')
|
||||
sysreq_re = latest_re % (version_re, 'system-requirements')
|
||||
android_sysreq_re = sysreq_re.replace(r'firefox', 'firefox/android')
|
||||
ios_sysreq_re = sysreq_re.replace(r'firefox', 'firefox/ios')
|
||||
latest_re = r"^firefox(?:/(?P<version>%s))?/%s/$"
|
||||
firstrun_re = latest_re % (version_re, "firstrun")
|
||||
whatsnew_re = latest_re % (version_re, "whatsnew")
|
||||
whatsnew_re_china = latest_re % (version_re, "whatsnew/china")
|
||||
whatsnew_re_en = latest_re % (version_re, "whatsnew/en")
|
||||
whatsnew_re_france = latest_re % (version_re, "whatsnew/france")
|
||||
whatsnew_re_all = latest_re % (version_re, "whatsnew/all")
|
||||
platform_re = "(?P<platform>android|ios)"
|
||||
channel_re = "(?P<channel>beta|aurora|developer|nightly|organizations)"
|
||||
releasenotes_re = latest_re % (version_re, r"(aurora|release)notes")
|
||||
android_releasenotes_re = releasenotes_re.replace(r"firefox", "firefox/android")
|
||||
ios_releasenotes_re = releasenotes_re.replace(r"firefox", "firefox/ios")
|
||||
sysreq_re = latest_re % (version_re, "system-requirements")
|
||||
android_sysreq_re = sysreq_re.replace(r"firefox", "firefox/android")
|
||||
ios_sysreq_re = sysreq_re.replace(r"firefox", "firefox/ios")
|
||||
|
||||
|
||||
urlpatterns = (
|
||||
url(r'^firefox/$', views.FirefoxHomeView.as_view(), name='firefox'),
|
||||
url(r'^firefox/all/$', views.firefox_all, name='firefox.all'),
|
||||
page('firefox/accounts', 'firefox/accounts.html', ftl_files=['firefox/accounts']),
|
||||
page('firefox/browsers', 'firefox/browsers/index.html', ftl_files=['firefox/browsers']),
|
||||
page('firefox/products', 'firefox/products/index.html', ftl_files=['firefox/products']),
|
||||
page('firefox/flashback', 'firefox/flashback/index.html', active_locales=['en-US', 'de', 'fr']),
|
||||
page('firefox/channel/desktop', 'firefox/channel/desktop.html', ftl_files=['firefox/channel']),
|
||||
page('firefox/channel/android', 'firefox/channel/android.html', ftl_files=['firefox/channel']),
|
||||
page('firefox/channel/ios', 'firefox/channel/ios.html', ftl_files=['firefox/channel']),
|
||||
page('firefox/developer', 'firefox/developer/index.html', ftl_files=['firefox/developer']),
|
||||
page('firefox/enterprise', 'firefox/enterprise/index.html', ftl_files=['firefox/enterprise']),
|
||||
page('firefox/facebookcontainer', 'firefox/facebookcontainer/index.html', ftl_files=['firefox/facebook_container']),
|
||||
|
||||
page('firefox/features', 'firefox/features/index.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/index']),
|
||||
page('firefox/features/adblocker', 'firefox/features/adblocker.html',
|
||||
ftl_files=['firefox/features/adblocker']),
|
||||
page('firefox/features/bookmarks', 'firefox/features/bookmarks.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/bookmarks']),
|
||||
page('firefox/features/fast', 'firefox/features/fast.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/fast']),
|
||||
page('firefox/features/block-fingerprinting', 'firefox/features/fingerprinting.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/fingerprinting']),
|
||||
page('firefox/features/independent', 'firefox/features/independent.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/independent']),
|
||||
page('firefox/features/memory', 'firefox/features/memory.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/memory']),
|
||||
page('firefox/features/password-manager', 'firefox/features/password-manager.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/password-manager']),
|
||||
page('firefox/features/private-browsing', 'firefox/features/private-browsing.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/private-browsing']),
|
||||
page('firefox/features/safebrowser', 'firefox/features/safebrowser.html'),
|
||||
url(r'^firefox/features/translate/$', views.firefox_features_translate, name='firefox.features.translate'),
|
||||
page('firefox/features/picture-in-picture', 'firefox/features/picture-in-picture.html',
|
||||
ftl_files=['firefox/features/shared', 'firefox/features/picture-in-picture']),
|
||||
|
||||
url(r'^firefox/features/tips/$',
|
||||
url(r"^firefox/$", views.FirefoxHomeView.as_view(), name="firefox"),
|
||||
url(r"^firefox/all/$", views.firefox_all, name="firefox.all"),
|
||||
page("firefox/accounts", "firefox/accounts.html", ftl_files=["firefox/accounts"]),
|
||||
page("firefox/browsers", "firefox/browsers/index.html", ftl_files=["firefox/browsers"]),
|
||||
page("firefox/products", "firefox/products/index.html", ftl_files=["firefox/products"]),
|
||||
page("firefox/flashback", "firefox/flashback/index.html", active_locales=["en-US", "de", "fr"]),
|
||||
page("firefox/channel/desktop", "firefox/channel/desktop.html", ftl_files=["firefox/channel"]),
|
||||
page("firefox/channel/android", "firefox/channel/android.html", ftl_files=["firefox/channel"]),
|
||||
page("firefox/channel/ios", "firefox/channel/ios.html", ftl_files=["firefox/channel"]),
|
||||
page("firefox/developer", "firefox/developer/index.html", ftl_files=["firefox/developer"]),
|
||||
page("firefox/enterprise", "firefox/enterprise/index.html", ftl_files=["firefox/enterprise"]),
|
||||
page("firefox/facebookcontainer", "firefox/facebookcontainer/index.html", ftl_files=["firefox/facebook_container"]),
|
||||
page("firefox/features", "firefox/features/index.html", ftl_files=["firefox/features/shared", "firefox/features/index"]),
|
||||
page("firefox/features/adblocker", "firefox/features/adblocker.html", ftl_files=["firefox/features/adblocker"]),
|
||||
page("firefox/features/bookmarks", "firefox/features/bookmarks.html", ftl_files=["firefox/features/shared", "firefox/features/bookmarks"]),
|
||||
page("firefox/features/fast", "firefox/features/fast.html", ftl_files=["firefox/features/shared", "firefox/features/fast"]),
|
||||
page(
|
||||
"firefox/features/block-fingerprinting",
|
||||
"firefox/features/fingerprinting.html",
|
||||
ftl_files=["firefox/features/shared", "firefox/features/fingerprinting"],
|
||||
),
|
||||
page("firefox/features/independent", "firefox/features/independent.html", ftl_files=["firefox/features/shared", "firefox/features/independent"]),
|
||||
page("firefox/features/memory", "firefox/features/memory.html", ftl_files=["firefox/features/shared", "firefox/features/memory"]),
|
||||
page(
|
||||
"firefox/features/password-manager",
|
||||
"firefox/features/password-manager.html",
|
||||
ftl_files=["firefox/features/shared", "firefox/features/password-manager"],
|
||||
),
|
||||
page(
|
||||
"firefox/features/private-browsing",
|
||||
"firefox/features/private-browsing.html",
|
||||
ftl_files=["firefox/features/shared", "firefox/features/private-browsing"],
|
||||
),
|
||||
page("firefox/features/safebrowser", "firefox/features/safebrowser.html"),
|
||||
url(r"^firefox/features/translate/$", views.firefox_features_translate, name="firefox.features.translate"),
|
||||
page(
|
||||
"firefox/features/picture-in-picture",
|
||||
"firefox/features/picture-in-picture.html",
|
||||
ftl_files=["firefox/features/shared", "firefox/features/picture-in-picture"],
|
||||
),
|
||||
url(
|
||||
r"^firefox/features/tips/$",
|
||||
VariationTemplateView.as_view(
|
||||
template_name='firefox/features/tips/tips.html',
|
||||
template_context_variations=['picture-in-picture', 'eyedropper', 'forget'],
|
||||
), name='firefox.features.tips'),
|
||||
|
||||
url(r'^firefox/ios/testflight/$', views.ios_testflight, name='firefox.ios.testflight'),
|
||||
page('firefox/mobile/get-app', 'firefox/mobile/get-app.html', ftl_files=['firefox/mobile']),
|
||||
url('^firefox/send-to-device-post/$', views.send_to_device_ajax,
|
||||
name='firefox.send-to-device-post'),
|
||||
page('firefox/unsupported-systems', 'firefox/unsupported-systems.html'),
|
||||
url(r'^firefox/new/$', views.NewView.as_view(), name='firefox.new'),
|
||||
url(r'^firefox/download/thanks/$', views.DownloadThanksView.as_view(), name='firefox.download.thanks'),
|
||||
page('firefox/nightly/firstrun', 'firefox/nightly/firstrun.html', ftl_files=['firefox/nightly/firstrun']),
|
||||
url(r'^firefox/installer-help/$', views.InstallerHelpView.as_view(), name='firefox.installer-help'),
|
||||
url(firstrun_re, views.FirstrunView.as_view(), name='firefox.firstrun'),
|
||||
url(whatsnew_re, views.WhatsNewRedirectorView.as_view(), name='firefox.whatsnew'),
|
||||
url(whatsnew_re_china, views.WhatsNewChinaView.as_view(), name='firefox.whatsnew.china'),
|
||||
url(whatsnew_re_en, views.WhatsNewEnglishView.as_view(), name='firefox.whatsnew.en'),
|
||||
url(whatsnew_re_france, views.WhatsNewFranceView.as_view(), name='firefox.whatsnew.france'),
|
||||
url(whatsnew_re_all, views.WhatsnewView.as_view(), name='firefox.whatsnew.all'),
|
||||
|
||||
template_name="firefox/features/tips/tips.html",
|
||||
template_context_variations=["picture-in-picture", "eyedropper", "forget"],
|
||||
),
|
||||
name="firefox.features.tips",
|
||||
),
|
||||
url(r"^firefox/ios/testflight/$", views.ios_testflight, name="firefox.ios.testflight"),
|
||||
page("firefox/mobile/get-app", "firefox/mobile/get-app.html", ftl_files=["firefox/mobile"]),
|
||||
url("^firefox/send-to-device-post/$", views.send_to_device_ajax, name="firefox.send-to-device-post"),
|
||||
page("firefox/unsupported-systems", "firefox/unsupported-systems.html"),
|
||||
url(r"^firefox/new/$", views.NewView.as_view(), name="firefox.new"),
|
||||
url(r"^firefox/download/thanks/$", views.DownloadThanksView.as_view(), name="firefox.download.thanks"),
|
||||
page("firefox/nightly/firstrun", "firefox/nightly/firstrun.html", ftl_files=["firefox/nightly/firstrun"]),
|
||||
url(r"^firefox/installer-help/$", views.InstallerHelpView.as_view(), name="firefox.installer-help"),
|
||||
url(firstrun_re, views.FirstrunView.as_view(), name="firefox.firstrun"),
|
||||
url(whatsnew_re, views.WhatsNewRedirectorView.as_view(), name="firefox.whatsnew"),
|
||||
url(whatsnew_re_china, views.WhatsNewChinaView.as_view(), name="firefox.whatsnew.china"),
|
||||
url(whatsnew_re_en, views.WhatsNewEnglishView.as_view(), name="firefox.whatsnew.en"),
|
||||
url(whatsnew_re_france, views.WhatsNewFranceView.as_view(), name="firefox.whatsnew.france"),
|
||||
url(whatsnew_re_all, views.WhatsnewView.as_view(), name="firefox.whatsnew.all"),
|
||||
# Release notes
|
||||
url('^firefox/(?:%s/)?(?:%s/)?notes/$' % (platform_re, channel_re),
|
||||
bedrock.releasenotes.views.latest_notes, name='firefox.notes'),
|
||||
url('^firefox/nightly/notes/feed/$',
|
||||
bedrock.releasenotes.views.nightly_feed, name='firefox.nightly.notes.feed'),
|
||||
url('firefox/(?:latest/)?releasenotes/$', bedrock.releasenotes.views.latest_notes,
|
||||
{'product': 'firefox'}),
|
||||
url('^firefox/(?:%s/)?(?:%s/)?system-requirements/$' % (platform_re, channel_re),
|
||||
url("^firefox/(?:%s/)?(?:%s/)?notes/$" % (platform_re, channel_re), bedrock.releasenotes.views.latest_notes, name="firefox.notes"),
|
||||
url("^firefox/nightly/notes/feed/$", bedrock.releasenotes.views.nightly_feed, name="firefox.nightly.notes.feed"),
|
||||
url("firefox/(?:latest/)?releasenotes/$", bedrock.releasenotes.views.latest_notes, {"product": "firefox"}),
|
||||
url(
|
||||
"^firefox/(?:%s/)?(?:%s/)?system-requirements/$" % (platform_re, channel_re),
|
||||
bedrock.releasenotes.views.latest_sysreq,
|
||||
{'product': 'firefox'}, name='firefox.sysreq'),
|
||||
url(releasenotes_re, bedrock.releasenotes.views.release_notes, name='firefox.desktop.releasenotes'),
|
||||
url(android_releasenotes_re, bedrock.releasenotes.views.release_notes,
|
||||
{'product': 'Firefox for Android'}, name='firefox.android.releasenotes'),
|
||||
url(ios_releasenotes_re, bedrock.releasenotes.views.release_notes,
|
||||
{'product': 'Firefox for iOS'}, name='firefox.ios.releasenotes'),
|
||||
url(sysreq_re, bedrock.releasenotes.views.system_requirements,
|
||||
name='firefox.system_requirements'),
|
||||
url(android_sysreq_re, bedrock.releasenotes.views.system_requirements,
|
||||
{'product': 'Firefox for Android'}, name='firefox.android.system_requirements'),
|
||||
url(ios_sysreq_re, bedrock.releasenotes.views.system_requirements,
|
||||
{'product': 'Firefox for iOS'}, name='firefox.ios.system_requirements'),
|
||||
url('^firefox/releases/$', bedrock.releasenotes.views.releases_index,
|
||||
{'product': 'Firefox'}, name='firefox.releases.index'),
|
||||
|
||||
url('^firefox/stub_attribution_code/$', views.stub_attribution_code,
|
||||
name='firefox.stub_attribution_code'),
|
||||
|
||||
url(r'^firefox/welcome/1/$', views.firefox_welcome_page1, name='firefox.welcome.page1'),
|
||||
page('firefox/welcome/2', 'firefox/welcome/page2.html', ftl_files=['firefox/welcome/page2']),
|
||||
page('firefox/welcome/3', 'firefox/welcome/page3.html', ftl_files=['firefox/welcome/page3']),
|
||||
page('firefox/welcome/4', 'firefox/welcome/page4.html', ftl_files=['firefox/welcome/page4']),
|
||||
page('firefox/welcome/5', 'firefox/welcome/page5.html', ftl_files=['firefox/welcome/page5']),
|
||||
page('firefox/welcome/6', 'firefox/welcome/page6.html', ftl_files=['firefox/welcome/page6']),
|
||||
page('firefox/welcome/7', 'firefox/welcome/page7.html', ftl_files=['firefox/welcome/page7']),
|
||||
url(r'^firefox/welcome/8/$',
|
||||
{"product": "firefox"},
|
||||
name="firefox.sysreq",
|
||||
),
|
||||
url(releasenotes_re, bedrock.releasenotes.views.release_notes, name="firefox.desktop.releasenotes"),
|
||||
url(android_releasenotes_re, bedrock.releasenotes.views.release_notes, {"product": "Firefox for Android"}, name="firefox.android.releasenotes"),
|
||||
url(ios_releasenotes_re, bedrock.releasenotes.views.release_notes, {"product": "Firefox for iOS"}, name="firefox.ios.releasenotes"),
|
||||
url(sysreq_re, bedrock.releasenotes.views.system_requirements, name="firefox.system_requirements"),
|
||||
url(
|
||||
android_sysreq_re,
|
||||
bedrock.releasenotes.views.system_requirements,
|
||||
{"product": "Firefox for Android"},
|
||||
name="firefox.android.system_requirements",
|
||||
),
|
||||
url(ios_sysreq_re, bedrock.releasenotes.views.system_requirements, {"product": "Firefox for iOS"}, name="firefox.ios.system_requirements"),
|
||||
url("^firefox/releases/$", bedrock.releasenotes.views.releases_index, {"product": "Firefox"}, name="firefox.releases.index"),
|
||||
url("^firefox/stub_attribution_code/$", views.stub_attribution_code, name="firefox.stub_attribution_code"),
|
||||
url(r"^firefox/welcome/1/$", views.firefox_welcome_page1, name="firefox.welcome.page1"),
|
||||
page("firefox/welcome/2", "firefox/welcome/page2.html", ftl_files=["firefox/welcome/page2"]),
|
||||
page("firefox/welcome/3", "firefox/welcome/page3.html", ftl_files=["firefox/welcome/page3"]),
|
||||
page("firefox/welcome/4", "firefox/welcome/page4.html", ftl_files=["firefox/welcome/page4"]),
|
||||
page("firefox/welcome/5", "firefox/welcome/page5.html", ftl_files=["firefox/welcome/page5"]),
|
||||
page("firefox/welcome/6", "firefox/welcome/page6.html", ftl_files=["firefox/welcome/page6"]),
|
||||
page("firefox/welcome/7", "firefox/welcome/page7.html", ftl_files=["firefox/welcome/page7"]),
|
||||
url(
|
||||
r"^firefox/welcome/8/$",
|
||||
utils_views.VariationTemplateView.as_view(
|
||||
template_name='firefox/welcome/page8.html',
|
||||
ftl_files=['firefox/welcome/page8'],
|
||||
template_context_variations=['text', 'image', 'animation', 'header-text'],
|
||||
variation_locales=['en-US', 'en-CA', 'en-GB', 'de', 'fr']),
|
||||
name='firefox.welcome.page8'),
|
||||
page('firefox/welcome/9', 'firefox/welcome/page9.html', active_locales=['de', 'fr']),
|
||||
page('firefox/welcome/10', 'firefox/welcome/page10.html'),
|
||||
|
||||
page('firefox/switch', 'firefox/switch.html', ftl_files=['firefox/switch']),
|
||||
page('firefox/pocket', 'firefox/pocket.html'),
|
||||
|
||||
|
||||
template_name="firefox/welcome/page8.html",
|
||||
ftl_files=["firefox/welcome/page8"],
|
||||
template_context_variations=["text", "image", "animation", "header-text"],
|
||||
variation_locales=["en-US", "en-CA", "en-GB", "de", "fr"],
|
||||
),
|
||||
name="firefox.welcome.page8",
|
||||
),
|
||||
page("firefox/welcome/9", "firefox/welcome/page9.html", active_locales=["de", "fr"]),
|
||||
page("firefox/welcome/10", "firefox/welcome/page10.html"),
|
||||
page("firefox/switch", "firefox/switch.html", ftl_files=["firefox/switch"]),
|
||||
page("firefox/pocket", "firefox/pocket.html"),
|
||||
# Issue 6604, SEO firefox/new pages
|
||||
url('firefox/linux', views.PlatformViewLinux.as_view(), name='firefox.linux'),
|
||||
url('firefox/mac', views.PlatformViewMac.as_view(), name='firefox.mac'),
|
||||
url('firefox/windows', views.PlatformViewWindows.as_view(), name='firefox.windows'),
|
||||
|
||||
page('firefox/browsers/compare', 'firefox/browsers/compare/index.html',
|
||||
ftl_files=['firefox/browsers/compare/index', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/brave', 'firefox/browsers/compare/brave.html',
|
||||
ftl_files=['firefox/browsers/compare/brave', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/chrome', 'firefox/browsers/compare/chrome.html',
|
||||
ftl_files=['firefox/browsers/compare/chrome', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/edge', 'firefox/browsers/compare/edge.html',
|
||||
ftl_files=['firefox/browsers/compare/edge', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/ie', 'firefox/browsers/compare/ie.html',
|
||||
ftl_files=['firefox/browsers/compare/ie', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/opera', 'firefox/browsers/compare/opera.html',
|
||||
ftl_files=['firefox/browsers/compare/opera', 'firefox/browsers/compare/shared']),
|
||||
page('firefox/browsers/compare/safari', 'firefox/browsers/compare/safari.html',
|
||||
ftl_files=['firefox/browsers/compare/safari', 'firefox/browsers/compare/shared']),
|
||||
|
||||
url("firefox/linux", views.PlatformViewLinux.as_view(), name="firefox.linux"),
|
||||
url("firefox/mac", views.PlatformViewMac.as_view(), name="firefox.mac"),
|
||||
url("firefox/windows", views.PlatformViewWindows.as_view(), name="firefox.windows"),
|
||||
page(
|
||||
"firefox/browsers/compare",
|
||||
"firefox/browsers/compare/index.html",
|
||||
ftl_files=["firefox/browsers/compare/index", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/brave",
|
||||
"firefox/browsers/compare/brave.html",
|
||||
ftl_files=["firefox/browsers/compare/brave", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/chrome",
|
||||
"firefox/browsers/compare/chrome.html",
|
||||
ftl_files=["firefox/browsers/compare/chrome", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/edge",
|
||||
"firefox/browsers/compare/edge.html",
|
||||
ftl_files=["firefox/browsers/compare/edge", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/ie",
|
||||
"firefox/browsers/compare/ie.html",
|
||||
ftl_files=["firefox/browsers/compare/ie", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/opera",
|
||||
"firefox/browsers/compare/opera.html",
|
||||
ftl_files=["firefox/browsers/compare/opera", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
page(
|
||||
"firefox/browsers/compare/safari",
|
||||
"firefox/browsers/compare/safari.html",
|
||||
ftl_files=["firefox/browsers/compare/safari", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
# Issue 10182
|
||||
url(r'^firefox/browsers/mobile/$', views.FirefoxMobileView.as_view(), name='firefox.browsers.mobile.index'),
|
||||
page('firefox/browsers/mobile/android', 'firefox/browsers/mobile/android.html',
|
||||
ftl_files=['firefox/browsers/mobile/android']),
|
||||
page('firefox/browsers/mobile/ios', 'firefox/browsers/mobile/ios.html',
|
||||
ftl_files=['firefox/browsers/mobile/ios']),
|
||||
page('firefox/browsers/mobile/focus', 'firefox/browsers/mobile/focus.html',
|
||||
ftl_files=['firefox/browsers/mobile/focus']),
|
||||
page('firefox/browsers/mobile/compare', 'firefox/browsers/mobile/compare.html',
|
||||
ftl_files=['firefox/browsers/mobile/compare', 'firefox/browsers/compare/shared']),
|
||||
|
||||
url(r"^firefox/browsers/mobile/$", views.FirefoxMobileView.as_view(), name="firefox.browsers.mobile.index"),
|
||||
page("firefox/browsers/mobile/android", "firefox/browsers/mobile/android.html", ftl_files=["firefox/browsers/mobile/android"]),
|
||||
page("firefox/browsers/mobile/ios", "firefox/browsers/mobile/ios.html", ftl_files=["firefox/browsers/mobile/ios"]),
|
||||
page("firefox/browsers/mobile/focus", "firefox/browsers/mobile/focus.html", ftl_files=["firefox/browsers/mobile/focus"]),
|
||||
page(
|
||||
"firefox/browsers/mobile/compare",
|
||||
"firefox/browsers/mobile/compare.html",
|
||||
ftl_files=["firefox/browsers/mobile/compare", "firefox/browsers/compare/shared"],
|
||||
),
|
||||
# Issue 8641
|
||||
page('firefox/browsers/best-browser', 'firefox/browsers/best-browser.html', ftl_files=['firefox/browsers/best-browser']),
|
||||
page('firefox/browsers/browser-history', 'firefox/browsers/browser-history.html', ftl_files=['firefox/browsers/history/browser-history']),
|
||||
page('firefox/browsers/incognito-browser', 'firefox/browsers/incognito-browser.html'),
|
||||
page('firefox/browsers/update-your-browser', 'firefox/browsers/update-browser.html'),
|
||||
page('firefox/browsers/what-is-a-browser', 'firefox/browsers/what-is-a-browser.html', ftl_files=['firefox/browsers/history/what-is-a-browser']),
|
||||
page('firefox/browsers/windows-64-bit', 'firefox/browsers/windows-64-bit.html', ftl_files=['firefox/browsers/windows-64-bit']),
|
||||
|
||||
page("firefox/browsers/best-browser", "firefox/browsers/best-browser.html", ftl_files=["firefox/browsers/best-browser"]),
|
||||
page("firefox/browsers/browser-history", "firefox/browsers/browser-history.html", ftl_files=["firefox/browsers/history/browser-history"]),
|
||||
page("firefox/browsers/incognito-browser", "firefox/browsers/incognito-browser.html"),
|
||||
page("firefox/browsers/update-your-browser", "firefox/browsers/update-browser.html"),
|
||||
page("firefox/browsers/what-is-a-browser", "firefox/browsers/what-is-a-browser.html", ftl_files=["firefox/browsers/history/what-is-a-browser"]),
|
||||
page("firefox/browsers/windows-64-bit", "firefox/browsers/windows-64-bit.html", ftl_files=["firefox/browsers/windows-64-bit"]),
|
||||
# Lockwise
|
||||
page('firefox/lockwise', 'firefox/products/lockwise.html', ftl_files=['firefox/products/lockwise']),
|
||||
|
||||
page("firefox/lockwise", "firefox/products/lockwise.html", ftl_files=["firefox/products/lockwise"]),
|
||||
# Issue 7765, 7709
|
||||
page('firefox/privacy', 'firefox/privacy/index.html', ftl_files=['firefox/privacy-hub']),
|
||||
page('firefox/privacy/products', 'firefox/privacy/products.html', ftl_files=['firefox/privacy-hub']),
|
||||
page('firefox/privacy/safe-passwords', 'firefox/privacy/passwords.html', ftl_files=['firefox/privacy-hub', 'firefox/privacy/passwords']),
|
||||
|
||||
page("firefox/privacy", "firefox/privacy/index.html", ftl_files=["firefox/privacy-hub"]),
|
||||
page("firefox/privacy/products", "firefox/privacy/products.html", ftl_files=["firefox/privacy-hub"]),
|
||||
page("firefox/privacy/safe-passwords", "firefox/privacy/passwords.html", ftl_files=["firefox/privacy-hub", "firefox/privacy/passwords"]),
|
||||
# Issue 8432
|
||||
page('firefox/set-as-default/thanks', 'firefox/set-as-default/thanks.html', ftl_files='firefox/set-as-default/thanks'),
|
||||
page("firefox/set-as-default/thanks", "firefox/set-as-default/thanks.html", ftl_files="firefox/set-as-default/thanks"),
|
||||
# Default browser campaign
|
||||
page('firefox/set-as-default', 'firefox/set-as-default/landing.html', ftl_files='firefox/set-as-default/landing'),
|
||||
|
||||
page("firefox/set-as-default", "firefox/set-as-default/landing.html", ftl_files="firefox/set-as-default/landing"),
|
||||
# Issue 8536
|
||||
page('firefox/retention/thank-you', 'firefox/retention/thank-you.html', ftl_files='firefox/retention/thank-you'),
|
||||
|
||||
page("firefox/retention/thank-you", "firefox/retention/thank-you.html", ftl_files="firefox/retention/thank-you"),
|
||||
# Unfck campaign
|
||||
page('firefox/unfck', 'firefox/campaign/unfck/index.html', active_locales=['de', 'en-US', 'fr']),
|
||||
|
||||
page("firefox/unfck", "firefox/campaign/unfck/index.html", active_locales=["de", "en-US", "fr"]),
|
||||
# Issue #9490 - Evergreen Content for SEO
|
||||
page('firefox/more', 'firefox/more.html', ftl_files='firefox/more'),
|
||||
page('firefox/browsers/quantum', 'firefox/browsers/quantum.html', ftl_files='firefox/browsers/quantum'),
|
||||
page('firefox/faq', 'firefox/faq.html', ftl_files='firefox/faq'),
|
||||
page('firefox/browsers/chromebook', 'firefox/browsers/chromebook.html', ftl_files='firefox/browsers/chromebook'),
|
||||
page('firefox/sync', 'firefox/sync.html', ftl_files='firefox/sync'),
|
||||
page('firefox/privacy/book', 'firefox/privacy/book.html', ftl_files='firefox/privacy/book'),
|
||||
|
||||
page("firefox/more", "firefox/more.html", ftl_files="firefox/more"),
|
||||
page("firefox/browsers/quantum", "firefox/browsers/quantum.html", ftl_files="firefox/browsers/quantum"),
|
||||
page("firefox/faq", "firefox/faq.html", ftl_files="firefox/faq"),
|
||||
page("firefox/browsers/chromebook", "firefox/browsers/chromebook.html", ftl_files="firefox/browsers/chromebook"),
|
||||
page("firefox/sync", "firefox/sync.html", ftl_files="firefox/sync"),
|
||||
page("firefox/privacy/book", "firefox/privacy/book.html", ftl_files="firefox/privacy/book"),
|
||||
# Issue 9957
|
||||
page('firefox/more/misinformation', 'firefox/more/misinformation.html', ftl_files='firefox/more/misinformation'),
|
||||
page("firefox/more/misinformation", "firefox/more/misinformation.html", ftl_files="firefox/more/misinformation"),
|
||||
)
|
||||
|
||||
# Contentful
|
||||
if settings.DEV:
|
||||
urlpatterns += (
|
||||
path('firefox/more/<content_id>/', views.FirefoxContenful.as_view()),
|
||||
)
|
||||
urlpatterns += (path("firefox/more/<content_id>/", views.FirefoxContenful.as_view()),)
|
||||
|
|
|
@ -11,8 +11,7 @@ def is_current_or_newer(user_version):
|
|||
"""
|
||||
Return true if the version (X.Y only) is for the latest Firefox or newer.
|
||||
"""
|
||||
latest = Version(product_details.firefox_versions[
|
||||
'LATEST_FIREFOX_VERSION'])
|
||||
latest = Version(product_details.firefox_versions["LATEST_FIREFOX_VERSION"])
|
||||
user = Version(user_version)
|
||||
|
||||
# check for ESR
|
||||
|
@ -21,6 +20,6 @@ def is_current_or_newer(user_version):
|
|||
|
||||
# similar to the way comparison is done in the Version class,
|
||||
# but only using the major and minor versions.
|
||||
latest_int = int('%d%02d' % (latest.major, latest.minor1))
|
||||
user_int = int('%d%02d' % (user.major or 0, user.minor1 or 0))
|
||||
latest_int = int("%d%02d" % (latest.major, latest.minor1))
|
||||
user_int = int("%d%02d" % (user.major or 0, user.minor1 or 0))
|
||||
return user_int >= latest_int
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,17 +1,15 @@
|
|||
from bedrock.redirects.util import redirect
|
||||
|
||||
redirectpatterns = (
|
||||
|
||||
redirect(r'^foundation/?$', 'https://foundation.mozilla.org/'),
|
||||
redirect(r'^foundation/about/?$', 'https://foundation.mozilla.org/about/'),
|
||||
redirect(r'^foundation/documents/?$', 'https://foundation.mozilla.org/about/public-records/'),
|
||||
redirect(r'^foundation/issues/?$', 'https://foundation.mozilla.org/initiatives/'),
|
||||
redirect(r'^foundation/leadership-network/?$', 'https://foundation.mozilla.org/'),
|
||||
redirect(r'^foundation/advocacy/?$', 'https://foundation.mozilla.org/'),
|
||||
redirect(r'^foundation/trademarks/?$', '/foundation/trademarks/policy/'),
|
||||
redirect(r'^foundation/trademarks/faq/?$', '/foundation/trademarks/policy/'),
|
||||
redirect(r'^foundation/documents/domain-name-license\.pdf$', '/foundation/trademarks/policy/'),
|
||||
redirect(r'^foundation/trademarks/poweredby/faq/?$', '/foundation/trademarks/policy/'),
|
||||
redirect(r'^foundation/trademarks/l10n-website-policy/?$', '/foundation/trademarks/policy/'),
|
||||
|
||||
redirect(r"^foundation/?$", "https://foundation.mozilla.org/"),
|
||||
redirect(r"^foundation/about/?$", "https://foundation.mozilla.org/about/"),
|
||||
redirect(r"^foundation/documents/?$", "https://foundation.mozilla.org/about/public-records/"),
|
||||
redirect(r"^foundation/issues/?$", "https://foundation.mozilla.org/initiatives/"),
|
||||
redirect(r"^foundation/leadership-network/?$", "https://foundation.mozilla.org/"),
|
||||
redirect(r"^foundation/advocacy/?$", "https://foundation.mozilla.org/"),
|
||||
redirect(r"^foundation/trademarks/?$", "/foundation/trademarks/policy/"),
|
||||
redirect(r"^foundation/trademarks/faq/?$", "/foundation/trademarks/policy/"),
|
||||
redirect(r"^foundation/documents/domain-name-license\.pdf$", "/foundation/trademarks/policy/"),
|
||||
redirect(r"^foundation/trademarks/poweredby/faq/?$", "/foundation/trademarks/policy/"),
|
||||
redirect(r"^foundation/trademarks/l10n-website-policy/?$", "/foundation/trademarks/policy/"),
|
||||
)
|
||||
|
|
|
@ -6,84 +6,63 @@ from bedrock.mozorg.util import page
|
|||
from bedrock.redirects.util import redirect
|
||||
|
||||
urlpatterns = (
|
||||
|
||||
# Issue 9727 /foundation/annualreport/2019/
|
||||
redirect(r'^annualreport/$', 'foundation.annualreport.2019.index',
|
||||
name='foundation.annualreport', locale_prefix=False),
|
||||
|
||||
redirect(r"^annualreport/$", "foundation.annualreport.2019.index", name="foundation.annualreport", locale_prefix=False),
|
||||
# Older annual report financial faqs - these are linked from blog posts
|
||||
# was e.g.: http://www.mozilla.org/foundation/documents/mozilla-2008-financial-faq.html
|
||||
page('documents/mozilla-2006-financial-faq', 'foundation/documents/mozilla-2006-financial-faq.html'),
|
||||
page('documents/mozilla-2007-financial-faq', 'foundation/documents/mozilla-2007-financial-faq.html'),
|
||||
page('documents/mozilla-2008-financial-faq', 'foundation/documents/mozilla-2008-financial-faq.html'),
|
||||
|
||||
page("documents/mozilla-2006-financial-faq", "foundation/documents/mozilla-2006-financial-faq.html"),
|
||||
page("documents/mozilla-2007-financial-faq", "foundation/documents/mozilla-2007-financial-faq.html"),
|
||||
page("documents/mozilla-2008-financial-faq", "foundation/documents/mozilla-2008-financial-faq.html"),
|
||||
# ported from PHP in Bug 960689
|
||||
page('documents/bylaws-amendment-1', 'foundation/documents/bylaws-amendment-1.html'),
|
||||
page('documents/bylaws-amendment-2', 'foundation/documents/bylaws-amendment-2.html'),
|
||||
page('documents/articles-of-incorporation', 'foundation/documents/articles-of-incorporation.html'),
|
||||
page('documents/articles-of-incorporation/amendment', 'foundation/documents/articles-of-incorporation-amendment.html'),
|
||||
page('documents/bylaws', 'foundation/documents/bylaws.html'),
|
||||
|
||||
page("documents/bylaws-amendment-1", "foundation/documents/bylaws-amendment-1.html"),
|
||||
page("documents/bylaws-amendment-2", "foundation/documents/bylaws-amendment-2.html"),
|
||||
page("documents/articles-of-incorporation", "foundation/documents/articles-of-incorporation.html"),
|
||||
page("documents/articles-of-incorporation/amendment", "foundation/documents/articles-of-incorporation-amendment.html"),
|
||||
page("documents/bylaws", "foundation/documents/bylaws.html"),
|
||||
# was https://www.mozilla.org/foundation/annualreport/2009/
|
||||
page('annualreport/2009', 'foundation/annualreport/2009/index.html'),
|
||||
page("annualreport/2009", "foundation/annualreport/2009/index.html"),
|
||||
# was .html
|
||||
page('annualreport/2009/a-competitive-world', 'foundation/annualreport/2009/a-competitive-world.html'),
|
||||
page("annualreport/2009/a-competitive-world", "foundation/annualreport/2009/a-competitive-world.html"),
|
||||
# was .html
|
||||
page('annualreport/2009/broadening-our-scope', 'foundation/annualreport/2009/broadening-our-scope.html'),
|
||||
page("annualreport/2009/broadening-our-scope", "foundation/annualreport/2009/broadening-our-scope.html"),
|
||||
# was .html
|
||||
page('annualreport/2009/sustainability', 'foundation/annualreport/2009/sustainability.html'),
|
||||
|
||||
page("annualreport/2009/sustainability", "foundation/annualreport/2009/sustainability.html"),
|
||||
# was https://www.mozilla.org/foundation/annualreport/2009/faq.html
|
||||
# changing to https://www.mozilla.org/foundation/annualreport/2009/faq/
|
||||
page('annualreport/2009/faq', 'foundation/annualreport/2009/faq.html'),
|
||||
|
||||
page('annualreport/2010', 'foundation/annualreport/2010/index.html'),
|
||||
page('annualreport/2010/ahead', 'foundation/annualreport/2010/ahead.html'),
|
||||
page('annualreport/2010/opportunities', 'foundation/annualreport/2010/opportunities.html'),
|
||||
page('annualreport/2010/people', 'foundation/annualreport/2010/people.html'),
|
||||
page('annualreport/2010/faq', 'foundation/annualreport/2010/faq.html'),
|
||||
|
||||
page('annualreport/2011', 'foundation/annualreport/2011.html'),
|
||||
page('annualreport/2011/faq', 'foundation/annualreport/2011faq.html'),
|
||||
|
||||
page('annualreport/2012', 'foundation/annualreport/2012/index.html'),
|
||||
page('annualreport/2012/faq', 'foundation/annualreport/2012/faq.html'),
|
||||
|
||||
page('annualreport/2013', 'foundation/annualreport/2013/index.html'),
|
||||
page('annualreport/2013/faq', 'foundation/annualreport/2013/faq.html'),
|
||||
|
||||
page('annualreport/2014', 'foundation/annualreport/2014/index.html'),
|
||||
page('annualreport/2014/faq', 'foundation/annualreport/2014/faq.html'),
|
||||
|
||||
page('annualreport/2015', 'foundation/annualreport/2015/index.html'),
|
||||
page('annualreport/2015/faq', 'foundation/annualreport/2015/faq.html'),
|
||||
|
||||
page('annualreport/2016', 'foundation/annualreport/2016/index.html'),
|
||||
|
||||
page('annualreport/2017', 'foundation/annualreport/2017/index.html'),
|
||||
|
||||
page('annualreport/2018', 'foundation/annualreport/2018/index.html'),
|
||||
|
||||
page('annualreport/2019', 'foundation/annualreport/2019/index.html'),
|
||||
|
||||
page('feed-icon-guidelines', 'foundation/feed-icon-guidelines/index.html'),
|
||||
page('feed-icon-guidelines/faq', 'foundation/feed-icon-guidelines/faq.html'),
|
||||
|
||||
page('licensing', 'foundation/licensing.html'),
|
||||
page('licensing/website-content', 'foundation/licensing/website-content.html'),
|
||||
page('licensing/website-markup', 'foundation/licensing/website-markup.html'),
|
||||
page('licensing/binary-components', 'foundation/licensing/binary-components/index.html'),
|
||||
page('licensing/binary-components/rationale', 'foundation/licensing/binary-components/rationale.html'),
|
||||
page('moco', 'foundation/moco.html'),
|
||||
|
||||
page('openwebfund/more', 'foundation/openwebfund/more.html'),
|
||||
page('openwebfund/thanks', 'foundation/openwebfund/thanks.html'),
|
||||
|
||||
page('trademarks/policy', 'foundation/trademarks/policy.html'),
|
||||
page('trademarks/list', 'foundation/trademarks/list.html'),
|
||||
page('trademarks/distribution-policy', 'foundation/trademarks/distribution-policy.html'),
|
||||
page('trademarks/community-edition-permitted-changes', 'foundation/trademarks/community-edition-permitted-changes.html'),
|
||||
page('trademarks/community-edition-policy', 'foundation/trademarks/community-edition-policy.html'),
|
||||
|
||||
page('reimagine-open', 'foundation/reimagine-open.html'),
|
||||
page("annualreport/2009/faq", "foundation/annualreport/2009/faq.html"),
|
||||
page("annualreport/2010", "foundation/annualreport/2010/index.html"),
|
||||
page("annualreport/2010/ahead", "foundation/annualreport/2010/ahead.html"),
|
||||
page("annualreport/2010/opportunities", "foundation/annualreport/2010/opportunities.html"),
|
||||
page("annualreport/2010/people", "foundation/annualreport/2010/people.html"),
|
||||
page("annualreport/2010/faq", "foundation/annualreport/2010/faq.html"),
|
||||
page("annualreport/2011", "foundation/annualreport/2011.html"),
|
||||
page("annualreport/2011/faq", "foundation/annualreport/2011faq.html"),
|
||||
page("annualreport/2012", "foundation/annualreport/2012/index.html"),
|
||||
page("annualreport/2012/faq", "foundation/annualreport/2012/faq.html"),
|
||||
page("annualreport/2013", "foundation/annualreport/2013/index.html"),
|
||||
page("annualreport/2013/faq", "foundation/annualreport/2013/faq.html"),
|
||||
page("annualreport/2014", "foundation/annualreport/2014/index.html"),
|
||||
page("annualreport/2014/faq", "foundation/annualreport/2014/faq.html"),
|
||||
page("annualreport/2015", "foundation/annualreport/2015/index.html"),
|
||||
page("annualreport/2015/faq", "foundation/annualreport/2015/faq.html"),
|
||||
page("annualreport/2016", "foundation/annualreport/2016/index.html"),
|
||||
page("annualreport/2017", "foundation/annualreport/2017/index.html"),
|
||||
page("annualreport/2018", "foundation/annualreport/2018/index.html"),
|
||||
page("annualreport/2019", "foundation/annualreport/2019/index.html"),
|
||||
page("feed-icon-guidelines", "foundation/feed-icon-guidelines/index.html"),
|
||||
page("feed-icon-guidelines/faq", "foundation/feed-icon-guidelines/faq.html"),
|
||||
page("licensing", "foundation/licensing.html"),
|
||||
page("licensing/website-content", "foundation/licensing/website-content.html"),
|
||||
page("licensing/website-markup", "foundation/licensing/website-markup.html"),
|
||||
page("licensing/binary-components", "foundation/licensing/binary-components/index.html"),
|
||||
page("licensing/binary-components/rationale", "foundation/licensing/binary-components/rationale.html"),
|
||||
page("moco", "foundation/moco.html"),
|
||||
page("openwebfund/more", "foundation/openwebfund/more.html"),
|
||||
page("openwebfund/thanks", "foundation/openwebfund/thanks.html"),
|
||||
page("trademarks/policy", "foundation/trademarks/policy.html"),
|
||||
page("trademarks/list", "foundation/trademarks/list.html"),
|
||||
page("trademarks/distribution-policy", "foundation/trademarks/distribution-policy.html"),
|
||||
page("trademarks/community-edition-permitted-changes", "foundation/trademarks/community-edition-permitted-changes.html"),
|
||||
page("trademarks/community-edition-policy", "foundation/trademarks/community-edition-policy.html"),
|
||||
page("reimagine-open", "foundation/reimagine-open.html"),
|
||||
)
|
||||
|
|
|
@ -19,108 +19,72 @@ class FraudReportForm(forms.Form):
|
|||
max_length=2000,
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': _lazy(u'Please enter a URL.'),
|
||||
"required": _lazy("Please enter a URL."),
|
||||
},
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
'size': 40,
|
||||
'placeholder': _lazy(u'http://offendingsite.com'),
|
||||
'class': 'required fill-width',
|
||||
'required': 'required',
|
||||
'aria-required': 'true',
|
||||
"size": 40,
|
||||
"placeholder": _lazy("http://offendingsite.com"),
|
||||
"class": "required fill-width",
|
||||
"required": "required",
|
||||
"aria-required": "true",
|
||||
}
|
||||
)
|
||||
),
|
||||
)
|
||||
input_category = forms.ChoiceField(
|
||||
choices=(
|
||||
('Charging for software',
|
||||
_lazy(u'Charging for software')),
|
||||
('Collecting personal information',
|
||||
_lazy(u'Collecting personal information')),
|
||||
('Domain name violation',
|
||||
_lazy(u'Domain name violation')),
|
||||
('Logo misuse/modification',
|
||||
_lazy(u'Logo misuse/modification')),
|
||||
('Distributing modified Firefox/malware',
|
||||
_lazy(u'Distributing modified Firefox/malware')),
|
||||
("Charging for software", _lazy("Charging for software")),
|
||||
("Collecting personal information", _lazy("Collecting personal information")),
|
||||
("Domain name violation", _lazy("Domain name violation")),
|
||||
("Logo misuse/modification", _lazy("Logo misuse/modification")),
|
||||
("Distributing modified Firefox/malware", _lazy("Distributing modified Firefox/malware")),
|
||||
),
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': _lazy('Please select a category.'),
|
||||
"required": _lazy("Please select a category."),
|
||||
},
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
'title': _lazy(u'Category'),
|
||||
'class': 'required',
|
||||
'required': 'required',
|
||||
'aria-required': 'true',
|
||||
"title": _lazy("Category"),
|
||||
"class": "required",
|
||||
"required": "required",
|
||||
"aria-required": "true",
|
||||
}
|
||||
)
|
||||
),
|
||||
)
|
||||
input_product = forms.ChoiceField(
|
||||
choices=(
|
||||
('Firefox', _lazy(u'Firefox')),
|
||||
('SeaMonkey', _lazy(u'SeaMonkey')),
|
||||
('Thunderbird', _lazy(u'Thunderbird')),
|
||||
('Other Mozilla Product/Project', _lazy(u'Other Mozilla Product/Project (specify)')),
|
||||
("Firefox", _lazy("Firefox")),
|
||||
("SeaMonkey", _lazy("SeaMonkey")),
|
||||
("Thunderbird", _lazy("Thunderbird")),
|
||||
("Other Mozilla Product/Project", _lazy("Other Mozilla Product/Project (specify)")),
|
||||
),
|
||||
required=True,
|
||||
error_messages={
|
||||
'required': _lazy('Please select a product.'),
|
||||
"required": _lazy("Please select a product."),
|
||||
},
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
'title': _lazy(u'Product'),
|
||||
'class': 'required',
|
||||
'required': 'required',
|
||||
'aria-required': 'true',
|
||||
"title": _lazy("Product"),
|
||||
"class": "required",
|
||||
"required": "required",
|
||||
"aria-required": "true",
|
||||
}
|
||||
)
|
||||
)
|
||||
input_specific_product = forms.CharField(
|
||||
max_length=254,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
'size': 20,
|
||||
'class': 'fill-width'
|
||||
}
|
||||
)
|
||||
)
|
||||
input_details = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
'rows': '',
|
||||
'cols': '',
|
||||
'class': 'fill-width'
|
||||
}
|
||||
)
|
||||
),
|
||||
)
|
||||
input_specific_product = forms.CharField(max_length=254, required=False, widget=forms.TextInput(attrs={"size": 20, "class": "fill-width"}))
|
||||
input_details = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": "", "cols": "", "class": "fill-width"}))
|
||||
input_attachment = forms.ImageField(required=False)
|
||||
input_attachment_desc = forms.CharField(
|
||||
max_length=254,
|
||||
required=False,
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
'rows': '',
|
||||
'cols': '',
|
||||
'class': 'fill-width'
|
||||
}
|
||||
)
|
||||
max_length=254, required=False, widget=forms.Textarea(attrs={"rows": "", "cols": "", "class": "fill-width"})
|
||||
)
|
||||
input_email = forms.EmailField(
|
||||
max_length=254,
|
||||
required=False,
|
||||
error_messages={
|
||||
'invalid': _lazy(u'Please enter a valid email address'),
|
||||
"invalid": _lazy("Please enter a valid email address"),
|
||||
},
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
'size': 20,
|
||||
'class': 'fill-width'
|
||||
}
|
||||
)
|
||||
widget=forms.TextInput(attrs={"size": 20, "class": "fill-width"}),
|
||||
)
|
||||
# honeypot
|
||||
office_fax = forms.CharField(widget=HoneyPotWidget, required=False)
|
||||
|
@ -130,14 +94,12 @@ class FraudReportForm(forms.Form):
|
|||
|
||||
if attachment:
|
||||
if attachment.size > FRAUD_REPORT_FILE_SIZE_LIMIT:
|
||||
raise forms.ValidationError(
|
||||
_("Attachment must not exceed 5MB"))
|
||||
raise forms.ValidationError(_("Attachment must not exceed 5MB"))
|
||||
|
||||
return attachment
|
||||
|
||||
def clean_office_fax(self):
|
||||
honeypot = self.cleaned_data.pop('office_fax', None)
|
||||
honeypot = self.cleaned_data.pop("office_fax", None)
|
||||
|
||||
if honeypot:
|
||||
raise forms.ValidationError(
|
||||
_('Your submission could not be processed'))
|
||||
raise forms.ValidationError(_("Your submission could not be processed"))
|
||||
|
|
|
@ -3,11 +3,9 @@ from bedrock.redirects.util import redirect
|
|||
|
||||
redirectpatterns = (
|
||||
# bug 1243240
|
||||
redirect(r'^about/legal/report-abuse/?$', 'legal.report-infringement'),
|
||||
|
||||
redirect(r"^about/legal/report-abuse/?$", "legal.report-infringement"),
|
||||
# bug 1321033
|
||||
redirect(r'^about/legal/terms/firefox-hello', 'privacy.archive.hello-2014-11'),
|
||||
|
||||
redirect(r"^about/legal/terms/firefox-hello", "privacy.archive.hello-2014-11"),
|
||||
# issue 5816, Issue 8418
|
||||
redirect(r'^about/logo', 'https://mozilla.design/')
|
||||
redirect(r"^about/logo", "https://mozilla.design/"),
|
||||
)
|
||||
|
|
|
@ -21,17 +21,17 @@ from bedrock.mozorg.tests import TestCase
|
|||
class TestFraudReport(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
with self.activate('en-US'):
|
||||
self.url = reverse('legal.fraud-report')
|
||||
with self.activate("en-US"):
|
||||
self.url = reverse("legal.fraud-report")
|
||||
|
||||
self.data = {
|
||||
'input_url': 'http://www.test.com/',
|
||||
'input_category': 'Charging for software',
|
||||
'input_product': 'Firefox',
|
||||
'input_specific_product': '',
|
||||
'input_details': 'test details',
|
||||
'input_attachment_desc': 'test attachment',
|
||||
'input_email': 'foo@bar.com',
|
||||
"input_url": "http://www.test.com/",
|
||||
"input_category": "Charging for software",
|
||||
"input_product": "Firefox",
|
||||
"input_specific_product": "",
|
||||
"input_details": "test details",
|
||||
"input_attachment_desc": "test attachment",
|
||||
"input_email": "foo@bar.com",
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -39,13 +39,13 @@ class TestFraudReport(TestCase):
|
|||
|
||||
def _create_image_file(self):
|
||||
io = BytesIO()
|
||||
image = Image.new('RGB', (200, 200), 'white')
|
||||
image.save(io, 'PNG')
|
||||
image = Image.new("RGB", (200, 200), "white")
|
||||
image.save(io, "PNG")
|
||||
io.seek(0)
|
||||
return SimpleUploadedFile('image.png', io.read(), 'image/png')
|
||||
return SimpleUploadedFile("image.png", io.read(), "image/png")
|
||||
|
||||
def _create_text_file(self):
|
||||
return SimpleUploadedFile('stuff.txt', b'This is not an image', 'text/plain')
|
||||
return SimpleUploadedFile("stuff.txt", b"This is not an image", "text/plain")
|
||||
|
||||
def test_view_post_valid_data(self):
|
||||
"""
|
||||
|
@ -60,7 +60,7 @@ class TestFraudReport(TestCase):
|
|||
response = legal_views.fraud_report(request)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert response['Location'] == '/en-US/about/legal/defend-mozilla-trademarks/?submitted=True'
|
||||
assert response["Location"] == "/en-US/about/legal/defend-mozilla-trademarks/?submitted=True"
|
||||
|
||||
def test_view_post_missing_data(self):
|
||||
"""
|
||||
|
@ -68,7 +68,7 @@ class TestFraudReport(TestCase):
|
|||
errors in the template.
|
||||
"""
|
||||
|
||||
self.data.update(input_url='') # remove required url
|
||||
self.data.update(input_url="") # remove required url
|
||||
|
||||
request = self.factory.post(self.url, self.data)
|
||||
|
||||
|
@ -78,7 +78,7 @@ class TestFraudReport(TestCase):
|
|||
response = legal_views.fraud_report(request)
|
||||
|
||||
assert response.status_code == 200
|
||||
self.assertIn(b'Please enter a URL.', response.content)
|
||||
self.assertIn(b"Please enter a URL.", response.content)
|
||||
|
||||
def test_view_post_honeypot(self):
|
||||
"""
|
||||
|
@ -86,7 +86,7 @@ class TestFraudReport(TestCase):
|
|||
contain general form error message.
|
||||
"""
|
||||
|
||||
self.data['office_fax'] = 'spammer'
|
||||
self.data["office_fax"] = "spammer"
|
||||
|
||||
request = self.factory.post(self.url, self.data)
|
||||
|
||||
|
@ -96,7 +96,7 @@ class TestFraudReport(TestCase):
|
|||
response = legal_views.fraud_report(request)
|
||||
|
||||
assert response.status_code == 200
|
||||
self.assertIn(b'An error has occurred', response.content)
|
||||
self.assertIn(b"An error has occurred", response.content)
|
||||
|
||||
def test_form_valid_data(self):
|
||||
"""
|
||||
|
@ -112,7 +112,7 @@ class TestFraudReport(TestCase):
|
|||
With incorrect data (missing url), form should not be valid and should
|
||||
have url in the errors hash.
|
||||
"""
|
||||
self.data.update(input_url='') # remove required url
|
||||
self.data.update(input_url="") # remove required url
|
||||
|
||||
form = FraudReportForm(self.data)
|
||||
|
||||
|
@ -120,13 +120,13 @@ class TestFraudReport(TestCase):
|
|||
assert not form.is_valid()
|
||||
|
||||
# make sure url errors are in form
|
||||
self.assertIn('input_url', form.errors)
|
||||
self.assertIn("input_url", form.errors)
|
||||
|
||||
def test_form_honeypot(self):
|
||||
"""
|
||||
Form with honeypot text box filled should not be valid.
|
||||
"""
|
||||
self.data['office_fax'] = 'spammer!'
|
||||
self.data["office_fax"] = "spammer!"
|
||||
|
||||
form = FraudReportForm(self.data)
|
||||
|
||||
|
@ -138,7 +138,7 @@ class TestFraudReport(TestCase):
|
|||
"""
|
||||
# attachment within size limit
|
||||
mock_attachment = self._create_image_file()
|
||||
form = FraudReportForm(self.data, {'input_attachment': mock_attachment})
|
||||
form = FraudReportForm(self.data, {"input_attachment": mock_attachment})
|
||||
# make sure form is valid
|
||||
assert form.is_valid()
|
||||
|
||||
|
@ -149,11 +149,11 @@ class TestFraudReport(TestCase):
|
|||
"""
|
||||
# attachment within size limit
|
||||
mock_attachment = self._create_text_file()
|
||||
form = FraudReportForm(self.data, {'input_attachment': mock_attachment})
|
||||
form = FraudReportForm(self.data, {"input_attachment": mock_attachment})
|
||||
# make sure form is not valid
|
||||
assert not form.is_valid()
|
||||
# make sure attachment errors are in form
|
||||
self.assertIn('input_attachment', form.errors)
|
||||
self.assertIn("input_attachment", form.errors)
|
||||
|
||||
def test_form_invalid_attachement_size(self):
|
||||
"""
|
||||
|
@ -162,16 +162,16 @@ class TestFraudReport(TestCase):
|
|||
"""
|
||||
# attachment within size limit
|
||||
mock_attachment = self._create_image_file()
|
||||
form = FraudReportForm(self.data, {'input_attachment': mock_attachment})
|
||||
with patch.object(legal_forms, 'FRAUD_REPORT_FILE_SIZE_LIMIT', 100):
|
||||
form = FraudReportForm(self.data, {"input_attachment": mock_attachment})
|
||||
with patch.object(legal_forms, "FRAUD_REPORT_FILE_SIZE_LIMIT", 100):
|
||||
# make sure form is not valid
|
||||
assert not form.is_valid()
|
||||
|
||||
# make sure attachment errors are in form
|
||||
self.assertIn('input_attachment', form.errors)
|
||||
self.assertIn("input_attachment", form.errors)
|
||||
|
||||
@patch('bedrock.legal.views.render_to_string', return_value='rendered')
|
||||
@patch('bedrock.legal.views.EmailMessage')
|
||||
@patch("bedrock.legal.views.render_to_string", return_value="rendered")
|
||||
@patch("bedrock.legal.views.EmailMessage")
|
||||
def test_email(self, mock_email_message, mock_render_to_string):
|
||||
"""
|
||||
Make sure email is sent with expected values.
|
||||
|
@ -182,7 +182,7 @@ class TestFraudReport(TestCase):
|
|||
form = FraudReportForm(self.data)
|
||||
|
||||
# submit form
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
submit_form(request, form)
|
||||
|
||||
# make sure email was sent
|
||||
|
@ -190,44 +190,41 @@ class TestFraudReport(TestCase):
|
|||
|
||||
# make sure email values are correct
|
||||
mock_email_message.assert_called_once_with(
|
||||
legal_views.FRAUD_REPORT_EMAIL_SUBJECT % (self.data['input_url'],
|
||||
self.data['input_category']),
|
||||
'rendered',
|
||||
legal_views.FRAUD_REPORT_EMAIL_SUBJECT % (self.data["input_url"], self.data["input_category"]),
|
||||
"rendered",
|
||||
legal_views.FRAUD_REPORT_EMAIL_FROM,
|
||||
legal_views.FRAUD_REPORT_EMAIL_TO)
|
||||
legal_views.FRAUD_REPORT_EMAIL_TO,
|
||||
)
|
||||
|
||||
@patch('bedrock.legal.views.render_to_string', return_value='rendered')
|
||||
@patch('bedrock.legal.views.EmailMessage')
|
||||
@patch("bedrock.legal.views.render_to_string", return_value="rendered")
|
||||
@patch("bedrock.legal.views.EmailMessage")
|
||||
def test_email_with_attachement(self, mock_email_message, mock_render_to_string):
|
||||
"""
|
||||
Make sure email is sent with attachment.
|
||||
"""
|
||||
mock_attachment = self._create_image_file()
|
||||
|
||||
form = FraudReportForm(self.data, {'input_attachment': mock_attachment})
|
||||
form = FraudReportForm(self.data, {"input_attachment": mock_attachment})
|
||||
|
||||
# submit form
|
||||
request = self.factory.get('/')
|
||||
request = self.factory.get("/")
|
||||
ret = submit_form(request, form)
|
||||
self.assertFalse(ret['form_error'])
|
||||
self.assertFalse(ret["form_error"])
|
||||
|
||||
# make sure attachment was attached
|
||||
mock_attachment.seek(0)
|
||||
mock_email_message.return_value.attach.assert_called_once_with(
|
||||
mock_attachment.name,
|
||||
mock_attachment.read(),
|
||||
mock_attachment.content_type)
|
||||
mock_email_message.return_value.attach.assert_called_once_with(mock_attachment.name, mock_attachment.read(), mock_attachment.content_type)
|
||||
|
||||
# make sure email was sent
|
||||
mock_email_message.return_value.send.assert_called_once()
|
||||
|
||||
# make sure email values are correct
|
||||
mock_email_message.assert_called_once_with(
|
||||
legal_views.FRAUD_REPORT_EMAIL_SUBJECT % (self.data['input_url'],
|
||||
self.data['input_category']),
|
||||
'rendered',
|
||||
legal_views.FRAUD_REPORT_EMAIL_SUBJECT % (self.data["input_url"], self.data["input_category"]),
|
||||
"rendered",
|
||||
legal_views.FRAUD_REPORT_EMAIL_FROM,
|
||||
legal_views.FRAUD_REPORT_EMAIL_TO)
|
||||
legal_views.FRAUD_REPORT_EMAIL_TO,
|
||||
)
|
||||
|
||||
def test_emails_not_escaped(self):
|
||||
"""
|
||||
|
@ -245,17 +242,16 @@ class TestFraudReport(TestCase):
|
|||
Tags are still stripped, though.
|
||||
"""
|
||||
|
||||
STRING1 = u"<em>J'adore Citröns</em> & <Piñatas> so there"
|
||||
EXPECTED1 = u"J'adore Citröns & so there"
|
||||
STRING1 = "<em>J'adore Citröns</em> & <Piñatas> so there"
|
||||
EXPECTED1 = "J'adore Citröns & so there"
|
||||
|
||||
STRING2 = u"<em>J'adore Piñatas</em> & <fromage> so here"
|
||||
EXPECTED2 = u"J'adore Piñatas & so here"
|
||||
STRING2 = "<em>J'adore Piñatas</em> & <fromage> so here"
|
||||
EXPECTED2 = "J'adore Piñatas & so here"
|
||||
|
||||
STRING3 = u"J'adore <coffee>el café</coffee> también"
|
||||
EXPECTED3 = u"J'adore el café también"
|
||||
STRING3 = "J'adore <coffee>el café</coffee> también"
|
||||
EXPECTED3 = "J'adore el café también"
|
||||
|
||||
self.data.update(input_specific_product=STRING1, input_details=STRING2,
|
||||
input_attachment_desc=STRING3)
|
||||
self.data.update(input_specific_product=STRING1, input_details=STRING2, input_attachment_desc=STRING3)
|
||||
request = self.factory.post(self.url, self.data)
|
||||
|
||||
# make sure CSRF doesn't hold us up
|
||||
|
|
|
@ -10,58 +10,79 @@ from bedrock.legal import views
|
|||
from bedrock.legal_docs.views import LegalDocView
|
||||
|
||||
urlpatterns = (
|
||||
page('', 'legal/index.html', ftl_files=['mozorg/about/legal']),
|
||||
|
||||
page('eula', 'legal/eula.html'),
|
||||
page('eula/firefox-2', 'legal/eula/firefox-2-eula.html'),
|
||||
page('eula/firefox-3', 'legal/eula/firefox-3-eula.html'),
|
||||
page('eula/thunderbird-1.5', 'legal/eula/thunderbird-1.5-eula.html'),
|
||||
page('eula/thunderbird-2', 'legal/eula/thunderbird-2-eula.html'),
|
||||
page('firefox', 'legal/firefox.html'),
|
||||
page("", "legal/index.html", ftl_files=["mozorg/about/legal"]),
|
||||
page("eula", "legal/eula.html"),
|
||||
page("eula/firefox-2", "legal/eula/firefox-2-eula.html"),
|
||||
page("eula/firefox-3", "legal/eula/firefox-3-eula.html"),
|
||||
page("eula/thunderbird-1.5", "legal/eula/thunderbird-1.5-eula.html"),
|
||||
page("eula/thunderbird-2", "legal/eula/thunderbird-2-eula.html"),
|
||||
page("firefox", "legal/firefox.html"),
|
||||
# The "impressum" page is intended for Germany. Redirect to German (de) if
|
||||
# requested in any other locale. (Bug 1248393)
|
||||
page('impressum', 'legal/impressum.html', active_locales=['de']),
|
||||
|
||||
url(r'^terms/betterweb/$', LegalDocView.as_view(template_name='legal/terms/betterweb.html', legal_doc_name='better_web_terms'),
|
||||
name='legal.terms.betterweb'),
|
||||
|
||||
url(r'^terms/mozilla/$', LegalDocView.as_view(template_name='legal/terms/mozilla.html', legal_doc_name='Websites_ToU'),
|
||||
name='legal.terms.mozilla'),
|
||||
|
||||
url(r'^terms/mozilla-vpn/$', LegalDocView.as_view(template_name='legal/terms/mozilla-vpn.html',
|
||||
legal_doc_name='Mozilla_VPN_ToS'), name='legal.terms.mozilla-vpn'),
|
||||
|
||||
url(r'^terms/firefox/$', LegalDocView.as_view(template_name='legal/terms/firefox.html', legal_doc_name='firefox_about_rights'),
|
||||
name='legal.terms.firefox'),
|
||||
|
||||
url(r'^terms/firefox-lite/$', LegalDocView.as_view(template_name='legal/terms/firefox-lite.html',
|
||||
legal_doc_name='firefox_lite_contentservices_ToS'), name='legal.terms.firefox-lite'),
|
||||
|
||||
url(r'^terms/firefox-lite/reward/$', LegalDocView.as_view(template_name='legal/terms/firefox-lite-reward.html',
|
||||
legal_doc_name='firefox_lite_contentservices_reward'), name='legal.terms.firefox-lite-reward'),
|
||||
|
||||
url(r'^terms/firefox-reality/$', LegalDocView.as_view(template_name='legal/terms/firefox-reality.html',
|
||||
legal_doc_name='firefox_reality_about_rights'), name='legal.terms.firefox-reality'),
|
||||
|
||||
url(r'^terms/firefox-private-network/$', LegalDocView.as_view(template_name='legal/terms/firefox-private-network.html',
|
||||
legal_doc_name='Firefox_Private_Network_ToS'), name='legal.terms.firefox-private-network'),
|
||||
|
||||
url(r'^terms/firefox-relay/$', LegalDocView.as_view(template_name='legal/terms/firefox-relay.html',
|
||||
legal_doc_name='firefox_relay_ToS'), name='legal.terms.firefox-relay'),
|
||||
|
||||
url(r'^terms/thunderbird/$', LegalDocView.as_view(template_name='legal/terms/thunderbird.html', legal_doc_name='thunderbird_about_rights'),
|
||||
name='legal.terms.thunderbird'),
|
||||
|
||||
url(r'^terms/services/$', LegalDocView.as_view(template_name='legal/terms/services.html', legal_doc_name='firefox_cloud_services_ToS'),
|
||||
name='legal.terms.services'),
|
||||
|
||||
page('terms/vpn', 'legal/terms/vpn.html'),
|
||||
|
||||
url(r'^acceptable-use/$', LegalDocView.as_view(template_name='legal/terms/acceptable-use.html', legal_doc_name='acceptable_use_policy'),
|
||||
name='legal.terms.acceptable-use'),
|
||||
|
||||
url(r'^report-infringement/$', LegalDocView.as_view(template_name='legal/report-infringement.html', legal_doc_name='report_infringement'),
|
||||
name='legal.report-infringement'),
|
||||
|
||||
url('^defend-mozilla-trademarks/$', views.fraud_report, name='legal.fraud-report'),
|
||||
page("impressum", "legal/impressum.html", active_locales=["de"]),
|
||||
url(
|
||||
r"^terms/betterweb/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/betterweb.html", legal_doc_name="better_web_terms"),
|
||||
name="legal.terms.betterweb",
|
||||
),
|
||||
url(
|
||||
r"^terms/mozilla/$", LegalDocView.as_view(template_name="legal/terms/mozilla.html", legal_doc_name="Websites_ToU"), name="legal.terms.mozilla"
|
||||
),
|
||||
url(
|
||||
r"^terms/mozilla-vpn/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/mozilla-vpn.html", legal_doc_name="Mozilla_VPN_ToS"),
|
||||
name="legal.terms.mozilla-vpn",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox.html", legal_doc_name="firefox_about_rights"),
|
||||
name="legal.terms.firefox",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox-lite/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox-lite.html", legal_doc_name="firefox_lite_contentservices_ToS"),
|
||||
name="legal.terms.firefox-lite",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox-lite/reward/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox-lite-reward.html", legal_doc_name="firefox_lite_contentservices_reward"),
|
||||
name="legal.terms.firefox-lite-reward",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox-reality/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox-reality.html", legal_doc_name="firefox_reality_about_rights"),
|
||||
name="legal.terms.firefox-reality",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox-private-network/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox-private-network.html", legal_doc_name="Firefox_Private_Network_ToS"),
|
||||
name="legal.terms.firefox-private-network",
|
||||
),
|
||||
url(
|
||||
r"^terms/firefox-relay/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/firefox-relay.html", legal_doc_name="firefox_relay_ToS"),
|
||||
name="legal.terms.firefox-relay",
|
||||
),
|
||||
url(
|
||||
r"^terms/thunderbird/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/thunderbird.html", legal_doc_name="thunderbird_about_rights"),
|
||||
name="legal.terms.thunderbird",
|
||||
),
|
||||
url(
|
||||
r"^terms/services/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/services.html", legal_doc_name="firefox_cloud_services_ToS"),
|
||||
name="legal.terms.services",
|
||||
),
|
||||
page("terms/vpn", "legal/terms/vpn.html"),
|
||||
url(
|
||||
r"^acceptable-use/$",
|
||||
LegalDocView.as_view(template_name="legal/terms/acceptable-use.html", legal_doc_name="acceptable_use_policy"),
|
||||
name="legal.terms.acceptable-use",
|
||||
),
|
||||
url(
|
||||
r"^report-infringement/$",
|
||||
LegalDocView.as_view(template_name="legal/report-infringement.html", legal_doc_name="report_infringement"),
|
||||
name="legal.report-infringement",
|
||||
),
|
||||
url("^defend-mozilla-trademarks/$", views.fraud_report, name="legal.fraud-report"),
|
||||
)
|
||||
|
|
|
@ -11,9 +11,9 @@ from bedrock.base.urlresolvers import reverse
|
|||
from bedrock.legal.forms import FraudReportForm
|
||||
from lib import l10n_utils
|
||||
|
||||
FRAUD_REPORT_EMAIL_FROM = 'Mozilla.com <noreply@mozilla.com>'
|
||||
FRAUD_REPORT_EMAIL_SUBJECT = 'New trademark infringement report: %s; %s'
|
||||
FRAUD_REPORT_EMAIL_TO = ['trademarks@mozilla.com']
|
||||
FRAUD_REPORT_EMAIL_FROM = "Mozilla.com <noreply@mozilla.com>"
|
||||
FRAUD_REPORT_EMAIL_SUBJECT = "New trademark infringement report: %s; %s"
|
||||
FRAUD_REPORT_EMAIL_TO = ["trademarks@mozilla.com"]
|
||||
|
||||
|
||||
def submit_form(request, form):
|
||||
|
@ -23,15 +23,14 @@ def submit_form(request, form):
|
|||
form_error = False
|
||||
data = form.cleaned_data
|
||||
|
||||
subject = FRAUD_REPORT_EMAIL_SUBJECT % (data['input_url'],
|
||||
data['input_category'])
|
||||
subject = FRAUD_REPORT_EMAIL_SUBJECT % (data["input_url"], data["input_category"])
|
||||
sender = FRAUD_REPORT_EMAIL_FROM
|
||||
to = FRAUD_REPORT_EMAIL_TO
|
||||
msg = render_to_string('legal/emails/fraud-report.txt', data, request=request)
|
||||
msg = render_to_string("legal/emails/fraud-report.txt", data, request=request)
|
||||
|
||||
email = EmailMessage(subject, msg, sender, to)
|
||||
|
||||
attachment = data['input_attachment']
|
||||
attachment = data["input_attachment"]
|
||||
|
||||
if attachment:
|
||||
email.attach(attachment.name, attachment.read(), attachment.content_type)
|
||||
|
@ -40,40 +39,40 @@ def submit_form(request, form):
|
|||
else:
|
||||
form_error = True
|
||||
|
||||
return {'form_submitted': form_submitted, 'form_error': form_error}
|
||||
return {"form_submitted": form_submitted, "form_error": form_error}
|
||||
|
||||
|
||||
@csrf_protect
|
||||
def fraud_report(request):
|
||||
form = FraudReportForm(auto_id='%s')
|
||||
form = FraudReportForm(auto_id="%s")
|
||||
|
||||
form_submitted = False
|
||||
form_error = False
|
||||
|
||||
if request.method == 'POST':
|
||||
if request.method == "POST":
|
||||
form = FraudReportForm(request.POST, request.FILES)
|
||||
form_results = submit_form(request, form)
|
||||
|
||||
form_submitted = form_results['form_submitted']
|
||||
form_error = form_results['form_error']
|
||||
form_submitted = form_results["form_submitted"]
|
||||
form_error = form_results["form_error"]
|
||||
|
||||
template_vars = {
|
||||
'form': form,
|
||||
'form_submitted': form_submitted,
|
||||
'form_error': form_error,
|
||||
"form": form,
|
||||
"form_submitted": form_submitted,
|
||||
"form_error": form_error,
|
||||
}
|
||||
|
||||
if request.POST and not form_error:
|
||||
# Seeing the form was submitted without error, redirect, do not simply
|
||||
# send a response to avoid problem described below.
|
||||
# @see https://bugzilla.mozilla.org/show_bug.cgi?id=873476 (3.2)
|
||||
response = redirect(reverse('legal.fraud-report'), template_vars)
|
||||
response['Location'] += '?submitted=%s' % form_submitted
|
||||
response = redirect(reverse("legal.fraud-report"), template_vars)
|
||||
response["Location"] += "?submitted=%s" % form_submitted
|
||||
|
||||
return response
|
||||
else:
|
||||
# If the below is called after a redirect the template_vars will be lost, therefore
|
||||
# we need to update the form_submitted state from the submitted url parameter.
|
||||
submitted = request.GET.get('submitted') == 'True'
|
||||
template_vars['form_submitted'] = submitted
|
||||
return l10n_utils.render(request, 'legal/fraud-report.html', template_vars)
|
||||
submitted = request.GET.get("submitted") == "True"
|
||||
template_vars["form_submitted"] = submitted
|
||||
return l10n_utils.render(request, "legal/fraud-report.html", template_vars)
|
||||
|
|
|
@ -10,10 +10,8 @@ from bedrock.legal_docs.models import LegalDoc
|
|||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from git.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git."),
|
||||
|
||||
def output(self, msg):
|
||||
if not self.quiet:
|
||||
|
@ -24,28 +22,25 @@ class Command(BaseCommand):
|
|||
requests.get(settings.LEGAL_DOCS_DMS_URL)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.quiet = options['quiet']
|
||||
repo = GitRepo(settings.LEGAL_DOCS_PATH,
|
||||
settings.LEGAL_DOCS_REPO,
|
||||
branch_name=settings.LEGAL_DOCS_BRANCH,
|
||||
name='Legal Docs')
|
||||
self.output('Updating git repo')
|
||||
self.quiet = options["quiet"]
|
||||
repo = GitRepo(settings.LEGAL_DOCS_PATH, settings.LEGAL_DOCS_REPO, branch_name=settings.LEGAL_DOCS_BRANCH, name="Legal Docs")
|
||||
self.output("Updating git repo")
|
||||
repo.update()
|
||||
if not (options['force'] or repo.has_changes()):
|
||||
self.output('No legal docs updates')
|
||||
if not (options["force"] or repo.has_changes()):
|
||||
self.output("No legal docs updates")
|
||||
self.snitch()
|
||||
return
|
||||
|
||||
self.output('Loading legal docs into database')
|
||||
self.output("Loading legal docs into database")
|
||||
count, errors = LegalDoc.objects.refresh()
|
||||
self.output(f'{count} legal docs successfully loaded')
|
||||
self.output(f"{count} legal docs successfully loaded")
|
||||
if errors:
|
||||
self.output(f'Encountered {errors} errors while loading docs')
|
||||
self.output(f"Encountered {errors} errors while loading docs")
|
||||
else:
|
||||
# only set latest if there are no errors so that it will try the errors again next time
|
||||
# also so that it will fail again and thus not ping the snitch so that we'll be notified
|
||||
repo.set_db_latest()
|
||||
self.output('Saved latest git repo state to database')
|
||||
self.output("Saved latest git repo state to database")
|
||||
self.snitch()
|
||||
|
||||
self.output('Done!')
|
||||
self.output("Done!")
|
||||
|
|
|
@ -9,9 +9,9 @@ from mdx_outline import OutlineExtension
|
|||
|
||||
|
||||
LEGAL_DOCS_LOCALES_TO_BEDROCK = {
|
||||
'hi': 'hi-IN',
|
||||
"hi": "hi-IN",
|
||||
}
|
||||
LOCALE_RE = re.compile(r'[a-z]{2,3}(-[A-Z]{2}(_[a-z])?)?$')
|
||||
LOCALE_RE = re.compile(r"[a-z]{2,3}(-[A-Z]{2}(_[a-z])?)?$")
|
||||
|
||||
|
||||
def process_md_file(file_path):
|
||||
|
@ -19,12 +19,11 @@ def process_md_file(file_path):
|
|||
try:
|
||||
# Parse the Markdown file
|
||||
md.markdownFromFile(
|
||||
input=str(file_path), output=output, extensions=[
|
||||
'markdown.extensions.attr_list',
|
||||
'markdown.extensions.toc',
|
||||
OutlineExtension((('wrapper_cls', ''),))
|
||||
])
|
||||
content = output.getvalue().decode('utf-8')
|
||||
input=str(file_path),
|
||||
output=output,
|
||||
extensions=["markdown.extensions.attr_list", "markdown.extensions.toc", OutlineExtension((("wrapper_cls", ""),))],
|
||||
)
|
||||
content = output.getvalue().decode("utf-8")
|
||||
except IOError:
|
||||
content = None
|
||||
finally:
|
||||
|
@ -43,13 +42,13 @@ def get_data_from_file_path(file_path):
|
|||
if locale in LEGAL_DOCS_LOCALES_TO_BEDROCK:
|
||||
locale = LEGAL_DOCS_LOCALES_TO_BEDROCK[locale]
|
||||
return {
|
||||
'locale': locale,
|
||||
'doc_name': doc_name,
|
||||
"locale": locale,
|
||||
"doc_name": doc_name,
|
||||
}
|
||||
|
||||
|
||||
def snake_case(name):
|
||||
return name.lower().replace('-', '_')
|
||||
return name.lower().replace("-", "_")
|
||||
|
||||
|
||||
class LegalDocsManager(models.Manager):
|
||||
|
@ -72,18 +71,18 @@ class LegalDocsManager(models.Manager):
|
|||
doc_name = snake_case(doc_name)
|
||||
doc = self.get(name=doc_name, locale=locale)
|
||||
|
||||
all_locales = list(self.filter(name=doc_name).values_list('locale', flat=True))
|
||||
if 'en' in all_locales:
|
||||
all_locales = list(self.filter(name=doc_name).values_list("locale", flat=True))
|
||||
if "en" in all_locales:
|
||||
# legal-docs now uses en but the site needs en-US
|
||||
all_locales[all_locales.index('en')] = 'en-US'
|
||||
all_locales[all_locales.index("en")] = "en-US"
|
||||
|
||||
# filter locales not active on the site
|
||||
all_locales = [l for l in all_locales if l in settings.PROD_LANGUAGES]
|
||||
|
||||
return {
|
||||
'content': doc.content,
|
||||
"content": doc.content,
|
||||
# sort and make unique
|
||||
'active_locales': sorted(set(all_locales)),
|
||||
"active_locales": sorted(set(all_locales)),
|
||||
}
|
||||
|
||||
def refresh(self):
|
||||
|
@ -92,7 +91,7 @@ class LegalDocsManager(models.Manager):
|
|||
docs_path = settings.LEGAL_DOCS_PATH
|
||||
with transaction.atomic(using=self.db):
|
||||
self.all().delete()
|
||||
doc_files = docs_path.glob('*/*.md')
|
||||
doc_files = docs_path.glob("*/*.md")
|
||||
for docf in doc_files:
|
||||
path_data = get_data_from_file_path(docf)
|
||||
content = process_md_file(docf)
|
||||
|
@ -100,11 +99,13 @@ class LegalDocsManager(models.Manager):
|
|||
errors += 1
|
||||
continue
|
||||
|
||||
doc_objs.append(LegalDoc(
|
||||
name=path_data['doc_name'],
|
||||
locale=path_data['locale'],
|
||||
content=content,
|
||||
))
|
||||
doc_objs.append(
|
||||
LegalDoc(
|
||||
name=path_data["doc_name"],
|
||||
locale=path_data["locale"],
|
||||
content=content,
|
||||
)
|
||||
)
|
||||
self.bulk_create(doc_objs)
|
||||
|
||||
return len(doc_objs), errors
|
||||
|
@ -118,4 +119,4 @@ class LegalDoc(models.Model):
|
|||
objects = LegalDocsManager()
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name} - {self.locale}'
|
||||
return f"{self.name} - {self.locale}"
|
||||
|
|
|
@ -13,182 +13,178 @@ from bedrock.legal_docs.models import LegalDoc, get_data_from_file_path
|
|||
from bedrock.mozorg.tests import TestCase
|
||||
|
||||
|
||||
@override_settings(PROD_LANGUAGES=['en-US', 'de', 'hi-IN'])
|
||||
@override_settings(PROD_LANGUAGES=["en-US", "de", "hi-IN"])
|
||||
class TestLoadLegalDoc(TestCase):
|
||||
def test_legal_doc_not_found(self):
|
||||
"""Missing doc should be None"""
|
||||
doc = views.load_legal_doc('the_dude_is_legal', 'de')
|
||||
doc = views.load_legal_doc("the_dude_is_legal", "de")
|
||||
self.assertIsNone(doc)
|
||||
|
||||
def test_legal_doc_exists(self):
|
||||
"""Should return the content of the en-US file if it exists."""
|
||||
LegalDoc.objects.create(
|
||||
name='the_dude_exists',
|
||||
locale='en-US',
|
||||
name="the_dude_exists",
|
||||
locale="en-US",
|
||||
content="You're not wrong Walter...",
|
||||
)
|
||||
doc = views.load_legal_doc('the_dude_exists', 'de')
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertEqual(doc['active_locales'], ['en-US'])
|
||||
doc = views.load_legal_doc("the_dude_exists", "de")
|
||||
self.assertEqual(doc["content"], "You're not wrong Walter...")
|
||||
self.assertEqual(doc["active_locales"], ["en-US"])
|
||||
|
||||
def test_legal_doc_exists_en_locale(self):
|
||||
"""Should return the content of the en file and say it's en-US."""
|
||||
LegalDoc.objects.create(
|
||||
name='the_dude_exists',
|
||||
locale='en',
|
||||
name="the_dude_exists",
|
||||
locale="en",
|
||||
content="You're not wrong Walter...",
|
||||
)
|
||||
doc = views.load_legal_doc('the_dude_exists', 'en-US')
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertEqual(doc['active_locales'], ['en-US'])
|
||||
doc = views.load_legal_doc("the_dude_exists", "en-US")
|
||||
self.assertEqual(doc["content"], "You're not wrong Walter...")
|
||||
self.assertEqual(doc["active_locales"], ["en-US"])
|
||||
|
||||
def test_legal_doc_exists_snake_case_convert(self):
|
||||
"""Should return the content of the file if it exists in snake case."""
|
||||
LegalDoc.objects.create(
|
||||
name='the_dude_exists',
|
||||
locale='en-US',
|
||||
name="the_dude_exists",
|
||||
locale="en-US",
|
||||
content="You're not wrong Walter...",
|
||||
)
|
||||
doc = views.load_legal_doc('The-Dude-Exists', 'de')
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertEqual(doc['active_locales'], ['en-US'])
|
||||
doc = views.load_legal_doc("The-Dude-Exists", "de")
|
||||
self.assertEqual(doc["content"], "You're not wrong Walter...")
|
||||
self.assertEqual(doc["active_locales"], ["en-US"])
|
||||
|
||||
def test_localized_legal_doc_exists(self):
|
||||
"""Localization works, and list of translations doesn't include non .md files and non-prod locales."""
|
||||
LegalDoc.objects.create(
|
||||
name='the_dude_exists',
|
||||
locale='en',
|
||||
name="the_dude_exists",
|
||||
locale="en",
|
||||
content="You're not wrong Walter...",
|
||||
)
|
||||
LegalDoc.objects.create(
|
||||
name='the_dude_exists',
|
||||
locale='de',
|
||||
name="the_dude_exists",
|
||||
locale="de",
|
||||
content="You're in German Walter...",
|
||||
)
|
||||
doc = views.load_legal_doc('the_dude_exists', 'de')
|
||||
self.assertEqual(doc['content'], "You're in German Walter...")
|
||||
self.assertEqual(set(doc['active_locales']), {'de', 'en-US'})
|
||||
doc = views.load_legal_doc("the_dude_exists", "de")
|
||||
self.assertEqual(doc["content"], "You're in German Walter...")
|
||||
self.assertEqual(set(doc["active_locales"]), {"de", "en-US"})
|
||||
|
||||
|
||||
class TestLegalDocView(TestCase):
|
||||
@patch.object(views, 'load_legal_doc')
|
||||
@patch.object(views, "load_legal_doc")
|
||||
def test_missing_doc_is_404(self, lld_mock):
|
||||
lld_mock.return_value = None
|
||||
req = RequestFactory().get('/dude/is/gone/')
|
||||
req.locale = 'de'
|
||||
view = views.LegalDocView.as_view(template_name='base.html',
|
||||
legal_doc_name='the_dude_is_gone')
|
||||
req = RequestFactory().get("/dude/is/gone/")
|
||||
req.locale = "de"
|
||||
view = views.LegalDocView.as_view(template_name="base.html", legal_doc_name="the_dude_is_gone")
|
||||
with self.assertRaises(Http404):
|
||||
view(req)
|
||||
|
||||
lld_mock.assert_called_with('the_dude_is_gone', 'de')
|
||||
lld_mock.assert_called_with("the_dude_is_gone", "de")
|
||||
|
||||
@patch.object(views, 'load_legal_doc')
|
||||
@patch.object(views.l10n_utils, 'render')
|
||||
@patch.object(views, "load_legal_doc")
|
||||
@patch.object(views.l10n_utils, "render")
|
||||
def test_good_doc_okay(self, render_mock, lld_mock):
|
||||
"""Should render correct thing when all is well"""
|
||||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'active_locales': ['de', 'en-US'],
|
||||
"content": doc_value,
|
||||
"active_locales": ["de", "en-US"],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
req = RequestFactory().get('/dude/exists/')
|
||||
req.locale = 'de'
|
||||
view = views.LegalDocView.as_view(template_name='base.html',
|
||||
legal_doc_name='the_dude_exists')
|
||||
req = RequestFactory().get("/dude/exists/")
|
||||
req.locale = "de"
|
||||
view = views.LegalDocView.as_view(template_name="base.html", legal_doc_name="the_dude_exists")
|
||||
resp = view(req)
|
||||
assert resp['cache-control'] == 'max-age={0!s}'.format(views.CACHE_TIMEOUT)
|
||||
assert resp.content.decode('utf-8') == doc_value
|
||||
assert render_mock.call_args[0][2]['doc'] == doc_value
|
||||
lld_mock.assert_called_with('the_dude_exists', 'de')
|
||||
assert resp["cache-control"] == "max-age={0!s}".format(views.CACHE_TIMEOUT)
|
||||
assert resp.content.decode("utf-8") == doc_value
|
||||
assert render_mock.call_args[0][2]["doc"] == doc_value
|
||||
lld_mock.assert_called_with("the_dude_exists", "de")
|
||||
|
||||
@patch.object(views, 'load_legal_doc')
|
||||
@patch.object(views.l10n_utils, 'render')
|
||||
@patch.object(views, "load_legal_doc")
|
||||
@patch.object(views.l10n_utils, "render")
|
||||
def test_cache_settings(self, render_mock, lld_mock):
|
||||
"""Should use the cache_timeout value from view."""
|
||||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'active_locales': ['es-ES', 'en-US'],
|
||||
"content": doc_value,
|
||||
"active_locales": ["es-ES", "en-US"],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
req = RequestFactory().get('/dude/exists/cached/')
|
||||
req.locale = 'es-ES'
|
||||
view = views.LegalDocView.as_view(template_name='base.html',
|
||||
legal_doc_name='the_dude_exists',
|
||||
cache_timeout=10)
|
||||
req = RequestFactory().get("/dude/exists/cached/")
|
||||
req.locale = "es-ES"
|
||||
view = views.LegalDocView.as_view(template_name="base.html", legal_doc_name="the_dude_exists", cache_timeout=10)
|
||||
resp = view(req)
|
||||
assert resp['cache-control'] == 'max-age=10'
|
||||
assert resp["cache-control"] == "max-age=10"
|
||||
|
||||
@patch.object(views, 'load_legal_doc')
|
||||
@patch.object(views.l10n_utils, 'render')
|
||||
@patch.object(views, "load_legal_doc")
|
||||
@patch.object(views.l10n_utils, "render")
|
||||
def test_cache_class_attrs(self, render_mock, lld_mock):
|
||||
"""Should use the cache_timeout value from view class."""
|
||||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'active_locales': ['es-ES', 'en-US'],
|
||||
"content": doc_value,
|
||||
"active_locales": ["es-ES", "en-US"],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
req = RequestFactory().get('/dude/exists/cached/2/')
|
||||
req.locale = 'es-ES'
|
||||
req = RequestFactory().get("/dude/exists/cached/2/")
|
||||
req.locale = "es-ES"
|
||||
|
||||
class DocTestView(views.LegalDocView):
|
||||
cache_timeout = 20
|
||||
template_name = 'base.html'
|
||||
legal_doc_name = 'the_dude_abides'
|
||||
template_name = "base.html"
|
||||
legal_doc_name = "the_dude_abides"
|
||||
|
||||
view = DocTestView.as_view()
|
||||
resp = view(req)
|
||||
assert resp['cache-control'] == 'max-age=20'
|
||||
lld_mock.assert_called_with('the_dude_abides', 'es-ES')
|
||||
assert resp["cache-control"] == "max-age=20"
|
||||
lld_mock.assert_called_with("the_dude_abides", "es-ES")
|
||||
|
||||
|
||||
class TestFilePathData(TestCase):
|
||||
def test_legacy_repo_layout(self):
|
||||
path = Path('/repo/data/legal_docs/websites_privacy_notice/en-US.md')
|
||||
path = Path("/repo/data/legal_docs/websites_privacy_notice/en-US.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'en-US',
|
||||
'doc_name': 'websites_privacy_notice',
|
||||
"locale": "en-US",
|
||||
"doc_name": "websites_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/websites_privacy_notice/de.md')
|
||||
path = Path("/repo/data/legal_docs/websites_privacy_notice/de.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'de',
|
||||
'doc_name': 'websites_privacy_notice',
|
||||
"locale": "de",
|
||||
"doc_name": "websites_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/firefox_privacy_notice/es-ES_b.md')
|
||||
path = Path("/repo/data/legal_docs/firefox_privacy_notice/es-ES_b.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'es-ES_b',
|
||||
'doc_name': 'firefox_privacy_notice',
|
||||
"locale": "es-ES_b",
|
||||
"doc_name": "firefox_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/WebRTC_ToS/cnh.md')
|
||||
path = Path("/repo/data/legal_docs/WebRTC_ToS/cnh.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'cnh',
|
||||
'doc_name': 'WebRTC_ToS',
|
||||
"locale": "cnh",
|
||||
"doc_name": "WebRTC_ToS",
|
||||
}
|
||||
|
||||
def test_new_repo_layout(self):
|
||||
path = Path('/repo/data/legal_docs/en-US/websites_privacy_notice.md')
|
||||
path = Path("/repo/data/legal_docs/en-US/websites_privacy_notice.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'en-US',
|
||||
'doc_name': 'websites_privacy_notice',
|
||||
"locale": "en-US",
|
||||
"doc_name": "websites_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/de/websites_privacy_notice.md')
|
||||
path = Path("/repo/data/legal_docs/de/websites_privacy_notice.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'de',
|
||||
'doc_name': 'websites_privacy_notice',
|
||||
"locale": "de",
|
||||
"doc_name": "websites_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/es-ES_b/firefox_privacy_notice.md')
|
||||
path = Path("/repo/data/legal_docs/es-ES_b/firefox_privacy_notice.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'es-ES_b',
|
||||
'doc_name': 'firefox_privacy_notice',
|
||||
"locale": "es-ES_b",
|
||||
"doc_name": "firefox_privacy_notice",
|
||||
}
|
||||
path = Path('/repo/data/legal_docs/cnh/WebRTC_ToS.md')
|
||||
path = Path("/repo/data/legal_docs/cnh/WebRTC_ToS.md")
|
||||
assert get_data_from_file_path(path) == {
|
||||
'locale': 'cnh',
|
||||
'doc_name': 'WebRTC_ToS',
|
||||
"locale": "cnh",
|
||||
"doc_name": "WebRTC_ToS",
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ def load_legal_doc(doc_name, locale):
|
|||
doc = LegalDoc.objects.get_doc(doc_name, locale)
|
||||
except LegalDoc.DoesNotExist:
|
||||
try:
|
||||
doc = LegalDoc.objects.get_doc(doc_name, 'en')
|
||||
doc = LegalDoc.objects.get_doc(doc_name, "en")
|
||||
except LegalDoc.DoesNotExist:
|
||||
try:
|
||||
doc = LegalDoc.objects.get_doc(doc_name, 'en-US')
|
||||
doc = LegalDoc.objects.get_doc(doc_name, "en-US")
|
||||
except LegalDoc.DoesNotExist:
|
||||
doc = None
|
||||
|
||||
|
@ -53,8 +53,9 @@ class LegalDocView(TemplateView):
|
|||
|
||||
See `bedrock/privacy/views.py` for usage examples.
|
||||
"""
|
||||
|
||||
legal_doc_name = None
|
||||
legal_doc_context_name = 'doc'
|
||||
legal_doc_context_name = "doc"
|
||||
cache_timeout = CACHE_TIMEOUT
|
||||
|
||||
def get_legal_doc(self):
|
||||
|
@ -62,23 +63,22 @@ class LegalDocView(TemplateView):
|
|||
return load_legal_doc(self.legal_doc_name, locale)
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
response_kwargs.setdefault('content_type', self.content_type)
|
||||
return l10n_utils.render(self.request,
|
||||
self.get_template_names()[0],
|
||||
context, ftl_files=['mozorg/about/legal', 'privacy/index'],
|
||||
**response_kwargs)
|
||||
response_kwargs.setdefault("content_type", self.content_type)
|
||||
return l10n_utils.render(
|
||||
self.request, self.get_template_names()[0], context, ftl_files=["mozorg/about/legal", "privacy/index"], **response_kwargs
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
legal_doc = self.get_legal_doc()
|
||||
if legal_doc is None:
|
||||
raise Http404('Legal doc not found')
|
||||
raise Http404("Legal doc not found")
|
||||
|
||||
context = super(LegalDocView, self).get_context_data(**kwargs)
|
||||
context[self.legal_doc_context_name] = legal_doc['content']
|
||||
context['active_locales'] = legal_doc['active_locales']
|
||||
context[self.legal_doc_context_name] = legal_doc["content"]
|
||||
context["active_locales"] = legal_doc["active_locales"]
|
||||
return context
|
||||
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
cache_timeout = initkwargs.pop('cache_timeout', cls.cache_timeout)
|
||||
cache_timeout = initkwargs.pop("cache_timeout", cls.cache_timeout)
|
||||
return cache_page(cache_timeout)(super(LegalDocView, cls).as_view(**initkwargs))
|
||||
|
|
|
@ -13,7 +13,7 @@ from lib.l10n_utils import get_locale
|
|||
|
||||
|
||||
# match 1 - 4 digits only
|
||||
FC_RE = re.compile(r'^\d{1,4}$')
|
||||
FC_RE = re.compile(r"^\d{1,4}$")
|
||||
|
||||
|
||||
def canonical_path(request):
|
||||
|
@ -21,9 +21,9 @@ def canonical_path(request):
|
|||
The canonical path can be overridden with a template variable like
|
||||
l10n_utils.render(request, template_name, {'canonical_path': '/firefox/'})
|
||||
"""
|
||||
lang = getattr(request, 'locale', settings.LANGUAGE_CODE)
|
||||
url = getattr(request, 'path', '/')
|
||||
return {'canonical_path': re.sub(r'^/' + lang, '', url)}
|
||||
lang = getattr(request, "locale", settings.LANGUAGE_CODE)
|
||||
url = getattr(request, "path", "/")
|
||||
return {"canonical_path": re.sub(r"^/" + lang, "", url)}
|
||||
|
||||
|
||||
def current_year(request):
|
||||
|
@ -32,22 +32,22 @@ def current_year(request):
|
|||
|
||||
def funnelcake_param(request):
|
||||
"""If a query param for a funnelcake is sent, add it to the context."""
|
||||
fc_id = request.GET.get('f', None)
|
||||
fc_id = request.GET.get("f", None)
|
||||
context = {}
|
||||
|
||||
if fc_id and FC_RE.match(fc_id):
|
||||
# special case for installer-help page
|
||||
# bug 933852
|
||||
installer_help_url = reverse('firefox.installer-help')
|
||||
installer_help_url = reverse("firefox.installer-help")
|
||||
if installer_help_url in request.path_info:
|
||||
fc_id = str(int(fc_id) + 1)
|
||||
context['funnelcake_id'] = fc_id
|
||||
context["funnelcake_id"] = fc_id
|
||||
|
||||
return context
|
||||
|
||||
|
||||
def facebook_locale(request):
|
||||
return {'facebook_locale': get_fb_like_locale(get_locale(request))}
|
||||
return {"facebook_locale": get_fb_like_locale(get_locale(request))}
|
||||
|
||||
|
||||
def contrib_numbers(request):
|
||||
|
|
|
@ -13,12 +13,12 @@ from bedrock.externalfiles import ExternalFile
|
|||
|
||||
class CreditsFile(ExternalFile):
|
||||
def validate_content(self, content):
|
||||
rows = list(csv.reader(content.strip().split('\n')))
|
||||
rows = list(csv.reader(content.strip().split("\n")))
|
||||
if len(rows) < 2200: # it's 2273 as of now
|
||||
raise ValueError('Much smaller file than expected. {0} rows.'.format(len(rows)))
|
||||
raise ValueError("Much smaller file than expected. {0} rows.".format(len(rows)))
|
||||
|
||||
if len(rows[0]) != 2 or len(rows[-1]) != 2:
|
||||
raise ValueError('CSV Content corrupted.')
|
||||
raise ValueError("CSV Content corrupted.")
|
||||
|
||||
return content
|
||||
|
||||
|
@ -57,7 +57,7 @@ class CreditsFile(ExternalFile):
|
|||
else:
|
||||
continue
|
||||
|
||||
sortkey = unicodedata.normalize('NFKD', sortkey).encode('ascii', 'ignore').decode()
|
||||
sortkey = unicodedata.normalize("NFKD", sortkey).encode("ascii", "ignore").decode()
|
||||
names.append([name, sortkey.upper()])
|
||||
|
||||
return sorted(names, key=itemgetter(1))
|
||||
|
|
|
@ -15,11 +15,12 @@ def cache_control_expires(num_hours):
|
|||
num_seconds = int(num_hours * 60 * 60)
|
||||
|
||||
def decorator(func):
|
||||
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
response = func(request, *args, **kwargs)
|
||||
patch_response_headers(response, num_seconds)
|
||||
return response
|
||||
|
||||
return inner
|
||||
|
||||
return decorator
|
||||
|
|
|
@ -15,16 +15,16 @@ from django.utils.safestring import mark_safe
|
|||
from lib.l10n_utils.fluent import ftl, ftl_lazy
|
||||
|
||||
|
||||
FORMATS = (('H', ftl_lazy('newsletter-form-html')), ('T', ftl_lazy('newsletter-form-text')))
|
||||
LANGS_TO_STRIP = ['en-US', 'es']
|
||||
PARENTHETIC_RE = re.compile(r' \([^)]+\)$')
|
||||
FORMATS = (("H", ftl_lazy("newsletter-form-html")), ("T", ftl_lazy("newsletter-form-text")))
|
||||
LANGS_TO_STRIP = ["en-US", "es"]
|
||||
PARENTHETIC_RE = re.compile(r" \([^)]+\)$")
|
||||
|
||||
|
||||
def strip_parenthetical(lang_name):
|
||||
"""
|
||||
Remove the parenthetical from the end of the language name string.
|
||||
"""
|
||||
return PARENTHETIC_RE.sub('', lang_name, 1)
|
||||
return PARENTHETIC_RE.sub("", lang_name, 1)
|
||||
|
||||
|
||||
class PrivacyWidget(widgets.CheckboxInput):
|
||||
|
@ -32,17 +32,13 @@ class PrivacyWidget(widgets.CheckboxInput):
|
|||
it should be standardized"""
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
attrs['required'] = 'required'
|
||||
attrs["required"] = "required"
|
||||
input_txt = super(PrivacyWidget, self).render(name, value, attrs)
|
||||
|
||||
policy_txt = ftl('newsletter-form-im-okay-with-mozilla',
|
||||
url=reverse('privacy.notices.websites'))
|
||||
policy_txt = ftl("newsletter-form-im-okay-with-mozilla", url=reverse("privacy.notices.websites"))
|
||||
|
||||
return mark_safe(
|
||||
'<label for="%s" class="privacy-check-label">'
|
||||
'%s '
|
||||
'<span class="title">%s</span></label>'
|
||||
% (attrs['id'], input_txt, policy_txt)
|
||||
'<label for="%s" class="privacy-check-label">' "%s " '<span class="title">%s</span></label>' % (attrs["id"], input_txt, policy_txt)
|
||||
)
|
||||
|
||||
|
||||
|
@ -50,36 +46,37 @@ class HoneyPotWidget(widgets.TextInput):
|
|||
"""Render a text field to (hopefully) trick bots. Will be used on many pages."""
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
honeypot_txt = ftl('newsletter-form-leave-this-field-empty')
|
||||
honeypot_txt = ftl("newsletter-form-leave-this-field-empty")
|
||||
# semi-randomized in case we have more than one per page.
|
||||
# this is maybe/probably overthought
|
||||
honeypot_id = 'office-fax-' + str(randrange(1001)) + '-' + str(datetime.now().strftime("%Y%m%d%H%M%S%f"))
|
||||
honeypot_id = "office-fax-" + str(randrange(1001)) + "-" + str(datetime.now().strftime("%Y%m%d%H%M%S%f"))
|
||||
return mark_safe(
|
||||
'<div class="super-priority-field">'
|
||||
'<label for="%s">%s</label>'
|
||||
'<input type="text" name="office_fax" id="%s">'
|
||||
'</div>' % (honeypot_id, honeypot_txt, honeypot_id))
|
||||
"</div>" % (honeypot_id, honeypot_txt, honeypot_id)
|
||||
)
|
||||
|
||||
|
||||
class URLInput(widgets.TextInput):
|
||||
input_type = 'url'
|
||||
input_type = "url"
|
||||
|
||||
|
||||
class EmailInput(widgets.TextInput):
|
||||
input_type = 'email'
|
||||
input_type = "email"
|
||||
|
||||
|
||||
class DateInput(widgets.DateInput):
|
||||
input_type = 'date'
|
||||
input_type = "date"
|
||||
|
||||
|
||||
class TimeInput(widgets.TimeInput):
|
||||
input_type = 'time'
|
||||
input_type = "time"
|
||||
|
||||
|
||||
class TelInput(widgets.TextInput):
|
||||
input_type = 'tel'
|
||||
input_type = "tel"
|
||||
|
||||
|
||||
class NumberInput(widgets.TextInput):
|
||||
input_type = 'number'
|
||||
input_type = "number"
|
||||
|
|
|
@ -27,6 +27,7 @@ class PageNode:
|
|||
In the example above, the template `child1.html` will be available at the
|
||||
url `/root/child1/`.
|
||||
"""
|
||||
|
||||
def __init__(self, display_name, path=None, template=None, children=None):
|
||||
"""
|
||||
Create a new PageNode.
|
||||
|
@ -59,15 +60,13 @@ class PageNode:
|
|||
The full url path for this node, including the paths of its parent
|
||||
nodes.
|
||||
"""
|
||||
return '/'.join([node.path for node in self.breadcrumbs
|
||||
if node.path is not None])
|
||||
return "/".join([node.path for node in self.breadcrumbs if node.path is not None])
|
||||
|
||||
@property
|
||||
def page(self):
|
||||
"""The page for this node, which is a RegexURLPattern."""
|
||||
if self.template:
|
||||
return page(self.full_path, self.template, node_root=self.root,
|
||||
node=self)
|
||||
return page(self.full_path, self.template, node_root=self.root, node=self)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -96,7 +95,7 @@ class PageNode:
|
|||
"""The root of the tree that this node is in."""
|
||||
root = list(self.path_to_root)[-1]
|
||||
if not isinstance(root, PageRoot):
|
||||
raise ValueError('Root node is not a PageRoot object.')
|
||||
raise ValueError("Root node is not a PageRoot object.")
|
||||
return root
|
||||
|
||||
@property
|
||||
|
@ -127,9 +126,7 @@ class PageNode:
|
|||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return u'{0}(display_name="{1}", path="{2}", template="{3})"'.format(
|
||||
self.__class__.__name__, self.display_name, self.full_path,
|
||||
self.template)
|
||||
return '{0}(display_name="{1}", path="{2}", template="{3})"'.format(self.__class__.__name__, self.display_name, self.full_path, self.template)
|
||||
|
||||
|
||||
class PageRoot(PageNode):
|
||||
|
@ -139,6 +136,7 @@ class PageRoot(PageNode):
|
|||
The root node of a PageNode tree MUST be a PageRoot. If it is not, any
|
||||
reference to the root of the tree with throw a ValueError.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PageRoot, self).__init__(*args, **kwargs)
|
||||
|
||||
|
@ -166,5 +164,4 @@ class PageRoot(PageNode):
|
|||
|
||||
def as_urlpatterns(self):
|
||||
"""Return a urlconf for this PageRoot and its children."""
|
||||
return [
|
||||
node.page for node in self.preordered_nodes if node.template]
|
||||
return [node.page for node in self.preordered_nodes if node.template]
|
||||
|
|
|
@ -9,13 +9,13 @@ from product_details.storage import PDDatabaseStorage, PDFileStorage
|
|||
from bedrock.utils.git import GitRepo
|
||||
|
||||
FIREFOX_VERSION_KEYS = (
|
||||
'FIREFOX_NIGHTLY',
|
||||
'FIREFOX_DEVEDITION',
|
||||
'FIREFOX_ESR',
|
||||
'FIREFOX_ESR_NEXT',
|
||||
'LATEST_FIREFOX_DEVEL_VERSION',
|
||||
'LATEST_FIREFOX_RELEASED_DEVEL_VERSION',
|
||||
'LATEST_FIREFOX_VERSION',
|
||||
"FIREFOX_NIGHTLY",
|
||||
"FIREFOX_DEVEDITION",
|
||||
"FIREFOX_ESR",
|
||||
"FIREFOX_ESR_NEXT",
|
||||
"LATEST_FIREFOX_DEVEL_VERSION",
|
||||
"LATEST_FIREFOX_RELEASED_DEVEL_VERSION",
|
||||
"LATEST_FIREFOX_VERSION",
|
||||
)
|
||||
|
||||
|
||||
|
@ -23,79 +23,70 @@ class Command(BaseCommand):
|
|||
def __init__(self, stdout=None, stderr=None, no_color=False):
|
||||
self.file_storage = PDFileStorage(json_dir=settings.PROD_DETAILS_TEST_DIR)
|
||||
self.db_storage = PDDatabaseStorage()
|
||||
self.repo = GitRepo(settings.PROD_DETAILS_JSON_REPO_PATH,
|
||||
settings.PROD_DETAILS_JSON_REPO_URI,
|
||||
settings.PROD_DETAILS_JSON_REPO_BRANCH,
|
||||
name='Product Details')
|
||||
self.repo = GitRepo(
|
||||
settings.PROD_DETAILS_JSON_REPO_PATH, settings.PROD_DETAILS_JSON_REPO_URI, settings.PROD_DETAILS_JSON_REPO_BRANCH, name="Product Details"
|
||||
)
|
||||
# fake last-modified string since the releng repo doesn't store those files
|
||||
# and we rely on git commits for updates
|
||||
self.last_modified = datetime.now().isoformat()
|
||||
super(Command, self).__init__(stdout, stderr, no_color)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument('--database', default='default',
|
||||
help=('Specifies the database to use, if using a db. '
|
||||
'Defaults to "default".')),
|
||||
parser.add_argument('-f', '--force', action='store_true', dest='force', default=False,
|
||||
help='Load the data even if nothing new from git.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
parser.add_argument("--database", default="default", help=("Specifies the database to use, if using a db. " 'Defaults to "default".')),
|
||||
parser.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="Load the data even if nothing new from git."),
|
||||
|
||||
def handle(self, *args, **options):
|
||||
# don't really care about deleted files. almost never happens in p-d.
|
||||
if not (self.update_file_data() or options['force']):
|
||||
if not options['quiet']:
|
||||
print('Product Details data was already up to date')
|
||||
if not (self.update_file_data() or options["force"]):
|
||||
if not options["quiet"]:
|
||||
print("Product Details data was already up to date")
|
||||
return
|
||||
|
||||
try:
|
||||
self.validate_data()
|
||||
except Exception:
|
||||
raise CommandError('Product Details data is invalid')
|
||||
raise CommandError("Product Details data is invalid")
|
||||
|
||||
if not options['quiet']:
|
||||
print('Product Details data is valid')
|
||||
if not options["quiet"]:
|
||||
print("Product Details data is valid")
|
||||
|
||||
if not settings.PROD_DETAILS_STORAGE.endswith('PDDatabaseStorage'):
|
||||
if not settings.PROD_DETAILS_STORAGE.endswith("PDDatabaseStorage"):
|
||||
# no need to continue if not using DB backend
|
||||
return
|
||||
|
||||
self.load_changes(options, self.file_storage.all_json_files())
|
||||
self.repo.set_db_latest()
|
||||
|
||||
if not options['quiet']:
|
||||
print('Product Details data update is complete')
|
||||
if not options["quiet"]:
|
||||
print("Product Details data update is complete")
|
||||
|
||||
def load_changes(self, options, modified_files):
|
||||
with transaction.atomic(using=options['database']):
|
||||
with transaction.atomic(using=options["database"]):
|
||||
for filename in modified_files:
|
||||
# skip the l10n directory for now
|
||||
if filename.startswith('l10n/'):
|
||||
if filename.startswith("l10n/"):
|
||||
continue
|
||||
|
||||
self.db_storage.update(filename,
|
||||
self.file_storage.content(filename),
|
||||
self.last_modified)
|
||||
if not options['quiet']:
|
||||
print('Updated ' + filename)
|
||||
self.db_storage.update(filename, self.file_storage.content(filename), self.last_modified)
|
||||
if not options["quiet"]:
|
||||
print("Updated " + filename)
|
||||
|
||||
self.db_storage.update('/', '', self.last_modified)
|
||||
self.db_storage.update('regions/', '', self.last_modified)
|
||||
self.db_storage.update("/", "", self.last_modified)
|
||||
self.db_storage.update("regions/", "", self.last_modified)
|
||||
|
||||
def update_file_data(self):
|
||||
self.repo.update()
|
||||
return self.repo.has_changes()
|
||||
|
||||
def count_builds(self, version_key, min_builds=20):
|
||||
version = self.file_storage.data('firefox_versions.json')[version_key]
|
||||
version = self.file_storage.data("firefox_versions.json")[version_key]
|
||||
if not version:
|
||||
if version_key == 'FIREFOX_ESR_NEXT':
|
||||
if version_key == "FIREFOX_ESR_NEXT":
|
||||
return
|
||||
builds = len([locale for locale, build in
|
||||
self.file_storage.data('firefox_primary_builds.json').items()
|
||||
if version in build])
|
||||
builds = len([locale for locale, build in self.file_storage.data("firefox_primary_builds.json").items() if version in build])
|
||||
if builds < min_builds:
|
||||
raise ValueError('Too few builds for {}'.format(version_key))
|
||||
raise ValueError("Too few builds for {}".format(version_key))
|
||||
|
||||
def validate_data(self):
|
||||
self.file_storage.clear_cache()
|
||||
|
|
|
@ -12,7 +12,6 @@ from django.utils.cache import add_never_cache_headers
|
|||
|
||||
|
||||
class CacheMiddleware:
|
||||
|
||||
def __init__(self, get_response=None):
|
||||
self.get_response = get_response
|
||||
|
||||
|
@ -21,16 +20,13 @@ class CacheMiddleware:
|
|||
return self.process_response(request, response)
|
||||
|
||||
def process_response(self, request, response):
|
||||
cache = (request.method != 'POST'
|
||||
and response.status_code != 404
|
||||
and 'Cache-Control' not in response)
|
||||
cache = request.method != "POST" and response.status_code != 404 and "Cache-Control" not in response
|
||||
if cache:
|
||||
d = datetime.datetime.now() + datetime.timedelta(minutes=10)
|
||||
stamp = time.mktime(d.timetuple())
|
||||
|
||||
response['Cache-Control'] = 'max-age=600'
|
||||
response['Expires'] = formatdate(timeval=stamp, localtime=False,
|
||||
usegmt=True)
|
||||
response["Cache-Control"] = "max-age=600"
|
||||
response["Expires"] = formatdate(timeval=stamp, localtime=False, usegmt=True)
|
||||
return response
|
||||
|
||||
|
||||
|
@ -47,7 +43,7 @@ class ClacksOverheadMiddleware:
|
|||
@staticmethod
|
||||
def process_response(request, response):
|
||||
if response.status_code == 200:
|
||||
response['X-Clacks-Overhead'] = 'GNU Terry Pratchett'
|
||||
response["X-Clacks-Overhead"] = "GNU Terry Pratchett"
|
||||
return response
|
||||
|
||||
|
||||
|
@ -56,8 +52,8 @@ class HostnameMiddleware:
|
|||
if not settings.ENABLE_HOSTNAME_MIDDLEWARE:
|
||||
raise MiddlewareNotUsed
|
||||
|
||||
values = [getattr(settings, x) for x in ['HOSTNAME', 'CLUSTER_NAME']]
|
||||
self.backend_server = '.'.join(x for x in values if x)
|
||||
values = [getattr(settings, x) for x in ["HOSTNAME", "CLUSTER_NAME"]]
|
||||
self.backend_server = ".".join(x for x in values if x)
|
||||
|
||||
self.get_response = get_response
|
||||
|
||||
|
@ -66,7 +62,7 @@ class HostnameMiddleware:
|
|||
return self.process_response(request, response)
|
||||
|
||||
def process_response(self, request, response):
|
||||
response['X-Backend-Server'] = self.backend_server
|
||||
response["X-Backend-Server"] = self.backend_server
|
||||
return response
|
||||
|
||||
|
||||
|
@ -82,12 +78,11 @@ class VaryNoCacheMiddleware:
|
|||
|
||||
@staticmethod
|
||||
def process_response(request, response):
|
||||
if 'vary' in response:
|
||||
if "vary" in response:
|
||||
path = request.path
|
||||
if path != '/' and not any(path.startswith(x) for x in
|
||||
settings.VARY_NOCACHE_EXEMPT_URL_PREFIXES):
|
||||
del response['vary']
|
||||
del response['expires']
|
||||
if path != "/" and not any(path.startswith(x) for x in settings.VARY_NOCACHE_EXEMPT_URL_PREFIXES):
|
||||
del response["vary"]
|
||||
del response["expires"]
|
||||
add_never_cache_headers(response)
|
||||
|
||||
return response
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,6 +14,7 @@ from django.template.loader import render_to_string
|
|||
from django.utils import six
|
||||
from django.utils.http import urlquote
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
try:
|
||||
from django.utils.encoding import smart_unicode as smart_text
|
||||
except ImportError:
|
||||
|
@ -27,45 +28,45 @@ from bedrock.base.templatetags.helpers import static
|
|||
from bedrock.firefox.firefox_details import firefox_ios
|
||||
|
||||
|
||||
ALL_FX_PLATFORMS = ('windows', 'linux', 'mac', 'android', 'ios')
|
||||
ALL_FX_PLATFORMS = ("windows", "linux", "mac", "android", "ios")
|
||||
|
||||
|
||||
def _strip_img_prefix(url):
|
||||
return re.sub(r'^/?img/', '', url)
|
||||
return re.sub(r"^/?img/", "", url)
|
||||
|
||||
|
||||
def _l10n_media_exists(type, locale, url):
|
||||
""" checks if a localized media file exists for the locale """
|
||||
return find_static(path.join(type, 'l10n', locale, url)) is not None
|
||||
"""checks if a localized media file exists for the locale"""
|
||||
return find_static(path.join(type, "l10n", locale, url)) is not None
|
||||
|
||||
|
||||
def add_string_to_image_url(url, addition):
|
||||
"""Add the platform string to an image url."""
|
||||
filename, ext = splitext(url)
|
||||
return ''.join([filename, '-', addition, ext])
|
||||
return "".join([filename, "-", addition, ext])
|
||||
|
||||
|
||||
def convert_to_high_res(url):
|
||||
"""Convert a file name to the high-resolution version."""
|
||||
return add_string_to_image_url(url, 'high-res')
|
||||
return add_string_to_image_url(url, "high-res")
|
||||
|
||||
|
||||
def l10n_img_file_name(ctx, url):
|
||||
"""Return the filename of the l10n image for use by static()"""
|
||||
url = url.lstrip('/')
|
||||
locale = getattr(ctx['request'], 'locale', None)
|
||||
url = url.lstrip("/")
|
||||
locale = getattr(ctx["request"], "locale", None)
|
||||
if not locale:
|
||||
locale = settings.LANGUAGE_CODE
|
||||
|
||||
# We use the same localized screenshots for all Spanishes
|
||||
if locale.startswith('es') and not _l10n_media_exists('img', locale, url):
|
||||
locale = 'es-ES'
|
||||
if locale.startswith("es") and not _l10n_media_exists("img", locale, url):
|
||||
locale = "es-ES"
|
||||
|
||||
if locale != settings.LANGUAGE_CODE:
|
||||
if not _l10n_media_exists('img', locale, url):
|
||||
if not _l10n_media_exists("img", locale, url):
|
||||
locale = settings.LANGUAGE_CODE
|
||||
|
||||
return path.join('img', 'l10n', locale, url)
|
||||
return path.join("img", "l10n", locale, url)
|
||||
|
||||
|
||||
@library.global_function
|
||||
|
@ -139,13 +140,12 @@ def l10n_css(ctx):
|
|||
$ROOT/media/css/l10n/fr/intl.css
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
if _l10n_media_exists('css', locale, 'intl.css'):
|
||||
markup = ('<link rel="stylesheet" media="screen,projection,tv" href='
|
||||
'"%s">' % static(path.join('css', 'l10n', locale, 'intl.css')))
|
||||
if _l10n_media_exists("css", locale, "intl.css"):
|
||||
markup = '<link rel="stylesheet" media="screen,projection,tv" href=' '"%s">' % static(path.join("css", "l10n", locale, "intl.css"))
|
||||
else:
|
||||
markup = ''
|
||||
markup = ""
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
@ -163,14 +163,14 @@ def field_with_attrs(bfield, **kwargs):
|
|||
def platform_img(ctx, url, optional_attributes=None):
|
||||
optional_attributes = optional_attributes or {}
|
||||
img_urls = {}
|
||||
platforms = optional_attributes.pop('platforms', ALL_FX_PLATFORMS)
|
||||
add_high_res = optional_attributes.pop('high-res', False)
|
||||
is_l10n = optional_attributes.pop('l10n', False)
|
||||
platforms = optional_attributes.pop("platforms", ALL_FX_PLATFORMS)
|
||||
add_high_res = optional_attributes.pop("high-res", False)
|
||||
is_l10n = optional_attributes.pop("l10n", False)
|
||||
|
||||
for platform in platforms:
|
||||
img_urls[platform] = add_string_to_image_url(url, platform)
|
||||
if add_high_res:
|
||||
img_urls[platform + '-high-res'] = convert_to_high_res(img_urls[platform])
|
||||
img_urls[platform + "-high-res"] = convert_to_high_res(img_urls[platform])
|
||||
|
||||
img_attrs = {}
|
||||
for platform, image in img_urls.items():
|
||||
|
@ -178,22 +178,23 @@ def platform_img(ctx, url, optional_attributes=None):
|
|||
image = l10n_img_file_name(ctx, _strip_img_prefix(image))
|
||||
|
||||
if find_static(image):
|
||||
key = 'data-src-' + platform
|
||||
key = "data-src-" + platform
|
||||
img_attrs[key] = static(image)
|
||||
|
||||
if add_high_res:
|
||||
img_attrs['data-high-res'] = 'true'
|
||||
img_attrs["data-high-res"] = "true"
|
||||
|
||||
img_attrs.update(optional_attributes)
|
||||
attrs = ' '.join('%s="%s"' % (attr, val)
|
||||
for attr, val in img_attrs.items())
|
||||
attrs = " ".join('%s="%s"' % (attr, val) for attr, val in img_attrs.items())
|
||||
|
||||
# Don't download any image until the javascript sets it based on
|
||||
# data-src so we can do platform detection. If no js, show the
|
||||
# windows version.
|
||||
markup = ('<img class="platform-img js" src="" data-processed="false" {attrs}>'
|
||||
'<noscript><img class="platform-img win" src="{win_src}" {attrs}>'
|
||||
'</noscript>').format(attrs=attrs, win_src=img_attrs['data-src-windows'])
|
||||
markup = (
|
||||
'<img class="platform-img js" src="" data-processed="false" {attrs}>'
|
||||
'<noscript><img class="platform-img win" src="{win_src}" {attrs}>'
|
||||
"</noscript>"
|
||||
).format(attrs=attrs, win_src=img_attrs["data-src-windows"])
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
@ -201,7 +202,7 @@ def platform_img(ctx, url, optional_attributes=None):
|
|||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def high_res_img(ctx, url, optional_attributes=None):
|
||||
if optional_attributes and optional_attributes.pop('l10n', False) is True:
|
||||
if optional_attributes and optional_attributes.pop("l10n", False) is True:
|
||||
url = _strip_img_prefix(url)
|
||||
url_high_res = convert_to_high_res(url)
|
||||
url = l10n_img(ctx, url)
|
||||
|
@ -212,35 +213,32 @@ def high_res_img(ctx, url, optional_attributes=None):
|
|||
url_high_res = static(url_high_res)
|
||||
|
||||
if optional_attributes:
|
||||
class_name = optional_attributes.pop('class', '')
|
||||
attrs = ' ' + ' '.join('%s="%s"' % (attr, val)
|
||||
for attr, val in optional_attributes.items())
|
||||
class_name = optional_attributes.pop("class", "")
|
||||
attrs = " " + " ".join('%s="%s"' % (attr, val) for attr, val in optional_attributes.items())
|
||||
else:
|
||||
class_name = ''
|
||||
attrs = ''
|
||||
class_name = ""
|
||||
attrs = ""
|
||||
|
||||
# Use native srcset attribute for high res images
|
||||
markup = ('<img class="{class_name}" src="{url}" '
|
||||
'srcset="{url_high_res} 1.5x"'
|
||||
'{attrs}>').format(url=url, url_high_res=url_high_res,
|
||||
attrs=attrs, class_name=class_name)
|
||||
markup = ('<img class="{class_name}" src="{url}" ' 'srcset="{url_high_res} 1.5x"' "{attrs}>").format(
|
||||
url=url, url_high_res=url_high_res, attrs=attrs, class_name=class_name
|
||||
)
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def lazy_img(ctx, image_url, placeholder_url, include_highres_image=False,
|
||||
optional_attributes=None, highres_image_url=None):
|
||||
def lazy_img(ctx, image_url, placeholder_url, include_highres_image=False, optional_attributes=None, highres_image_url=None):
|
||||
placeholder = static(placeholder_url)
|
||||
|
||||
external_img = re.match(r'^https?://', image_url, flags=re.I)
|
||||
external_img = re.match(r"^https?://", image_url, flags=re.I)
|
||||
|
||||
if include_highres_image and not external_img:
|
||||
image_high_res = static(convert_to_high_res(image_url))
|
||||
srcset = f'data-srcset="{image_high_res} 2x"'
|
||||
else:
|
||||
srcset = ''
|
||||
srcset = ""
|
||||
|
||||
# image could be external
|
||||
if not external_img:
|
||||
|
@ -250,21 +248,22 @@ def lazy_img(ctx, image_url, placeholder_url, include_highres_image=False,
|
|||
srcset = f'data-srcset="{highres_image_url} 2x"'
|
||||
|
||||
if optional_attributes:
|
||||
class_name = optional_attributes.pop('class', 'lazy-image')
|
||||
alt_text = optional_attributes.pop('alt', '')
|
||||
attrs = ' '.join('%s="%s"' % (attr, val)
|
||||
for attr, val in optional_attributes.items())
|
||||
class_name = optional_attributes.pop("class", "lazy-image")
|
||||
alt_text = optional_attributes.pop("alt", "")
|
||||
attrs = " ".join('%s="%s"' % (attr, val) for attr, val in optional_attributes.items())
|
||||
else:
|
||||
class_name = 'lazy-image'
|
||||
alt_text = ''
|
||||
attrs = ''
|
||||
class_name = "lazy-image"
|
||||
alt_text = ""
|
||||
attrs = ""
|
||||
|
||||
markup = (f'<div class="lazy-image-container">'
|
||||
f'<img class="{class_name}" src="{placeholder}" data-src="{image_url}" {srcset} alt="{alt_text}" {attrs}>'
|
||||
f'<noscript>'
|
||||
f'<img class="{class_name}" src="{image_url}" {srcset} alt="{alt_text}" {attrs}>'
|
||||
f'</noscript>'
|
||||
f'</div>')
|
||||
markup = (
|
||||
f'<div class="lazy-image-container">'
|
||||
f'<img class="{class_name}" src="{placeholder}" data-src="{image_url}" {srcset} alt="{alt_text}" {attrs}>'
|
||||
f"<noscript>"
|
||||
f'<img class="{class_name}" src="{image_url}" {srcset} alt="{alt_text}" {attrs}>'
|
||||
f"</noscript>"
|
||||
f"</div>"
|
||||
)
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
@ -292,40 +291,37 @@ def video(ctx, *args, **kwargs):
|
|||
mp4, ogv. If you want anything else, patches welcome.
|
||||
"""
|
||||
|
||||
filetypes = ('webm', 'ogv', 'mp4')
|
||||
mime = {'webm': 'video/webm',
|
||||
'ogv': 'video/ogg; codecs="theora, vorbis"',
|
||||
'mp4': 'video/mp4'}
|
||||
filetypes = ("webm", "ogv", "mp4")
|
||||
mime = {"webm": "video/webm", "ogv": 'video/ogg; codecs="theora, vorbis"', "mp4": "video/mp4"}
|
||||
|
||||
videos = {}
|
||||
for v in args:
|
||||
try:
|
||||
ext = v.rsplit('.', 1)[1].lower()
|
||||
ext = v.rsplit(".", 1)[1].lower()
|
||||
except IndexError:
|
||||
# TODO: Perhaps we don't want to swallow this quietly in the future
|
||||
continue
|
||||
if ext not in filetypes:
|
||||
continue
|
||||
videos[ext] = (v if 'prefix' not in kwargs else
|
||||
urllib.parse.urljoin(kwargs['prefix'], v))
|
||||
videos[ext] = v if "prefix" not in kwargs else urllib.parse.urljoin(kwargs["prefix"], v)
|
||||
|
||||
if not videos:
|
||||
return ''
|
||||
return ""
|
||||
|
||||
# defaults
|
||||
data = {
|
||||
'w': 640,
|
||||
'h': 360,
|
||||
'autoplay': False,
|
||||
'preload': False,
|
||||
'id': 'htmlPlayer',
|
||||
'fluent_l10n': ctx['fluent_l10n'],
|
||||
"w": 640,
|
||||
"h": 360,
|
||||
"autoplay": False,
|
||||
"preload": False,
|
||||
"id": "htmlPlayer",
|
||||
"fluent_l10n": ctx["fluent_l10n"],
|
||||
}
|
||||
|
||||
data.update(**kwargs)
|
||||
data.update(filetypes=filetypes, mime=mime, videos=videos)
|
||||
|
||||
return jinja2.Markup(render_to_string('mozorg/videotag.html', data, request=ctx['request']))
|
||||
return jinja2.Markup(render_to_string("mozorg/videotag.html", data, request=ctx["request"]))
|
||||
|
||||
|
||||
@library.global_function
|
||||
|
@ -358,16 +354,16 @@ def press_blog_url(ctx):
|
|||
https://blog.mozilla.org/press-de/
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
if locale not in settings.PRESS_BLOGS:
|
||||
locale = 'en-US'
|
||||
locale = "en-US"
|
||||
|
||||
return settings.PRESS_BLOG_ROOT + settings.PRESS_BLOGS[locale]
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def donate_url(ctx, source=''):
|
||||
def donate_url(ctx, source=""):
|
||||
"""Output a donation link to the donation page formatted using settings.DONATE_PARAMS
|
||||
|
||||
Examples
|
||||
|
@ -391,18 +387,20 @@ def donate_url(ctx, source=''):
|
|||
https://donate.mozilla.org/?utm_source=mozilla.org&utm_medium=referral&utm_content=footer
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
donate_url_params = settings.DONATE_PARAMS.get(
|
||||
locale, settings.DONATE_PARAMS['en-US'])
|
||||
donate_url_params = settings.DONATE_PARAMS.get(locale, settings.DONATE_PARAMS["en-US"])
|
||||
|
||||
donate_url = settings.DONATE_LINK_UNKNOWN.format(source=source)
|
||||
|
||||
if locale in settings.DONATE_PARAMS:
|
||||
donate_url = settings.DONATE_LINK.format(
|
||||
locale=locale, presets=donate_url_params['presets'],
|
||||
default=donate_url_params['default'], source=source,
|
||||
currency=donate_url_params['currency'])
|
||||
locale=locale,
|
||||
presets=donate_url_params["presets"],
|
||||
default=donate_url_params["default"],
|
||||
source=source,
|
||||
currency=donate_url_params["currency"],
|
||||
)
|
||||
|
||||
return donate_url
|
||||
|
||||
|
@ -437,9 +435,9 @@ def firefox_twitter_url(ctx):
|
|||
https://twitter.com/firefoxbrasil
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
if locale not in settings.FIREFOX_TWITTER_ACCOUNTS:
|
||||
locale = 'en-US'
|
||||
locale = "en-US"
|
||||
|
||||
return settings.FIREFOX_TWITTER_ACCOUNTS[locale]
|
||||
|
||||
|
@ -470,9 +468,9 @@ def firefox_instagram_url(ctx):
|
|||
https://www.instagram.com/unfcktheinternet/
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
if locale not in settings.FIREFOX_INSTAGRAM_ACCOUNTS:
|
||||
locale = 'en-US'
|
||||
locale = "en-US"
|
||||
|
||||
return settings.FIREFOX_INSTAGRAM_ACCOUNTS[locale]
|
||||
|
||||
|
@ -499,12 +497,12 @@ def absolute_url(url):
|
|||
{% endfilter %}
|
||||
"""
|
||||
|
||||
if url.startswith('//'):
|
||||
prefix = 'https:'
|
||||
elif url.startswith('/'):
|
||||
if url.startswith("//"):
|
||||
prefix = "https:"
|
||||
elif url.startswith("/"):
|
||||
prefix = settings.CANONICAL_URL
|
||||
else:
|
||||
prefix = ''
|
||||
prefix = ""
|
||||
|
||||
return prefix + url
|
||||
|
||||
|
@ -543,11 +541,11 @@ def firefox_ios_url(ctx, ct_param=None):
|
|||
https://itunes.apple.com/jp/app/firefox-private-safe-browser/id989804926
|
||||
|
||||
"""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
link = firefox_ios.get_download_url('release', locale)
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
link = firefox_ios.get_download_url("release", locale)
|
||||
|
||||
if ct_param:
|
||||
return link + '?ct=' + ct_param
|
||||
return link + "?ct=" + ct_param
|
||||
|
||||
return link
|
||||
|
||||
|
@ -593,7 +591,7 @@ def slugify(text):
|
|||
|
||||
@library.filter
|
||||
def bleach_tags(text):
|
||||
return bleach.clean(text, tags=[], strip=True).replace('&', '&')
|
||||
return bleach.clean(text, tags=[], strip=True).replace("&", "&")
|
||||
|
||||
|
||||
# from jingo
|
||||
|
@ -624,39 +622,39 @@ def f(s, *args, **kwargs):
|
|||
def datetime(t, fmt=None):
|
||||
"""Call ``datetime.strftime`` with the given format string."""
|
||||
if fmt is None:
|
||||
fmt = _(u'%B %e, %Y')
|
||||
fmt = _("%B %e, %Y")
|
||||
if not six.PY3:
|
||||
# The datetime.strftime function strictly does not
|
||||
# support Unicode in Python 2 but is Unicode only in 3.x.
|
||||
fmt = fmt.encode('utf-8')
|
||||
return smart_text(t.strftime(fmt)) if t else ''
|
||||
fmt = fmt.encode("utf-8")
|
||||
return smart_text(t.strftime(fmt)) if t else ""
|
||||
|
||||
|
||||
@library.filter
|
||||
def ifeq(a, b, text):
|
||||
"""Return ``text`` if ``a == b``."""
|
||||
return jinja2.Markup(text if a == b else '')
|
||||
return jinja2.Markup(text if a == b else "")
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def app_store_url(ctx, product):
|
||||
"""Returns a localised app store URL for a given product"""
|
||||
base_url = getattr(settings, f'APPLE_APPSTORE_{product.upper()}_LINK')
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
base_url = getattr(settings, f"APPLE_APPSTORE_{product.upper()}_LINK")
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
countries = settings.APPLE_APPSTORE_COUNTRY_MAP
|
||||
if locale in countries:
|
||||
return base_url.format(country=countries[locale])
|
||||
else:
|
||||
return base_url.replace('/{country}/', '/')
|
||||
return base_url.replace("/{country}/", "/")
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def play_store_url(ctx, product):
|
||||
"""Returns a localised play store URL for a given product"""
|
||||
base_url = getattr(settings, f'GOOGLE_PLAY_{product.upper()}_LINK')
|
||||
return f'{base_url}&hl={lang_short(ctx)}'
|
||||
base_url = getattr(settings, f"GOOGLE_PLAY_{product.upper()}_LINK")
|
||||
return f"{base_url}&hl={lang_short(ctx)}"
|
||||
|
||||
|
||||
@library.global_function
|
||||
|
@ -667,46 +665,46 @@ def structured_data_id(ctx, id, domain=None):
|
|||
a supplied id e.g. https://www.mozilla.org/#firefoxbrowser
|
||||
"""
|
||||
canonical = settings.CANONICAL_URL if not domain else domain
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
suffix = ''
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
suffix = ""
|
||||
|
||||
if locale != 'en-US':
|
||||
suffix = '-' + locale.lower()
|
||||
if locale != "en-US":
|
||||
suffix = "-" + locale.lower()
|
||||
|
||||
return canonical + '/#' + id + suffix
|
||||
return canonical + "/#" + id + suffix
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def lang_short(ctx):
|
||||
"""Returns a shortened locale code e.g. en."""
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
return locale.split('-')[0]
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
return locale.split("-")[0]
|
||||
|
||||
|
||||
def _get_adjust_link(adjust_url, app_store_url, google_play_url, redirect, locale, adgroup, creative=None):
|
||||
link = adjust_url
|
||||
params = 'campaign=www.mozilla.org&adgroup=' + adgroup
|
||||
params = "campaign=www.mozilla.org&adgroup=" + adgroup
|
||||
redirect_url = None
|
||||
|
||||
# Get the appropriate app store URL to use as a fallback redirect.
|
||||
if redirect == 'ios':
|
||||
if redirect == "ios":
|
||||
countries = settings.APPLE_APPSTORE_COUNTRY_MAP
|
||||
if locale in countries:
|
||||
redirect_url = app_store_url.format(country=countries[locale])
|
||||
else:
|
||||
redirect_url = app_store_url.replace('/{country}/', '/')
|
||||
elif redirect == 'android':
|
||||
redirect_url = app_store_url.replace("/{country}/", "/")
|
||||
elif redirect == "android":
|
||||
redirect_url = google_play_url
|
||||
|
||||
# Optional creative parameter.
|
||||
if creative:
|
||||
params += '&creative=' + creative
|
||||
params += "&creative=" + creative
|
||||
|
||||
if redirect_url:
|
||||
link += '?redirect=' + urlquote(redirect_url, safe='') + '&' + params
|
||||
link += "?redirect=" + urlquote(redirect_url, safe="") + "&" + params
|
||||
else:
|
||||
link += '?' + params
|
||||
link += "?" + params
|
||||
|
||||
return link
|
||||
|
||||
|
@ -728,7 +726,7 @@ def firefox_adjust_url(ctx, redirect, adgroup, creative=None):
|
|||
adjust_url = settings.ADJUST_FIREFOX_URL
|
||||
app_store_url = settings.APPLE_APPSTORE_FIREFOX_LINK
|
||||
play_store_url = settings.GOOGLE_PLAY_FIREFOX_LINK
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
return _get_adjust_link(adjust_url, app_store_url, play_store_url, redirect, locale, adgroup, creative)
|
||||
|
||||
|
@ -747,11 +745,11 @@ def focus_adjust_url(ctx, redirect, adgroup, creative=None):
|
|||
|
||||
{{ focus_adjust_url('ios', 'fights-for-you-page') }}
|
||||
"""
|
||||
klar_locales = ['de']
|
||||
klar_locales = ["de"]
|
||||
adjust_url = settings.ADJUST_FOCUS_URL
|
||||
app_store_url = settings.APPLE_APPSTORE_FOCUS_LINK
|
||||
play_store_url = settings.GOOGLE_PLAY_FOCUS_LINK
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
if locale in klar_locales:
|
||||
adjust_url = settings.ADJUST_KLAR_URL
|
||||
|
@ -778,7 +776,7 @@ def pocket_adjust_url(ctx, redirect, adgroup, creative=None):
|
|||
adjust_url = settings.ADJUST_POCKET_URL
|
||||
app_store_url = settings.APPLE_APPSTORE_POCKET_LINK
|
||||
play_store_url = settings.GOOGLE_PLAY_POCKET_LINK
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
return _get_adjust_link(adjust_url, app_store_url, play_store_url, redirect, locale, adgroup, creative)
|
||||
|
||||
|
@ -800,51 +798,58 @@ def lockwise_adjust_url(ctx, redirect, adgroup, creative=None):
|
|||
adjust_url = settings.ADJUST_LOCKWISE_URL
|
||||
app_store_url = settings.APPLE_APPSTORE_LOCKWISE_LINK
|
||||
play_store_url = settings.GOOGLE_PLAY_LOCKWISE_LINK
|
||||
locale = getattr(ctx['request'], 'locale', 'en-US')
|
||||
locale = getattr(ctx["request"], "locale", "en-US")
|
||||
|
||||
return _get_adjust_link(adjust_url, app_store_url, play_store_url, redirect, locale, adgroup, creative)
|
||||
|
||||
|
||||
def _fxa_product_url(product_url, entrypoint, optional_parameters=None):
|
||||
separator = '&' if '?' in product_url else '?'
|
||||
url = f'{product_url}{separator}entrypoint={entrypoint}&form_type=button&utm_source={entrypoint}&utm_medium=referral'
|
||||
separator = "&" if "?" in product_url else "?"
|
||||
url = f"{product_url}{separator}entrypoint={entrypoint}&form_type=button&utm_source={entrypoint}&utm_medium=referral"
|
||||
|
||||
if optional_parameters:
|
||||
params = '&'.join('%s=%s' % (param, val) for param, val in optional_parameters.items())
|
||||
url += f'&{params}'
|
||||
params = "&".join("%s=%s" % (param, val) for param, val in optional_parameters.items())
|
||||
url += f"&{params}"
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def _fxa_product_button(product_url, entrypoint, button_text, class_name=None, is_button_class=True,
|
||||
include_metrics=True, optional_parameters=None, optional_attributes=None):
|
||||
def _fxa_product_button(
|
||||
product_url,
|
||||
entrypoint,
|
||||
button_text,
|
||||
class_name=None,
|
||||
is_button_class=True,
|
||||
include_metrics=True,
|
||||
optional_parameters=None,
|
||||
optional_attributes=None,
|
||||
):
|
||||
href = _fxa_product_url(product_url, entrypoint, optional_parameters)
|
||||
css_class = 'js-fxa-cta-link'
|
||||
attrs = ''
|
||||
css_class = "js-fxa-cta-link"
|
||||
attrs = ""
|
||||
|
||||
if optional_attributes:
|
||||
attrs += ' '.join('%s="%s"' % (attr, val) for attr, val in optional_attributes.items())
|
||||
attrs += " ".join('%s="%s"' % (attr, val) for attr, val in optional_attributes.items())
|
||||
|
||||
if include_metrics:
|
||||
css_class += ' js-fxa-product-button'
|
||||
css_class += " js-fxa-product-button"
|
||||
|
||||
if is_button_class:
|
||||
css_class += ' mzp-c-button mzp-t-product'
|
||||
css_class += " mzp-c-button mzp-t-product"
|
||||
|
||||
if class_name:
|
||||
css_class += f' {class_name}'
|
||||
css_class += f" {class_name}"
|
||||
|
||||
markup = (f'<a href="{href}" data-action="{settings.FXA_ENDPOINT}" class="{css_class}" {attrs}>'
|
||||
f'{button_text}'
|
||||
f'</a>')
|
||||
markup = f'<a href="{href}" data-action="{settings.FXA_ENDPOINT}" class="{css_class}" {attrs}>' f"{button_text}" f"</a>"
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def pocket_fxa_button(ctx, entrypoint, button_text, class_name=None, is_button_class=True, include_metrics=True,
|
||||
optional_parameters=None, optional_attributes=None):
|
||||
def pocket_fxa_button(
|
||||
ctx, entrypoint, button_text, class_name=None, is_button_class=True, include_metrics=True, optional_parameters=None, optional_attributes=None
|
||||
):
|
||||
"""
|
||||
Render a getpocket.com link with required params for FxA authentication.
|
||||
|
||||
|
@ -856,15 +861,17 @@ def pocket_fxa_button(ctx, entrypoint, button_text, class_name=None, is_button_c
|
|||
|
||||
{{ pocket_fxa_button(entrypoint='mozilla.org-firefox-pocket', button_text='Try Pocket Now') }}
|
||||
"""
|
||||
product_url = 'https://getpocket.com/ff_signup'
|
||||
return _fxa_product_button(product_url, entrypoint, button_text, class_name, is_button_class, include_metrics,
|
||||
optional_parameters, optional_attributes)
|
||||
product_url = "https://getpocket.com/ff_signup"
|
||||
return _fxa_product_button(
|
||||
product_url, entrypoint, button_text, class_name, is_button_class, include_metrics, optional_parameters, optional_attributes
|
||||
)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def monitor_fxa_button(ctx, entrypoint, button_text, class_name=None, is_button_class=True, include_metrics=True,
|
||||
optional_parameters=None, optional_attributes=None):
|
||||
def monitor_fxa_button(
|
||||
ctx, entrypoint, button_text, class_name=None, is_button_class=True, include_metrics=True, optional_parameters=None, optional_attributes=None
|
||||
):
|
||||
"""
|
||||
Render a monitor.firefox.com link with required params for FxA authentication.
|
||||
|
||||
|
@ -876,14 +883,15 @@ def monitor_fxa_button(ctx, entrypoint, button_text, class_name=None, is_button_
|
|||
|
||||
{{ monitor_fxa_button(entrypoint='mozilla.org-firefox-accounts', button_text='Sign In to Monitor') }}
|
||||
"""
|
||||
product_url = 'https://monitor.firefox.com/oauth/init'
|
||||
return _fxa_product_button(product_url, entrypoint, button_text, class_name, is_button_class, include_metrics,
|
||||
optional_parameters, optional_attributes)
|
||||
product_url = "https://monitor.firefox.com/oauth/init"
|
||||
return _fxa_product_button(
|
||||
product_url, entrypoint, button_text, class_name, is_button_class, include_metrics, optional_parameters, optional_attributes
|
||||
)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def fxa_link_fragment(ctx, entrypoint, action='signup', optional_parameters=None):
|
||||
def fxa_link_fragment(ctx, entrypoint, action="signup", optional_parameters=None):
|
||||
"""
|
||||
Returns `href` and `data-mozillaonline-link` attributes as a string fragment.
|
||||
This is useful for inline links that appear inside a string of localized copy,
|
||||
|
@ -900,22 +908,30 @@ def fxa_link_fragment(ctx, entrypoint, action='signup', optional_parameters=None
|
|||
<p>Already have an account? <a {{ sign_in }} class="{{ class_name }}">Sign In</a> to start syncing.</p>
|
||||
"""
|
||||
|
||||
if action == 'email':
|
||||
action = '?action=email'
|
||||
if action == "email":
|
||||
action = "?action=email"
|
||||
|
||||
fxa_url = _fxa_product_url(f'{settings.FXA_ENDPOINT}{action}', entrypoint, optional_parameters)
|
||||
mozillaonline_url = _fxa_product_url(f'{settings.FXA_ENDPOINT_MOZILLAONLINE}{action}', entrypoint, optional_parameters)
|
||||
fxa_url = _fxa_product_url(f"{settings.FXA_ENDPOINT}{action}", entrypoint, optional_parameters)
|
||||
mozillaonline_url = _fxa_product_url(f"{settings.FXA_ENDPOINT_MOZILLAONLINE}{action}", entrypoint, optional_parameters)
|
||||
|
||||
markup = (f'href="{fxa_url}" data-mozillaonline-link="{mozillaonline_url}" '
|
||||
f'data-mozillaonline-action="{settings.FXA_ENDPOINT_MOZILLAONLINE}"')
|
||||
markup = f'href="{fxa_url}" data-mozillaonline-link="{mozillaonline_url}" ' f'data-mozillaonline-action="{settings.FXA_ENDPOINT_MOZILLAONLINE}"'
|
||||
|
||||
return jinja2.Markup(markup)
|
||||
|
||||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def fxa_button(ctx, entrypoint, button_text, action='signup', class_name=None, is_button_class=True,
|
||||
include_metrics=True, optional_parameters=None, optional_attributes=None):
|
||||
def fxa_button(
|
||||
ctx,
|
||||
entrypoint,
|
||||
button_text,
|
||||
action="signup",
|
||||
class_name=None,
|
||||
is_button_class=True,
|
||||
include_metrics=True,
|
||||
optional_parameters=None,
|
||||
optional_attributes=None,
|
||||
):
|
||||
"""
|
||||
Render a accounts.firefox.com link with required params for FxA authentication.
|
||||
|
||||
|
@ -928,19 +944,20 @@ def fxa_button(ctx, entrypoint, button_text, action='signup', class_name=None, i
|
|||
{{ fxa_button(entrypoint='mozilla.org-firefox-accounts', button_text='Sign In') }}
|
||||
"""
|
||||
|
||||
if action == 'email':
|
||||
action = '?action=email'
|
||||
if action == "email":
|
||||
action = "?action=email"
|
||||
|
||||
product_url = f'{settings.FXA_ENDPOINT}{action}'
|
||||
mozillaonline_product_url = f'{settings.FXA_ENDPOINT_MOZILLAONLINE}{action}'
|
||||
product_url = f"{settings.FXA_ENDPOINT}{action}"
|
||||
mozillaonline_product_url = f"{settings.FXA_ENDPOINT_MOZILLAONLINE}{action}"
|
||||
|
||||
mozillaonline_attribute = {
|
||||
'data-mozillaonline-link': _fxa_product_url(mozillaonline_product_url, entrypoint, optional_parameters),
|
||||
'data-mozillaonline-action': settings.FXA_ENDPOINT_MOZILLAONLINE
|
||||
"data-mozillaonline-link": _fxa_product_url(mozillaonline_product_url, entrypoint, optional_parameters),
|
||||
"data-mozillaonline-action": settings.FXA_ENDPOINT_MOZILLAONLINE,
|
||||
}
|
||||
|
||||
optional_attributes = optional_attributes or {}
|
||||
optional_attributes.update(mozillaonline_attribute)
|
||||
|
||||
return _fxa_product_button(product_url, entrypoint, button_text, class_name, is_button_class, include_metrics,
|
||||
optional_parameters, optional_attributes)
|
||||
return _fxa_product_button(
|
||||
product_url, entrypoint, button_text, class_name, is_button_class, include_metrics, optional_parameters, optional_attributes
|
||||
)
|
||||
|
|
|
@ -9,20 +9,18 @@ from jinja2 import Markup
|
|||
from qrcode.image.svg import SvgPathImage
|
||||
|
||||
|
||||
cache = caches['qrcode']
|
||||
cache = caches["qrcode"]
|
||||
|
||||
|
||||
@library.global_function
|
||||
def qrcode(data, box_size=20):
|
||||
key = sha1(f'{data}-{box_size}'.encode('utf-8')).hexdigest()
|
||||
key = sha1(f"{data}-{box_size}".encode("utf-8")).hexdigest()
|
||||
svg = cache.get(key)
|
||||
if not svg:
|
||||
img = qr.make(data,
|
||||
image_factory=SvgPathImage,
|
||||
box_size=box_size)
|
||||
img = qr.make(data, image_factory=SvgPathImage, box_size=box_size)
|
||||
svg = BytesIO()
|
||||
img.save(svg)
|
||||
svg = svg.getvalue().decode('utf-8')
|
||||
svg = svg.getvalue().decode("utf-8")
|
||||
cache.set(key, svg)
|
||||
|
||||
return Markup(svg)
|
||||
|
|
|
@ -6,7 +6,7 @@ from contextlib import contextmanager
|
|||
|
||||
from django.test import RequestFactory, TestCase as DjTestCase
|
||||
|
||||
from bedrock.base.urlresolvers import (get_url_prefix, Prefixer, set_url_prefix)
|
||||
from bedrock.base.urlresolvers import get_url_prefix, Prefixer, set_url_prefix
|
||||
from lib.l10n_utils import translation
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ class TestCase(DjTestCase):
|
|||
old_prefix = get_url_prefix()
|
||||
old_locale = translation.get_language()
|
||||
rf = RequestFactory()
|
||||
set_url_prefix(Prefixer(rf.get('/%s/' % (locale,))))
|
||||
set_url_prefix(Prefixer(rf.get("/%s/" % (locale,))))
|
||||
translation.activate(locale)
|
||||
yield
|
||||
set_url_prefix(old_prefix)
|
||||
|
|
|
@ -11,11 +11,10 @@ from bedrock.mozorg.tests import TestCase
|
|||
from bedrock.mozorg.management.commands import update_product_details_files
|
||||
|
||||
|
||||
PD_REPO_TEST_PATH = Path(__file__).parent.joinpath('test_pd_repo')
|
||||
PD_REPO_TEST_PATH = Path(__file__).parent.joinpath("test_pd_repo")
|
||||
|
||||
|
||||
@override_settings(PROD_DETAILS_STORAGE='PDDatabaseStorage',
|
||||
PROD_DETAILS_TEST_DIR=str(PD_REPO_TEST_PATH.joinpath('product-details')))
|
||||
@override_settings(PROD_DETAILS_STORAGE="PDDatabaseStorage", PROD_DETAILS_TEST_DIR=str(PD_REPO_TEST_PATH.joinpath("product-details")))
|
||||
class TestUpdateProductDetailsFiles(TestCase):
|
||||
def setUp(self):
|
||||
self.command = update_product_details_files.Command()
|
||||
|
@ -23,33 +22,28 @@ class TestUpdateProductDetailsFiles(TestCase):
|
|||
self.command.repo.path_str = str(PD_REPO_TEST_PATH)
|
||||
|
||||
def test_handle_diff_loads_all(self):
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT,
|
||||
file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database='default', force=False)
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT, file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database="default", force=False)
|
||||
self.command.update_file_data.return_value = True
|
||||
self.command.handle(**options)
|
||||
assert self.command.file_storage.all_json_files.called
|
||||
self.command.load_changes. \
|
||||
assert_called_with(options, self.command.file_storage.all_json_files())
|
||||
self.command.load_changes.assert_called_with(options, self.command.file_storage.all_json_files())
|
||||
assert self.command.repo.set_db_latest.called
|
||||
|
||||
def test_handle_error_no_set_latest(self):
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT,
|
||||
file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database='default', force=False)
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT, file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database="default", force=False)
|
||||
self.command.update_file_data.return_value = True
|
||||
self.command.load_changes.side_effect = Exception('broke yo')
|
||||
self.command.load_changes.side_effect = Exception("broke yo")
|
||||
with self.assertRaises(Exception):
|
||||
self.command.handle(**options)
|
||||
assert self.command.file_storage.all_json_files.called
|
||||
self.command.load_changes.\
|
||||
assert_called_with(options, self.command.file_storage.all_json_files())
|
||||
self.command.load_changes.assert_called_with(options, self.command.file_storage.all_json_files())
|
||||
assert not self.command.repo.set_db_latest.called
|
||||
|
||||
def test_handle_no_diff_does_nothing(self):
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT,
|
||||
file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database='default', force=False)
|
||||
with patch.multiple(self.command, update_file_data=DEFAULT, validate_data=DEFAULT, file_storage=DEFAULT, load_changes=DEFAULT, repo=DEFAULT):
|
||||
options = dict(quiet=False, database="default", force=False)
|
||||
self.command.update_file_data.return_value = False
|
||||
self.command.handle(**options)
|
||||
assert not self.command.file_storage.all_json_files.called
|
||||
|
|
|
@ -14,7 +14,7 @@ class TestFunnelcakeParam(TestCase):
|
|||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def _funnelcake(self, url='/', **kwargs):
|
||||
def _funnelcake(self, url="/", **kwargs):
|
||||
return funnelcake_param(self.rf.get(url, kwargs))
|
||||
|
||||
def test_funnelcake_param_noop(self):
|
||||
|
@ -23,22 +23,22 @@ class TestFunnelcakeParam(TestCase):
|
|||
|
||||
def test_funnelcake_param_f(self):
|
||||
"""Should inject funnelcake into context."""
|
||||
assert self._funnelcake(f='5') == {'funnelcake_id': '5'}
|
||||
assert self._funnelcake(f='234') == {'funnelcake_id': '234'}
|
||||
assert self._funnelcake(f="5") == {"funnelcake_id": "5"}
|
||||
assert self._funnelcake(f="234") == {"funnelcake_id": "234"}
|
||||
|
||||
def test_funnelcake_param_bad(self):
|
||||
"""Should not inject bad funnelcake into context."""
|
||||
assert self._funnelcake(f='5dude') == {}
|
||||
assert self._funnelcake(f='123456') == {}
|
||||
assert self._funnelcake(f="5dude") == {}
|
||||
assert self._funnelcake(f="123456") == {}
|
||||
|
||||
def test_funnelcake_param_increment_installer_help(self):
|
||||
"""FC param should be +1 on the firefox/installer-help/ page.
|
||||
|
||||
Bug 933852.
|
||||
"""
|
||||
url = reverse('firefox.installer-help')
|
||||
ctx = self._funnelcake(url, f='20')
|
||||
assert ctx['funnelcake_id'] == '21'
|
||||
url = reverse("firefox.installer-help")
|
||||
ctx = self._funnelcake(url, f="20")
|
||||
assert ctx["funnelcake_id"] == "21"
|
||||
|
||||
ctx = self._funnelcake(url, f='10')
|
||||
assert ctx['funnelcake_id'] == '11'
|
||||
ctx = self._funnelcake(url, f="10")
|
||||
assert ctx["funnelcake_id"] == "11"
|
||||
|
|
|
@ -13,46 +13,57 @@ from bedrock.mozorg.tests import TestCase
|
|||
|
||||
class TestCredits(TestCase):
|
||||
def setUp(self):
|
||||
self.credits_file = credits.CreditsFile('credits')
|
||||
self.credits_file = credits.CreditsFile("credits")
|
||||
self.credits_file.clear_cache()
|
||||
|
||||
def test_credits_list(self):
|
||||
self.credits_file.read = Mock(return_value=dedent("""\
|
||||
self.credits_file.read = Mock(
|
||||
return_value=dedent(
|
||||
"""\
|
||||
The Dude,Dude
|
||||
Walter Sobchak,Sobchak
|
||||
Theodore Donald Kerabatsos,Kerabatsos
|
||||
Tantek Çelik,Çelik
|
||||
"""))
|
||||
self.assertListEqual(self.credits_file.rows, [
|
||||
['Tantek Çelik', 'CELIK'],
|
||||
['The Dude', 'DUDE'],
|
||||
['Theodore Donald Kerabatsos', 'KERABATSOS'],
|
||||
['Walter Sobchak', 'SOBCHAK'],
|
||||
])
|
||||
"""
|
||||
)
|
||||
)
|
||||
self.assertListEqual(
|
||||
self.credits_file.rows,
|
||||
[
|
||||
["Tantek Çelik", "CELIK"],
|
||||
["The Dude", "DUDE"],
|
||||
["Theodore Donald Kerabatsos", "KERABATSOS"],
|
||||
["Walter Sobchak", "SOBCHAK"],
|
||||
],
|
||||
)
|
||||
|
||||
def test_credits_ordered_no_sortkey(self):
|
||||
"""Should give an ordered dict or ordered lists keyed on first letter of name."""
|
||||
self.credits_file.readlines = Mock(return_value=[
|
||||
'Bunny Lebowski',
|
||||
'Maude Lebowski',
|
||||
'Jeffrey Lebowski',
|
||||
'Uli Kunkel',
|
||||
'The Dude',
|
||||
'Walter Sobchak',
|
||||
'Theodore Donald Kerabatsos',
|
||||
])
|
||||
self.credits_file.readlines = Mock(
|
||||
return_value=[
|
||||
"Bunny Lebowski",
|
||||
"Maude Lebowski",
|
||||
"Jeffrey Lebowski",
|
||||
"Uli Kunkel",
|
||||
"The Dude",
|
||||
"Walter Sobchak",
|
||||
"Theodore Donald Kerabatsos",
|
||||
]
|
||||
)
|
||||
good_names = OrderedDict()
|
||||
good_names['B'] = ['Bunny Lebowski']
|
||||
good_names['J'] = ['Jeffrey Lebowski']
|
||||
good_names['M'] = ['Maude Lebowski']
|
||||
good_names['T'] = ['The Dude', 'Theodore Donald Kerabatsos']
|
||||
good_names['U'] = ['Uli Kunkel']
|
||||
good_names['W'] = ['Walter Sobchak']
|
||||
good_names["B"] = ["Bunny Lebowski"]
|
||||
good_names["J"] = ["Jeffrey Lebowski"]
|
||||
good_names["M"] = ["Maude Lebowski"]
|
||||
good_names["T"] = ["The Dude", "Theodore Donald Kerabatsos"]
|
||||
good_names["U"] = ["Uli Kunkel"]
|
||||
good_names["W"] = ["Walter Sobchak"]
|
||||
self.assertEqual(self.credits_file.ordered, good_names)
|
||||
|
||||
def test_credits_ordered(self):
|
||||
"""Should give an ordered dict or ordered lists keyed on first letter of sortkey."""
|
||||
self.credits_file.read = Mock(return_value=dedent("""\
|
||||
self.credits_file.read = Mock(
|
||||
return_value=dedent(
|
||||
"""\
|
||||
Bunny Lebowski,Lebowski Bunny
|
||||
Maude Lebowski,Lebowski Maude
|
||||
Jeffrey Lebowski,Lebowski Jeffrey
|
||||
|
@ -61,30 +72,34 @@ class TestCredits(TestCase):
|
|||
Walter Sobchak,Sobchak
|
||||
Theodore Donald Kerabatsos,Kerabatsos
|
||||
Tantek Çelik,Çelik
|
||||
"""))
|
||||
"""
|
||||
)
|
||||
)
|
||||
good_names = OrderedDict()
|
||||
good_names['C'] = ['Tantek Çelik']
|
||||
good_names['D'] = ['The Dude']
|
||||
good_names['K'] = ['Theodore Donald Kerabatsos', 'Uli Kunkel']
|
||||
good_names['L'] = ['Bunny Lebowski', 'Jeffrey Lebowski', 'Maude Lebowski']
|
||||
good_names['S'] = ['Walter Sobchak']
|
||||
good_names["C"] = ["Tantek Çelik"]
|
||||
good_names["D"] = ["The Dude"]
|
||||
good_names["K"] = ["Theodore Donald Kerabatsos", "Uli Kunkel"]
|
||||
good_names["L"] = ["Bunny Lebowski", "Jeffrey Lebowski", "Maude Lebowski"]
|
||||
good_names["S"] = ["Walter Sobchak"]
|
||||
self.assertEqual(self.credits_file.ordered, good_names)
|
||||
|
||||
def test_credits_ordered_skips(self):
|
||||
"""Should skip lines with more than 2 items."""
|
||||
self.credits_file.readlines = Mock(return_value=[
|
||||
'Bunny Lebowski,Lebowski Bunny',
|
||||
'Maude Lebowski,Lebowski Maude',
|
||||
'Jeffrey Lebowski,Lebowski Jeffrey',
|
||||
'Karl Hungus,Karl,Inappropriate',
|
||||
'Uli Kunkel,Kunkel',
|
||||
'The Dude,Dude',
|
||||
'Walter Sobchak,Sobchak',
|
||||
'Theodore Donald Kerabatsos,Kerabatsos',
|
||||
])
|
||||
self.credits_file.readlines = Mock(
|
||||
return_value=[
|
||||
"Bunny Lebowski,Lebowski Bunny",
|
||||
"Maude Lebowski,Lebowski Maude",
|
||||
"Jeffrey Lebowski,Lebowski Jeffrey",
|
||||
"Karl Hungus,Karl,Inappropriate",
|
||||
"Uli Kunkel,Kunkel",
|
||||
"The Dude,Dude",
|
||||
"Walter Sobchak,Sobchak",
|
||||
"Theodore Donald Kerabatsos,Kerabatsos",
|
||||
]
|
||||
)
|
||||
good_names = OrderedDict()
|
||||
good_names['D'] = ['The Dude']
|
||||
good_names['K'] = ['Theodore Donald Kerabatsos', 'Uli Kunkel']
|
||||
good_names['L'] = ['Bunny Lebowski', 'Jeffrey Lebowski', 'Maude Lebowski']
|
||||
good_names['S'] = ['Walter Sobchak']
|
||||
good_names["D"] = ["The Dude"]
|
||||
good_names["K"] = ["Theodore Donald Kerabatsos", "Uli Kunkel"]
|
||||
good_names["L"] = ["Bunny Lebowski", "Jeffrey Lebowski", "Maude Lebowski"]
|
||||
good_names["S"] = ["Walter Sobchak"]
|
||||
self.assertEqual(self.credits_file.ordered, good_names)
|
||||
|
|
|
@ -20,13 +20,13 @@ class ViewDecoratorTests(TestCase):
|
|||
"""
|
||||
Should have appropriate Cache-Control and Expires headers.
|
||||
"""
|
||||
test_request = self.rf.get('/hi-there-dude/')
|
||||
test_request = self.rf.get("/hi-there-dude/")
|
||||
resp = view(test_request)
|
||||
num_seconds = hours * 60 * 60
|
||||
self.assertEqual(resp['cache-control'], 'max-age=%d' % num_seconds)
|
||||
self.assertEqual(resp["cache-control"], "max-age=%d" % num_seconds)
|
||||
|
||||
now_date = floor(time.time())
|
||||
exp_date = parse_http_date(resp['expires'])
|
||||
exp_date = parse_http_date(resp["expires"])
|
||||
self.assertAlmostEqual(now_date + num_seconds, exp_date, delta=2)
|
||||
|
||||
def test_cache_headers_48_hours(self):
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -6,17 +6,17 @@ from bedrock.mozorg.tests import TestCase
|
|||
from bedrock.mozorg.templatetags.qrcode import qrcode
|
||||
|
||||
|
||||
@patch('bedrock.mozorg.templatetags.qrcode.cache')
|
||||
@patch('bedrock.mozorg.templatetags.qrcode.qr')
|
||||
@patch("bedrock.mozorg.templatetags.qrcode.cache")
|
||||
@patch("bedrock.mozorg.templatetags.qrcode.qr")
|
||||
class TestQRCode(TestCase):
|
||||
def test_qrcode_cache_cold(self, qr_mock, cache_mock):
|
||||
cache_mock.get.return_value = None
|
||||
data = 'https://dude.abide'
|
||||
data = "https://dude.abide"
|
||||
qrcode(data, 20)
|
||||
qr_mock.make.assert_called_with(data, image_factory=SvgPathImage, box_size=20)
|
||||
|
||||
def test_qrcode_cache_warm(self, qr_mock, cache_mock):
|
||||
cache_mock.get.return_value = '<svg>stuff</svg>'
|
||||
data = 'https://dude.abide'
|
||||
cache_mock.get.return_value = "<svg>stuff</svg>"
|
||||
data = "https://dude.abide"
|
||||
qrcode(data, 20)
|
||||
qr_mock.make.assert_not_called()
|
||||
|
|
|
@ -14,8 +14,8 @@ class TestPageNode(TestCase):
|
|||
If a node is given children in the constructor, the children must mark
|
||||
the node as their parent.
|
||||
"""
|
||||
children = [PageNode('test'), PageNode('test2')]
|
||||
parent = PageRoot('parent', children=children)
|
||||
children = [PageNode("test"), PageNode("test2")]
|
||||
parent = PageRoot("parent", children=children)
|
||||
for child in children:
|
||||
assert child.parent == parent
|
||||
|
||||
|
@ -24,44 +24,41 @@ class TestPageNode(TestCase):
|
|||
full_path should return the path of this node and all of its parents
|
||||
joined by slashes.
|
||||
"""
|
||||
child = PageNode('test', path='asdf')
|
||||
PageRoot('test', path='blah', children=[
|
||||
PageNode('test', path='whoo', children=[child])
|
||||
])
|
||||
assert child.full_path == 'blah/whoo/asdf'
|
||||
child = PageNode("test", path="asdf")
|
||||
PageRoot("test", path="blah", children=[PageNode("test", path="whoo", children=[child])])
|
||||
assert child.full_path == "blah/whoo/asdf"
|
||||
|
||||
def test_full_path_empty(self):
|
||||
"""
|
||||
If one of a node's parents have an empty path, they should not be
|
||||
included in the full path.
|
||||
"""
|
||||
child = PageNode('test', path='asdf')
|
||||
PageRoot('', path='blah', children=[PageNode('', children=[child])])
|
||||
assert child.full_path == 'blah/asdf'
|
||||
child = PageNode("test", path="asdf")
|
||||
PageRoot("", path="blah", children=[PageNode("", children=[child])])
|
||||
assert child.full_path == "blah/asdf"
|
||||
|
||||
@patch('bedrock.mozorg.hierarchy.page')
|
||||
@patch("bedrock.mozorg.hierarchy.page")
|
||||
def test_page(self, page):
|
||||
"""
|
||||
If a pagenode is given a template, it should provide a page for
|
||||
inclusion in a urlconf.
|
||||
"""
|
||||
page.return_value = 'testreturn'
|
||||
assert PageNode('test').page is None
|
||||
page.return_value = "testreturn"
|
||||
assert PageNode("test").page is None
|
||||
|
||||
node = PageNode('test', path='blah', template='test.html')
|
||||
parent = PageRoot('testparent', path='yo', children=[node])
|
||||
assert node.page == 'testreturn'
|
||||
page.assert_called_with('yo/blah', 'test.html', node_root=parent,
|
||||
node=node)
|
||||
node = PageNode("test", path="blah", template="test.html")
|
||||
parent = PageRoot("testparent", path="yo", children=[node])
|
||||
assert node.page == "testreturn"
|
||||
page.assert_called_with("yo/blah", "test.html", node_root=parent, node=node)
|
||||
|
||||
def test_path_to_root(self):
|
||||
"""
|
||||
path_to_root should return an iterable of nodes following the route from
|
||||
the child node to the root of the tree.
|
||||
"""
|
||||
child1 = PageNode('test')
|
||||
child2 = PageNode('test', children=[child1])
|
||||
root = PageRoot('test', children=[child2, PageNode('test')])
|
||||
child1 = PageNode("test")
|
||||
child2 = PageNode("test", children=[child1])
|
||||
root = PageRoot("test", children=[child2, PageNode("test")])
|
||||
assert list(child1.path_to_root) == [child1, child2, root]
|
||||
|
||||
def test_breadcrumbs(self):
|
||||
|
@ -69,23 +66,23 @@ class TestPageNode(TestCase):
|
|||
breadcrumbs should return a list of nodes following the path from the
|
||||
root to the child node.
|
||||
"""
|
||||
child1 = PageNode('test')
|
||||
child2 = PageNode('test', children=[child1])
|
||||
root = PageRoot('test', children=[child2, PageNode('test')])
|
||||
child1 = PageNode("test")
|
||||
child2 = PageNode("test", children=[child1])
|
||||
root = PageRoot("test", children=[child2, PageNode("test")])
|
||||
assert list(child1.breadcrumbs) == [root, child2, child1]
|
||||
|
||||
def test_root(self):
|
||||
"""root should return the root of the page tree."""
|
||||
child1 = PageNode('test')
|
||||
child2 = PageNode('test', children=[child1])
|
||||
root = PageRoot('test', children=[child2, PageNode('test')])
|
||||
child1 = PageNode("test")
|
||||
child2 = PageNode("test", children=[child1])
|
||||
root = PageRoot("test", children=[child2, PageNode("test")])
|
||||
assert child1.root == root
|
||||
|
||||
def test_no_root(self):
|
||||
"""If the root of a tree is not a PageRoot, raise a ValueError."""
|
||||
child1 = PageNode('test')
|
||||
child2 = PageNode('test', children=[child1])
|
||||
PageNode('test', children=[child2, PageNode('test')])
|
||||
child1 = PageNode("test")
|
||||
child2 = PageNode("test", children=[child1])
|
||||
PageNode("test", children=[child2, PageNode("test")])
|
||||
self.assertRaises(ValueError, lambda: child1.root)
|
||||
|
||||
def test_previous(self):
|
||||
|
@ -93,9 +90,9 @@ class TestPageNode(TestCase):
|
|||
Previous should return the previous sibling node, or None if one doesn't
|
||||
exist.
|
||||
"""
|
||||
child1 = PageNode('', template='test1.html')
|
||||
child2 = PageNode('', template='test2.html')
|
||||
PageRoot('', children=[child1, child2])
|
||||
child1 = PageNode("", template="test1.html")
|
||||
child2 = PageNode("", template="test2.html")
|
||||
PageRoot("", children=[child1, child2])
|
||||
assert child2.previous == child1
|
||||
assert child1.previous is None
|
||||
|
||||
|
@ -112,19 +109,18 @@ class TestPageNode(TestCase):
|
|||
# O O O
|
||||
# / / \ / \
|
||||
# c1 c2 c3 c4 O
|
||||
child1 = PageNode('', template='test1.html')
|
||||
child2 = PageNode('', template='test2.html')
|
||||
child3 = PageNode('', template='test3.html')
|
||||
child4 = PageNode('', template='test4.html')
|
||||
root = PageRoot('', template='root.html', children=[
|
||||
PageNode('', children=[
|
||||
PageNode('', children=[child1])
|
||||
]),
|
||||
PageNode('', children=[
|
||||
PageNode('', children=[child2, child3]),
|
||||
PageNode('', children=[child4, PageNode('')])
|
||||
])
|
||||
])
|
||||
child1 = PageNode("", template="test1.html")
|
||||
child2 = PageNode("", template="test2.html")
|
||||
child3 = PageNode("", template="test3.html")
|
||||
child4 = PageNode("", template="test4.html")
|
||||
root = PageRoot(
|
||||
"",
|
||||
template="root.html",
|
||||
children=[
|
||||
PageNode("", children=[PageNode("", children=[child1])]),
|
||||
PageNode("", children=[PageNode("", children=[child2, child3]), PageNode("", children=[child4, PageNode("")])]),
|
||||
],
|
||||
)
|
||||
assert root.previous is None
|
||||
assert child1.previous == root
|
||||
assert child2.previous == child1
|
||||
|
@ -135,9 +131,9 @@ class TestPageNode(TestCase):
|
|||
"""
|
||||
Next should return the next sibling node, or None if one doesn't exist.
|
||||
"""
|
||||
child1 = PageNode('', template='test1.html')
|
||||
child2 = PageNode('', template='test1.html')
|
||||
PageRoot('', children=[child1, child2])
|
||||
child1 = PageNode("", template="test1.html")
|
||||
child2 = PageNode("", template="test1.html")
|
||||
PageRoot("", children=[child1, child2])
|
||||
assert child1.next == child2
|
||||
assert child2.next is None
|
||||
|
||||
|
@ -154,67 +150,65 @@ class TestPageNode(TestCase):
|
|||
# O O O
|
||||
# / / \ / \
|
||||
# c1 c2 c3 c4 O
|
||||
child1 = PageNode('', template='test1.html')
|
||||
child2 = PageNode('', template='test2.html')
|
||||
child3 = PageNode('', template='test3.html')
|
||||
child4 = PageNode('', template='test4.html')
|
||||
root = PageRoot('', template='root.html', children=[
|
||||
PageNode('', children=[
|
||||
PageNode('', children=[child1])
|
||||
]),
|
||||
PageNode('', children=[
|
||||
PageNode('', children=[child2, child3]),
|
||||
PageNode('', children=[child4, PageNode('')])
|
||||
])
|
||||
])
|
||||
child1 = PageNode("", template="test1.html")
|
||||
child2 = PageNode("", template="test2.html")
|
||||
child3 = PageNode("", template="test3.html")
|
||||
child4 = PageNode("", template="test4.html")
|
||||
root = PageRoot(
|
||||
"",
|
||||
template="root.html",
|
||||
children=[
|
||||
PageNode("", children=[PageNode("", children=[child1])]),
|
||||
PageNode("", children=[PageNode("", children=[child2, child3]), PageNode("", children=[child4, PageNode("")])]),
|
||||
],
|
||||
)
|
||||
assert root.next == child1
|
||||
assert child1.next == child2
|
||||
assert child2.next == child3
|
||||
assert child3.next == child4
|
||||
assert child4.next is None
|
||||
|
||||
@patch('bedrock.mozorg.hierarchy.reverse')
|
||||
@patch("bedrock.mozorg.hierarchy.reverse")
|
||||
def test_url(self, reverse):
|
||||
"""If a node has a page, url should return the url for that page."""
|
||||
node = PageRoot('test', path='asdf/qwer', template='fake.html')
|
||||
reverse.return_value = 'asdf'
|
||||
assert node.url == 'asdf'
|
||||
reverse.assert_called_with('fake')
|
||||
node = PageRoot("test", path="asdf/qwer", template="fake.html")
|
||||
reverse.return_value = "asdf"
|
||||
assert node.url == "asdf"
|
||||
reverse.assert_called_with("fake")
|
||||
|
||||
@patch('bedrock.mozorg.hierarchy.reverse')
|
||||
@patch("bedrock.mozorg.hierarchy.reverse")
|
||||
def test_url_child(self, reverse):
|
||||
"""
|
||||
If a node doesn't have a page, but has children, it should return the
|
||||
url of its first child.
|
||||
"""
|
||||
child1 = PageNode('test', path='asdf/qwer', template='fake.html')
|
||||
child2 = PageNode('test', path='bb/qr', template='fake2.html')
|
||||
parent = PageRoot('', children=[child1, child2])
|
||||
child1 = PageNode("test", path="asdf/qwer", template="fake.html")
|
||||
child2 = PageNode("test", path="bb/qr", template="fake2.html")
|
||||
parent = PageRoot("", children=[child1, child2])
|
||||
|
||||
reverse.return_value = 'asdf'
|
||||
assert parent.url == 'asdf'
|
||||
reverse.assert_called_with('fake')
|
||||
reverse.return_value = "asdf"
|
||||
assert parent.url == "asdf"
|
||||
reverse.assert_called_with("fake")
|
||||
|
||||
def test_url_none(self):
|
||||
"""If a node doesn't have a page or children, url should return None."""
|
||||
node = PageNode('')
|
||||
node = PageNode("")
|
||||
assert node.url is None
|
||||
|
||||
|
||||
class TestPageRoot(TestCase):
|
||||
@patch.object(PageNode, 'page')
|
||||
@patch.object(PageNode, "page")
|
||||
def test_as_urlpatterns(self, page):
|
||||
"""
|
||||
as_urlpatterns should return a urlconf with the pages for all the nodes
|
||||
included in the tree.
|
||||
"""
|
||||
child1 = PageNode('child1', path='asdf/qwer', template='fake.html')
|
||||
child2 = PageNode('child2', path='bb/qr', template='fake2.html')
|
||||
parent = PageNode('parent', children=[child1, child2])
|
||||
root = PageRoot('root', path='badsbi', template='fake3.html',
|
||||
children=[parent])
|
||||
child1 = PageNode("child1", path="asdf/qwer", template="fake.html")
|
||||
child2 = PageNode("child2", path="bb/qr", template="fake2.html")
|
||||
parent = PageNode("parent", children=[child1, child2])
|
||||
root = PageRoot("root", path="badsbi", template="fake3.html", children=[parent])
|
||||
|
||||
# Mocking properties
|
||||
page.__get__ = lambda mock, self, cls: self.display_name
|
||||
|
||||
assert root.as_urlpatterns() == ['root', 'child1', 'child2']
|
||||
assert root.as_urlpatterns() == ["root", "child1", "child2"]
|
||||
|
|
|
@ -18,34 +18,34 @@ class TestClacksOverheadMiddleware(TestCase):
|
|||
def test_good_response_has_header(self):
|
||||
self.response.status_code = 200
|
||||
self.middleware.process_response(self.request, self.response)
|
||||
self.assertEqual(self.response['X-Clacks-Overhead'], 'GNU Terry Pratchett')
|
||||
self.assertEqual(self.response["X-Clacks-Overhead"], "GNU Terry Pratchett")
|
||||
|
||||
def test_other_response_has_no_header(self):
|
||||
self.response.status_code = 301
|
||||
self.middleware.process_response(self.request, self.response)
|
||||
self.assertNotIn('X-Clacks-Overhead', self.response)
|
||||
self.assertNotIn("X-Clacks-Overhead", self.response)
|
||||
|
||||
self.response.status_code = 404
|
||||
self.middleware.process_response(self.request, self.response)
|
||||
self.assertNotIn('X-Clacks-Overhead', self.response)
|
||||
self.assertNotIn("X-Clacks-Overhead", self.response)
|
||||
|
||||
|
||||
@override_settings(ENABLE_HOSTNAME_MIDDLEWARE=True)
|
||||
class TestHostnameMiddleware(TestCase):
|
||||
@override_settings(HOSTNAME='foobar', CLUSTER_NAME='oregon-b')
|
||||
@override_settings(HOSTNAME="foobar", CLUSTER_NAME="oregon-b")
|
||||
def test_base(self):
|
||||
self.middleware = HostnameMiddleware()
|
||||
self.request = HttpRequest()
|
||||
self.response = HttpResponse()
|
||||
|
||||
self.middleware.process_response(self.request, self.response)
|
||||
self.assertEqual(self.response['X-Backend-Server'], 'foobar.oregon-b')
|
||||
self.assertEqual(self.response["X-Backend-Server"], "foobar.oregon-b")
|
||||
|
||||
@override_settings(
|
||||
MIDDLEWARE=(list(settings.MIDDLEWARE) + ['bedrock.mozorg.middleware.HostnameMiddleware']),
|
||||
HOSTNAME='foobar',
|
||||
CLUSTER_NAME='el-dudarino',
|
||||
MIDDLEWARE=(list(settings.MIDDLEWARE) + ["bedrock.mozorg.middleware.HostnameMiddleware"]),
|
||||
HOSTNAME="foobar",
|
||||
CLUSTER_NAME="el-dudarino",
|
||||
)
|
||||
def test_request(self):
|
||||
response = self.client.get('/en-US/')
|
||||
self.assertEqual(response['X-Backend-Server'], 'foobar.el-dudarino')
|
||||
response = self.client.get("/en-US/")
|
||||
self.assertEqual(response["X-Backend-Server"], "foobar.el-dudarino")
|
||||
|
|
|
@ -15,16 +15,15 @@ from bedrock.mozorg.util import (
|
|||
page,
|
||||
)
|
||||
|
||||
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_files')
|
||||
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files")
|
||||
|
||||
|
||||
class TestGetFacebookLikeLocale(TestCase):
|
||||
|
||||
def test_supported_locale(self):
|
||||
"""
|
||||
Return the given locale if supported.
|
||||
"""
|
||||
assert get_fb_like_locale('en-PI') == 'en_PI'
|
||||
assert get_fb_like_locale("en-PI") == "en_PI"
|
||||
|
||||
def test_first_supported_locale_for_language(self):
|
||||
"""
|
||||
|
@ -32,40 +31,38 @@ class TestGetFacebookLikeLocale(TestCase):
|
|||
the supported locales and return the first one that
|
||||
matches the language.
|
||||
"""
|
||||
assert get_fb_like_locale('es-AR') == 'es_ES'
|
||||
assert get_fb_like_locale("es-AR") == "es_ES"
|
||||
|
||||
def test_unsupported_locale(self):
|
||||
"""
|
||||
Return the default en_US when locale isn't supported.
|
||||
"""
|
||||
assert get_fb_like_locale('zz-ZZ') == 'en_US'
|
||||
assert get_fb_like_locale("zz-ZZ") == "en_US"
|
||||
|
||||
|
||||
@patch('bedrock.mozorg.util.l10n_utils')
|
||||
@patch("bedrock.mozorg.util.l10n_utils")
|
||||
class TestPageUtil(TestCase):
|
||||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def test_locale_redirect(self, l10n_mock):
|
||||
"""Should use l10n render."""
|
||||
url = page('walter/abides', 'walter/abides.html', donny='ashes')
|
||||
url.callback(self.rf.get('/walter/abides/'))
|
||||
l10n_mock.render.assert_called_with(ANY, 'walter/abides.html', {'urlname': 'walter.abides',
|
||||
'donny': 'ashes'}, ftl_files=None)
|
||||
url = page("walter/abides", "walter/abides.html", donny="ashes")
|
||||
url.callback(self.rf.get("/walter/abides/"))
|
||||
l10n_mock.render.assert_called_with(ANY, "walter/abides.html", {"urlname": "walter.abides", "donny": "ashes"}, ftl_files=None)
|
||||
|
||||
def test_locale_redirect_works_home_page(self, l10n_mock):
|
||||
"""Make sure the home page still works. "/" is a special case."""
|
||||
url = page('', 'index.html')
|
||||
url.callback(self.rf.get('/'))
|
||||
l10n_mock.render.assert_called_with(ANY, 'index.html', {'urlname': 'index'}, ftl_files=None)
|
||||
url = page("", "index.html")
|
||||
url.callback(self.rf.get("/"))
|
||||
l10n_mock.render.assert_called_with(ANY, "index.html", {"urlname": "index"}, ftl_files=None)
|
||||
|
||||
def test_url_name_set_from_template(self, l10n_mock):
|
||||
"""If not provided the URL pattern name should be set from the template path."""
|
||||
url = page('lebowski/urban_achievers', 'lebowski/achievers.html')
|
||||
assert url.name == 'lebowski.achievers'
|
||||
url = page("lebowski/urban_achievers", "lebowski/achievers.html")
|
||||
assert url.name == "lebowski.achievers"
|
||||
|
||||
def test_url_name_set_from_param(self, l10n_mock):
|
||||
"""If provided the URL pattern name should be set from the parameter."""
|
||||
url = page('lebowski/urban_achievers', 'lebowski/achievers.html',
|
||||
url_name='proud.we.are.of.all.of.them')
|
||||
assert url.name == 'proud.we.are.of.all.of.them'
|
||||
url = page("lebowski/urban_achievers", "lebowski/achievers.html", url_name="proud.we.are.of.all.of.them")
|
||||
assert url.name == "proud.we.are.of.all.of.them"
|
||||
|
|
|
@ -14,23 +14,23 @@ from bedrock.mozorg import views
|
|||
|
||||
|
||||
class TestViews(TestCase):
|
||||
@patch.dict(os.environ, FUNNELCAKE_5_LOCALES='en-US', FUNNELCAKE_5_PLATFORMS='win')
|
||||
@patch.dict(os.environ, FUNNELCAKE_5_LOCALES="en-US", FUNNELCAKE_5_PLATFORMS="win")
|
||||
def test_download_button_funnelcake(self):
|
||||
"""The download button should have the funnelcake ID."""
|
||||
with self.activate('en-US'):
|
||||
resp = self.client.get(reverse('mozorg.home'), {'f': '5'})
|
||||
assert b'product=firefox-stub-f5&' in resp.content
|
||||
with self.activate("en-US"):
|
||||
resp = self.client.get(reverse("mozorg.home"), {"f": "5"})
|
||||
assert b"product=firefox-stub-f5&" in resp.content
|
||||
|
||||
def test_download_button_bad_funnelcake(self):
|
||||
"""The download button should not have a bad funnelcake ID."""
|
||||
with self.activate('en-US'):
|
||||
resp = self.client.get(reverse('mozorg.home'), {'f': '5dude'})
|
||||
assert b'product=firefox-stub&' in resp.content
|
||||
assert b'product=firefox-stub-f5dude&' not in resp.content
|
||||
with self.activate("en-US"):
|
||||
resp = self.client.get(reverse("mozorg.home"), {"f": "5dude"})
|
||||
assert b"product=firefox-stub&" in resp.content
|
||||
assert b"product=firefox-stub-f5dude&" not in resp.content
|
||||
|
||||
resp = self.client.get(reverse('mozorg.home'), {'f': '999999999'})
|
||||
assert b'product=firefox-stub&' in resp.content
|
||||
assert b'product=firefox-stub-f999999999&' not in resp.content
|
||||
resp = self.client.get(reverse("mozorg.home"), {"f": "999999999"})
|
||||
assert b"product=firefox-stub&" in resp.content
|
||||
assert b"product=firefox-stub-f999999999&" not in resp.content
|
||||
|
||||
|
||||
class TestRobots(TestCase):
|
||||
|
@ -39,45 +39,45 @@ class TestRobots(TestCase):
|
|||
self.view = views.Robots()
|
||||
|
||||
def test_production_disallow_all_is_false(self):
|
||||
self.view.request = self.rf.get('/', HTTP_HOST='www.mozilla.org')
|
||||
self.assertFalse(self.view.get_context_data()['disallow_all'])
|
||||
self.view.request = self.rf.get("/", HTTP_HOST="www.mozilla.org")
|
||||
self.assertFalse(self.view.get_context_data()["disallow_all"])
|
||||
|
||||
def test_non_production_disallow_all_is_true(self):
|
||||
self.view.request = self.rf.get('/', HTTP_HOST='www.allizom.org')
|
||||
self.assertTrue(self.view.get_context_data()['disallow_all'])
|
||||
self.view.request = self.rf.get("/", HTTP_HOST="www.allizom.org")
|
||||
self.assertTrue(self.view.get_context_data()["disallow_all"])
|
||||
|
||||
def test_robots_no_redirect(self):
|
||||
response = self.client.get('/robots.txt', HTTP_HOST='www.mozilla.org')
|
||||
response = self.client.get("/robots.txt", HTTP_HOST="www.mozilla.org")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(response.context_data['disallow_all'])
|
||||
self.assertEqual(response.get('Content-Type'), 'text/plain')
|
||||
self.assertFalse(response.context_data["disallow_all"])
|
||||
self.assertEqual(response.get("Content-Type"), "text/plain")
|
||||
|
||||
|
||||
@patch('bedrock.mozorg.views.l10n_utils.render')
|
||||
@patch("bedrock.mozorg.views.l10n_utils.render")
|
||||
class TestHomePage(TestCase):
|
||||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def test_home_en_template(self, render_mock):
|
||||
req = RequestFactory().get('/')
|
||||
req.locale = 'en-US'
|
||||
req = RequestFactory().get("/")
|
||||
req.locale = "en-US"
|
||||
views.home_view(req)
|
||||
render_mock.assert_called_once_with(req, 'mozorg/home/home-en.html', ANY)
|
||||
render_mock.assert_called_once_with(req, "mozorg/home/home-en.html", ANY)
|
||||
|
||||
def test_home_de_template(self, render_mock):
|
||||
req = RequestFactory().get('/')
|
||||
req.locale = 'de'
|
||||
req = RequestFactory().get("/")
|
||||
req.locale = "de"
|
||||
views.home_view(req)
|
||||
render_mock.assert_called_once_with(req, 'mozorg/home/home-de.html', ANY)
|
||||
render_mock.assert_called_once_with(req, "mozorg/home/home-de.html", ANY)
|
||||
|
||||
def test_home_fr_template(self, render_mock):
|
||||
req = RequestFactory().get('/')
|
||||
req.locale = 'fr'
|
||||
req = RequestFactory().get("/")
|
||||
req.locale = "fr"
|
||||
views.home_view(req)
|
||||
render_mock.assert_called_once_with(req, 'mozorg/home/home-fr.html', ANY)
|
||||
render_mock.assert_called_once_with(req, "mozorg/home/home-fr.html", ANY)
|
||||
|
||||
def test_home_locale_template(self, render_mock):
|
||||
req = RequestFactory().get('/')
|
||||
req.locale = 'es'
|
||||
req = RequestFactory().get("/")
|
||||
req.locale = "es"
|
||||
views.home_view(req)
|
||||
render_mock.assert_called_once_with(req, 'mozorg/home/home.html', ANY)
|
||||
render_mock.assert_called_once_with(req, "mozorg/home/home.html", ANY)
|
||||
|
|
|
@ -10,12 +10,11 @@ from bedrock.mozorg.util import page
|
|||
|
||||
|
||||
def mock_view(request):
|
||||
return HttpResponse('test')
|
||||
return HttpResponse("test")
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include('%s.urls' % settings.PROJECT_MODULE)),
|
||||
|
||||
url(r"", include("%s.urls" % settings.PROJECT_MODULE)),
|
||||
# Used by test_helper
|
||||
page('base', 'base-protocol.html'),
|
||||
page("base", "base-protocol.html"),
|
||||
]
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.http import HttpResponse
|
|||
from bedrock.mozorg.decorators import cache_control_expires
|
||||
|
||||
|
||||
RESPONSE_CONTENT = 'The Dude abides, man.'
|
||||
RESPONSE_CONTENT = "The Dude abides, man."
|
||||
|
||||
|
||||
@cache_control_expires(48)
|
||||
|
|
|
@ -12,131 +12,108 @@ from bedrock.redirects.util import redirect
|
|||
|
||||
|
||||
urlpatterns = (
|
||||
url(r'^$', views.home_view, name='mozorg.home'),
|
||||
page('about', 'mozorg/about/index.html', ftl_files=['mozorg/about']),
|
||||
page('about/manifesto', 'mozorg/about/manifesto.html', ftl_files=['mozorg/about/manifesto']),
|
||||
page('about/manifesto/details', 'mozorg/about/manifesto-details.html', ftl_files=['mozorg/about/manifesto']),
|
||||
page('about/leadership', 'mozorg/about/leadership.html'),
|
||||
page('about/policy/lean-data', 'mozorg/about/policy/lean-data/index.html'),
|
||||
page('about/policy/lean-data/build-security', 'mozorg/about/policy/lean-data/build-security.html'),
|
||||
page('about/policy/lean-data/stay-lean', 'mozorg/about/policy/lean-data/stay-lean.html'),
|
||||
page('about/policy/lean-data/engage-users', 'mozorg/about/policy/lean-data/engage-users.html'),
|
||||
page('about/policy/patents', 'mozorg/about/policy/patents/index.html'),
|
||||
page('about/policy/patents/license', 'mozorg/about/policy/patents/license.html'),
|
||||
page('about/policy/patents/license/1.0', 'mozorg/about/policy/patents/license-1.0.html'),
|
||||
page('about/policy/patents/guide', 'mozorg/about/policy/patents/guide.html'),
|
||||
page('book', 'mozorg/book.html'),
|
||||
url('^credits/$', views.credits_view, name='mozorg.credits'),
|
||||
page('credits/faq', 'mozorg/credits-faq.html'),
|
||||
page('about/history', 'mozorg/about/history.html', ftl_files=['mozorg/about/history']),
|
||||
url(r"^$", views.home_view, name="mozorg.home"),
|
||||
page("about", "mozorg/about/index.html", ftl_files=["mozorg/about"]),
|
||||
page("about/manifesto", "mozorg/about/manifesto.html", ftl_files=["mozorg/about/manifesto"]),
|
||||
page("about/manifesto/details", "mozorg/about/manifesto-details.html", ftl_files=["mozorg/about/manifesto"]),
|
||||
page("about/leadership", "mozorg/about/leadership.html"),
|
||||
page("about/policy/lean-data", "mozorg/about/policy/lean-data/index.html"),
|
||||
page("about/policy/lean-data/build-security", "mozorg/about/policy/lean-data/build-security.html"),
|
||||
page("about/policy/lean-data/stay-lean", "mozorg/about/policy/lean-data/stay-lean.html"),
|
||||
page("about/policy/lean-data/engage-users", "mozorg/about/policy/lean-data/engage-users.html"),
|
||||
page("about/policy/patents", "mozorg/about/policy/patents/index.html"),
|
||||
page("about/policy/patents/license", "mozorg/about/policy/patents/license.html"),
|
||||
page("about/policy/patents/license/1.0", "mozorg/about/policy/patents/license-1.0.html"),
|
||||
page("about/policy/patents/guide", "mozorg/about/policy/patents/guide.html"),
|
||||
page("book", "mozorg/book.html"),
|
||||
url("^credits/$", views.credits_view, name="mozorg.credits"),
|
||||
page("credits/faq", "mozorg/credits-faq.html"),
|
||||
page("about/history", "mozorg/about/history.html", ftl_files=["mozorg/about/history"]),
|
||||
# Bug 981063, catch all for old calendar urls.
|
||||
# must be here to avoid overriding the above
|
||||
redirect(r'^projects/calendar/', 'https://www.thunderbird.net/calendar/', locale_prefix=False),
|
||||
page('mission', 'mozorg/mission.html', ftl_files=['mozorg/mission']),
|
||||
url('^about/forums/$', views.forums_view, name='mozorg.about.forums.forums'),
|
||||
page('about/forums/etiquette', 'mozorg/about/forums/etiquette.html'),
|
||||
page('about/forums/cancellation', 'mozorg/about/forums/cancellation.html'),
|
||||
page('about/governance', 'mozorg/about/governance/governance.html'),
|
||||
page('about/governance/roles', 'mozorg/about/governance/roles.html'),
|
||||
page('about/governance/policies', 'mozorg/about/governance/policies/policies.html'),
|
||||
page('about/governance/policies/commit', 'mozorg/about/governance/policies/commit.html'),
|
||||
page('about/governance/policies/commit/access-policy',
|
||||
'mozorg/about/governance/policies/commit/access-policy.html'),
|
||||
page('about/governance/policies/commit/requirements',
|
||||
'mozorg/about/governance/policies/commit/requirements.html'),
|
||||
page('about/governance/policies/security-group/bugs',
|
||||
'mozorg/about/governance/policies/security/bugs.html'),
|
||||
page('about/governance/policies/security-group/membership',
|
||||
'mozorg/about/governance/policies/security/membership.html'),
|
||||
page('about/governance/policies/security-group/certs/policy',
|
||||
'mozorg/about/governance/policies/security/certs/policy.html'),
|
||||
page('about/governance/organizations', 'mozorg/about/governance/organizations.html'),
|
||||
page('about/governance/policies/participation/reporting',
|
||||
'mozorg/about/governance/policies/reporting.html',
|
||||
ftl_files=['mozorg/about/governance/policies/reporting']),
|
||||
page('about/governance/policies/participation',
|
||||
'mozorg/about/governance/policies/participation.html',
|
||||
ftl_files=['mozorg/about/governance/policies/participation']),
|
||||
page('about/governance/policies/participation/reporting/community-hotline',
|
||||
'mozorg/about/governance/policies/community-hotline.html',
|
||||
ftl_files=['mozorg/about/governance/policies/community-hotline']),
|
||||
page('about/governance/policies/module-ownership',
|
||||
'mozorg/about/governance/policies/module-ownership.html'),
|
||||
page('about/governance/policies/regressions',
|
||||
'mozorg/about/governance/policies/regressions.html'),
|
||||
|
||||
page('about/policy/transparency', 'mozorg/about/policy/transparency/index.html'),
|
||||
page('about/policy/transparency/jan-dec-2015',
|
||||
'mozorg/about/policy/transparency/jan-dec-2015.html'),
|
||||
page('about/policy/transparency/jan-jun-2016',
|
||||
'mozorg/about/policy/transparency/jan-jun-2016.html'),
|
||||
page('about/policy/transparency/jul-dec-2016',
|
||||
'mozorg/about/policy/transparency/jul-dec-2016.html'),
|
||||
page('about/policy/transparency/jan-jun-2017',
|
||||
'mozorg/about/policy/transparency/jan-jun-2017.html'),
|
||||
page('about/policy/transparency/jul-dec-2017',
|
||||
'mozorg/about/policy/transparency/jul-dec-2017.html'),
|
||||
page('about/policy/transparency/jan-jun-2018',
|
||||
'mozorg/about/policy/transparency/jan-jun-2018.html'),
|
||||
page('about/policy/transparency/jul-dec-2018',
|
||||
'mozorg/about/policy/transparency/jul-dec-2018.html'),
|
||||
page('about/policy/transparency/jan-jun-2019',
|
||||
'mozorg/about/policy/transparency/jan-jun-2019.html'),
|
||||
page('about/policy/transparency/jul-dec-2019',
|
||||
'mozorg/about/policy/transparency/jul-dec-2019.html'),
|
||||
page('about/policy/transparency/jan-jun-2020',
|
||||
'mozorg/about/policy/transparency/jan-jun-2020.html'),
|
||||
page('about/policy/transparency/jul-dec-2020',
|
||||
'mozorg/about/policy/transparency/jul-dec-2020.html'),
|
||||
page('about/policy/transparency/jan-jun-2021',
|
||||
'mozorg/about/policy/transparency/jan-jun-2021.html'),
|
||||
|
||||
page('contact', 'mozorg/contact/contact-landing.html'),
|
||||
page('contact/spaces', 'mozorg/contact/spaces/spaces-landing.html'),
|
||||
page('contact/spaces/beijing', 'mozorg/contact/spaces/beijing.html'),
|
||||
page('contact/spaces/berlin', 'mozorg/contact/spaces/berlin.html'),
|
||||
page('contact/spaces/paris', 'mozorg/contact/spaces/paris.html'),
|
||||
page('contact/spaces/portland', 'mozorg/contact/spaces/portland.html'),
|
||||
page('contact/spaces/san-francisco', 'mozorg/contact/spaces/san-francisco.html'),
|
||||
page('contact/spaces/toronto', 'mozorg/contact/spaces/toronto.html'),
|
||||
|
||||
page('MPL', 'mozorg/mpl/index.html'),
|
||||
page('MPL/historical', 'mozorg/mpl/historical.html'),
|
||||
page('MPL/license-policy', 'mozorg/mpl/license-policy.html'),
|
||||
page('MPL/headers', 'mozorg/mpl/headers/index.html'),
|
||||
page('MPL/1.1', 'mozorg/mpl/1.1/index.html'),
|
||||
page('MPL/1.1/FAQ', 'mozorg/mpl/1.1/faq.html'),
|
||||
page('MPL/1.1/annotated', 'mozorg/mpl/1.1/annotated/index.html'),
|
||||
page('MPL/2.0', 'mozorg/mpl/2.0/index.html'),
|
||||
page('MPL/2.0/FAQ', 'mozorg/mpl/2.0/faq.html'),
|
||||
page('MPL/2.0/Revision-FAQ', 'mozorg/mpl/2.0/revision-faq.html'),
|
||||
page('MPL/2.0/combining-mpl-and-gpl', 'mozorg/mpl/2.0/combining-mpl-and-gpl.html'),
|
||||
page('MPL/2.0/differences', 'mozorg/mpl/2.0/differences.html'),
|
||||
page('MPL/2.0/permissive-code-into-mpl', 'mozorg/mpl/2.0/permissive-code-into-mpl.html'),
|
||||
|
||||
page('contribute', 'mozorg/contribute.html', ftl_files=['mozorg/contribute']),
|
||||
|
||||
page('moss', 'mozorg/moss/index.html'),
|
||||
page('moss/foundational-technology', 'mozorg/moss/foundational-technology.html'),
|
||||
page('moss/mission-partners', 'mozorg/moss/mission-partners.html'),
|
||||
page('moss/secure-open-source', 'mozorg/moss/secure-open-source.html'),
|
||||
|
||||
url(r'^robots\.txt$', views.Robots.as_view(), name='robots.txt'),
|
||||
|
||||
redirect(r"^projects/calendar/", "https://www.thunderbird.net/calendar/", locale_prefix=False),
|
||||
page("mission", "mozorg/mission.html", ftl_files=["mozorg/mission"]),
|
||||
url("^about/forums/$", views.forums_view, name="mozorg.about.forums.forums"),
|
||||
page("about/forums/etiquette", "mozorg/about/forums/etiquette.html"),
|
||||
page("about/forums/cancellation", "mozorg/about/forums/cancellation.html"),
|
||||
page("about/governance", "mozorg/about/governance/governance.html"),
|
||||
page("about/governance/roles", "mozorg/about/governance/roles.html"),
|
||||
page("about/governance/policies", "mozorg/about/governance/policies/policies.html"),
|
||||
page("about/governance/policies/commit", "mozorg/about/governance/policies/commit.html"),
|
||||
page("about/governance/policies/commit/access-policy", "mozorg/about/governance/policies/commit/access-policy.html"),
|
||||
page("about/governance/policies/commit/requirements", "mozorg/about/governance/policies/commit/requirements.html"),
|
||||
page("about/governance/policies/security-group/bugs", "mozorg/about/governance/policies/security/bugs.html"),
|
||||
page("about/governance/policies/security-group/membership", "mozorg/about/governance/policies/security/membership.html"),
|
||||
page("about/governance/policies/security-group/certs/policy", "mozorg/about/governance/policies/security/certs/policy.html"),
|
||||
page("about/governance/organizations", "mozorg/about/governance/organizations.html"),
|
||||
page(
|
||||
"about/governance/policies/participation/reporting",
|
||||
"mozorg/about/governance/policies/reporting.html",
|
||||
ftl_files=["mozorg/about/governance/policies/reporting"],
|
||||
),
|
||||
page(
|
||||
"about/governance/policies/participation",
|
||||
"mozorg/about/governance/policies/participation.html",
|
||||
ftl_files=["mozorg/about/governance/policies/participation"],
|
||||
),
|
||||
page(
|
||||
"about/governance/policies/participation/reporting/community-hotline",
|
||||
"mozorg/about/governance/policies/community-hotline.html",
|
||||
ftl_files=["mozorg/about/governance/policies/community-hotline"],
|
||||
),
|
||||
page("about/governance/policies/module-ownership", "mozorg/about/governance/policies/module-ownership.html"),
|
||||
page("about/governance/policies/regressions", "mozorg/about/governance/policies/regressions.html"),
|
||||
page("about/policy/transparency", "mozorg/about/policy/transparency/index.html"),
|
||||
page("about/policy/transparency/jan-dec-2015", "mozorg/about/policy/transparency/jan-dec-2015.html"),
|
||||
page("about/policy/transparency/jan-jun-2016", "mozorg/about/policy/transparency/jan-jun-2016.html"),
|
||||
page("about/policy/transparency/jul-dec-2016", "mozorg/about/policy/transparency/jul-dec-2016.html"),
|
||||
page("about/policy/transparency/jan-jun-2017", "mozorg/about/policy/transparency/jan-jun-2017.html"),
|
||||
page("about/policy/transparency/jul-dec-2017", "mozorg/about/policy/transparency/jul-dec-2017.html"),
|
||||
page("about/policy/transparency/jan-jun-2018", "mozorg/about/policy/transparency/jan-jun-2018.html"),
|
||||
page("about/policy/transparency/jul-dec-2018", "mozorg/about/policy/transparency/jul-dec-2018.html"),
|
||||
page("about/policy/transparency/jan-jun-2019", "mozorg/about/policy/transparency/jan-jun-2019.html"),
|
||||
page("about/policy/transparency/jul-dec-2019", "mozorg/about/policy/transparency/jul-dec-2019.html"),
|
||||
page("about/policy/transparency/jan-jun-2020", "mozorg/about/policy/transparency/jan-jun-2020.html"),
|
||||
page("about/policy/transparency/jul-dec-2020", "mozorg/about/policy/transparency/jul-dec-2020.html"),
|
||||
page("about/policy/transparency/jan-jun-2021", "mozorg/about/policy/transparency/jan-jun-2021.html"),
|
||||
page("contact", "mozorg/contact/contact-landing.html"),
|
||||
page("contact/spaces", "mozorg/contact/spaces/spaces-landing.html"),
|
||||
page("contact/spaces/beijing", "mozorg/contact/spaces/beijing.html"),
|
||||
page("contact/spaces/berlin", "mozorg/contact/spaces/berlin.html"),
|
||||
page("contact/spaces/paris", "mozorg/contact/spaces/paris.html"),
|
||||
page("contact/spaces/portland", "mozorg/contact/spaces/portland.html"),
|
||||
page("contact/spaces/san-francisco", "mozorg/contact/spaces/san-francisco.html"),
|
||||
page("contact/spaces/toronto", "mozorg/contact/spaces/toronto.html"),
|
||||
page("MPL", "mozorg/mpl/index.html"),
|
||||
page("MPL/historical", "mozorg/mpl/historical.html"),
|
||||
page("MPL/license-policy", "mozorg/mpl/license-policy.html"),
|
||||
page("MPL/headers", "mozorg/mpl/headers/index.html"),
|
||||
page("MPL/1.1", "mozorg/mpl/1.1/index.html"),
|
||||
page("MPL/1.1/FAQ", "mozorg/mpl/1.1/faq.html"),
|
||||
page("MPL/1.1/annotated", "mozorg/mpl/1.1/annotated/index.html"),
|
||||
page("MPL/2.0", "mozorg/mpl/2.0/index.html"),
|
||||
page("MPL/2.0/FAQ", "mozorg/mpl/2.0/faq.html"),
|
||||
page("MPL/2.0/Revision-FAQ", "mozorg/mpl/2.0/revision-faq.html"),
|
||||
page("MPL/2.0/combining-mpl-and-gpl", "mozorg/mpl/2.0/combining-mpl-and-gpl.html"),
|
||||
page("MPL/2.0/differences", "mozorg/mpl/2.0/differences.html"),
|
||||
page("MPL/2.0/permissive-code-into-mpl", "mozorg/mpl/2.0/permissive-code-into-mpl.html"),
|
||||
page("contribute", "mozorg/contribute.html", ftl_files=["mozorg/contribute"]),
|
||||
page("moss", "mozorg/moss/index.html"),
|
||||
page("moss/foundational-technology", "mozorg/moss/foundational-technology.html"),
|
||||
page("moss/mission-partners", "mozorg/moss/mission-partners.html"),
|
||||
page("moss/secure-open-source", "mozorg/moss/secure-open-source.html"),
|
||||
url(r"^robots\.txt$", views.Robots.as_view(), name="robots.txt"),
|
||||
# namespaces
|
||||
url(r'^2004/em-rdf$', views.namespaces, {'namespace': 'em-rdf'}),
|
||||
url(r'^2005/app-update$', views.namespaces, {'namespace': 'update'}),
|
||||
url(r'^2006/addons-blocklist$', views.namespaces, {'namespace': 'addons-bl'}),
|
||||
url(r'^2006/browser/search/$', views.namespaces, {'namespace': 'mozsearch'}),
|
||||
url(r'^keymaster/gatekeeper/there\.is\.only\.xul$', views.namespaces, {'namespace': 'xul'}),
|
||||
url(r'^microsummaries/0\.1$', views.namespaces, {'namespace': 'microsummaries'}),
|
||||
url(r'^projects/xforms/2005/type$', views.namespaces, {'namespace': 'xforms-type'}),
|
||||
url(r'^xbl$', views.namespaces, {'namespace': 'xbl'}),
|
||||
|
||||
page('locales', 'mozorg/locales.html'),
|
||||
url(r"^2004/em-rdf$", views.namespaces, {"namespace": "em-rdf"}),
|
||||
url(r"^2005/app-update$", views.namespaces, {"namespace": "update"}),
|
||||
url(r"^2006/addons-blocklist$", views.namespaces, {"namespace": "addons-bl"}),
|
||||
url(r"^2006/browser/search/$", views.namespaces, {"namespace": "mozsearch"}),
|
||||
url(r"^keymaster/gatekeeper/there\.is\.only\.xul$", views.namespaces, {"namespace": "xul"}),
|
||||
url(r"^microsummaries/0\.1$", views.namespaces, {"namespace": "microsummaries"}),
|
||||
url(r"^projects/xforms/2005/type$", views.namespaces, {"namespace": "xforms-type"}),
|
||||
url(r"^xbl$", views.namespaces, {"namespace": "xbl"}),
|
||||
page("locales", "mozorg/locales.html"),
|
||||
)
|
||||
|
||||
if settings.DEV:
|
||||
urlpatterns += (
|
||||
path('contentful-preview/<content_id>/', views.ContentfulPreviewView.as_view()),
|
||||
)
|
||||
urlpatterns += (path("contentful-preview/<content_id>/", views.ContentfulPreviewView.as_view()),)
|
||||
|
|
|
@ -17,7 +17,7 @@ except ImportError:
|
|||
newrelic = False
|
||||
|
||||
|
||||
log = commonware.log.getLogger('mozorg.util')
|
||||
log = commonware.log.getLogger("mozorg.util")
|
||||
|
||||
|
||||
def page(name, tmpl, decorators=None, url_name=None, ftl_files=None, **kwargs):
|
||||
|
@ -47,12 +47,12 @@ def page(name, tmpl, decorators=None, url_name=None, ftl_files=None, **kwargs):
|
|||
@param kwargs: Any additional arguments are passed to l10n_utils.render
|
||||
as the context.
|
||||
"""
|
||||
pattern = r'^%s/$' % name if name else r'^$'
|
||||
pattern = r"^%s/$" % name if name else r"^$"
|
||||
|
||||
if url_name is None:
|
||||
# Set the name of the view to the template path replaced with dots
|
||||
(base, ext) = os.path.splitext(tmpl)
|
||||
url_name = base.replace('/', '.')
|
||||
url_name = base.replace("/", ".")
|
||||
|
||||
# we don't have a caching backend yet, so no csrf (it's just a
|
||||
# newsletter form anyway)
|
||||
|
@ -60,10 +60,9 @@ def page(name, tmpl, decorators=None, url_name=None, ftl_files=None, **kwargs):
|
|||
def _view(request):
|
||||
if newrelic:
|
||||
# Name this in New Relic to differentiate pages
|
||||
newrelic.agent.set_transaction_name(
|
||||
'mozorg.util.page:' + url_name.replace('.', '_'))
|
||||
newrelic.agent.set_transaction_name("mozorg.util.page:" + url_name.replace(".", "_"))
|
||||
|
||||
kwargs.setdefault('urlname', url_name)
|
||||
kwargs.setdefault("urlname", url_name)
|
||||
return l10n_utils.render(request, tmpl, kwargs, ftl_files=ftl_files)
|
||||
|
||||
# This is for graphite so that we can differentiate pages
|
||||
|
@ -81,8 +80,7 @@ def page(name, tmpl, decorators=None, url_name=None, ftl_files=None, **kwargs):
|
|||
for decorator in reversed(decorators):
|
||||
_view = decorator(_view)
|
||||
except TypeError:
|
||||
log.exception('decorators not iterable or does not contain '
|
||||
'callable items')
|
||||
log.exception("decorators not iterable or does not contain callable items")
|
||||
|
||||
return url(pattern, _view, name=url_name)
|
||||
|
||||
|
@ -98,15 +96,14 @@ def get_fb_like_locale(request_locale):
|
|||
Adapted from the facebookapp get_best_locale() util
|
||||
"""
|
||||
|
||||
lang = request_locale.replace('-', '_')
|
||||
lang = request_locale.replace("-", "_")
|
||||
|
||||
if lang not in settings.FACEBOOK_LIKE_LOCALES:
|
||||
lang_prefix = lang.split('_')[0]
|
||||
lang_prefix = lang.split("_")[0]
|
||||
|
||||
try:
|
||||
lang = next(locale for locale in settings.FACEBOOK_LIKE_LOCALES
|
||||
if locale.startswith(lang_prefix))
|
||||
lang = next(locale for locale in settings.FACEBOOK_LIKE_LOCALES if locale.startswith(lang_prefix))
|
||||
except StopIteration:
|
||||
lang = 'en_US'
|
||||
lang = "en_US"
|
||||
|
||||
return lang
|
||||
|
|
|
@ -20,165 +20,159 @@ from bedrock.contentful.models import ContentfulEntry
|
|||
from bedrock.mozorg.credits import CreditsFile
|
||||
from bedrock.pocketfeed.models import PocketArticle
|
||||
|
||||
credits_file = CreditsFile('credits')
|
||||
TECH_BLOG_SLUGS = ['hacks', 'cd', 'futurereleases']
|
||||
credits_file = CreditsFile("credits")
|
||||
TECH_BLOG_SLUGS = ["hacks", "cd", "futurereleases"]
|
||||
|
||||
|
||||
def csrf_failure(request, reason=''):
|
||||
template_vars = {'reason': reason}
|
||||
return l10n_utils.render(request, 'mozorg/csrf-failure.html', template_vars,
|
||||
status=403)
|
||||
def csrf_failure(request, reason=""):
|
||||
template_vars = {"reason": reason}
|
||||
return l10n_utils.render(request, "mozorg/csrf-failure.html", template_vars, status=403)
|
||||
|
||||
|
||||
@xframe_allow
|
||||
def hacks_newsletter(request):
|
||||
return l10n_utils.render(request,
|
||||
'mozorg/newsletter/hacks.mozilla.org.html')
|
||||
return l10n_utils.render(request, "mozorg/newsletter/hacks.mozilla.org.html")
|
||||
|
||||
|
||||
@require_safe
|
||||
def credits_view(request):
|
||||
"""Display the names of our contributors."""
|
||||
ctx = {'credits': credits_file}
|
||||
ctx = {"credits": credits_file}
|
||||
# not translated
|
||||
return django_render(request, 'mozorg/credits.html', ctx)
|
||||
return django_render(request, "mozorg/credits.html", ctx)
|
||||
|
||||
|
||||
@require_safe
|
||||
def forums_view(request):
|
||||
"""Display our mailing lists and newsgroups."""
|
||||
return l10n_utils.render(request, 'mozorg/about/forums/forums.html')
|
||||
return l10n_utils.render(request, "mozorg/about/forums/forums.html")
|
||||
|
||||
|
||||
class Robots(TemplateView):
|
||||
template_name = 'mozorg/robots.txt'
|
||||
content_type = 'text/plain'
|
||||
template_name = "mozorg/robots.txt"
|
||||
content_type = "text/plain"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
hostname = self.request.get_host()
|
||||
return {'disallow_all': not hostname == 'www.mozilla.org'}
|
||||
return {"disallow_all": not hostname == "www.mozilla.org"}
|
||||
|
||||
|
||||
NAMESPACES = {
|
||||
'addons-bl': {
|
||||
'namespace': 'http://www.mozilla.org/2006/addons-blocklist',
|
||||
'standard': 'Add-ons Blocklist',
|
||||
'docs': 'https://wiki.mozilla.org/Extension_Blocklisting:Code_Design',
|
||||
"addons-bl": {
|
||||
"namespace": "http://www.mozilla.org/2006/addons-blocklist",
|
||||
"standard": "Add-ons Blocklist",
|
||||
"docs": "https://wiki.mozilla.org/Extension_Blocklisting:Code_Design",
|
||||
},
|
||||
'em-rdf': {
|
||||
'namespace': 'http://www.mozilla.org/2004/em-rdf',
|
||||
'standard': 'Extension Manifest',
|
||||
'docs': 'https://developer.mozilla.org/en/Install_Manifests',
|
||||
"em-rdf": {
|
||||
"namespace": "http://www.mozilla.org/2004/em-rdf",
|
||||
"standard": "Extension Manifest",
|
||||
"docs": "https://developer.mozilla.org/en/Install_Manifests",
|
||||
},
|
||||
'microsummaries': {
|
||||
'namespace': 'http://www.mozilla.org/microsummaries/0.1',
|
||||
'standard': 'Microsummaries',
|
||||
'docs': 'https://developer.mozilla.org/en/Microsummary_XML_grammar_reference',
|
||||
"microsummaries": {
|
||||
"namespace": "http://www.mozilla.org/microsummaries/0.1",
|
||||
"standard": "Microsummaries",
|
||||
"docs": "https://developer.mozilla.org/en/Microsummary_XML_grammar_reference",
|
||||
},
|
||||
'mozsearch': {
|
||||
'namespace': 'http://www.mozilla.org/2006/browser/search/',
|
||||
'standard': 'MozSearch plugin format',
|
||||
'docs': 'https://developer.mozilla.org/en/Creating_MozSearch_plugins',
|
||||
"mozsearch": {
|
||||
"namespace": "http://www.mozilla.org/2006/browser/search/",
|
||||
"standard": "MozSearch plugin format",
|
||||
"docs": "https://developer.mozilla.org/en/Creating_MozSearch_plugins",
|
||||
},
|
||||
'update': {
|
||||
'namespace': 'http://www.mozilla.org/2005/app-update',
|
||||
'standard': 'Software Update Service',
|
||||
'docs': 'https://wiki.mozilla.org/Software_Update:Testing',
|
||||
"update": {
|
||||
"namespace": "http://www.mozilla.org/2005/app-update",
|
||||
"standard": "Software Update Service",
|
||||
"docs": "https://wiki.mozilla.org/Software_Update:Testing",
|
||||
},
|
||||
'xbl': {
|
||||
'namespace': 'http://www.mozilla.org/xbl',
|
||||
'standard': 'XML Binding Language (XBL)',
|
||||
'docs': 'https://developer.mozilla.org/en/XBL',
|
||||
"xbl": {
|
||||
"namespace": "http://www.mozilla.org/xbl",
|
||||
"standard": "XML Binding Language (XBL)",
|
||||
"docs": "https://developer.mozilla.org/en/XBL",
|
||||
},
|
||||
'xforms-type': {
|
||||
'namespace': 'http://www.mozilla.org/projects/xforms/2005/type',
|
||||
'standard': 'XForms mozType extension',
|
||||
'docs': 'https://developer.mozilla.org/en/XForms/Custom_Controls',
|
||||
"xforms-type": {
|
||||
"namespace": "http://www.mozilla.org/projects/xforms/2005/type",
|
||||
"standard": "XForms mozType extension",
|
||||
"docs": "https://developer.mozilla.org/en/XForms/Custom_Controls",
|
||||
},
|
||||
'xul': {
|
||||
'namespace': 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
|
||||
'standard': 'XML User Interface Language (XUL)',
|
||||
'docs': 'https://developer.mozilla.org/en/XUL',
|
||||
"xul": {
|
||||
"namespace": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"standard": "XML User Interface Language (XUL)",
|
||||
"docs": "https://developer.mozilla.org/en/XUL",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def namespaces(request, namespace):
|
||||
context = NAMESPACES[namespace]
|
||||
context['slug'] = namespace
|
||||
template = 'mozorg/namespaces.html'
|
||||
context["slug"] = namespace
|
||||
template = "mozorg/namespaces.html"
|
||||
return django_render(request, template, context)
|
||||
|
||||
|
||||
def home_view(request):
|
||||
locale = l10n_utils.get_locale(request)
|
||||
donate_params = settings.DONATE_PARAMS.get(
|
||||
locale, settings.DONATE_PARAMS['en-US'])
|
||||
donate_params = settings.DONATE_PARAMS.get(locale, settings.DONATE_PARAMS["en-US"])
|
||||
|
||||
# presets are stored as a string but, for the home banner
|
||||
# we need it as a list.
|
||||
donate_params['preset_list'] = donate_params['presets'].split(',')
|
||||
donate_params["preset_list"] = donate_params["presets"].split(",")
|
||||
ctx = {
|
||||
'donate_params': donate_params,
|
||||
'pocket_articles': PocketArticle.objects.all()[:4],
|
||||
'ftl_files': ['mozorg/home', 'mozorg/home-mr1-promo'],
|
||||
'add_active_locales': ['de', 'fr']
|
||||
"donate_params": donate_params,
|
||||
"pocket_articles": PocketArticle.objects.all()[:4],
|
||||
"ftl_files": ["mozorg/home", "mozorg/home-mr1-promo"],
|
||||
"add_active_locales": ["de", "fr"],
|
||||
}
|
||||
|
||||
if locale.startswith('en-'):
|
||||
if switch('contentful-homepage-en'):
|
||||
if locale.startswith("en-"):
|
||||
if switch("contentful-homepage-en"):
|
||||
try:
|
||||
template_name = 'mozorg/contentful-homepage.html'
|
||||
template_name = "mozorg/contentful-homepage.html"
|
||||
# TODO: use a better system to get the pages than the ID
|
||||
ctx.update(ContentfulEntry.objects.get_page_by_id('58YIvwDmzSDjtvpSqstDcL'))
|
||||
ctx.update(ContentfulEntry.objects.get_page_by_id("58YIvwDmzSDjtvpSqstDcL"))
|
||||
except Exception:
|
||||
# if anything goes wrong, use the old page
|
||||
template_name = 'mozorg/home/home-en.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-en', 'en-US')
|
||||
template_name = "mozorg/home/home-en.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-en", "en-US")
|
||||
else:
|
||||
template_name = 'mozorg/home/home-en.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-en', 'en-US')
|
||||
elif locale == 'de':
|
||||
if switch('contentful-homepage-de'):
|
||||
template_name = "mozorg/home/home-en.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-en", "en-US")
|
||||
elif locale == "de":
|
||||
if switch("contentful-homepage-de"):
|
||||
try:
|
||||
template_name = 'mozorg/contentful-homepage.html'
|
||||
ctx.update(ContentfulEntry.objects.get_page_by_id('4k3CxqZGjxXOjR1I0dhyto'))
|
||||
template_name = "mozorg/contentful-homepage.html"
|
||||
ctx.update(ContentfulEntry.objects.get_page_by_id("4k3CxqZGjxXOjR1I0dhyto"))
|
||||
except Exception:
|
||||
# if anything goes wrong, use the old page
|
||||
template_name = 'mozorg/home/home-de.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-de', 'de')
|
||||
template_name = "mozorg/home/home-de.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-de", "de")
|
||||
else:
|
||||
template_name = 'mozorg/home/home-de.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-de', 'de')
|
||||
elif locale == 'fr':
|
||||
template_name = 'mozorg/home/home-fr.html'
|
||||
ctx['page_content_cards'] = get_page_content_cards('home-fr', 'fr')
|
||||
template_name = "mozorg/home/home-de.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-de", "de")
|
||||
elif locale == "fr":
|
||||
template_name = "mozorg/home/home-fr.html"
|
||||
ctx["page_content_cards"] = get_page_content_cards("home-fr", "fr")
|
||||
else:
|
||||
template_name = 'mozorg/home/home.html'
|
||||
template_name = "mozorg/home/home.html"
|
||||
|
||||
return l10n_utils.render(request, template_name, ctx)
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
class ContentfulPreviewView(L10nTemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
content_id = ctx['content_id']
|
||||
content_id = ctx["content_id"]
|
||||
page = ContentfulPage(self.request, content_id)
|
||||
ctx.update(page.get_content())
|
||||
return ctx
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
page_type = context['page_type']
|
||||
theme = context['info']['theme']
|
||||
page_type = context["page_type"]
|
||||
theme = context["info"]["theme"]
|
||||
if page_type == "pageHome":
|
||||
template = 'mozorg/contentful-homepage.html'
|
||||
template = "mozorg/contentful-homepage.html"
|
||||
elif theme == "firefox":
|
||||
template = 'firefox/contentful-all.html'
|
||||
template = "firefox/contentful-all.html"
|
||||
else:
|
||||
template = 'mozorg/contentful-all.html'
|
||||
template = "mozorg/contentful-all.html"
|
||||
|
||||
return l10n_utils.render(self.request,
|
||||
template,
|
||||
context,
|
||||
**response_kwargs)
|
||||
return l10n_utils.render(self.request, template, context, **response_kwargs)
|
||||
|
|
|
@ -12,45 +12,43 @@ from django.utils.safestring import mark_safe
|
|||
from lib.l10n_utils.fluent import ftl, ftl_lazy
|
||||
from product_details import product_details
|
||||
|
||||
from bedrock.mozorg.forms import (FORMATS, EmailInput, PrivacyWidget,
|
||||
strip_parenthetical)
|
||||
from bedrock.mozorg.forms import FORMATS, EmailInput, PrivacyWidget, strip_parenthetical
|
||||
from bedrock.newsletter import utils
|
||||
|
||||
_newsletters_re = re.compile(r'^[\w,-]+$')
|
||||
_newsletters_re = re.compile(r"^[\w,-]+$")
|
||||
|
||||
|
||||
def validate_newsletters(newsletters):
|
||||
if not newsletters:
|
||||
raise ValidationError('No Newsletter Provided')
|
||||
raise ValidationError("No Newsletter Provided")
|
||||
|
||||
newsletters = newsletters.replace(' ', '')
|
||||
newsletters = newsletters.replace(" ", "")
|
||||
if not _newsletters_re.match(newsletters):
|
||||
raise ValidationError('Invalid Newsletter')
|
||||
raise ValidationError("Invalid Newsletter")
|
||||
|
||||
return newsletters
|
||||
|
||||
|
||||
def get_lang_choices(newsletters=None):
|
||||
"""
|
||||
Return a localized list of choices for language.
|
||||
Return a localized list of choices for language.
|
||||
|
||||
List looks like: [[lang_code, lang_name], [lang_code, lang_name], ...]
|
||||
List looks like: [[lang_code, lang_name], [lang_code, lang_name], ...]
|
||||
|
||||
:param newsletters: Either a comma separated string or a list of newsletter ids.
|
||||
:param newsletters: Either a comma separated string or a list of newsletter ids.
|
||||
"""
|
||||
lang_choices = []
|
||||
languages = utils.get_languages_for_newsletters(newsletters)
|
||||
|
||||
for lang in languages:
|
||||
if lang in product_details.languages:
|
||||
lang_name = product_details.languages[lang]['native']
|
||||
lang_name = product_details.languages[lang]["native"]
|
||||
else:
|
||||
try:
|
||||
locale = [loc for loc in product_details.languages
|
||||
if loc.startswith(lang)][0]
|
||||
locale = [loc for loc in product_details.languages if loc.startswith(lang)][0]
|
||||
except IndexError:
|
||||
continue
|
||||
lang_name = product_details.languages[locale]['native']
|
||||
lang_name = product_details.languages[locale]["native"]
|
||||
lang_choices.append([lang, strip_parenthetical(lang_name)])
|
||||
return sorted(lang_choices, key=itemgetter(1))
|
||||
|
||||
|
@ -59,50 +57,54 @@ class SimpleRadioSelect(widgets.RadioSelect):
|
|||
"""
|
||||
Render radio buttons as just labels with no <ul> chrome.
|
||||
"""
|
||||
template_name = 'newsletter/forms/simple_radio_select.html'
|
||||
|
||||
template_name = "newsletter/forms/simple_radio_select.html"
|
||||
|
||||
|
||||
class BooleanTabularRadioSelect(widgets.RadioSelect):
|
||||
"""
|
||||
A Select Widget intended to be used with NullBooleanField.
|
||||
"""
|
||||
template_name = 'newsletter/forms/tabular_radio_select.html'
|
||||
|
||||
template_name = "newsletter/forms/tabular_radio_select.html"
|
||||
wrap_label = False
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
choices = (
|
||||
('true', ftl('newsletter-form-yes')),
|
||||
('false', ftl('newsletter-form-no')),
|
||||
("true", ftl("newsletter-form-yes")),
|
||||
("false", ftl("newsletter-form-no")),
|
||||
)
|
||||
super(BooleanTabularRadioSelect, self).__init__(attrs, choices)
|
||||
|
||||
def format_value(self, value):
|
||||
try:
|
||||
return {
|
||||
True: 'true', False: 'false',
|
||||
'true': 'true', 'false': 'false',
|
||||
True: "true",
|
||||
False: "false",
|
||||
"true": "true",
|
||||
"false": "false",
|
||||
}[value]
|
||||
except KeyError:
|
||||
return 'unknown'
|
||||
return "unknown"
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
value = data.get(name)
|
||||
return {
|
||||
True: True,
|
||||
False: False,
|
||||
'true': True,
|
||||
'false': False,
|
||||
"true": True,
|
||||
"false": False,
|
||||
}.get(value)
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super(BooleanTabularRadioSelect, self).get_context(
|
||||
name, value, attrs)
|
||||
context['wrap_label'] = False
|
||||
context = super(BooleanTabularRadioSelect, self).get_context(name, value, attrs)
|
||||
context["wrap_label"] = False
|
||||
return context
|
||||
|
||||
|
||||
class TableCheckboxInput(widgets.CheckboxInput):
|
||||
"""Add table cell markup around the rendered checkbox"""
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
out = super(TableCheckboxInput, self).render(*args, **kwargs)
|
||||
return mark_safe("<td>" + out + "</td>")
|
||||
|
@ -114,13 +116,14 @@ class CountrySelectForm(forms.Form):
|
|||
us with their country so that we can include them in mailings relevant to
|
||||
their area of the world.
|
||||
"""
|
||||
|
||||
country = forms.ChoiceField(choices=[]) # will set choices based on locale
|
||||
|
||||
def __init__(self, locale, *args, **kwargs):
|
||||
regions = product_details.get_regions(locale)
|
||||
regions = sorted(iter(regions.items()), key=itemgetter(1))
|
||||
super(CountrySelectForm, self).__init__(*args, **kwargs)
|
||||
self.fields['country'].choices = regions
|
||||
self.fields["country"].choices = regions
|
||||
|
||||
|
||||
class ManageSubscriptionsForm(forms.Form):
|
||||
|
@ -135,15 +138,11 @@ class ManageSubscriptionsForm(forms.Form):
|
|||
@param kwargs: Other standard form kwargs
|
||||
"""
|
||||
|
||||
format = forms.ChoiceField(widget=SimpleRadioSelect,
|
||||
choices=FORMATS,
|
||||
initial='H')
|
||||
format = forms.ChoiceField(widget=SimpleRadioSelect, choices=FORMATS, initial="H")
|
||||
remove_all = forms.BooleanField(required=False)
|
||||
|
||||
country = forms.ChoiceField(choices=[], # will set choices based on locale
|
||||
required=False)
|
||||
lang = forms.ChoiceField(choices=[], # will set choices based on newsletter languages
|
||||
required=False)
|
||||
country = forms.ChoiceField(choices=[], required=False) # will set choices based on locale
|
||||
lang = forms.ChoiceField(choices=[], required=False) # will set choices based on newsletter languages
|
||||
|
||||
def __init__(self, locale, *args, **kwargs):
|
||||
regions_dict = product_details.get_regions(locale)
|
||||
|
@ -152,24 +151,24 @@ class ManageSubscriptionsForm(forms.Form):
|
|||
languages = [x[0] for x in lang_choices]
|
||||
|
||||
lang = country = locale.lower()
|
||||
if '-' in lang:
|
||||
lang, country = lang.split('-', 1)
|
||||
lang = lang if lang in languages else 'en'
|
||||
if "-" in lang:
|
||||
lang, country = lang.split("-", 1)
|
||||
lang = lang if lang in languages else "en"
|
||||
|
||||
self.newsletters = kwargs.pop('newsletters', [])
|
||||
self.newsletters = kwargs.pop("newsletters", [])
|
||||
|
||||
# Get initial - work with a copy so we're not modifying the
|
||||
# data that was passed to us
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
if 'country' in initial and initial['country'] not in regions_dict:
|
||||
initial = kwargs.get("initial", {}).copy()
|
||||
if "country" in initial and initial["country"] not in regions_dict:
|
||||
# clear the initial country if it's not in the list
|
||||
del initial['country']
|
||||
if not initial.get('country', None):
|
||||
initial['country'] = country
|
||||
if not initial.get('lang', None):
|
||||
initial['lang'] = lang
|
||||
del initial["country"]
|
||||
if not initial.get("country", None):
|
||||
initial["country"] = country
|
||||
if not initial.get("lang", None):
|
||||
initial["lang"] = lang
|
||||
else:
|
||||
lang = initial['lang']
|
||||
lang = initial["lang"]
|
||||
|
||||
# Sometimes people are in ET with a language that is spelled a
|
||||
# little differently from our list. E.g. we have 'es' on our
|
||||
|
@ -188,24 +187,22 @@ class ManageSubscriptionsForm(forms.Form):
|
|||
else:
|
||||
# No luck - guess from the locale
|
||||
lang = locale.lower()
|
||||
if '-' in lang:
|
||||
lang, _unused = lang.split('-', 1)
|
||||
initial['lang'] = lang
|
||||
if "-" in lang:
|
||||
lang, _unused = lang.split("-", 1)
|
||||
initial["lang"] = lang
|
||||
|
||||
kwargs['initial'] = initial
|
||||
kwargs["initial"] = initial
|
||||
super(ManageSubscriptionsForm, self).__init__(*args, **kwargs)
|
||||
self.fields['country'].choices = regions
|
||||
self.fields['lang'].choices = lang_choices
|
||||
self.fields["country"].choices = regions
|
||||
self.fields["lang"].choices = lang_choices
|
||||
|
||||
self.already_subscribed = initial.get('newsletters', [])
|
||||
self.already_subscribed = initial.get("newsletters", [])
|
||||
|
||||
def clean(self):
|
||||
valid_newsletters = utils.get_newsletters()
|
||||
for newsletter in self.newsletters:
|
||||
if newsletter not in valid_newsletters:
|
||||
msg = ftl('newsletters-is-not-a-valid-newsletter',
|
||||
newsletter=newsletter,
|
||||
ftl_files=['mozorg/newsletters'])
|
||||
msg = ftl("newsletters-is-not-a-valid-newsletter", newsletter=newsletter, ftl_files=["mozorg/newsletters"])
|
||||
raise ValidationError(msg)
|
||||
return super(ManageSubscriptionsForm, self).clean()
|
||||
|
||||
|
@ -215,6 +212,7 @@ class NewsletterForm(forms.Form):
|
|||
Form to let a user subscribe to or unsubscribe from a newsletter
|
||||
on the manage existing newsletters page. Used in a FormSet.
|
||||
"""
|
||||
|
||||
title = forms.CharField(required=False)
|
||||
description = forms.CharField(required=False)
|
||||
subscribed_radio = forms.BooleanField(
|
||||
|
@ -234,19 +232,16 @@ class NewsletterFooterForm(forms.Form):
|
|||
footer of a page (see newsletters/middleware.py) but sometimes
|
||||
on a dedicated page.
|
||||
"""
|
||||
email = forms.EmailField(widget=EmailInput(attrs={'required': 'required'}))
|
||||
|
||||
email = forms.EmailField(widget=EmailInput(attrs={"required": "required"}))
|
||||
# first/last_name not yet included in email_newsletter_form helper
|
||||
# currently used on /contribute/friends/ (custom markup)
|
||||
first_name = forms.CharField(widget=forms.TextInput, required=False)
|
||||
last_name = forms.CharField(widget=forms.TextInput, required=False)
|
||||
fmt = forms.ChoiceField(widget=SimpleRadioSelect,
|
||||
choices=FORMATS,
|
||||
initial='H')
|
||||
fmt = forms.ChoiceField(widget=SimpleRadioSelect, choices=FORMATS, initial="H")
|
||||
privacy = forms.BooleanField(widget=PrivacyWidget)
|
||||
source_url = forms.CharField(required=False)
|
||||
newsletters = forms.CharField(widget=forms.HiddenInput,
|
||||
required=True,
|
||||
max_length=100)
|
||||
newsletters = forms.CharField(widget=forms.HiddenInput, required=True, max_length=100)
|
||||
|
||||
# has to take a newsletters argument so it can figure
|
||||
# out which languages to list in the form.
|
||||
|
@ -259,15 +254,14 @@ class NewsletterFooterForm(forms.Form):
|
|||
except ValidationError:
|
||||
# replace with most common good newsletter
|
||||
# form validation will work with submitted data
|
||||
newsletters = 'mozilla-and-you'
|
||||
newsletters = "mozilla-and-you"
|
||||
|
||||
lang = locale.lower()
|
||||
if '-' in lang:
|
||||
lang, country = lang.split('-', 1)
|
||||
if "-" in lang:
|
||||
lang, country = lang.split("-", 1)
|
||||
else:
|
||||
country = ''
|
||||
regions.insert(0, ('', ftl_lazy('newsletter-form-select-country-or-region',
|
||||
fallback='newsletter-form-select-country')))
|
||||
country = ""
|
||||
regions.insert(0, ("", ftl_lazy("newsletter-form-select-country-or-region", fallback="newsletter-form-select-country")))
|
||||
lang_choices = get_lang_choices(newsletters)
|
||||
languages = [x[0] for x in lang_choices]
|
||||
if lang not in languages:
|
||||
|
@ -275,32 +269,26 @@ class NewsletterFooterForm(forms.Form):
|
|||
# are translated into. Initialize the language field to no
|
||||
# choice, to force the user to pick one of the languages that
|
||||
# we do support.
|
||||
lang = ''
|
||||
lang_choices.insert(0, ('', ftl_lazy('newsletter-form-available-languages')))
|
||||
lang = ""
|
||||
lang_choices.insert(0, ("", ftl_lazy("newsletter-form-available-languages")))
|
||||
|
||||
super(NewsletterFooterForm, self).__init__(data, *args, **kwargs)
|
||||
|
||||
required_args = {
|
||||
'required': 'required',
|
||||
'aria-required': 'true',
|
||||
"required": "required",
|
||||
"aria-required": "true",
|
||||
}
|
||||
country_widget = widgets.Select(attrs=required_args)
|
||||
self.fields['country'] = forms.ChoiceField(widget=country_widget,
|
||||
choices=regions,
|
||||
initial=country,
|
||||
required=False)
|
||||
self.fields["country"] = forms.ChoiceField(widget=country_widget, choices=regions, initial=country, required=False)
|
||||
lang_widget = widgets.Select(attrs=required_args)
|
||||
self.fields['lang'] = forms.TypedChoiceField(widget=lang_widget,
|
||||
choices=lang_choices,
|
||||
initial=lang,
|
||||
required=False)
|
||||
self.fields['newsletters'].initial = newsletters
|
||||
self.fields["lang"] = forms.TypedChoiceField(widget=lang_widget, choices=lang_choices, initial=lang, required=False)
|
||||
self.fields["newsletters"].initial = newsletters
|
||||
|
||||
def clean_newsletters(self):
|
||||
return validate_newsletters(self.cleaned_data['newsletters'])
|
||||
return validate_newsletters(self.cleaned_data["newsletters"])
|
||||
|
||||
def clean_source_url(self):
|
||||
su = self.cleaned_data['source_url'].strip()
|
||||
su = self.cleaned_data["source_url"].strip()
|
||||
if su:
|
||||
# limit to 255 characters by truncation
|
||||
return su[:255]
|
||||
|
@ -312,4 +300,5 @@ class EmailForm(forms.Form):
|
|||
"""
|
||||
Form to enter email, e.g. to be sent a recovery message
|
||||
"""
|
||||
email = forms.EmailField(widget=EmailInput(attrs={'required': 'required'}))
|
||||
|
||||
email = forms.EmailField(widget=EmailInput(attrs={"required": "required"}))
|
||||
|
|
|
@ -7,17 +7,16 @@ from bedrock.newsletter.models import Newsletter
|
|||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
|
||||
help='If no error occurs, swallow all output.'),
|
||||
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="If no error occurs, swallow all output."),
|
||||
|
||||
def handle(self, *args, **options):
|
||||
newsletters = basket.get_newsletters()
|
||||
if not newsletters:
|
||||
raise CommandError('No data from basket')
|
||||
raise CommandError("No data from basket")
|
||||
|
||||
count = Newsletter.objects.refresh(newsletters)
|
||||
if not options['quiet']:
|
||||
if not options["quiet"]:
|
||||
if count:
|
||||
print('Updated %d newsletters' % count)
|
||||
print("Updated %d newsletters" % count)
|
||||
else:
|
||||
print('Nothing to update')
|
||||
print("Nothing to update")
|
||||
|
|
|
@ -3,8 +3,8 @@ from bedrock.redirects.util import redirect
|
|||
|
||||
redirectpatterns = (
|
||||
# bug 926629
|
||||
redirect(r'^newsletter/about_mobile(?:/(?:index\.html)?)?$', 'newsletter.subscribe'),
|
||||
redirect(r'^newsletter/about_mozilla(?:/(?:index\.html)?)?$', 'mozorg.contribute'),
|
||||
redirect(r'^newsletter/new(?:/(?:index\.html)?)?$', 'newsletter.subscribe'),
|
||||
redirect(r'^newsletter/ios(?:/(?:index\.html)?)?$', 'firefox.browsers.mobile.ios'),
|
||||
redirect(r"^newsletter/about_mobile(?:/(?:index\.html)?)?$", "newsletter.subscribe"),
|
||||
redirect(r"^newsletter/about_mozilla(?:/(?:index\.html)?)?$", "mozorg.contribute"),
|
||||
redirect(r"^newsletter/new(?:/(?:index\.html)?)?$", "newsletter.subscribe"),
|
||||
redirect(r"^newsletter/ios(?:/(?:index\.html)?)?$", "firefox.browsers.mobile.ios"),
|
||||
)
|
||||
|
|
|
@ -17,47 +17,61 @@ log = logging.getLogger(__name__)
|
|||
|
||||
@library.global_function
|
||||
@jinja2.contextfunction
|
||||
def email_newsletter_form(ctx, newsletters='mozilla-and-you', title=None,
|
||||
subtitle=None, desc=None, include_country=True,
|
||||
include_language=True, details=None,
|
||||
use_thankyou=True, thankyou_head=None,
|
||||
thankyou_content=None, footer=True,
|
||||
process_form=True, include_title=None,
|
||||
submit_text=None, button_class=None,
|
||||
spinner_color=None, email_label=None,
|
||||
email_placeholder=None):
|
||||
request = ctx['request']
|
||||
def email_newsletter_form(
|
||||
ctx,
|
||||
newsletters="mozilla-and-you",
|
||||
title=None,
|
||||
subtitle=None,
|
||||
desc=None,
|
||||
include_country=True,
|
||||
include_language=True,
|
||||
details=None,
|
||||
use_thankyou=True,
|
||||
thankyou_head=None,
|
||||
thankyou_content=None,
|
||||
footer=True,
|
||||
process_form=True,
|
||||
include_title=None,
|
||||
submit_text=None,
|
||||
button_class=None,
|
||||
spinner_color=None,
|
||||
email_label=None,
|
||||
email_placeholder=None,
|
||||
):
|
||||
request = ctx["request"]
|
||||
context = ctx.get_all()
|
||||
|
||||
success = bool(ctx.get('success'))
|
||||
success = bool(ctx.get("success"))
|
||||
if success and not use_thankyou:
|
||||
return
|
||||
|
||||
form = ctx.get('newsletter_form', None)
|
||||
form = ctx.get("newsletter_form", None)
|
||||
if not form:
|
||||
form = NewsletterFooterForm(newsletters, get_locale(request))
|
||||
|
||||
context.update(dict(
|
||||
id=newsletters,
|
||||
title=title,
|
||||
subtitle=subtitle, # nested in/depends on include_title
|
||||
desc=desc, # nested in/depends on include_title
|
||||
include_country=include_country,
|
||||
include_language=include_language,
|
||||
details=details,
|
||||
use_thankyou=use_thankyou,
|
||||
thankyou_head=thankyou_head,
|
||||
thankyou_content=thankyou_content,
|
||||
footer=footer,
|
||||
include_title=include_title if include_title is not None else footer,
|
||||
form=form,
|
||||
submit_text=submit_text,
|
||||
button_class=button_class,
|
||||
spinner_color=spinner_color,
|
||||
success=success,
|
||||
email_label=email_label,
|
||||
email_placeholder=email_placeholder,
|
||||
))
|
||||
context.update(
|
||||
dict(
|
||||
id=newsletters,
|
||||
title=title,
|
||||
subtitle=subtitle, # nested in/depends on include_title
|
||||
desc=desc, # nested in/depends on include_title
|
||||
include_country=include_country,
|
||||
include_language=include_language,
|
||||
details=details,
|
||||
use_thankyou=use_thankyou,
|
||||
thankyou_head=thankyou_head,
|
||||
thankyou_content=thankyou_content,
|
||||
footer=footer,
|
||||
include_title=include_title if include_title is not None else footer,
|
||||
form=form,
|
||||
submit_text=submit_text,
|
||||
button_class=button_class,
|
||||
spinner_color=spinner_color,
|
||||
success=success,
|
||||
email_label=email_label,
|
||||
email_placeholder=email_placeholder,
|
||||
)
|
||||
)
|
||||
|
||||
html = render_to_string('newsletter/includes/form.html', context, request=request)
|
||||
html = render_to_string("newsletter/includes/form.html", context, request=request)
|
||||
return jinja2.Markup(html)
|
||||
|
|
|
@ -12,36 +12,36 @@ utils.basket.get_newsletters = news_mock
|
|||
# Test data for newsletters
|
||||
# In the format returned by utils.get_newsletters()
|
||||
newsletters = {
|
||||
u'mozilla-and-you': {
|
||||
'active': True,
|
||||
'show': True,
|
||||
'title': "Firefox & You",
|
||||
'languages': ['en', 'fr', 'de', 'pt', 'ru'],
|
||||
'description': 'Firefox and you',
|
||||
'order': 4,
|
||||
"mozilla-and-you": {
|
||||
"active": True,
|
||||
"show": True,
|
||||
"title": "Firefox & You",
|
||||
"languages": ["en", "fr", "de", "pt", "ru"],
|
||||
"description": "Firefox and you",
|
||||
"order": 4,
|
||||
},
|
||||
u'firefox-tips': {
|
||||
'active': True,
|
||||
'show': True,
|
||||
'title': 'Firefox Tips',
|
||||
'languages': ['en', 'fr', 'de', 'pt', 'ru'],
|
||||
'description': 'Firefox tips',
|
||||
'order': 2,
|
||||
"firefox-tips": {
|
||||
"active": True,
|
||||
"show": True,
|
||||
"title": "Firefox Tips",
|
||||
"languages": ["en", "fr", "de", "pt", "ru"],
|
||||
"description": "Firefox tips",
|
||||
"order": 2,
|
||||
},
|
||||
u'beta': {
|
||||
'active': False,
|
||||
'show': False,
|
||||
'title': 'Beta News',
|
||||
'languages': ['en'],
|
||||
'description': 'Beta News',
|
||||
'order': 3,
|
||||
"beta": {
|
||||
"active": False,
|
||||
"show": False,
|
||||
"title": "Beta News",
|
||||
"languages": ["en"],
|
||||
"description": "Beta News",
|
||||
"order": 3,
|
||||
},
|
||||
u'join-mozilla': {
|
||||
'active': True,
|
||||
'show': False,
|
||||
'title': 'Join Mozilla',
|
||||
'languages': ['en', 'es'],
|
||||
'description': 'Join Mozilla',
|
||||
'order': 1,
|
||||
"join-mozilla": {
|
||||
"active": True,
|
||||
"show": False,
|
||||
"title": "Join Mozilla",
|
||||
"languages": ["en", "es"],
|
||||
"description": "Join Mozilla",
|
||||
"order": 1,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -6,34 +6,32 @@ from pyquery import PyQuery as pq
|
|||
from bedrock.mozorg.tests import TestCase
|
||||
|
||||
|
||||
@patch('bedrock.newsletter.forms.get_lang_choices',
|
||||
lambda *x: [['en', 'English'], ['fr', 'French'], ['pt', 'Portuguese']])
|
||||
@patch('lib.l10n_utils.translations_for_template',
|
||||
lambda *x: ['en-US', 'fr', 'pt-BR', 'af'])
|
||||
@patch("bedrock.newsletter.forms.get_lang_choices", lambda *x: [["en", "English"], ["fr", "French"], ["pt", "Portuguese"]])
|
||||
@patch("lib.l10n_utils.translations_for_template", lambda *x: ["en-US", "fr", "pt-BR", "af"])
|
||||
class TestNewsletterFooter(TestCase):
|
||||
def setUp(self):
|
||||
self.view_name = 'newsletter.subscribe'
|
||||
self.view_name = "newsletter.subscribe"
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_country_selected(self):
|
||||
"""
|
||||
The correct country for the locale should be initially selected.
|
||||
"""
|
||||
with self.activate('en-US'):
|
||||
with self.activate("en-US"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_country option[selected="selected"]').val() == 'us'
|
||||
assert doc('#id_country option[selected="selected"]').val() == "us"
|
||||
|
||||
# no country in locale, no country selected
|
||||
with self.activate('fr'):
|
||||
with self.activate("fr"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_country option[selected="selected"]').val() == ''
|
||||
assert doc('#id_country option[selected="selected"]').val() == ""
|
||||
|
||||
with self.activate('pt-BR'):
|
||||
with self.activate("pt-BR"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_country option[selected="selected"]').val() == 'br'
|
||||
assert doc('#id_country option[selected="selected"]').val() == "br"
|
||||
|
||||
@override_settings(DEV=True)
|
||||
def test_language_selected(self):
|
||||
|
@ -41,19 +39,19 @@ class TestNewsletterFooter(TestCase):
|
|||
The correct language for the locale should be initially selected or
|
||||
'en' if it's not an option.
|
||||
"""
|
||||
with self.activate('fr'):
|
||||
with self.activate("fr"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_lang option[selected="selected"]').val() == 'fr'
|
||||
assert doc('#id_lang option[selected="selected"]').val() == "fr"
|
||||
|
||||
# with hyphenated regional locale, should have only lang
|
||||
with self.activate('pt-BR'):
|
||||
with self.activate("pt-BR"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_lang option[selected="selected"]').val() == 'pt'
|
||||
assert doc('#id_lang option[selected="selected"]').val() == "pt"
|
||||
|
||||
# not supported. should default to ''
|
||||
with self.activate('af'):
|
||||
with self.activate("af"):
|
||||
resp = self.client.get(reverse(self.view_name))
|
||||
doc = pq(resp.content)
|
||||
assert doc('#id_lang option[selected="selected"]').val() == ''
|
||||
assert doc('#id_lang option[selected="selected"]').val() == ""
|
||||
|
|
|
@ -5,8 +5,10 @@ import mock
|
|||
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from bedrock.newsletter.forms import (
|
||||
BooleanTabularRadioSelect, ManageSubscriptionsForm,
|
||||
NewsletterFooterForm, NewsletterForm,
|
||||
BooleanTabularRadioSelect,
|
||||
ManageSubscriptionsForm,
|
||||
NewsletterFooterForm,
|
||||
NewsletterForm,
|
||||
)
|
||||
from bedrock.newsletter.tests import newsletters
|
||||
|
||||
|
@ -16,7 +18,6 @@ newsletters_mock.return_value = newsletters
|
|||
|
||||
|
||||
class TestRenderers(TestCase):
|
||||
|
||||
def test_str_true(self):
|
||||
"""renderer starts with True selected if value given is True"""
|
||||
widget = BooleanTabularRadioSelect()
|
||||
|
@ -51,91 +52,101 @@ class TestRenderers(TestCase):
|
|||
|
||||
|
||||
class TestManageSubscriptionsForm(TestCase):
|
||||
@mock.patch('bedrock.newsletter.forms.get_lang_choices')
|
||||
@mock.patch("bedrock.newsletter.forms.get_lang_choices")
|
||||
def test_locale(self, langs_mock):
|
||||
"""Get initial lang, country from the right places"""
|
||||
# Get initial lang and country from 'initial' if provided there,
|
||||
# else from the locale passed in
|
||||
# First, not passed in
|
||||
langs_mock.return_value = [['en', 'English'], ['pt', 'Portuguese']]
|
||||
langs_mock.return_value = [["en", "English"], ["pt", "Portuguese"]]
|
||||
locale = "en-US"
|
||||
form = ManageSubscriptionsForm(locale=locale, initial={})
|
||||
self.assertEqual('en', form.initial['lang'])
|
||||
self.assertEqual('us', form.initial['country'])
|
||||
self.assertEqual("en", form.initial["lang"])
|
||||
self.assertEqual("us", form.initial["country"])
|
||||
# now, test with them passed in.
|
||||
form = ManageSubscriptionsForm(locale=locale,
|
||||
initial={
|
||||
'lang': 'pt',
|
||||
'country': 'br',
|
||||
})
|
||||
self.assertEqual('pt', form.initial['lang'])
|
||||
self.assertEqual('br', form.initial['country'])
|
||||
form = ManageSubscriptionsForm(
|
||||
locale=locale,
|
||||
initial={
|
||||
"lang": "pt",
|
||||
"country": "br",
|
||||
},
|
||||
)
|
||||
self.assertEqual("pt", form.initial["lang"])
|
||||
self.assertEqual("br", form.initial["country"])
|
||||
|
||||
@mock.patch('bedrock.newsletter.forms.get_lang_choices')
|
||||
@mock.patch("bedrock.newsletter.forms.get_lang_choices")
|
||||
def test_long_language(self, langs_mock):
|
||||
"""Fuzzy match their language preference"""
|
||||
# Suppose their selected language in ET is a long form ("es-ES")
|
||||
# while we only have the short forms ("es") in our list of
|
||||
# valid languages. Or vice-versa. Find the match to the one
|
||||
# in our list and use that, not the lang from ET.
|
||||
locale = 'en-US'
|
||||
langs_mock.return_value = [['en', 'English'], ['es', 'Spanish']]
|
||||
form = ManageSubscriptionsForm(locale=locale,
|
||||
initial={
|
||||
'lang': 'es-ES',
|
||||
'country': 'es',
|
||||
})
|
||||
locale = "en-US"
|
||||
langs_mock.return_value = [["en", "English"], ["es", "Spanish"]]
|
||||
form = ManageSubscriptionsForm(
|
||||
locale=locale,
|
||||
initial={
|
||||
"lang": "es-ES",
|
||||
"country": "es",
|
||||
},
|
||||
)
|
||||
# Initial value is 'es'
|
||||
self.assertEqual('es', form.initial['lang'])
|
||||
self.assertEqual("es", form.initial["lang"])
|
||||
|
||||
def test_bad_language(self):
|
||||
"""Handle their language preference if it's not valid"""
|
||||
# Suppose their selected language in ET is one we don't recognize
|
||||
# at all. Use the language from their locale instead.
|
||||
locale = "pt-BR"
|
||||
form = ManageSubscriptionsForm(locale=locale,
|
||||
initial={
|
||||
'lang': 'zz',
|
||||
'country': 'es',
|
||||
})
|
||||
self.assertEqual('pt', form.initial['lang'])
|
||||
form = ManageSubscriptionsForm(
|
||||
locale=locale,
|
||||
initial={
|
||||
"lang": "zz",
|
||||
"country": "es",
|
||||
},
|
||||
)
|
||||
self.assertEqual("pt", form.initial["lang"])
|
||||
|
||||
def test_bad_country(self):
|
||||
"""Handle their country preference if it's not valid"""
|
||||
# Suppose their selected country in CTMS is one we don't recognize
|
||||
# at all. Use the country from their locale instead.
|
||||
locale = "pt-BR"
|
||||
form = ManageSubscriptionsForm(locale=locale,
|
||||
initial={
|
||||
'lang': 'zz',
|
||||
'country': 'Spain',
|
||||
})
|
||||
self.assertEqual('br', form.initial['country'])
|
||||
form = ManageSubscriptionsForm(
|
||||
locale=locale,
|
||||
initial={
|
||||
"lang": "zz",
|
||||
"country": "Spain",
|
||||
},
|
||||
)
|
||||
self.assertEqual("br", form.initial["country"])
|
||||
|
||||
def test_no_country(self):
|
||||
"""Handle their country preference if it's not set"""
|
||||
# Suppose they have no selected country in CTMS.
|
||||
# Use the country from their locale instead.
|
||||
locale = "en-US"
|
||||
form = ManageSubscriptionsForm(locale=locale,
|
||||
initial={
|
||||
'lang': 'zz',
|
||||
})
|
||||
self.assertEqual('us', form.initial['country'])
|
||||
form = ManageSubscriptionsForm(
|
||||
locale=locale,
|
||||
initial={
|
||||
"lang": "zz",
|
||||
},
|
||||
)
|
||||
self.assertEqual("us", form.initial["country"])
|
||||
|
||||
|
||||
@mock.patch('bedrock.newsletter.utils.get_newsletters', newsletters_mock)
|
||||
@mock.patch("bedrock.newsletter.utils.get_newsletters", newsletters_mock)
|
||||
class TestNewsletterForm(TestCase):
|
||||
def test_form(self):
|
||||
"""test NewsletterForm"""
|
||||
# not much to test, but at least construct one
|
||||
title = "Newsletter title"
|
||||
newsletter = 'newsletter-a'
|
||||
newsletter = "newsletter-a"
|
||||
initial = {
|
||||
'title': title,
|
||||
'newsletter': newsletter,
|
||||
'subscribed_radio': True,
|
||||
'subscribed_check': True,
|
||||
"title": title,
|
||||
"newsletter": newsletter,
|
||||
"subscribed_radio": True,
|
||||
"subscribed_check": True,
|
||||
}
|
||||
form = NewsletterForm(initial=initial)
|
||||
rendered = str(form)
|
||||
|
@ -144,148 +155,148 @@ class TestNewsletterForm(TestCase):
|
|||
# And validate one
|
||||
form = NewsletterForm(data=initial)
|
||||
self.assertTrue(form.is_valid())
|
||||
self.assertEqual(title, form.cleaned_data['title'])
|
||||
self.assertEqual(title, form.cleaned_data["title"])
|
||||
|
||||
def test_multiple_newsletters(self):
|
||||
"""Should allow to subscribe to multiple newsletters at a time."""
|
||||
newsletters = 'mozilla-and-you,beta'
|
||||
newsletters = "mozilla-and-you,beta"
|
||||
data = {
|
||||
'email': 'dude@example.com',
|
||||
'lang': 'en',
|
||||
'privacy': 'Y',
|
||||
'fmt': 'H',
|
||||
'newsletters': newsletters,
|
||||
"email": "dude@example.com",
|
||||
"lang": "en",
|
||||
"privacy": "Y",
|
||||
"fmt": "H",
|
||||
"newsletters": newsletters,
|
||||
}
|
||||
form = NewsletterFooterForm(newsletters, 'en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(newsletters, "en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid())
|
||||
self.assertEqual(form.cleaned_data['newsletters'], newsletters)
|
||||
self.assertEqual(form.cleaned_data["newsletters"], newsletters)
|
||||
|
||||
# whitespace shouldn't matter
|
||||
form = NewsletterFooterForm('mozilla-and-you , beta ', 'en-US', data=data.copy())
|
||||
form = NewsletterFooterForm("mozilla-and-you , beta ", "en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid())
|
||||
self.assertEqual(form.cleaned_data['newsletters'], newsletters)
|
||||
self.assertEqual(form.cleaned_data["newsletters"], newsletters)
|
||||
|
||||
|
||||
@mock.patch('bedrock.newsletter.utils.get_newsletters', newsletters_mock)
|
||||
@mock.patch("bedrock.newsletter.utils.get_newsletters", newsletters_mock)
|
||||
class TestNewsletterFooterForm(TestCase):
|
||||
newsletter_name = 'mozilla-and-you'
|
||||
newsletter_name = "mozilla-and-you"
|
||||
|
||||
def test_form(self):
|
||||
"""Form works normally"""
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'lang': 'fr',
|
||||
'first_name': 'Walter',
|
||||
'last_name': 'Sobchak',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'source_url': 'https://accounts.firefox.com',
|
||||
'newsletters': self.newsletter_name,
|
||||
"email": "foo@example.com",
|
||||
"lang": "fr",
|
||||
"first_name": "Walter",
|
||||
"last_name": "Sobchak",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"source_url": "https://accounts.firefox.com",
|
||||
"newsletters": self.newsletter_name,
|
||||
}
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid(), form.errors)
|
||||
cleaned_data = form.cleaned_data
|
||||
self.assertEqual(data['fmt'], cleaned_data['fmt'])
|
||||
self.assertEqual(data['lang'], cleaned_data['lang'])
|
||||
self.assertEqual(data['source_url'], cleaned_data['source_url'])
|
||||
self.assertEqual(data["fmt"], cleaned_data["fmt"])
|
||||
self.assertEqual(data["lang"], cleaned_data["lang"])
|
||||
self.assertEqual(data["source_url"], cleaned_data["source_url"])
|
||||
|
||||
def test_source_url_non_url(self):
|
||||
"""Form works normally"""
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'lang': 'fr',
|
||||
'first_name': 'Walter',
|
||||
'last_name': 'Sobchak',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'source_url': 'about:devtools?dude=abiding',
|
||||
'newsletters': self.newsletter_name,
|
||||
"email": "foo@example.com",
|
||||
"lang": "fr",
|
||||
"first_name": "Walter",
|
||||
"last_name": "Sobchak",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"source_url": "about:devtools?dude=abiding",
|
||||
"newsletters": self.newsletter_name,
|
||||
}
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid(), form.errors)
|
||||
cleaned_data = form.cleaned_data
|
||||
self.assertEqual(data['source_url'], cleaned_data['source_url'])
|
||||
self.assertEqual(data["source_url"], cleaned_data["source_url"])
|
||||
|
||||
def test_source_url_too_long(self):
|
||||
"""Form works normally"""
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'lang': 'fr',
|
||||
'first_name': 'Walter',
|
||||
'last_name': 'Sobchak',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'source_url': 'about:devtools' * 20,
|
||||
'newsletters': self.newsletter_name,
|
||||
"email": "foo@example.com",
|
||||
"lang": "fr",
|
||||
"first_name": "Walter",
|
||||
"last_name": "Sobchak",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"source_url": "about:devtools" * 20,
|
||||
"newsletters": self.newsletter_name,
|
||||
}
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid(), form.errors)
|
||||
cleaned_data = form.cleaned_data
|
||||
self.assertEqual(data['source_url'][:255], cleaned_data['source_url'])
|
||||
self.assertEqual(data["source_url"][:255], cleaned_data["source_url"])
|
||||
|
||||
def test_country_default(self):
|
||||
"""country defaults based on the locale.
|
||||
|
||||
But only for country based locales (e.g. pt-BR)"""
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='fr')
|
||||
self.assertEqual('', form.fields['country'].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='pt-BR')
|
||||
self.assertEqual('br', form.fields['country'].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='zh-TW')
|
||||
self.assertEqual('tw', form.fields['country'].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="fr")
|
||||
self.assertEqual("", form.fields["country"].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="pt-BR")
|
||||
self.assertEqual("br", form.fields["country"].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="zh-TW")
|
||||
self.assertEqual("tw", form.fields["country"].initial)
|
||||
|
||||
def test_lang_choices_per_newsletter(self):
|
||||
"""Lang choices should be based on the newsletter."""
|
||||
form = NewsletterFooterForm('beta', 'en-US')
|
||||
choices = [lang[0] for lang in form.fields['lang'].choices]
|
||||
self.assertEqual(choices, ['en'])
|
||||
form = NewsletterFooterForm("beta", "en-US")
|
||||
choices = [lang[0] for lang in form.fields["lang"].choices]
|
||||
self.assertEqual(choices, ["en"])
|
||||
|
||||
form = NewsletterFooterForm('join-mozilla', 'en-US')
|
||||
choices = [lang[0] for lang in form.fields['lang'].choices]
|
||||
self.assertEqual(choices, ['en', 'es'])
|
||||
form = NewsletterFooterForm("join-mozilla", "en-US")
|
||||
choices = [lang[0] for lang in form.fields["lang"].choices]
|
||||
self.assertEqual(choices, ["en", "es"])
|
||||
|
||||
def test_lang_choices_multiple_newsletters(self):
|
||||
"""Lang choices should be based on all newsletters."""
|
||||
form = NewsletterFooterForm('join-mozilla,firefox-tips', 'en-US')
|
||||
choices = [lang[0] for lang in form.fields['lang'].choices]
|
||||
self.assertEqual(choices, ['de', 'en', 'es', 'fr', 'pt', 'ru'])
|
||||
form = NewsletterFooterForm("join-mozilla,firefox-tips", "en-US")
|
||||
choices = [lang[0] for lang in form.fields["lang"].choices]
|
||||
self.assertEqual(choices, ["de", "en", "es", "fr", "pt", "ru"])
|
||||
|
||||
def test_lang_default(self):
|
||||
"""lang defaults based on the locale"""
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='pt-BR')
|
||||
self.assertEqual('pt', form.fields['lang'].initial)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="pt-BR")
|
||||
self.assertEqual("pt", form.fields["lang"].initial)
|
||||
|
||||
def test_lang_default_not_supported(self):
|
||||
"""lang defaults to blank if not supported by newsletter."""
|
||||
form = NewsletterFooterForm('beta', locale='pt-BR')
|
||||
self.assertEqual('', form.fields['lang'].initial)
|
||||
form = NewsletterFooterForm("beta", locale="pt-BR")
|
||||
self.assertEqual("", form.fields["lang"].initial)
|
||||
|
||||
def test_lang_not_required(self):
|
||||
"""lang not required since field not always displayed"""
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'newsletters': self.newsletter_name,
|
||||
"email": "foo@example.com",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"newsletters": self.newsletter_name,
|
||||
}
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="en-US", data=data.copy())
|
||||
self.assertTrue(form.is_valid(), form.errors)
|
||||
# Form returns '' for lang, so we don't accidentally change the user's
|
||||
# preferred language thinking they entered something here that they
|
||||
# didn't.
|
||||
self.assertEqual(u'', form.cleaned_data['lang'])
|
||||
self.assertEqual("", form.cleaned_data["lang"])
|
||||
|
||||
def test_privacy_required(self):
|
||||
"""they have to check the privacy box"""
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'privacy': False,
|
||||
'fmt': 'H',
|
||||
'newsletters': self.newsletter_name,
|
||||
"email": "foo@example.com",
|
||||
"privacy": False,
|
||||
"fmt": "H",
|
||||
"newsletters": self.newsletter_name,
|
||||
}
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale='en-US', data=data)
|
||||
form = NewsletterFooterForm(self.newsletter_name, locale="en-US", data=data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertIn('privacy', form.errors)
|
||||
self.assertIn("privacy", form.errors)
|
||||
|
||||
def test_invalid_newsletter_is_error(self):
|
||||
"""Invalid newsletter should not raise exception. Bug 1072302.
|
||||
|
@ -294,26 +305,26 @@ class TestNewsletterFooterForm(TestCase):
|
|||
form error.
|
||||
"""
|
||||
data = {
|
||||
'email': 'fred@example.com',
|
||||
'lang': 'fr',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'newsletters': '',
|
||||
"email": "fred@example.com",
|
||||
"lang": "fr",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"newsletters": "",
|
||||
}
|
||||
form = NewsletterFooterForm('', locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm("", locale="en-US", data=data.copy())
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertIn('newsletters', form.errors)
|
||||
self.assertEqual(form.errors['newsletters'], [u'This field is required.'])
|
||||
self.assertIn("newsletters", form.errors)
|
||||
self.assertEqual(form.errors["newsletters"], ["This field is required."])
|
||||
|
||||
invalid_newsletter = '!nv@l1d'
|
||||
invalid_newsletter = "!nv@l1d"
|
||||
data = {
|
||||
'email': 'fred@example.com',
|
||||
'lang': 'fr',
|
||||
'privacy': True,
|
||||
'fmt': 'H',
|
||||
'newsletters': invalid_newsletter,
|
||||
"email": "fred@example.com",
|
||||
"lang": "fr",
|
||||
"privacy": True,
|
||||
"fmt": "H",
|
||||
"newsletters": invalid_newsletter,
|
||||
}
|
||||
form = NewsletterFooterForm(invalid_newsletter, locale='en-US', data=data.copy())
|
||||
form = NewsletterFooterForm(invalid_newsletter, locale="en-US", data=data.copy())
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertIn('newsletters', form.errors)
|
||||
self.assertEqual(form.errors['newsletters'], [u'Invalid Newsletter'])
|
||||
self.assertIn("newsletters", form.errors)
|
||||
self.assertEqual(form.errors["newsletters"], ["Invalid Newsletter"])
|
||||
|
|
|
@ -13,32 +13,26 @@ newsletters_mock.return_value = newsletters
|
|||
class TestNewsletterModel(TestCase):
|
||||
def setUp(self):
|
||||
Newsletter.objects.create(
|
||||
slug='dude',
|
||||
data={
|
||||
'title': 'Abide',
|
||||
'languages': ['en']
|
||||
},
|
||||
slug="dude",
|
||||
data={"title": "Abide", "languages": ["en"]},
|
||||
)
|
||||
Newsletter.objects.create(
|
||||
slug='donnie',
|
||||
data={
|
||||
'title': 'Walrus',
|
||||
'languages': ['de']
|
||||
},
|
||||
slug="donnie",
|
||||
data={"title": "Walrus", "languages": ["de"]},
|
||||
)
|
||||
self.data = {
|
||||
'dude': {
|
||||
'title': 'Abide',
|
||||
'languages': ['en'],
|
||||
"dude": {
|
||||
"title": "Abide",
|
||||
"languages": ["en"],
|
||||
},
|
||||
'donnie': {
|
||||
'title': 'Walrus',
|
||||
'languages': ['de'],
|
||||
"donnie": {
|
||||
"title": "Walrus",
|
||||
"languages": ["de"],
|
||||
},
|
||||
}
|
||||
|
||||
def test_refresh_with_change(self):
|
||||
self.data['donnie']['languages'].append('fr')
|
||||
self.data["donnie"]["languages"].append("fr")
|
||||
count = Newsletter.objects.refresh(self.data)
|
||||
self.assertEqual(count, 2)
|
||||
# run again to verify updated
|
||||
|
@ -55,32 +49,32 @@ class TestNewsletterModel(TestCase):
|
|||
self.assertEqual(result, self.data)
|
||||
|
||||
|
||||
@mock.patch('bedrock.newsletter.utils.get_newsletters', newsletters_mock)
|
||||
@mock.patch("bedrock.newsletter.utils.get_newsletters", newsletters_mock)
|
||||
class TestGetNewsletterLanguages(TestCase):
|
||||
def test_newsletter_langs(self):
|
||||
"""Without args should return all langs."""
|
||||
result = utils.get_languages_for_newsletters()
|
||||
good_set = {'en', 'es', 'fr', 'de', 'pt', 'ru'}
|
||||
good_set = {"en", "es", "fr", "de", "pt", "ru"}
|
||||
self.assertSetEqual(good_set, result)
|
||||
|
||||
def test_single_newsletter_langs(self):
|
||||
"""Should return languages for a single newsletter."""
|
||||
result = utils.get_languages_for_newsletters('join-mozilla')
|
||||
good_set = {'en', 'es'}
|
||||
result = utils.get_languages_for_newsletters("join-mozilla")
|
||||
good_set = {"en", "es"}
|
||||
self.assertSetEqual(good_set, result)
|
||||
|
||||
def test_list_newsletter_langs(self):
|
||||
"""Should return all languages for specified list of newsletters."""
|
||||
result = utils.get_languages_for_newsletters(['join-mozilla', 'beta'])
|
||||
good_set = {'en', 'es'}
|
||||
result = utils.get_languages_for_newsletters(["join-mozilla", "beta"])
|
||||
good_set = {"en", "es"}
|
||||
self.assertSetEqual(good_set, result)
|
||||
|
||||
result = utils.get_languages_for_newsletters(['firefox-tips', 'beta'])
|
||||
good_set = {'en', 'fr', 'de', 'pt', 'ru'}
|
||||
result = utils.get_languages_for_newsletters(["firefox-tips", "beta"])
|
||||
good_set = {"en", "fr", "de", "pt", "ru"}
|
||||
self.assertSetEqual(good_set, result)
|
||||
|
||||
def test_works_with_bad_newsletter(self):
|
||||
"""If given a bad newsletter name, should still return a set."""
|
||||
result = utils.get_languages_for_newsletters(['join-mozilla', 'eldudarino'])
|
||||
good_set = {'en', 'es'}
|
||||
result = utils.get_languages_for_newsletters(["join-mozilla", "eldudarino"])
|
||||
good_set = {"en", "es"}
|
||||
self.assertSetEqual(good_set, result)
|
||||
|
|
|
@ -27,132 +27,119 @@ class TestViews(TestCase):
|
|||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
@patch('bedrock.newsletter.views.l10n_utils.render')
|
||||
@patch("bedrock.newsletter.views.l10n_utils.render")
|
||||
def test_updated_allows_good_tokens(self, mock_render):
|
||||
token = str(uuid.uuid4())
|
||||
req = self.rf.get('/', {'token': token, 'unsub': 1})
|
||||
req = self.rf.get("/", {"token": token, "unsub": 1})
|
||||
updated(req)
|
||||
self.assertEqual(mock_render.call_args[0][2]['token'], token)
|
||||
self.assertEqual(mock_render.call_args[0][2]["token"], token)
|
||||
|
||||
@patch('bedrock.newsletter.views.l10n_utils.render')
|
||||
@patch("bedrock.newsletter.views.l10n_utils.render")
|
||||
def test_updated_disallows_bad_tokens(self, mock_render):
|
||||
token = 'the-dude'
|
||||
req = self.rf.get('/', {'token': token, 'unsub': 1})
|
||||
token = "the-dude"
|
||||
req = self.rf.get("/", {"token": token, "unsub": 1})
|
||||
updated(req)
|
||||
assert mock_render.call_args[0][2]['token'] is None
|
||||
assert mock_render.call_args[0][2]["token"] is None
|
||||
|
||||
token = '\'>"><img src=x onerror=alert(1)>'
|
||||
req = self.rf.get('/', {'token': token, 'unsub': 1})
|
||||
token = "'>\"><img src=x onerror=alert(1)>"
|
||||
req = self.rf.get("/", {"token": token, "unsub": 1})
|
||||
updated(req)
|
||||
assert mock_render.call_args[0][2]['token'] is None
|
||||
assert mock_render.call_args[0][2]["token"] is None
|
||||
|
||||
|
||||
# Always mock basket.request to be sure we never actually call basket
|
||||
# during tests.
|
||||
@patch('basket.base.request')
|
||||
@patch("basket.base.request")
|
||||
class TestExistingNewsletterView(TestCase):
|
||||
def setUp(self):
|
||||
self.token = str(uuid.uuid4())
|
||||
self.user = {
|
||||
'newsletters': [u'mozilla-and-you'],
|
||||
'token': self.token,
|
||||
'email': u'user@example.com',
|
||||
'lang': u'pt',
|
||||
'country': u'br',
|
||||
'format': u'T',
|
||||
"newsletters": ["mozilla-and-you"],
|
||||
"token": self.token,
|
||||
"email": "user@example.com",
|
||||
"lang": "pt",
|
||||
"country": "br",
|
||||
"format": "T",
|
||||
}
|
||||
# By default, data matches user's existing data; change it
|
||||
# in the test as desired. Also, user has accepted privacy
|
||||
# checkbox.
|
||||
self.data = {
|
||||
u'form-MAX_NUM_FORMS': 4,
|
||||
u'form-INITIAL_FORMS': 4,
|
||||
u'form-TOTAL_FORMS': 4,
|
||||
u'email': self.user['email'],
|
||||
u'lang': self.user['lang'],
|
||||
u'country': self.user['country'],
|
||||
u'format': self.user['format'],
|
||||
u'privacy': u'on',
|
||||
u'form-0-newsletter': u'mozilla-and-you',
|
||||
u'form-0-subscribed_radio': u'true',
|
||||
u'form-1-newsletter': u'mobile',
|
||||
u'form-1-subscribed_radio': u'false',
|
||||
u'form-2-newsletter': u'firefox-tips',
|
||||
u'form-2-subscribed_check': u'false',
|
||||
u'form-3-newsletter': u'join-mozilla',
|
||||
u'form-3-subscribed_check': u'false',
|
||||
u'submit': u'Save Preferences',
|
||||
"form-MAX_NUM_FORMS": 4,
|
||||
"form-INITIAL_FORMS": 4,
|
||||
"form-TOTAL_FORMS": 4,
|
||||
"email": self.user["email"],
|
||||
"lang": self.user["lang"],
|
||||
"country": self.user["country"],
|
||||
"format": self.user["format"],
|
||||
"privacy": "on",
|
||||
"form-0-newsletter": "mozilla-and-you",
|
||||
"form-0-subscribed_radio": "true",
|
||||
"form-1-newsletter": "mobile",
|
||||
"form-1-subscribed_radio": "false",
|
||||
"form-2-newsletter": "firefox-tips",
|
||||
"form-2-subscribed_check": "false",
|
||||
"form-3-newsletter": "join-mozilla",
|
||||
"form-3-subscribed_check": "false",
|
||||
"submit": "Save Preferences",
|
||||
}
|
||||
super(TestExistingNewsletterView, self).setUp()
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_will_show_confirm_copy(self, get_newsletters, mock_basket_request):
|
||||
# After successful confirm, ensure proper context var is set to display
|
||||
# confirmation-specific copy.
|
||||
get_newsletters.return_value = newsletters
|
||||
url = "%s?confirm=1" % reverse('newsletter.existing.token', args=(self.token,))
|
||||
url = "%s?confirm=1" % reverse("newsletter.existing.token", args=(self.token,))
|
||||
# noinspection PyUnresolvedReferences
|
||||
with patch.multiple('basket',
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
basket_patches['request'].return_value = self.user
|
||||
render.return_value = HttpResponse('')
|
||||
with patch.multiple("basket", request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
basket_patches["request"].return_value = self.user
|
||||
render.return_value = HttpResponse("")
|
||||
self.client.get(url)
|
||||
request, template_name, context = render.call_args[0]
|
||||
self.assertEqual(context['did_confirm'], True)
|
||||
self.assertEqual(context["did_confirm"], True)
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_get_token(self, get_newsletters, mock_basket_request):
|
||||
# If user gets page with valid token in their URL, they
|
||||
# see their data, and no privacy checkbox is presented
|
||||
get_newsletters.return_value = newsletters
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
# noinspection PyUnresolvedReferences
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
basket_patches['request'].return_value = self.user
|
||||
render.return_value = HttpResponse('')
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
basket_patches["request"].return_value = self.user
|
||||
render.return_value = HttpResponse("")
|
||||
self.client.get(url)
|
||||
request, template_name, context = render.call_args[0]
|
||||
form = context['form']
|
||||
self.assertNotIn('privacy', form.fields)
|
||||
self.assertEqual(self.user['lang'], form.initial['lang'])
|
||||
form = context["form"]
|
||||
self.assertNotIn("privacy", form.fields)
|
||||
self.assertEqual(self.user["lang"], form.initial["lang"])
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_show(self, get_newsletters, mock_basket_request):
|
||||
# Newsletters are only listed if the user is subscribed to them,
|
||||
# or they are marked 'show' and 'active' in the settings
|
||||
get_newsletters.return_value = newsletters
|
||||
# Find a newsletter without 'show' and subscribe the user to it
|
||||
for newsletter, data in newsletters.items():
|
||||
if not data.get('show', False):
|
||||
self.user['newsletters'] = [newsletter]
|
||||
if not data.get("show", False):
|
||||
self.user["newsletters"] = [newsletter]
|
||||
break
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
basket_patches['request'].return_value = self.user
|
||||
render.return_value = HttpResponse('')
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
basket_patches["request"].return_value = self.user
|
||||
render.return_value = HttpResponse("")
|
||||
self.client.get(url)
|
||||
request, template_name, context = render.call_args[0]
|
||||
forms = context['formset'].initial_forms
|
||||
forms = context["formset"].initial_forms
|
||||
|
||||
shown = set([form.initial['newsletter'] for form in forms])
|
||||
inactive = set([newsletter for newsletter, data
|
||||
in newsletters.items()
|
||||
if not data.get('active', False)])
|
||||
to_show = set([newsletter for newsletter, data
|
||||
in newsletters.items()
|
||||
if data.get('show', False)]) - inactive
|
||||
subscribed = set(self.user['newsletters'])
|
||||
shown = set([form.initial["newsletter"] for form in forms])
|
||||
inactive = set([newsletter for newsletter, data in newsletters.items() if not data.get("active", False)])
|
||||
to_show = set([newsletter for newsletter, data in newsletters.items() if data.get("show", False)]) - inactive
|
||||
subscribed = set(self.user["newsletters"])
|
||||
|
||||
# All subscribed newsletters except inactive ones are shown
|
||||
self.assertEqual(set(), subscribed - inactive - shown)
|
||||
|
@ -163,372 +150,325 @@ class TestExistingNewsletterView(TestCase):
|
|||
|
||||
def test_get_no_token(self, mock_basket_request):
|
||||
# No token in URL - should redirect to recovery
|
||||
url = reverse('newsletter.existing.token', args=('',))
|
||||
url = reverse("newsletter.existing.token", args=("",))
|
||||
rsp = self.client.get(url)
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith(reverse('newsletter.recovery')))
|
||||
self.assertTrue(rsp["Location"].endswith(reverse("newsletter.recovery")))
|
||||
|
||||
def test_get_user_not_found(self, mock_basket_request):
|
||||
# Token in URL but not a valid token - should redirect to recovery
|
||||
rand_token = str(uuid.uuid4())
|
||||
url = reverse('newsletter.existing.token', args=(rand_token,))
|
||||
with patch.multiple('basket',
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
render.return_value = HttpResponse('')
|
||||
with patch('django.contrib.messages.add_message') as add_msg:
|
||||
basket_patches['request'].side_effect = basket.BasketException
|
||||
url = reverse("newsletter.existing.token", args=(rand_token,))
|
||||
with patch.multiple("basket", request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
render.return_value = HttpResponse("")
|
||||
with patch("django.contrib.messages.add_message") as add_msg:
|
||||
basket_patches["request"].side_effect = basket.BasketException
|
||||
rsp = self.client.get(url)
|
||||
# Should have given a message
|
||||
self.assertEqual(1, add_msg.call_count,
|
||||
msg=repr(add_msg.call_args_list))
|
||||
self.assertEqual(1, add_msg.call_count, msg=repr(add_msg.call_args_list))
|
||||
# Should have been redirected to recovery page
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith(reverse('newsletter.recovery')))
|
||||
self.assertTrue(rsp["Location"].endswith(reverse("newsletter.recovery")))
|
||||
|
||||
def test_invalid_token(self, mock_basket_request):
|
||||
# "Token" in URL is not syntactically a UUID - should redirect to
|
||||
# recovery *without* calling Exact Target
|
||||
token = "not a token"
|
||||
url = reverse('newsletter.existing.token', args=(token,))
|
||||
with patch.multiple('basket', request=DEFAULT) as basket_patches:
|
||||
with patch('django.contrib.messages.add_message') as add_msg:
|
||||
url = reverse("newsletter.existing.token", args=(token,))
|
||||
with patch.multiple("basket", request=DEFAULT) as basket_patches:
|
||||
with patch("django.contrib.messages.add_message") as add_msg:
|
||||
rsp = self.client.get(url, follow=False)
|
||||
self.assertEqual(0, basket_patches['request'].call_count)
|
||||
self.assertEqual(0, basket_patches["request"].call_count)
|
||||
self.assertEqual(1, add_msg.call_count)
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith(reverse('newsletter.recovery')))
|
||||
self.assertTrue(rsp["Location"].endswith(reverse("newsletter.recovery")))
|
||||
|
||||
def test_post_user_not_found(self, mock_basket_request):
|
||||
# User submits form and passed token, but no user was found
|
||||
# Should issue message and redirect to recovery
|
||||
rand_token = str(uuid.uuid4())
|
||||
url = reverse('newsletter.existing.token', args=(rand_token,))
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
render.return_value = HttpResponse('')
|
||||
with patch('django.contrib.messages.add_message') as add_msg:
|
||||
basket_patches['request'].side_effect = basket.BasketException
|
||||
url = reverse("newsletter.existing.token", args=(rand_token,))
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
render.return_value = HttpResponse("")
|
||||
with patch("django.contrib.messages.add_message") as add_msg:
|
||||
basket_patches["request"].side_effect = basket.BasketException
|
||||
rsp = self.client.post(url, self.data)
|
||||
# Shouldn't call basket except for the attempt to find the user
|
||||
self.assertEqual(0, basket_patches['update_user'].call_count)
|
||||
self.assertEqual(0, basket_patches['unsubscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches['subscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["update_user"].call_count)
|
||||
self.assertEqual(0, basket_patches["unsubscribe"].call_count)
|
||||
self.assertEqual(0, basket_patches["subscribe"].call_count)
|
||||
# Should have given a message
|
||||
self.assertEqual(1, add_msg.call_count,
|
||||
msg=repr(add_msg.call_args_list))
|
||||
self.assertEqual(1, add_msg.call_count, msg=repr(add_msg.call_args_list))
|
||||
# Should have been redirected to recovery page
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith(reverse('newsletter.recovery')))
|
||||
self.assertTrue(rsp["Location"].endswith(reverse("newsletter.recovery")))
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_subscribing(self, get_newsletters, mock_basket_request):
|
||||
get_newsletters.return_value = newsletters
|
||||
# They subscribe to firefox-tips
|
||||
self.data['form-2-subscribed_check'] = u'true'
|
||||
self.data["form-2-subscribed_check"] = "true"
|
||||
# in English - and that's their language too
|
||||
self.user['lang'] = u'en'
|
||||
self.data['lang'] = u'en'
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('django.contrib.messages.add_message') as add_msg:
|
||||
with patch('lib.l10n_utils.render'):
|
||||
basket_patches['request'].return_value = self.user
|
||||
self.user["lang"] = "en"
|
||||
self.data["lang"] = "en"
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("django.contrib.messages.add_message") as add_msg:
|
||||
with patch("lib.l10n_utils.render"):
|
||||
basket_patches["request"].return_value = self.user
|
||||
rsp = self.client.post(url, self.data)
|
||||
# Should have given no messages
|
||||
self.assertEqual(0, add_msg.call_count,
|
||||
msg=repr(add_msg.call_args_list))
|
||||
self.assertEqual(0, add_msg.call_count, msg=repr(add_msg.call_args_list))
|
||||
# Should have called update_user with subscription list
|
||||
self.assertEqual(1, basket_patches['update_user'].call_count)
|
||||
kwargs = basket_patches['update_user'].call_args[1]
|
||||
self.assertEqual(set(kwargs), set(['newsletters', 'lang']))
|
||||
self.assertEqual(kwargs['lang'], 'en')
|
||||
self.assertEqual(set(kwargs['newsletters'].split(',')), set(['mozilla-and-you', 'firefox-tips']))
|
||||
self.assertEqual(1, basket_patches["update_user"].call_count)
|
||||
kwargs = basket_patches["update_user"].call_args[1]
|
||||
self.assertEqual(set(kwargs), set(["newsletters", "lang"]))
|
||||
self.assertEqual(kwargs["lang"], "en")
|
||||
self.assertEqual(set(kwargs["newsletters"].split(",")), set(["mozilla-and-you", "firefox-tips"]))
|
||||
# Should not have called unsubscribe
|
||||
self.assertEqual(0, basket_patches['unsubscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["unsubscribe"].call_count)
|
||||
# Should not have called subscribe
|
||||
self.assertEqual(0, basket_patches['subscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["subscribe"].call_count)
|
||||
# Should redirect to the 'updated' view
|
||||
url = reverse('newsletter.updated')
|
||||
assert rsp['Location'] == url
|
||||
url = reverse("newsletter.updated")
|
||||
assert rsp["Location"] == url
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_unsubscribing(self, get_newsletters, mock_basket_request):
|
||||
get_newsletters.return_value = newsletters
|
||||
# They unsubscribe from the one newsletter they're subscribed to
|
||||
self.data['form-0-subscribed_radio'] = u'False'
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render'):
|
||||
basket_patches['request'].return_value = self.user
|
||||
self.data["form-0-subscribed_radio"] = "False"
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render"):
|
||||
basket_patches["request"].return_value = self.user
|
||||
rsp = self.client.post(url, self.data)
|
||||
# Should have called update_user with list of newsletters
|
||||
self.assertEqual(1, basket_patches['update_user'].call_count)
|
||||
kwargs = basket_patches['update_user'].call_args[1]
|
||||
self.assertEqual(
|
||||
{'newsletters': u'', 'lang': u'pt'},
|
||||
kwargs
|
||||
)
|
||||
self.assertEqual(1, basket_patches["update_user"].call_count)
|
||||
kwargs = basket_patches["update_user"].call_args[1]
|
||||
self.assertEqual({"newsletters": "", "lang": "pt"}, kwargs)
|
||||
# Should not have called subscribe
|
||||
self.assertEqual(0, basket_patches['subscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["subscribe"].call_count)
|
||||
# Should not have called unsubscribe
|
||||
self.assertEqual(0, basket_patches['unsubscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["unsubscribe"].call_count)
|
||||
# Should redirect to the 'updated' view
|
||||
url = reverse('newsletter.updated')
|
||||
assert rsp['Location'] == url
|
||||
url = reverse("newsletter.updated")
|
||||
assert rsp["Location"] == url
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_remove_all(self, get_newsletters, mock_basket_request):
|
||||
get_newsletters.return_value = newsletters
|
||||
self.data['remove_all'] = 'on' # any value should do
|
||||
self.data["remove_all"] = "on" # any value should do
|
||||
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render'):
|
||||
basket_patches['request'].return_value = self.user
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render"):
|
||||
basket_patches["request"].return_value = self.user
|
||||
rsp = self.client.post(url, self.data)
|
||||
# Should not have updated user details at all
|
||||
self.assertEqual(0, basket_patches['update_user'].call_count)
|
||||
self.assertEqual(0, basket_patches["update_user"].call_count)
|
||||
# Should have called unsubscribe
|
||||
self.assertEqual(1, basket_patches['unsubscribe'].call_count)
|
||||
self.assertEqual(1, basket_patches["unsubscribe"].call_count)
|
||||
# and said user opts out
|
||||
args, kwargs = basket_patches['unsubscribe'].call_args
|
||||
self.assertEqual((self.token, self.user['email']), args)
|
||||
self.assertTrue(kwargs['optout'])
|
||||
args, kwargs = basket_patches["unsubscribe"].call_args
|
||||
self.assertEqual((self.token, self.user["email"]), args)
|
||||
self.assertTrue(kwargs["optout"])
|
||||
# Should redirect to the 'updated' view with unsub=1 and token
|
||||
url = reverse('newsletter.updated') + "?unsub=1"
|
||||
url = reverse("newsletter.updated") + "?unsub=1"
|
||||
url += "&token=%s" % self.token
|
||||
assert rsp['Location'] == url
|
||||
assert rsp["Location"] == url
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_change_lang_country(self, get_newsletters, mock_basket_request):
|
||||
get_newsletters.return_value = newsletters
|
||||
self.data['lang'] = 'en'
|
||||
self.data['country'] = 'us'
|
||||
self.data["lang"] = "en"
|
||||
self.data["country"] = "us"
|
||||
|
||||
with self.activate('en-US'):
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with self.activate("en-US"):
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render'):
|
||||
with patch('django.contrib.messages.add_message') as add_msg:
|
||||
basket_patches['request'].return_value = self.user
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render"):
|
||||
with patch("django.contrib.messages.add_message") as add_msg:
|
||||
basket_patches["request"].return_value = self.user
|
||||
rsp = self.client.post(url, self.data)
|
||||
|
||||
# We have an existing user with a change to their email data,
|
||||
# but none to their subscriptions.
|
||||
# 'subscribe' should not be called
|
||||
self.assertEqual(0, basket_patches['subscribe'].call_count)
|
||||
self.assertEqual(0, basket_patches["subscribe"].call_count)
|
||||
# update_user should be called once
|
||||
self.assertEqual(1, basket_patches['update_user'].call_count)
|
||||
self.assertEqual(1, basket_patches["update_user"].call_count)
|
||||
# with the new lang and country and the newsletter list
|
||||
kwargs = basket_patches['update_user'].call_args[1]
|
||||
self.assertEqual(
|
||||
{'lang': u'en',
|
||||
'country': u'us',
|
||||
'newsletters': u'mozilla-and-you'},
|
||||
kwargs
|
||||
)
|
||||
kwargs = basket_patches["update_user"].call_args[1]
|
||||
self.assertEqual({"lang": "en", "country": "us", "newsletters": "mozilla-and-you"}, kwargs)
|
||||
# No messages should be emitted
|
||||
self.assertEqual(0, add_msg.call_count,
|
||||
msg=repr(add_msg.call_args_list))
|
||||
self.assertEqual(0, add_msg.call_count, msg=repr(add_msg.call_args_list))
|
||||
# Should redirect to the 'updated' view
|
||||
url = reverse('newsletter.updated')
|
||||
assert rsp['Location'] == url
|
||||
url = reverse("newsletter.updated")
|
||||
assert rsp["Location"] == url
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_newsletter_ordering(self, get_newsletters, mock_basket_request):
|
||||
# Newsletters are listed in 'order' order, if they have an 'order'
|
||||
# field
|
||||
get_newsletters.return_value = newsletters
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
self.user['newsletters'] = [u'mozilla-and-you', u'firefox-tips',
|
||||
u'beta']
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
basket_patches['request'].return_value = self.user
|
||||
render.return_value = HttpResponse('')
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
self.user["newsletters"] = ["mozilla-and-you", "firefox-tips", "beta"]
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
basket_patches["request"].return_value = self.user
|
||||
render.return_value = HttpResponse("")
|
||||
self.client.get(url)
|
||||
request, template_name, context = render.call_args[0]
|
||||
forms = context['formset'].initial_forms
|
||||
forms = context["formset"].initial_forms
|
||||
|
||||
newsletters_in_order = [form.initial['newsletter'] for form in forms]
|
||||
self.assertEqual([u'firefox-tips', u'mozilla-and-you'],
|
||||
newsletters_in_order)
|
||||
newsletters_in_order = [form.initial["newsletter"] for form in forms]
|
||||
self.assertEqual(["firefox-tips", "mozilla-and-you"], newsletters_in_order)
|
||||
|
||||
@patch('bedrock.newsletter.utils.get_newsletters')
|
||||
@patch("bedrock.newsletter.utils.get_newsletters")
|
||||
def test_newsletter_no_order(self, get_newsletters, mock_basket_request):
|
||||
"""Newsletter views should work if we get no order from basket."""
|
||||
orderless_newsletters = {}
|
||||
for key, val in newsletters.items():
|
||||
nl_copy = val.copy()
|
||||
del nl_copy['order']
|
||||
del nl_copy["order"]
|
||||
orderless_newsletters[key] = nl_copy
|
||||
|
||||
get_newsletters.return_value = orderless_newsletters
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
self.user['newsletters'] = [u'mozilla-and-you', u'firefox-tips',
|
||||
u'beta']
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
unsubscribe=DEFAULT,
|
||||
request=DEFAULT) as basket_patches:
|
||||
with patch('lib.l10n_utils.render') as render:
|
||||
basket_patches['request'].return_value = self.user
|
||||
render.return_value = HttpResponse('')
|
||||
url = reverse("newsletter.existing.token", args=(self.token,))
|
||||
self.user["newsletters"] = ["mozilla-and-you", "firefox-tips", "beta"]
|
||||
with patch.multiple("basket", update_user=DEFAULT, subscribe=DEFAULT, unsubscribe=DEFAULT, request=DEFAULT) as basket_patches:
|
||||
with patch("lib.l10n_utils.render") as render:
|
||||
basket_patches["request"].return_value = self.user
|
||||
render.return_value = HttpResponse("")
|
||||
self.client.get(url)
|
||||
request, template_name, context = render.call_args[0]
|
||||
forms = context['formset'].initial_forms
|
||||
forms = context["formset"].initial_forms
|
||||
|
||||
newsletters_in_order = [form.initial['newsletter'] for form in forms]
|
||||
self.assertEqual([u'mozilla-and-you', u'firefox-tips'],
|
||||
newsletters_in_order)
|
||||
newsletters_in_order = [form.initial["newsletter"] for form in forms]
|
||||
self.assertEqual(["mozilla-and-you", "firefox-tips"], newsletters_in_order)
|
||||
|
||||
|
||||
class TestConfirmView(TestCase):
|
||||
def setUp(self):
|
||||
self.token = str(uuid.uuid4())
|
||||
self.url = reverse('newsletter.confirm', kwargs={'token': self.token})
|
||||
self.url = reverse("newsletter.confirm", kwargs={"token": self.token})
|
||||
|
||||
def test_normal(self):
|
||||
"""Confirm works with a valid token"""
|
||||
with patch('basket.confirm') as confirm:
|
||||
confirm.return_value = {'status': 'ok'}
|
||||
with patch("basket.confirm") as confirm:
|
||||
confirm.return_value = {"status": "ok"}
|
||||
rsp = self.client.get(self.url)
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith("%s?confirm=1" %
|
||||
reverse('newsletter.existing.token',
|
||||
kwargs={'token': self.token})))
|
||||
self.assertTrue(rsp["Location"].endswith("%s?confirm=1" % reverse("newsletter.existing.token", kwargs={"token": self.token})))
|
||||
|
||||
def test_normal_with_query_params(self):
|
||||
"""Confirm works with a valid token"""
|
||||
with patch('basket.confirm') as confirm:
|
||||
confirm.return_value = {'status': 'ok'}
|
||||
rsp = self.client.get(self.url + '?utm_tracking=oh+definitely+yes&utm_source=malibu')
|
||||
with patch("basket.confirm") as confirm:
|
||||
confirm.return_value = {"status": "ok"}
|
||||
rsp = self.client.get(self.url + "?utm_tracking=oh+definitely+yes&utm_source=malibu")
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
self.assertTrue(rsp['Location'].endswith("%s?confirm=1&utm_tracking=oh+definitely+yes&"
|
||||
"utm_source=malibu" %
|
||||
reverse('newsletter.existing.token',
|
||||
kwargs={'token': self.token})))
|
||||
self.assertTrue(
|
||||
rsp["Location"].endswith(
|
||||
"%s?confirm=1&utm_tracking=oh+definitely+yes&"
|
||||
"utm_source=malibu" % reverse("newsletter.existing.token", kwargs={"token": self.token})
|
||||
)
|
||||
)
|
||||
|
||||
def test_basket_down(self):
|
||||
"""If basket is down, we report the appropriate error"""
|
||||
with patch('basket.confirm') as confirm:
|
||||
with patch("basket.confirm") as confirm:
|
||||
confirm.side_effect = basket.BasketException()
|
||||
with patch('lib.l10n_utils.render') as mock_render:
|
||||
mock_render.return_value = HttpResponse('')
|
||||
with patch("lib.l10n_utils.render") as mock_render:
|
||||
mock_render.return_value = HttpResponse("")
|
||||
rsp = self.client.get(self.url, follow=True)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
confirm.assert_called_with(self.token)
|
||||
context = mock_render.call_args[0][2]
|
||||
self.assertFalse(context['success'])
|
||||
self.assertTrue(context['generic_error'])
|
||||
self.assertFalse(context['token_error'])
|
||||
self.assertFalse(context["success"])
|
||||
self.assertTrue(context["generic_error"])
|
||||
self.assertFalse(context["token_error"])
|
||||
|
||||
def test_bad_token(self):
|
||||
"""If the token is bad, we report the appropriate error"""
|
||||
with patch('basket.confirm') as confirm:
|
||||
confirm.side_effect = basket.BasketException(status_code=403,
|
||||
code=basket.errors.BASKET_UNKNOWN_TOKEN)
|
||||
with patch('lib.l10n_utils.render') as mock_render:
|
||||
mock_render.return_value = HttpResponse('')
|
||||
with patch("basket.confirm") as confirm:
|
||||
confirm.side_effect = basket.BasketException(status_code=403, code=basket.errors.BASKET_UNKNOWN_TOKEN)
|
||||
with patch("lib.l10n_utils.render") as mock_render:
|
||||
mock_render.return_value = HttpResponse("")
|
||||
rsp = self.client.get(self.url, follow=True)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
confirm.assert_called_with(self.token)
|
||||
context = mock_render.call_args[0][2]
|
||||
self.assertFalse(context['success'])
|
||||
self.assertFalse(context['generic_error'])
|
||||
self.assertTrue(context['token_error'])
|
||||
self.assertFalse(context["success"])
|
||||
self.assertFalse(context["generic_error"])
|
||||
self.assertTrue(context["token_error"])
|
||||
|
||||
|
||||
class TestSetCountryView(TestCase):
|
||||
def setUp(self):
|
||||
self.token = str(uuid.uuid4())
|
||||
self.url = reverse('newsletter.country', kwargs={'token': self.token})
|
||||
self.url = reverse("newsletter.country", kwargs={"token": self.token})
|
||||
|
||||
def test_normal_submit(self):
|
||||
"""Confirm works with a valid token"""
|
||||
with patch('basket.request') as basket_mock:
|
||||
basket_mock.return_value = {'status': 'ok'}
|
||||
rsp = self.client.post(self.url, {'country': 'gb'})
|
||||
with patch("basket.request") as basket_mock:
|
||||
basket_mock.return_value = {"status": "ok"}
|
||||
rsp = self.client.post(self.url, {"country": "gb"})
|
||||
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
basket_mock.assert_called_with('post', 'user-meta', data={'country': 'gb'}, token=self.token)
|
||||
assert rsp['Location'] == reverse('newsletter.country_success')
|
||||
basket_mock.assert_called_with("post", "user-meta", data={"country": "gb"}, token=self.token)
|
||||
assert rsp["Location"] == reverse("newsletter.country_success")
|
||||
|
||||
@patch('basket.request')
|
||||
@patch('bedrock.newsletter.views.messages')
|
||||
@patch("basket.request")
|
||||
@patch("bedrock.newsletter.views.messages")
|
||||
def test_basket_down(self, messages_mock, basket_mock):
|
||||
"""If basket is down, we report the appropriate error"""
|
||||
basket_mock.side_effect = basket.BasketException()
|
||||
rsp = self.client.post(self.url, {'country': 'gb'})
|
||||
rsp = self.client.post(self.url, {"country": "gb"})
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
basket_mock.assert_called_with('post', 'user-meta', data={'country': 'gb'}, token=self.token)
|
||||
basket_mock.assert_called_with("post", "user-meta", data={"country": "gb"}, token=self.token)
|
||||
messages_mock.add_message.assert_called_with(ANY, messages_mock.ERROR, ANY)
|
||||
|
||||
|
||||
class TestRecoveryView(TestCase):
|
||||
def setUp(self):
|
||||
with self.activate('en-US'):
|
||||
self.url = reverse('newsletter.recovery')
|
||||
with self.activate("en-US"):
|
||||
self.url = reverse("newsletter.recovery")
|
||||
|
||||
def test_bad_email(self):
|
||||
"""Email syntax errors are caught"""
|
||||
data = {'email': 'not_an_email'}
|
||||
data = {"email": "not_an_email"}
|
||||
rsp = self.client.post(self.url, data)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertIn('email', rsp.context['form'].errors)
|
||||
self.assertIn("email", rsp.context["form"].errors)
|
||||
|
||||
@patch('basket.send_recovery_message', autospec=True)
|
||||
@patch("basket.send_recovery_message", autospec=True)
|
||||
def test_unknown_email(self, mock_basket):
|
||||
"""Unknown email addresses give helpful error message"""
|
||||
data = {'email': 'unknown@example.com'}
|
||||
mock_basket.side_effect = basket.BasketException(
|
||||
status_code=404, code=basket.errors.BASKET_UNKNOWN_EMAIL)
|
||||
data = {"email": "unknown@example.com"}
|
||||
mock_basket.side_effect = basket.BasketException(status_code=404, code=basket.errors.BASKET_UNKNOWN_EMAIL)
|
||||
rsp = self.client.post(self.url, data)
|
||||
self.assertTrue(mock_basket.called)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
|
||||
@patch('django.contrib.messages.add_message', autospec=True)
|
||||
@patch('basket.send_recovery_message', autospec=True)
|
||||
@patch("django.contrib.messages.add_message", autospec=True)
|
||||
@patch("basket.send_recovery_message", autospec=True)
|
||||
def test_good_email(self, mock_basket, add_msg):
|
||||
"""If basket returns success, don't report errors"""
|
||||
data = {'email': 'known@example.com'}
|
||||
mock_basket.return_value = {'status': 'ok'}
|
||||
data = {"email": "known@example.com"}
|
||||
mock_basket.return_value = {"status": "ok"}
|
||||
rsp = self.client.post(self.url, data)
|
||||
self.assertTrue(mock_basket.called)
|
||||
# On successful submit, we redirect
|
||||
self.assertEqual(302, rsp.status_code)
|
||||
rsp = self.client.get(rsp['Location'])
|
||||
rsp = self.client.get(rsp["Location"])
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertFalse(rsp.context['form'])
|
||||
self.assertFalse(rsp.context["form"])
|
||||
# We also give them a success message
|
||||
self.assertEqual(1, add_msg.call_count,
|
||||
msg=repr(add_msg.call_args_list))
|
||||
self.assertEqual(1, add_msg.call_count, msg=repr(add_msg.call_args_list))
|
||||
self.assertIn(recovery_text, add_msg.call_args[0])
|
||||
|
||||
|
||||
|
@ -537,170 +477,158 @@ class TestNewsletterSubscribe(TestCase):
|
|||
self.rf = RequestFactory()
|
||||
|
||||
def ajax_request(self, data, **kwargs):
|
||||
return self.request(data, HTTP_X_REQUESTED_WITH='XMLHttpRequest', **kwargs)
|
||||
return self.request(data, HTTP_X_REQUESTED_WITH="XMLHttpRequest", **kwargs)
|
||||
|
||||
def request(self, data=None, **kwargs):
|
||||
if data:
|
||||
req = self.rf.post('/', data, **kwargs)
|
||||
req = self.rf.post("/", data, **kwargs)
|
||||
else:
|
||||
req = self.rf.get('/', **kwargs)
|
||||
req = self.rf.get("/", **kwargs)
|
||||
|
||||
return newsletter_subscribe(req)
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_returns_ajax_errors(self, basket_mock):
|
||||
"""Incomplete data should return specific errors in JSON"""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
}
|
||||
resp = self.ajax_request(data)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertFalse(resp_data['success'])
|
||||
self.assertEqual(len(resp_data['errors']), 1)
|
||||
self.assertIn('privacy', resp_data['errors'][0])
|
||||
self.assertFalse(resp_data["success"])
|
||||
self.assertEqual(len(resp_data["errors"]), 1)
|
||||
self.assertIn("privacy", resp_data["errors"][0])
|
||||
self.assertFalse(basket_mock.called)
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_returns_sanitized_ajax_errors(self, basket_mock):
|
||||
"""Error messages should be HTML escaped.
|
||||
|
||||
Bug 1116754
|
||||
"""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
'country': '<svg/onload=alert("NEFARIOUSNESS")>',
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
"country": '<svg/onload=alert("NEFARIOUSNESS")>',
|
||||
}
|
||||
resp = self.ajax_request(data)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertFalse(resp_data['success'])
|
||||
self.assertEqual(len(resp_data['errors']), 1)
|
||||
self.assertNotIn(data['country'], resp_data['errors'][0])
|
||||
self.assertIn('NEFARIOUSNESS', resp_data['errors'][0])
|
||||
self.assertIn('<svg', resp_data['errors'][0])
|
||||
self.assertFalse(resp_data["success"])
|
||||
self.assertEqual(len(resp_data["errors"]), 1)
|
||||
self.assertNotIn(data["country"], resp_data["errors"][0])
|
||||
self.assertIn("NEFARIOUSNESS", resp_data["errors"][0])
|
||||
self.assertIn("<svg", resp_data["errors"][0])
|
||||
self.assertFalse(basket_mock.called)
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_no_source_url_use_referrer(self, basket_mock):
|
||||
"""Should set source_url to referrer if not sent"""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
}
|
||||
source_url = 'https://example.com/bambam'
|
||||
source_url = "https://example.com/bambam"
|
||||
resp = self.ajax_request(data, HTTP_REFERER=source_url)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertDictEqual(resp_data, {'success': True})
|
||||
basket_mock.subscribe.assert_called_with('fred@example.com', 'flintstones',
|
||||
format='H', source_url=source_url)
|
||||
self.assertDictEqual(resp_data, {"success": True})
|
||||
basket_mock.subscribe.assert_called_with("fred@example.com", "flintstones", format="H", source_url=source_url)
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_use_source_url_with_referer(self, basket_mock):
|
||||
"""Should use source_url even if there's a good referrer"""
|
||||
source_url = 'https://example.com/bambam'
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
'source_url': source_url
|
||||
}
|
||||
resp = self.ajax_request(data, HTTP_REFERER=source_url + '_WILMA')
|
||||
source_url = "https://example.com/bambam"
|
||||
data = {"newsletters": "flintstones", "email": "fred@example.com", "fmt": "H", "privacy": True, "source_url": source_url}
|
||||
resp = self.ajax_request(data, HTTP_REFERER=source_url + "_WILMA")
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertDictEqual(resp_data, {'success': True})
|
||||
basket_mock.subscribe.assert_called_with('fred@example.com', 'flintstones',
|
||||
format='H', source_url=source_url)
|
||||
self.assertDictEqual(resp_data, {"success": True})
|
||||
basket_mock.subscribe.assert_called_with("fred@example.com", "flintstones", format="H", source_url=source_url)
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_returns_ajax_success(self, basket_mock):
|
||||
"""Good post should return success JSON"""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
}
|
||||
resp = self.ajax_request(data)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertDictEqual(resp_data, {'success': True})
|
||||
basket_mock.subscribe.assert_called_with('fred@example.com', 'flintstones',
|
||||
format='H')
|
||||
self.assertDictEqual(resp_data, {"success": True})
|
||||
basket_mock.subscribe.assert_called_with("fred@example.com", "flintstones", format="H")
|
||||
|
||||
@patch.object(basket, 'subscribe')
|
||||
@patch.object(basket, "subscribe")
|
||||
def test_returns_ajax_invalid_email(self, subscribe_mock):
|
||||
"""Invalid email AJAX post should return proper error."""
|
||||
subscribe_mock.side_effect = basket.BasketException(
|
||||
code=basket.errors.BASKET_INVALID_EMAIL)
|
||||
subscribe_mock.side_effect = basket.BasketException(code=basket.errors.BASKET_INVALID_EMAIL)
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
}
|
||||
resp = self.ajax_request(data)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertFalse(resp_data['success'])
|
||||
self.assertEqual(resp_data['errors'][0], str(invalid_email_address))
|
||||
self.assertFalse(resp_data["success"])
|
||||
self.assertEqual(resp_data["errors"][0], str(invalid_email_address))
|
||||
|
||||
@patch.object(basket, 'subscribe')
|
||||
@patch.object(basket, "subscribe")
|
||||
def test_returns_ajax_basket_error(self, subscribe_mock):
|
||||
"""Basket error AJAX post should return proper error."""
|
||||
subscribe_mock.side_effect = basket.BasketException(
|
||||
code=basket.errors.BASKET_NETWORK_FAILURE)
|
||||
subscribe_mock.side_effect = basket.BasketException(code=basket.errors.BASKET_NETWORK_FAILURE)
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
}
|
||||
resp = self.ajax_request(data)
|
||||
resp_data = json.loads(resp.content)
|
||||
self.assertFalse(resp_data['success'])
|
||||
self.assertEqual(resp_data['errors'][0], str(general_error))
|
||||
self.assertFalse(resp_data["success"])
|
||||
self.assertEqual(resp_data["errors"][0], str(general_error))
|
||||
|
||||
def test_shows_normal_form(self):
|
||||
"""A normal GET should show the form."""
|
||||
resp = self.request()
|
||||
doc = pq(resp.content)
|
||||
self.assertTrue(doc('#newsletter-form'))
|
||||
self.assertTrue(doc("#newsletter-form"))
|
||||
self.assertTrue(doc('input[value="mozilla-foundation"]'))
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_returns_success(self, basket_mock):
|
||||
"""Good non-ajax post should return thank-you page."""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
'privacy': True,
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
"privacy": True,
|
||||
}
|
||||
resp = self.request(data)
|
||||
doc = pq(resp.content)
|
||||
self.assertFalse(doc('#newsletter-submit'))
|
||||
self.assertFalse(doc("#newsletter-submit"))
|
||||
self.assertFalse(doc('input[value="mozilla-and-you"]'))
|
||||
self.assertTrue(doc('#newsletter-thanks'))
|
||||
basket_mock.subscribe.assert_called_with('fred@example.com', 'flintstones',
|
||||
format='H')
|
||||
self.assertTrue(doc("#newsletter-thanks"))
|
||||
basket_mock.subscribe.assert_called_with("fred@example.com", "flintstones", format="H")
|
||||
|
||||
@patch('bedrock.newsletter.views.basket')
|
||||
@patch("bedrock.newsletter.views.basket")
|
||||
def test_returns_failure(self, basket_mock):
|
||||
"""Bad non-ajax post should return form with errors."""
|
||||
data = {
|
||||
'newsletters': 'flintstones',
|
||||
'email': 'fred@example.com',
|
||||
'fmt': 'H',
|
||||
"newsletters": "flintstones",
|
||||
"email": "fred@example.com",
|
||||
"fmt": "H",
|
||||
}
|
||||
resp = self.request(data)
|
||||
doc = pq(resp.content)
|
||||
self.assertTrue(doc('#newsletter-form'))
|
||||
self.assertTrue(doc("#newsletter-form"))
|
||||
self.assertFalse(doc('input[value="mozilla-and-you"]'))
|
||||
self.assertTrue(doc('input[value="flintstones"]'))
|
||||
self.assertFalse(doc('#email-form'))
|
||||
self.assertIn('privacy', doc('#newsletter-errors .mzp-u-list-styled li').eq(0).text())
|
||||
self.assertFalse(doc("#email-form"))
|
||||
self.assertIn("privacy", doc("#newsletter-errors .mzp-u-list-styled li").eq(0).text())
|
||||
self.assertFalse(basket_mock.subscribe.called)
|
||||
|
|
|
@ -8,53 +8,31 @@ from bedrock.mozorg.util import page
|
|||
|
||||
# A UUID looks like: f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
# Here's a regex to match a UUID:
|
||||
uuid_regex = r'[0-Fa-f]{8}-[0-Fa-f]{4}-[0-Fa-f]{4}-[0-Fa-f]{4}-[0-Fa-f]{12}'
|
||||
uuid_regex = r"[0-Fa-f]{8}-[0-Fa-f]{4}-[0-Fa-f]{4}-[0-Fa-f]{4}-[0-Fa-f]{12}"
|
||||
|
||||
|
||||
urlpatterns = (
|
||||
# view.existing allows a user who has a link including their token to
|
||||
# subscribe, unsubscribe, change their preferences. Each newsletter
|
||||
# includes that link for them.
|
||||
|
||||
url('^newsletter/existing/(?P<token>[^/]*)/?$',
|
||||
views.existing,
|
||||
name='newsletter.existing.token'),
|
||||
|
||||
url("^newsletter/existing/(?P<token>[^/]*)/?$", views.existing, name="newsletter.existing.token"),
|
||||
# After submitting on the `existing` page, users end up on the
|
||||
# `updated` page. There are optional query params; see the view.
|
||||
url('^newsletter/updated/$',
|
||||
views.updated,
|
||||
name='newsletter.updated'),
|
||||
|
||||
url("^newsletter/updated/$", views.updated, name="newsletter.updated"),
|
||||
# Confirm subscriptions
|
||||
url('^newsletter/confirm/(?P<token>%s)/$' % uuid_regex,
|
||||
views.confirm,
|
||||
name='newsletter.confirm'),
|
||||
|
||||
url("^newsletter/confirm/(?P<token>%s)/$" % uuid_regex, views.confirm, name="newsletter.confirm"),
|
||||
# Update country
|
||||
url('^newsletter/country/(?P<token>%s)/$' % uuid_regex,
|
||||
views.set_country,
|
||||
name='newsletter.country'),
|
||||
|
||||
url("^newsletter/country/(?P<token>%s)/$" % uuid_regex, views.set_country, name="newsletter.country"),
|
||||
# Request recovery message with link to manage subscriptions
|
||||
url('^newsletter/recovery/$',
|
||||
views.recovery,
|
||||
name='newsletter.recovery'),
|
||||
|
||||
url("^newsletter/recovery/$", views.recovery, name="newsletter.recovery"),
|
||||
# Receives POSTs from all subscribe forms
|
||||
url('^newsletter/$',
|
||||
views.newsletter_subscribe,
|
||||
name='newsletter.subscribe'),
|
||||
|
||||
url("^newsletter/$", views.newsletter_subscribe, name="newsletter.subscribe"),
|
||||
# Welcome program out-out confirmation page (bug 1442129)
|
||||
url('^newsletter/opt-out-confirmation/$',
|
||||
views.recovery,
|
||||
name='newsletter.opt-out-confirmation'),
|
||||
|
||||
url("^newsletter/opt-out-confirmation/$", views.recovery, name="newsletter.opt-out-confirmation"),
|
||||
# Branded signup pages for individual newsletters
|
||||
page('newsletter/mozilla', 'newsletter/mozilla.html', ftl_files=['mozorg/newsletters']),
|
||||
page('newsletter/firefox', 'newsletter/firefox.html', ftl_files=['mozorg/newsletters']),
|
||||
page('newsletter/developer', 'newsletter/developer.html', ftl_files=['mozorg/newsletters']),
|
||||
page('newsletter/country/success', 'newsletter/country_success.html', ftl_files=['mozorg/newsletters']),
|
||||
page('newsletter/fxa-error', 'newsletter/fxa-error.html', ftl_files=['mozorg/newsletters']),
|
||||
page("newsletter/mozilla", "newsletter/mozilla.html", ftl_files=["mozorg/newsletters"]),
|
||||
page("newsletter/firefox", "newsletter/firefox.html", ftl_files=["mozorg/newsletters"]),
|
||||
page("newsletter/developer", "newsletter/developer.html", ftl_files=["mozorg/newsletters"]),
|
||||
page("newsletter/country/success", "newsletter/country_success.html", ftl_files=["mozorg/newsletters"]),
|
||||
page("newsletter/fxa-error", "newsletter/fxa-error.html", ftl_files=["mozorg/newsletters"]),
|
||||
)
|
||||
|
|
|
@ -22,12 +22,12 @@ def get_languages_for_newsletters(newsletters=None):
|
|||
newsletters = list(all_newsletters.values())
|
||||
else:
|
||||
if isinstance(newsletters, str):
|
||||
newsletters = [nl.strip() for nl in newsletters.split(',')]
|
||||
newsletters = [nl.strip() for nl in newsletters.split(",")]
|
||||
newsletters = [all_newsletters.get(nl, {}) for nl in newsletters]
|
||||
|
||||
langs = set()
|
||||
for newsletter in newsletters:
|
||||
langs.update(newsletter.get('languages', []))
|
||||
langs.update(newsletter.get("languages", []))
|
||||
|
||||
return langs
|
||||
|
||||
|
@ -38,7 +38,7 @@ def custom_unsub_reason(token, reason):
|
|||
This is calling a basket API that's custom to Mozilla, that's
|
||||
why there's not a helper in the basket-client package."""
|
||||
data = {
|
||||
'token': token,
|
||||
'reason': reason,
|
||||
"token": token,
|
||||
"reason": reason,
|
||||
}
|
||||
return basket.request('post', 'custom_unsub_reason', data=data)
|
||||
return basket.request("post", "custom_unsub_reason", data=data)
|
||||
|
|
|
@ -25,154 +25,163 @@ from lib.l10n_utils.fluent import ftl, ftl_lazy
|
|||
|
||||
from bedrock.base import waffle
|
||||
from bedrock.base.urlresolvers import reverse
|
||||
|
||||
# Cannot use short "from . import utils" because we need to mock
|
||||
# utils.get_newsletters in our tests
|
||||
from bedrock.base.geo import get_country_from_request
|
||||
from bedrock.newsletter import utils
|
||||
|
||||
from .forms import (CountrySelectForm, EmailForm, ManageSubscriptionsForm,
|
||||
NewsletterFooterForm, NewsletterForm)
|
||||
from .forms import CountrySelectForm, EmailForm, ManageSubscriptionsForm, NewsletterFooterForm, NewsletterForm
|
||||
|
||||
log = commonware.log.getLogger('b.newsletter')
|
||||
log = commonware.log.getLogger("b.newsletter")
|
||||
|
||||
FTL_FILES = ['mozorg/newsletters']
|
||||
FTL_FILES = ["mozorg/newsletters"]
|
||||
|
||||
general_error = ftl_lazy('newsletters-we-are-sorry-but-there', ftl_files=FTL_FILES)
|
||||
thank_you = ftl_lazy('newsletters-your-email-preferences',
|
||||
fallback='newsletters-thanks-for-updating-your',
|
||||
ftl_files=FTL_FILES)
|
||||
bad_token = ftl_lazy('newsletters-the-supplied-link-has-expired-long', ftl_files=FTL_FILES)
|
||||
recovery_text = ftl_lazy('newsletters-success-an-email-has-been-sent', ftl_files=FTL_FILES)
|
||||
invalid_email_address = ftl_lazy('newsletters-this-is-not-a-valid-email', ftl_files=FTL_FILES)
|
||||
general_error = ftl_lazy("newsletters-we-are-sorry-but-there", ftl_files=FTL_FILES)
|
||||
thank_you = ftl_lazy("newsletters-your-email-preferences", fallback="newsletters-thanks-for-updating-your", ftl_files=FTL_FILES)
|
||||
bad_token = ftl_lazy("newsletters-the-supplied-link-has-expired-long", ftl_files=FTL_FILES)
|
||||
recovery_text = ftl_lazy("newsletters-success-an-email-has-been-sent", ftl_files=FTL_FILES)
|
||||
invalid_email_address = ftl_lazy("newsletters-this-is-not-a-valid-email", ftl_files=FTL_FILES)
|
||||
|
||||
NEWSLETTER_STRINGS = {
|
||||
u'about-mozilla': {
|
||||
'description': ftl_lazy('newsletters-join-mozillians-all-around', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla-community', ftl_files=FTL_FILES)},
|
||||
u'about-standards': {
|
||||
'title': ftl_lazy('newsletters-about-standards', ftl_files=FTL_FILES)},
|
||||
u'addon-dev': {
|
||||
'title': ftl_lazy('newsletters-addon-development', ftl_files=FTL_FILES)},
|
||||
u'affiliates': {
|
||||
'description': ftl_lazy('newsletters-a-monthly-newsletter-affiliates', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-affiliates', ftl_files=FTL_FILES)},
|
||||
u'ambassadors': {
|
||||
'description': ftl_lazy('newsletters-a-monthly-newsletter-ambassadors', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-student-ambassadors', ftl_files=FTL_FILES)},
|
||||
u'app-dev': {
|
||||
'description': ftl_lazy('newsletters-a-developers-guide', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-developer-newsletter', ftl_files=FTL_FILES)},
|
||||
u'aurora': {
|
||||
'description': ftl_lazy('newsletters-aurora', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-aurora', ftl_files=FTL_FILES)},
|
||||
u'beta': {
|
||||
'description': ftl_lazy('newsletters-read-about-the-latest-features', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-beta-news', ftl_files=FTL_FILES)},
|
||||
u'download-firefox-android': {
|
||||
'title': ftl_lazy('newsletters-download-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'download-firefox-androidsn': {
|
||||
'title': ftl_lazy('newsletters-get-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'download-firefox-androidsnus': {
|
||||
'title': ftl_lazy('newsletters-get-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'download-firefox-ios': {
|
||||
'title': ftl_lazy('newsletters-download-firefox-for-ios', ftl_files=FTL_FILES)},
|
||||
u'download-firefox-mobile': {
|
||||
'title': ftl_lazy('newsletters-download-firefox-for-mobile', ftl_files=FTL_FILES)},
|
||||
u'drumbeat': {
|
||||
'title': ftl_lazy('newsletters-drumbeat-newsgroup', ftl_files=FTL_FILES)},
|
||||
u'firefox-accounts-journey': {
|
||||
'description': ftl_lazy('newsletters-get-the-most-firefox-account', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-accounts-tips', ftl_files=FTL_FILES)},
|
||||
u'firefox-desktop': {
|
||||
'description': ftl_lazy('newsletters-dont-miss-the-latest', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-for-desktop', ftl_files=FTL_FILES)},
|
||||
u'firefox-flicks': {
|
||||
'description': ftl_lazy('newsletters-periodic-email-updates', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-flicks', ftl_files=FTL_FILES)},
|
||||
u'firefox-ios': {
|
||||
'description': ftl_lazy('newsletters-be-the-first-to-know', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-ios', ftl_files=FTL_FILES)},
|
||||
u'firefox-os': {
|
||||
'description': ftl_lazy('newsletters-dont-miss-important-news', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-os-smartphone-owner', ftl_files=FTL_FILES)},
|
||||
u'firefox-os-news': {
|
||||
'description': ftl_lazy('newsletters-a-monthly-newsletter-and-special', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-os-and-you', ftl_files=FTL_FILES)},
|
||||
u'firefox-tips': {
|
||||
'description': ftl_lazy('newsletters-get-a-weekly-tip', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-weekly-tips', ftl_files=FTL_FILES)},
|
||||
u'get-android-embed': {
|
||||
'title': ftl_lazy('newsletters-get-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'get-android-notembed': {
|
||||
'title': ftl_lazy('newsletters-get-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'get-involved': {
|
||||
'title': ftl_lazy('newsletters-get-involved', ftl_files=FTL_FILES)},
|
||||
u'internet-health-report': {
|
||||
'title': ftl_lazy('newsletters-insights',
|
||||
fallback='newsletters-internet-health-report',
|
||||
ftl_files=FTL_FILES),
|
||||
'description': ftl_lazy('newsletters-mozilla-published-articles-and-deep',
|
||||
fallback='newsletters-keep-up-with-our-annual',
|
||||
ftl_files=FTL_FILES)},
|
||||
u'join-mozilla': {
|
||||
'title': ftl_lazy('newsletters-join-mozilla', ftl_files=FTL_FILES)},
|
||||
u'knowledge-is-power': {
|
||||
'description': ftl_lazy('newsletters-get-all-the-knowledge', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-knowledge-is-power', ftl_files=FTL_FILES)},
|
||||
u'labs': {
|
||||
'title': ftl_lazy('newsletters-about-labs', ftl_files=FTL_FILES)},
|
||||
u'maker-party': {
|
||||
'description': ftl_lazy('newsletters-mozillas-largest-celebration', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-maker-party', ftl_files=FTL_FILES)},
|
||||
u'marketplace': {
|
||||
'description': ftl_lazy('newsletters-discover-the-latest', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-os', ftl_files=FTL_FILES)},
|
||||
u'marketplace-android': {
|
||||
'title': ftl_lazy('newsletters-android', ftl_files=FTL_FILES)},
|
||||
u'marketplace-desktop': {
|
||||
'title': ftl_lazy('newsletters-desktop', ftl_files=FTL_FILES)},
|
||||
u'mobile': {
|
||||
'description': ftl_lazy('newsletters-keep-up-with-releases', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-for-android', ftl_files=FTL_FILES)},
|
||||
u'mozilla-and-you': {
|
||||
'description': ftl_lazy('newsletters-get-how-tos', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-news', ftl_files=FTL_FILES)},
|
||||
u'mozilla-festival': {
|
||||
'description': ftl_lazy('newsletters-special-announcements-about-mozilla-v2', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla-festival', ftl_files=FTL_FILES)},
|
||||
u'mozilla-foundation': {
|
||||
'description': ftl_lazy('newsletters-regular-updates-to-keep-v2', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla-news', ftl_files=FTL_FILES)},
|
||||
u'mozilla-general': {
|
||||
'description': ftl_lazy('newsletters-special-accouncements-and-messages', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla', ftl_files=FTL_FILES)},
|
||||
u'mozilla-learning-network': {
|
||||
'description': ftl_lazy('newsletters-updates-from-our-global', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla-learning-network', ftl_files=FTL_FILES)},
|
||||
u'mozilla-phone': {
|
||||
'description': ftl_lazy('newsletters-email-updates-from-vouched', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozillians', ftl_files=FTL_FILES)},
|
||||
u'mozilla-technology': {
|
||||
'description': ftl_lazy('newsletters-were-building-the-technology', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-mozilla-labs', ftl_files=FTL_FILES)},
|
||||
u'os': {
|
||||
'description': ftl_lazy('newsletters-firefox-os-news', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-firefox-os', ftl_files=FTL_FILES)},
|
||||
u'shape-web': {
|
||||
'description': ftl_lazy('newsletters-news-and-information', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-shapre-of-the-web', ftl_files=FTL_FILES)},
|
||||
u'student-reps': {
|
||||
'description': ftl_lazy('newsletters-former-university-program', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-student-reps', ftl_files=FTL_FILES)},
|
||||
u'take-action-for-the-internet': {
|
||||
'description': ftl_lazy('newsletters-add-your-voice', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-take-action', ftl_files=FTL_FILES)},
|
||||
u'test-pilot': {
|
||||
'description': ftl_lazy('newsletters-help-us-make-a-better', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-new-product-testing', ftl_files=FTL_FILES)},
|
||||
u'webmaker': {
|
||||
'description': ftl_lazy('newsletters-special-announcements-helping-you', ftl_files=FTL_FILES),
|
||||
'title': ftl_lazy('newsletters-webmaker', ftl_files=FTL_FILES)},
|
||||
"about-mozilla": {
|
||||
"description": ftl_lazy("newsletters-join-mozillians-all-around", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla-community", ftl_files=FTL_FILES),
|
||||
},
|
||||
"about-standards": {"title": ftl_lazy("newsletters-about-standards", ftl_files=FTL_FILES)},
|
||||
"addon-dev": {"title": ftl_lazy("newsletters-addon-development", ftl_files=FTL_FILES)},
|
||||
"affiliates": {
|
||||
"description": ftl_lazy("newsletters-a-monthly-newsletter-affiliates", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-affiliates", ftl_files=FTL_FILES),
|
||||
},
|
||||
"ambassadors": {
|
||||
"description": ftl_lazy("newsletters-a-monthly-newsletter-ambassadors", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-student-ambassadors", ftl_files=FTL_FILES),
|
||||
},
|
||||
"app-dev": {
|
||||
"description": ftl_lazy("newsletters-a-developers-guide", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-developer-newsletter", ftl_files=FTL_FILES),
|
||||
},
|
||||
"aurora": {"description": ftl_lazy("newsletters-aurora", ftl_files=FTL_FILES), "title": ftl_lazy("newsletters-aurora", ftl_files=FTL_FILES)},
|
||||
"beta": {
|
||||
"description": ftl_lazy("newsletters-read-about-the-latest-features", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-beta-news", ftl_files=FTL_FILES),
|
||||
},
|
||||
"download-firefox-android": {"title": ftl_lazy("newsletters-download-firefox-for-android", ftl_files=FTL_FILES)},
|
||||
"download-firefox-androidsn": {"title": ftl_lazy("newsletters-get-firefox-for-android", ftl_files=FTL_FILES)},
|
||||
"download-firefox-androidsnus": {"title": ftl_lazy("newsletters-get-firefox-for-android", ftl_files=FTL_FILES)},
|
||||
"download-firefox-ios": {"title": ftl_lazy("newsletters-download-firefox-for-ios", ftl_files=FTL_FILES)},
|
||||
"download-firefox-mobile": {"title": ftl_lazy("newsletters-download-firefox-for-mobile", ftl_files=FTL_FILES)},
|
||||
"drumbeat": {"title": ftl_lazy("newsletters-drumbeat-newsgroup", ftl_files=FTL_FILES)},
|
||||
"firefox-accounts-journey": {
|
||||
"description": ftl_lazy("newsletters-get-the-most-firefox-account", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-accounts-tips", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-desktop": {
|
||||
"description": ftl_lazy("newsletters-dont-miss-the-latest", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-for-desktop", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-flicks": {
|
||||
"description": ftl_lazy("newsletters-periodic-email-updates", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-flicks", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-ios": {
|
||||
"description": ftl_lazy("newsletters-be-the-first-to-know", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-ios", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-os": {
|
||||
"description": ftl_lazy("newsletters-dont-miss-important-news", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-os-smartphone-owner", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-os-news": {
|
||||
"description": ftl_lazy("newsletters-a-monthly-newsletter-and-special", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-os-and-you", ftl_files=FTL_FILES),
|
||||
},
|
||||
"firefox-tips": {
|
||||
"description": ftl_lazy("newsletters-get-a-weekly-tip", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-weekly-tips", ftl_files=FTL_FILES),
|
||||
},
|
||||
"get-android-embed": {"title": ftl_lazy("newsletters-get-firefox-for-android", ftl_files=FTL_FILES)},
|
||||
"get-android-notembed": {"title": ftl_lazy("newsletters-get-firefox-for-android", ftl_files=FTL_FILES)},
|
||||
"get-involved": {"title": ftl_lazy("newsletters-get-involved", ftl_files=FTL_FILES)},
|
||||
"internet-health-report": {
|
||||
"title": ftl_lazy("newsletters-insights", fallback="newsletters-internet-health-report", ftl_files=FTL_FILES),
|
||||
"description": ftl_lazy(
|
||||
"newsletters-mozilla-published-articles-and-deep", fallback="newsletters-keep-up-with-our-annual", ftl_files=FTL_FILES
|
||||
),
|
||||
},
|
||||
"join-mozilla": {"title": ftl_lazy("newsletters-join-mozilla", ftl_files=FTL_FILES)},
|
||||
"knowledge-is-power": {
|
||||
"description": ftl_lazy("newsletters-get-all-the-knowledge", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-knowledge-is-power", ftl_files=FTL_FILES),
|
||||
},
|
||||
"labs": {"title": ftl_lazy("newsletters-about-labs", ftl_files=FTL_FILES)},
|
||||
"maker-party": {
|
||||
"description": ftl_lazy("newsletters-mozillas-largest-celebration", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-maker-party", ftl_files=FTL_FILES),
|
||||
},
|
||||
"marketplace": {
|
||||
"description": ftl_lazy("newsletters-discover-the-latest", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-os", ftl_files=FTL_FILES),
|
||||
},
|
||||
"marketplace-android": {"title": ftl_lazy("newsletters-android", ftl_files=FTL_FILES)},
|
||||
"marketplace-desktop": {"title": ftl_lazy("newsletters-desktop", ftl_files=FTL_FILES)},
|
||||
"mobile": {
|
||||
"description": ftl_lazy("newsletters-keep-up-with-releases", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-for-android", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-and-you": {
|
||||
"description": ftl_lazy("newsletters-get-how-tos", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-news", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-festival": {
|
||||
"description": ftl_lazy("newsletters-special-announcements-about-mozilla-v2", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla-festival", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-foundation": {
|
||||
"description": ftl_lazy("newsletters-regular-updates-to-keep-v2", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla-news", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-general": {
|
||||
"description": ftl_lazy("newsletters-special-accouncements-and-messages", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-learning-network": {
|
||||
"description": ftl_lazy("newsletters-updates-from-our-global", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla-learning-network", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-phone": {
|
||||
"description": ftl_lazy("newsletters-email-updates-from-vouched", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozillians", ftl_files=FTL_FILES),
|
||||
},
|
||||
"mozilla-technology": {
|
||||
"description": ftl_lazy("newsletters-were-building-the-technology", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-mozilla-labs", ftl_files=FTL_FILES),
|
||||
},
|
||||
"os": {
|
||||
"description": ftl_lazy("newsletters-firefox-os-news", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-firefox-os", ftl_files=FTL_FILES),
|
||||
},
|
||||
"shape-web": {
|
||||
"description": ftl_lazy("newsletters-news-and-information", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-shapre-of-the-web", ftl_files=FTL_FILES),
|
||||
},
|
||||
"student-reps": {
|
||||
"description": ftl_lazy("newsletters-former-university-program", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-student-reps", ftl_files=FTL_FILES),
|
||||
},
|
||||
"take-action-for-the-internet": {
|
||||
"description": ftl_lazy("newsletters-add-your-voice", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-take-action", ftl_files=FTL_FILES),
|
||||
},
|
||||
"test-pilot": {
|
||||
"description": ftl_lazy("newsletters-help-us-make-a-better", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-new-product-testing", ftl_files=FTL_FILES),
|
||||
},
|
||||
"webmaker": {
|
||||
"description": ftl_lazy("newsletters-special-announcements-helping-you", ftl_files=FTL_FILES),
|
||||
"title": ftl_lazy("newsletters-webmaker", ftl_files=FTL_FILES),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -181,8 +190,7 @@ UNSUB_REASONS_SUBMITTED = 2
|
|||
|
||||
# A UUID looks like: f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
# Here's a regex to match a UUID:
|
||||
UUID_REGEX = re.compile(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
|
||||
re.IGNORECASE)
|
||||
UUID_REGEX = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", re.IGNORECASE)
|
||||
|
||||
|
||||
def set_country(request, token):
|
||||
|
@ -190,21 +198,19 @@ def set_country(request, token):
|
|||
initial = {}
|
||||
countrycode = get_country_from_request(request)
|
||||
if countrycode:
|
||||
initial['country'] = countrycode.lower()
|
||||
initial["country"] = countrycode.lower()
|
||||
|
||||
form = CountrySelectForm('en-US', data=request.POST or None, initial=initial)
|
||||
form = CountrySelectForm("en-US", data=request.POST or None, initial=initial)
|
||||
if form.is_valid():
|
||||
try:
|
||||
basket.request('post', 'user-meta', data=form.cleaned_data, token=token)
|
||||
basket.request("post", "user-meta", data=form.cleaned_data, token=token)
|
||||
except basket.BasketException:
|
||||
log.exception("Error updating user's country in basket")
|
||||
messages.add_message(
|
||||
request, messages.ERROR, general_error
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, general_error)
|
||||
else:
|
||||
return redirect(reverse('newsletter.country_success'))
|
||||
return redirect(reverse("newsletter.country_success"))
|
||||
|
||||
return l10n_utils.render(request, 'newsletter/country.html', {'form': form})
|
||||
return l10n_utils.render(request, "newsletter/country.html", {"form": form})
|
||||
|
||||
|
||||
@never_cache
|
||||
|
@ -226,7 +232,7 @@ def confirm(request, token):
|
|||
# Any other exception
|
||||
generic_error = True
|
||||
else:
|
||||
if result['status'] == 'ok':
|
||||
if result["status"] == "ok":
|
||||
success = True
|
||||
else:
|
||||
# Shouldn't happen (errors should raise exception),
|
||||
|
@ -236,21 +242,15 @@ def confirm(request, token):
|
|||
# Assume rate limit error means user already confirmed and clicked confirm
|
||||
# link twice in quick succession
|
||||
if success or rate_limit_error:
|
||||
qparams = ['confirm=1']
|
||||
qs = request.META.get('QUERY_STRING', '')
|
||||
qparams = ["confirm=1"]
|
||||
qs = request.META.get("QUERY_STRING", "")
|
||||
if qs:
|
||||
qparams.append(qs)
|
||||
return HttpResponseRedirect("%s?%s" % (reverse('newsletter.existing.token',
|
||||
kwargs={'token': token}),
|
||||
'&'.join(qparams)))
|
||||
return HttpResponseRedirect("%s?%s" % (reverse("newsletter.existing.token", kwargs={"token": token}), "&".join(qparams)))
|
||||
else:
|
||||
return l10n_utils.render(
|
||||
request,
|
||||
'newsletter/confirm.html',
|
||||
{'success': success,
|
||||
'generic_error': generic_error,
|
||||
'token_error': token_error},
|
||||
ftl_files=FTL_FILES)
|
||||
request, "newsletter/confirm.html", {"success": success, "generic_error": generic_error, "token_error": token_error}, ftl_files=FTL_FILES
|
||||
)
|
||||
|
||||
|
||||
@never_cache
|
||||
|
@ -270,16 +270,16 @@ def existing(request, token=None):
|
|||
locale = l10n_utils.get_locale(request)
|
||||
|
||||
if not token:
|
||||
return redirect(reverse('newsletter.recovery'))
|
||||
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'))
|
||||
return redirect(reverse("newsletter.recovery"))
|
||||
|
||||
if waffle.switch('newsletter-maintenance-mode'):
|
||||
return l10n_utils.render(request, 'newsletter/existing.html', ftl_files=FTL_FILES)
|
||||
if waffle.switch("newsletter-maintenance-mode"):
|
||||
return l10n_utils.render(request, "newsletter/existing.html", ftl_files=FTL_FILES)
|
||||
|
||||
unsub_parm = None
|
||||
|
||||
|
@ -294,19 +294,19 @@ def existing(request, token=None):
|
|||
# u'email': u'user@example.com'
|
||||
# }
|
||||
|
||||
has_fxa = 'fxa' in request.GET
|
||||
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)
|
||||
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', ftl_files=FTL_FILES)
|
||||
return l10n_utils.render(request, "newsletter/existing.html", ftl_files=FTL_FILES)
|
||||
except basket.BasketException as e:
|
||||
log.exception("FAILED to get user from token (%s)", e.desc)
|
||||
|
||||
|
@ -314,10 +314,10 @@ def existing(request, token=None):
|
|||
# Bad or no token
|
||||
messages.add_message(request, messages.ERROR, bad_token)
|
||||
# Redirect to the recovery page
|
||||
return redirect(reverse('newsletter.recovery'))
|
||||
return redirect(reverse("newsletter.recovery"))
|
||||
|
||||
# if `has_fxa` not returned from basket, set it from the URL
|
||||
user.setdefault('has_fxa', has_fxa)
|
||||
user.setdefault("has_fxa", has_fxa)
|
||||
# Get the newsletter data - it's a dictionary of dictionaries
|
||||
newsletter_data = utils.get_newsletters()
|
||||
|
||||
|
@ -327,54 +327,55 @@ def existing(request, token=None):
|
|||
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):
|
||||
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']
|
||||
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'):
|
||||
if newsletter == "firefox-accounts-journey" and locale.startswith("en"):
|
||||
# alternate english title
|
||||
title = u'Firefox Account Tips'
|
||||
title = "Firefox Account Tips"
|
||||
else:
|
||||
title = nstrings['title']
|
||||
description = nstrings.get('description', u'')
|
||||
title = nstrings["title"]
|
||||
description = nstrings.get("description", "")
|
||||
else:
|
||||
title = data['title']
|
||||
description = data['description']
|
||||
title = data["title"]
|
||||
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),
|
||||
"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']
|
||||
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'
|
||||
keyfield = "order" if "order" in initial[0] else "title"
|
||||
initial.sort(key=itemgetter(keyfield))
|
||||
|
||||
NewsletterFormSet = formset_factory(NewsletterForm, extra=0,
|
||||
max_num=len(initial))
|
||||
NewsletterFormSet = formset_factory(NewsletterForm, extra=0, max_num=len(initial))
|
||||
|
||||
if request.method == 'POST':
|
||||
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']
|
||||
remove_all = form.is_valid() and form.cleaned_data["remove_all"]
|
||||
|
||||
formset_is_valid = False
|
||||
|
||||
|
@ -392,14 +393,16 @@ def existing(request, token=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
|
||||
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)
|
||||
form = ManageSubscriptionsForm(locale, data=request.POST, initial=user, **form_kwargs)
|
||||
|
||||
if formset_is_valid and form.is_valid():
|
||||
|
||||
|
@ -410,56 +413,45 @@ def existing(request, token=None):
|
|||
# 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']:
|
||||
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)
|
||||
kwargs["newsletters"] = ",".join(newsletters)
|
||||
if kwargs:
|
||||
# always send lang so basket doesn't try to guess
|
||||
kwargs['lang'] = data['lang']
|
||||
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',
|
||||
ftl_files=FTL_FILES)
|
||||
messages.add_message(request, messages.ERROR, general_error)
|
||||
return l10n_utils.render(request, "newsletter/existing.html", ftl_files=FTL_FILES)
|
||||
|
||||
# If they chose to remove all, tell basket that they've opted out
|
||||
if remove_all:
|
||||
try:
|
||||
basket.unsubscribe(token, user['email'], optout=True)
|
||||
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',
|
||||
ftl_files=FTL_FILES)
|
||||
messages.add_message(request, messages.ERROR, general_error)
|
||||
return l10n_utils.render(request, "newsletter/existing.html", ftl_files=FTL_FILES)
|
||||
# We need to pass their token to the next view
|
||||
url = reverse('newsletter.updated') \
|
||||
+ "?unsub=%s&token=%s" % (UNSUB_UNSUBSCRIBED_ALL, token)
|
||||
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')
|
||||
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
|
||||
)
|
||||
form = ManageSubscriptionsForm(locale, initial=user)
|
||||
formset = NewsletterFormSet(initial=initial)
|
||||
|
||||
# For the template, we want a dictionary whose keys are language codes
|
||||
|
@ -467,35 +459,32 @@ def existing(request, token=None):
|
|||
# that language code.
|
||||
newsletter_languages = defaultdict(list)
|
||||
for newsletter, data in newsletter_data.items():
|
||||
for lang in data['languages']:
|
||||
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']))
|
||||
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'],
|
||||
"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,
|
||||
ftl_files=FTL_FILES)
|
||||
return l10n_utils.render(request, "newsletter/existing.html", context, ftl_files=FTL_FILES)
|
||||
|
||||
|
||||
# Possible reasons for unsubscribing
|
||||
REASONS = [
|
||||
ftl_lazy('newsletters-you-send-too-many-emails', ftl_files=FTL_FILES),
|
||||
ftl_lazy('newsletters-your-content-wasnt-relevant', ftl_files=FTL_FILES),
|
||||
ftl_lazy('newsletters-your-email-design', ftl_files=FTL_FILES),
|
||||
ftl_lazy('newsletters-i-didnt-sign-up', ftl_files=FTL_FILES),
|
||||
ftl_lazy('newsletters-im-keeping-in-touch-v2', ftl_files=FTL_FILES),
|
||||
ftl_lazy("newsletters-you-send-too-many-emails", ftl_files=FTL_FILES),
|
||||
ftl_lazy("newsletters-your-content-wasnt-relevant", ftl_files=FTL_FILES),
|
||||
ftl_lazy("newsletters-your-email-design", ftl_files=FTL_FILES),
|
||||
ftl_lazy("newsletters-i-didnt-sign-up", ftl_files=FTL_FILES),
|
||||
ftl_lazy("newsletters-im-keeping-in-touch-v2", ftl_files=FTL_FILES),
|
||||
]
|
||||
|
||||
|
||||
|
@ -515,7 +504,7 @@ def updated(request):
|
|||
all.
|
||||
|
||||
"""
|
||||
unsub = _post_or_get(request, 'unsub', '0')
|
||||
unsub = _post_or_get(request, "unsub", "0")
|
||||
try:
|
||||
unsub = int(unsub)
|
||||
except ValueError:
|
||||
|
@ -527,7 +516,7 @@ def updated(request):
|
|||
reasons_submitted = unsub == UNSUB_REASONS_SUBMITTED
|
||||
|
||||
# Token might also have been passed (on remove_all only)
|
||||
token = _post_or_get(request, 'token', None)
|
||||
token = _post_or_get(request, "token", None)
|
||||
# token must be a UUID
|
||||
if token is not None and not UUID_REGEX.match(token):
|
||||
token = None
|
||||
|
@ -536,7 +525,7 @@ def updated(request):
|
|||
if not unsub:
|
||||
messages.add_message(request, messages.INFO, thank_you)
|
||||
|
||||
if request.method == 'POST' and reasons_submitted and token:
|
||||
if request.method == "POST" and reasons_submitted and token:
|
||||
# Tell basket about their reasons
|
||||
reasons = []
|
||||
|
||||
|
@ -544,25 +533,22 @@ def updated(request):
|
|||
# paste together the English versions of the reasons they submitted,
|
||||
# so we can read them. (Well, except for the free-form reason.)
|
||||
for i, reason in enumerate(REASONS):
|
||||
if _post_or_get(request, 'reason%d' % i):
|
||||
if _post_or_get(request, "reason%d" % i):
|
||||
reasons.append(str(reason))
|
||||
if _post_or_get(request, 'reason-text-p'):
|
||||
reasons.append(_post_or_get(request, 'reason-text', ''))
|
||||
if _post_or_get(request, "reason-text-p"):
|
||||
reasons.append(_post_or_get(request, "reason-text", ""))
|
||||
|
||||
reason_text = "\n\n".join(reasons) + "\n\n"
|
||||
|
||||
utils.custom_unsub_reason(token, reason_text)
|
||||
|
||||
context = {
|
||||
'unsubscribed_all': unsubscribed_all,
|
||||
'reasons_submitted': reasons_submitted,
|
||||
'token': token,
|
||||
'reasons': enumerate(REASONS),
|
||||
"unsubscribed_all": unsubscribed_all,
|
||||
"reasons_submitted": reasons_submitted,
|
||||
"token": token,
|
||||
"reasons": enumerate(REASONS),
|
||||
}
|
||||
return l10n_utils.render(request,
|
||||
'newsletter/updated.html',
|
||||
context,
|
||||
ftl_files=FTL_FILES)
|
||||
return l10n_utils.render(request, "newsletter/updated.html", context, ftl_files=FTL_FILES)
|
||||
|
||||
|
||||
@never_cache
|
||||
|
@ -572,34 +558,30 @@ def recovery(request):
|
|||
to manage their subscriptions.
|
||||
"""
|
||||
|
||||
if request.method == 'POST':
|
||||
if request.method == "POST":
|
||||
form = EmailForm(request.POST)
|
||||
if form.is_valid():
|
||||
email = form.cleaned_data['email']
|
||||
email = form.cleaned_data["email"]
|
||||
try:
|
||||
# Try it - basket will return an error if the email is unknown
|
||||
basket.send_recovery_message(email)
|
||||
except basket.BasketException as e:
|
||||
# Was it that their email was not known? Or it could be invalid,
|
||||
# but that doesn't really make a difference.
|
||||
if e.code in (basket.errors.BASKET_UNKNOWN_EMAIL,
|
||||
basket.errors.BASKET_INVALID_EMAIL):
|
||||
if e.code in (basket.errors.BASKET_UNKNOWN_EMAIL, basket.errors.BASKET_INVALID_EMAIL):
|
||||
# Tell them, give them a link to go subscribe if they want
|
||||
url = reverse('newsletter.subscribe')
|
||||
form.errors['email'] = \
|
||||
form.error_class([ftl('newsletters-this-email-address-is-not',
|
||||
url=url,
|
||||
ftl_files=FTL_FILES)])
|
||||
url = reverse("newsletter.subscribe")
|
||||
form.errors["email"] = form.error_class([ftl("newsletters-this-email-address-is-not", url=url, ftl_files=FTL_FILES)])
|
||||
else:
|
||||
# Log the details
|
||||
log.exception("Error sending recovery message")
|
||||
# and tell the user that something went wrong
|
||||
form.errors['__all__'] = form.error_class([general_error])
|
||||
form.errors["__all__"] = form.error_class([general_error])
|
||||
else:
|
||||
messages.add_message(request, messages.INFO, recovery_text)
|
||||
# Redir as GET, signalling success
|
||||
return redirect(request.path + "?success")
|
||||
elif 'success' in request.GET:
|
||||
elif "success" in request.GET:
|
||||
# We were redirected after a successful submission.
|
||||
# A message will be displayed; don't display the form again.
|
||||
form = None
|
||||
|
@ -607,57 +589,60 @@ def recovery(request):
|
|||
form = EmailForm()
|
||||
|
||||
# This view is shared between two different templates. For context see bug 1442129.
|
||||
if '/newsletter/opt-out-confirmation/' in request.get_full_path():
|
||||
if "/newsletter/opt-out-confirmation/" in request.get_full_path():
|
||||
template = "newsletter/opt-out-confirmation.html"
|
||||
ftl_files = ['newsletter/opt-out-confirmation']
|
||||
ftl_files = ["newsletter/opt-out-confirmation"]
|
||||
else:
|
||||
template = "newsletter/recovery.html"
|
||||
ftl_files = FTL_FILES
|
||||
|
||||
return l10n_utils.render(request, template, {'form': form}, ftl_files=ftl_files)
|
||||
return l10n_utils.render(request, template, {"form": form}, ftl_files=ftl_files)
|
||||
|
||||
|
||||
def newsletter_subscribe(request):
|
||||
if request.method == 'POST':
|
||||
newsletters = request.POST.get('newsletters', None)
|
||||
form = NewsletterFooterForm(newsletters,
|
||||
l10n_utils.get_locale(request),
|
||||
request.POST)
|
||||
if request.method == "POST":
|
||||
newsletters = request.POST.get("newsletters", None)
|
||||
form = NewsletterFooterForm(newsletters, l10n_utils.get_locale(request), request.POST)
|
||||
errors = []
|
||||
if form.is_valid():
|
||||
data = form.cleaned_data
|
||||
|
||||
kwargs = {'format': data['fmt']}
|
||||
kwargs = {"format": data["fmt"]}
|
||||
# add optional data
|
||||
kwargs.update(dict((k, data[k]) for k in ['country',
|
||||
'lang',
|
||||
'source_url',
|
||||
'first_name',
|
||||
'last_name', ]
|
||||
if data[k]))
|
||||
kwargs.update(
|
||||
dict(
|
||||
(k, data[k])
|
||||
for k in [
|
||||
"country",
|
||||
"lang",
|
||||
"source_url",
|
||||
"first_name",
|
||||
"last_name",
|
||||
]
|
||||
if data[k]
|
||||
)
|
||||
)
|
||||
|
||||
# NOTE this is not a typo; Referrer is misspelled in the HTTP spec
|
||||
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36
|
||||
if not kwargs.get('source_url') and request.META.get('HTTP_REFERER'):
|
||||
kwargs['source_url'] = request.META['HTTP_REFERER']
|
||||
if not kwargs.get("source_url") and request.META.get("HTTP_REFERER"):
|
||||
kwargs["source_url"] = request.META["HTTP_REFERER"]
|
||||
|
||||
try:
|
||||
basket.subscribe(data['email'], data['newsletters'],
|
||||
**kwargs)
|
||||
basket.subscribe(data["email"], data["newsletters"], **kwargs)
|
||||
except basket.BasketException as e:
|
||||
if e.code == basket.errors.BASKET_INVALID_EMAIL:
|
||||
errors.append(str(invalid_email_address))
|
||||
else:
|
||||
log.exception("Error subscribing %s to newsletter %s" %
|
||||
(data['email'], data['newsletters']))
|
||||
log.exception("Error subscribing %s to newsletter %s" % (data["email"], data["newsletters"]))
|
||||
errors.append(str(general_error))
|
||||
|
||||
else:
|
||||
if 'email' in form.errors:
|
||||
errors.append(ftl('newsletter-form-please-enter-a-valid'))
|
||||
if 'privacy' in form.errors:
|
||||
errors.append(ftl('newsletter-form-you-must-agree-to'))
|
||||
for fieldname in ('fmt', 'lang', 'country'):
|
||||
if "email" in form.errors:
|
||||
errors.append(ftl("newsletter-form-please-enter-a-valid"))
|
||||
if "privacy" in form.errors:
|
||||
errors.append(ftl("newsletter-form-you-must-agree-to"))
|
||||
for fieldname in ("fmt", "lang", "country"):
|
||||
if fieldname in form.errors:
|
||||
errors.extend(form.errors[fieldname])
|
||||
|
||||
|
@ -668,18 +653,18 @@ def newsletter_subscribe(request):
|
|||
# return JSON
|
||||
if errors:
|
||||
resp = {
|
||||
'success': False,
|
||||
'errors': errors,
|
||||
"success": False,
|
||||
"errors": errors,
|
||||
}
|
||||
else:
|
||||
resp = {'success': True}
|
||||
resp = {"success": True}
|
||||
|
||||
return JsonResponse(resp)
|
||||
else:
|
||||
ctx = {'newsletter_form': form}
|
||||
ctx = {"newsletter_form": form}
|
||||
if not errors:
|
||||
ctx['success'] = True
|
||||
ctx["success"] = True
|
||||
|
||||
return l10n_utils.render(request, 'newsletter/index.html', ctx, ftl_files=FTL_FILES)
|
||||
return l10n_utils.render(request, "newsletter/index.html", ctx, ftl_files=FTL_FILES)
|
||||
|
||||
return l10n_utils.render(request, 'newsletter/index.html', ftl_files=FTL_FILES)
|
||||
return l10n_utils.render(request, "newsletter/index.html", ftl_files=FTL_FILES)
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче