Merge branch 'small' into 'master'
[mailman-postorious.git] / src / postorius / views / template.py
blobbe621495553e0000daa2c839e11099880f65cf30
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2018-2022 by the Free Software Foundation, Inc.
4 # This file is part of Postorius.
6 # Postorius is free software: you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation, either version 3 of the License, or (at your option)
9 # any later version.
11 # Postorius is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 # more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Postorius. If not, see <http://www.gnu.org/licenses/>.
20 from django.conf import settings
21 from django.core.exceptions import MultipleObjectsReturned
22 from django.db import IntegrityError
23 from django.http import Http404, HttpResponse, HttpResponseBadRequest
24 from django.shortcuts import redirect
25 from django.urls import reverse
26 from django.views.decorators.http import require_safe
27 from django.views.generic import CreateView, DeleteView, ListView, UpdateView
29 from postorius.auth.mixins import DomainOwnerMixin, ListOwnerMixin
30 from postorius.forms.list_forms import TemplateUpdateForm
31 from postorius.models import Domain, EmailTemplate, List
34 def _clean_with_no_strip(field, data):
35 """Clean Django's CharField field without strip=True.
37 :param field: Instance of the Charfield.
38 :param data: The un-cleaned data from the form.
39 :returns: Cleaned data.
40 """
41 field.strip = False
42 return field.clean(data)
45 class ListContextMixin:
47 def get_context_data(self, *args, **kwargs):
48 context = super().get_context_data(*args, **kwargs)
49 context['list'] = List.objects.get_or_404(
50 fqdn_listname=self.kwargs['list_id'])
51 return context
54 class ListTemplateIndexView(ListOwnerMixin, ListContextMixin, ListView):
56 model = EmailTemplate
57 template_name = 'postorius/lists/template_list.html'
59 def get_queryset(self):
60 return EmailTemplate.objects.filter(
61 identifier=self.kwargs['list_id'])
64 class ListTemplateCreateView(ListOwnerMixin, ListContextMixin, CreateView):
66 template_name = 'postorius/lists/template_add.html'
67 model = EmailTemplate
68 fields = ['name', 'data']
70 def get_success_url(self):
71 return reverse(
72 'list_template_list', args=(self.kwargs['list_id'],))
74 def form_valid(self, form):
75 formdata = form.cleaned_data
76 formdata['data'] = _clean_with_no_strip(form.fields['data'],
77 form.data['data'])
78 formdata['identifier'] = self.kwargs['list_id']
79 formdata['context'] = 'list'
80 email_template = EmailTemplate(**formdata)
81 # Try to save the model. Some of the unique constraints that we have
82 # depend on the mailing_list attribute added above. So, even though
83 # we check for form validity, the save can fail.
84 try:
85 email_template.save()
86 except IntegrityError as e:
87 form.add_error('name', str(e))
88 form.add_error('name',
89 'You already have this template set. '
90 'Use edit instead of creating a new one.')
91 return self.form_invalid(form)
92 return redirect(self.get_success_url())
95 class ListTemplateUpdateView(ListOwnerMixin, ListContextMixin, UpdateView):
97 template_name = 'postorius/lists/template_update.html'
98 model = EmailTemplate
99 form_class = TemplateUpdateForm
101 def get_success_url(self):
102 return reverse(
103 'list_template_list', args=(self.kwargs['list_id'],))
106 class ListTemplateDeleteView(ListOwnerMixin, ListContextMixin, DeleteView):
108 template_name = 'postorius/lists/template_delete.html'
109 model = EmailTemplate
111 def get_success_url(self):
112 return reverse(
113 'list_template_list', args=(self.kwargs['list_id'],))
116 class DomainContextMixin:
118 def get_context_data(self, *args, **kwargs):
119 context = super().get_context_data(*args, **kwargs)
120 context['domain'] = Domain.objects.get_or_404(
121 mail_host=self.kwargs['domain'])
122 return context
125 class DomainTemplateIndexView(DomainOwnerMixin, DomainContextMixin, ListView):
127 model = EmailTemplate
128 template_name = 'postorius/domain/template_index.html'
129 header = 'Index'
131 def get_queryset(self):
132 return EmailTemplate.objects.filter(identifier=self.kwargs['domain'])
135 class DomainTemplateCreateView(
136 DomainOwnerMixin, DomainContextMixin, CreateView):
138 model = EmailTemplate
139 template_name = 'postorius/domain/template_add.html'
140 fields = ['name', 'data']
141 header = 'New Template'
143 def get_success_url(self):
144 return reverse('domain_template_list',
145 args=(self.kwargs['domain'],))
147 def form_valid(self, form):
148 formdata = form.cleaned_data
149 formdata['data'] = _clean_with_no_strip(form.fields['data'],
150 form.data['data'])
151 formdata['identifier'] = self.kwargs['domain']
152 formdata['context'] = 'domain'
153 template = EmailTemplate(**formdata)
154 # Try to save the model. Some of the unique constraints that we have
155 # depend on the mailing_list attribute added above. So, even though we
156 # check for form validity, the save can fail.
157 try:
158 template.save()
159 except IntegrityError as e:
160 form.add_error('name', str(e))
161 form.add_error('name',
162 'You already have this template set.'
163 'Use edit instead of creating a new one.')
164 return self.form_invalid(form)
165 return redirect(self.get_success_url())
168 class DomainTemplateUpdateView(
169 DomainOwnerMixin, DomainContextMixin, UpdateView):
171 model = EmailTemplate
172 template_name = 'postorius/domain/template_add.html'
173 form_class = TemplateUpdateForm
174 header = 'Edit Template'
176 def get_success_url(self):
177 return reverse('domain_template_list',
178 args=(self.kwargs['domain'],))
181 class DomainTemplateDeleteView(
182 DomainOwnerMixin, DomainContextMixin, DeleteView):
184 template_name = 'postorius/domain/template_delete.html'
185 model = EmailTemplate
187 def get_success_url(self):
188 return reverse('domain_template_list',
189 args=(self.kwargs['domain'],))
192 @require_safe
193 def get_template_data(request, context, identifier, name):
194 # At this point, the request should be authenticated and it's method should
195 # be GET. We just need to find the correct template and return it's
196 # content, if it exists, return a 404 otherwise.
197 if context not in ('list', 'domain'):
198 return HttpResponseBadRequest(
199 'context should be either "list" or "domain"')
200 data = dict(name=name, identifier=identifier, context=context)
201 # Depending on the context, populate the right identifier.
202 try:
203 template = EmailTemplate.objects.get(**data)
204 except EmailTemplate.DoesNotExist:
205 raise Http404('Template is not defined.')
206 except MultipleObjectsReturned:
207 raise HttpResponseBadRequest('Multiple Templates exist')
209 content_type = 'text/plain'
210 if settings.DEFAULT_CHARSET:
211 content_type += '; charset=' + settings.DEFAULT_CHARSET
212 return HttpResponse(template.data,
213 content_type=content_type)