diff --git a/src/postorius/tests/fixtures/vcr_cassettes/mailman_user_none_prefs.yaml b/src/postorius/tests/fixtures/vcr_cassettes/mailman_user_none_prefs.yaml new file mode 100644 index 0000000..3c5401e --- /dev/null +++ b/src/postorius/tests/fixtures/vcr_cassettes/mailman_user_none_prefs.yaml @@ -0,0 +1,357 @@ +interactions: +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/lists/foo.example.com + response: + body: {string: !!python/unicode '{"display_name": "Foo", "fqdn_listname": "foo@example.com", + "http_etag": "\"698a819bbb6b902096a8c5543cc7fac2328960d5\"", "list_id": "foo.example.com", + "list_name": "foo", "mail_host": "example.com", "member_count": 0, "self_link": + "http://localhost:9001/3.0/lists/foo.example.com", "volume": 1}'} + headers: + content-length: ['294'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: display_name=None&list_id=foo.example.com&pre_approved=True&pre_confirmed=True&pre_verified=True&subscriber=user%40example.com + headers: + accept-encoding: ['gzip, deflate'] + !!python/unicode content-type: [!!python/unicode application/x-www-form-urlencoded] + method: !!python/unicode POST + uri: http://localhost:9001/3.0/members + response: + body: {string: !!python/unicode ''} + headers: + content-length: ['0'] + location: ['http://localhost:9001/3.0/members/1'] + status: {code: 201, message: Created} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/preferences + response: + body: {string: !!python/unicode '{"http_etag": "\"b74f203eb6bb291486b3f3f98887098a7bec7de8\"", + "self_link": "http://localhost:9001/3.0/users/1/preferences"}'} + headers: + content-length: ['123'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/lists/foo.example.com + response: + body: {string: !!python/unicode '{"display_name": "Foo", "fqdn_listname": "foo@example.com", + "http_etag": "\"98cc998d4a30293ec17da639bce10617912a6e1e\"", "list_id": "foo.example.com", + "list_name": "foo", "mail_host": "example.com", "member_count": 1, "self_link": + "http://localhost:9001/3.0/lists/foo.example.com", "volume": 1}'} + headers: + content-length: ['294'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: subscriber=user%40example.com + headers: + accept-encoding: ['gzip, deflate'] + !!python/unicode content-type: [!!python/unicode application/x-www-form-urlencoded] + method: !!python/unicode POST + uri: http://localhost:9001/3.0/members/find + response: + body: {string: !!python/unicode '{"entries": [{"address": "http://localhost:9001/3.0/addresses/user@example.com", + "delivery_mode": "regular", "email": "user@example.com", "http_etag": "\"daa2142986cd1722628e6cdc494b6649d7e635f4\"", + "list_id": "foo.example.com", "member_id": 1, "moderation_action": "defer", + "role": "member", "self_link": "http://localhost:9001/3.0/members/1", "user": + "http://localhost:9001/3.0/users/1"}], "http_etag": "\"507c947b2e96fe6b2037a446f6d9897e0707b684\"", + "start": 0, "total_size": 1}'} + headers: + content-length: ['481'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/members/1/preferences + response: + body: {string: !!python/unicode '{"http_etag": "\"4747934e4e9b7e99c3faf1c663491719cbfe2dd0\"", + "self_link": "http://localhost:9001/3.0/members/1/preferences"}'} + headers: + content-length: ['125'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/addresses/user@example.com/preferences + response: + body: {string: !!python/unicode '{"http_etag": "\"c557a0cd7d400f67152a6f6e5e2c14858e632323\"", + "self_link": "http://localhost:9001/3.0/addresses/user@example.com/preferences"}'} + headers: + content-length: ['142'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/user@example.com + response: + body: {string: !!python/unicode '{"created_on": "2005-08-01T07:49:23", "http_etag": + "\"ef76f3eeb810f6a24e4157eb493e81b8dbf76260\"", "is_server_owner": false, + "password": "$6$rounds=640174$Aw8ypj2F6ngmjWkv$B.xNJDSwm0TzBmtsRsSmvswdu1oeEl2uaaA1NbiL83E8ylEycVikTdZsLv/C9/MO9Qbpr4M3mDYVXiTEmM.dp.", + "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}'} + headers: + content-length: ['324'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/users/1/addresses + response: + body: {string: !!python/unicode '{"entries": [{"email": "user@example.com", "http_etag": + "\"52ace72a40745c0e003216ad8fbeb62bbf38e072\"", "original_email": "user@example.com", + "registered_on": "2005-08-01T07:49:23", "self_link": "http://localhost:9001/3.0/addresses/user@example.com", + "user": "http://localhost:9001/3.0/users/1", "verified_on": "2005-08-01T07:49:23"}], + "http_etag": "\"9580a3b8be6093d25a53e0656b32438452dabdbd\"", "start": 0, "total_size": + 1}'} + headers: + content-length: ['425'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: subscriber=user%40example.com + headers: + accept-encoding: ['gzip, deflate'] + !!python/unicode content-type: [!!python/unicode application/x-www-form-urlencoded] + method: !!python/unicode POST + uri: http://localhost:9001/3.0/members/find + response: + body: {string: !!python/unicode '{"entries": [{"address": "http://localhost:9001/3.0/addresses/user@example.com", + "delivery_mode": "regular", "email": "user@example.com", "http_etag": "\"daa2142986cd1722628e6cdc494b6649d7e635f4\"", + "list_id": "foo.example.com", "member_id": 1, "moderation_action": "defer", + "role": "member", "self_link": "http://localhost:9001/3.0/members/1", "user": + "http://localhost:9001/3.0/users/1"}], "http_etag": "\"507c947b2e96fe6b2037a446f6d9897e0707b684\"", + "start": 0, "total_size": 1}'} + headers: + content-length: ['481'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +- request: + body: null + headers: + accept-encoding: ['gzip, deflate'] + method: !!python/unicode GET + uri: http://localhost:9001/3.0/members/1/preferences + response: + body: {string: !!python/unicode '{"http_etag": "\"4747934e4e9b7e99c3faf1c663491719cbfe2dd0\"", + "self_link": "http://localhost:9001/3.0/members/1/preferences"}'} + headers: + content-length: ['125'] + content-type: [application/json; charset=utf-8] + status: {code: 200, message: OK} +version: 1 diff --git a/src/postorius/tests/mailman_api_tests/test_user.py b/src/postorius/tests/mailman_api_tests/test_user.py index a40d973..3adf81d 100644 --- a/src/postorius/tests/mailman_api_tests/test_user.py +++ b/src/postorius/tests/mailman_api_tests/test_user.py @@ -25,6 +25,7 @@ from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.test import TestCase +from mock import patch from six.moves.urllib_parse import quote from postorius.models import MailmanUser @@ -78,3 +79,47 @@ self.assertEqual(len(response.context["zipped_data"]), 3) #self.assertEqual( # response.context["formset"].initial['archive_policy'], 'public') + + @MM_VCR.use_cassette('mailman_user_none_prefs.yaml') + def test_preferences_none(self): + # Mailman does not accept None values for boolean preferences. When + # those preferences are unset, they must be excluded from the POST + # data. + self.client.login(username='user', password='testpass') + self.foo_list.subscribe(self.user.email, + pre_verified=True, pre_confirmed=True, pre_approved=True) + prefs_with_none = ( + 'receive_own_postings', 'acknowledge_posts', + 'hide_address', 'receive_list_copy', + ) + # Prepare a Preferences subclass that will check the POST data + import mailmanclient._client + class TestingPrefs(mailmanclient._client.Preferences): + testcase = self + def save(self): + for pref in prefs_with_none: + self.testcase.assertNotIn(pref, self._changed_rest_data) + # Now check the relevant URLs + with patch('mailmanclient._client.Preferences') as pref_class: + pref_class.side_effect = TestingPrefs + # Simple forms + for url in ( + reverse('user_mailmansettings'), + reverse('user_list_options', args=[self.foo_list.list_id]), + ): + response = self.client.post(url, + dict((pref, None) for pref in prefs_with_none)) + self.assertEqual(response.status_code, 302) + # Formsets + for url in ('user_address_preferences', 'user_subscription_preferences'): + url = reverse(url) + post_data = dict( + ('form-0-%s' % pref, None) + for pref in prefs_with_none) + post_data.update({ + 'form-TOTAL_FORMS': '1', + 'form-INITIAL_FORMS': '0', + 'form-MAX_NUM_FORMS': '' + }) + response = self.client.post(url, post_data) + self.assertEqual(response.status_code, 302) diff --git a/src/postorius/views/user.py b/src/postorius/views/user.py index 6fcad66..0761ae2 100644 --- a/src/postorius/views/user.py +++ b/src/postorius/views/user.py @@ -47,12 +47,15 @@ def post(self, request): try: mm_user = MailmanUser.objects.get(address=request.user.email) - global_preferences_form = UserPreferences(request.POST) - if global_preferences_form.is_valid(): + form = UserPreferences(request.POST) + if form.is_valid(): preferences = mm_user.preferences - for key in global_preferences_form.fields.keys(): - preferences[ - key] = global_preferences_form.cleaned_data[key] + for key in form.fields.keys(): + if form.cleaned_data[key] is not None: + # None: nothing set yet. Remember to remove this test + # when Mailman accepts None as a "reset to default" + # value. + preferences[key] = form.cleaned_data[key] preferences.save() messages.success(request, _('Your preferences have been updated.')) else: @@ -87,7 +90,11 @@ for form, address in zip(formset.forms, mm_user.addresses): preferences = address.preferences for key in form.fields.keys(): - preferences[key] = form.cleaned_data[key] + if form.cleaned_data[key] is not None: + # None: nothing set yet. Remember to remove this test + # when Mailman accepts None as a "reset to default" + # value. + preferences[key] = form.cleaned_data[key] preferences.save() messages.success(request, _('Your preferences have been updated.')) else: @@ -135,17 +142,23 @@ form = UserPreferences(request.POST) if form.is_valid(): for key in form.cleaned_data.keys(): - preferences[key] = form.cleaned_data[key] + if form.cleaned_data[key] is not None: + # None: nothing set yet. Remember to remove this test + # when Mailman accepts None as a "reset to default" + # value. + preferences[key] = form.cleaned_data[key] preferences.save() messages.success(request, _('Your preferences have been updated.')) + return redirect("user_list_options", list_id) else: messages.error(request, _('Something went wrong.')) else: form = UserPreferences(initial=subscription.preferences) user_emails = [request.user.email] + request.user.other_emails subscription_form = ChangeSubscriptionForm(user_emails, initial={'email': subscription.email}) - return render_to_response('postorius/user/list_options.html', - {'form': form, 'list': mlist, 'change_subscription_form': subscription_form}, context_instance=RequestContext(request)) + return render(request, 'postorius/user/list_options.html', + {'form': form, 'list': mlist, + 'change_subscription_form': subscription_form}) class UserSubscriptionPreferencesView(MailmanUserView): @@ -161,7 +174,11 @@ for form, subscription in zip(formset.forms, subscriptions): preferences = subscription.preferences for key in form.cleaned_data.keys(): - preferences[key] = form.cleaned_data[key] + if form.cleaned_data[key] is not None: + # None: nothing set yet. Remember to remove this test + # when Mailman accepts None as a "reset to default" + # value. + preferences[key] = form.cleaned_data[key] preferences.save() messages.success(request, _('Your preferences have been updated.')) else: