Commit dcdeb845 authored by Flavio TARSETTI's avatar Flavio TARSETTI

Merge branch 'cleanup_ui_registration' into 'django3_migration'

Cleanup ui registration

See merge request !367
parents fc1e747c be218a1e
Pipeline #42699 passed with stage
in 15 minutes and 34 seconds
......@@ -27,8 +27,8 @@
from django.contrib import admin
from .models import RegistrationProfile
from .models import PreregistrationProfile
from .models import RegistrationProfile
class RegistrationAdmin(admin.ModelAdmin):
......
......@@ -32,7 +32,7 @@ so. This model's sole purpose is to store data temporarily during
account registration and activation, and a mechanism for automatically
creating an instance of a site-specific profile model is provided via
the ``create_inactive_user`` on ``RegistrationManager``.
``RegistrationProfile`` objects have the following fields:
``activation_key``
......@@ -47,15 +47,15 @@ the ``create_inactive_user`` on ``RegistrationManager``.
``activation_key_expired()``
Determines whether this ``RegistrationProfile``'s activation key
has expired.
Returns ``True`` if the key has expired, ``False`` otherwise.
Key expiration is determined by a two-step process:
1. If the user has already activated, the key will have been reset
to the string constant ``ACTIVATED``. Re-activating is not
permitted, and so this method returns ``True`` in this case.
2. Otherwise, the date the user signed up is incremented by the
number of days specified in the setting
``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of days
......@@ -68,25 +68,25 @@ The ``RegistrationManager``
===========================
Custom manager for the ``RegistrationProfile`` model.
The methods defined here provide shortcuts for account creation and
activation (including generation and emailing of activation keys), and
for cleaning out expired inactive accounts.
Methods:
``activate_user(activation_key)``
Validate an activation key and activate the corresponding
``User`` if valid.
If the key is valid and has not expired, return the ``User``
after activating.
If the key is not valid or has expired, return ``False``.
If the key is valid but the ``User`` is already active,
return ``False``.
To prevent reactivation of an account which has been
deactivated by site administrators, the activation key is
reset to the string constant ``RegistrationProfile.ACTIVATED``
......@@ -102,7 +102,7 @@ Methods:
Create a new, inactive ``User``, generate a
``RegistrationProfile`` and email its activation key to the
``User``, returning the new ``User``.
To disable the email, call with ``send_email=False``.
The activation email will make use of two templates:
......@@ -133,11 +133,11 @@ Methods:
argument ``user``) after the ``User`` and
``RegistrationProfile`` have been created, and after the email (if
any) has been sent.
``create_profile(user)``
Creates a ``RegistrationProfile`` for a given ``User``. Returns
the ``RegistrationProfile``.
The activation key for the ``RegistrationProfile`` will be a SHA1
hash, generated from a combination of the ``User``'s username and
a random salt.
......@@ -145,34 +145,34 @@ Methods:
``deleted_expired_users()``
Removes expired instances of ``RegistrationProfile`` and their
associated ``User`` objects.
Accounts to be deleted are identified by searching for instances
of ``RegistrationProfile`` with expired activation keys, and then
checking to see if their associated ``User`` instances have the
field ``is_active`` set to ``False``; any ``User`` who is both
inactive and has an expired activation key will be deleted.
It is recommended that this method be executed regularly as part
of your routine site maintenance; this application provides a
custom management command which will call this method, accessible
as ``manage.py cleanupregistration``.
Regularly clearing out accounts which have never been activated
serves two useful purposes:
1. It alleviates the ocasional need to reset a
``RegistrationProfile`` and/or re-send an activation email when
a user does not receive or does not act upon the initial
activation email; since the account will be deleted, the user
will be able to simply re-register and receive a new activation
key.
2. It prevents the possibility of a malicious user registering one
or more accounts and never activating them (thus denying the
use of those usernames to anyone else); since those accounts
will be deleted, the usernames will become available for use
again.
If you have a troublesome ``User`` and wish to disable their
account while keeping it in the database, simply delete the
associated ``RegistrationProfile``; an inactive ``User`` which
......
......@@ -127,7 +127,7 @@ need to do the following:
for details).
4. Add this line to your site's root URLConf::
(r'^accounts/', include('registration.urls')),
5. Link people to ``/accounts/register/`` so they can start signing
......
......@@ -23,7 +23,7 @@ change this, pass the name of a template as the keyword argument
``activation_key``
The activation key to validate and use for activating the
``User``.
**Optional arguments**
``extra_context``
......@@ -40,7 +40,7 @@ change this, pass the name of a template as the keyword argument
The ``User`` object corresponding to the account, if the
activation was successful. ``False`` if the activation was not
successful.
``expiration_days``
The number of days for which activation keys stay valid after
registration.
......@@ -49,7 +49,7 @@ Any extra variables supplied in the ``extra_context`` argument (see
above).
**Template:**
registration/activate.html or ``template_name`` keyword argument.
......@@ -57,14 +57,14 @@ registration/activate.html or ``template_name`` keyword argument.
============
Allow a new user to register an account.
Following successful registration, issue a redirect; by default, this
will be whatever URL corresponds to the named URL pattern
``registration_complete``, which will be
``/accounts/register/complete/`` if using the included URLConf. To
change this, point that named pattern at another URL, or pass your
preferred URL as the keyword argument ``success_url``.
By default, ``registration.forms.RegistrationForm`` will be used as
the registration form; to change this, pass a different form class as
the ``form_class`` keyword argument. The form class you specify must
......@@ -98,7 +98,7 @@ None.
``form``
The registration form.
Any extra variables supplied in the ``extra_context`` argument (see
above).
......
This diff is collapsed.
......@@ -27,8 +27,9 @@
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
......@@ -39,28 +40,54 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='PreregistrationProfile',
name="PreregistrationProfile",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('first_name', models.CharField(max_length=30)),
('last_name', models.CharField(max_length=30)),
('email', models.CharField(max_length=75)),
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
("first_name", models.CharField(max_length=30)),
("last_name", models.CharField(max_length=30)),
("email", models.CharField(max_length=75)),
],
options={
'verbose_name': 'preregistration profile',
'verbose_name_plural': 'preregistration profiles',
"verbose_name": "preregistration profile",
"verbose_name_plural": "preregistration profiles",
},
),
migrations.CreateModel(
name='RegistrationProfile',
name="RegistrationProfile",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('activation_key', models.CharField(max_length=40, verbose_name='activation key')),
('user', models.OneToOneField(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
(
"activation_key",
models.CharField(max_length=40, verbose_name="activation key"),
),
(
"user",
models.OneToOneField(
verbose_name="user",
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'registration profile',
'verbose_name_plural': 'registration profiles',
"verbose_name": "registration profile",
"verbose_name_plural": "registration profiles",
},
),
]
......@@ -27,7 +27,6 @@
from django.dispatch import Signal
# A new user has registered.
user_registered = Signal(providing_args=["user"])
......
......@@ -2,21 +2,21 @@
{% comment %}
* Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
* Contact: beat.support@idiap.ch
*
*
* This file is part of the beat.web module of the BEAT platform.
*
*
* Commercial License Usage
* Licensees holding valid commercial BEAT licenses may use this file in
* accordance with the terms contained in a written agreement between you
* and Idiap. For further information contact tto@idiap.ch
*
*
* Alternatively, this file may be used under the terms of the GNU Affero
* Public License version 3 as published by the Free Software and appearing
* in the file LICENSE.AGPL included in the packaging of this file.
* The BEAT platform is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
*
* You should have received a copy of the GNU Affero Public License along
* with the BEAT platform. If not, see http://www.gnu.org/licenses/.
{% endcomment %}
......
......@@ -2,21 +2,21 @@
{% comment %}
* Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
* Contact: beat.support@idiap.ch
*
*
* This file is part of the beat.web module of the BEAT platform.
*
*
* Commercial License Usage
* Licensees holding valid commercial BEAT licenses may use this file in
* accordance with the terms contained in a written agreement between you
* and Idiap. For further information contact tto@idiap.ch
*
*
* Alternatively, this file may be used under the terms of the GNU Affero
* Public License version 3 as published by the Free Software and appearing
* in the file LICENSE.AGPL included in the packaging of this file.
* The BEAT platform is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
*
* You should have received a copy of the GNU Affero Public License along
* with the BEAT platform. If not, see http://www.gnu.org/licenses/.
{% endcomment %}
......
......@@ -2,21 +2,21 @@
{% comment %}
* Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
* Contact: beat.support@idiap.ch
*
*
* This file is part of the beat.web module of the BEAT platform.
*
*
* Commercial License Usage
* Licensees holding valid commercial BEAT licenses may use this file in
* accordance with the terms contained in a written agreement between you
* and Idiap. For further information contact tto@idiap.ch
*
*
* Alternatively, this file may be used under the terms of the GNU Affero
* Public License version 3 as published by the Free Software and appearing
* in the file LICENSE.AGPL included in the packaging of this file.
* The BEAT platform is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
*
* You should have received a copy of the GNU Affero Public License along
* with the BEAT platform. If not, see http://www.gnu.org/licenses/.
{% endcomment %}
......
......@@ -2,21 +2,21 @@
{% comment %}
* Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
* Contact: beat.support@idiap.ch
*
*
* This file is part of the beat.web module of the BEAT platform.
*
*
* Commercial License Usage
* Licensees holding valid commercial BEAT licenses may use this file in
* accordance with the terms contained in a written agreement between you
* and Idiap. For further information contact tto@idiap.ch
*
*
* Alternatively, this file may be used under the terms of the GNU Affero
* Public License version 3 as published by the Free Software and appearing
* in the file LICENSE.AGPL included in the packaging of this file.
* The BEAT platform is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
*
* You should have received a copy of the GNU Affero Public License along
* with the BEAT platform. If not, see http://www.gnu.org/licenses/.
{% endcomment %}
......
......@@ -2,21 +2,21 @@
{% comment %}
* Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
* Contact: beat.support@idiap.ch
*
*
* This file is part of the beat.web module of the BEAT platform.
*
*
* Commercial License Usage
* Licensees holding valid commercial BEAT licenses may use this file in
* accordance with the terms contained in a written agreement between you
* and Idiap. For further information contact tto@idiap.ch
*
*
* Alternatively, this file may be used under the terms of the GNU Affero
* Public License version 3 as published by the Free Software and appearing
* in the file LICENSE.AGPL included in the packaging of this file.
* The BEAT platform is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
*
* You should have received a copy of the GNU Affero Public License along
* with the BEAT platform. If not, see http://www.gnu.org/licenses/.
{% endcomment %}
......
......@@ -27,15 +27,18 @@
from django import template
register = template.Library()
@register.filter
def tabindex(value, index):
"""Add a tabindex attribute to the widget for a bound field."""
value.field.widget.attrs['tabindex'] = index
value.field.widget.attrs["tabindex"] = index
return value
@register.filter(name='addclass')
@register.filter(name="addclass")
def addclass(field, kls):
"""Adds a class to the widget"""
return field.as_widget(attrs={"class":kls})
"""Adds a class to the widget"""
return field.as_widget(attrs={"class": kls})
......@@ -29,12 +29,10 @@
Views which allow users to create and activate accounts.
"""
from django.conf import settings
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.views.decorators.csrf import csrf_protect
from .forms import RegistrationFormTermsOfService
......@@ -43,9 +41,12 @@ from .forms import RegistrationSupervisorForm
from .models import RegistrationProfile
def activate(request, activation_key,
template_name='registration/activate.html',
extra_context=None):
def activate(
request,
activation_key,
template_name="registration/activate.html",
extra_context=None,
):
"""
Activate a ``User``'s account from an activation key, if their key
is valid and hasn't expired.
......@@ -89,11 +90,10 @@ def activate(request, activation_key,
registration/activate.html or ``template_name`` keyword argument.
"""
activationKey = activation_key.lower() # Normalize before trying anything with it.
account = RegistrationProfile.objects.activate_user(activation_key)
context = { 'account': account,
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS }
context = {"account": account, "expiration_days": settings.ACCOUNT_ACTIVATION_DAYS}
if extra_context is not None:
for key, value in extra_context.items():
......@@ -103,10 +103,13 @@ def activate(request, activation_key,
@csrf_protect
def register(request, success_url=None,
form_class=RegistrationFormTermsOfService,
template_name='registration/registration_form.html',
extra_context=None):
def register(
request,
success_url=None,
form_class=RegistrationFormTermsOfService,
template_name="registration/registration_form.html",
extra_context=None,
):
"""
Allow a new user to register an account.
......@@ -164,17 +167,19 @@ def register(request, success_url=None,
supervisor_form_active = False
if request.GET.get('registration') == "supervisor":
if request.GET.get("registration") == "supervisor":
supervisor_form_active = True
if request.method == 'POST':
if request.method == "POST":
# Check the form
if 'supervisor' not in request.POST:
if "supervisor" not in request.POST:
supervisor_form_active = True
form_supervisor = RegistrationSupervisorForm(data=request.POST, files=request.FILES)
form_supervisor = RegistrationSupervisorForm(
data=request.POST, files=request.FILES
)
form = form_class()
if form_supervisor.is_valid():
new_user = form_supervisor.save()
form_supervisor.save()
# success_url needs to be dynamically generated here; setting a
# a default value using reverse() will cause circular-import
# problems with the default URLConf for this application, which
......@@ -185,7 +190,7 @@ def register(request, success_url=None,
form = form_class(data=request.POST, files=request.FILES)
form_supervisor = RegistrationSupervisorForm()
if form.is_valid():
new_user = form.save()
form.save()
# success_url needs to be dynamically generated here; setting a
# a default value using reverse() will cause circular-import
# problems with the default URLConf for this application, which
......@@ -195,12 +200,13 @@ def register(request, success_url=None,
form = form_class()
form_supervisor = RegistrationFormTermsOfServiceSupervisor()
context = { 'form': form,
'form_supervisor': form_supervisor,
'supervisor_form_active': supervisor_form_active,
'documentation_link': settings.DOCUMENTATION_LINK,
'url_prefix':settings.URL_PREFIX }
context = {
"form": form,
"form_supervisor": form_supervisor,
"supervisor_form_active": supervisor_form_active,
"documentation_link": settings.DOCUMENTATION_LINK,
"url_prefix": settings.URL_PREFIX,
}
if extra_context is not None:
for key, value in extra_context.items():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment