1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2018-2023 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)
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
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.
42 return field
.clean(data
)
45 class ListContextMixin
:
46 def get_context_data(self
, *args
, **kwargs
):
47 context
= super().get_context_data(*args
, **kwargs
)
48 context
['list'] = List
.objects
.get_or_404(
49 fqdn_listname
=self
.kwargs
['list_id']
54 class ListTemplateIndexView(ListOwnerMixin
, ListContextMixin
, ListView
):
57 template_name
= 'postorius/lists/template_list.html'
59 def get_queryset(self
):
60 return EmailTemplate
.objects
.filter(identifier
=self
.kwargs
['list_id'])
63 class ListTemplateCreateView(ListOwnerMixin
, ListContextMixin
, CreateView
):
65 template_name
= 'postorius/lists/template_add.html'
67 fields
= ['name', 'data']
69 def get_success_url(self
):
70 return reverse('list_template_list', args
=(self
.kwargs
['list_id'],))
72 def form_valid(self
, form
):
73 formdata
= form
.cleaned_data
74 formdata
['data'] = _clean_with_no_strip(
75 form
.fields
['data'], form
.data
['data']
77 formdata
['identifier'] = self
.kwargs
['list_id']
78 formdata
['context'] = 'list'
79 email_template
= EmailTemplate(**formdata
)
80 # Try to save the model. Some of the unique constraints that we have
81 # depend on the mailing_list attribute added above. So, even though
82 # we check for form validity, the save can fail.
85 except IntegrityError
as e
:
86 form
.add_error('name', str(e
))
89 'You already have this template set. '
90 'Use edit instead of creating a new one.',
92 return self
.form_invalid(form
)
93 return redirect(self
.get_success_url())
96 class ListTemplateUpdateView(ListOwnerMixin
, ListContextMixin
, UpdateView
):
98 template_name
= 'postorius/lists/template_update.html'
100 form_class
= TemplateUpdateForm
102 def get_success_url(self
):
103 return reverse('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('list_template_list', args
=(self
.kwargs
['list_id'],))
115 class DomainContextMixin
:
116 def get_context_data(self
, *args
, **kwargs
):
117 context
= super().get_context_data(*args
, **kwargs
)
118 context
['domain'] = Domain
.objects
.get_or_404(
119 mail_host
=self
.kwargs
['domain']
124 class DomainTemplateIndexView(DomainOwnerMixin
, DomainContextMixin
, ListView
):
126 model
= EmailTemplate
127 template_name
= 'postorius/domain/template_index.html'
130 def get_queryset(self
):
131 return EmailTemplate
.objects
.filter(identifier
=self
.kwargs
['domain'])
134 class DomainTemplateCreateView(
135 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', args
=(self
.kwargs
['domain'],))
146 def form_valid(self
, form
):
147 formdata
= form
.cleaned_data
148 formdata
['data'] = _clean_with_no_strip(
149 form
.fields
['data'], 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.
159 except IntegrityError
as e
:
160 form
.add_error('name', str(e
))
163 'You already have this template set.'
164 'Use edit instead of creating a new one.',
166 return self
.form_invalid(form
)
167 return redirect(self
.get_success_url())
170 class DomainTemplateUpdateView(
171 DomainOwnerMixin
, DomainContextMixin
, UpdateView
174 model
= EmailTemplate
175 template_name
= 'postorius/domain/template_add.html'
176 form_class
= TemplateUpdateForm
177 header
= 'Edit Template'
179 def get_success_url(self
):
180 return reverse('domain_template_list', args
=(self
.kwargs
['domain'],))
183 class DomainTemplateDeleteView(
184 DomainOwnerMixin
, DomainContextMixin
, DeleteView
187 template_name
= 'postorius/domain/template_delete.html'
188 model
= EmailTemplate
190 def get_success_url(self
):
191 return reverse('domain_template_list', args
=(self
.kwargs
['domain'],))
195 def get_template_data(request
, context
, identifier
, name
):
196 # At this point, the request should be authenticated and it's method should
197 # be GET. We just need to find the correct template and return it's
198 # content, if it exists, return a 404 otherwise.
199 if context
not in ('list', 'domain'):
200 return HttpResponseBadRequest(
201 'context should be either "list" or "domain"'
203 data
= dict(name
=name
, identifier
=identifier
, context
=context
)
204 # Depending on the context, populate the right identifier.
206 template
= EmailTemplate
.objects
.get(**data
)
207 except EmailTemplate
.DoesNotExist
:
208 raise Http404('Template is not defined.')
209 except MultipleObjectsReturned
:
210 raise HttpResponseBadRequest('Multiple Templates exist')
212 content_type
= 'text/plain'
213 if settings
.DEFAULT_CHARSET
:
214 content_type
+= '; charset=' + settings
.DEFAULT_CHARSET
215 return HttpResponse(template
.data
, content_type
=content_type
)