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)
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
:
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'])
54 class ListTemplateIndexView(ListOwnerMixin
, ListContextMixin
, ListView
):
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'
68 fields
= ['name', 'data']
70 def get_success_url(self
):
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'],
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.
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'
99 form_class
= TemplateUpdateForm
101 def get_success_url(self
):
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
):
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'])
125 class DomainTemplateIndexView(DomainOwnerMixin
, DomainContextMixin
, ListView
):
127 model
= EmailTemplate
128 template_name
= 'postorius/domain/template_index.html'
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'],
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
))
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'],))
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.
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
)