Files
allianceauth/allianceauth/services/abstract.py
2024-12-04 22:08:47 +10:00

114 lines
3.9 KiB
Python

"""
Abstract view classes for building services.
These view classes are provided as convenience only. If they
don't make sense to use in your service, there is no obligation
to use these views. You are free to build the internal structure
of the service as you like.
"""
import logging
from collections import OrderedDict
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError, models
from django.shortcuts import Http404, redirect, render
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import DeleteView, UpdateView
from django.views.generic.detail import SingleObjectMixin
from .forms import ServicePasswordModelForm
logger = logging.getLogger(__name__)
class AbstractServiceModel(models.Model):
user = models.OneToOneField('auth.User',
primary_key=True,
on_delete=models.CASCADE,
related_name='%(app_label)s'
)
class Meta:
abstract = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.credentials = OrderedDict()
# Should be set with a dict of service credentials (username, password etc) when changed
def update_password(self, password=None):
pass
def reset_password(self):
pass
class BaseServiceView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""
Define:
permission_required
"""
index_redirect = 'services:services'
success_url = reverse_lazy(index_redirect)
model = AbstractServiceModel # Overload
service_name = 'base' # Overload
class ServiceCredentialsViewMixin:
template_name = 'services/service_credentials.html'
class BaseCreatePasswordServiceAccountView(BaseServiceView, ServiceCredentialsViewMixin):
def get(self, request):
logger.debug(f"{self.__class__.__name__} called by user {request.user}")
try:
svc_obj = self.model.objects.create(user=request.user)
except IntegrityError:
messages.error(request, _("That service account already exists"))
return redirect(self.index_redirect)
return render(request, self.template_name, context={'credentials': svc_obj.credentials, 'service': self.service_name, 'view': self})
class ServicesCRUDMixin(SingleObjectMixin):
def get_object(self, queryset=None):
"""
Returns the object the view is displaying.
"""
if queryset is None:
queryset = self.get_queryset()
try:
return queryset.get(user__pk=self.request.user.pk)
except ObjectDoesNotExist as e:
raise Http404 from e
class BaseDeactivateServiceAccountView(ServicesCRUDMixin, BaseServiceView, DeleteView):
template_name = 'services/service_confirm_delete.html'
class BaseSetPasswordServiceAccountView(ServicesCRUDMixin, BaseServiceView, UpdateView):
template_name = 'services/service_password.html'
form_class = ServicePasswordModelForm # You should overload this with a subclass
def post(self, request, *args, **kwargs):
result = super().post(request, *args, **kwargs)
if self.get_form().is_valid():
messages.success(request, _(f"Successfully set your {self.service_name} password"))
return result
class BaseResetPasswordServiceAccountView(ServicesCRUDMixin, BaseServiceView, ServiceCredentialsViewMixin):
"""
Set a random password
"""
def get(self, request):
svc_obj = self.get_object()
svc_obj.reset_password()
return render(request, self.template_name, context={'credentials': svc_obj.credentials, 'service': self.service_name, 'view': self})