Add admin UI to cancel pending retirements
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n admin_static admin_modify %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="content-main">
|
||||
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form.non_field_errors|length > 0 %}
|
||||
<p class="errornote">
|
||||
{% trans "Please correct the errors below." %}
|
||||
</p>
|
||||
{{ form.non_field_errors }}
|
||||
{% endif %}
|
||||
|
||||
{% blocktrans with username=retirement.user.username %}Are you sure you want to cancel retirement for user "{{ username }}"? {% endblocktrans %}
|
||||
|
||||
<fieldset class="module aligned">
|
||||
{% for field in form %}
|
||||
<div class="form-row">
|
||||
{{ field.errors }}
|
||||
{{ field.label_tag }}
|
||||
{{ field }}
|
||||
{% if field.field.help_text %}
|
||||
<p class="help">
|
||||
{{ field.field.help_text|safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
|
||||
<div class="submit-row">
|
||||
<input type="submit" class="default" value="{% trans 'Submit' %}">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
40
openedx/core/djangoapps/user_api/accounts/forms.py
Normal file
40
openedx/core/djangoapps/user_api/accounts/forms.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Django forms for accounts
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class RetirementQueueDeletionForm(forms.Form):
|
||||
"""
|
||||
Admin form to facilitate learner retirement cancellation
|
||||
"""
|
||||
cancel_retirement = forms.BooleanField(required=True)
|
||||
|
||||
def save(self, retirement):
|
||||
"""
|
||||
When the form is POSTed we double-check the retirment status
|
||||
and perform the necessary steps to cancel the retirement
|
||||
request.
|
||||
"""
|
||||
if retirement.current_state.state_name != 'PENDING':
|
||||
self.add_error(
|
||||
None,
|
||||
# Translators: 'current_state' is a string from an enumerated list indicating the learner's retirement
|
||||
# state. Example: FORUMS_COMPLETE
|
||||
"Retirement requests can only be cancelled for users in the PENDING state."
|
||||
" Current request state for '{original_username}': {current_state}".format(
|
||||
original_username=retirement.original_username,
|
||||
current_state=retirement.current_state.state_name
|
||||
)
|
||||
)
|
||||
raise ValidationError('Retirement is in the wrong state!')
|
||||
|
||||
# Load the user record using the retired email address -and- change the email address back.
|
||||
retirement.user.email = retirement.original_email
|
||||
retirement.user.save()
|
||||
|
||||
# Delete the user retirement status record.
|
||||
# No need to delete the accompanying "permanent" retirement request record - it gets done via Django signal.
|
||||
retirement.delete()
|
||||
@@ -1,8 +1,16 @@
|
||||
"""
|
||||
Django admin configuration pages for the user_api app
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin, messages
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseForbidden, HttpResponseRedirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openedx.core.djangoapps.user_api.accounts.forms import RetirementQueueDeletionForm
|
||||
from .models import UserRetirementPartnerReportingStatus, RetirementState, UserRetirementStatus, UserRetirementRequest
|
||||
|
||||
|
||||
@@ -24,23 +32,108 @@ class UserRetirementStatusAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin interface for the UserRetirementStatus model.
|
||||
"""
|
||||
list_display = ('user', 'original_username', 'current_state', 'modified')
|
||||
list_display = ('user', 'original_username', 'current_state', 'modified', 'retirement_actions')
|
||||
list_filter = ('current_state',)
|
||||
raw_id_fields = ('user',)
|
||||
search_fields = ('original_username', 'retired_username', 'original_email', 'retired_email', 'original_name')
|
||||
|
||||
def cancel_retirement(self, request, retirement_id):
|
||||
"""
|
||||
Executed when the admin clicks the "Cancel" button on a UserRetirementStatus row,
|
||||
this handles the confirmation view form, top level error handling, and permissions.
|
||||
"""
|
||||
if not request.user.has_perm('user_api.change_userretirementstatus'):
|
||||
return HttpResponseForbidden(_("Permission Denied"))
|
||||
|
||||
retirement = self.get_object(request, retirement_id)
|
||||
|
||||
redirect_url = reverse(
|
||||
'admin:user_api_userretirementstatus_changelist',
|
||||
current_app=self.admin_site.name,
|
||||
)
|
||||
|
||||
if retirement is None:
|
||||
self.message_user(request, _('Retirement does not exist!'), level=messages.ERROR)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
if request.method != 'POST':
|
||||
form = RetirementQueueDeletionForm()
|
||||
else:
|
||||
form = RetirementQueueDeletionForm(request.POST)
|
||||
if form.is_valid():
|
||||
try:
|
||||
form.save(retirement)
|
||||
self.message_user(request, _('Success'))
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
except ValidationError:
|
||||
# An exception in form.save will display errors on the form page
|
||||
pass
|
||||
|
||||
context = self.admin_site.each_context(request)
|
||||
context['opts'] = self.model._meta
|
||||
context['form'] = form
|
||||
context['retirement'] = retirement
|
||||
|
||||
return TemplateResponse(
|
||||
request,
|
||||
'admin/user_api/accounts/cancel_retirement_action.html',
|
||||
context,
|
||||
)
|
||||
|
||||
def get_urls(self):
|
||||
"""
|
||||
Adds our custom URL to the admin
|
||||
"""
|
||||
urls = super(UserRetirementStatusAdmin, self).get_urls()
|
||||
custom_urls = [
|
||||
url(
|
||||
r'^(?P<retirement_id>.+)/cancel_retirement/$',
|
||||
self.admin_site.admin_view(self.cancel_retirement),
|
||||
name='cancel-retirement',
|
||||
),
|
||||
]
|
||||
return custom_urls + urls
|
||||
|
||||
def retirement_actions(self, obj):
|
||||
"""
|
||||
Creates the HTML button in the admin for cancelling retirements,
|
||||
but only if the row is in the right state.
|
||||
"""
|
||||
try:
|
||||
if obj.current_state.state_name == 'PENDING':
|
||||
return format_html(
|
||||
'<a class="button" href="{}">{}</a> ',
|
||||
reverse('admin:cancel-retirement', args=[obj.pk]),
|
||||
_('Cancel')
|
||||
)
|
||||
return format_html('')
|
||||
except RetirementState.DoesNotExist:
|
||||
# If the states don't exist, nothing to do here
|
||||
return format_html('')
|
||||
|
||||
retirement_actions.short_description = _('Actions')
|
||||
retirement_actions.allow_tags = True
|
||||
|
||||
def get_actions(self, request):
|
||||
"""
|
||||
Removes the default bulk delete option provided by Django,
|
||||
it doesn't do what we need for this model.
|
||||
"""
|
||||
actions = super(UserRetirementStatusAdmin, self).get_actions(request)
|
||||
if 'delete_selected' in actions:
|
||||
del actions['delete_selected']
|
||||
return actions
|
||||
|
||||
# Remove "add" button from admin
|
||||
def has_add_permission(self, request):
|
||||
"""
|
||||
Removes the "add" button from admin
|
||||
"""
|
||||
return False
|
||||
|
||||
# Remove "delete" button from admin
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
"""
|
||||
Removes the "delete" button from admin
|
||||
"""
|
||||
return False
|
||||
|
||||
class Meta(object):
|
||||
|
||||
Reference in New Issue
Block a user