· 5 years ago · Mar 28, 2020, 06:16 AM
1# -*- coding: utf-8 -*-
2#########################################################################
3#
4# Copyright (C) 2016 OSGeo
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19#########################################################################
20
21# Django settings for the GeoNode project.
22import os
23import re
24import ast
25import sys
26from datetime import timedelta
27from distutils.util import strtobool # noqa
28from urllib.parse import urlparse, urlunparse, urljoin
29
30import django
31import dj_database_url
32#
33# General Django development settings
34#
35from django.conf.global_settings import DATETIME_INPUT_FORMATS
36from geonode import get_version
37from kombu import Queue, Exchange
38
39SILENCED_SYSTEM_CHECKS = [
40 '1_8.W001',
41 'fields.W340',
42 'auth.W004',
43 'urls.W002'
44]
45
46# GeoNode Version
47VERSION = get_version()
48
49DEFAULT_CHARSET = "utf-8"
50
51# Defines the directory that contains the settings file as the PROJECT_ROOT
52# It is used for relative settings elsewhere.
53PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
54
55# Setting debug to true makes Django serve static media and
56# present pretty error pages.
57DEBUG=True
58
59# Set to True to load non-minified versions of (static) client dependencies
60# Requires to set-up Node and tools that are required for static development
61# otherwise it will raise errors for the missing non-minified dependencies
62DEBUG_STATIC = ast.literal_eval(os.getenv('DEBUG_STATIC', 'False'))
63
64FORCE_SCRIPT_NAME = os.getenv('FORCE_SCRIPT_NAME', '')
65
66# Define email service on GeoNode
67EMAIL_ENABLE = ast.literal_eval(os.getenv('EMAIL_ENABLE', 'False'))
68
69if EMAIL_ENABLE:
70 EMAIL_BACKEND = os.getenv('DJANGO_EMAIL_BACKEND',
71 default='django.core.mail.backends.smtp.EmailBackend')
72 EMAIL_HOST = os.getenv('DJANGO_EMAIL_HOST', 'localhost')
73 EMAIL_PORT = os.getenv('DJANGO_EMAIL_PORT', 25)
74 EMAIL_HOST_USER = os.getenv('DJANGO_EMAIL_HOST_USER', '')
75 EMAIL_HOST_PASSWORD = os.getenv('DJANGO_EMAIL_HOST_PASSWORD', '')
76 EMAIL_USE_TLS = ast.literal_eval(os.getenv('DJANGO_EMAIL_USE_TLS', 'False'))
77 DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'GeoNode <no-reply@geonode.org>')
78else:
79 EMAIL_BACKEND = os.getenv('DJANGO_EMAIL_BACKEND',
80 default='django.core.mail.backends.console.EmailBackend')
81
82# Make this unique, and don't share it with anybody.
83_DEFAULT_SECRET_KEY = 'myv-y4#7j-d*p-__@j#*3z@!y24fz8%^z2v6atuy4bo9vqr1_a'
84SECRET_KEY = os.getenv('SECRET_KEY', _DEFAULT_SECRET_KEY)
85
86DATABASE_URL = os.getenv(
87 'DATABASE_URL',
88 'sqlite:///{path}'.format(
89 path=os.path.join(PROJECT_ROOT, 'development.db')
90 )
91)
92
93# DATABASE_URL = 'postgresql://test_geonode:test_geonode@localhost:5432/geonode'
94
95# Defines settings for development
96
97# since GeoDjango is in use, you should use gis-enabled engine, for example:
98# 'ENGINE': 'django.contrib.gis.db.backends.postgis'
99# see https://docs.djangoproject.com/en/1.8/ref/contrib/gis/db-api/#module-django.contrib.gis.db.backends for
100# detailed list of supported backends and notes.
101_db_conf = dj_database_url.parse(DATABASE_URL, conn_max_age=5)
102if 'spatialite' in DATABASE_URL:
103 SPATIALITE_LIBRARY_PATH = 'mod_spatialite.so'
104
105if 'CONN_TOUT' in _db_conf:
106 _db_conf['CONN_TOUT'] = 5
107if 'postgresql' in DATABASE_URL or 'postgis' in DATABASE_URL:
108 if 'OPTIONS' not in _db_conf:
109 _db_conf['OPTIONS'] = {}
110 _db_conf['OPTIONS'].update({
111 'connect_timeout': 5,
112 })
113
114DATABASES = {
115 'default': _db_conf
116}
117
118if os.getenv('DEFAULT_BACKEND_DATASTORE'):
119 GEODATABASE_URL = os.getenv('GEODATABASE_URL',
120 'postgis://\
121geonode_data:geonode_data@localhost:5432/geonode_data')
122 DATABASES[os.getenv('DEFAULT_BACKEND_DATASTORE')] = dj_database_url.parse(
123 GEODATABASE_URL, conn_max_age=5
124 )
125 _geo_db = DATABASES[os.getenv('DEFAULT_BACKEND_DATASTORE')]
126 if 'CONN_TOUT' in DATABASES['default']:
127 _geo_db['CONN_TOUT'] = 5
128 if 'postgresql' in GEODATABASE_URL or 'postgis' in GEODATABASE_URL:
129 _geo_db['OPTIONS'] = DATABASES['default']['OPTIONS'] if 'OPTIONS' in DATABASES['default'] else {}
130 _geo_db['OPTIONS'].update({
131 'connect_timeout': 5,
132 })
133
134 DATABASES[os.getenv('DEFAULT_BACKEND_DATASTORE')] = _geo_db
135
136# If set to 'True' it will refresh/regenrate all resource links everytime a 'migrate' will be performed
137UPDATE_RESOURCE_LINKS_AT_MIGRATE = ast.literal_eval(os.getenv('UPDATE_RESOURCE_LINKS_AT_MIGRATE', 'False'))
138
139MANAGERS = ADMINS = os.getenv('ADMINS', [])
140
141# Local time zone for this installation. Choices can be found here:
142# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
143# although not all choices may be available on all operating systems.
144# If running in a Windows environment this must be set to the same as your
145# system time zone.
146TIME_ZONE = os.getenv('TIME_ZONE', "UTC")
147
148SITE_ID = int(os.getenv('SITE_ID', '1'))
149
150USE_TZ = True
151USE_I18N = ast.literal_eval(os.getenv('USE_I18N', 'True'))
152USE_L10N = ast.literal_eval(os.getenv('USE_I18N', 'True'))
153
154# Language code for this installation. All choices can be found here:
155# http://www.i18nguy.com/unicode/language-identifiers.html
156LANGUAGE_CODE = os.getenv('LANGUAGE_CODE', "en")
157
158_DEFAULT_LANGUAGES = (
159 ('af', 'Afrikaans'),
160 ('sq', 'Albanian'),
161 ('am', 'Amharic'),
162 ('ar', 'Arabic'),
163 ('id', 'Bahasa Indonesia'),
164 ('bn', 'Bengali'),
165 ('de', 'Deutsch'),
166 ('en', 'English'),
167 ('es', 'Español'),
168 ('fr', 'Français'),
169 ('it', 'Italiano'),
170 ('km', 'Khmer'),
171 ('nl', 'Nederlands'),
172 ('ne', 'Nepali'),
173 ('fa', 'Persian'),
174 ('pl', 'Polish'),
175 ('pt', 'Portuguese'),
176 ('pt-br', 'Portuguese (Brazil)'),
177 ('ru', 'Russian'),
178 ('si', 'Sinhala'),
179 ('sw', 'Swahili'),
180 ('sv', 'Swedish'),
181 ('tl', 'Tagalog'),
182 ('ta', 'Tamil'),
183 ('uk', 'Ukranian'),
184 ('vi', 'Vietnamese'),
185 ('el', 'Ελληνικά'),
186 ('th', 'ไทย'),
187 ('zh-cn', '中文'),
188 ('ja', '日本語'),
189 ('ko', '한국어'),
190)
191
192LANGUAGES = os.getenv('LANGUAGES', _DEFAULT_LANGUAGES)
193
194EXTRA_LANG_INFO = {
195 'am': {
196 'bidi': False,
197 'code': 'am',
198 'name': 'Amharic',
199 'name_local': 'Amharic',
200 },
201 'tl': {
202 'bidi': False,
203 'code': 'tl',
204 'name': 'Tagalog',
205 'name_local': 'tagalog',
206 },
207 'ta': {
208 'bidi': False,
209 'code': 'ta',
210 'name': 'Tamil',
211 'name_local': 'tamil',
212 },
213 'si': {
214 'bidi': False,
215 'code': 'si',
216 'name': 'Sinhala',
217 'name_local': 'sinhala',
218 },
219}
220
221
222AUTH_USER_MODEL = os.getenv('AUTH_USER_MODEL', 'people.Profile')
223
224PASSWORD_HASHERS = [
225 'django.contrib.auth.hashers.SHA1PasswordHasher',
226 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
227 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
228 # 'django.contrib.auth.hashers.Argon2PasswordHasher',
229 # 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
230 # 'django.contrib.auth.hashers.BCryptPasswordHasher',
231]
232
233MODELTRANSLATION_LANGUAGES = ['en', ]
234MODELTRANSLATION_DEFAULT_LANGUAGE = 'en'
235MODELTRANSLATION_FALLBACK_LANGUAGES = ('en',)
236
237# Location of translation files
238_DEFAULT_LOCALE_PATHS = (
239 os.path.join(PROJECT_ROOT, "locale"),
240)
241
242LOCALE_PATHS = os.getenv('LOCALE_PATHS', _DEFAULT_LOCALE_PATHS)
243
244# Location of url mappings
245ROOT_URLCONF = os.getenv('ROOT_URLCONF', 'geonode.urls')
246
247# ########################################################################### #
248# MEDIA / STATICS STORAGES SETTINGS
249# ########################################################################### #
250
251STATICFILES_LOCATION = 'static'
252MEDIAFILES_LOCATION = 'uploaded'
253
254# Absolute path to the directory that holds media.
255# Example: "/home/media/media.lawrence.com/"
256MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(PROJECT_ROOT, MEDIAFILES_LOCATION))
257
258# URL that handles the media served from MEDIA_ROOT. Make sure to use a
259# trailing slash if there is a path component (optional in other cases).
260# Examples: "http://media.lawrence.com", "http://example.com/media/"
261MEDIA_URL = os.getenv('MEDIA_URL', '%s/%s/' % (FORCE_SCRIPT_NAME, MEDIAFILES_LOCATION))
262LOCAL_MEDIA_URL = os.getenv('LOCAL_MEDIA_URL', '%s/%s/' % (FORCE_SCRIPT_NAME, MEDIAFILES_LOCATION))
263
264# Absolute path to the directory that holds static files like app media.
265# Example: "/home/media/media.lawrence.com/apps/"
266STATIC_ROOT = os.getenv('STATIC_ROOT',
267 os.path.join(PROJECT_ROOT, 'static_root')
268 )
269
270# URL that handles the static files like app media.
271# Example: "http://media.lawrence.com"
272STATIC_URL = os.getenv('STATIC_URL', '%s/%s/' % (FORCE_SCRIPT_NAME, STATICFILES_LOCATION))
273
274# Additional directories which hold static files
275_DEFAULT_STATICFILES_DIRS = [
276 os.path.join(PROJECT_ROOT, STATICFILES_LOCATION),
277]
278
279STATICFILES_DIRS = os.getenv('STATICFILES_DIRS', _DEFAULT_STATICFILES_DIRS)
280
281# List of finder classes that know how to find static files in
282# various locations.
283STATICFILES_FINDERS = (
284 'django.contrib.staticfiles.finders.FileSystemFinder',
285 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
286 # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
287)
288
289# AWS S3 Settings
290S3_STATIC_ENABLED = ast.literal_eval(os.environ.get('S3_STATIC_ENABLED', 'False'))
291S3_MEDIA_ENABLED = ast.literal_eval(os.environ.get('S3_MEDIA_ENABLED', 'False'))
292
293# Required to run Sync Media to S3
294AWS_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME', '')
295
296AWS_STORAGE_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME', '')
297AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID', '')
298AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY', '')
299AWS_S3_BUCKET_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
300
301AWS_QUERYSTRING_AUTH = False
302if not DEBUG and S3_STATIC_ENABLED:
303 STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
304 STATIC_URL = "https://%s/%s/" % (AWS_S3_BUCKET_DOMAIN,
305 STATICFILES_LOCATION)
306
307if not DEBUG and S3_MEDIA_ENABLED:
308 MEDIAFILES_LOCATION = 'media'
309 DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
310 MEDIA_URL = "https://%s/%s/" % (AWS_S3_BUCKET_DOMAIN, MEDIAFILES_LOCATION)
311
312# Cache Bustin Settings
313CACHE_BUSTING_STATIC_ENABLED = ast.literal_eval(os.environ.get('CACHE_BUSTING_STATIC_ENABLED', 'False'))
314CACHE_BUSTING_MEDIA_ENABLED = ast.literal_eval(os.environ.get('CACHE_BUSTING_MEDIA_ENABLED', 'False'))
315
316if not DEBUG and not S3_STATIC_ENABLED and not S3_MEDIA_ENABLED:
317 if CACHE_BUSTING_STATIC_ENABLED or CACHE_BUSTING_MEDIA_ENABLED:
318 from django.contrib.staticfiles import storage
319 storage.ManifestStaticFilesStorage.manifest_strict = False
320 if CACHE_BUSTING_STATIC_ENABLED:
321 STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
322 if CACHE_BUSTING_MEDIA_ENABLED:
323 DEFAULT_FILE_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
324
325CACHES = {
326 # DUMMY CACHE FOR DEVELOPMENT
327 'default': {
328 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
329 },
330 # MEMCACHED EXAMPLE
331 # 'default': {
332 # 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
333 # 'LOCATION': '127.0.0.1:11211',
334 # },
335 # FILECACHE EXAMPLE
336 # 'default': {
337 # 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
338 # 'LOCATION': '/tmp/django_cache',
339 # }
340 # DATABASE EXAMPLE -> python manage.py createcachetable
341 # 'default': {
342 # 'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
343 # 'LOCATION': 'my_cache_table',
344 # }
345 # LOCAL-MEMORY CACHING
346 # 'default': {
347 # 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
348 # 'LOCATION': 'geonode-cache',
349 # 'TIMEOUT': 10,
350 # 'OPTIONS': {
351 # 'MAX_ENTRIES': 10000
352 # }
353 # },
354 'resources': {
355 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
356 'TIMEOUT': 600,
357 'OPTIONS': {
358 'MAX_ENTRIES': 10000
359 }
360 }
361}
362
363GEONODE_CORE_APPS = (
364 # GeoNode internal apps
365 'geonode.api',
366 'geonode.base',
367 'geonode.layers',
368 'geonode.maps',
369 'geonode.documents',
370 'geonode.security',
371 'geonode.catalogue',
372 'geonode.catalogue.metadataxsl',
373)
374
375GEONODE_INTERNAL_APPS = (
376 # GeoNode internal apps
377 'geonode.people',
378 'geonode.client',
379 'geonode.themes',
380 'geonode.proxy',
381 'geonode.social',
382 'geonode.groups',
383 'geonode.services',
384
385 # QGIS Server Apps
386 # Only enable this if using QGIS Server
387 # 'geonode.qgis_server',
388
389 # GeoServer Apps
390 # Geoserver needs to come last because
391 # it's signals may rely on other apps' signals.
392 'geonode.geoserver',
393 'geonode.upload',
394 'geonode.tasks',
395 'geonode.messaging',
396 'geonode.monitoring',
397)
398
399GEONODE_CONTRIB_APPS = (
400 # GeoNode Contrib Apps
401)
402
403# Uncomment the following line to enable contrib apps
404GEONODE_APPS = GEONODE_CORE_APPS + GEONODE_INTERNAL_APPS + GEONODE_CONTRIB_APPS
405
406INSTALLED_APPS = (
407
408
409 # Boostrap admin theme
410 # 'django_admin_bootstrapped.bootstrap3',
411 # 'django_admin_bootstrapped',
412
413 # Apps bundled with Django
414 'modeltranslation',
415 'dal',
416 'dal_select2',
417
418 'django.contrib.auth',
419 'django.contrib.contenttypes',
420 'django.contrib.sessions',
421 'django.contrib.sites',
422 'django.contrib.admin',
423 'django.contrib.sitemaps',
424 'django.contrib.staticfiles',
425 'django.contrib.messages',
426 'django.contrib.humanize',
427 'django.contrib.gis',
428
429 # Utility
430 'dj_pagination',
431 'taggit',
432 'treebeard',
433 'leaflet',
434 'bootstrap3_datetime',
435 'django_filters',
436 'mptt',
437 'storages',
438 'floppyforms',
439
440 # Theme
441 'django_forms_bootstrap',
442
443 # Social
444 'avatar',
445 'dialogos',
446 'pinax.ratings',
447 'announcements',
448 'actstream',
449 'user_messages',
450 'tastypie',
451 'polymorphic',
452 'guardian',
453 'oauth2_provider',
454 'corsheaders',
455
456 'invitations',
457
458 # login with external providers
459 'allauth',
460 'allauth.account',
461 'allauth.socialaccount',
462
463 # GeoNode
464 'geonode',
465)
466
467if 'postgresql' in DATABASE_URL or 'postgis' in DATABASE_URL:
468 INSTALLED_APPS += ('django_celery_beat',)
469
470INSTALLED_APPS += GEONODE_APPS
471
472REST_FRAMEWORK = {
473 # Use Django's standard `django.contrib.auth` permissions,
474 # or allow read-only access for unauthenticated users.
475 'DEFAULT_PERMISSION_CLASSES': [
476 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
477 ]
478}
479
480# Documents application
481try:
482 # try to parse python notation, default in dockerized env
483 ALLOWED_DOCUMENT_TYPES = ast.literal_eval(os.getenv('ALLOWED_DOCUMENT_TYPES'))
484except ValueError:
485 # fallback to regular list of values separated with misc chars
486 ALLOWED_DOCUMENT_TYPES = [
487 'doc', 'docx', 'gif', 'jpg', 'jpeg', 'ods', 'odt', 'odp', 'pdf', 'png',
488 'ppt', 'pptx', 'rar', 'sld', 'tif', 'tiff', 'txt', 'xls', 'xlsx', 'xml',
489 'zip', 'gz', 'qml'
490 ] if os.getenv('ALLOWED_DOCUMENT_TYPES') is None \
491 else re.split(r' *[,|:|;] *', os.getenv('ALLOWED_DOCUMENT_TYPES'))
492
493MAX_DOCUMENT_SIZE = int(os.getenv('MAX_DOCUMENT_SIZE ', '2')) # MB
494
495# DOCUMENT_TYPE_MAP and DOCUMENT_MIMETYPE_MAP update enumerations in
496# documents/enumerations.py and should only
497# need to be uncommented if adding other types
498# to settings.ALLOWED_DOCUMENT_TYPES
499
500# DOCUMENT_TYPE_MAP = {}
501# DOCUMENT_MIMETYPE_MAP = {}
502
503UNOCONV_ENABLE = ast.literal_eval(os.getenv('UNOCONV_ENABLE', 'False'))
504
505if UNOCONV_ENABLE:
506 UNOCONV_EXECUTABLE = os.getenv('UNOCONV_EXECUTABLE', '/usr/bin/unoconv')
507 UNOCONV_TIMEOUT = int(os.getenv('UNOCONV_TIMEOUT', 30)) # seconds
508
509LOGGING = {
510 'version': 1,
511 'disable_existing_loggers': True,
512 'formatters': {
513 'verbose': {
514 'format': '%(levelname)s %(asctime)s %(module)s %(process)d '
515 '%(thread)d %(message)s'
516 },
517 'simple': {
518 'format': '%(message)s',
519 },
520 },
521 'filters': {
522 'require_debug_false': {
523 '()': 'django.utils.log.RequireDebugFalse'
524 }
525 },
526 'handlers': {
527 'console': {
528 'level': 'INFO',
529 'class': 'logging.StreamHandler',
530 'formatter': 'simple'
531 },
532 'mail_admins': {
533 'level': 'ERROR',
534 'filters': ['require_debug_false'],
535 'class': 'django.utils.log.AdminEmailHandler',
536 }
537 },
538 "loggers": {
539 "django": {
540 "handlers": ["console"]},
541 "geonode": {
542 "handlers": ["console"], "level": "INFO", },
543 "geonode.qgis_server": {
544 "handlers": ["console"], "level": "ERROR", },
545 "geoserver-restconfig.catalog": {
546 "handlers": ["console"], "level": "ERROR", },
547 "owslib": {
548 "handlers": ["console"], "level": "ERROR", },
549 "pycsw": {
550 "handlers": ["console"], "level": "ERROR", },
551 "celery": {
552 "handlers": ["console"], "level": "ERROR", },
553 },
554}
555
556#
557# Test Settings
558#
559on_travis = ast.literal_eval(os.environ.get('ON_TRAVIS', 'False'))
560core_tests = ast.literal_eval(os.environ.get('TEST_RUN_CORE', 'False'))
561internal_apps_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTERNAL_APPS', 'False'))
562integration_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION', 'False'))
563integration_server_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION_SERVER', 'False'))
564integration_upload_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION_UPLOAD', 'False'))
565integration_monitoring_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION_MONITORING', 'False'))
566integration_csw_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION_CSW', 'False'))
567integration_bdd_tests = ast.literal_eval(os.environ.get('TEST_RUN_INTEGRATION_BDD', 'False'))
568selenium_tests = ast.literal_eval(os.environ.get('TEST_RUN_SELENIUM', 'False'))
569
570# Django 1.11 ParallelTestSuite
571TEST_RUNNER = 'geonode.tests.suite.runner.GeoNodeBaseSuiteDiscoverRunner'
572TEST_RUNNER_KEEPDB = os.environ.get('TEST_RUNNER_KEEPDB', 0)
573TEST_RUNNER_PARALLEL = os.environ.get('TEST_RUNNER_PARALLEL', 1)
574
575# GeoNode test suite
576# TEST_RUNNER = 'geonode.tests.suite.runner.DjangoParallelTestSuiteRunner'
577# TEST_RUNNER_WORKER_MAX = 3
578# TEST_RUNNER_WORKER_COUNT = 'auto'
579# TEST_RUNNER_NOT_THREAD_SAFE = None
580# TEST_RUNNER_PARENT_TIMEOUT = 10
581# TEST_RUNNER_WORKER_TIMEOUT = 10
582
583TEST = 'test' in sys.argv
584INTEGRATION = 'geonode.tests.integration' in sys.argv
585
586#
587# Customizations to built in Django settings required by GeoNode
588#
589
590# Django automatically includes the "templates" dir in all the INSTALLED_APPS.
591TEMPLATES = [
592 {
593 'NAME': 'GeoNode Project Templates',
594 'BACKEND': 'django.template.backends.django.DjangoTemplates',
595 'DIRS': [os.path.join(PROJECT_ROOT, "templates")],
596 'APP_DIRS': True,
597 'OPTIONS': {
598 'context_processors': [
599 'django.template.context_processors.debug',
600 'django.template.context_processors.i18n',
601 'django.template.context_processors.tz',
602 'django.template.context_processors.request',
603 'django.template.context_processors.media',
604 'django.template.context_processors.static',
605 'django.contrib.auth.context_processors.auth',
606 'django.contrib.messages.context_processors.messages',
607 'django.contrib.auth.context_processors.auth',
608 # 'django.core.context_processors.debug',
609 # 'django.core.context_processors.i18n',
610 # 'django.core.context_processors.tz',
611 # 'django.core.context_processors.media',
612 # 'django.core.context_processors.static',
613 # 'django.core.context_processors.request',
614 'geonode.context_processors.resource_urls',
615 'geonode.geoserver.context_processors.geoserver_urls',
616 'geonode.themes.context_processors.custom_theme'
617 ],
618 # Either remove APP_DIRS or remove the 'loaders' option.
619 # 'loaders': [
620 # 'django.template.loaders.filesystem.Loader',
621 # 'django.template.loaders.app_directories.Loader',
622 # ],
623 'debug': DEBUG,
624 },
625 },
626]
627
628MIDDLEWARE = (
629 'corsheaders.middleware.CorsMiddleware',
630 'django.middleware.common.CommonMiddleware',
631 'django.contrib.sessions.middleware.SessionMiddleware',
632 'django.contrib.messages.middleware.MessageMiddleware',
633 'django.contrib.sites.middleware.CurrentSiteMiddleware',
634 'dj_pagination.middleware.PaginationMiddleware',
635 # The setting below makes it possible to serve different languages per
636 # user depending on things like headers in HTTP requests.
637 'django.middleware.locale.LocaleMiddleware',
638 'django.middleware.csrf.CsrfViewMiddleware',
639 'django.contrib.auth.middleware.AuthenticationMiddleware',
640 'django.middleware.clickjacking.XFrameOptionsMiddleware',
641 'django.middleware.security.SecurityMiddleware',
642 'oauth2_provider.middleware.OAuth2TokenMiddleware',
643 'geonode.base.middleware.MaintenanceMiddleware',
644 'geonode.base.middleware.ReadOnlyMiddleware', # a Middleware enabling Read Only mode of Geonode
645)
646
647MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
648
649# Security stuff
650SESSION_EXPIRED_CONTROL_ENABLED = ast.literal_eval(os.environ.get('SESSION_EXPIRED_CONTROL_ENABLED', 'True'))
651
652if SESSION_EXPIRED_CONTROL_ENABLED:
653 # This middleware checks for ACCESS_TOKEN validity and if expired forces
654 # user logout
655 MIDDLEWARE += \
656 ('geonode.security.middleware.SessionControlMiddleware',)
657
658SESSION_COOKIE_SECURE = ast.literal_eval(os.environ.get('SESSION_COOKIE_SECURE', 'False'))
659CSRF_COOKIE_SECURE = ast.literal_eval(os.environ.get('CSRF_COOKIE_SECURE', 'False'))
660CSRF_COOKIE_HTTPONLY = ast.literal_eval(os.environ.get('CSRF_COOKIE_HTTPONLY', 'False'))
661CORS_ORIGIN_ALLOW_ALL = ast.literal_eval(os.environ.get('CORS_ORIGIN_ALLOW_ALL', 'False'))
662X_FRAME_OPTIONS = os.environ.get('X_FRAME_OPTIONS', 'DENY')
663SECURE_CONTENT_TYPE_NOSNIFF = ast.literal_eval(os.environ.get('SECURE_CONTENT_TYPE_NOSNIFF', 'True'))
664SECURE_BROWSER_XSS_FILTER = ast.literal_eval(os.environ.get('SECURE_BROWSER_XSS_FILTER', 'True'))
665SECURE_SSL_REDIRECT = ast.literal_eval(os.environ.get('SECURE_SSL_REDIRECT', 'False'))
666SECURE_HSTS_SECONDS = int(os.getenv('SECURE_HSTS_SECONDS', '3600'))
667SECURE_HSTS_INCLUDE_SUBDOMAINS = ast.literal_eval(os.environ.get('SECURE_HSTS_INCLUDE_SUBDOMAINS', 'True'))
668
669# Replacement of the default authentication backend in order to support
670# permissions per object.
671AUTHENTICATION_BACKENDS = (
672 'oauth2_provider.backends.OAuth2Backend',
673 'django.contrib.auth.backends.ModelBackend',
674 'guardian.backends.ObjectPermissionBackend',
675 'allauth.account.auth_backends.AuthenticationBackend',
676)
677
678if 'announcements' in INSTALLED_APPS:
679 AUTHENTICATION_BACKENDS += (
680 'announcements.auth_backends.AnnouncementPermissionsBackend',
681 )
682
683OAUTH2_PROVIDER = {
684 'SCOPES': {
685 'openid': 'Default to OpenID',
686 'read': 'Read scope',
687 'write': 'Write scope',
688 'groups': 'Access to your groups'
689 },
690
691 'CLIENT_ID_GENERATOR_CLASS': 'oauth2_provider.generators.ClientIdGenerator',
692 # 'OAUTH2_VALIDATOR_CLASS': 'geonode.security.oauth2_validators.OIDCValidator',
693
694 # OpenID Connect
695 # "OIDC_ISS_ENDPOINT": "http://localhost:8000",
696 # "OIDC_USERINFO_ENDPOINT": "http://localhost:8000/api/o/v4/tokeninfo/",
697 "OIDC_RSA_PRIVATE_KEY": b"""-----BEGIN RSA PRIVATE KEY-----
698MIICXQIBAAKBgQCIThjbTwpYu4Lwqp8oA7PqD6Ij/GwpLFJuPbWVaeCDaX6T7mh8
699mJMIEgl/VIZasLH8SwU5mZ4sPeiqk7NgJq1XDo97q5mlFoNVHMCH38KQzSIBWtbq
700WnEEnQdiqBbCmmIebLd4OcfpbIVUI89cnCq7U0M1ie0KOopWSHWOP6/35QIDAQAB
701AoGBAIdwmtBotM5A3LaJxAY9z6uXhzSc4Vj0OqBiXymtgDL0Q5t4/Yg5D3ioe5lz
702guFgzCr23KVEmOA7UBMXGtlC9V+iizVSbF4g2GqPLBKk+IYcAhfbSCg5rbbtQ5m2
703PZxKZlJOQnjFLeh4sxitd84GfX16RfAhsvIiaN4d4CG+RAlhAkEA1Vitep0aHKmA
704KRIGvZrgfH7uEZh2rRsCoo9lTxCT8ocCU964iEUxNH050yKdqYzVnNyFysY7wFgL
705gsVzPROE6QJBAKOOWj9mN7uxhjRv2L4iYJ/rZaloVA49KBZEhvI+PgC5kAIrNVaS
706n1kbJtFg54IS8HsYIP4YxONLqmDuhZL2rZ0CQQDId9wCo85eclMPxHV7AiXANdDj
707zbxt6jxunYlXYr9yG7RvNI921HVo2eZU42j8YW5zR6+cGusYUGL4jSo8kLPJAkAG
708SLPi97Rwe7OiVCHJvFxmCI9RYPbJzUO7B0sAB7AuKvMDglF8UAnbTJXDOavrbXrb
7093+N0n9MAwKl9K+zp5pxpAkBSEUlYA0kDUqRgfuAXrrO/JYErGzE0UpaHxq5gCvTf
710g+gp5fQ4nmDrSNHjakzQCX2mKMsx/GLWZzoIDd7ECV9f
711-----END RSA PRIVATE KEY-----"""
712}
713OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth2_provider.Application"
714OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "oauth2_provider.AccessToken"
715OAUTH2_PROVIDER_ID_TOKEN_MODEL = "oauth2_provider.IDToken"
716OAUTH2_PROVIDER_GRANT_MODEL = "oauth2_provider.Grant"
717OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "oauth2_provider.RefreshToken"
718
719# In order to protect oauth2 REST endpoints, used by GeoServer to fetch user roles and
720# infos, you should set this key and configure the "geonode REST role service"
721# accordingly. Keep it secret!
722# WARNING: If not set, the endpoint can be accessed by users without authorization.
723OAUTH2_API_KEY = os.environ.get('OAUTH2_API_KEY', None)
724
725# 1 day expiration time by default
726ACCESS_TOKEN_EXPIRE_SECONDS = int(os.getenv('ACCESS_TOKEN_EXPIRE_SECONDS', '86400'))
727
728# Require users to authenticate before using Geonode
729LOCKDOWN_GEONODE = ast.literal_eval(os.getenv('LOCKDOWN_GEONODE', 'False'))
730
731# Add additional paths (as regular expressions) that don't require
732# authentication.
733# - authorized exempt urls needed for oauth when GeoNode is set to lockdown
734AUTH_EXEMPT_URLS = (
735 r'^%s/?$' % FORCE_SCRIPT_NAME,
736 '%s/o/*' % FORCE_SCRIPT_NAME,
737 '%s/gs/*' % FORCE_SCRIPT_NAME,
738 '%s/account/*' % FORCE_SCRIPT_NAME,
739 '%s/static/*' % FORCE_SCRIPT_NAME,
740 '%s/api/o/*' % FORCE_SCRIPT_NAME,
741 '%s/api/roles' % FORCE_SCRIPT_NAME,
742 '%s/api/adminRole' % FORCE_SCRIPT_NAME,
743 '%s/api/users' % FORCE_SCRIPT_NAME,
744 '%s/api/layers' % FORCE_SCRIPT_NAME,
745 '%s/monitoring' % FORCE_SCRIPT_NAME,
746 r'^/i18n/setlang/?$',
747)
748
749ANONYMOUS_USER_ID = os.getenv('ANONYMOUS_USER_ID', '-1')
750GUARDIAN_GET_INIT_ANONYMOUS_USER = os.getenv(
751 'GUARDIAN_GET_INIT_ANONYMOUS_USER',
752 'geonode.people.models.get_anonymous_user_instance'
753)
754
755# Whether the uplaoded resources should be public and downloadable by default
756# or not
757DEFAULT_ANONYMOUS_VIEW_PERMISSION = ast.literal_eval(
758 os.getenv('DEFAULT_ANONYMOUS_VIEW_PERMISSION', 'True')
759)
760DEFAULT_ANONYMOUS_DOWNLOAD_PERMISSION = ast.literal_eval(
761 os.getenv('DEFAULT_ANONYMOUS_DOWNLOAD_PERMISSION', 'True')
762)
763
764#
765# Settings for default search size
766#
767DEFAULT_SEARCH_SIZE = int(os.getenv('DEFAULT_SEARCH_SIZE', '10'))
768
769
770#
771# Settings for third party apps
772#
773
774# Pinax Ratings
775PINAX_RATINGS_CATEGORY_CHOICES = {
776 "maps.Map": {
777 "map": "How good is this map?"
778 },
779 "layers.Layer": {
780 "layer": "How good is this layer?"
781 },
782 "documents.Document": {
783 "document": "How good is this document?"
784 }
785}
786
787# Activity Stream
788ACTSTREAM_SETTINGS = {
789 'FETCH_RELATIONS': True,
790 'USE_PREFETCH': False,
791 'USE_JSONFIELD': True,
792 'GFK_FETCH_DEPTH': 1,
793}
794
795
796# Email for users to contact admins.
797THEME_ACCOUNT_CONTACT_EMAIL = os.getenv(
798 'THEME_ACCOUNT_CONTACT_EMAIL', 'admin@example.com'
799)
800
801#
802# GeoNode specific settings
803#
804# per-deployment settings should go here
805SITE_HOST_SCHEMA = os.getenv('SITE_HOST_SCHEMA', 'http')
806SITE_HOST_NAME = os.getenv('SITE_HOST_NAME', 'localhost')
807SITE_HOST_PORT = os.getenv('SITE_HOST_PORT', 8000)
808_default_siteurl = "%s://%s:%s/" % (SITE_HOST_SCHEMA,
809 SITE_HOST_NAME,
810 SITE_HOST_PORT) if SITE_HOST_PORT else "%s://%s/" % (SITE_HOST_SCHEMA, SITE_HOST_NAME)
811SITEURL = os.getenv('SITEURL', _default_siteurl)
812
813# we need hostname for deployed
814_surl = urlparse(SITEURL)
815HOSTNAME = _surl.hostname
816
817# add trailing slash to site url. geoserver url will be relative to this
818if not SITEURL.endswith('/'):
819 SITEURL = '{}/'.format(SITEURL)
820
821# Login and logout urls override
822LOGIN_URL = os.getenv('LOGIN_URL', '{}account/login/'.format(SITEURL))
823LOGOUT_URL = os.getenv('LOGOUT_URL', '{}account/logout/'.format(SITEURL))
824
825ACCOUNT_LOGIN_REDIRECT_URL = os.getenv('LOGIN_REDIRECT_URL', SITEURL)
826ACCOUNT_LOGOUT_REDIRECT_URL = os.getenv('LOGOUT_REDIRECT_URL', SITEURL)
827
828# Backend
829DEFAULT_WORKSPACE = os.getenv('DEFAULT_WORKSPACE', 'geonode')
830CASCADE_WORKSPACE = os.getenv('CASCADE_WORKSPACE', 'geonode')
831
832OGP_URL = os.getenv('OGP_URL', "http://geodata.tufts.edu/solr/select")
833
834# Topic Categories list should not be modified (they are ISO). In case you
835# absolutely need it set to True this variable
836MODIFY_TOPICCATEGORY = ast.literal_eval(os.getenv('MODIFY_TOPICCATEGORY', 'True'))
837
838# If this option is enabled, Topic Categories will become strictly Mandatory on
839# Metadata Wizard
840TOPICCATEGORY_MANDATORY = ast.literal_eval(os.environ.get('TOPICCATEGORY_MANDATORY', 'False'))
841
842MISSING_THUMBNAIL = os.getenv(
843 'MISSING_THUMBNAIL', 'geonode/img/missing_thumb.png'
844)
845
846GEOSERVER_LOCATION = os.getenv(
847 'GEOSERVER_LOCATION', 'http://localhost:8080/geoserver/'
848)
849
850GEOSERVER_PUBLIC_SCHEMA = os.getenv(
851 'GEOSERVER_PUBLIC_SCHEMA', SITE_HOST_SCHEMA
852)
853
854GEOSERVER_PUBLIC_HOST = os.getenv(
855 'GEOSERVER_PUBLIC_HOST', SITE_HOST_NAME
856)
857
858GEOSERVER_PUBLIC_PORT = os.getenv(
859 'GEOSERVER_PUBLIC_PORT', 8080
860)
861
862_default_public_location = '{}://{}:{}/geoserver/'.format(
863 GEOSERVER_PUBLIC_SCHEMA,
864 GEOSERVER_PUBLIC_HOST,
865 GEOSERVER_PUBLIC_PORT) if GEOSERVER_PUBLIC_PORT else '{}://{}/geoserver/'.format(GEOSERVER_PUBLIC_SCHEMA, GEOSERVER_PUBLIC_HOST)
866
867GEOSERVER_PUBLIC_LOCATION = os.getenv(
868 'GEOSERVER_PUBLIC_LOCATION', _default_public_location
869)
870
871GEOSERVER_WEB_UI_LOCATION = os.getenv(
872 'GEOSERVER_WEB_UI_LOCATION', GEOSERVER_PUBLIC_LOCATION
873)
874
875OGC_SERVER_DEFAULT_USER = os.getenv(
876 'GEOSERVER_ADMIN_USER', 'admin'
877)
878
879OGC_SERVER_DEFAULT_PASSWORD = os.getenv(
880 'GEOSERVER_ADMIN_PASSWORD', 'geoserver'
881)
882
883GEOFENCE_SECURITY_ENABLED = False if TEST and not INTEGRATION else ast.literal_eval(os.getenv('GEOFENCE_SECURITY_ENABLED', 'True'))
884
885# OGC (WMS/WFS/WCS) Server Settings
886# OGC (WMS/WFS/WCS) Server Settings
887OGC_SERVER = {
888 'default': {
889 'BACKEND': os.getenv('BACKEND', 'geonode.geoserver'),
890 'LOCATION': GEOSERVER_LOCATION,
891 'WEB_UI_LOCATION': GEOSERVER_WEB_UI_LOCATION,
892 'LOGIN_ENDPOINT': 'j_spring_oauth2_geonode_login',
893 'LOGOUT_ENDPOINT': 'j_spring_oauth2_geonode_logout',
894 # PUBLIC_LOCATION needs to be kept like this because in dev mode
895 # the proxy won't work and the integration tests will fail
896 # the entire block has to be overridden in the local_settings
897 'PUBLIC_LOCATION': GEOSERVER_PUBLIC_LOCATION,
898 'USER': OGC_SERVER_DEFAULT_USER,
899 'PASSWORD': OGC_SERVER_DEFAULT_PASSWORD,
900 'MAPFISH_PRINT_ENABLED': ast.literal_eval(os.getenv('MAPFISH_PRINT_ENABLED', 'True')),
901 'PRINT_NG_ENABLED': ast.literal_eval(os.getenv('PRINT_NG_ENABLED', 'True')),
902 'GEONODE_SECURITY_ENABLED': ast.literal_eval(os.getenv('GEONODE_SECURITY_ENABLED', 'True')),
903 'GEOFENCE_SECURITY_ENABLED': GEOFENCE_SECURITY_ENABLED,
904 'GEOFENCE_URL': os.getenv('GEOFENCE_URL', 'internal:/'),
905 'WMST_ENABLED': ast.literal_eval(os.getenv('WMST_ENABLED', 'False')),
906 'BACKEND_WRITE_ENABLED': ast.literal_eval(os.getenv('BACKEND_WRITE_ENABLED', 'True')),
907 'WPS_ENABLED': ast.literal_eval(os.getenv('WPS_ENABLED', 'False')),
908 'LOG_FILE': '%s/geoserver/data/logs/geoserver.log'
909 % os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir)),
910 # Set to name of database in DATABASES dictionary to enable
911 # 'datastore',
912 'DATASTORE': os.getenv('DEFAULT_BACKEND_DATASTORE', ''),
913 'TIMEOUT': int(os.getenv('OGC_REQUEST_TIMEOUT', '10')),
914 'MAX_RETRIES': int(os.getenv('OGC_REQUEST_MAX_RETRIES', '3')),
915 'BACKOFF_FACTOR': float(os.getenv('OGC_REQUEST_BACKOFF_FACTOR', '0.3')),
916 'POOL_MAXSIZE': int(os.getenv('OGC_REQUEST_POOL_MAXSIZE', '10')),
917 'POOL_CONNECTIONS': int(os.getenv('OGC_REQUEST_POOL_CONNECTIONS', '10')),
918 }
919}
920
921USE_GEOSERVER = 'geonode.geoserver' in INSTALLED_APPS and OGC_SERVER['default']['BACKEND'] == 'geonode.geoserver'
922
923# Uploader Settings
924DATA_UPLOAD_MAX_NUMBER_FIELDS = 100000
925"""
926 DEFAULT_BACKEND_UPLOADER = {'geonode.rest', 'geonode.importer'}
927"""
928UPLOADER = {
929 'BACKEND': os.getenv('DEFAULT_BACKEND_UPLOADER', 'geonode.rest'),
930 'OPTIONS': {
931 'TIME_ENABLED': ast.literal_eval(os.getenv('TIME_ENABLED', 'False')),
932 'MOSAIC_ENABLED': ast.literal_eval(os.getenv('MOSAIC_ENABLED', 'False')),
933 },
934 'SUPPORTED_CRS': [
935 'EPSG:4326',
936 'EPSG:3785',
937 'EPSG:3857',
938 'EPSG:32647',
939 'EPSG:32736'
940 ],
941 'SUPPORTED_EXT': [
942 '.shp',
943 '.csv',
944 '.kml',
945 '.kmz',
946 '.json',
947 '.geojson',
948 '.tif',
949 '.tiff',
950 '.geotiff',
951 '.gml',
952 '.xml'
953 ]
954}
955
956EPSG_CODE_MATCHES = {
957 'EPSG:4326': '(4326) WGS 84',
958 'EPSG:900913': '(900913) Google Maps Global Mercator',
959 'EPSG:3857': '(3857) WGS 84 / Pseudo-Mercator',
960 'EPSG:3785': '(3785 DEPRECATED) Popular Visualisation CRS / Mercator',
961 'EPSG:32647': '(32647) WGS 84 / UTM zone 47N',
962 'EPSG:32736': '(32736) WGS 84 / UTM zone 36S'
963}
964
965# CSW settings
966CATALOGUE = {
967 'default': {
968 # The underlying CSW implementation
969 # default is pycsw in local mode (tied directly to GeoNode Django DB)
970 'ENGINE': 'geonode.catalogue.backends.pycsw_local',
971 # pycsw in non-local mode
972 # 'ENGINE': 'geonode.catalogue.backends.pycsw_http',
973 # GeoNetwork opensource
974 # 'ENGINE': 'geonode.catalogue.backends.geonetwork',
975 # deegree and others
976 # 'ENGINE': 'geonode.catalogue.backends.generic',
977
978 # The FULLY QUALIFIED base url to the CSW instance for this GeoNode
979 'URL': urljoin(SITEURL, '/catalogue/csw'),
980 # 'URL': 'http://localhost:8080/geonetwork/srv/en/csw',
981 # 'URL': 'http://localhost:8080/deegree-csw-demo-3.0.4/services',
982
983 # login credentials (for GeoNetwork)
984 # 'USER': 'admin',
985 # 'PASSWORD': 'admin',
986
987 # 'ALTERNATES_ONLY': True,
988 }
989}
990
991# pycsw settings
992PYCSW = {
993 # pycsw configuration
994 'CONFIGURATION': {
995 # uncomment / adjust to override server config system defaults
996 # 'server': {
997 # 'maxrecords': '10',
998 # 'pretty_print': 'true',
999 # 'federatedcatalogues': 'http://catalog.data.gov/csw'
1000 # },
1001 'server': {
1002 'home': '.',
1003 'url': CATALOGUE['default']['URL'],
1004 'encoding': 'UTF-8',
1005 'language': LANGUAGE_CODE,
1006 'maxrecords': '20',
1007 'pretty_print': 'true',
1008 # 'domainquerytype': 'range',
1009 'domaincounts': 'true',
1010 'profiles': 'apiso,ebrim',
1011 },
1012 'manager': {
1013 # authentication/authorization is handled by Django
1014 'transactions': 'false',
1015 'allowed_ips': '*',
1016 # 'csw_harvest_pagesize': '10',
1017 },
1018 'metadata:main': {
1019 'identification_title': 'GeoNode Catalogue',
1020 'identification_abstract': 'GeoNode is an open source platform' \
1021 ' that facilitates the creation, sharing, and collaborative use' \
1022 ' of geospatial data',
1023 'identification_keywords': 'sdi, catalogue, discovery, metadata,' \
1024 ' GeoNode',
1025 'identification_keywords_type': 'theme',
1026 'identification_fees': 'None',
1027 'identification_accessconstraints': 'None',
1028 'provider_name': 'Organization Name',
1029 'provider_url': SITEURL,
1030 'contact_name': 'Lastname, Firstname',
1031 'contact_position': 'Position Title',
1032 'contact_address': 'Mailing Address',
1033 'contact_city': 'City',
1034 'contact_stateorprovince': 'Administrative Area',
1035 'contact_postalcode': 'Zip or Postal Code',
1036 'contact_country': 'Country',
1037 'contact_phone': '+xx-xxx-xxx-xxxx',
1038 'contact_fax': '+xx-xxx-xxx-xxxx',
1039 'contact_email': 'Email Address',
1040 'contact_url': 'Contact URL',
1041 'contact_hours': 'Hours of Service',
1042 'contact_instructions': 'During hours of service. Off on ' \
1043 'weekends.',
1044 'contact_role': 'pointOfContact',
1045 },
1046 'metadata:inspire': {
1047 'enabled': 'true',
1048 'languages_supported': 'eng,gre',
1049 'default_language': 'eng',
1050 'date': 'YYYY-MM-DD',
1051 'gemet_keywords': 'Utility and governmental services',
1052 'conformity_service': 'notEvaluated',
1053 'contact_name': 'Organization Name',
1054 'contact_email': 'Email Address',
1055 'temp_extent': 'YYYY-MM-DD/YYYY-MM-DD',
1056 }
1057 }
1058}
1059
1060_DATETIME_INPUT_FORMATS = ['%Y-%m-%d %H:%M:%S.%f %Z', '%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S%Z']
1061DATETIME_INPUT_FORMATS = DATETIME_INPUT_FORMATS + _DATETIME_INPUT_FORMATS
1062
1063DISPLAY_SOCIAL = ast.literal_eval(os.getenv('DISPLAY_SOCIAL', 'True'))
1064DISPLAY_COMMENTS = ast.literal_eval(os.getenv('DISPLAY_COMMENTS', 'True'))
1065DISPLAY_RATINGS = ast.literal_eval(os.getenv('DISPLAY_RATINGS', 'True'))
1066DISPLAY_WMS_LINKS = ast.literal_eval(os.getenv('DISPLAY_WMS_LINKS', 'True'))
1067
1068SOCIAL_ORIGINS = [{
1069 "label": "Email",
1070 "url": "mailto:?subject={name}&body={url}",
1071 "css_class": "email"
1072}, {
1073 "label": "Facebook",
1074 "url": "http://www.facebook.com/sharer.php?u={url}",
1075 "css_class": "fb"
1076}, {
1077 "label": "Twitter",
1078 "url": "https://twitter.com/share?url={url}&hashtags={hashtags}",
1079 "css_class": "tw"
1080}]
1081
1082# CKAN Query String Parameters names pulled from
1083# http://tinyurl.com/og2jofn
1084CKAN_ORIGINS = [{
1085 "label": "Humanitarian Data Exchange (HDX)",
1086 "url": "https://data.hdx.rwlabs.org/dataset/new?title={name}&"
1087 "dataset_date={date}¬es={abstract}&caveats={caveats}",
1088 "css_class": "hdx"
1089}]
1090# SOCIAL_ORIGINS.extend(CKAN_ORIGINS)
1091
1092# Setting TWITTER_CARD to True will enable Twitter Cards
1093# https://dev.twitter.com/cards/getting-started
1094# Be sure to replace @GeoNode with your organization or site's twitter handle.
1095TWITTER_CARD = ast.literal_eval(os.getenv('TWITTER_CARD', 'True'))
1096TWITTER_SITE = '@GeoNode'
1097TWITTER_HASHTAGS = ['geonode']
1098
1099OPENGRAPH_ENABLED = ast.literal_eval(os.getenv('OPENGRAPH_ENABLED', 'True'))
1100
1101# Enable Licenses User Interface
1102# Regardless of selection, license field stil exists as a field in the
1103# Resourcebase model.
1104# Detail Display: above, below, never
1105# Metadata Options: verbose, light, never
1106LICENSES = {
1107 'ENABLED': True,
1108 'DETAIL': 'above',
1109 'METADATA': 'verbose',
1110}
1111
1112SRID = {
1113 'DETAIL': 'never',
1114}
1115
1116SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
1117
1118try:
1119 # try to parse python notation, default in dockerized env
1120 ALLOWED_HOSTS = ast.literal_eval(os.getenv('ALLOWED_HOSTS'))
1121except ValueError:
1122 # fallback to regular list of values separated with misc chars
1123 ALLOWED_HOSTS = [HOSTNAME, 'localhost', 'django', 'geonode'] if os.getenv('ALLOWED_HOSTS') is None \
1124 else re.split(r' *[,|:|;] *', os.getenv('ALLOWED_HOSTS'))
1125
1126# AUTH_IP_WHITELIST property limits access to users/groups REST endpoints
1127# to only whitelisted IP addresses.
1128#
1129# Empty list means 'allow all'
1130#
1131# If you need to limit 'api' REST calls to only some specific IPs
1132# fill the list like below:
1133#
1134# AUTH_IP_WHITELIST = ['192.168.1.158', '192.168.1.159']
1135AUTH_IP_WHITELIST = [HOSTNAME, 'localhost', 'django', 'geonode'] if os.getenv('AUTH_IP_WHITELIST') is None \
1136 else re.split(r' *[,|:|;] *', os.getenv('AUTH_IP_WHITELIST'))
1137
1138# A tuple of hosts the proxy can send requests to.
1139try:
1140 # try to parse python notation, default in dockerized env
1141 PROXY_ALLOWED_HOSTS = ast.literal_eval(os.getenv('PROXY_ALLOWED_HOSTS'))
1142except ValueError:
1143 # fallback to regular list of values separated with misc chars
1144 PROXY_ALLOWED_HOSTS = [HOSTNAME, 'localhost', 'django', 'geonode', 'spatialreference.org', 'nominatim.openstreetmap.org', 'dev.openlayers.org'] if os.getenv('PROXY_ALLOWED_HOSTS') is None \
1145 else re.split(r' *[,|:|;] *', os.getenv('PROXY_ALLOWED_HOSTS'))
1146
1147# The proxy to use when making cross origin requests.
1148PROXY_URL = os.environ.get('PROXY_URL', '/proxy/?url=')
1149
1150# Haystack Search Backend Configuration. To enable,
1151# first install the following:
1152# - pip install django-haystack
1153# - pip install pyelasticsearch
1154# Set HAYSTACK_SEARCH to True
1155# Run "python manage.py rebuild_index"
1156HAYSTACK_SEARCH = ast.literal_eval(os.getenv('HAYSTACK_SEARCH', 'False'))
1157# Avoid permissions prefiltering
1158SKIP_PERMS_FILTER = ast.literal_eval(os.getenv('SKIP_PERMS_FILTER', 'False'))
1159# Update facet counts from Haystack
1160HAYSTACK_FACET_COUNTS = ast.literal_eval(os.getenv('HAYSTACK_FACET_COUNTS', 'True'))
1161if HAYSTACK_SEARCH:
1162 if 'haystack' not in INSTALLED_APPS:
1163 INSTALLED_APPS += ('haystack', )
1164 HAYSTACK_CONNECTIONS = {
1165 'default': {
1166 'ENGINE': 'haystack.backends.elasticsearch2_backend.Elasticsearch2SearchEngine',
1167 'URL': os.getenv('HAYSTACK_ENGINE_URL', 'http://127.0.0.1:9200/'),
1168 'INDEX_NAME': os.getenv('HAYSTACK_ENGINE_INDEX_NAME', 'haystack'),
1169 },
1170 }
1171 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
1172 HAYSTACK_SEARCH_RESULTS_PER_PAGE = int(os.getenv('HAYSTACK_SEARCH_RESULTS_PER_PAGE', '200'))
1173
1174# Available download formats
1175DOWNLOAD_FORMATS_METADATA = [
1176 'Atom', 'DIF', 'Dublin Core', 'ebRIM', 'FGDC', 'ISO',
1177]
1178DOWNLOAD_FORMATS_VECTOR = [
1179 'JPEG', 'PDF', 'PNG', 'Zipped Shapefile', 'GML 2.0', 'GML 3.1.1', 'CSV',
1180 'Excel', 'GeoJSON', 'KML', 'View in Google Earth', 'Tiles',
1181 'QGIS layer file (.qlr)',
1182 'QGIS project file (.qgs)',
1183]
1184DOWNLOAD_FORMATS_RASTER = [
1185 'JPEG',
1186 'PDF',
1187 'PNG',
1188 'ArcGrid',
1189 'GeoTIFF',
1190 'Gtopo30',
1191 'ImageMosaic',
1192 'KML',
1193 'View in Google Earth',
1194 'Tiles',
1195 'GML',
1196 'GZIP',
1197 'QGIS layer file (.qlr)',
1198 'QGIS project file (.qgs)',
1199 'Zipped All Files'
1200]
1201
1202
1203DISPLAY_ORIGINAL_DATASET_LINK = ast.literal_eval(
1204 os.getenv('DISPLAY_ORIGINAL_DATASET_LINK', 'True'))
1205
1206ACCOUNT_NOTIFY_ON_PASSWORD_CHANGE = ast.literal_eval(
1207 os.getenv('ACCOUNT_NOTIFY_ON_PASSWORD_CHANGE', 'False'))
1208
1209TASTYPIE_DEFAULT_FORMATS = ['json']
1210
1211# gravatar settings
1212AUTO_GENERATE_AVATAR_SIZES = (
1213 20, 30, 32, 40, 50, 65, 70, 80, 100, 140, 200, 240
1214)
1215AVATAR_GRAVATAR_SSL = ast.literal_eval(os.getenv('AVATAR_GRAVATAR_SSL', 'False'))
1216
1217# Number of results per page listed in the GeoNode search pages
1218CLIENT_RESULTS_LIMIT = int(os.getenv('CLIENT_RESULTS_LIMIT', '5'))
1219
1220# LOCKDOWN API endpoints to prevent unauthenticated access.
1221# If set to True, search won't deliver results and filtering ResourceBase-objects is not possible for anonymous users
1222API_LOCKDOWN = ast.literal_eval(
1223 os.getenv('API_LOCKDOWN', 'False'))
1224
1225# Number of items returned by the apis 0 equals no limit
1226API_LIMIT_PER_PAGE = int(os.getenv('API_LIMIT_PER_PAGE', '200'))
1227API_INCLUDE_REGIONS_COUNT = ast.literal_eval(
1228 os.getenv('API_INCLUDE_REGIONS_COUNT', 'False'))
1229
1230# option to enable/disable resource unpublishing for administrators
1231RESOURCE_PUBLISHING = ast.literal_eval(os.getenv('RESOURCE_PUBLISHING', 'False'))
1232
1233# Settings for EXIF plugin
1234EXIF_ENABLED = ast.literal_eval(os.getenv('EXIF_ENABLED', 'True'))
1235
1236if EXIF_ENABLED:
1237 if 'geonode.documents.exif' not in INSTALLED_APPS:
1238 INSTALLED_APPS += ('geonode.documents.exif',)
1239
1240# Settings for CREATE_LAYER plugin
1241CREATE_LAYER = ast.literal_eval(os.getenv('CREATE_LAYER', 'False'))
1242
1243if CREATE_LAYER:
1244 if 'geonode.geoserver.createlayer' not in INSTALLED_APPS:
1245 INSTALLED_APPS += ('geonode.geoserver.createlayer',)
1246
1247# Settings for FAVORITE plugin
1248FAVORITE_ENABLED = ast.literal_eval(os.getenv('FAVORITE_ENABLED', 'True'))
1249
1250if FAVORITE_ENABLED:
1251 if 'geonode.favorite' not in INSTALLED_APPS:
1252 INSTALLED_APPS += ('geonode.favorite',)
1253
1254
1255# Settings for RECAPTCHA plugin
1256RECAPTCHA_ENABLED = ast.literal_eval(os.environ.get('RECAPTCHA_ENABLED', 'False'))
1257
1258if RECAPTCHA_ENABLED:
1259 if 'captcha' not in INSTALLED_APPS:
1260 INSTALLED_APPS += ('captcha',)
1261 ACCOUNT_SIGNUP_FORM_CLASS = os.getenv("ACCOUNT_SIGNUP_FORM_CLASS",
1262 'geonode.people.forms.AllauthReCaptchaSignupForm')
1263 """
1264 In order to generate reCaptcha keys, please see:
1265 - https://pypi.org/project/django-recaptcha/#installation
1266 - https://pypi.org/project/django-recaptcha/#local-development-and-functional-testing
1267 """
1268 RECAPTCHA_PUBLIC_KEY = os.getenv("RECAPTCHA_PUBLIC_KEY", 'geonode_RECAPTCHA_PUBLIC_KEY')
1269 RECAPTCHA_PRIVATE_KEY = os.getenv("RECAPTCHA_PRIVATE_KEY", 'geonode_RECAPTCHA_PRIVATE_KEY')
1270
1271GEONODE_CATALOGUE_METADATA_XSL = ast.literal_eval(os.getenv('GEONODE_CATALOGUE_METADATA_XSL', 'True'))
1272
1273# -- START Client Hooksets Setup
1274
1275# GeoNode javascript client configuration
1276
1277# default map projection
1278# Note: If set to EPSG:4326, then only EPSG:4326 basemaps will work.
1279DEFAULT_MAP_CRS = os.environ.get('DEFAULT_MAP_CRS', "EPSG:3857")
1280
1281DEFAULT_LAYER_FORMAT = os.environ.get('DEFAULT_LAYER_FORMAT', "image/png")
1282
1283# Where should newly created maps be focused?
1284DEFAULT_MAP_CENTER = (os.environ.get('DEFAULT_MAP_CENTER_X', 0), os.environ.get('DEFAULT_MAP_CENTER_Y', 0))
1285
1286# How tightly zoomed should newly created maps be?
1287# 0 = entire world;
1288# maximum zoom is between 12 and 15 (for Google Maps, coverage varies by area)
1289DEFAULT_MAP_ZOOM = int(os.environ.get('DEFAULT_MAP_ZOOM', 0))
1290
1291MAPBOX_ACCESS_TOKEN = os.environ.get('MAPBOX_ACCESS_TOKEN', None)
1292BING_API_KEY = os.environ.get('BING_API_KEY', None)
1293GOOGLE_API_KEY = "AIzaSyCePErHwQYoGxM3BXKoMAHxIefQh3R6Rm4"
1294
1295GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY = os.getenv('GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY', 'mapstore')
1296
1297MAP_BASELAYERS = [{
1298 "source": {"ptype": "gxp_olsource"},
1299 "type": "OpenLayers.Layer",
1300 "args": ["No background"],
1301 "visibility": False,
1302 "fixed": True,
1303 "group":"background"
1304}, {
1305 "source": {"ptype": "gxp_osmsource"},
1306 "type": "OpenLayers.Layer.OSM",
1307 "name": "mapnik",
1308 "visibility": False,
1309 "fixed": True,
1310 "group": "background"
1311}, {
1312 "source": {"ptype": "gxp_mapquestsource"},
1313 "name": "osm",
1314 "group": "background",
1315 "visibility": True
1316}, {
1317 "source": {"ptype": "gxp_mapquestsource"},
1318 "name": "naip",
1319 "group": "background",
1320 "visibility": False
1321}, {
1322 "source": {"ptype": "gxp_bingsource",
1323 "apiKey": "UEHn2pM3zwY4zZWhD9Lk~HjxfugiYQUhefpzEmenDaQ~AjzBr4vrfaS3yOu9P3PtZ8k-LnzRwd2c8H85Czk9d0GLK-X_nw393KXIO7_q1Or2",
1324 },
1325 "name": "Aerial",
1326 "visibility": True,
1327 "fixed": True,
1328 "group": "background"
1329}, {
1330 "source": {"ptype": "gxp_googlesource",
1331 "apiKey": "AIzaSyDVPqNb2D2f9-AmtENeUF5VsC3Iq6EGIX0",
1332 },
1333 "name": "SATELLITE",
1334 "visibility": True,
1335 "fixed": True,
1336 "group": "background"
1337}]
1338"""
1339To enable the REACT based Client:
13401. pip install pip install django-geonode-client==1.0.9
13412. enable those:
1342"""
1343
1344if GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY == 'react':
1345 GEONODE_CLIENT_HOOKSET = os.getenv('GEONODE_CLIENT_HOOKSET', 'geonode.client.hooksets.ReactHookSet')
1346 if 'geonode-client' not in INSTALLED_APPS:
1347 INSTALLED_APPS += ('geonode-client', )
1348
1349"""
1350To enable the Leaflet based Client:
13511. enable those:
1352"""
1353if GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY == 'leaflet':
1354 GEONODE_CLIENT_HOOKSET = os.getenv('GEONODE_CLIENT_HOOKSET', 'geonode.client.hooksets.LeafletHookSet')
1355
1356 LEAFLET_CONFIG = {
1357 'TILES': [
1358 # Find tiles at:
1359 # http://leaflet-extras.github.io/leaflet-providers/preview/
1360
1361 # Stamen toner lite.
1362 ('Watercolor',
1363 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png',
1364 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
1365 <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> \
1366 — Map data © \
1367 <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, \
1368 <a href="http://creativecommons.org/licenses/by-sa/2.0/"> \
1369 CC-BY-SA</a>'),
1370 ('Toner Lite',
1371 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png',
1372 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
1373 <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> \
1374 — Map data © \
1375 <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, \
1376 <a href="http://creativecommons.org/licenses/by-sa/2.0/"> \
1377 CC-BY-SA</a>'),
1378 ],
1379 'PLUGINS': {
1380 'esri-leaflet': {
1381 'js': 'lib/js/leaflet.js',
1382 'auto-include': True,
1383 },
1384 'leaflet-fullscreen': {
1385 'css': 'lib/css/leaflet.fullscreen.css',
1386 'js': 'lib/js/Leaflet.fullscreen.min.js',
1387 'auto-include': True,
1388 },
1389 'leaflet-opacity': {
1390 'css': 'lib/css/L.Control.Opacity.css',
1391 'js': 'lib/js/L.Control.Opacity.js',
1392 'auto-include': True,
1393 },
1394 'leaflet-navbar': {
1395 'css': 'lib/css/Leaflet.NavBar.css',
1396 'js': 'lib/js/index.js',
1397 'auto-include': True,
1398 },
1399 'leaflet-measure': {
1400 'css': 'lib/css/leaflet-measure.css',
1401 'js': 'lib/js/leaflet-measure.js',
1402 'auto-include': True,
1403 },
1404 },
1405 'SRID': 3857,
1406 'RESET_VIEW': False
1407 }
1408
1409 if not DEBUG_STATIC:
1410 # if not DEBUG_STATIC, use minified css and js
1411 LEAFLET_CONFIG['PLUGINS'] = {
1412 'leaflet-plugins': {
1413 'js': 'lib/js/leaflet-plugins.min.js',
1414 'css': 'lib/css/leaflet-plugins.min.css',
1415 'auto-include': True,
1416 }
1417 }
1418
1419"""
1420To enable the MapStore2 REACT based Client:
14211. pip install pip install django-geonode-mapstore-client==1.0
14222. enable those:
1423"""
1424if GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY == 'mapstore':
1425 GEONODE_CLIENT_HOOKSET = os.getenv('GEONODE_CLIENT_HOOKSET', 'geonode_mapstore_client.hooksets.MapStoreHookSet')
1426
1427 if 'geonode_mapstore_client' not in INSTALLED_APPS:
1428 INSTALLED_APPS += (
1429 'mapstore2_adapter',
1430 'geonode_mapstore_client',)
1431
1432 # This must be set to True in case you run the client in DEBUG mode with `npm run start`
1433 MAPSTORE_DEBUG = False
1434
1435 def get_geonode_catalogue_service():
1436 if PYCSW:
1437 pycsw_config = PYCSW["CONFIGURATION"]
1438 if pycsw_config:
1439 pycsw_catalogue = {
1440 ("%s" % pycsw_config['metadata:main']['identification_title']): {
1441 "url": CATALOGUE['default']['URL'],
1442 "type": "csw",
1443 "title": pycsw_config['metadata:main']['identification_title'],
1444 "autoload": True
1445 }
1446 }
1447 return pycsw_catalogue
1448 return None
1449
1450 GEONODE_CATALOGUE_SERVICE = get_geonode_catalogue_service()
1451
1452 MAPSTORE_CATALOGUE_SERVICES = {
1453 "Demo WMS Service": {
1454 "url": "https://demo.geo-solutions.it/geoserver/wms",
1455 "type": "wms",
1456 "title": "Demo WMS Service",
1457 "autoload": False
1458 },
1459 "Demo WMTS Service": {
1460 "url": "https://demo.geo-solutions.it/geoserver/gwc/service/wmts",
1461 "type": "wmts",
1462 "title": "Demo WMTS Service",
1463 "autoload": False
1464 }
1465 }
1466
1467 MAPSTORE_CATALOGUE_SELECTED_SERVICE = "Demo WMS Service"
1468
1469 if GEONODE_CATALOGUE_SERVICE:
1470 MAPSTORE_CATALOGUE_SERVICES[list(list(GEONODE_CATALOGUE_SERVICE.keys()))[0]] = GEONODE_CATALOGUE_SERVICE[list(list(GEONODE_CATALOGUE_SERVICE.keys()))[0]]
1471 MAPSTORE_CATALOGUE_SELECTED_SERVICE = list(list(GEONODE_CATALOGUE_SERVICE.keys()))[0]
1472
1473 DEFAULT_MS2_BACKGROUNDS = [
1474 {
1475 "type": "osm",
1476 "title": "Open Street Map",
1477 "name": "mapnik",
1478 "source": "osm",
1479 "group": "background",
1480 "visibility": True
1481 }, {
1482 "type": "tileprovider",
1483 "title": "OpenTopoMap",
1484 "provider": "OpenTopoMap",
1485 "name": "OpenTopoMap",
1486 "source": "OpenTopoMap",
1487 "group": "background",
1488 "visibility": False
1489 }, {
1490 "type": "wms",
1491 "title": "Sentinel-2 cloudless - https://s2maps.eu",
1492 "format": "image/jpeg",
1493 "id": "s2cloudless",
1494 "name": "s2cloudless:s2cloudless",
1495 "url": "https://maps.geo-solutions.it/geoserver/wms",
1496 "group": "background",
1497 "thumbURL": "%sstatic/mapstorestyle/img/s2cloudless-s2cloudless.png" % SITEURL,
1498 "visibility": False
1499 }, {
1500 "source": "ol",
1501 "group": "background",
1502 "id": "none",
1503 "name": "empty",
1504 "title": "Empty Background",
1505 "type": "empty",
1506 "visibility": False,
1507 "args": ["Empty Background", {"visibility": False}]
1508 }
1509 ]
1510
1511 if MAPBOX_ACCESS_TOKEN:
1512 BASEMAP = {
1513 "type": "tileprovider",
1514 "title": "MapBox streets-v11",
1515 "provider": "MapBoxStyle",
1516 "name": "MapBox streets-v11",
1517 "accessToken": "%s" % MAPBOX_ACCESS_TOKEN,
1518 "source": "streets-v11",
1519 "thumbURL": "https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/6/33/23?access_token=%s" % MAPBOX_ACCESS_TOKEN,
1520 "group": "background",
1521 "visibility": True
1522 }
1523 DEFAULT_MS2_BACKGROUNDS = [BASEMAP,] + DEFAULT_MS2_BACKGROUNDS
1524
1525 if BING_API_KEY:
1526 BASEMAP = {
1527 "type": "bing",
1528 "title": "Bing Aerial",
1529 "name": "AerialWithLabels",
1530 "source": "bing",
1531 "group": "background",
1532 "apiKey": "{{apiKey}}",
1533 "visibility": False
1534 }
1535 DEFAULT_MS2_BACKGROUNDS = [BASEMAP,] + DEFAULT_MS2_BACKGROUNDS
1536
1537 MAPSTORE_BASELAYERS = DEFAULT_MS2_BACKGROUNDS
1538
1539# -- END Client Hooksets Setup
1540
1541SERVICE_UPDATE_INTERVAL = 0
1542
1543SEARCH_FILTERS = {
1544 'TEXT_ENABLED': True,
1545 'TYPE_ENABLED': True,
1546 'CATEGORIES_ENABLED': True,
1547 'OWNERS_ENABLED': True,
1548 'KEYWORDS_ENABLED': True,
1549 'H_KEYWORDS_ENABLED': True,
1550 'T_KEYWORDS_ENABLED': True,
1551 'DATE_ENABLED': True,
1552 'REGION_ENABLED': True,
1553 'EXTENT_ENABLED': True,
1554 'GROUPS_ENABLED': True,
1555 'GROUP_CATEGORIES_ENABLED': True,
1556}
1557
1558# Make Free-Text Kaywords writable from users or read-only
1559# - if True only admins can edit free-text kwds from admin dashboard
1560FREETEXT_KEYWORDS_READONLY = ast.literal_eval(os.environ.get('FREETEXT_KEYWORDS_READONLY', 'False'))
1561
1562# notification settings
1563NOTIFICATION_ENABLED = ast.literal_eval(os.environ.get('NOTIFICATION_ENABLED', 'True')) or TEST
1564#PINAX_NOTIFICATIONS_LANGUAGE_MODEL = "people.Profile"
1565
1566# notifications backends
1567_EMAIL_BACKEND = "geonode.notifications_backend.EmailBackend"
1568PINAX_NOTIFICATIONS_BACKENDS = [
1569 ("email", _EMAIL_BACKEND, 0),
1570]
1571PINAX_NOTIFICATIONS_HOOKSET = "pinax.notifications.hooks.DefaultHookSet"
1572
1573# Queue non-blocking notifications.
1574PINAX_NOTIFICATIONS_QUEUE_ALL = ast.literal_eval(os.environ.get('NOTIFICATIONS_QUEUE_ALL', 'False'))
1575PINAX_NOTIFICATIONS_LOCK_WAIT_TIMEOUT = os.environ.get('NOTIFICATIONS_LOCK_WAIT_TIMEOUT', -1)
1576
1577# explicitly define NOTIFICATION_LOCK_LOCATION
1578# NOTIFICATION_LOCK_LOCATION = <path>
1579
1580# pinax.notifications
1581# or notification
1582NOTIFICATIONS_MODULE = 'pinax.notifications'
1583
1584# set to true to have multiple recipients in /message/create/
1585USER_MESSAGES_ALLOW_MULTIPLE_RECIPIENTS = ast.literal_eval(
1586 os.environ.get('USER_MESSAGES_ALLOW_MULTIPLE_RECIPIENTS', 'True'))
1587
1588if NOTIFICATION_ENABLED:
1589 if NOTIFICATIONS_MODULE not in INSTALLED_APPS:
1590 INSTALLED_APPS += (NOTIFICATIONS_MODULE, )
1591
1592# async signals can be the same as broker url
1593# but they should have separate setting anyway
1594# use amqp://localhost for local rabbitmq server
1595"""
1596 sudo apt-get install -y erlang
1597 sudo apt-get install rabbitmq-server
1598
1599 sudo update-rc.d rabbitmq-server enable
1600
1601 sudo rabbitmqctl stop_app
1602 sudo rabbitmqctl reset
1603 sudo rabbitmqctl start_app
1604
1605 sudo rabbitmqctl list_queues
1606"""
1607# Disabling the heartbeat because workers seems often disabled in flower,
1608# thanks to http://stackoverflow.com/a/14831904/654755
1609BROKER_HEARTBEAT = 0
1610
1611# Avoid long running and retried tasks to be run over-and-over again.
1612BROKER_TRANSPORT_OPTIONS = {
1613 'fanout_prefix': True,
1614 'fanout_patterns': True,
1615 'socket_timeout': 60,
1616 'visibility_timeout': 86400
1617}
1618
1619ASYNC_SIGNALS = ast.literal_eval(os.environ.get('ASYNC_SIGNALS', 'False'))
1620RABBITMQ_SIGNALS_BROKER_URL = 'amqp://localhost:5672'
1621# REDIS_SIGNALS_BROKER_URL = 'redis://localhost:6379/0'
1622LOCAL_SIGNALS_BROKER_URL = 'memory://'
1623
1624if ASYNC_SIGNALS:
1625 _BROKER_URL = RABBITMQ_SIGNALS_BROKER_URL
1626 CELERY_RESULT_BACKEND = _BROKER_URL
1627else:
1628 _BROKER_URL = LOCAL_SIGNALS_BROKER_URL
1629 CELERY_RESULT_BACKEND_PATH = os.getenv(
1630 'CELERY_RESULT_BACKEND_PATH', os.path.join(PROJECT_ROOT, '.celery_results'))
1631 if not os.path.exists(CELERY_RESULT_BACKEND_PATH):
1632 os.makedirs(CELERY_RESULT_BACKEND_PATH)
1633 CELERY_RESULT_BACKEND = 'file:///%s' % CELERY_RESULT_BACKEND_PATH
1634
1635CELERY_BROKER_URL = os.environ.get('BROKER_URL', _BROKER_URL)
1636CELERY_RESULT_PERSISTENT = ast.literal_eval(os.environ.get('CELERY_RESULT_PERSISTENT', 'False'))
1637
1638# Allow to recover from any unknown crash.
1639CELERY_ACKS_LATE = ast.literal_eval(os.environ.get('CELERY_ACKS_LATE', 'True'))
1640
1641# Set this to False in order to run async
1642CELERY_TASK_ALWAYS_EAGER = ast.literal_eval(os.environ.get('CELERY_TASK_ALWAYS_EAGER', 'False' if ASYNC_SIGNALS else 'True'))
1643CELERY_TASK_EAGER_PROPAGATES = ast.literal_eval(os.environ.get('CELERY_TASK_EAGER_PROPAGATES', 'False' if ASYNC_SIGNALS else 'True'))
1644CELERY_TASK_IGNORE_RESULT = ast.literal_eval(os.environ.get('CELERY_TASK_IGNORE_RESULT', 'True'))
1645
1646# I use these to debug kombu crashes; we get a more informative message.
1647CELERY_TASK_SERIALIZER = os.environ.get('CELERY_TASK_SERIALIZER', 'json')
1648CELERY_RESULT_SERIALIZER = os.environ.get('CELERY_RESULT_SERIALIZER', 'json')
1649CELERY_ACCEPT_CONTENT = [CELERY_RESULT_SERIALIZER, ]
1650
1651# Set Tasks Queues
1652# CELERY_TASK_DEFAULT_QUEUE = "default"
1653# CELERY_TASK_DEFAULT_EXCHANGE = "default"
1654# CELERY_TASK_DEFAULT_EXCHANGE_TYPE = "direct"
1655# CELERY_TASK_DEFAULT_ROUTING_KEY = "default"
1656CELERY_TASK_CREATE_MISSING_QUEUES = ast.literal_eval(os.environ.get('CELERY_TASK_CREATE_MISSING_QUEUES', 'True'))
1657GEONODE_EXCHANGE = Exchange("default", type="direct", durable=True)
1658GEOSERVER_EXCHANGE = Exchange("geonode", type="topic", durable=False)
1659CELERY_TASK_QUEUES = (
1660 Queue('default', GEONODE_EXCHANGE, routing_key='default'),
1661 Queue('geonode', GEONODE_EXCHANGE, routing_key='geonode'),
1662 Queue('update', GEONODE_EXCHANGE, routing_key='update'),
1663 Queue('cleanup', GEONODE_EXCHANGE, routing_key='cleanup'),
1664 Queue('email', GEONODE_EXCHANGE, routing_key='email'),
1665)
1666
1667if USE_GEOSERVER:
1668 CELERY_TASK_QUEUES += (
1669 Queue("broadcast", GEOSERVER_EXCHANGE, routing_key="#"),
1670 Queue("email.events", GEOSERVER_EXCHANGE, routing_key="email"),
1671 Queue("all.geoserver", GEOSERVER_EXCHANGE, routing_key="geoserver.#"),
1672 Queue("geoserver.catalog", GEOSERVER_EXCHANGE, routing_key="geoserver.catalog"),
1673 Queue("geoserver.data", GEOSERVER_EXCHANGE, routing_key="geoserver.catalog"),
1674 Queue("geoserver.events", GEOSERVER_EXCHANGE, routing_key="geonode.geoserver"),
1675 Queue("notifications.events", GEOSERVER_EXCHANGE, routing_key="notifications"),
1676 Queue("geonode.layer.viewer", GEOSERVER_EXCHANGE, routing_key="geonode.viewer"),
1677 )
1678
1679# from celery.schedules import crontab
1680# EXAMPLES
1681# ...
1682# 'update_feeds': {
1683# 'task': 'arena.social.tasks.Update',
1684# 'schedule': crontab(minute='*/6'),
1685# },
1686# ...
1687# 'send-summary-every-hour': {
1688# 'task': 'summary',
1689# # There are 4 ways we can handle time, read further
1690# 'schedule': 3600.0,
1691# # If you're using any arguments
1692# 'args': (‘We don’t need any’,),
1693# },
1694# # Executes every Friday at 4pm
1695# 'send-notification-on-friday-afternoon': {
1696# 'task': 'my_app.tasks.send_notification',
1697# 'schedule': crontab(hour=16, day_of_week=5),
1698# },
1699CELERY_BEAT_SCHEDULE = {}
1700
1701DELAYED_SECURITY_SIGNALS = ast.literal_eval(os.environ.get('DELAYED_SECURITY_SIGNALS', 'False'))
1702CELERY_ENABLE_UTC = ast.literal_eval(os.environ.get('CELERY_ENABLE_UTC', 'True'))
1703CELERY_TIMEZONE = TIME_ZONE
1704
1705# Half a day is enough
1706CELERY_TASK_RESULT_EXPIRES = os.environ.get('CELERY_TASK_RESULT_EXPIRES', 43200)
1707
1708# Sometimes, Ask asks us to enable this to debug issues.
1709# BTW, it will save some CPU cycles.
1710CELERY_DISABLE_RATE_LIMITS = ast.literal_eval(os.environ.get('CELERY_DISABLE_RATE_LIMITS', 'False'))
1711CELERY_SEND_TASK_EVENTS = ast.literal_eval(os.environ.get('CELERY_SEND_TASK_EVENTS', 'True'))
1712CELERY_WORKER_DISABLE_RATE_LIMITS = ast.literal_eval(os.environ.get('CELERY_WORKER_DISABLE_RATE_LIMITS', 'False'))
1713CELERY_WORKER_SEND_TASK_EVENTS = ast.literal_eval(os.environ.get('CELERY_WORKER_SEND_TASK_EVENTS', 'True'))
1714
1715# Allow our remote workers to get tasks faster if they have a
1716# slow internet connection (yes Gurney, I'm thinking of you).
1717CELERY_MESSAGE_COMPRESSION = os.environ.get('CELERY_MESSAGE_COMPRESSION', 'gzip')
1718
1719# The default beiing 5000, we need more than this.
1720CELERY_MAX_CACHED_RESULTS = os.environ.get('CELERY_MAX_CACHED_RESULTS', 32768)
1721
1722# NOTE: I don't know if this is compatible with upstart.
1723CELERYD_POOL_RESTARTS = ast.literal_eval(os.environ.get('CELERYD_POOL_RESTARTS', 'True'))
1724CELERY_TRACK_STARTED = ast.literal_eval(os.environ.get('CELERY_TRACK_STARTED', 'True'))
1725CELERY_SEND_TASK_SENT_EVENT = ast.literal_eval(os.environ.get('CELERY_SEND_TASK_SENT_EVENT', 'True'))
1726
1727# Disabled by default and I like it, because we use Sentry for this.
1728CELERY_SEND_TASK_ERROR_EMAILS = ast.literal_eval(os.environ.get('CELERY_SEND_TASK_ERROR_EMAILS', 'False'))
1729
1730# ########################################################################### #
1731# SECURITY SETTINGS
1732# ########################################################################### #
1733
1734# Require users to authenticate before using Geonode
1735if LOCKDOWN_GEONODE:
1736 MIDDLEWARE += \
1737 ('geonode.security.middleware.LoginRequiredMiddleware',)
1738
1739# for windows users check if they didn't set GEOS and GDAL in local_settings.py
1740# maybe they set it as a windows environment
1741if os.name == 'nt':
1742 if "GEOS_LIBRARY_PATH" not in locals() \
1743 or "GDAL_LIBRARY_PATH" not in locals():
1744 if os.environ.get("GEOS_LIBRARY_PATH", None) \
1745 and os.environ.get("GDAL_LIBRARY_PATH", None):
1746 GEOS_LIBRARY_PATH = os.environ.get('GEOS_LIBRARY_PATH')
1747 GDAL_LIBRARY_PATH = os.environ.get('GDAL_LIBRARY_PATH')
1748 else:
1749 # maybe it will be found regardless if not it will throw 500 error
1750 from django.contrib.gis.geos import GEOSGeometry # flake8: noqa
1751
1752# Keywords thesauri
1753# e.g. THESAURUS = {'name':'inspire_themes', 'required':True, 'filter':True}
1754# Required: (boolean, optional, default false) mandatory while editing metadata (not implemented yet)
1755# Filter: (boolean, optional, default false) a filter option on that thesaurus will appear in the main search page
1756# THESAURUS = {'name': 'inspire_themes', 'required': True, 'filter': True}
1757
1758# Each uploaded Layer must be approved by an Admin before becoming visible
1759ADMIN_MODERATE_UPLOADS = ast.literal_eval(os.environ.get('ADMIN_MODERATE_UPLOADS', 'False'))
1760
1761# If this option is enabled, Resources belonging to a Group won't be
1762# visible by others
1763GROUP_PRIVATE_RESOURCES = ast.literal_eval(os.environ.get('GROUP_PRIVATE_RESOURCES', 'False'))
1764
1765# If this option is enabled, Groups will become strictly Mandatory on
1766# Metadata Wizard
1767GROUP_MANDATORY_RESOURCES = ast.literal_eval(os.environ.get('GROUP_MANDATORY_RESOURCES', 'False'))
1768
1769# A boolean which specifies wether to display the email in user's profile
1770SHOW_PROFILE_EMAIL = ast.literal_eval(os.environ.get('SHOW_PROFILE_EMAIL', 'False'))
1771
1772# Enables cross origin requests for geonode-client
1773MAP_CLIENT_USE_CROSS_ORIGIN_CREDENTIALS = ast.literal_eval(os.getenv(
1774 'MAP_CLIENT_USE_CROSS_ORIGIN_CREDENTIALS',
1775 'False'
1776))
1777
1778ACCOUNT_OPEN_SIGNUP = ast.literal_eval(os.environ.get('ACCOUNT_OPEN_SIGNUP', 'True'))
1779ACCOUNT_APPROVAL_REQUIRED = ast.literal_eval(
1780 os.getenv('ACCOUNT_APPROVAL_REQUIRED', 'False')
1781)
1782ACCOUNT_ADAPTER = 'geonode.people.adapters.LocalAccountAdapter'
1783ACCOUNT_CONFIRM_EMAIL_ON_GET = ast.literal_eval(os.environ.get('ACCOUNT_CONFIRM_EMAIL_ON_GET', 'True'))
1784ACCOUNT_EMAIL_REQUIRED = ast.literal_eval(os.environ.get('ACCOUNT_EMAIL_REQUIRED', 'True'))
1785ACCOUNT_EMAIL_VERIFICATION = os.environ.get('ACCOUNT_EMAIL_VERIFICATION', 'optional')
1786
1787SOCIALACCOUNT_ADAPTER = 'geonode.people.adapters.SocialAccountAdapter'
1788SOCIALACCOUNT_AUTO_SIGNUP = False
1789
1790# Uncomment this to enable Linkedin and Facebook login
1791# INSTALLED_APPS += (
1792# 'allauth.socialaccount.providers.linkedin_oauth2',
1793# 'allauth.socialaccount.providers.facebook',
1794# )
1795
1796SOCIALACCOUNT_PROVIDERS = {
1797 'linkedin_oauth2': {
1798 'SCOPE': [
1799 'r_emailaddress',
1800 'r_liteprofile',
1801 ],
1802 'PROFILE_FIELDS': [
1803 'id',
1804 'email-address',
1805 'first-name',
1806 'last-name',
1807 'picture-url',
1808 'public-profile-url',
1809 ]
1810 },
1811 'facebook': {
1812 'METHOD': 'oauth2',
1813 'SCOPE': [
1814 'email',
1815 'public_profile',
1816 ],
1817 'FIELDS': [
1818 'id',
1819 'email',
1820 'name',
1821 'first_name',
1822 'last_name',
1823 'verified',
1824 'locale',
1825 'timezone',
1826 'link',
1827 'gender',
1828 ]
1829 },
1830}
1831
1832SOCIALACCOUNT_PROFILE_EXTRACTORS = {
1833 "facebook": "geonode.people.profileextractors.FacebookExtractor",
1834 "linkedin_oauth2": "geonode.people.profileextractors.LinkedInExtractor",
1835}
1836
1837INVITATIONS_ADAPTER = ACCOUNT_ADAPTER
1838
1839# Choose thumbnail generator -- this is the default generator
1840THUMBNAIL_GENERATOR = "geonode.layers.utils.create_gs_thumbnail_geonode"
1841#THUMBNAIL_GENERATOR_DEFAULT_BG = r"http://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
1842THUMBNAIL_GENERATOR_DEFAULT_BG = r"https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png"
1843THUMBNAIL_GENERATOR_DEFAULT_SIZE = {'width': 240, 'height': 200}
1844
1845# define the urls after the settings are overridden
1846if USE_GEOSERVER:
1847 LOCAL_GXP_PTYPE = 'gxp_wmscsource'
1848 PUBLIC_GEOSERVER = {
1849 "source": {
1850 "title": "GeoServer - Public Layers",
1851 "attribution": "© %s" % SITEURL,
1852 "ptype": LOCAL_GXP_PTYPE,
1853 "url": OGC_SERVER['default']['PUBLIC_LOCATION'] + "ows",
1854 "restUrl": "/gs/rest"
1855 }
1856 }
1857 LOCAL_GEOSERVER = {
1858 "source": {
1859 "title": "GeoServer - Private Layers",
1860 "attribution": "© %s" % SITEURL,
1861 "ptype": LOCAL_GXP_PTYPE,
1862 "url": "/gs/ows",
1863 "restUrl": "/gs/rest"
1864 }
1865 }
1866 if 'geonode.geoserver' in INSTALLED_APPS:
1867 LOCAL_GEOSERVER = {
1868 "source": {
1869 "ptype": "gxp_wmscsource",
1870 "url": OGC_SERVER['default']['PUBLIC_LOCATION'] + "wms",
1871 "restUrl": "/gs/rest"
1872 }
1873 }
1874 baselayers = MAP_BASELAYERS
1875 MAP_BASELAYERS = [LOCAL_GEOSERVER]
1876 MAP_BASELAYERS.extend(baselayers)
1877
1878# Settings for MONITORING plugin
1879MONITORING_ENABLED = ast.literal_eval(os.environ.get('MONITORING_ENABLED', 'False'))
1880
1881MONITORING_CONFIG = os.getenv("MONITORING_CONFIG", None)
1882MONITORING_HOST_NAME = os.getenv("MONITORING_HOST_NAME", HOSTNAME)
1883MONITORING_SERVICE_NAME = os.getenv("MONITORING_SERVICE_NAME", 'geonode')
1884
1885# how long monitoring data should be stored
1886MONITORING_DATA_TTL = timedelta(days=int(os.getenv("MONITORING_DATA_TTL", 7)))
1887
1888# this will disable csrf check for notification config views,
1889# use with caution - for dev purpose only
1890MONITORING_DISABLE_CSRF = ast.literal_eval(os.environ.get('MONITORING_DISABLE_CSRF', 'False'))
1891
1892if MONITORING_ENABLED:
1893 if 'geonode.monitoring' not in INSTALLED_APPS:
1894 INSTALLED_APPS += ('geonode.monitoring',)
1895 if 'geonode.monitoring.middleware.MonitoringMiddleware' not in MIDDLEWARE:
1896 MIDDLEWARE += \
1897 ('geonode.monitoring.middleware.MonitoringMiddleware',)
1898
1899 # skip certain paths to not to mud stats too much
1900 MONITORING_SKIP_PATHS = ('/api/o/',
1901 '/monitoring/',
1902 '/admin',
1903 '/jsi18n',
1904 STATIC_URL,
1905 MEDIA_URL,
1906 re.compile('^/[a-z]{2}/admin/'),
1907 )
1908
1909 # configure aggregation of past data to control data resolution
1910 # list of data age, aggregation, in reverse order
1911 # for current data, 1 minute resolution
1912 # for data older than 1 day, 1-hour resolution
1913 # for data older than 2 weeks, 1 day resolution
1914 MONITORING_DATA_AGGREGATION = (
1915 (timedelta(seconds=0), timedelta(minutes=1),),
1916 (timedelta(days=1), timedelta(minutes=60),),
1917 (timedelta(days=14), timedelta(days=1),),
1918 )
1919
1920 CELERY_BEAT_SCHEDULE['collect_metrics'] = {
1921 'task': 'geonode.monitoring.tasks.collect_metrics',
1922 'schedule': 600.0,
1923 }
1924
1925USER_ANALYTICS_ENABLED = ast.literal_eval(os.getenv('USER_ANALYTICS_ENABLED', 'False'))
1926USER_ANALYTICS_GZIP = ast.literal_eval(os.getenv('USER_ANALYTICS_GZIP', 'False'))
1927
1928GEOIP_PATH = os.getenv('GEOIP_PATH', os.path.join(PROJECT_ROOT, 'GeoIPCities.dat'))
1929# -- END Settings for MONITORING plugin