Fixed registration step of first time setup. General improvements to settings and first time setup wizard.

This commit is contained in:
Tiberiu Chibici 2018-12-28 19:47:35 +02:00
parent 022d3aa4f5
commit 33ee71dcbb
10 changed files with 263 additions and 177 deletions

View File

@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/1.11/ref/settings/
""" """
import os import os
import sys
import logging import logging
from os.path import dirname as up from os.path import dirname as up
@ -128,7 +129,6 @@ BASE_DIR = up(os.path.dirname(__file__)) # Base dir of the ap
CONFIG_DIR = os.getenv("YTSM_CONFIG_DIR", os.path.join(PROJECT_ROOT, "config")) CONFIG_DIR = os.getenv("YTSM_CONFIG_DIR", os.path.join(PROJECT_ROOT, "config"))
DATA_DIR = os.getenv("YTSM_DATA_DIR", os.path.join(PROJECT_ROOT, "data")) DATA_DIR = os.getenv("YTSM_DATA_DIR", os.path.join(PROJECT_ROOT, "data"))
os.chdir(DATA_DIR)
STATIC_ROOT = os.path.join(PROJECT_ROOT, "static") STATIC_ROOT = os.path.join(PROJECT_ROOT, "static")
MEDIA_ROOT = os.path.join(DATA_DIR, 'media') MEDIA_ROOT = os.path.join(DATA_DIR, 'media')
@ -154,6 +154,16 @@ CONFIG_ERRORS = []
CONFIG_WARNINGS = [] CONFIG_WARNINGS = []
#
# Config parser options
#
CFG_PARSER_OPTS = {
'PROJECT_ROOT' : PROJECT_ROOT,
'BASE_DIR' : BASE_DIR,
'CONFIG_DIR' : CONFIG_DIR,
'DATA_DIR' : DATA_DIR,
}
# #
# Load globals from config.ini # Load globals from config.ini
# #
@ -189,7 +199,7 @@ def get_global_opt(name, cfgparser, env_variable=None, fallback=None, boolean=Fa
# Get from config parser # Get from config parser
if boolean: if boolean:
try: try:
return cfgparser.getboolean('global', name, fallback=fallback) return cfgparser.getboolean('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
except ValueError: except ValueError:
CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} is not valid! ' CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} is not valid! '
f'Valid options: true, false, on, off.') f'Valid options: true, false, on, off.')
@ -197,12 +207,12 @@ def get_global_opt(name, cfgparser, env_variable=None, fallback=None, boolean=Fa
if integer: if integer:
try: try:
return cfgparser.getint('global', name, fallback=fallback) return cfgparser.getint('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
except ValueError: except ValueError:
CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} must be an integer number! ') CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} must be an integer number! ')
return fallback return fallback
return cfgparser.get('global', name, fallback=fallback) return cfgparser.get('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
def load_config_ini(): def load_config_ini():
@ -210,6 +220,13 @@ def load_config_ini():
from YtManagerApp.utils.extended_interpolation_with_env import ExtendedInterpolatorWithEnv from YtManagerApp.utils.extended_interpolation_with_env import ExtendedInterpolatorWithEnv
import dj_database_url import dj_database_url
try:
os.makedirs(DATA_DIR, exist_ok=True)
logging.info(f"Using data directory {DATA_DIR}")
except OSError as e:
print(f'CRITICAL ERROR! Cannot create data directory {DATA_DIR}! {e}', file=sys.stderr)
return;
cfg = ConfigParser(allow_no_value=True, interpolation=ExtendedInterpolatorWithEnv()) cfg = ConfigParser(allow_no_value=True, interpolation=ExtendedInterpolatorWithEnv())
cfg_file = os.path.join(CONFIG_DIR, "config.ini") cfg_file = os.path.join(CONFIG_DIR, "config.ini")
@ -235,7 +252,7 @@ def load_config_ini():
} }
if cfg.has_option('global', 'DatabaseURL'): if cfg.has_option('global', 'DatabaseURL'):
DATABASES['default'] = dj_database_url.parse(cfg.get('global', 'DatabaseURL'), conn_max_age=600) DATABASES['default'] = dj_database_url.parse(cfg.get('global', 'DatabaseURL', vars=CFG_PARSER_OPTS), conn_max_age=600)
else: else:
DATABASES['default'] = { DATABASES['default'] = {

View File

@ -1,6 +1,6 @@
#main_body { #main_body {
margin-bottom: 4rem; margin-bottom: 4rem;
margin-top: 2rem; } margin-top: 0; }
#main_footer { #main_footer {
position: fixed; position: fixed;

View File

@ -1,6 +1,6 @@
{ {
"version": 3, "version": 3,
"mappings": "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", "mappings": "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",
"sources": ["style.scss"], "sources": ["style.scss"],
"names": [], "names": [],
"file": "style.css" "file": "style.css"

View File

@ -2,7 +2,7 @@ $accent-color: #007bff;
#main_body { #main_body {
margin-bottom: 4rem; margin-bottom: 4rem;
margin-top: 2rem; margin-top: 0;
} }
#main_footer { #main_footer {

View File

@ -63,8 +63,7 @@ urlpatterns = [
# First time setup # First time setup
path('first_time/step0_welcome', first_time.Step0WelcomeView.as_view(), name='first_time_0'), path('first_time/step0_welcome', first_time.Step0WelcomeView.as_view(), name='first_time_0'),
path('first_time/step1_apikey', first_time.Step1ApiKeyView.as_view(), name='first_time_1'), path('first_time/step1_apikey', first_time.Step1ApiKeyView.as_view(), name='first_time_1'),
path('first_time/step2_admin', first_time.Step2CreateAdminUserView.as_view(), name='first_time_2'), path('first_time/step2_admin', first_time.Step2SetupAdminUserView.as_view(), name='first_time_2'),
path('first_time/step2_login', first_time.Step2LoginAdminUserView.as_view(), name='first_time_2_login'),
path('first_time/step3_config', first_time.Step3ConfigureView.as_view(), name='first_time_3'), path('first_time/step3_config', first_time.Step3ConfigureView.as_view(), name='first_time_3'),
path('first_time/done', first_time.DoneView.as_view(), name='first_time_done'), path('first_time/done', first_time.DoneView.as_view(), name='first_time_done'),

View File

@ -1,57 +1,17 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.contrib.auth import login, authenticate from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic import FormView, TemplateView from django.views.generic import FormView, TemplateView
from .forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
class ExtendedAuthenticationForm(AuthenticationForm):
remember_me = forms.BooleanField(label='Remember me', required=False, initial=False)
def clean(self):
remember_me = self.cleaned_data.get('remember_me')
if remember_me:
expiry = 3600 * 24 * 30
else:
expiry = 0
self.request.session.set_expiry(expiry)
return super().clean()
class ExtendedLoginView(LoginView): class ExtendedLoginView(LoginView):
form_class = ExtendedAuthenticationForm form_class = ExtendedAuthenticationForm
class ExtendedUserCreationForm(UserCreationForm):
email = forms.EmailField(required=False,
label='E-mail address',
help_text='The e-mail address is optional, but it is the only way to recover a lost '
'password.')
first_name = forms.CharField(max_length=30, required=False,
label='First name')
last_name = forms.CharField(max_length=150, required=False,
label='Last name')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.label_class = 'col-3'
self.helper.field_class = 'col-9'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
self.helper.form_action = reverse_lazy('register')
self.helper.add_input(Submit('submit', 'register'))
class Meta(UserCreationForm.Meta):
fields = ['username', 'email', 'first_name', 'last_name']
class RegisterView(FormView): class RegisterView(FormView):
template_name = 'registration/register.html' template_name = 'registration/register.html'
form_class = ExtendedUserCreationForm form_class = ExtendedUserCreationForm

View File

@ -1,30 +1,28 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit, Column
from django import forms
from django.contrib.auth import authenticate, login
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.urls import reverse_lazy
from django.views.generic import UpdateView, FormView
from django.shortcuts import render, redirect
from YtManagerApp.views.auth import RegisterView, ExtendedUserCreationForm, ExtendedAuthenticationForm
from YtManagerApp.models import UserSettings
from YtManagerApp.management.appconfig import global_prefs
from django.http import HttpResponseForbidden
import logging import logging
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views.generic import FormView
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm
from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, UserCreationForm
logger = logging.getLogger("FirstTimeWizard") logger = logging.getLogger("FirstTimeWizard")
class ProtectInitializedMixin(object): class WizardStepMixin(object):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
# Prevent access if application is already initialized
if global_prefs['hidden__initialized']: if global_prefs['hidden__initialized']:
logger.debug(f"Attempted to access {request.path}, but first time setup already run. Redirected to home page.") logger.debug(f"Attempted to access {request.path}, but first time setup already run. Redirected to home "
f"page.")
return redirect('home') return redirect('home')
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -37,44 +35,25 @@ class ProtectInitializedMixin(object):
# #
# Step 0: welcome screen # Step 0: welcome screen
# #
class Step0WelcomeForm(forms.Form): class Step0WelcomeView(WizardStepMixin, FormView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Submit('submit', value='Continue')
)
class Step0WelcomeView(ProtectInitializedMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step0_welcome.html' template_name = 'YtManagerApp/first_time_setup/step0_welcome.html'
form_class = Step0WelcomeForm form_class = WelcomeForm
success_url = reverse_lazy('first_time_1') success_url = reverse_lazy('first_time_1')
# #
# Step 1: setup API key # Step 1: setup API key
# #
class Step1ApiKeyForm(forms.Form): class Step1ApiKeyView(WizardStepMixin, FormView):
api_key = forms.CharField(label="YouTube API Key:")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'api_key',
Column(
Submit('submit', value='Continue'),
HTML('<a href="{% url \'first_time_2\' %}" class="btn">Skip</a>')
)
)
class Step1ApiKeyView(ProtectInitializedMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step1_apikey.html' template_name = 'YtManagerApp/first_time_setup/step1_apikey.html'
form_class = Step1ApiKeyForm form_class = ApiKeyForm
success_url = reverse_lazy('first_time_2') success_url = reverse_lazy('first_time_2')
def get_initial(self):
initial = super().get_initial()
initial['api_key'] = global_prefs['general__youtube_api_key']
return initial
def form_valid(self, form): def form_valid(self, form):
key = form.cleaned_data['api_key'] key = form.cleaned_data['api_key']
# TODO: validate key # TODO: validate key
@ -85,101 +64,87 @@ class Step1ApiKeyView(ProtectInitializedMixin, FormView):
# #
# Step 2: create admin user # Step 2: create admin user
# #
class Step2CreateAdminUserView(ProtectInitializedMixin, FormView): class Step2SetupAdminUserView(WizardStepMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step2_admin.html' template_name = 'YtManagerApp/first_time_setup/step2_admin.html'
success_url = reverse_lazy('first_time_3') success_url = reverse_lazy('first_time_3')
form_class = ExtendedUserCreationForm
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__form_class = UserCreationForm
def get_form_class(self):
return self.__form_class
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
have_users = User.objects.count() > 0
have_admin = User.objects.filter(is_superuser=True).count() > 0
# Skip if admin is already logged in # Skip if admin is already logged in
if request.user.is_authenticated and request.user.is_superuser: if request.user.is_authenticated and request.user.is_superuser:
logger.debug("Admin user already exists and is logged in!") logger.debug("Admin user already exists and is logged in!")
return redirect(self.success_url) return redirect(self.success_url)
# Check if an admin user already exists # Check if an admin user already exists
if User.objects.filter(is_superuser=True).count() > 0: elif have_admin:
logger.warn("Admin user already exists! Will redirect to login page!") logger.debug("Admin user already exists and is not logged in!")
return redirect('first_time_2_login') self.__form_class = ExtendedAuthenticationForm
elif have_users and 'register' not in kwargs:
logger.debug("There are users but no admin!")
self.__form_class = PickAdminUserForm
else:
logger.debug("No admin user exists, will register a new account!")
self.__form_class = UserCreationForm
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def form_valid(self, form): def form_invalid(self, form):
user = form.save() print("FORM INVALID!")
user.is_staff = True
user.is_superuser = True
user.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(self.request, user)
return super().form_valid(form)
#
# Step 2: create admin user
#
class Step2LoginAdminUserView(ProtectInitializedMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step2_admin.html'
success_url = reverse_lazy('first_time_3')
form_class = ExtendedAuthenticationForm
def form_valid(self, form): def form_valid(self, form):
login(self.request, form.get_user()) if isinstance(form, ExtendedAuthenticationForm):
login(self.request, form.get_user())
elif isinstance(form, UserCreationForm):
user = form.save()
user.is_staff = True
user.is_superuser = True
user.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(self.request, user)
elif isinstance(form, PickAdminUserForm):
user = form.cleaned_data['admin_user']
user.is_staff = True
user.is_superuser = True
user.save()
return redirect('first_time_2', assigned_success='1')
return super().form_valid(form) return super().form_valid(form)
# #
# Step 3: configure server # Step 3: configure server
# #
class Step3ConfigureForm(forms.Form): class Step3ConfigureView(WizardStepMixin, FormView):
allow_registrations = forms.BooleanField(
label="Allow user registrations",
help_text="Disabling this option will prevent anyone from registering to the site.",
initial=True,
required=False
)
sync_schedule = forms.CharField(
label="Synchronization schedule",
help_text="How often should the application look for new videos.",
initial="5 * * * *",
required=True
)
auto_download = forms.BooleanField(
label="Download videos automatically",
required=False
)
download_location = forms.CharField(
label="Download location",
help_text="Location on the server where videos are downloaded.",
required=True
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
HTML('<h3>Server settings</h3>'),
'sync_schedule',
'allow_registrations',
HTML('<h3>User settings</h3>'),
'auto_download',
'download_location',
Submit('submit', value='Continue'),
)
class Step3ConfigureView(ProtectInitializedMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step3_configure.html' template_name = 'YtManagerApp/first_time_setup/step3_configure.html'
form_class = Step3ConfigureForm form_class = ServerConfigForm
success_url = reverse_lazy('first_time_done') success_url = reverse_lazy('first_time_done')
def get_initial(self):
initial = super().get_initial()
initial['allow_registrations'] = global_prefs['general__allow_registrations']
initial['sync_schedule'] = global_prefs['scheduler__synchronization_schedule']
initial['auto_download'] = self.request.user.preferences['downloader__auto_enabled']
initial['download_location'] = self.request.user.preferences['downloader__download_path']
return initial
def form_valid(self, form): def form_valid(self, form):
allow_registrations = form.cleaned_data['allow_registrations'] allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None: if allow_registrations is not None:
@ -202,18 +167,10 @@ class Step3ConfigureView(ProtectInitializedMixin, FormView):
return super().form_valid(form) return super().form_valid(form)
# #
# Done screen # Done screen
# #
class DoneForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Submit('submit', value='Finish')
)
class DoneView(FormView): class DoneView(FormView):
template_name = 'YtManagerApp/first_time_setup/done.html' template_name = 'YtManagerApp/first_time_setup/done.html'
form_class = DoneForm form_class = DoneForm

View File

@ -0,0 +1,45 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.urls import reverse_lazy
class ExtendedAuthenticationForm(AuthenticationForm):
remember_me = forms.BooleanField(label='Remember me', required=False, initial=False)
def clean(self):
remember_me = self.cleaned_data.get('remember_me')
if remember_me:
expiry = 3600 * 24 * 30
else:
expiry = 0
self.request.session.set_expiry(expiry)
return super().clean()
class ExtendedUserCreationForm(UserCreationForm):
email = forms.EmailField(required=False,
label='E-mail address',
help_text='The e-mail address is optional, but it is the only way to recover a lost '
'password.')
first_name = forms.CharField(max_length=30, required=False,
label='First name')
last_name = forms.CharField(max_length=150, required=False,
label='Last name')
form_action = reverse_lazy('register')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.label_class = 'col-3'
self.helper.field_class = 'col-9'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
self.helper.form_action = self.form_action
self.helper.add_input(Submit('submit', 'register'))
class Meta(UserCreationForm.Meta):
fields = ['username', 'email', 'first_name', 'last_name']

View File

@ -0,0 +1,108 @@
import logging
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit, Column
from django import forms
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from YtManagerApp.views.forms.auth import ExtendedUserCreationForm
from django.urls import reverse_lazy
from YtManagerApp.management.appconfig import global_prefs
logger = logging.getLogger("FirstTimeWizard")
class WelcomeForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Submit('submit', value='Continue')
)
class ApiKeyForm(forms.Form):
api_key = forms.CharField(label="YouTube API Key:")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'api_key',
Column(
Submit('submit', value='Continue'),
HTML('<a href="{% url \'first_time_2\' %}" class="btn">Skip</a>')
)
)
class UserCreationForm(ExtendedUserCreationForm):
form_action = reverse_lazy('first_time_2')
class PickAdminUserForm(forms.Form):
admin_user = forms.ModelChoiceField(
User.objects.order_by('username'),
label='User to promote to admin',
required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'admin_user',
Column(
Submit('submit', value='Continue'),
HTML('<a href="{% url \'first_time_2\' %}&register=1" class="btn">Register a new admin user</a>')
)
)
class ServerConfigForm(forms.Form):
allow_registrations = forms.BooleanField(
label="Allow user registrations",
help_text="Disabling this option will prevent anyone from registering to the site.",
initial=True,
required=False
)
sync_schedule = forms.CharField(
label="Synchronization schedule",
help_text="How often should the application look for new videos.",
initial="5 * * * *",
required=True
)
auto_download = forms.BooleanField(
label="Download videos automatically",
required=False
)
download_location = forms.CharField(
label="Download location",
help_text="Location on the server where videos are downloaded.",
required=True
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
HTML('<h3>Server settings</h3>'),
'sync_schedule',
'allow_registrations',
HTML('<h3>User settings</h3>'),
'auto_download',
'download_location',
Submit('submit', value='Continue'),
)
class DoneForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Submit('submit', value='Finish')
)

View File

@ -15,7 +15,7 @@ SecretKey=^zv8@i2h!ko2lo=%ivq(9e#x=%q*i^^)6#4@(juzdx%&0c+9a0
; Others databases might be supported by installing the corect pip package. ; Others databases might be supported by installing the corect pip package.
DatabaseEngine=django.db.backends.sqlite3 DatabaseEngine=django.db.backends.sqlite3
DatabaseName=ytmanager.db DatabaseName=${DATA_DIR}/ytmanager.db
;DatabaseHost= ;DatabaseHost=
;DatabaseUser= ;DatabaseUser=
;DatabasePassword= ;DatabasePassword=