diff --git a/CHANGELOG b/CHANGELOG index 81a48f0ee..8cefbe113 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,6 +34,7 @@ Users: - Added new matrix-interface for managing groups and their permissions. Other: +- Django 1.10 is now supported. Dropped support for Django 1.8 and 1.9. - Removed config cache to support multiple threads or processes. - Fixed bug, that the last change of a config value was not send via autoupdate. - Added template hooks for plugins (in item detail view and motion poll form). diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index 23483d59e..8e1564187 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -1,11 +1,11 @@ from collections import defaultdict -from datetime import datetime from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models, transaction +from django.utils import timezone from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy @@ -415,7 +415,7 @@ class Speaker(RESTModelMixin, models.Model): else: current_speaker.end_speech() self.weight = None - self.begin_time = datetime.now() + self.begin_time = timezone.now() self.save() if config['agenda_couple_countdown_and_speakers']: Countdown.control(action='reset') @@ -425,7 +425,7 @@ class Speaker(RESTModelMixin, models.Model): """ The speech is finished. Set the time to now. """ - self.end_time = datetime.now() + self.end_time = timezone.now() self.save() if config['agenda_couple_countdown_and_speakers']: Countdown.control(action='stop') diff --git a/openslides/core/views.py b/openslides/core/views.py index 7669a5a02..aa5db8258 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -778,7 +778,7 @@ class MediaEncoder(utils_views.APIView): :return: dictionary with the string representation (content) and the name of the file for the pdfMake.vfs structure """ - path = os.path.join(settings.SITE_ROOT, 'static/fonts', os.path.basename(file_path)) + path = os.path.join(settings.MODULE_DIR, 'static', 'fonts', os.path.basename(file_path)) try: with open(path, "rb") as file: string_representation = "{}".format(base64.b64encode(file.read()).decode()) diff --git a/openslides/global_settings.py b/openslides/global_settings.py index 4a85eecf2..3338817cb 100644 --- a/openslides/global_settings.py +++ b/openslides/global_settings.py @@ -2,16 +2,55 @@ import os from openslides.utils.plugins import collect_plugins -SITE_ROOT = os.path.realpath(os.path.dirname(__file__)) +MODULE_DIR = os.path.realpath(os.path.dirname(os.path.abspath(__file__))) -AUTH_USER_MODEL = 'users.User' -AUTHENTICATION_BACKENDS = ('openslides.users.auth.CustomizedModelBackend',) +# Application definition -# Uses a db session backend, that saves the user_id directly in the db -SESSION_ENGINE = 'openslides.core.session_backend' +INSTALLED_APPS = [ + 'openslides.core', + 'openslides.users', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.staticfiles', + 'rest_framework', + 'channels', + 'openslides.agenda', + 'openslides.motions', + 'openslides.assignments', + 'openslides.mediafiles', +] -SESSION_COOKIE_NAME = 'OpenSlidesSessionID' +INSTALLED_PLUGINS = collect_plugins() # Adds all automaticly collected plugins + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'openslides.urls' + +ALLOWED_HOSTS = ['*'] + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ + +LANGUAGE_CODE = 'en' LANGUAGES = ( ('en', 'English'), @@ -22,83 +61,44 @@ LANGUAGES = ( ('cs', 'Český'), ) -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. +TIME_ZONE = 'UTC' + 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 -LOCALE_PATHS = ( - os.path.join(SITE_ROOT, 'locale'), -) +USE_TZ = True -# 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/' +LOCALE_PATHS = [ + os.path.join(MODULE_DIR, 'locale'), +] -# Absolute path to the directory that holds static media from ``collectstatic`` -# Example: "/home/media/static.lawrence.com/" -STATIC_ROOT = os.path.join(SITE_ROOT, '../collected-site-static') -# URL that handles the media served from STATIC_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://static.lawrence.com", "http://example.com/static/" +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.10/howto/static-files/ + STATIC_URL = '/static/' -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) +STATIC_ROOT = os.path.join(MODULE_DIR, '..', 'collected-static') STATICFILES_DIRS = [ - os.path.join(SITE_ROOT, 'static')] - -MIDDLEWARE_CLASSES = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'openslides.users.auth.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -) - -ROOT_URLCONF = 'openslides.urls' - -INSTALLED_APPS = ( - 'openslides.core', - 'openslides.users', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.humanize', - 'rest_framework', - 'channels', - 'openslides.poll', # TODO: try to remove this line - 'openslides.agenda', - 'openslides.motions', - 'openslides.assignments', - 'openslides.mediafiles', -) + os.path.join(MODULE_DIR, 'static'), +] -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'openslidecache' - } -} +# Sessions and user authentication +# https://docs.djangoproject.com/en/1.10/topics/http/sessions/ +# https://docs.djangoproject.com/en/1.10/topics/auth/ -# Hosts/domain names that are valid for this site; required if DEBUG is False -# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts -ALLOWED_HOSTS = ['*'] +AUTH_USER_MODEL = 'users.User' -# Adds all automaticly collected plugins -INSTALLED_PLUGINS = collect_plugins() +AUTHENTICATION_BACKENDS = [ + 'openslides.users.auth.CustomizedModelBackend', +] + +SESSION_ENGINE = 'openslides.core.session_backend' + +SESSION_COOKIE_NAME = 'OpenSlidesSessionID' PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', @@ -106,12 +106,30 @@ PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', - 'django.contrib.auth.hashers.MD5PasswordHasher', # MD5 is only used for inital passwords. + 'django.contrib.auth.hashers.MD5PasswordHasher', # MD5 is only used for initial passwords. ] -TEST_RUNNER = 'openslides.utils.test.OpenSlidesDiscoverRunner' -# Config for the REST Framework +# Files +# https://docs.djangoproject.com/en/1.10/topics/files/ + +MEDIA_URL = '/media/' + + +# Cache +# https://docs.djangoproject.com/en/1.10/topics/cache/ + +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'openslides-cache' + } +} + + +# Django REST framework +# http://www.django-rest-framework.org/api-guide/settings/ + REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', @@ -119,7 +137,10 @@ REST_FRAMEWORK = { ) } -# Config for channels + +# Django Channels +# http://channels.readthedocs.io/en/latest/ + CHANNEL_LAYERS = { 'default': { 'BACKEND': 'asgiref.inmemory.ChannelLayer', diff --git a/openslides/utils/pdf.py b/openslides/utils/pdf.py index 8bb030e1a..921f31002 100644 --- a/openslides/utils/pdf.py +++ b/openslides/utils/pdf.py @@ -15,13 +15,13 @@ from openslides.core.config import config # register new truetype fonts pdfmetrics.registerFont(TTFont( - 'Ubuntu', path_join(settings.SITE_ROOT, 'core/static/fonts/Ubuntu-R.ttf'))) + 'Ubuntu', path_join(settings.MODULE_DIR, 'core', 'static', 'fonts', 'Ubuntu-R.ttf'))) pdfmetrics.registerFont(TTFont( - 'Ubuntu-Bold', path_join(settings.SITE_ROOT, 'core/static/fonts/Ubuntu-B.ttf'))) + 'Ubuntu-Bold', path_join(settings.MODULE_DIR, 'core', 'static', 'fonts', 'Ubuntu-B.ttf'))) pdfmetrics.registerFont(TTFont( - 'Ubuntu-Italic', path_join(settings.SITE_ROOT, 'core/static/fonts/Ubuntu-RI.ttf'))) + 'Ubuntu-Italic', path_join(settings.MODULE_DIR, 'core', 'static', 'fonts', 'Ubuntu-RI.ttf'))) pdfmetrics.registerFont(TTFont( - 'circlefont', path_join(settings.SITE_ROOT, 'core/static/fonts/circle.ttf'))) + 'circlefont', path_join(settings.MODULE_DIR, 'core', 'static', 'fonts', 'circle.ttf'))) pdfmetrics.registerFontFamily('Ubuntu', normal='Ubuntu', bold='Ubuntu-Bold', italic='Ubuntu-Italic') diff --git a/openslides/utils/settings.py.tpl b/openslides/utils/settings.py.tpl index 80e7d9f2d..eb8da2300 100644 --- a/openslides/utils/settings.py.tpl +++ b/openslides/utils/settings.py.tpl @@ -1,11 +1,11 @@ """ -Settings file for OpenSlides +Settings file for OpenSlides. For more information on this file, see -https://docs.djangoproject.com/en/1.9/topics/settings/ +https://docs.djangoproject.com/en/1.10/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.9/ref/settings/ +https://docs.djangoproject.com/en/1.10/ref/settings/ """ import os @@ -17,18 +17,8 @@ from openslides.global_settings import * OPENSLIDES_USER_DATA_PATH = %(openslides_user_data_path)s -# SECURITY WARNING: Keep the secret key used in production secret! - -SECRET_KEY = %(secret_key)r - - -# Use 'DEBUG = True' to get more details for server errors. -# SECURITY WARNING: Don't run with debug turned on in production! - -DEBUG = %(debug)s - - # OpenSlides plugins + # Add plugins to this list (see example entry in comment). INSTALLED_PLUGINS += ( @@ -38,26 +28,44 @@ INSTALLED_PLUGINS += ( INSTALLED_APPS += INSTALLED_PLUGINS +# Important settings for production use +# https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ + +# SECURITY WARNING: Keep the secret key used in production secret! + +SECRET_KEY = %(secret_key)r + +# Use 'DEBUG = True' to get more details for server errors. +# SECURITY WARNING: Don't run with debug turned on in production! + +DEBUG = %(debug)s + + # Database -# Change this to use MySQL or PostgreSQL. -# See https://docs.djangoproject.com/en/1.9/ref/settings/#databases +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +# Change this setting to use e. g. PostgreSQL or MySQL. DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(OPENSLIDES_USER_DATA_PATH, 'database.sqlite') + 'NAME': os.path.join(OPENSLIDES_USER_DATA_PATH, 'db.sqlite3'), } } -# Big Mode -# Uncomment the following lines to activate redis as channel and cache backend. -# You have to install a redis server and the python packages asgi_redis and -# django-redis for this to work. -# See https://channels.readthedocs.io/en/latest/backends.html#redis -# https://niwinz.github.io/django-redis/latest/#_user_guide +# Django Channels + +# Unless you have only a small assembly uncomment the following lines to +# activate Redis as backend for Django Channels and Cache. You have to install +# a Redis server and the python packages asgi_redis and django-redis. + +# https://channels.readthedocs.io/en/latest/backends.html#redis # CHANNEL_LAYERS['default']['BACKEND'] = 'asgi_redis.RedisChannelLayer' + +# https://niwinz.github.io/django-redis/latest/#_user_guide + # CACHES = { # "default": { # "BACKEND": "django_redis.cache.RedisCache", @@ -69,16 +77,25 @@ DATABASES = { # } -# Some other settings +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ TIME_ZONE = 'Europe/Berlin' -MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, 'media', '') -TEMPLATE_DIRS = ( - os.path.join(OPENSLIDES_USER_DATA_PATH, 'templates'), -) +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.10/howto/static-files/ STATICFILES_DIRS = [os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')] + STATICFILES_DIRS + +# Files +# https://docs.djangoproject.com/en/1.10/topics/files/ + +MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, 'media', '') + + +# Whoosh search library +# https://whoosh.readthedocs.io/en/latest/ + SEARCH_INDEX = os.path.join(OPENSLIDES_USER_DATA_PATH, 'search_index') diff --git a/requirements_production.txt b/requirements_production.txt index f81f60541..61b992e0a 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -1,5 +1,5 @@ # Requirements for OpenSlides in production in alphabetical order -Django>=1.9,<1.11,!=1.10.0 +Django>=1.10.1,<1.11 beautifulsoup4>=4.5,<4.6 channels>=0.15,<1.0 djangorestframework>=3.4,<3.5 diff --git a/tests/integration/motions/test_viewset.py b/tests/integration/motions/test_viewset.py index a1fac3d5f..46d459d9b 100644 --- a/tests/integration/motions/test_viewset.py +++ b/tests/integration/motions/test_viewset.py @@ -149,7 +149,7 @@ class RetrieveMotion(TestCase): self.motion.create_poll() def test_number_of_queries(self): - with self.assertNumQueries(17): + with self.assertNumQueries(16): self.client.get(reverse('motion-detail', args=[self.motion.pk])) diff --git a/tests/old/settings.py b/tests/old/settings.py index e50cb34cc..73aab0e76 100644 --- a/tests/old/settings.py +++ b/tests/old/settings.py @@ -1,19 +1,19 @@ """ -Settings file for OpenSlides' tests +Settings file for OpenSlides' tests. """ import os from openslides.global_settings import * # noqa + # Path to the directory for user specific data files -OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(__file__)) +OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(os.path.abspath(__file__))) -# SECURITY WARNING: Keep the secret key used in production secret! -SECRET_KEY = 'secret' # OpenSlides plugins + # Add plugins to this list. INSTALLED_PLUGINS += ( # noqa @@ -23,9 +23,18 @@ INSTALLED_PLUGINS += ( # noqa INSTALLED_APPS += INSTALLED_PLUGINS # noqa +# Important settings for production use +# https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ + +SECRET_KEY = 'secret' + +DEBUG = False + + # Database -# Change this to use MySQL or PostgreSQL. -# See https://docs.djangoproject.com/en/1.7/ref/settings/#databases +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +# Change this setting to use e. g. PostgreSQL or MySQL. DATABASES = { 'default': { @@ -34,20 +43,36 @@ DATABASES = { } -# Some other settings +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ TIME_ZONE = 'Europe/Berlin' -MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.10/howto/static-files/ STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')) # noqa + +# Files +# https://docs.djangoproject.com/en/1.10/topics/files/ + +MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') + + +# Whoosh search library +# https://whoosh.readthedocs.io/en/latest/ + SEARCH_INDEX = 'ram' -# Special test settings +# Special settings only for testing + +TEST_RUNNER = 'openslides.utils.test.OpenSlidesDiscoverRunner' + # Use a faster password hasher. -PASSWORD_HASHERS = ( +PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', -) +] diff --git a/tests/settings.py b/tests/settings.py index aad6b237b..9454ca91d 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,19 +1,19 @@ """ -Settings file for OpenSlides' tests +Settings file for OpenSlides' tests. """ import os from openslides.global_settings import * # noqa + # Path to the directory for user specific data files -OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(__file__)) +OPENSLIDES_USER_DATA_PATH = os.path.realpath(os.path.dirname(os.path.abspath(__file__))) -# SECURITY WARNING: Keep the secret key used in production secret! -SECRET_KEY = 'secret' # OpenSlides plugins + # Add plugins to this list. INSTALLED_PLUGINS += ( # noqa @@ -23,9 +23,18 @@ INSTALLED_PLUGINS += ( # noqa INSTALLED_APPS += INSTALLED_PLUGINS # noqa +# Important settings for production use +# https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ + +SECRET_KEY = 'secret' + +DEBUG = False + + # Database -# Change this to use MySQL or PostgreSQL. -# See https://docs.djangoproject.com/en/1.7/ref/settings/#databases +# https://docs.djangoproject.com/en/1.10/ref/settings/#databases + +# Change this setting to use e. g. PostgreSQL or MySQL. DATABASES = { 'default': { @@ -34,20 +43,36 @@ DATABASES = { } -# Some other settings +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ TIME_ZONE = 'Europe/Berlin' -MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.10/howto/static-files/ STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, 'static')) # noqa + +# Files +# https://docs.djangoproject.com/en/1.10/topics/files/ + +MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, '') + + +# Whoosh search library +# https://whoosh.readthedocs.io/en/latest/ + SEARCH_INDEX = 'ram' -# Special test settings +# Special settings only for testing + +TEST_RUNNER = 'openslides.utils.test.OpenSlidesDiscoverRunner' + # Use a faster password hasher. -PASSWORD_HASHERS = ( +PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', -) +]