Files
breccia-mapper/breccia_mapper/settings.py

614 lines
18 KiB
Python

"""
Django settings for breccia_mapper project.
Generated by 'django-admin startproject' using Django 2.2.9.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
Before production deployment, see
https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
Many configuration settings are input from `.env`.
The most likely required settings are: SECRET_KEY, DEBUG, ALLOWED_HOSTS, PROJECT_*_NAME, EMAIL_*
- SECRET_KEY (REQUIRED)
Used to generate CSRF tokens - must never be made public
- DEBUG
default: False
Should the server run in debug mode? Provides information to users which is unsafe in production
- SITE_URL
default: localhost
The URL the site will be deployed on. Do not include http://, https://, or a trailing slash.
- SITE_PROTOCOL
default: http
The protocol the site uses. Valid options are http or https.
- PROJECT_LONG_NAME
default: Project Network Mapper
The project's full name.
- PROJECT_SHORT_NAME
default: Network Mapper
The project's short/abbreviated name. This will also be used as the app's name when installed as PWA.
- PROJECT_DESCRIPTION
default: Application to map network relationships in the organisation.
The project's description. Used when installed as a PWA.
- THEME_COLOR
default: 212121
The project's theme color, in hex format (excluding the leading #).
- BACKGROUND_COLOR
default: ffffff
The project's background color, in hex format (excluding the leading #).
- ALLOWED_HOSTS
default: * if DEBUG else localhost
Accepted values for server header in request - protects against CSRF and CSS attacks
- DBBACKUP_STORAGE_LOCATION
default: .dbbackup
Directory where database backups should be stored
- LANGUAGE_CODE
default: en-gb
Default language - used for translation - has not been enabled
- TIME_ZONE
default: UTC
Default timezone
- LOG_LEVEL
default: INFO
Level of messages written to log file
- LOG_FILENAME
default: debug.log
Path to logfile
- LOG_DAYS
default: 14
Number of days of logs to keep - logfile is rotated out at the end of each day
- EMAIL_HOST
default: None
Hostname of SMTP server
- DEFAULT_FROM_EMAIL
default: None
Email address from which messages are sent
- EMAIL_FILE_PATH (debug only)
default: mail.log
Directory where emails will be stored if not using an SMTP server
- EMAIL_HOST_USER
default: None
Username to authenticate with SMTP server
- EMAIL_HOST_PASSWORD
default: None
Password to authenticate with SMTP server
- EMAIL_PORT
default: 25
Port to access on SMTP server
- EMAIL_USE_TLS
default: True if EMAIL_PORT == 587 else False
Use TLS to communicate with SMTP server? Usually on port 587
- EMAIL_USE_SSL
default: True if EMAIL_PORT == 465 else False
Use SSL to communicate with SMTP server? Usually on port 465
- GOOGLE_MAPS_API_KEY
default: None
Google Maps API key to display maps of people's locations
"""
import logging
import logging.config
import pathlib
from django.urls import reverse_lazy
from decouple import config, Csv
import dj_database_url
# Settings exported to templates
# https://github.com/jakubroztocil/django-settings-export
SETTINGS_EXPORT = [
'DEBUG',
'SITE_URL',
'SITE_PROTOCOL',
'GOOGLE_MAPS_API_KEY',
'PROJECT_LONG_NAME',
'PROJECT_SHORT_NAME',
'PROJECT_DESCRIPTION',
'THEME_COLOR',
'BACKGROUND_COLOR',
]
# Build paths inside the project like this: BASE_DIR.joinpath(...)
BASE_DIR = pathlib.Path(__file__).parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config(
'ALLOWED_HOSTS',
default='*' if DEBUG else '127.0.0.1,localhost,localhost.localdomain',
cast=Csv())
# Site URL
SITE_URL = config('SITE_URL', default='localhost')
SITE_PROTOCOL = config('SITE_PROTOCOL', default='http')
# CORS settings
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = config(
'TRUSTED_ORIGINS',
default='*' if DEBUG else 'http://127.0.0.1,http://localhost,http://localhost.localdomain',
cast=Csv())
CORS_REPLACE_HTTPS_REFERER = True
CSRF_COOKIE_DOMAIN = config(
'SITE_URL',
default='localhost')
CORS_ORIGIN_WHITELIST = config(
'TRUSTED_ORIGINS',
default='*' if DEBUG else 'http://127.0.0.1,http://localhost,http://localhost.localdomain',
cast=Csv())
# Application definition
DJANGO_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
'bootstrap4',
'constance',
'constance.backends.database',
'dbbackup',
'django_countries',
'django_select2',
'rest_framework',
'post_office',
'bootstrap_datepicker_plus',
'hijack',
'pwa',
]
FIRST_PARTY_APPS = [
'people',
'activities',
'export',
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + FIRST_PARTY_APPS
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'hijack.middleware.HijackUserMiddleware',
]
ROOT_URLCONF = 'breccia_mapper.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR.joinpath('breccia_mapper', 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django_settings_export.settings_export',
'constance.context_processors.config',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'breccia_mapper.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': dj_database_url.parse('sqlite:///' + str(BASE_DIR.joinpath('db.sqlite3')))
}
# Django DBBackup
# https://django-dbbackup.readthedocs.io/en/stable/index.html
DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage'
DBBACKUP_STORAGE_OPTIONS = {
'location':
config('DBBACKUP_STORAGE_LOCATION',
default=BASE_DIR.joinpath('.dbbackup')),
}
# Django REST Framework
# https://www.django-rest-framework.org/
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework_csv.renderers.CSVRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME':
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Custom user model
# https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project
AUTH_USER_MODEL = 'people.User'
# Login flow
LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('people:person.profile')
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = config('LANGUAGE_CODE', default='en-gb')
TIME_ZONE = config('TIME_ZONE', default='UTC')
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR.joinpath('static')
STATICFILES_DIRS = [BASE_DIR.joinpath('breccia_mapper', 'static')]
# Media uploads
MEDIA_ROOT = BASE_DIR.joinpath('media')
MEDIA_URL = "/media/"
# Logging - NB the logger name is empty to capture all output
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': config('LOG_LEVEL', default='INFO'),
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': config('LOG_FILENAME', default='debug.log'),
'when': 'midnight',
'backupCount': config('LOG_DAYS', default=14, cast=int),
'formatter': 'timestamped',
},
'console': {
'level': config('LOG_LEVEL', default='INFO'),
'class': 'logging.StreamHandler',
'formatter': 'timestamped',
},
},
'loggers': {
'': {
'handlers': ['console', 'file'],
'level': config('LOG_LEVEL', default='INFO'),
'propagate': True,
},
},
'formatters': {
'timestamped': {
'format': '[{asctime} {levelname} {module} {funcName}] {message}',
'style': '{',
}
}
}
# Initialise logger now so we can use it in this file
LOGGING_CONFIG = None
logging.config.dictConfig(LOGGING)
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
# Admin panel variables
CONSTANCE_ADDITIONAL_FIELDS = {
'image_field': ['django.forms.ImageField', {}]
}
CONSTANCE_CONFIG = {
'NOTICE_TEXT': (
'',
'Text to be displayed in a notice banner at the top of every page.'),
'NOTICE_CLASS': (
'alert-warning',
'CSS class to use for background of notice banner.'),
'CONSENT_TEXT': (
'This is template consent text and should have been replaced. Please contact an admin.',
'Text to be displayed to ask for consent for data collection.'),
'PERSON_LIST_HELP': (
'',
'Help text to display at the top of the people list.'),
'ORGANISATION_LIST_HELP': (
'',
'Help text to display at the top of the organisaton list.'),
'RELATIONSHIP_FORM_HELP': (
'',
'Help text to display at the top of relationship forms.'),
'PARENT_PROJECT_NAME': (
'',
'Parent project name'),
'PROJECT_LEAD': (
'Project Lead',
'Project lead'),
'PROJECT_TAGLINE': (
'Here is your project\'s tagline.',
'Project tagline'),
'HOMEPAGE_HEADER_IMAGE': (
'800x500.png',
'Homepage header image',
'image_field'),
'HOMEPAGE_HEADER_IMAGE_SHRINK': (
False,
'Shrink the homepage header image to display the whole image at all times'),
'HOMEPAGE_CARD_1_TITLE': (
'Step 1',
'Homepage card #1 title'),
'HOMEPAGE_CARD_1_DESCRIPTION': (
'Tell us about your position within the project',
'Homepage card #1 description'),
'HOMEPAGE_CARD_1_ICON': (
'building-user',
'Homepage card #1 icon'),
'HOMEPAGE_CARD_2_TITLE': (
'Step 2',
'Homepage card #2 title'),
'HOMEPAGE_CARD_2_DESCRIPTION': (
'Describe your relationships with other stakeholders',
'Homepage card #2 description'),
'HOMEPAGE_CARD_2_ICON': (
'handshake-simple',
'Homepage card #2 icon'),
'HOMEPAGE_CARD_3_TITLE': (
'Step 3',
'Homepage card #3 title'),
'HOMEPAGE_CARD_3_DESCRIPTION': (
'Use the network view to build new relationships',
'Homepage card #3 description'),
'HOMEPAGE_CARD_3_ICON': (
'diagram-project',
'Homepage card #3 icon'),
'HOMEPAGE_ABOUT_TITLE': (
'About Us',
'Homepage about section title'),
'HOMEPAGE_ABOUT_CONTENT': (
"""Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In massa tempor nec feugiat nisl. Eget dolor morbi non arcu risus quis varius quam quisque. Nisl pretium fusce id velit ut tortor pretium viverra suspendisse. Vitae auctor eu augue ut lectus arcu. Tellus molestie nunc non blandit massa enim nec. At consectetur lorem donec massa sapien. Placerat orci nulla pellentesque dignissim enim sit. Sit amet mauris commodo quis imperdiet. Tellus at urna condimentum mattis pellentesque.<br/>In vitae turpis massa sed. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Ut consequat semper viverra nam libero justo laoreet. Velit ut tortor pretium viverra suspendisse potenti nullam ac tortor. Nunc id cursus metus aliquam eleifend mi in nulla posuere. Aliquam eleifend mi in nulla posuere sollicitudin aliquam. Est ante in nibh mauris cursus mattis molestie a iaculis. Nunc id cursus metus aliquam. Auctor urna nunc id cursus metus aliquam. Porttitor lacus luctus accumsan tortor posuere ac ut consequat semper. Volutpat consequat mauris nunc congue nisi. Leo vel fringilla est ullamcorper eget. Vitae purus faucibus ornare suspendisse sed nisi lacus sed. Massa id neque aliquam vestibulum morbi blandit. Iaculis nunc sed augue lacus viverra vitae congue. Sodales neque sodales ut etiam.""",
'Homepage about section content'),
'HOMEPAGE_ABOUT_IMAGE': (
'400x400.png',
'Homepage about section image',
'image_field'),
} # yapf: disable
CONSTANCE_CONFIG_FIELDSETS = {
'Project options': (
'PARENT_PROJECT_NAME',
'PROJECT_LEAD',
'PROJECT_TAGLINE',
),
'Homepage configuration': (
'HOMEPAGE_HEADER_IMAGE_SHRINK',
'HOMEPAGE_HEADER_IMAGE',
'HOMEPAGE_CARD_1_TITLE',
'HOMEPAGE_CARD_1_DESCRIPTION',
'HOMEPAGE_CARD_1_ICON',
'HOMEPAGE_CARD_2_TITLE',
'HOMEPAGE_CARD_2_DESCRIPTION',
'HOMEPAGE_CARD_2_ICON',
'HOMEPAGE_CARD_3_TITLE',
'HOMEPAGE_CARD_3_DESCRIPTION',
'HOMEPAGE_CARD_3_ICON',
'HOMEPAGE_ABOUT_TITLE',
'HOMEPAGE_ABOUT_CONTENT',
'HOMEPAGE_ABOUT_IMAGE',
),
'Notice banner': (
'NOTICE_TEXT',
'NOTICE_CLASS',
),
'Data Collection': (
'CONSENT_TEXT',
),
'Help text': (
'PERSON_LIST_HELP',
'ORGANISATION_LIST_HELP',
'RELATIONSHIP_FORM_HELP',
),
} # yapf: disable
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
PROJECT_LONG_NAME = config('PROJECT_LONG_NAME', 'Project Network Mapper')
PROJECT_SHORT_NAME = config('PROJECT_SHORT_NAME', 'Network Mapper')
PROJECT_DESCRIPTION = config('PROJECT_DESCRIPTION', 'Application to map network relationships in the organisation.')
THEME_COLOR = '#' + config('THEME_COLOR', '212121')
BACKGROUND_COLOR = '#' + config('BACKGROUND_COLOR', 'ffffff')
# Django Hijack settings
# See https://django-hijack.readthedocs.io/en/stable/
HIJACK_USE_BOOTSTRAP = True
# Bootstrap settings
# See https://django-bootstrap4.readthedocs.io/en/latest/settings.html
BOOTSTRAP4 = {
'include_jquery': 'full',
}
# Email backend settings
# See https://docs.djangoproject.com/en/3.0/topics/email
EMAIL_HOST = config('EMAIL_HOST', default=None)
DEFAULT_FROM_EMAIL = config(
'DEFAULT_FROM_EMAIL',
default=f'{PROJECT_SHORT_NAME.replace(" ","")}@localhost.localdomain')
SERVER_EMAIL = DEFAULT_FROM_EMAIL
if EMAIL_HOST is None:
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = config('EMAIL_FILE_PATH',
default=str(BASE_DIR.joinpath('mail.log')))
else:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST_USER = config('EMAIL_HOST_USER', default=None)
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default=None)
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)
EMAIL_USE_TLS = config('EMAIL_USE_TLS',
default=(EMAIL_PORT == 587),
cast=bool)
EMAIL_USE_SSL = config('EMAIL_USE_SSL',
default=(EMAIL_PORT == 465),
cast=bool)
# Bootstrap Datepicker Plus Settings
BOOTSTRAP_DATEPICKER_PLUS = {
"variant_options": {
"date": {
"format": "YYYY-MM-DD",
},
}
}
# PWA settings
PWA_SERVICE_WORKER_PATH = BASE_DIR.joinpath('static/js', 'serviceworker.js')
PWA_APP_NAME = PROJECT_SHORT_NAME
PWA_APP_DESCRIPTION = PROJECT_DESCRIPTION
PWA_APP_THEME_COLOR = THEME_COLOR
PWA_APP_BACKGROUND_COLOR = BACKGROUND_COLOR
PWA_APP_DISPLAY = 'standalone'
PWA_APP_SCOPE = '/'
PWA_APP_ORIENTATION = 'any'
PWA_APP_START_URL = '/'
PWA_APP_STATUS_BAR_COLOR = 'default'
PWA_APP_ICONS = [
{
'src': '/media/icon-192x192.png',
'sizes': '192x192'
}
]
PWA_APP_ICONS_APPLE = [
{
'src': '/media/icon-192x192.png',
'sizes': '192x192'
}
]
PWA_APP_SPLASH_SCREEN = [
{
'src': '/media/icon-192x192.png',
'media': '(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)'
}
]
PWA_APP_DIR = 'ltr'
PWA_APP_LANG = 'en-GB'
PWA_APP_DEBUG_MODE = DEBUG
# Database default automatic primary key
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Upstream API keys
GOOGLE_MAPS_API_KEY = config('GOOGLE_MAPS_API_KEY', default=None)
# Import customisation app settings if present
try:
from custom.settings import (
CUSTOMISATION_NAME,
TEMPLATE_NAME_INDEX,
TEMPLATE_WELCOME_EMAIL_NAME,
CONSTANCE_CONFIG as constance_config_custom,
CONSTANCE_CONFIG_FIELDSETS as constance_config_fieldsets_custom
) # yapf: disable
CONSTANCE_CONFIG.update(constance_config_custom)
CONSTANCE_CONFIG_FIELDSETS.update(constance_config_fieldsets_custom)
INSTALLED_APPS.append('custom')
logger.info("Loaded customisation app: %s", CUSTOMISATION_NAME)
except ImportError as exc:
logger.info("No customisation app loaded: %s", exc)
# Set default values if no customisations loaded
CUSTOMISATION_NAME = None
TEMPLATE_NAME_INDEX = 'index.html'
TEMPLATE_WELCOME_EMAIL_NAME = 'welcome-email'