diff --git a/example_project/settings.py b/example_project/settings.py index 99aacd1..1146d54 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -63,6 +63,7 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'postorius', + 'django_mailman3', 'django_browserid', ) @@ -199,22 +200,27 @@ # EMAIL_CONFIRMATION_SUBJECT = 'Confirmation needed' # You can enable logging by uncommenting the following lines -# LOGGING = { -# 'version': 1, -# 'disable_existing_loggers': False, -# 'handlers': { -# 'console': { -# 'class': 'logging.StreamHandler' -# }, -# }, -# 'loggers': { -# 'django': { -# 'handlers': ['console'], -# 'level': 'INFO', -# }, -# 'django_browserid': { -# 'handlers': ['console'], -# 'level': 'DEBUG', -# }, -# }, -# } +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + # 'class': 'logging.StreamHandler' + 'class': 'logging.NullHandler' + }, + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + 'level': 'INFO', + }, + 'postorius': { + 'handlers': ['console'], + 'level': 'INFO', + }, + 'django_browserid': { + 'handlers': ['console'], + 'level': 'INFO', + }, + }, +} diff --git a/setup.py b/setup.py index 2bb5baf..81c41ef 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ install_requires=[ 'Django>=1.8', 'Django<1.10', + 'django-mailman3', 'django-browserid', 'mailmanclient', ], diff --git a/src/postorius/lib/scrub.py b/src/postorius/lib/scrub.py deleted file mode 100644 index 86952a6..0000000 --- a/src/postorius/lib/scrub.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (C) 2011-2012 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -"""Cleanse a message for archiving.""" - -from __future__ import absolute_import, unicode_literals - -import os -import re -import binascii -from mimetypes import guess_all_extensions -from email.header import decode_header, make_header -from email.errors import HeaderParseError - -pre = re.compile(r'[/\\:]') -sre = re.compile(r'[^-\w.]') -dre = re.compile(r'^\.*') - -BR = '
\n' - -NEXT_PART = re.compile(r'--------------[ ]next[ ]part[ ]--------------\n') - - -def guess_extension(ctype, ext): - all_exts = guess_all_extensions(ctype, strict=False) - if ext in all_exts: - return ext - return all_exts and all_exts[0] - - -def get_charset(message, default="ascii", guess=False): - if message.get_content_charset(): - return message.get_content_charset().decode("ascii") - if message.get_charset(): - return message.get_charset().decode("ascii") - charset = default - if not guess: - return charset - text = message.get_payload(decode=True) - for encoding in ["ascii", "utf-8", "iso-8859-15"]: - try: - text.decode(encoding) - except UnicodeDecodeError: - continue - else: - charset = encoding - break - return charset - - -def oneline(s): - """Inspired by mailman.utilities.string.oneline""" - try: - h = make_header(decode_header(s)) - ustr = h.__unicode__() - return ''.join(ustr.splitlines()) - except (LookupError, UnicodeError, ValueError, HeaderParseError): - return ''.join(s.splitlines()) - - -class Scrubber(object): - def __init__(self, msg): - self.msg = msg - - def scrub(self): - attachments = [] - for part_num, part in enumerate(self.msg.walk()): - ctype = part.get_content_type() - if not isinstance(ctype, unicode): - ctype = ctype.decode("ascii") - if ctype == 'text/plain': - disposition = part.get('content-disposition') - if disposition and disposition.decode( - "ascii", "replace").strip().startswith("attachment"): - attachments.append(self.parse_attachment(part, part_num)) - part.set_payload('') - elif ctype == 'text/html': - attachments.append(self.parse_attachment(part, part_num, - filter_html=False)) - part.set_payload('') - elif ctype == 'message/rfc822': - attachments.append(self.parse_attachment(part, part_num)) - part.set_payload('') - elif part.get_payload() and not part.is_multipart(): - payload = part.get_payload(decode=True) - ctype = part.get_content_type() - if not isinstance(ctype, unicode): - ctype.decode("ascii") - if payload is None: - continue - attachments.append(self.parse_attachment(part, part_num)) - if self.msg.is_multipart(): - text = [] - for part in self.msg.walk(): - if not part.get_payload() or part.is_multipart(): - continue - partctype = part.get_content_type() - if partctype != 'text/plain' and partctype != 'text/html': - continue - try: - t = part.get_payload(decode=True) or '' - except (binascii.Error, TypeError): - t = part.get_payload() or '' - partcharset = get_charset(part, guess=True) - try: - t = t.decode(partcharset, 'replace') - except (UnicodeError, LookupError, ValueError, - AssertionError): - t = t.decode('ascii', 'replace') - if isinstance(t, basestring): - if not t.endswith('\n'): - t += '\n' - text.append(t) - - text = u"\n".join(text) - else: - text = self.msg.get_payload(decode=True) - charset = get_charset(self.msg, guess=True) - try: - text = text.decode(charset, "replace") - except (UnicodeError, LookupError, ValueError, AssertionError): - text = text.decode('ascii', 'replace') - - next_part_match = NEXT_PART.search(text) - if next_part_match: - text = text[0:next_part_match.start(0)] - - return (text, attachments) - - def parse_attachment(self, part, counter, filter_html=True): - decodedpayload = part.get_payload(decode=True) - ctype = part.get_content_type() - if not isinstance(ctype, unicode): - ctype = ctype.decode("ascii") - charset = get_charset(part, default=None, guess=False) - try: - filename = oneline(part.get_filename('')) - except (TypeError, UnicodeDecodeError): - filename = u"attachment.bin" - filename, fnext = os.path.splitext(filename) - ext = fnext or guess_extension(ctype, fnext) - if not ext: - if ctype == 'message/rfc822': - ext = '.txt' - else: - ext = '.bin' - ext = sre.sub('', ext) - if not filename: - filebase = u'attachment' - else: - parts = pre.split(filename) - filename = parts[-1] - filename = dre.sub('', filename) - filename = sre.sub('', filename) - filebase = filename - if ctype == 'message/rfc822': - submsg = part.get_payload() - decodedpayload = str(submsg) - return (counter, filebase + ext, ctype, charset, decodedpayload) diff --git a/src/postorius/templatetags/pagination.py b/src/postorius/templatetags/pagination.py deleted file mode 100644 index f73fb7f..0000000 --- a/src/postorius/templatetags/pagination.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 1998-2016 by the Free Software Foundation, Inc. -# -# This file is part of Postorius. -# -# Postorius is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# Postorius is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# Postorius. If not, see . - -from django import template -from django.utils.html import conditional_escape - - -register = template.Library() - - -@register.simple_tag(takes_context=True) -def add_to_query_string(context, *args, **kwargs): - """Adds or replaces parameters in the query string""" - qs = context["request"].GET.copy() - # create a dict from every args couple - new_qs_elements = dict(zip(args[::2], args[1::2])) - new_qs_elements.update(kwargs) - # don't use the .update() method, it appends instead of overwriting. - for key, value in new_qs_elements.iteritems(): - qs[key] = value - return conditional_escape(qs.urlencode()) diff --git a/src/postorius/tests/mailman_api_tests/test_list_member_options.py b/src/postorius/tests/mailman_api_tests/test_list_member_options.py index 78bc29d..6fd7fa9 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_member_options.py +++ b/src/postorius/tests/mailman_api_tests/test_list_member_options.py @@ -87,7 +87,6 @@ UserPreferences) def test_moderation_action(self): - member = self.foo_list.get_member('test@example.com') self.assertIsNone( self.foo_list.get_member('test@example.com').moderation_action) self.client.login(username='testsu', password='testpass') diff --git a/src/postorius/tests/utils.py b/src/postorius/tests/utils.py index e3c18c6..47b0ec0 100644 --- a/src/postorius/tests/utils.py +++ b/src/postorius/tests/utils.py @@ -21,9 +21,10 @@ from django.conf import settings from django.contrib import messages from django.core.urlresolvers import reverse -from django.test import RequestFactory, TestCase +from django.test import TestCase from mock import MagicMock from six.moves.urllib_parse import quote +from django_mailman3.tests.utils import get_flash_messages from postorius.utils import get_client from mailmanclient.testing.vcr_helpers import get_vcr @@ -88,20 +89,6 @@ return mock_object -def get_flash_messages(response, empty=True): - if "messages" not in response.cookies: - return [] - # A RequestFactory will not run the messages middleware, and thus will - # not delete the messages after retrieval. - dummy_request = RequestFactory().get("/") - dummy_request.COOKIES["messages"] = response.cookies["messages"].value - msgs = list(messages.storage.cookie.CookieStorage(dummy_request)) - if empty: - del response.client.cookies["messages"] - return msgs -get_flash_messages.__test__ = False - - class ViewTestCase(TestCase): use_vcr = True diff --git a/src/postorius/views/rest.py b/src/postorius/views/rest.py index 1d21b55..18043e8 100644 --- a/src/postorius/views/rest.py +++ b/src/postorius/views/rest.py @@ -28,7 +28,7 @@ from postorius.auth.decorators import list_moderator_required from postorius.models import List -from postorius.lib.scrub import Scrubber +from django_mailman3.lib.scrub import Scrubber def parse(message):