diff --git a/.gitignore b/.gitignore index 39eda8c09d..15e6f1c6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ tmp/* .vagrant /env/ /static/ +.env diff --git a/manage.py b/manage.py index 8e2180d8d5..1fa53ccb6a 100755 --- a/manage.py +++ b/manage.py @@ -2,6 +2,10 @@ import os import sys +# Read .env file and inject it's values into the environment +import dotenv +dotenv.read_dotenv() + # Edit this if necessary or override the variable in your environment. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') diff --git a/requirements.txt b/requirements.txt index bcc2b8942b..68067cc4e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -165,3 +165,9 @@ tower==0.4.1 # sha256: EeMy8qKdhkQ2S0ynm0rAed8yhibsjCesDozEVGlnGco translate-toolkit==1.12.0 +# sha256: 8uJz7TSsu1YJYtXPEpF5NtjfAil98JvTCJuFRtRYQTg +# sha256: ygF2j97N4TQwHzFwdDIm9g7f9cOTXxJDc3jr2RFQY1M +dj-database-url==0.3.0 +# sha256: 3-W4wr5as0NbPXHlocZrsU2oEE2kTvDdjMw-ipJ2Ftk +# sha256: EgxGIdHk9a2r4KaDRj0b56WmuZLt1HZNMjxifSKSUeA +django-dotenv==1.3.0 diff --git a/settings/__init__.py b/settings/__init__.py index 8938f3d0a9..5ea078ed62 100644 --- a/settings/__init__.py +++ b/settings/__init__.py @@ -1,6 +1,7 @@ from .base import * + +# Import local settings if they exist (usually only in development). try: from .local import * except ImportError, exc: - exc.args = tuple(['%s (did you rename settings/local.py-dist?)' % exc.args[0]]) - raise exc + pass diff --git a/settings/base.py b/settings/base.py index 2b98300a3a..b6f2f871a5 100644 --- a/settings/base.py +++ b/settings/base.py @@ -1,93 +1,362 @@ -# This is your project's main settings file that can be committed to your -# repo. If you need to override a setting locally, use settings_local.py +"""Django settings for Pontoon.""" +import logging +import os +import socket -from funfactory.settings_base import * +from django.utils.functional import lazy -# Name of the top-level module where you put all your apps. -# If you did not install Playdoh with the funfactory installer script -# you may need to edit this value. See the docs about installing from a -# clone. -PROJECT_MODULE = 'pontoon' +import dj_database_url -# Defines the views served for root URLs. -ROOT_URLCONF = '%s.urls' % PROJECT_MODULE +from funfactory.manage import ROOT, path -INSTALLED_APPS = list(INSTALLED_APPS) + [ - # Application base, containing global templates. - '%s.base' % PROJECT_MODULE, - '%s.administration' % PROJECT_MODULE, - '%s.intro' % PROJECT_MODULE, + +# Environment-dependent settings. These are loaded from environment +# variables. + +# Make this unique, and don't share it with anybody. +SECRET_KEY = os.environ['SECRET_KEY'] + +# Is this a dev instance? +DEV = os.environ.get('DJANGO_DEV', 'False') != 'False' + +DEBUG = TEMPLATE_DEBUG = os.environ.get('DJANGO_DEBUG', 'False') != 'False' + +ADMINS = MANAGERS = ( + (os.environ.get('ADMIN_NAME', ''), + os.environ.get('ADMIN_EMAIL', '')), +) + +DATABASES = { + 'default': dj_database_url.config(default='mysql://root@localhost/pontoon') +} + +SESSION_COOKIE_HTTPONLY = os.environ.get('SESSION_COOKIE_HTTPONLY', 'True') != 'False' +SESSION_COOKIE_SECURE = os.environ.get('SESSION_COOKIE_SECURE', 'True') != 'False' + +HMAC_KEYS = { + 'hmac_key': os.environ['HMAC_KEY'], +} + +SITE_URL = os.environ['SITE_URL'] + +# Microsoft Translator API Key +MICROSOFT_TRANSLATOR_API_KEY = os.environ.get('MICROSOFT_TRANSLATOR_API_KEY', '') + +# Google Analytics Key +GOOGLE_ANALYTICS_KEY = os.environ.get('GOOGLE_ANALYTICS_KEY', '') + +# Mozillians API Key +MOZILLIANS_API_KEY = os.environ.get('MOZILLIANS_API_KEY', '') + + +# Environment-independent settings. These shouldn't have to change +# between server environments. +ROOT_URLCONF = 'pontoon.urls' + +INSTALLED_APPS = ( + 'pontoon.base', + 'pontoon.administration', + 'pontoon.intro', + + # Django contrib apps 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.staticfiles', + + # Third-party apps, patches, fixes + 'commonware.response.cookies', + 'compressor', + 'cronjobs', + 'django_browserid', + 'django_nose', + 'djcelery', + 'funfactory', + 'product_details', + 'session_csrf', 'south', + 'tower', +) + +MIDDLEWARE_CLASSES = ( + 'multidb.middleware.PinningRouterMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'session_csrf.CsrfMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'commonware.middleware.FrameOptionsHeader', + 'mobility.middleware.DetectMobileMiddleware', + 'mobility.middleware.XMobileMiddleware', +) + +TEMPLATE_CONTEXT_PROCESSORS = ( + 'django.contrib.auth.context_processors.auth', + 'django.core.context_processors.debug', + 'django.core.context_processors.media', + 'django.core.context_processors.request', + 'session_csrf.context_processor', + 'django.contrib.messages.context_processors.messages', + 'funfactory.context_processors.i18n', + 'funfactory.context_processors.globals', + 'django_browserid.context_processors.browserid', +) + +TEMPLATE_LOADERS = ( + 'jingo.Loader', + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +) + +TEMPLATE_DIRS = ( + path('templates'), +) + +AUTHENTICATION_BACKENDS = [ + 'django_browserid.auth.BrowserIDBackend', + 'django.contrib.auth.backends.ModelBackend', ] +# Paths containing translation files for the site. LOCALE_PATHS = ( - os.path.join(ROOT, PROJECT_MODULE, 'locale'), + os.path.join(ROOT, 'pontoon', 'locale'), ) -# Remove LocaleURLMiddleware since we are not localizing our website -MIDDLEWARE_CLASSES = filter( - lambda x: x != 'funfactory.middleware.LocaleURLMiddleware', - MIDDLEWARE_CLASSES) - -# Because Jinja2 is the default template loader, add any non-Jinja templated -# apps here: +# Template directories that contain Django templates instead of Jinja. JINGO_EXCLUDE_APPS = [ 'admin', 'registration', ] -# BrowserID configuration -AUTHENTICATION_BACKENDS = [ - 'django_browserid.auth.BrowserIDBackend', - 'django.contrib.auth.backends.ModelBackend', -] +# Tells the extract script what files to look for L10n in and what function +# handles the extraction. The Tower library expects this. +DOMAIN_METHODS = { + 'messages': [ + ('pontoon/**.py', + 'tower.management.commands.extract.extract_tower_python'), + ('pontoon/**/templates/**.html', + 'tower.management.commands.extract.extract_tower_template'), + ('templates/**.html', + 'tower.management.commands.extract.extract_tower_template'), + ] +} + +# Required for storing additional information about users +AUTH_PROFILE_MODULE = 'base.UserProfile' + +DATABASE_ROUTERS = ('multidb.PinningMasterSlaveRouter',) + +# Site ID is used by Django's Sites framework. +SITE_ID = 1 + +## Media and templates. -SITE_URL = 'http://127.0.0.1:8000' +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = path('media') + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '/media/' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = path('static') + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'compressor.finders.CompressorFinder', +) + +# Storage of static files +COMPRESS_ROOT = STATIC_ROOT +COMPRESS_CSS_FILTERS = ( + 'compressor.filters.css_default.CssAbsoluteFilter', + 'compressor.filters.cssmin.CSSMinFilter' +) +COMPRESS_PRECOMPILERS = ( + ('text/less', 'lessc {infile} {outfile}'), +) + +# Path to Java. Used for compress_assets. +JAVA_BIN = '/usr/bin/java' + +def JINJA_CONFIG(): + return { + 'extensions': [ + 'tower.template.i18n', + 'jinja2.ext.do', + 'jinja2.ext.with_', + 'jinja2.ext.loopcontrols' + ], + 'finalize': lambda x: x if x is not None else '' + } + +## Auth +# The first hasher in this list will be used for new passwords. +# Any other hasher in the list can be used for existing passwords. +# Playdoh ships with Bcrypt+HMAC by default because it's the most secure. +# To use bcrypt, fill in a secret HMAC key in your local settings. +BASE_PASSWORD_HASHERS = ( + 'django_sha2.hashers.BcryptHMACCombinedPasswordVerifier', + 'django_sha2.hashers.SHA512PasswordHasher', + 'django_sha2.hashers.SHA256PasswordHasher', + 'django.contrib.auth.hashers.SHA1PasswordHasher', + 'django.contrib.auth.hashers.MD5PasswordHasher', + 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', +) + +from django_sha2 import get_password_hashers +PASSWORD_HASHERS = get_password_hashers(BASE_PASSWORD_HASHERS, HMAC_KEYS) + +## Logging +LOG_LEVEL = logging.INFO +HAS_SYSLOG = True +SYSLOG_TAG = "http_app_pontoon" # Change this after you fork. +LOGGING_CONFIG = None +LOGGING = {} + +# CEF Logging +CEF_PRODUCT = 'Pontoon' +CEF_VENDOR = 'Mozilla' +CEF_VERSION = '0' +CEF_DEVICE_VERSION = '0' + +## Tests +TEST_RUNNER = 'test_utils.runner.RadicalTestSuiteRunner' + +## Celery + +# True says to simulate background tasks without actually using celeryd. +# Good for local development in case celeryd is not running. +CELERY_ALWAYS_EAGER = True + +BROKER_CONNECTION_TIMEOUT = 0.1 +CELERY_RESULT_BACKEND = 'amqp' +CELERY_IGNORE_RESULT = True +CELERY_EAGER_PROPAGATES_EXCEPTIONS = True + +# Time in seconds before celery.exceptions.SoftTimeLimitExceeded is raised. +# The task can catch that and recover but should exit ASAP. +CELERYD_TASK_SOFT_TIME_LIMIT = 60 * 2 + +## Arecibo +# when ARECIBO_SERVER_URL is set, it can use celery or the regular wrapper +ARECIBO_USES_CELERY = True + +# django-browserid LOGIN_URL = '/' LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL_FAILURE = '/' -TEMPLATE_CONTEXT_PROCESSORS = list(TEMPLATE_CONTEXT_PROCESSORS) + [ - 'django_browserid.context_processors.browserid', -] - -# Should robots.txt deny everything or disallow a calculated list of URLs we -# don't want to be crawled? Default is false, disallow everything. -# Also see http://www.google.com/support/webmasters/bin/answer.py?answer=93710 +# Should robots.txt deny everything or disallow a calculated list of +# URLs we don't want to be crawled? Default is false, disallow +# everything. ENGAGE_ROBOTS = False # Always generate a CSRF token for anonymous users. ANON_ALWAYS = True -# Tells the extract script what files to look for L10n in and what function -# handles the extraction. The Tower library expects this. -DOMAIN_METHODS['messages'] = [ - ('%s/**.py' % PROJECT_MODULE, - 'tower.management.commands.extract.extract_tower_python'), - ('%s/**/templates/**.html' % PROJECT_MODULE, - 'tower.management.commands.extract.extract_tower_template'), - ('templates/**.html', - 'tower.management.commands.extract.extract_tower_template'), -] +# For absolute urls +try: + DOMAIN = socket.gethostname() +except socket.error: + DOMAIN = 'localhost' +PROTOCOL = "http://" +PORT = 80 -# # Use this if you have localizable HTML files: -# DOMAIN_METHODS['lhtml'] = [ -# ('**/templates/**.lhtml', -# 'tower.management.commands.extract.extract_tower_template'), -# ] +## django-mobility +MOBILE_COOKIE = 'mobile' -# # Use this if you have localizable JS files: -# DOMAIN_METHODS['javascript'] = [ -# # Make sure that this won't pull in strings from external libraries you -# # may use. -# ('media/js/**.js', 'javascript'), -# ] +# Names for slave databases from the DATABASES setting. +SLAVE_DATABASES = [] -LOGGING = dict(loggers=dict(pontoon={'level': logging.DEBUG})) +## Internationalization. -# Required for storing additional information about users -AUTH_PROFILE_MODULE = 'base.UserProfile' +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Los_Angeles' + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Gettext text domain +TEXT_DOMAIN = 'messages' +STANDALONE_DOMAINS = [TEXT_DOMAIN, 'javascript'] +TOWER_KEYWORDS = {'_lazy': None} +TOWER_ADD_HEADERS = True + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-US' + +## Accepted locales + +# Tells the product_details module where to find our local JSON files. +# This ultimately controls how LANGUAGES are constructed. +PROD_DETAILS_DIR = path('lib/product_details_json') + +# On dev instances, the list of accepted locales defaults to the contents of +# the `locale` directory within a project module or, for older Playdoh apps, +# the root locale directory. A localizer can add their locale in the l10n +# repository (copy of which is checked out into `locale`) in order to start +# testing the localization on the dev server. +import glob +import itertools +try: + DEV_LANGUAGES = [ + os.path.basename(loc).replace('_', '-') + for loc in itertools.chain(glob.iglob(ROOT + '/locale/*'), # old style + glob.iglob(ROOT + '/*/locale/*')) + if (os.path.isdir(loc) and os.path.basename(loc) != 'templates') + ] +except OSError: + DEV_LANGUAGES = ('en-US',) + +# On stage/prod, the list of accepted locales is manually maintained. Only +# locales whose localizers have signed off on their work should be listed here. +PROD_LANGUAGES = ( + 'en-US', +) + +def lazy_lang_url_map(): + from django.conf import settings + langs = settings.DEV_LANGUAGES if settings.DEV else settings.PROD_LANGUAGES + return dict([(i.lower(), i) for i in langs]) + +LANGUAGE_URL_MAP = lazy(lazy_lang_url_map, dict)() + +# Override Django's built-in with our native names +def lazy_langs(): + from django.conf import settings + from product_details import product_details + langs = DEV_LANGUAGES if settings.DEV else settings.PROD_LANGUAGES + return dict([(lang.lower(), product_details.languages[lang]['native']) + for lang in langs if lang in product_details.languages]) + +LANGUAGES = lazy(lazy_langs, dict)() + +# Paths that don't require a locale code in the URL. +SUPPORTED_NONLOCALES = ['media', 'static', 'admin'] # Microsoft Translator Locales MICROSOFT_TRANSLATOR_LOCALES = [ diff --git a/settings/local.py-dist b/settings/local.py-dist deleted file mode 100644 index 1b230c3512..0000000000 --- a/settings/local.py-dist +++ /dev/null @@ -1,99 +0,0 @@ -# This is an example settings/local.py file. -# These settings overrides what's in settings/base.py - -from . import base - -# To extend any settings from settings/base.py here's an example: -#INSTALLED_APPS = base.INSTALLED_APPS + ['debug_toolbar'] - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'pontoon', - 'USER': 'root', - 'PASSWORD': '', - 'HOST': '', - 'PORT': '', - 'OPTIONS': { - 'init_command': 'SET storage_engine=InnoDB', - 'charset' : 'utf8', - 'use_unicode' : True, - }, - 'TEST_CHARSET': 'utf8', - 'TEST_COLLATION': 'utf8_general_ci', - }, - # 'slave': { - # ... - # }, -} - -# Uncomment this and set to all slave DBs in use on the site. -# SLAVE_DATABASES = ['slave'] - -# Recipients of traceback emails and other notifications. -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) -MANAGERS = ADMINS - -# Debugging displays nice error messages, but leaks memory. Set this to False -# on all server instances and True only for development. -DEBUG = TEMPLATE_DEBUG = True - -# Is this a development instance? Set this to True on development/master -# instances and False on stage/prod. -DEV = True - -# By default, BrowserID expects your app to use http://127.0.0.1:8000 -# Uncomment the following line if you prefer to access your app via localhost -SITE_URL = 'http://localhost:8000' - -# Playdoh ships with Bcrypt+HMAC by default because it's the most secure. -# To use bcrypt, fill in a secret HMAC key. It cannot be blank. -HMAC_KEYS = { - '2014-01-20': 'some secret', -} - -from django_sha2 import get_password_hashers -PASSWORD_HASHERS = get_password_hashers(base.BASE_PASSWORD_HASHERS, HMAC_KEYS) - -# Make this unique, and don't share it with anybody. It cannot be blank. -SECRET_KEY = '' - -# Should robots.txt allow web crawlers? Set this to True for production -ENGAGE_ROBOTS = True - -# Uncomment these to activate and customize Celery: -# CELERY_ALWAYS_EAGER = False # required to activate celeryd -# BROKER_HOST = 'localhost' -# BROKER_PORT = 5672 -# BROKER_USER = 'pontoon' -# BROKER_PASSWORD = 'pontoon' -# BROKER_VHOST = 'pontoon' -# CELERY_RESULT_BACKEND = 'amqp' - -## Log settings - -# SYSLOG_TAG = "http_app_playdoh" # Make this unique to your project. -# LOGGING = dict(loggers=dict(pontoon={'level': logging.DEBUG})) - -# Common Event Format logging parameters -#CEF_PRODUCT = 'Playdoh' -#CEF_VENDOR = 'Mozilla' - -# Uncomment this line if you are running a local development install without -# HTTPS to disable HTTPS-only cookies. -SESSION_COOKIE_SECURE = False - -# In production: if behind a load balancer, make request.is_secure() -# and consequently django-browserid work again (bug 961545) -# SECURE_PROXY_SSL_HEADER = ('HTTP_X_SSL', 'On') - -# Microsoft Translator API Key -MICROSOFT_TRANSLATOR_API_KEY = '' - -# Google Analytics Key -GOOGLE_ANALYTICS_KEY = '' - -# Mozillians API Key -MOZILLIANS_API_KEY = ''