Update copyright years
[mailman-postorious.git] / src / postorius / views / user.py
blobea83d2194fe77af2e90e0238b85f1dde98ebc035
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 1998-2021 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 import logging
21 from urllib.error import HTTPError
23 from django.contrib import messages
24 from django.contrib.auth.decorators import login_required
25 from django.forms import formset_factory
26 from django.http import Http404
27 from django.shortcuts import render
28 from django.urls import reverse, reverse_lazy
29 from django.utils.decorators import method_decorator
30 from django.utils.translation import gettext as _
31 from django.views.generic import FormView
33 from allauth.account.models import EmailAddress
34 from django_mailman3.lib.mailman import get_mailman_client, get_mailman_user
36 from postorius.forms import (
37 ChangeSubscriptionForm, UserPreferences, UserPreferencesFormset)
38 from postorius.models import List, MailmanUser, SubscriptionMode
39 from postorius.utils import set_preferred
40 from postorius.views.generic import MailmanClientMixin
43 logger = logging.getLogger(__name__)
46 class UserPreferencesView(FormView, MailmanClientMixin):
47 """Generic view for the logged-in user's various preferences."""
49 form_class = UserPreferences
51 def get_context_data(self, **kwargs):
52 data = super(UserPreferencesView, self).get_context_data(**kwargs)
53 data['mm_user'] = self.mm_user
54 return data
56 def get_form_kwargs(self):
57 kwargs = super(UserPreferencesView, self).get_form_kwargs()
58 kwargs['preferences'] = self._get_preferences()
59 return kwargs
61 def _set_view_attributes(self, request, *args, **kwargs):
62 self.mm_user = MailmanUser.objects.get_or_create_from_django(
63 request.user)
65 @method_decorator(login_required)
66 def dispatch(self, request, *args, **kwargs):
67 self._set_view_attributes(request, *args, **kwargs)
68 return super(UserPreferencesView, self).dispatch(
69 request, *args, **kwargs)
71 def form_valid(self, form):
72 try:
73 form.save()
74 except HTTPError as e:
75 messages.error(self.request, e.msg)
76 if form.has_changed():
77 messages.success(
78 self.request, _('Your preferences have been updated.'))
79 else:
80 messages.info(self.request, _('Your preferences did not change.'))
81 return super(UserPreferencesView, self).form_valid(form)
84 class UserMailmanSettingsView(UserPreferencesView):
85 """The logged-in user's global Mailman preferences."""
87 form_class = UserPreferences
88 template_name = 'postorius/user/mailman_settings.html'
89 success_url = reverse_lazy('user_mailmansettings')
91 def _get_preferences(self):
92 # Get the defaults and pre-populate so view shows them
93 combinedpreferences = self._get_combined_preferences()
94 for key in combinedpreferences:
95 if key != "self_link":
96 self.mm_user.preferences[key] = combinedpreferences[key]
98 # This is a bit of a hack so preferences behave as users expect
99 # We probably don't want to save, only display here
100 # but this means that whatever preferences the users see first are
101 # the ones they have unless they explicitly change them
102 self.mm_user.preferences.save()
104 return self.mm_user.preferences
106 def _get_combined_preferences(self):
107 # Get layers of default preferences to match how they are applied
108 # We ignore self_link as we don't want to over-write it
109 defaultpreferences = get_mailman_client().preferences
110 combinedpreferences = {}
111 for key in defaultpreferences:
112 if key != "self_link":
113 combinedpreferences[key] = defaultpreferences[key]
115 # Clobber defaults with any preferences already set
116 for key in self.mm_user.preferences:
117 if key != "self_link":
118 combinedpreferences[key] = self.mm_user.preferences[key]
120 return(combinedpreferences)
123 class UserAddressPreferencesView(UserPreferencesView):
124 """The logged-in user's address-based Mailman Preferences."""
126 template_name = 'postorius/user/address_preferences.html'
127 success_url = reverse_lazy('user_address_preferences')
129 def get_form_class(self):
130 return formset_factory(
131 UserPreferences, formset=UserPreferencesFormset, extra=0)
133 def _get_preferences(self):
134 return [address.preferences for address in self.mm_user.addresses]
136 def _get_combined_preferences(self):
137 # grab the default preferences
138 defaultpreferences = get_mailman_client().preferences
140 # grab your global preferences
141 globalpreferences = self.mm_user.preferences
143 # start a new combined preferences object
144 combinedpreferences = []
146 for address in self.mm_user.addresses:
147 # make a per-address prefs object
148 prefs = {}
150 # initialize with default preferences
151 for key in defaultpreferences:
152 if key != "self_link":
153 prefs[key] = defaultpreferences[key]
155 # overwrite with user's global preferences
156 for key in globalpreferences:
157 if key != "self_link":
158 prefs[key] = globalpreferences[key]
160 # overwrite with address-specific preferences
161 for key in address.preferences:
162 if key != "self_link":
163 prefs[key] = address.preferences[key]
164 combinedpreferences.append(prefs)
166 # put the combined preferences back on the original object
167 for key in prefs:
168 if key != "self_link":
169 address.preferences[key] = prefs[key]
171 return combinedpreferences
173 def get_context_data(self, **kwargs):
174 data = super(UserAddressPreferencesView, self).get_context_data(
175 **kwargs)
176 data['formset'] = data.pop('form')
177 for form, address in list(zip(
178 data['formset'].forms, self.mm_user.addresses)):
179 form.address = address
180 return data
183 class UserListOptionsView(UserPreferencesView):
184 """The logged-in user's subscription preferences."""
186 form_class = UserPreferences
187 template_name = 'postorius/user/list_options.html'
189 def _get_subscription(self):
190 subscription = None
191 for s in self.mm_user.subscriptions:
192 if s.role == 'member' and s.list_id == self.mlist.list_id:
193 subscription = s
194 break
195 if not subscription:
196 raise Http404(_('Subscription does not exist'))
197 return subscription
199 def _set_view_attributes(self, request, *args, **kwargs):
200 super(UserListOptionsView, self)._set_view_attributes(
201 request, *args, **kwargs)
202 self.mlist = List.objects.get_or_404(fqdn_listname=kwargs['list_id'])
203 self.subscription = self._get_subscription()
204 if (self.subscription.subscription_mode ==
205 SubscriptionMode.as_user.name):
206 self.subscriber = self.subscription.user.user_id
207 else:
208 self.subscriber = self.subscription.email
210 def _get_preferences(self):
211 return self.subscription.preferences
213 def get_context_data(self, **kwargs):
214 data = super(UserListOptionsView, self).get_context_data(**kwargs)
215 data['mlist'] = self.mlist
216 user_emails = EmailAddress.objects.filter(
217 user=self.request.user, verified=True).order_by(
218 "email").values_list("email", flat=True)
219 mm_user = get_mailman_user(self.request.user)
220 primary_email = None
221 if mm_user.preferred_address is None:
222 primary_email = set_preferred(self.request.user, mm_user)
223 else:
224 primary_email = mm_user.preferred_address.email
225 data['change_subscription_form'] = ChangeSubscriptionForm(
226 user_emails, mm_user.user_id, primary_email,
227 initial={'subscriber': self.subscriber})
228 return data
230 def get_success_url(self):
231 return reverse(
232 'user_list_options', kwargs=dict(list_id=self.mlist.list_id))
235 class UserSubscriptionPreferencesView(UserPreferencesView):
236 """The logged-in user's subscription-based Mailman Preferences."""
238 template_name = 'postorius/user/subscription_preferences.html'
239 success_url = reverse_lazy('user_subscription_preferences')
241 def _get_subscriptions(self):
242 subscriptions = []
243 for s in self.mm_user.subscriptions:
244 if s.role != 'member':
245 continue
246 subscriptions.append(s)
247 return subscriptions
249 def _set_view_attributes(self, request, *args, **kwargs):
250 super(UserSubscriptionPreferencesView, self)._set_view_attributes(
251 request, *args, **kwargs)
252 self.subscriptions = self._get_subscriptions()
254 def get_form_class(self):
255 return formset_factory(
256 UserPreferences, formset=UserPreferencesFormset, extra=0)
258 def _get_preferences(self):
259 return [sub.preferences for sub in self.subscriptions]
261 def _get_combined_preferences(self):
262 # grab the default preferences
263 defaultpreferences = get_mailman_client().preferences
265 # grab your global preferences
266 globalpreferences = self.mm_user.preferences
268 # start a new combined preferences object
269 combinedpreferences = []
271 for sub in self.subscriptions:
272 # make a per-address prefs object
273 prefs = {}
275 # initialize with default preferences
276 for key in defaultpreferences:
277 if key != "self_link":
278 prefs[key] = defaultpreferences[key]
280 # overwrite with user's global preferences
281 for key in globalpreferences:
282 if key != "self_link":
283 prefs[key] = globalpreferences[key]
285 # overwrite with address-based preferences
286 # There is currently no better way to do this,
287 # we may consider revisiting.
288 addresspreferences = {}
289 for address in self.mm_user.addresses:
290 if sub.email == address.email:
291 addresspreferences = address.preferences
293 for key in addresspreferences:
294 if key != "self_link":
295 prefs[key] = addresspreferences[key]
297 # overwrite with subscription-specific preferences
298 for key in sub.preferences:
299 if key != "self_link":
300 prefs[key] = sub.preferences[key]
302 combinedpreferences.append(prefs)
304 return combinedpreferences
305 # return [sub.preferences for sub in self.subscriptions]
307 def get_context_data(self, **kwargs):
308 data = super(UserSubscriptionPreferencesView, self).get_context_data(
309 **kwargs)
310 data['formset'] = data.pop('form')
311 for form, subscription in list(zip(
312 data['formset'].forms, self.subscriptions)):
313 form.list_id = subscription.list_id
314 return data
317 @login_required
318 def user_subscriptions(request):
319 """Shows the subscriptions of a user."""
320 mm_user = MailmanUser.objects.get_or_create_from_django(request.user)
321 memberships = [m for m in mm_user.subscriptions]
322 return render(request, 'postorius/user/subscriptions.html',
323 {'memberships': memberships})