Some changes in __main__.py and utils.main and other.

Reinsert --no-browser option.

Move some code to utils.main.

Some other small style changes.

Fix merge conflict misstakes.

Change settings behavior. Add tests.

Fix rebase problems.
This commit is contained in:
Norman Jäckel 2013-10-13 17:17:56 +02:00
parent 21a1d77e29
commit a1ad1e4c49
6 changed files with 267 additions and 159 deletions

View File

@ -15,17 +15,15 @@ import threading
import wx import wx
import openslides import openslides
from openslides.utils.main import ( from openslides.utils.main import (
filesystem2unicode,
is_portable,
detect_openslides_type, detect_openslides_type,
filesystem2unicode,
get_default_user_data_path,
get_port,
get_win32_portable_path, get_win32_portable_path,
) )
from openslides.__main__ import (
get_port,
get_default_settings_path
)
# NOTE: djangos translation module can't be used here since it requires # NOTE: djangos translation module can't be used here since it requires
# a defined settings module # a defined settings module
@ -346,12 +344,14 @@ class MainWindow(wx.Frame):
self.SetIcons(icons) self.SetIcons(icons)
self.server_running = False self.server_running = False
# Set path for gui settings to default user data according to the
# OpenSlides type. This does not depend on any argument the user might
# type in.
openslides_type = detect_openslides_type() openslides_type = detect_openslides_type()
# XXX: this works, but I'd prefer keeping get_user_config_path default_user_data_path = get_default_user_data_path(openslides_type)
# it was much clearer what path was intended IMHO ...
self.gui_settings_path = os.path.join( self.gui_settings_path = os.path.join(
os.path.dirname(get_default_settings_path(openslides_type)), default_user_data_path, 'openslides', 'gui_settings.json')
"gui_settings.json")
self.backupdb_enabled = False self.backupdb_enabled = False
self.backupdb_destination = "" self.backupdb_destination = ""
@ -633,9 +633,8 @@ class MainWindow(wx.Frame):
else: else:
args = ["--address", self._host, "--port", self._port] args = ["--address", self._host, "--port", self._port]
# XXX: --no-browser is missing if not self.cb_start_browser.GetValue():
#if not self.cb_start_browser.GetValue(): args.append("--no-browser")
# args.append("--no-browser")
self.server_running = True self.server_running = True
self.cmd_run_ctrl.run_command("start", *args) self.cmd_run_ctrl.run_command("start", *args)

View File

@ -9,7 +9,6 @@
import sys import sys
from openslides.__main__ import main from openslides.__main__ import main

View File

@ -12,28 +12,29 @@
import argparse import argparse
import base64 import base64
import imp
import os import os
import shutil import shutil
import socket
import sys import sys
import time import time
import threading import threading
import webbrowser import webbrowser
from django.conf import ENVIRONMENT_VARIABLE from django.conf import ENVIRONMENT_VARIABLE
from django.core.exceptions import ImproperlyConfigured
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
from openslides import get_version from openslides import get_version
from openslides.utils.tornado_webserver import run_tornado
from openslides.utils.main import ( from openslides.utils.main import (
filesystem2unicode, filesystem2unicode,
detect_openslides_type, detect_openslides_type,
get_default_user_data_path,
get_port,
get_win32_app_data_path, get_win32_app_data_path,
get_win32_portable_path, get_win32_portable_path,
UNIX_VERSION, UNIX_VERSION,
WINDOWS_VERSION, WINDOWS_VERSION,
WINDOWS_PORTABLE_VERSION) WINDOWS_PORTABLE_VERSION)
from openslides.utils.tornado_webserver import run_tornado
SETTINGS_TEMPLATE = """# -*- coding: utf-8 -*- SETTINGS_TEMPLATE = """# -*- coding: utf-8 -*-
@ -75,6 +76,9 @@ INSTALLED_APPS += INSTALLED_PLUGINS
# Absolute path to the directory that holds media. # Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/" # Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = %(media_path_value)s MEDIA_ROOT = %(media_path_value)s
# Path to Whoosh search index
HAYSTACK_CONNECTIONS['default']['PATH'] = %(whoosh_index_path_value)s
""" """
@ -86,25 +90,35 @@ def main():
args = parse_args() args = parse_args()
# Setup settings path: Take it either from command line or get default path # Setup settings path: Take it either from command line or get default path
if not hasattr(args, 'settings') or not args.settings: if hasattr(args, 'settings') and args.settings:
settings = args.settings
setup_django_settings_module(settings)
else:
if ENVIRONMENT_VARIABLE not in os.environ:
openslides_type = detect_openslides_type() openslides_type = detect_openslides_type()
args.settings = get_default_settings_path(openslides_type) settings = get_default_settings_path(openslides_type)
setup_django_settings_module(settings)
else:
# The environment variable is set, so we do not need to process
# anything more here.
settings = None
# Create settings if if still does not exist. # Create settings if if still does not exist.
if not os.path.exists(args.settings): if settings and not os.path.exists(settings):
# Setup path for local data (SQLite3 database, media, search index, ...): # Setup path for user specific data (SQLite3 database, media, search index, ...):
# Take it either from command line or get default path # Take it either from command line or get default path
if not hasattr(args, 'localpath') or not args.localpath: if hasattr(args, 'user_data_path') and args.user_data_path:
openslides_type = detect_openslides_type() user_data_path_values = get_user_data_path_values(
args.localpath = get_default_local_path(openslides_type) user_data_path=args.user_data_path,
localpath_values = get_localpath_values(localpath=args.localpath, default=True, openslides_type=openslides_type) default=False)
else: else:
localpath_values = get_localpath_values(localpath=args.localpath, default=False) openslides_type = detect_openslides_type()
create_settings(args.settings, localpath_values) args.user_data_path = get_default_user_data_path(openslides_type)
user_data_path_values = get_user_data_path_values(
# Setup DJANGO_SETTINGS_MODULE environment variable user_data_path=args.user_data_path,
if 'DJANGO_SETTINGS_MODULE' not in os.environ: default=True,
setup_django_settings_module(args.settings) openslides_type=openslides_type)
create_settings(settings, user_data_path_values)
# Process the subcommand's callback # Process the subcommand's callback
return args.callback(args) return args.callback(args)
@ -118,62 +132,48 @@ def parse_args():
if len(sys.argv) == 1: if len(sys.argv) == 1:
sys.argv.append('start') sys.argv.append('start')
# Init parser
description = 'Start script for OpenSlides.' description = 'Start script for OpenSlides.'
if 'manage.py' not in sys.argv[0]: if 'manage.py' not in sys.argv[0]:
description += (' If it is called without any argument, this will be ' description += (' If it is called without any argument, this will be '
'treated as if it is called with the "start" subcommand. ' 'treated as if it is called with the "start" subcommand. '
'That means OpenSlides will setup settings and database, ' 'That means OpenSlides will setup default settings and '
'start the tornado webserver, launch the default web ' 'database, start the tornado webserver, launch the '
'browser and open the webinterface.') 'default web browser and open the webinterface.')
parser = argparse.ArgumentParser(description=description) parser = argparse.ArgumentParser(description=description)
# Add version argument
parser.add_argument( parser.add_argument(
'--version', '--version',
action='version', action='version',
version=get_version(), version=get_version(),
help='Show version number and exit.') help='Show version number and exit.')
# Init subparsers
subparsers = parser.add_subparsers( subparsers = parser.add_subparsers(
dest='subcommand', dest='subcommand',
title='Available subcommands', title='Available subcommands',
description="Type 'python %s <subcommand> --help' for help on a specific subcommand." % parser.prog, description="Type 'python %s <subcommand> --help' for help on a "
"specific subcommand." % parser.prog,
help='You can choose only one subcommand at once.') help='You can choose only one subcommand at once.')
settings_args, settings_kwargs = (
('-s', '--settings'),
dict(help='Path to settings file.'))
localpath_args, localpath_kwargs = (
('-l', '--localpath'),
dict(help='Path to the directory for local files like SQLite3 database, '
'uploaded media and search index. This is only used, when a new '
'settings file is created.'))
address_args, address_kwargs = (
('-a', '--address',),
dict(default='0.0.0.0', help='IP address to listen on. Default is %(default)s.'))
port_args, port_kwargs = (
('-p', '--port'),
dict(type=int, default=80, help='Port to listen on. Default as admin or root is %(default)d, else 8000.'))
# Subcommand start # Subcommand start
subcommand_start = subparsers.add_parser( subcommand_start = subparsers.add_parser(
'start', 'start',
help='Setup settings and database, start tornado webserver, launch the ' help='Setup settings and database, start tornado webserver, launch the '
'default web browser and open the webinterface.') 'default web browser and open the webinterface.')
subcommand_start.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_start, ('settings', 'user_data_path', 'address', 'port'))
subcommand_start.add_argument(*localpath_args, **localpath_kwargs) subcommand_start.add_argument(
subcommand_start.add_argument(*address_args, **address_kwargs) '--no-browser',
subcommand_start.add_argument(*port_args, **port_kwargs) action='store_true',
help='Do not launch the default web browser.')
subcommand_start.set_defaults(callback=start) subcommand_start.set_defaults(callback=start)
# Subcommand runserver # Subcommand runserver
subcommand_runserver = subparsers.add_parser( subcommand_runserver = subparsers.add_parser(
'runserver', 'runserver',
help='Run OpenSlides using tornado webserver.') help='Run OpenSlides using tornado webserver.')
subcommand_runserver.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_runserver, ('settings', 'user_data_path', 'address', 'port'))
subcommand_runserver.add_argument(*localpath_args, **localpath_kwargs)
subcommand_runserver.add_argument(*address_args, **address_kwargs)
subcommand_runserver.add_argument(*port_args, **port_kwargs)
subcommand_runserver.add_argument( subcommand_runserver.add_argument(
'--start-browser', '--start-browser',
action='store_true', action='store_true',
@ -188,24 +188,21 @@ def parse_args():
subcommand_syncdb = subparsers.add_parser( subcommand_syncdb = subparsers.add_parser(
'syncdb', 'syncdb',
help='Create or update database tables.') help='Create or update database tables.')
subcommand_syncdb.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_syncdb, ('settings', 'user_data_path'))
subcommand_syncdb.add_argument(*localpath_args, **localpath_kwargs)
subcommand_syncdb.set_defaults(callback=syncdb) subcommand_syncdb.set_defaults(callback=syncdb)
# Subcommand createsuperuser # Subcommand createsuperuser
subcommand_createsuperuser = subparsers.add_parser( subcommand_createsuperuser = subparsers.add_parser(
'createsuperuser', 'createsuperuser',
help="Make sure the user 'admin' exists and uses 'admin' as password.") help="Make sure the user 'admin' exists and uses 'admin' as password.")
subcommand_createsuperuser.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_createsuperuser, ('settings', 'user_data_path'))
subcommand_createsuperuser.add_argument(*localpath_args, **localpath_kwargs)
subcommand_createsuperuser.set_defaults(callback=createsuperuser) subcommand_createsuperuser.set_defaults(callback=createsuperuser)
# Subcommand backupdb # Subcommand backupdb
subcommand_backupdb = subparsers.add_parser( subcommand_backupdb = subparsers.add_parser(
'backupdb', 'backupdb',
help='Store a backup copy of the SQLite3 database.') help='Store a backup copy of the SQLite3 database at the given path.')
subcommand_backupdb.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_backupdb, ('settings', 'user_data_path'))
subcommand_backupdb.add_argument(*localpath_args, **localpath_kwargs)
subcommand_backupdb.add_argument( subcommand_backupdb.add_argument(
'path', 'path',
help='Path to the backup file. An existing file will be overwritten.') help='Path to the backup file. An existing file will be overwritten.')
@ -215,14 +212,14 @@ def parse_args():
subcommand_deletedb = subparsers.add_parser( subcommand_deletedb = subparsers.add_parser(
'deletedb', 'deletedb',
help='Delete the SQLite3 database.') help='Delete the SQLite3 database.')
subcommand_deletedb.add_argument(*settings_args, **settings_kwargs) add_general_arguments(subcommand_deletedb, ('settings', 'user_data_path'))
subcommand_deletedb.add_argument(*localpath_args, **localpath_kwargs)
subcommand_deletedb.set_defaults(callback=deletedb) subcommand_deletedb.set_defaults(callback=deletedb)
# Subcommand django # Subcommand django
subcommand_django_command_line_utility = subparsers.add_parser( subcommand_django_command_line_utility = subparsers.add_parser(
'django', 'django',
description="Link to Django's command-line utility. Type 'python %s django help' for more help on this." % parser.prog, description="Link to Django's command-line utility. Type "
"'python %s django help' for more help on this." % parser.prog,
help="Call Django's command-line utility.") help="Call Django's command-line utility.")
subcommand_django_command_line_utility.set_defaults( subcommand_django_command_line_utility.set_defaults(
callback=django_command_line_utility, callback=django_command_line_utility,
@ -241,6 +238,45 @@ def parse_args():
return known_args return known_args
def add_general_arguments(subcommand, arguments):
"""
Adds the named arguments to the subcommand.
"""
general_arguments = {}
openslides_type = detect_openslides_type()
general_arguments['settings'] = (
('-s', '--settings'),
dict(help="Path to settings file. If this isn't provided, the "
"%s environment variable will be used. "
"If this isn't provided too, a default path according to the "
"OpenSlides type will be used. At the moment it is %s" % (
ENVIRONMENT_VARIABLE,
get_default_settings_path(openslides_type))))
general_arguments['user_data_path'] = (
('-d', '--user-data-path'),
dict(help='Path to the directory for user specific data files like SQLite3 '
'database, uploaded media and search index. This is only used, '
'when a new settings file is created. The given path is only '
'written into the new settings file. Default according to the '
'OpenSlides is at the moment %s' % get_default_user_data_path(openslides_type)))
general_arguments['address'] = (
('-a', '--address',),
dict(default='0.0.0.0', help='IP address to listen on. Default is %(default)s.'))
general_arguments['port'] = (
('-p', '--port'),
dict(type=int,
default=80,
help='Port to listen on. Default as admin or root is %(default)d, else 8000.'))
for argument in arguments:
try:
args, kwargs = general_arguments[argument]
except KeyError:
raise TypeError('The argument %s is not a valid general argument.' % argument)
subcommand.add_argument(*args, **kwargs)
def get_default_settings_path(openslides_type): def get_default_settings_path(openslides_type):
""" """
Returns the default settings path according to the OpenSlides type. Returns the default settings path according to the OpenSlides type.
@ -260,54 +296,44 @@ def get_default_settings_path(openslides_type):
return os.path.join(parent_directory, 'openslides', 'settings.py') return os.path.join(parent_directory, 'openslides', 'settings.py')
def get_default_local_path(openslides_type): def get_user_data_path_values(user_data_path, default=False, openslides_type=None):
""" """
Returns the default local path according to the OpenSlides type. Returns a dictionary of the user specific data path values for the new
settings file.
The argument 'openslides_type' has to be one of the three types mentioned in The argument 'user_data_path' is a path to the directory where OpenSlides
openslides.utils.main. should store the user specific data like SQLite3 database, media and search
""" index.
if openslides_type == UNIX_VERSION:
default_local_path = filesystem2unicode(os.environ.get(
'XDG_DATA_HOME', os.path.join(os.path.expanduser('~'), '.local', 'share')))
elif openslides_type == WINDOWS_VERSION:
default_local_path = get_win32_app_data_path()
elif openslides_type == WINDOWS_PORTABLE_VERSION:
default_local_path = get_win32_portable_path()
else:
raise TypeError('%s is not a valid OpenSlides type.' % openslides_type)
return default_local_path
def get_localpath_values(localpath, default=False, openslides_type=None):
"""
Returns the local path values for the new settings file.
The argument 'localpath' is a path to the directory where OpenSlides should
store the local data like SQLite3 database, media and search index.
The argument 'default' is a simple flag. If it is True and the OpenSlides The argument 'default' is a simple flag. If it is True and the OpenSlides
type is the Windows portable version, this function returns callable type is the Windows portable version, the returned dictionary contains
functions for the settings file, else it returns string paths. strings of callable functions for the settings file, else it contains
string paths.
The argument 'openslides_type' can to be one of the three types mentioned in The argument 'openslides_type' can to be one of the three types mentioned in
openslides.utils.main. openslides.utils.main.
""" """
localpath_values = {} user_data_path_values = {}
if default and openslides_type == WINDOWS_PORTABLE_VERSION: if default and openslides_type == WINDOWS_PORTABLE_VERSION:
localpath_values['import_function'] = 'from openslides.utils.main import get_portable_paths' user_data_path_values['import_function'] = 'from openslides.utils.main import get_portable_paths'
localpath_values['database_path_value'] = "get_portable_paths('database')" user_data_path_values['database_path_value'] = "get_portable_paths('database')"
localpath_values['media_path_value'] = "get_portable_paths('media')" user_data_path_values['media_path_value'] = "get_portable_paths('media')"
user_data_path_values['whoosh_index_path_value'] = "get_portable_paths('whoosh_index')"
else: else:
localpath_values['import_function'] = '' user_data_path_values['import_function'] = ''
# TODO: Decide whether to use only absolute paths here. # TODO: Decide whether to use only absolute paths here.
localpath_values['database_path_value'] = "'%s'" % os.path.join(localpath, 'openslides', 'database.sqlite') user_data_path_values['database_path_value'] = "'%s'" % os.path.join(
user_data_path, 'openslides', 'database.sqlite')
# TODO: Decide whether to use only absolute paths here. # TODO: Decide whether to use only absolute paths here.
localpath_values['media_path_value'] = "'%s'" % os.path.join(localpath, 'openslides', 'media', '') user_data_path_values['media_path_value'] = "'%s'" % os.path.join(
return localpath_values user_data_path, 'openslides', 'media', '')
# TODO: Decide whether to use only absolute paths here.
user_data_path_values['whoosh_index_path_value'] = "'%s'" % os.path.join(
user_data_path, 'openslides', 'whoosh_index', '')
return user_data_path_values
def create_settings(settings_path, local_path_values): def create_settings(settings_path, user_data_path_values):
""" """
Creates the settings file at the given path using the given values for the Creates the settings file at the given path using the given values for the
file template. file template.
@ -316,7 +342,7 @@ def create_settings(settings_path, local_path_values):
if not os.path.exists(settings_module): if not os.path.exists(settings_module):
os.makedirs(settings_module) os.makedirs(settings_module)
context = {'secret_key': base64.b64encode(os.urandom(30))} context = {'secret_key': base64.b64encode(os.urandom(30))}
context.update(local_path_values) context.update(user_data_path_values)
settings_content = SETTINGS_TEMPLATE % context settings_content = SETTINGS_TEMPLATE % context
with open(settings_path, 'w') as settings_file: with open(settings_path, 'w') as settings_file:
settings_file.write(settings_content) settings_file.write(settings_content)
@ -325,25 +351,24 @@ def create_settings(settings_path, local_path_values):
def setup_django_settings_module(settings_path): def setup_django_settings_module(settings_path):
""" """
Sets the environment variable DJANGO_SETTINGS_MODULE to the given settings. Sets the environment variable ENVIRONMENT_VARIABLE, that means
'DJANGO_SETTINGS_MODULE', to the given settings.
""" """
settings_file = os.path.basename(settings_path) settings_file = os.path.basename(settings_path)
settings_module_name = "".join(settings_file.split('.')[:-1]) settings_module_name = ".".join(settings_file.split('.')[:-1])
if '.' in settings_module_name: if '.' in settings_module_name:
print("'.' is not an allowed character in the settings-file") raise ImproperlyConfigured("'.' is not an allowed character in the settings-file")
sys.exit(1) settings_module_dir = os.path.dirname(settings_path) # TODO: Use absolute path here or not?
settings_module_dir = os.path.dirname(settings_path) sys.path.insert(0, settings_module_dir)
sys.path.append(settings_module_dir)
os.environ[ENVIRONMENT_VARIABLE] = '%s' % settings_module_name os.environ[ENVIRONMENT_VARIABLE] = '%s' % settings_module_name
def start(args): def start(args):
""" """
Starts OpenSlides: Runs syncdb and runs runserver (tornado webserver) with Starts OpenSlides: Runs syncdb and runs runserver (tornado webserver).
the flag 'start_browser'.
""" """
syncdb(args) syncdb(args)
args.start_browser = True args.start_browser = not args.no_browser
args.no_reload = False args.no_reload = False
runserver(args) runserver(args)
@ -360,27 +385,6 @@ def runserver(args):
run_tornado(args.address, port, not args.no_reload) run_tornado(args.address, port, not args.no_reload)
def get_port(address, port):
"""
Returns the port for the server. If port 80 is given, checks if it is
available. If not returns port 8000.
The argument 'address' should be an IP address. The argument 'port' should
be an integer.
"""
if port == 80:
# test if we can use port 80
s = socket.socket()
try:
s.bind((address, port))
s.listen(-1)
except socket.error:
port = 8000
finally:
s.close()
return port
def get_browser_url(address, port): def get_browser_url(address, port):
""" """
Returns the url to open the web browser. Returns the url to open the web browser.
@ -418,15 +422,16 @@ def syncdb(args):
Run syncdb to create or update the database. Run syncdb to create or update the database.
""" """
# TODO: Check use of filesystem2unicode here. # TODO: Check use of filesystem2unicode here.
path = filesystem2unicode(os.path.dirname(get_database_path_from_settings(args.settings))) path = filesystem2unicode(os.path.dirname(get_database_path_from_settings()))
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
execute_from_command_line(["", "syncdb", "--noinput"]) execute_from_command_line(["", "syncdb", "--noinput"])
return 0
def get_database_path_from_settings(settings_path): def get_database_path_from_settings():
""" """
Retrieves the database path out of the given settings file. Returns None, Retrieves the database path out of the settings file. Returns None,
if it is not a SQLite3 database. if it is not a SQLite3 database.
""" """
from django.conf import settings as django_settings from django.conf import settings as django_settings
@ -460,7 +465,7 @@ def createsuperuser(args):
def backupdb(args): def backupdb(args):
""" """
Stores a backup copy of the SQlite3 database. Stores a backup copy of the SQlite3 database. Returns 0 on success, else 1.
""" """
from django.db import connection, transaction from django.db import connection, transaction
@ -474,18 +479,19 @@ def backupdb(args):
# now copy the file # now copy the file
try: try:
shutil.copy(src_path, dest_path) shutil.copy(src_path, dest_path)
except IOError as e: except IOError:
raise Exception("Database backup failed.") raise Exception("Database backup failed.")
# and release the lock again # and release the lock again
transaction.commit() transaction.commit()
database_path = get_database_path_from_settings(args.settings) database_path = get_database_path_from_settings()
if database_path: if database_path:
do_backup(database_path, args.path) do_backup(database_path, args.path)
print('Database %s successfully stored at %s.' % (database_path, args.path)) print('Database %s successfully stored at %s.' % (database_path, args.path))
return_value = 0 return_value = 0
else: else:
print('Error: Default database is not SQLite3. Only SQLite3 databases can currently be backuped.') print('Error: Default database is not SQLite3. Only SQLite3 databases '
'can currently be backuped.')
return_value = 1 return_value = 1
return return_value return return_value
@ -494,7 +500,7 @@ def deletedb(args):
""" """
Deletes the sqlite3 database. Returns 0 on success, else 1. Deletes the sqlite3 database. Returns 0 on success, else 1.
""" """
database_path = get_database_path_from_settings(args.settings) database_path = get_database_path_from_settings()
if database_path and os.path.exists(database_path): if database_path and os.path.exists(database_path):
os.remove(database_path) os.remove(database_path)
print('SQLite3 database file %s successfully deleted.' % database_path) print('SQLite3 database file %s successfully deleted.' % database_path)
@ -518,7 +524,8 @@ def django_command_line_utility(args):
else: else:
command = None command = None
if command: if command:
print("Error: The command '%s' is disabled for use via Django's command line utility." % command) print("Error: The command '%s' is disabled in OpenSlides for use via Django's "
"command line utility." % command)
return_value = 1 return_value = 1
else: else:
execute_from_command_line(args.django_args) execute_from_command_line(args.django_args)

View File

@ -67,11 +67,9 @@ STATICFILES_DIRS = (
filesystem2unicode(os.path.join(SITE_ROOT, 'static')), filesystem2unicode(os.path.join(SITE_ROOT, 'static')),
) )
#XXX: Note this setting (as well as our workaround finder)
# can be removed again once django-bug-#18404 has been resolved
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',
'openslides.utils.staticfiles.AppDirectoriesFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
) )
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'

View File

@ -10,9 +10,10 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
import os
import sys
import ctypes import ctypes
import os
import socket
import sys
import tempfile import tempfile
@ -20,9 +21,11 @@ UNIX_VERSION = 'Unix Version'
WINDOWS_VERSION = 'Windows Version' WINDOWS_VERSION = 'Windows Version'
WINDOWS_PORTABLE_VERSION = 'Windows Portable Version' WINDOWS_PORTABLE_VERSION = 'Windows Portable Version'
class PortableDirNotWritable(Exception): class PortableDirNotWritable(Exception):
pass pass
def filesystem2unicode(path): def filesystem2unicode(path):
""" """
Transforms a path string to unicode according to the filesystem's encoding. Transforms a path string to unicode according to the filesystem's encoding.
@ -36,7 +39,7 @@ def filesystem2unicode(path):
def detect_openslides_type(): def detect_openslides_type():
""" """
Returns the type of this version. Returns the type of this OpenSlides version.
""" """
if sys.platform == 'win32': if sys.platform == 'win32':
if os.path.basename(sys.executable).lower() == 'openslides.exe': if os.path.basename(sys.executable).lower() == 'openslides.exe':
@ -54,12 +57,24 @@ def detect_openslides_type():
return openslides_type return openslides_type
def is_portable(): def get_default_user_data_path(openslides_type):
""" """
Helper function just for the GUI. Returns the default path 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.
""" """
# TODO: Remove this function. if openslides_type == UNIX_VERSION:
return detect_openslides_type() == WINDOWS_PORTABLE_VERSION default_user_data_path = filesystem2unicode(os.environ.get(
'XDG_DATA_HOME', os.path.join(os.path.expanduser('~'), '.local', 'share')))
elif openslides_type == WINDOWS_VERSION:
default_user_data_path = get_win32_app_data_path()
elif openslides_type == WINDOWS_PORTABLE_VERSION:
default_user_data_path = get_win32_portable_path()
else:
raise TypeError('%s is not a valid OpenSlides type.' % openslides_type)
return default_user_data_path
def get_win32_app_data_path(): def get_win32_app_data_path():
@ -79,7 +94,7 @@ def get_win32_app_data_path():
buf = ctypes.create_unicode_buffer(MAX_PATH) buf = ctypes.create_unicode_buffer(MAX_PATH)
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf) res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
if res != 0: if res != 0:
raise Exception("Could not deterime APPDATA path") raise Exception("Could not determine Windows' APPDATA path")
return buf.value return buf.value
@ -97,7 +112,7 @@ def get_win32_portable_path():
except OSError: except OSError:
raise PortableDirNotWritable( raise PortableDirNotWritable(
'Portable directory is not writeable. ' 'Portable directory is not writeable. '
'Please choose another directory for settings and local files.') 'Please choose another directory for settings and data files.')
else: else:
os.close(fd) os.close(fd)
os.unlink(test_file) os.unlink(test_file)
@ -107,13 +122,36 @@ def get_win32_portable_path():
def get_portable_paths(name): def get_portable_paths(name):
""" """
Returns the paths for the Windows portable version on runtime for the Returns the paths for the Windows portable version on runtime for the
SQLite3 database and the media directory. The argument 'name' can be SQLite3 database, the media directory and the search index. The argument
'database' or 'media'. 'name' can be 'database', 'media' or 'whoosh_index'.
""" """
if name == 'database': if name == 'database':
path = os.path.join(get_win32_portable_path(), 'openslides', 'database.sqlite') path = os.path.join(get_win32_portable_path(), 'openslides', 'database.sqlite')
elif name == 'media': elif name == 'media':
path = os.path.join(get_win32_portable_path(), 'openslides', 'media', '') path = os.path.join(get_win32_portable_path(), 'openslides', 'media', '')
elif name == 'whoosh_index':
path = os.path.join(get_win32_portable_path(), 'openslides', 'whoosh_index', '')
else: else:
raise TypeError('Unknow type %s' % name) raise TypeError('Unknown type %s' % name)
return path return path
def get_port(address, port):
"""
Returns the port for the server. If port 80 is given, checks if it is
available. If not returns port 8000.
The argument 'address' should be an IP address. The argument 'port' should
be an integer.
"""
if port == 80:
# test if we can use port 80
s = socket.socket()
try:
s.bind((address, port))
s.listen(-1)
except socket.error:
port = 8000
finally:
s.close()
return port

67
tests/utils/test_main.py Normal file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Tests for openslides.utils.main
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 20112013 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
import os
import sys
from django.core.exceptions import ImproperlyConfigured
from openslides.__main__ import (
get_default_settings_path,
get_browser_url,
get_user_data_path_values,
setup_django_settings_module)
from openslides.utils.test import TestCase
from openslides.utils.main import (
get_default_user_data_path,
UNIX_VERSION,
WINDOWS_PORTABLE_VERSION)
class TestFunctions(TestCase):
def test_get_default_user_data_path(self):
self.assertTrue('.local/share' in get_default_user_data_path(UNIX_VERSION))
def test_get_default_settings_path(self):
self.assertTrue('.config/openslides/settings.py' in get_default_settings_path(UNIX_VERSION))
def test_get_user_data_path_values_case_one(self):
self.assertEqual(
get_user_data_path_values('test_path_dfhvndshfgsef', default=False),
{'import_function': '',
'database_path_value': "'test_path_dfhvndshfgsef/openslides/database.sqlite'",
'media_path_value': "'test_path_dfhvndshfgsef/openslides/media/'",
'whoosh_index_path_value': "'test_path_dfhvndshfgsef/openslides/whoosh_index/'"})
def test_get_user_data_path_values_case_two(self):
self.assertEqual(
get_user_data_path_values('test_path_dfhvndshfgsef', default=True, openslides_type=WINDOWS_PORTABLE_VERSION),
{'import_function': 'from openslides.utils.main import get_portable_paths',
'database_path_value': "get_portable_paths('database')",
'media_path_value': "get_portable_paths('media')",
'whoosh_index_path_value': "get_portable_paths('whoosh_index')"})
def test_setup_django_settings_module(self):
setup_django_settings_module('test_dir_dhvnghfjdh456fzheg2f/test_path_bngjdhc756dzwncshdfnx.py')
self.assertEqual(os.environ['DJANGO_SETTINGS_MODULE'], 'test_path_bngjdhc756dzwncshdfnx')
self.assertEqual(sys.path[0], 'test_dir_dhvnghfjdh456fzheg2f')
def test_setup_django_settings_module_error(self):
self.assertRaisesMessage(
ImproperlyConfigured,
"'.' is not an allowed character in the settings-file",
setup_django_settings_module,
'wrong.file.py')
def test_get_browser_url(self):
self.assertEqual(get_browser_url('123.456.789.365', 6789), 'http://123.456.789.365:6789')
self.assertEqual(get_browser_url('123.456.789.365', 80), 'http://123.456.789.365')
self.assertEqual(get_browser_url('0.0.0.0', 6789), 'http://localhost:6789')
self.assertEqual(get_browser_url('0.0.0.0', 80), 'http://localhost')