Skip to content

Commit

Permalink
Merge 3007268 into bdad809
Browse files Browse the repository at this point in the history
  • Loading branch information
triole committed Jan 15, 2019
2 parents bdad809 + 3007268 commit 96520ae
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 17 deletions.
22 changes: 22 additions & 0 deletions rdmo/accounts/forms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import logging
from django import forms
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from .models import AdditionalField, AdditionalFieldValue, ConsentFieldValue

log = logging.getLogger(__name__)


class ProfileForm(forms.ModelForm):

Expand Down Expand Up @@ -81,3 +84,22 @@ def signup(self, request, user):
if settings.ACCOUNT_TERMS_OF_USE:
consent = ConsentFieldValue(user=user, consent=self.cleaned_data['consent'])
consent.save()


class RemoveForm(forms.Form):

def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
kwargs.setdefault('label_suffix', '')
super(RemoveForm, self).__init__(*args, **kwargs)

email = forms.CharField(widget=forms.TextInput(attrs={'required': 'false'}))
email.label = _('Email')
email.widget.attrs = {'class': 'form-control', 'placeholder': email.label}

password = forms.CharField(widget=forms.PasswordInput)
password.label = _('Password')
password.widget.attrs = {'class': 'form-control', 'placeholder': password.label}

consent = forms.BooleanField(required=True)
consent.label = _("I confirm that I want my profile to be completely removed. This can not be undone!")
12 changes: 12 additions & 0 deletions rdmo/accounts/templates/profile/profile_remove_closed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends 'core/page.html' %}
{% load i18n %}

{% block page %}

<h2>{% trans "Delete profile" %}</h2>

<p>
{% trans "We are sorry, but you cannot remove your profile here." %}
</p>

{% endblock %}
12 changes: 12 additions & 0 deletions rdmo/accounts/templates/profile/profile_remove_failed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends 'core/page.html' %}
{% load i18n %}

{% block page %}

<h2>{% trans "Delete profile" %}</h2>

<p>
{% trans "Profile removal failed. Please make sure that you enter the correct data." %}
</p>

{% endblock %}
21 changes: 21 additions & 0 deletions rdmo/accounts/templates/profile/profile_remove_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends 'core/page.html' %}
{% load i18n %}
{% load core_tags %}

{% block page %}

<h1>{% trans "Delete profile" %}</h1>

<p>{% trans "If you are willing to remove all your account information please proceed by entering the necessary data below. Once completed this can not be undone." %} </p>

<form method="post" action="{% url 'profile_remove' %}" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />

{% include 'core/bootstrap_form_fields.html' %}

<input type="submit" value="{% trans 'Delete profile' %}" class="btn btn-danger" />
<input type="submit" name="cancel" value="{% trans 'Cancel' %}" class="btn" formnovalidate/>
</form>

{% endblock %}
12 changes: 12 additions & 0 deletions rdmo/accounts/templates/profile/profile_remove_success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends 'core/page.html' %}
{% load i18n %}

{% block page %}

<h2>{% trans "Delete profile" %}</h2>

<p>
{% trans "Your profile was successfully removed." %}
</p>

{% endblock %}
13 changes: 13 additions & 0 deletions rdmo/accounts/templates/profile/profile_update_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,17 @@ <h1>{% trans "Update profile" %}</h1>
<input type="submit" name="cancel" value="{% trans 'Cancel' %}" class="btn" />
</form>

<h2>{% trans "Delete profile" %}</h2>

{% if settings.PROFILE_DELETE %}

<p>
{% trans 'If you want to remove all your account information please proceed by clicking the button below.' %}
</p>

<a class="btn btn-danger" role="button" href="{% url 'profile_remove' %}">
{% trans "Delete profile" %}
</a>
{% endif %}

{% endblock %}
67 changes: 67 additions & 0 deletions rdmo/accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,70 @@ def test_password_reset_post_valid(self):
# get the password_reset page
response = self.client.get(urls[0])
self.assertRedirects(response, reverse('account_reset_password_from_key', args=['4','set-password']))


class RemoveTests(AccountsViewTestCase):

def setUp(self):
translation.activate('en')
self.instances = User.objects.all()

def test_remove_user_get(self):
if settings.PROFILE_DELETE:
self.client.login(username='user', password='user')

url = reverse('profile_remove')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

def test_remove_user_post(self):
if settings.PROFILE_DELETE:
self.client.login(username='user', password='user')

url = reverse('profile_remove')
response = self.client.post(url, {
'email': 'user@example.com',
'password': 'user',
'consent': True,
})
self.assertEqual(response.status_code, 200)
self.assertEqual(User.objects.filter(username='user').exists(), False)

def test_remove_user_post_invalid_email(self):
if settings.PROFILE_DELETE:
self.client.login(username='user', password='user')

url = reverse('profile_remove')
response = self.client.post(url, {
'email': 'invalid',
'password': 'user',
'consent': True,
})
self.assertEqual(response.status_code, 200)
self.assertEqual(User.objects.filter(username='user').exists(), True)

def test_remove_user_post_invalid_password(self):
if settings.PROFILE_DELETE:
self.client.login(username='user', password='user')

url = reverse('profile_remove')
response = self.client.post(url, {
'email': 'user@example.com',
'password': 'invalid',
'consent': True,
})
self.assertEqual(response.status_code, 200)
self.assertEqual(User.objects.filter(username='user').exists(), True)

def test_remove_user_post_invalid_consent(self):
if settings.PROFILE_DELETE:
self.client.login(username='user', password='user')

url = reverse('profile_remove')
response = self.client.post(url, {
'email': 'user@example.com',
'password': 'user',
'consent': False,
})
self.assertEqual(response.status_code, 200)
self.assertEqual(User.objects.filter(username='user').exists(), True)
5 changes: 3 additions & 2 deletions rdmo/accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@

from rest_framework import routers

from .views import profile_update
from .views import profile_update, remove_user
from .viewsets import UserApiViewSet

# regular views

accounts_patterns = [
# edit own profile
url(r'^$', profile_update, name='profile_update'),
url('^remove', remove_user, name='profile_remove'),
]

if settings.ACCOUNT or settings.SOCIALACCOUNT:
# include django-allauth urls
accounts_patterns += [
url(r'^', include('allauth.urls')),
url(r'^', include('allauth.urls'))
]
else:
accounts_patterns += [
Expand Down
29 changes: 28 additions & 1 deletion rdmo/accounts/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from django.contrib.auth.models import Group, Permission
import logging

from django.contrib.auth.models import User, Group, Permission

from .settings import GROUPS

log = logging.getLogger(__name__)


def get_full_name(user):
if user.first_name and user.last_name:
Expand All @@ -15,3 +19,26 @@ def set_group_permissions():
group = Group.objects.get(name=name)
for codename in permissions:
group.permissions.add(Permission.objects.get(codename=codename))


def delete_user(user, email, password):
username = user.username

try:
database_user = User.objects.get(email=email)
except User.DoesNotExist:
log.debug('User with email "%s" requested for deletion does not exist', email)
return False

if user == database_user and user.check_password(password):
try:
user.delete()
log.debug('User "%s" deleted', username)
return True

except Exception as e:
log.debug('An exception (%s) occured during user "%s" deletion: ', (str(e), username))
return False
else:
log.debug('Deletion of user "%s" failed because of an invalid password', username)
return False
40 changes: 39 additions & 1 deletion rdmo/accounts/views.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import logging

from django.conf import settings
from django.shortcuts import render
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from rdmo.core.utils import get_referer_path_info, get_next

from .forms import ProfileForm
from .forms import ProfileForm, RemoveForm
from .utils import delete_user

log = logging.getLogger(__name__)


@login_required()
def profile_update(request):
if settings.PROFILE_UPDATE:
log.debug('Update user %s', request.user.username)

form = ProfileForm(request.POST or None, instance=request.user)

if request.method == 'POST':
if 'cancel' in request.POST:
log.debug('User %s uldate cancelled', request.user.username)
return HttpResponseRedirect(get_next(request))

if form.is_valid():
Expand All @@ -26,3 +35,32 @@ def profile_update(request):
})
else:
return render(request, 'profile/profile_update_closed.html')


@login_required()
def remove_user(request):
if settings.PROFILE_DELETE:
log.debug('Remove user form for "%s"', request.user.username)

form = RemoveForm(request.POST or None, request=request)

if request.method == 'POST':
if 'cancel' in request.POST:
log.debug('User %s removal cancelled', str(request.user))
return HttpResponseRedirect('/account')

if form.is_valid():
log.debug('Deleting user %s', request.user.username)

if delete_user(request.user, request.POST['email'], request.POST['password']):
logout(request)
return render(request, 'profile/profile_remove_success.html')
else:
return render(request, 'profile/profile_remove_failed.html')

return render(request, 'profile/profile_remove_form.html', {
'form': form,
'next': get_referer_path_info(request, default='/')
})
else:
return render(request, 'profile/profile_remove_closed.html')
4 changes: 3 additions & 1 deletion rdmo/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
]

PROFILE_UPDATE = True
PROFILE_DELETE = True

ACCOUNT = False
ACCOUNT_SIGNUP = False
Expand Down Expand Up @@ -160,6 +161,7 @@
'ACCOUNT_TERMS_OF_USE',
'SOCIALACCOUNT',
'PROFILE_UPDATE',
'PROFILE_DELETE',
'SHIBBOLETH'
]

Expand Down Expand Up @@ -275,7 +277,7 @@
'path': 'addon/mode/overlay.min.js',
'sri': 'sha256-ffWkw3Pn4ieMygm1vwdRKcMtBJ6E6kuBi8GlVVPXWEs='
},
{
{
'path': 'mode/django/django.min.js',
'sri': 'sha256-6hO1TjC+3W73p+kXnCqcHVjfRa4KMdG7hvWencnu0XM='
}
Expand Down
14 changes: 14 additions & 0 deletions rdmo/core/static/core/css/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ a {
&:focus {
color: $link-color-focus;
}

&.btn {
color: white;

&:visited {
color: white;
}
&:hover {
color: white;
}
&:focus {
color: white;
}
}
}

code {
Expand Down
3 changes: 3 additions & 0 deletions rdmo/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from django.contrib.auth.mixins import \
PermissionRequiredMixin as DjangoPermissionRequiredMixin
from django.contrib.auth.views import redirect_to_login
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils import translation
from django.views.generic.base import View

from rest_framework import mixins, viewsets
from rules.contrib.views import \
PermissionRequiredMixin as RulesPermissionRequiredMixin
Expand All @@ -32,6 +34,7 @@ def home(request):
return render(request, 'core/home.html', {'form': AuthenticationForm()})


@login_required
def about(request):
return render(request, 'core/about.html')

Expand Down
Binary file modified rdmo/locale/de/LC_MESSAGES/django.mo
Binary file not shown.

0 comments on commit 96520ae

Please sign in to comment.