Commit 27d6276a authored by Chris Lamb's avatar Chris Lamb
Browse files

Add ability for admins to manage users. (Closes: #47)

parent b293f7a2
from django import forms
from django.utils import timezone
from django.contrib.auth import get_user_model
User = get_user_model()
......@@ -28,3 +30,30 @@ class FilterForm(forms.Form):
val = self.cleaned_data['order_by']
return val or self.fields['order_by'].choices[0][0]
class UserForm(forms.ModelForm):
email_validated = forms.BooleanField(required=False)
class Meta:
model = User
fields = (
'email',
'is_active',
'is_staff',
)
def save(self):
instance = super().save(commit=False)
if self.cleaned_data['email_validated']:
# Was unset before; set to "now".
if instance.email_validated is None:
instance.email_validated = timezone.now()
else:
# Clear the validation
instance.email_validated = None
instance.save()
return instance
......@@ -5,25 +5,27 @@ class SmokeTest(TestCase):
def test_view(self):
self.assertGET(200, 'admin:users:view', login=self.admin)
def test_set_admin(self):
def test_edit_GET(self):
self.assertGET(200, 'admin:users:edit', self.user.pk, login=self.admin)
def test_edit_POST(self):
new_email = 'updated@example.com'
self.user.email_validated = None
self.user.save()
self.assertTrue(self.user.is_active)
self.assertFalse(self.user.is_staff)
self.assertPOST(
{},
'admin:users:set-admin',
self.user.pk,
login=self.admin,
)
self.user.refresh_from_db()
self.assertTrue(self.user.is_staff)
def test_remove_admin(self):
self.test_set_admin()
self.assertPOST({
'email': new_email,
'is_staff': 'on',
'is_active': '',
'email_validated': True,
}, 'admin:users:edit', self.user.pk, login=self.admin)
self.assertPOST(
{},
'admin:users:remove-admin',
self.user.pk,
login=self.admin,
)
self.user.refresh_from_db()
self.assertFalse(self.user.is_staff)
self.assertTrue(self.user.is_staff)
self.assertTrue(self.user.email_validated)
self.assertFalse(self.user.is_active)
self.assertEqual(self.user.email, new_email)
......@@ -7,8 +7,6 @@ app_name = 'admin_users'
urlpatterns = (
path('admin/users', views.view,
name='view'),
path('admin/users/<int:user_id>/set-admin', views.admin_toggle,
{'enable': True}, name='set-admin'),
path('admin/users/<int:user_id>/remove-admin', views.admin_toggle,
{'enable': False}, name='remove-admin'),
path('admin/users/<int:user_id>', views.edit,
name='edit'),
)
......@@ -7,7 +7,7 @@ from django.views.decorators.http import require_POST
from freenodejobs.utils.paginator import AutoPaginator
from freenodejobs.utils.decorators import staff_required
from .forms import FilterForm
from .forms import FilterForm, UserForm
User = get_user_model()
......@@ -28,6 +28,28 @@ def view(request):
})
@staff_required
def edit(request, user_id):
user = get_object_or_404(User, pk=user_id)
if request.method == 'POST':
form = UserForm(request.POST, instance=user)
if form.is_valid():
form.save()
messages.success(request, "User updated.")
return redirect(request.path)
else:
form = UserForm(instance=user)
return render(request, 'admin/users/edit.html', {
'form': form,
'user': user,
})
@require_POST
@staff_required
def admin_toggle(request, user_id, enable):
......
{% extends "admin/base.html" %}
{% block title %}Editing user {{ user.email }}&mdash; {{ block.super }}{% endblock %}
{% block subtitle %}Editing user {{ user.email }}{% endblock %}
{% block admin %}
{% if form.non_field_errors %}
<div class="alert alert-danger mb-5">
<h4 class="alert-heading">
Validation error
</h4>
<hr>
{% for x in form.non_field_errors %}
<p class="mb-1">{{ x }}</p>
{% endfor %}
</div>
{% endif %}
<form
method="POST"
class=""
action=""
{% if form.is_multipart %}enctype="multipart/form-data"{% endif %}
>
{% csrf_token %}
<div class="form-group col-md-12">
<label class="col-form-label" for="{{ form.email.id_for_label }}">
Email
</label>
<input
id="{{ form.email.id_for_label }}"
type="email"
class="form-control {% if form.errors %}{% if form.errors.email %}is-invalid{% else %}{% if not form.non_field_errors %}is-valid{% endif %}{% endif %}{% endif %}"
name="{{ form.email.html_name }}"
value="{{ form.email.value|default:"" }}"
maxlength="{{ form.email.field.max_length|default:"" }}"
placeholder=""
{% if form.email.field.required %}required{% endif %}
>
{% for x in form.errors.email %}
<div class="invalid-feedback">{{ x }}</div>
{% endfor %}
</div>
<div class="form-group col-md-12">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input
id="{{ form.email_validated.id_for_label }}"
type="checkbox"
name="{{ form.email_validated.html_name }}"
class="form-check-input {% if form.errors %}{% if form.errors.email_validated %}is-invalid{% else %}{% if not form.non_field_errors %}is-valid{% endif %}{% endif %}{% endif %}"
{% if form.instance.email_validated %}checked{% endif %}
>
Email validated?
{% if form.email_validated.value %}
<small class="form-text text-muted">
Previously validated {{ form.instance.email_validated }}
</small>
{% endif %}
{% for x in form.errors.email_validated %}
<div class="invalid-feedback">{{ x }}</div>
{% endfor %}
</label>
</div>
</div>
<div class="form-group col-md-12">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input
id="{{ form.is_staff.id_for_label }}"
type="checkbox"
name="{{ form.is_staff.html_name }}"
class="form-check-input {% if form.errors %}{% if form.errors.is_staff %}is-invalid{% else %}{% if not form.non_field_errors %}is-valid{% endif %}{% endif %}{% endif %}"
{% if form.is_staff.value %}checked{% endif %}
>
User is admin?
{% for x in form.errors.is_staff %}
<div class="invalid-feedback">{{ x }}</div>
{% endfor %}
</label>
</div>
</div>
<div class="form-group col-md-12">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input
id="{{ form.is_active.id_for_label }}"
type="checkbox"
name="{{ form.is_active.html_name }}"
class="form-check-input {% if form.errors %}{% if form.errors.is_active %}is-invalid{% else %}{% if not form.non_field_errors %}is-valid{% endif %}{% endif %}{% endif %}"
{% if form.is_active.value %}checked{% endif %}
>
Account enabled?
{% for x in form.errors.is_active %}
<div class="invalid-feedback">{{ x }}</div>
{% endfor %}
</label>
</div>
</div>
<button type="submit" class="btn btn-primary my-3">
Save
</button>
<dl>
<dt>Date joined</dt>
<dd>{{ user.date_joined }}</dd>
<dt>Last login</dt>
<dd>{{ user.last_login|default:"(none)" }}</dd>
</dl>
</form>
{% endblock %}
......@@ -60,17 +60,7 @@
<abbr title="{{ user.date_joined }} UTC">{{ user.date_joined|timesince }} ago</abbr>
</td>
<td>
{% if user.is_staff %}
<form method="POST" action="{% url "admin:users:remove-admin" user.pk %}" class="form-inline">
{% csrf_token %}
<button type="submit" class="btn btn-outline btn-sm">Remove as admin</button>
</form>
{% else %}
<form method="POST" action="{% url "admin:users:set-admin" user.pk %}" class="form-inline">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-sm">Set as admin</button>
</form>
{% endif %}
<a href="{% url "admin:users:edit" user.pk %}" class="btn btn-primary btn-outline">Edit</a>
</td>
</tr>
{% endfor %}
......
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