# SPDX-License-Identifier: AGPL-3.0-or-later """Views for the dynamicsdns module.""" import datetime from django.contrib import messages from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView from django.views.generic.edit import FormView from plinth import views from plinth.modules import dynamicdns from .forms import DomainForm class DynamicDNSAppView(views.AppView): """View to show app status.""" app_id = 'dynamicdns' template_name = 'dynamicdns.html' _error_messages = { 'timeout': _('Connection timed out'), 'gaierror': _('Could not find server'), 'TimeoutError': _('Connection timed out'), 'ConnectionRefusedError': _('Server refused connection'), 'ValueError': _('Already up-to-date') } def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" context = super().get_context_data(**kwargs) config = dynamicdns.get_config() context['domains'] = config['domains'] status = dynamicdns.get_status() domains_status = {} for domain_name, domain in status['domains'].items(): if domain_name not in config['domains']: continue # Create naive datetime object in local timezone domain['timestamp'] = datetime.datetime.fromtimestamp( domain['timestamp']) if domain['timestamp'] else None domains_status[domain_name] = domain if domain['error_code'] in self._error_messages: domain['error_message'] = self._error_messages[ domain['error_code']] context['domains_status'] = domains_status return context class DomainView(FormView): """View to add/edit a dynamic DNS domain.""" template_name = 'dynamicdns-domain.html' form_class = DomainForm prefix = 'domain' success_url = reverse_lazy('dynamicdns:index') def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" context = super().get_context_data(**kwargs) domain_name = self.kwargs.get('domain') if not domain_name: context['title'] = _('Add Dynamic Domain') else: context['title'] = _('Edit Dynamic Domain') return context def get_initial(self): """Get the current values for the form.""" initial = super().get_initial() domain_name = self.kwargs.get('domain') domains = dynamicdns.get_config()['domains'] domain = {} if domains and domain_name and domain_name in domains: domain = domains[domain_name] initial.update(domain) return domain def form_valid(self, form): """Apply the changes submitted in the form.""" old_status = form.initial new_status = form.cleaned_data if old_status != new_status: try: _domain_delete(old_status['domain']) except KeyError: pass _domain_add(new_status['domain'], new_status) messages.success(self.request, _('Configuration updated')) # Perform an immediate update, even when configuration is not changed. dynamicdns.update_dns(None) return super().form_valid(form) def _domain_add(domain: str, domain_config: dict): """Add a domain to the configuration.""" config = dynamicdns.get_config() config['domains'][domain] = domain_config dynamicdns.set_config(config) dynamicdns.notify_domain_added(domain) def _domain_delete(domain: str): """Remove a domain from the configuration. Raises KeyError if the domain is not found in the configuration. """ config = dynamicdns.get_config() del config['domains'][domain] dynamicdns.set_config(config) if domain: dynamicdns.notify_domain_removed(domain) class DomainDeleteView(TemplateView): """Confirm and delete a domain.""" template_name = 'dynamicdns-domain-delete.html' def get_context_data(self, **kwargs): """Return additional context data for rendering the template.""" context = super().get_context_data(**kwargs) domain = self.kwargs['domain'] context['domain'] = domain context['title'] = str( _('Delete Domain {domain}?')).format(domain=domain) return context def post(self, request, domain): """Delete a domain.""" try: _domain_delete(domain) messages.success(request, _('Domain deleted.')) except KeyError: raise return redirect('dynamicdns:index')