Preparations for the SAML plugin; Fixed caching of main views.

This commit is contained in:
FinnStutzenstein 2018-01-16 16:02:23 +01:00 committed by Emanuel Schütze
parent 39c499dc68
commit 5e089bfcdc
13 changed files with 143 additions and 107 deletions

View File

@ -105,6 +105,7 @@ Core:
- Use native twisted mode for daphne [#3487].
- Save language selection to session storage [#3543]
- Set default of projector resolution to 1220x915 [#2549].
- Preparations for the SAML plugin; Fixed caching of main views [#3535].
Mediafiles:
- Fixed reloading of PDF on page change [#3274].

View File

@ -3,3 +3,5 @@ __description__ = 'Presentation and assembly system'
__version__ = '2.2b2-dev'
__license__ = 'MIT'
__url__ = 'https://openslides.org'
args = None

View File

@ -8,13 +8,13 @@ from typing import Dict # noqa
import django
from django.core.management import call_command, execute_from_command_line
from openslides import __version__ as openslides_version
import openslides
from openslides.utils.main import (
ExceptionArgumentParser,
UnknownCommand,
get_default_settings_path,
get_default_settings_dir,
get_geiss_path,
get_local_settings_path,
get_local_settings_dir,
is_local_installation,
open_browser,
setup_django_settings_module,
@ -43,6 +43,10 @@ def main():
# Check for unknown_args.
if unknown_args:
parser.error('Unknown arguments {}'.format(' '.join(unknown_args)))
# Save arguments, if one wants to access them later.
openslides.args = known_args
# Run a command that is defined here
# These are commands that can not rely on an existing settings
known_args.callback(known_args)
@ -81,7 +85,7 @@ def get_parser():
parser.add_argument(
'--version',
action='version',
version=openslides_version,
version=openslides.__version__,
help='Show version number and exit.')
# Init subparsers
@ -122,10 +126,15 @@ def get_parser():
default='8000',
help='Port to listen on. Default is 8000.')
subcommand_start.add_argument(
'--settings_path',
'--settings_dir',
action='store',
default=None,
help='The used settings file. The file is created, if it does not exist.')
help='The settings directory.')
subcommand_start.add_argument(
'--settings_filename',
action='store',
default='settings.py',
help='The used settings file name. The file is created, if it does not exist.')
subcommand_start.add_argument(
'--local-installation',
action='store_true',
@ -143,10 +152,10 @@ def get_parser():
help=createsettings_help)
subcommand_createsettings.set_defaults(callback=createsettings)
subcommand_createsettings.add_argument(
'--settings_path',
'--settings_dir',
action='store',
default=None,
help='The used settings file. The file is created, even if it exists.')
help='The used settings file directory. All settings files are created, even if they exist.')
subcommand_createsettings.add_argument(
'--local-installation',
action='store_true',
@ -173,16 +182,18 @@ def start(args):
"""
Starts OpenSlides: Runs migrations and runs runserver.
"""
settings_path = args.settings_path
settings_dir = args.settings_dir
settings_filename = args.settings_filename
local_installation = is_local_installation()
if settings_path is None:
if settings_dir is None:
if local_installation:
settings_path = get_local_settings_path()
settings_dir = get_local_settings_dir()
else:
settings_path = get_default_settings_path()
settings_dir = get_default_settings_dir()
# Write settings if it does not exists.
# Write django settings if it does not exists.
settings_path = os.path.join(settings_dir, settings_filename)
if not os.path.isfile(settings_path):
createsettings(args)
@ -254,18 +265,18 @@ def createsettings(args):
"""
Creates settings for OpenSlides.
"""
settings_path = args.settings_path
settings_dir = args.settings_dir
local_installation = is_local_installation()
context = {} # type: Dict[str, str]
if local_installation:
if settings_path is None:
settings_path = get_local_settings_path()
if settings_dir is None:
settings_dir = get_local_settings_dir()
context = {
'openslides_user_data_path': repr(os.path.join(os.getcwd(), 'personal_data', 'var')),
'openslides_user_data_dir': repr(os.path.join(os.getcwd(), 'personal_data', 'var')),
'debug': 'True'}
settings_path = write_settings(settings_path, **context)
settings_path = write_settings(settings_dir, args.settings_filename, **context)
print('Settings created at %s' % settings_path)

View File

@ -595,7 +595,7 @@ angular.module('OpenSlidesApp.core', [
// to be given. If a scope is provided, the schope of this templateHook
// is populated with the given functions/values.
if (hook.template) {
return '<div>' + hook.template + '</div>';
return hook.template;
} else {
return $templateCache.get(hook.templateUrl);
}

View File

@ -23,6 +23,7 @@
<button ng-if="guestAllowed" ng-click="guestLogin()" class="btn btn-default" translate>
Continue as guest
</button>
<template-hook hook-name="loginFormButtons"></template-hook>
</div>
</div>
</form>

View File

@ -23,6 +23,6 @@ urlpatterns = [
# Main entry point for all angular pages.
# Has to be the last entry in the urls.py
url(r'^.*$', views.IndexView.as_view()),
url(r'^.*$', views.IndexView.as_view(), name="index"),
]

View File

@ -96,11 +96,15 @@ class WebclientJavaScriptView(utils_views.View):
AngularJS app for the requested realm (site or projector). Also code
for plugins is appended. The result is not uglified.
"""
cache = {} # type: Dict[str, str]
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.cache = {} # type: Dict[str, str]
self.init_cache('site')
self.init_cache('projector')
if 'site' not in self.cache:
self.init_cache('site')
if 'projector' not in self.cache:
self.init_cache('projector')
def init_cache(self, realm: str) -> None:
angular_modules = [] # type: List[str]

View File

@ -1643,20 +1643,29 @@ angular.module('OpenSlidesApp.users.site', [
}
])
.controller('userMenu', [
'$scope',
.factory('Logout', [
'$http',
'OpenSlides',
'ngDialog',
'UserProfileForm',
'UserPasswordForm',
function($scope, $http, OpenSlides, ngDialog, UserProfileForm, UserPasswordForm) {
$scope.logout = function () {
function ($http, OpenSlides) {
return function () {
$http.post('/users/logout/').then(function (response) {
// Success: User logged out, so reboot OpenSlides.
OpenSlides.reboot();
});
};
}
])
.controller('userMenu', [
'$scope',
'$http',
'ngDialog',
'UserProfileForm',
'UserPasswordForm',
'Logout',
function($scope, $http, ngDialog, UserProfileForm, UserPasswordForm, Logout) {
$scope.logout = Logout;
$scope.editProfile = function () {
ngDialog.open(UserProfileForm.getDialog());
};

View File

@ -60,7 +60,7 @@ def detect_openslides_type() -> str:
return openslides_type
def get_default_settings_path(openslides_type: str=None) -> str:
def get_default_settings_dir(openslides_type: str=None) -> str:
"""
Returns the default settings path according to the OpenSlides type.
@ -74,21 +74,21 @@ def get_default_settings_path(openslides_type: str=None) -> str:
parent_directory = os.environ.get(
'XDG_CONFIG_HOME', os.path.expanduser('~/.config'))
elif openslides_type == WINDOWS_VERSION:
parent_directory = get_win32_app_data_path()
parent_directory = get_win32_app_data_dir()
elif openslides_type == WINDOWS_PORTABLE_VERSION:
parent_directory = get_win32_portable_path()
parent_directory = get_win32_portable_dir()
else:
raise TypeError('%s is not a valid OpenSlides type.' % openslides_type)
return os.path.join(parent_directory, 'openslides', 'settings.py')
return os.path.join(parent_directory, 'openslides')
def get_local_settings_path() -> str:
def get_local_settings_dir() -> str:
"""
Returns the path to a local settings.
On Unix systems: 'personal_data/var/settings.py'
On Unix systems: 'personal_data/var/'
"""
return os.path.join('personal_data', 'var', 'settings.py')
return os.path.join('personal_data', 'var')
def setup_django_settings_module(settings_path: str =None, local_installation: bool=False) -> None:
@ -107,9 +107,10 @@ def setup_django_settings_module(settings_path: str =None, local_installation: b
if settings_path is None:
if local_installation:
settings_path = get_local_settings_path()
settings_dir = get_local_settings_dir()
else:
settings_path = get_default_settings_path()
settings_dir = get_default_settings_dir()
settings_path = os.path.join(settings_dir, 'settings.py')
settings_file = os.path.basename(settings_path)
settings_module_name = ".".join(settings_file.split('.')[:-1])
@ -130,7 +131,7 @@ def setup_django_settings_module(settings_path: str =None, local_installation: b
os.environ[ENVIRONMENT_VARIABLE] = settings_module_name
def get_default_settings_context(user_data_path: str=None) -> Dict[str, str]:
def get_default_settings_context(user_data_dir: str=None) -> Dict[str, str]:
"""
Returns the default context values for the settings template:
'openslides_user_data_path', 'import_function' and 'debug'.
@ -140,45 +141,45 @@ def get_default_settings_context(user_data_path: str=None) -> Dict[str, str]:
# Setup path for user specific data (SQLite3 database, media, ...):
# Take it either from command line or get default path
default_context = {}
if user_data_path:
default_context['openslides_user_data_path'] = repr(user_data_path)
if user_data_dir:
default_context['openslides_user_data_dir'] = repr(user_data_dir)
default_context['import_function'] = ''
else:
openslides_type = detect_openslides_type()
if openslides_type == WINDOWS_PORTABLE_VERSION:
default_context['openslides_user_data_path'] = 'get_win32_portable_user_data_path()'
default_context['import_function'] = 'from openslides.utils.main import get_win32_portable_user_data_path'
default_context['openslides_user_data_dir'] = 'get_win32_portable_user_data_dir()'
default_context['import_function'] = 'from openslides.utils.main import get_win32_portable_user_data_dir'
else:
path = get_default_user_data_path(openslides_type)
default_context['openslides_user_data_path'] = repr(os.path.join(path, 'openslides'))
data_dir = get_default_user_data_dir(openslides_type)
default_context['openslides_user_data_dir'] = repr(os.path.join(data_dir, 'openslides'))
default_context['import_function'] = ''
default_context['debug'] = 'False'
return default_context
def get_default_user_data_path(openslides_type: str) -> str:
def get_default_user_data_dir(openslides_type: str) -> str:
"""
Returns the default path for user specific data according to the OpenSlides
Returns the default directory for user specific data according to the OpenSlides
type.
The argument 'openslides_type' has to be one of the three types mentioned
in openslides.utils.main.
"""
if openslides_type == UNIX_VERSION:
default_user_data_path = os.environ.get(
default_user_data_dir = os.environ.get(
'XDG_DATA_HOME', os.path.expanduser('~/.local/share'))
elif openslides_type == WINDOWS_VERSION:
default_user_data_path = get_win32_app_data_path()
default_user_data_dir = get_win32_app_data_dir()
elif openslides_type == WINDOWS_PORTABLE_VERSION:
default_user_data_path = get_win32_portable_path()
default_user_data_dir = get_win32_portable_dir()
else:
raise TypeError('%s is not a valid OpenSlides type.' % openslides_type)
return default_user_data_path
return default_user_data_dir
def get_win32_app_data_path() -> str:
def get_win32_app_data_dir() -> str:
"""
Returns the path to Windows' AppData directory.
Returns the directory of Windows' AppData directory.
"""
shell32 = ctypes.WinDLL("shell32.dll")
SHGetFolderPath = shell32.SHGetFolderPathW
@ -199,16 +200,16 @@ def get_win32_app_data_path() -> str:
return buf.value
def get_win32_portable_path() -> str:
def get_win32_portable_dir() -> str:
"""
Returns the path to the Windows portable version.
Returns the directory of the Windows portable version.
"""
# NOTE: sys.executable will be the path to openslides.exe
# since it is essentially a small wrapper that embeds the
# python interpreter
portable_path = os.path.dirname(os.path.abspath(sys.executable))
portable_dir = os.path.dirname(os.path.abspath(sys.executable))
try:
fd, test_file = tempfile.mkstemp(dir=portable_path)
fd, test_file = tempfile.mkstemp(dir=portable_dir)
except OSError:
raise PortableDirNotWritable(
'Portable directory is not writeable. '
@ -216,25 +217,26 @@ def get_win32_portable_path() -> str:
else:
os.close(fd)
os.unlink(test_file)
return portable_path
return portable_dir
def get_win32_portable_user_data_path() -> str:
def get_win32_portable_user_data_dir() -> str:
"""
Returns the user data path to the Windows portable version.
Returns the user data directory to the Windows portable version.
"""
return os.path.join(get_win32_portable_path(), 'openslides')
return os.path.join(get_win32_portable_dir(), 'openslides')
def write_settings(settings_path: str=None, template: str=None, **context: str) -> str:
def write_settings(settings_dir: str=None, settings_filename: str='settings.py', template: str=None, **context: str) -> str:
"""
Creates the settings file at the given path using the given values for the
Creates the settings file at the given dir using the given values for the
file template.
Retuns the path to the created settings.
"""
if settings_path is None:
settings_path = get_default_settings_path()
if settings_dir is None:
settings_dir = get_default_settings_dir()
settings_path = os.path.join(settings_dir, settings_filename)
if template is None:
with open(os.path.join(os.path.dirname(__file__), 'settings.py.tpl')) as template_file:
@ -248,16 +250,17 @@ def write_settings(settings_path: str=None, template: str=None, **context: str)
context.setdefault(key, value)
content = template % context
settings_module = os.path.realpath(os.path.dirname(settings_path))
settings_module = os.path.realpath(settings_dir)
if not os.path.exists(settings_module):
os.makedirs(settings_module)
with open(settings_path, 'w') as settings_file:
settings_file.write(content)
if context['openslides_user_data_path'] == 'get_win32_portable_user_data_path()':
openslides_user_data_path = get_win32_portable_user_data_path()
if context['openslides_user_data_dir'] == 'get_win32_portable_user_data_dir()':
openslides_user_data_dir = get_win32_portable_user_data_dir()
else:
openslides_user_data_path = context['openslides_user_data_path'].strip("'")
os.makedirs(os.path.join(openslides_user_data_path, 'static'), exist_ok=True)
openslides_user_data_dir = context['openslides_user_data_dir'].strip("'")
os.makedirs(os.path.join(openslides_user_data_dir, 'static'), exist_ok=True)
return os.path.realpath(settings_path)
@ -329,9 +332,9 @@ def get_geiss_path() -> str:
Returns the path and file to the Geiss binary.
"""
from django.conf import settings
download_path = getattr(settings, 'OPENSLIDES_USER_DATA_PATH', '')
download_dir = getattr(settings, 'OPENSLIDES_USER_DATA_PATH', '')
bin_name = 'geiss.exe' if is_windows() else 'geiss'
return os.path.join(download_path, bin_name)
return os.path.join(download_dir, bin_name)
def is_windows() -> bool:

View File

@ -10,7 +10,7 @@ from pkg_resources import iter_entry_points
from openslides.utils.main import (
WINDOWS_PORTABLE_VERSION,
detect_openslides_type,
get_win32_portable_user_data_path,
get_win32_portable_user_data_dir,
)
@ -25,12 +25,12 @@ def collect_plugins_from_entry_points() -> Tuple[str, ...]:
return tuple(entry_point.module_name for entry_point in iter_entry_points('openslides_plugins'))
def collect_plugins_from_path(path: str) -> Tuple[str, ...]:
def collect_plugins_from_dir(plugin_dir: str) -> Tuple[str, ...]:
"""
Collects all modules/packages in the given `path` and returns a tuple
Collects all modules/packages in the given `plugin_dir` and returns a tuple
of their names.
"""
return tuple(x[1] for x in pkgutil.iter_modules([path]))
return tuple(x[1] for x in pkgutil.iter_modules([plugin_dir]))
def collect_plugins() -> Tuple[str, ...]:
@ -42,11 +42,11 @@ def collect_plugins() -> Tuple[str, ...]:
# Collect plugins in plugins/ directory of portable.
if detect_openslides_type() == WINDOWS_PORTABLE_VERSION:
plugins_path = os.path.join(
get_win32_portable_user_data_path(), 'plugins')
if plugins_path not in sys.path:
sys.path.append(plugins_path)
collected_plugins += collect_plugins_from_path(plugins_path)
plugins_dir = os.path.join(
get_win32_portable_user_data_dir(), 'plugins')
if plugins_dir not in sys.path:
sys.path.append(plugins_dir)
collected_plugins += collect_plugins_from_dir(plugins_dir)
return collected_plugins

View File

@ -12,9 +12,9 @@ import os
from openslides.global_settings import *
%(import_function)s
# Path to the directory for user specific data files
# The directory for user specific data files
OPENSLIDES_USER_DATA_PATH = %(openslides_user_data_path)s
OPENSLIDES_USER_DATA_DIR = %(openslides_user_data_dir)s
# OpenSlides plugins
@ -70,7 +70,7 @@ EMAIL_HOST_PASSWORD = ''
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(OPENSLIDES_USER_DATA_PATH, 'db.sqlite3'),
'NAME': os.path.join(OPENSLIDES_USER_DATA_DIR, 'db.sqlite3'),
}
}
@ -133,15 +133,15 @@ TIME_ZONE = 'Europe/Berlin'
# 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
STATICFILES_DIRS = [os.path.join(OPENSLIDES_USER_DATA_DIR, 'static')] + STATICFILES_DIRS
STATIC_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, 'collected-static')
STATIC_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, 'collected-static')
# Files
# https://docs.djangoproject.com/en/1.10/topics/files/
MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, 'media', '')
MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_DIR, 'media', '')
# Password validation

View File

@ -53,8 +53,12 @@ class APIView(_APIView):
class TemplateView(View):
"""
A view to serve a single cached template file. Subclasses have to provide 'template_name'.
The state dict is used to cache the template. The state variable is static, but the object ID
is not allowed to change. So the State has to be saved in this dict. Search for 'Borg design
pattern' for more information.
"""
template_name = None # type: str
state = {} # type: Dict[str, str]
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
@ -62,8 +66,9 @@ class TemplateView(View):
if self.template_name is None:
raise ImproperlyConfigured("'template_name' is not provided")
with open(finders.find(self.template_name)) as template:
self.template = template.read()
if 'template' not in self.state:
with open(finders.find(self.template_name)) as template:
self.state['template'] = template.read()
def get(self, *args: Any, **kwargs: Any) -> HttpResponse:
return HttpResponse(self.template)
return HttpResponse(self.state['template'])

View File

@ -37,25 +37,25 @@ class TestFunctions(TestCase):
@patch('openslides.utils.main.detect_openslides_type')
@patch('openslides.utils.main.os.path.expanduser')
def test_get_default_settings_path_unix(self, mock_expanduser, mock_detect):
def test_get_default_settings_dir_unix(self, mock_expanduser, mock_detect):
mock_expanduser.return_value = '/home/test/.config'
self.assertEqual(main.get_default_settings_path(main.UNIX_VERSION),
'/home/test/.config/openslides/settings.py')
self.assertEqual(main.get_default_settings_dir(main.UNIX_VERSION),
'/home/test/.config/openslides')
@patch('openslides.utils.main.get_win32_app_data_path')
def test_get_default_settings_path_win(self, mock_win):
@patch('openslides.utils.main.get_win32_app_data_dir')
def test_get_default_settings_dir_win(self, mock_win):
mock_win.return_value = 'win32'
self.assertEqual(main.get_default_settings_path(main.WINDOWS_VERSION),
'win32/openslides/settings.py')
self.assertEqual(main.get_default_settings_dir(main.WINDOWS_VERSION),
'win32/openslides')
@patch('openslides.utils.main.get_win32_portable_path')
def test_get_default_settings_path_portable(self, mock_portable):
@patch('openslides.utils.main.get_win32_portable_dir')
def test_get_default_settings_dir_portable(self, mock_portable):
mock_portable.return_value = 'portable'
self.assertEqual(main.get_default_settings_path(main.WINDOWS_PORTABLE_VERSION),
'portable/openslides/settings.py')
self.assertEqual(main.get_default_settings_dir(main.WINDOWS_PORTABLE_VERSION),
'portable/openslides')
def test_get_local_settings_path(self):
self.assertEqual(main.get_local_settings_path(), os.sep.join(('personal_data', 'var', 'settings.py')))
def test_get_local_settings_dir(self):
self.assertEqual(main.get_local_settings_dir(), os.sep.join(('personal_data', 'var')))
def test_setup_django_settings_module(self):
main.setup_django_settings_module('test_dir_dhvnghfjdh456fzheg2f/test_path_bngjdhc756dzwncshdfnx.py')
@ -67,10 +67,10 @@ class TestFunctions(TestCase):
def test_get_default_settings_context_portable(self, detect_mock):
detect_mock.return_value = main.WINDOWS_PORTABLE_VERSION
context = main.get_default_settings_context()
self.assertEqual(context['openslides_user_data_path'], 'get_win32_portable_user_data_path()')
self.assertEqual(context['openslides_user_data_dir'], 'get_win32_portable_user_data_dir()')
def test_get_default_user_data_path(self):
self.assertIn(os.path.join('.local', 'share'), main.get_default_user_data_path(main.UNIX_VERSION))
def test_get_default_user_data_dir(self):
self.assertIn(os.path.join('.local', 'share'), main.get_default_user_data_dir(main.UNIX_VERSION))
@patch('openslides.utils.main.threading.Thread')
@patch('openslides.utils.main.time')