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 sys
import logging
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"))
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")
MEDIA_ROOT = os.path.join(DATA_DIR, 'media')
@ -154,6 +154,16 @@ CONFIG_ERRORS = []
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
#
@ -189,7 +199,7 @@ def get_global_opt(name, cfgparser, env_variable=None, fallback=None, boolean=Fa
# Get from config parser
if boolean:
try:
return cfgparser.getboolean('global', name, fallback=fallback)
return cfgparser.getboolean('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
except ValueError:
CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} is not valid! '
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:
try:
return cfgparser.getint('global', name, fallback=fallback)
return cfgparser.getint('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
except ValueError:
CONFIG_WARNINGS.append(f'config.ini file: Value set for option global.{name} must be an integer number! ')
return fallback
return cfgparser.get('global', name, fallback=fallback)
return cfgparser.get('global', name, fallback=fallback, vars=CFG_PARSER_OPTS)
def load_config_ini():
@ -210,6 +220,13 @@ def load_config_ini():
from YtManagerApp.utils.extended_interpolation_with_env import ExtendedInterpolatorWithEnv
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_file = os.path.join(CONFIG_DIR, "config.ini")
@ -235,7 +252,7 @@ def load_config_ini():
}
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:
DATABASES['default'] = {

View File

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

View File

@ -1,6 +1,6 @@
{
"version": 3,
"mappings": "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",
"mappings": "AAEA,UAAW;EACP,aAAa,EAAE,IAAI;EACnB,UAAU,EAAE,CAAC;;AAGjB,YAAa;EACT,QAAQ,EAAE,KAAK;EACf,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,CAAC;EACR,MAAM,EAAE,CAAC;EACT,MAAM,EAAE,IAAI;EACZ,WAAW,EAAE,IAAI;EACjB,OAAO,EAAE,SAAS;EAClB,OAAO,EAAE,IAAI;EACb,aAAa,EAAE,MAAM;EACrB,SAAS,EAAE,IAAI;;AAqBnB,uBAAuB;AACvB,kBAAmB;EAlBf,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAa;EACpB,MAAM,EAAE,IAAa;EAErB,wBAAQ;IACJ,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAa;IACpB,MAAM,EAAE,IAAa;IACrB,MAAM,EAAE,GAAG;IACX,aAAa,EAAE,GAAG;IAClB,MAAM,EAAE,iBAAkC;IAC1C,YAAY,EAAE,uCAAmD;IACjE,SAAS,EAAE,sCAAsC;;AASzD,wBAAyB;EAtBrB,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAa;EACpB,MAAM,EAAE,IAAa;EAErB,8BAAQ;IACJ,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,IAAa;IACpB,MAAM,EAAE,IAAa;IACrB,MAAM,EAAE,GAAG;IACX,aAAa,EAAE,GAAG;IAClB,MAAM,EAAE,mBAAkC;IAC1C,YAAY,EAAE,uCAAmD;IACjE,SAAS,EAAE,sCAAsC;;AAazD,4BAOC;EANG,EAAG;IACC,SAAS,EAAE,YAAY;EAE3B,IAAK;IACD,SAAS,EAAE,cAAc;AAIjC,gCAAiC;EAC7B,QAAQ,EAAE,KAAK;EACf,GAAG,EAAE,GAAG;EACR,IAAI,EAAE,GAAG;EACT,UAAU,EAAE,KAAK;EACjB,WAAW,EAAE,KAAK;;AAGtB,cAAe;EACX,QAAQ,EAAE,KAAK;EAAE,oCAAoC;EACrD,OAAO,EAAE,IAAI;EAAE,uBAAuB;EACtC,KAAK,EAAE,IAAI;EAAE,uCAAuC;EACpD,MAAM,EAAE,IAAI;EAAE,wCAAwC;EACtD,GAAG,EAAE,CAAC;EACN,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,CAAC;EACR,MAAM,EAAE,CAAC;EACT,gBAAgB,EAAE,kBAAe;EAAE,mCAAmC;EACtE,OAAO,EAAE,CAAC;EAAE,qFAAqF;EACjG,MAAM,EAAE,OAAO;EAAE,4BAA4B;;AAI7C,4BAAc;EACV,OAAO,EAAE,MAAM;EACf,aAAa,EAAE,KAAK;AAGpB,+BAAW;EACP,OAAO,EAAE,MAAM;AAEnB,+BAAW;EACP,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;AAExB,gCAAY;EACR,SAAS,EAAE,IAAI;EACf,aAAa,EAAE,KAAK;EAEpB,uCAAO;IACH,SAAS,EAAE,GAAG;AAGtB,iCAAa;EACT,OAAO,EAAE,YAAY;AAGzB,+BAAW;EACP,YAAY,EAAE,QAAQ;EACtB,qCAAQ;IACJ,eAAe,EAAE,IAAI;AAO7B,8BAAU;EACN,KAAK,EAAE,KAAK;AAKpB,8BAAgB;EACZ,KAAK,EAxHE,OAAO;AA0HlB,6BAAe;EACX,KAAK,EAAE,OAAO;;AAItB,WAAY;EACR,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM;;AAId,2BAAe;EACX,OAAO,EAAE,IAAI;;AAIrB,kBAAmB;EACf,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,QAAQ;EAEjB,qBAAG;IACC,MAAM,EAAE,CAAC;;AAIjB,YAAa;EACT,OAAO,EAAE,YAAY;EACrB,aAAa,EAAE,MAAM;;AAGzB,YAAa;EACT,MAAM,EAAE,OAAO;EACf,iBAAK;IACD,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,IAAI",
"sources": ["style.scss"],
"names": [],
"file": "style.css"

View File

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

View File

@ -63,8 +63,7 @@ urlpatterns = [
# First time setup
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/step2_admin', first_time.Step2CreateAdminUserView.as_view(), name='first_time_2'),
path('first_time/step2_login', first_time.Step2LoginAdminUserView.as_view(), name='first_time_2_login'),
path('first_time/step2_admin', first_time.Step2SetupAdminUserView.as_view(), name='first_time_2'),
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'),

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.forms import AuthenticationForm, UserCreationForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
from django.views.generic import FormView, TemplateView
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()
from .forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
class ExtendedLoginView(LoginView):
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):
template_name = 'registration/register.html'
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
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")
class ProtectInitializedMixin(object):
class WizardStepMixin(object):
def get(self, request, *args, **kwargs):
# Prevent access if application is already 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 super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
@ -37,44 +35,25 @@ class ProtectInitializedMixin(object):
#
# Step 0: welcome screen
#
class Step0WelcomeForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Submit('submit', value='Continue')
)
class Step0WelcomeView(ProtectInitializedMixin, FormView):
class Step0WelcomeView(WizardStepMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step0_welcome.html'
form_class = Step0WelcomeForm
form_class = WelcomeForm
success_url = reverse_lazy('first_time_1')
#
# Step 1: setup API key
#
class Step1ApiKeyForm(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 Step1ApiKeyView(ProtectInitializedMixin, FormView):
class Step1ApiKeyView(WizardStepMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step1_apikey.html'
form_class = Step1ApiKeyForm
form_class = ApiKeyForm
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):
key = form.cleaned_data['api_key']
# TODO: validate key
@ -85,101 +64,87 @@ class Step1ApiKeyView(ProtectInitializedMixin, FormView):
#
# Step 2: create admin user
#
class Step2CreateAdminUserView(ProtectInitializedMixin, FormView):
class Step2SetupAdminUserView(WizardStepMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step2_admin.html'
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):
have_users = User.objects.count() > 0
have_admin = User.objects.filter(is_superuser=True).count() > 0
# Skip if admin is already logged in
if request.user.is_authenticated and request.user.is_superuser:
logger.debug("Admin user already exists and is logged in!")
return redirect(self.success_url)
# Check if an admin user already exists
if User.objects.filter(is_superuser=True).count() > 0:
logger.warn("Admin user already exists! Will redirect to login page!")
return redirect('first_time_2_login')
elif have_admin:
logger.debug("Admin user already exists and is not logged in!")
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)
def form_valid(self, form):
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)
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_invalid(self, form):
print("FORM INVALID!")
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)
#
# Step 3: configure server
#
class Step3ConfigureForm(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 Step3ConfigureView(ProtectInitializedMixin, FormView):
class Step3ConfigureView(WizardStepMixin, FormView):
template_name = 'YtManagerApp/first_time_setup/step3_configure.html'
form_class = Step3ConfigureForm
form_class = ServerConfigForm
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):
allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None:
@ -202,18 +167,10 @@ class Step3ConfigureView(ProtectInitializedMixin, FormView):
return super().form_valid(form)
#
# 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):
template_name = 'YtManagerApp/first_time_setup/done.html'
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.
DatabaseEngine=django.db.backends.sqlite3
DatabaseName=ytmanager.db
DatabaseName=${DATA_DIR}/ytmanager.db
;DatabaseHost=
;DatabaseUser=
;DatabasePassword=