Merge Request #40
Private mailman lists
Only show public lists by default. Private lists are only visible if user if a member of that list. That applies to /dashboard /archives/thread and /accounts/username pages. So you cannot view a collaboration of an user in a private-list in their profile if you are not also in the privatelist. Note: You'll need mailman-api running to see this functionality. The tests uses mocked data from the mailman-api.
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Carolina Ramalho <carol15022@hotmail.com>
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Alexandre Barbosa <alexandreab@live.com>
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Alexandre Barbosa <alexandreab@live.com>
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Alexandre Barbosa <alexandreab@live.com>
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Alexandre Barbosa <alexandreab@live.com>
-
Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Alexandre Barbosa <alexandreab@live.com>
- 8 of 13 commits displayed. Click here to show all
| @@ -38,7 +38,7 @@ def unsubscribe(listname, address): | @@ -38,7 +38,7 @@ def unsubscribe(listname, address): | ||
| 38 | 38 | ||
| 39 | 39 | ||
| 40 | def update_subscription(address, lists): | 40 | def update_subscription(address, lists): |
| 41 | - current_lists = address_lists(address) | 41 | + current_lists = mailing_lists(address=address) |
| 1 |
|
||
| 42 | 42 | ||
| 43 | for maillist in current_lists: | 43 | for maillist in current_lists: |
| 44 | if maillist not in lists: | 44 | if maillist not in lists: |
| @@ -49,14 +49,11 @@ def update_subscription(address, lists): | @@ -49,14 +49,11 @@ def update_subscription(address, lists): | ||
| 49 | subscribe(maillist, address) | 49 | subscribe(maillist, address) |
| 50 | 50 | ||
| 51 | 51 | ||
| 52 | -def address_lists(address, description=''): | 52 | +def mailing_lists(**kwargs): |
| 53 | url = get_url() | 53 | url = get_url() |
| 54 | 54 | ||
| 55 | - params = {'address': address, | ||
| 56 | - 'description': description} | ||
| 57 | - | ||
| 58 | try: | 55 | try: |
| 59 | - lists = requests.get(url, timeout=TIMEOUT, params=params) | 56 | + lists = requests.get(url, timeout=TIMEOUT, params=kwargs) |
| 60 | except: | 57 | except: |
| 61 | LOGGER.exception('Unable to list mailing lists') | 58 | LOGGER.exception('Unable to list mailing lists') |
| 62 | return [] | 59 | return [] |
| @@ -64,15 +61,22 @@ def address_lists(address, description=''): | @@ -64,15 +61,22 @@ def address_lists(address, description=''): | ||
| 64 | return lists.json() | 61 | return lists.json() |
| 65 | 62 | ||
| 66 | 63 | ||
| 67 | -def all_lists(*args, **kwargs): | ||
| 68 | - return address_lists('', *args, **kwargs) | 64 | +def is_private_list(name): |
| 65 | + try: | ||
| 66 | + return dict(all_lists(private=True))[name] | ||
| 67 | + except KeyError: | ||
| 68 | + return [] | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +def all_lists(**kwargs): | ||
| 72 | + return mailing_lists(**kwargs) | ||
| 69 | 73 | ||
| 1 |
|
||
| 70 | 74 | ||
| 71 | def user_lists(user): | 75 | def user_lists(user): |
| 72 | list_set = set() | 76 | list_set = set() |
| 73 | 77 | ||
| 74 | for email in user.emails.values_list('address', flat=True): | 78 | for email in user.emails.values_list('address', flat=True): |
| 75 | - list_set.update(address_lists(email)) | 79 | + list_set.update(mailing_lists(address=email)) |
| 76 | 80 | ||
| 1 |
|
||
| 77 | return tuple(list_set) | 81 | return tuple(list_set) |
| 78 | 82 | ||
| @@ -97,3 +101,16 @@ def list_users(listname): | @@ -97,3 +101,16 @@ def list_users(listname): | ||
| 97 | return [] | 101 | return [] |
| 98 | 102 | ||
| 99 | return users.json() | 103 | return users.json() |
| 104 | + | ||
| 105 | + | ||
| 106 | +def get_user_mailinglists(user): | ||
| 107 | + lists_for_user = [] | ||
| 108 | + emails = '' | ||
| 109 | + | ||
| 110 | + if user: | ||
| 111 | + emails = user.emails.values_list('address', flat=True) | ||
| 112 | + | ||
| 113 | + for email in emails: | ||
| 114 | + lists_for_user.extend(mailing_lists(address=email)) | ||
| 1 |
|
||
| 115 | + | ||
| 116 | + return lists_for_user |
| @@ -15,9 +15,10 @@ from django.views.generic import DetailView, UpdateView, TemplateView | @@ -15,9 +15,10 @@ from django.views.generic import DetailView, UpdateView, TemplateView | ||
| 15 | from conversejs import xmpp | 15 | from conversejs import xmpp |
| 16 | from conversejs.models import XMPPAccount | 16 | from conversejs.models import XMPPAccount |
| 17 | 17 | ||
| 18 | -from colab.super_archives.models import (EmailAddress, Message, | 18 | +from colab.super_archives.models import (EmailAddress, |
| 19 | EmailAddressValidation) | 19 | EmailAddressValidation) |
| 20 | -from colab.search.utils import get_collaboration_data | 20 | +from colab.search.utils import get_collaboration_data, get_visible_threads |
| 21 | +from colab.accounts.models import User | ||
| 21 | 22 | ||
| 22 | from .forms import (UserCreationForm, UserForm, ListsForm, | 23 | from .forms import (UserCreationForm, UserForm, ListsForm, |
| 23 | UserUpdateForm, ChangeXMPPPasswordForm) | 24 | UserUpdateForm, ChangeXMPPPasswordForm) |
| @@ -60,12 +61,17 @@ class UserProfileDetailView(UserProfileBaseMixin, DetailView): | @@ -60,12 +61,17 @@ class UserProfileDetailView(UserProfileBaseMixin, DetailView): | ||
| 60 | template_name = 'accounts/user_detail.html' | 61 | template_name = 'accounts/user_detail.html' |
| 61 | 62 | ||
| 62 | def get_context_data(self, **kwargs): | 63 | def get_context_data(self, **kwargs): |
| 63 | - user = self.object | 64 | + profile_user = self.object |
| 64 | context = {} | 65 | context = {} |
| 65 | 66 | ||
| 66 | count_types = OrderedDict() | 67 | count_types = OrderedDict() |
| 67 | 68 | ||
| 68 | - collaborations, count_types_extras = get_collaboration_data(user) | 69 | + logged_user = None |
| 70 | + if self.request.user.is_authenticated(): | ||
| 71 | + logged_user = User.objects.get(username=self.request.user) | ||
| 72 | + | ||
| 73 | + collaborations, count_types_extras = get_collaboration_data( | ||
| 74 | + logged_user, profile_user) | ||
| 69 | 75 | ||
| 70 | collaborations.sort(key=lambda elem: elem.modified, reverse=True) | 76 | collaborations.sort(key=lambda elem: elem.modified, reverse=True) |
| 71 | 77 | ||
| @@ -74,16 +80,13 @@ class UserProfileDetailView(UserProfileBaseMixin, DetailView): | @@ -74,16 +80,13 @@ class UserProfileDetailView(UserProfileBaseMixin, DetailView): | ||
| 74 | context['type_count'] = count_types | 80 | context['type_count'] = count_types |
| 75 | context['results'] = collaborations[:10] | 81 | context['results'] = collaborations[:10] |
| 76 | 82 | ||
| 77 | - email_pks = [addr.pk for addr in user.emails.iterator()] | ||
| 78 | - query = Message.objects.filter(from_address__in=email_pks) | ||
| 79 | - query = query.order_by('-received_time') | ||
| 80 | - context['emails'] = query[:10] | 83 | + query = get_visible_threads(logged_user, profile_user) |
| 84 | + context['emails'] = query.order_by('-received_time')[:10] | ||
| 81 | 85 | ||
| 82 | - messages = Message.objects.filter(from_address__user__pk=user.pk) | ||
| 83 | count_by = 'thread__mailinglist__name' | 86 | count_by = 'thread__mailinglist__name' |
| 84 | - context['list_activity'] = dict(messages.values_list(count_by) | ||
| 85 | - .annotate(Count(count_by)) | ||
| 86 | - .order_by(count_by)) | 87 | + context['list_activity'] = dict(query.values_list(count_by) |
| 88 | + .annotate(Count(count_by)) | ||
| 89 | + .order_by(count_by)) | ||
| 87 | 90 | ||
| 88 | context.update(kwargs) | 91 | context.update(kwargs) |
| 89 | return super(UserProfileDetailView, self).get_context_data(**context) | 92 | return super(UserProfileDetailView, self).get_context_data(**context) |
| @@ -185,7 +188,7 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView): | @@ -185,7 +188,7 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView): | ||
| 185 | 188 | ||
| 186 | for email in emails: | 189 | for email in emails: |
| 187 | lists = [] | 190 | lists = [] |
| 188 | - lists_for_address = mailman.address_lists(email) | 191 | + lists_for_address = mailman.mailing_lists(address=email) |
| 189 | for listname, description in all_lists: | 192 | for listname, description in all_lists: |
| 190 | if listname in lists_for_address: | 193 | if listname in lists_for_address: |
| 191 | checked = True | 194 | checked = True |
| @@ -4,23 +4,45 @@ from django.http import HttpResponse, Http404 | @@ -4,23 +4,45 @@ from django.http import HttpResponse, Http404 | ||
| 4 | 4 | ||
| 5 | from colab.search.utils import get_collaboration_data | 5 | from colab.search.utils import get_collaboration_data |
| 6 | from colab.super_archives.models import Thread | 6 | from colab.super_archives.models import Thread |
| 7 | +from colab.accounts.utils import mailman | ||
| 8 | +from colab.accounts.models import User | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +def get_user_threads(threads, lists_for_user, key): | ||
| 12 | + visible_threads = [] | ||
| 13 | + for t in threads: | ||
| 14 | + if not t.mailinglist.is_private or \ | ||
| 15 | + t.mailinglist.name in lists_for_user: | ||
| 16 | + visible_threads.append(key(t)) | ||
| 17 | + | ||
| 18 | + return visible_threads | ||
| 7 | 19 | ||
| 8 | 20 | ||
| 9 | def dashboard(request): | 21 | def dashboard(request): |
| 10 | """Dashboard page""" | 22 | """Dashboard page""" |
| 11 | 23 | ||
| 12 | - highest_score_threads = Thread.highest_score.all()[:6] | 24 | + highest_score_threads = Thread.highest_score.all() |
| 25 | + | ||
| 26 | + all_threads = Thread.objects.all() | ||
| 27 | + latest_threads = [] | ||
| 28 | + lists_for_user = [] | ||
| 13 | 29 | ||
| 14 | - hottest_threads = [t.latest_message for t in highest_score_threads] | 30 | + user = None |
| 31 | + if request.user.is_authenticated(): | ||
| 32 | + user = User.objects.get(username=request.user) | ||
| 33 | + lists_for_user = mailman.get_user_mailinglists(user) | ||
| 15 | 34 | ||
| 16 | - latest_threads = Thread.objects.all()[:6] | 35 | + latest_threads = get_user_threads( |
| 36 | + all_threads, lists_for_user, lambda t: t) | ||
| 37 | + hottest_threads = get_user_threads( | ||
| 38 | + highest_score_threads, lists_for_user, lambda t: t.latest_message) | ||
| 17 | 39 | ||
| 18 | - latest_results, count_types = get_collaboration_data() | 40 | + latest_results, count_types = get_collaboration_data(user) |
| 19 | latest_results.sort(key=lambda elem: elem.modified, reverse=True) | 41 | latest_results.sort(key=lambda elem: elem.modified, reverse=True) |
| 20 | 42 | ||
| 21 | context = { | 43 | context = { |
| 22 | 'hottest_threads': hottest_threads[:6], | 44 | 'hottest_threads': hottest_threads[:6], |
| 23 | - 'latest_threads': latest_threads, | 45 | + 'latest_threads': latest_threads[:6], |
| 24 | 'type_count': count_types, | 46 | 'type_count': count_types, |
| 25 | 'latest_results': latest_results[:6], | 47 | 'latest_results': latest_results[:6], |
| 26 | } | 48 | } |
| @@ -13,7 +13,7 @@ class SearchViewTest(TestCase): | @@ -13,7 +13,7 @@ class SearchViewTest(TestCase): | ||
| 13 | self.client = Client() | 13 | self.client = Client() |
| 14 | 14 | ||
| 15 | def tearDown(self): | 15 | def tearDown(self): |
| 16 | - call_command('clear_index', interactive=False,verbosity=0) | 16 | + call_command('clear_index', interactive=False, verbosity=0) |
| 17 | 17 | ||
| 18 | def test_search_thread(self): | 18 | def test_search_thread(self): |
| 19 | request = self.client.get('/search/?q=thread') | 19 | request = self.client.get('/search/?q=thread') |
| @@ -6,11 +6,40 @@ from collections import OrderedDict | @@ -6,11 +6,40 @@ from collections import OrderedDict | ||
| 6 | from django.core.cache import cache | 6 | from django.core.cache import cache |
| 7 | from django.utils.translation import ugettext as _ | 7 | from django.utils.translation import ugettext as _ |
| 8 | from django.conf import settings | 8 | from django.conf import settings |
| 9 | +from django.db.models import Q as Condition | ||
| 1 |
|
||
| 10 | + | ||
| 9 | from colab.super_archives.models import Thread, Message | 11 | from colab.super_archives.models import Thread, Message |
| 10 | from colab.proxy.utils.models import Collaboration | 12 | from colab.proxy.utils.models import Collaboration |
| 13 | +from colab.accounts.utils import mailman | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +def get_visible_threads_queryset(logged_user): | ||
| 17 | + queryset = Thread.objects | ||
| 18 | + lists_for_user = [] | ||
| 19 | + if logged_user: | ||
| 20 | + lists_for_user = mailman.get_user_mailinglists(logged_user) | ||
| 21 | + | ||
| 22 | + user_lists = Condition(mailinglist__name__in=lists_for_user) | ||
| 1 |
|
||
| 23 | + public_lists = Condition(mailinglist__is_private=False) | ||
| 24 | + queryset = Thread.objects.filter(user_lists | public_lists) | ||
| 25 | + | ||
| 26 | + return queryset | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +def get_visible_threads(logged_user, filter_by_user=None): | ||
| 30 | + thread_qs = get_visible_threads_queryset(logged_user) | ||
| 31 | + if filter_by_user: | ||
| 32 | + message_qs = Message.objects.filter(thread__in=thread_qs) | ||
| 33 | + messages = message_qs.filter( | ||
| 34 | + from_address__user__pk=filter_by_user.pk) | ||
| 35 | + else: | ||
| 36 | + latest_threads = thread_qs.all() | ||
| 37 | + messages = [t.latest_message for t in latest_threads] | ||
| 38 | + | ||
| 39 | + return messages | ||
| 11 | 40 | ||
| 12 | 41 | ||
| 13 | -def get_collaboration_data(filter_by_user=None): | 42 | +def get_collaboration_data(logged_user, filter_by_user=None): |
| 14 | latest_results = [] | 43 | latest_results = [] |
| 15 | count_types = cache.get('home_chart') | 44 | count_types = cache.get('home_chart') |
| 16 | populate_count_types = False | 45 | populate_count_types = False |
| @@ -18,14 +47,10 @@ def get_collaboration_data(filter_by_user=None): | @@ -18,14 +47,10 @@ def get_collaboration_data(filter_by_user=None): | ||
| 18 | if count_types is None: | 47 | if count_types is None: |
| 19 | populate_count_types = True | 48 | populate_count_types = True |
| 20 | count_types = OrderedDict() | 49 | count_types = OrderedDict() |
| 21 | - count_types[_('Emails')] = Thread.objects.count() | 50 | + visible_threads = get_visible_threads(logged_user) |
| 51 | + count_types[_('Emails')] = len(visible_threads) | ||
| 22 | 52 | ||
| 23 | - if filter_by_user: | ||
| 24 | - messages = Message.objects.filter( | ||
| 25 | - from_address__user__pk=filter_by_user.pk) | ||
| 26 | - else: | ||
| 27 | - latest_threads = Thread.objects.all()[:6] | ||
| 28 | - messages = [t.latest_message for t in latest_threads] | 53 | + messages = get_visible_threads(logged_user, filter_by_user) |
| 29 | 54 | ||
| 30 | latest_results.extend(messages) | 55 | latest_results.extend(messages) |
| 31 | 56 |
| @@ -0,0 +1,186 @@ | @@ -0,0 +1,186 @@ | ||
| 1 | +[ | ||
| 2 | + { | ||
| 3 | + "fields": { | ||
| 4 | + "last_name": "Jar", | ||
| 5 | + "webpage": null, | ||
| 6 | + "twitter": null, | ||
| 7 | + "is_staff": false, | ||
| 8 | + "user_permissions": [ | ||
| 9 | + | ||
| 10 | + ], | ||
| 11 | + "date_joined": "2015-02-24T21:10:35.004Z", | ||
| 12 | + "google_talk": null, | ||
| 13 | + "first_name": "Gust", | ||
| 14 | + "is_superuser": false, | ||
| 15 | + "last_login": "2015-02-26T17:56:13.378Z", | ||
| 16 | + "verification_hash": null, | ||
| 17 | + "role": null, | ||
| 18 | + "email": "gustmax@hotmail.com", | ||
| 19 | + "username": "gustmax", | ||
| 20 | + "bio": null, | ||
| 21 | + "needs_update": false, | ||
| 22 | + "is_active": true, | ||
| 23 | + "facebook": null, | ||
| 24 | + "groups": [ | ||
| 25 | + | ||
| 26 | + ], | ||
| 27 | + "password": "pbkdf2_sha256$12000$ez83ccNOUQZk$vYT/QcYMukXZ7D7L1qQPyYlzCUEEEF20J7/Xjef0Rqg=", | ||
| 28 | + "institution": null, | ||
| 29 | + "github": null, | ||
| 30 | + "modified": "2015-02-24T21:11:22.323Z" | ||
| 31 | + }, | ||
| 32 | + "model": "accounts.user", | ||
| 33 | + "pk": 1 | ||
| 34 | + }, | ||
| 35 | + { | ||
| 36 | + "fields": { | ||
| 37 | + "real_name": "", | ||
| 38 | + "user": 1, | ||
| 39 | + "md5": "ed8f47ae6048f8d4456c0554578f53ff", | ||
| 40 | + "address": "gustmax@hotmail.com" | ||
| 41 | + }, | ||
| 42 | + "model": "super_archives.emailaddress", | ||
| 43 | + "pk": 1 | ||
| 44 | + }, | ||
| 45 | + { | ||
| 46 | + "fields": { | ||
| 47 | + "description": "", | ||
| 48 | + "email": "", | ||
| 49 | + "logo": "", | ||
| 50 | + "last_imported_index": 0, | ||
| 51 | + "is_private": true, | ||
| 52 | + "name": "mailman" | ||
| 53 | + }, | ||
| 54 | + "model": "super_archives.mailinglist", | ||
| 55 | + "pk": 1 | ||
| 56 | + }, | ||
| 57 | + { | ||
| 58 | + "fields": { | ||
| 59 | + "description": "", | ||
| 60 | + "email": "", | ||
| 61 | + "logo": "", | ||
| 62 | + "last_imported_index": 0, | ||
| 63 | + "is_private": false, | ||
| 64 | + "name": "lista" | ||
| 65 | + }, | ||
| 66 | + "model": "super_archives.mailinglist", | ||
| 67 | + "pk": 2 | ||
| 68 | + }, | ||
| 69 | + { | ||
| 70 | + "fields": { | ||
| 71 | + "description": "", | ||
| 72 | + "email": "", | ||
| 73 | + "logo": "", | ||
| 74 | + "last_imported_index": 0, | ||
| 75 | + "is_private": true, | ||
| 76 | + "name": "privatelist" | ||
| 77 | + }, | ||
| 78 | + "model": "super_archives.mailinglist", | ||
| 79 | + "pk": 3 | ||
| 80 | + }, | ||
| 81 | + { | ||
| 82 | + "fields": { | ||
| 83 | + "spam": false, | ||
| 84 | + "subject_token": "no-subject", | ||
| 85 | + "mailinglist": 1, | ||
| 86 | + "score": 34, | ||
| 87 | + "latest_message": 1 | ||
| 88 | + }, | ||
| 89 | + "model": "super_archives.thread", | ||
| 90 | + "pk": 1 | ||
| 91 | + }, | ||
| 92 | + { | ||
| 93 | + "fields": { | ||
| 94 | + "spam": false, | ||
| 95 | + "subject_token": "no-subject", | ||
| 96 | + "mailinglist": 2, | ||
| 97 | + "score": 34, | ||
| 98 | + "latest_message": 2 | ||
| 99 | + }, | ||
| 100 | + "model": "super_archives.thread", | ||
| 101 | + "pk": 2 | ||
| 102 | + }, | ||
| 103 | + { | ||
| 104 | + "fields": { | ||
| 105 | + "spam": false, | ||
| 106 | + "subject_token": "no-subject", | ||
| 107 | + "mailinglist": 3, | ||
| 108 | + "score": 33, | ||
| 109 | + "latest_message": 3 | ||
| 110 | + }, | ||
| 111 | + "model": "super_archives.thread", | ||
| 112 | + "pk": 3 | ||
| 113 | + }, | ||
| 114 | + { | ||
| 115 | + "fields": { | ||
| 116 | + "body": "lista Mailman email", | ||
| 117 | + "received_time": "2015-02-24T14:23:42Z", | ||
| 118 | + "from_address": 1, | ||
| 119 | + "thread": 1, | ||
| 120 | + "spam": false, | ||
| 121 | + "subject_clean": "(no subject)", | ||
| 122 | + "message_id": "<20150224142347.9ED2419A5B0@localhost.localdomain>", | ||
| 123 | + "subject": "[Mailman] (no subject)" | ||
| 124 | + }, | ||
| 125 | + "model": "super_archives.message", | ||
| 126 | + "pk": 1 | ||
| 127 | + }, | ||
| 128 | + { | ||
| 129 | + "fields": { | ||
| 130 | + "body": "nada", | ||
| 131 | + "received_time": "2015-02-24T14:15:39Z", | ||
| 132 | + "from_address": 1, | ||
| 133 | + "thread": 2, | ||
| 134 | + "spam": false, | ||
| 135 | + "subject_clean": "(no subject)", | ||
| 136 | + "message_id": "<20150224141545.1AECA19A5A0@localhost.localdomain>", | ||
| 137 | + "subject": "[Lista] (no subject)" | ||
| 138 | + }, | ||
| 139 | + "model": "super_archives.message", | ||
| 140 | + "pk": 2 | ||
| 141 | + }, | ||
| 142 | + { | ||
| 143 | + "fields": { | ||
| 144 | + "body": "Mensagem da lista privada nada", | ||
| 145 | + "received_time": "2015-02-24T14:15:39Z", | ||
| 146 | + "from_address": 1, | ||
| 147 | + "thread": 3, | ||
| 148 | + "spam": false, | ||
| 149 | + "subject_clean": "(no subject)", | ||
| 150 | + "message_id": "<20150224141545.1AECA19A5A0@localhost.localdomain>", | ||
| 151 | + "subject": "[PrivateList] (no subject)" | ||
| 152 | + }, | ||
| 153 | + "model": "super_archives.message", | ||
| 154 | + "pk": 3 | ||
| 155 | + }, | ||
| 156 | + { | ||
| 157 | + "fields": { | ||
| 158 | + "text": "lista Mailman email\n", | ||
| 159 | + "message": 1, | ||
| 160 | + "is_reply": false, | ||
| 161 | + "order": 0 | ||
| 162 | + }, | ||
| 163 | + "model": "super_archives.messageblock", | ||
| 164 | + "pk": 1 | ||
| 165 | + }, | ||
| 166 | + { | ||
| 167 | + "fields": { | ||
| 168 | + "text": "nada\n", | ||
| 169 | + "message": 2, | ||
| 170 | + "is_reply": false, | ||
| 171 | + "order": 0 | ||
| 172 | + }, | ||
| 173 | + "model": "super_archives.messageblock", | ||
| 174 | + "pk": 2 | ||
| 175 | + }, | ||
| 176 | + { | ||
| 177 | + "fields": { | ||
| 178 | + "text": "Mensagem da lista privada nada\n", | ||
| 179 | + "message": 3, | ||
| 180 | + "is_reply": false, | ||
| 181 | + "order": 0 | ||
| 182 | + }, | ||
| 183 | + "model": "super_archives.messageblock", | ||
| 184 | + "pk": 3 | ||
| 185 | + } | ||
| 186 | +] |
| @@ -295,3 +295,7 @@ class Command(BaseCommand, object): | @@ -295,3 +295,7 @@ class Command(BaseCommand, object): | ||
| 295 | raise | 295 | raise |
| 296 | finally: | 296 | finally: |
| 297 | os.remove(self.lock_file) | 297 | os.remove(self.lock_file) |
| 298 | + | ||
| 299 | + for mlist in MailingList.objects.all(): | ||
| 300 | + mlist.update_privacy() | ||
| 301 | + mlist.save() |
| @@ -79,7 +79,7 @@ class Message(mailbox.mboxMessage): | @@ -79,7 +79,7 @@ class Message(mailbox.mboxMessage): | ||
| 79 | return body.strip() | 79 | return body.strip() |
| 80 | 80 | ||
| 81 | def get_received_datetime(self): | 81 | def get_received_datetime(self): |
| 82 | - if self not in ('Received'): | 82 | + if 'Received' not in self: |
| 1 |
|
||
| 83 | return None | 83 | return None |
| 84 | # The time received should always be the last element | 84 | # The time received should always be the last element |
| 85 | # in the `Received` attribute from the message headers | 85 | # in the `Received` attribute from the message headers |
| @@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +from __future__ import unicode_literals | ||
| 3 | + | ||
| 4 | +from django.db import models, migrations | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +class Migration(migrations.Migration): | ||
| 8 | + | ||
| 9 | + dependencies = [ | ||
| 10 | + ('super_archives', '0001_initial'), | ||
| 11 | + ] | ||
| 12 | + | ||
| 13 | + operations = [ | ||
| 14 | + migrations.AddField( | ||
| 15 | + model_name='mailinglist', | ||
| 16 | + name='is_private', | ||
| 17 | + field=models.BooleanField(default=False), | ||
| 18 | + preserve_default=True, | ||
| 19 | + ), | ||
| 20 | + ] |
| @@ -19,6 +19,7 @@ from hitcounter.models import HitCounterModelMixin | @@ -19,6 +19,7 @@ from hitcounter.models import HitCounterModelMixin | ||
| 19 | from .managers import NotSpamManager, MostVotedManager, HighestScore | 19 | from .managers import NotSpamManager, MostVotedManager, HighestScore |
| 20 | from .utils import blocks, email | 20 | from .utils import blocks, email |
| 21 | from .utils.etiquetador import etiquetador | 21 | from .utils.etiquetador import etiquetador |
| 22 | +from colab.accounts.utils import mailman | ||
| 22 | 23 | ||
| 23 | 24 | ||
| 24 | def get_validation_key(): | 25 | def get_validation_key(): |
| @@ -79,6 +80,10 @@ class MailingList(models.Model): | @@ -79,6 +80,10 @@ class MailingList(models.Model): | ||
| 79 | description = models.TextField() | 80 | description = models.TextField() |
| 80 | logo = models.FileField(upload_to='list_logo') # TODO | 81 | logo = models.FileField(upload_to='list_logo') # TODO |
| 81 | last_imported_index = models.IntegerField(default=0) | 82 | last_imported_index = models.IntegerField(default=0) |
| 83 | + is_private = models.BooleanField(default=False) | ||
| 84 | + | ||
| 85 | + def update_privacy(self): | ||
| 86 | + self.is_private = mailman.is_private_list(self.name) | ||
| 82 | 87 | ||
| 83 | def get_absolute_url(self): | 88 | def get_absolute_url(self): |
| 84 | params = { | 89 | params = { |
| @@ -85,10 +85,12 @@ class ThreadIndex(BaseIndex, indexes.Indexable): | @@ -85,10 +85,12 @@ class ThreadIndex(BaseIndex, indexes.Indexable): | ||
| 85 | return u'thread' | 85 | return u'thread' |
| 86 | 86 | ||
| 87 | def index_queryset(self, using=None): | 87 | def index_queryset(self, using=None): |
| 88 | - return self.get_model().objects.filter( | ||
| 89 | - spam=False | 88 | + elements = self.get_model().objects.filter( |
| 89 | + spam=False, mailinglist__is_private=False | ||
| 90 | ).exclude(subject_token='') | 90 | ).exclude(subject_token='') |
| 91 | 91 | ||
| 92 | + return elements | ||
| 93 | + | ||
| 92 | def get_boost(self, obj): | 94 | def get_boost(self, obj): |
| 93 | boost = super(ThreadIndex, self).get_boost(obj) | 95 | boost = super(ThreadIndex, self).get_boost(obj) |
| 94 | 96 |
| @@ -0,0 +1,66 @@ | @@ -0,0 +1,66 @@ | ||
| 1 | +# -*- coding:utf-8 -*- | ||
| 2 | +import mock | ||
| 3 | + | ||
| 4 | +from colab.accounts.utils import mailman | ||
| 5 | +from django.test import TestCase, Client | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class ArchivesViewTest(TestCase): | ||
| 9 | + | ||
| 10 | + fixtures = ['mailinglistdata.json'] | ||
| 11 | + | ||
| 12 | + def setUp(self): | ||
| 13 | + self.client = Client() | ||
| 14 | + | ||
| 15 | + def authenticate_user(self): | ||
| 16 | + self.client.login(username='gustmax', password='1234') | ||
| 17 | + | ||
| 18 | + def test_see_only_private_list_if_member(self): | ||
| 19 | + mailman.get_user_mailinglists = mock.Mock( | ||
| 20 | + return_value="['privatelist']") | ||
| 21 | + | ||
| 22 | + self.authenticate_user() | ||
| 23 | + request = self.client.get('/archives/thread/') | ||
| 24 | + | ||
| 25 | + list_data = request.context['lists'] | ||
| 26 | + | ||
| 27 | + self.assertEqual('lista', list_data[0][0]) | ||
| 28 | + self.assertEqual('privatelist', list_data[1][0]) | ||
| 29 | + self.assertEqual(2, len(list_data)) | ||
| 30 | + | ||
| 31 | + def test_see_only_public_if_not_logged_in(self): | ||
| 32 | + request = self.client.get('/archives/thread/') | ||
| 33 | + | ||
| 34 | + list_data = request.context['lists'] | ||
| 35 | + | ||
| 36 | + self.assertEqual('lista', list_data[0][0]) | ||
| 37 | + self.assertEqual(1, len(list_data)) | ||
| 38 | + | ||
| 39 | + def test_see_private_thread_in_dashboard_if_member(self): | ||
| 40 | + mailman.get_user_mailinglists = mock.Mock( | ||
| 41 | + return_value="['privatelist']") | ||
| 42 | + | ||
| 43 | + self.authenticate_user() | ||
| 44 | + request = self.client.get('/dashboard') | ||
| 45 | + | ||
| 46 | + latest_threads = request.context['latest_threads'] | ||
| 47 | + hottest_threads = request.context['hottest_threads'] | ||
| 48 | + | ||
| 49 | + self.assertEqual(2, len(latest_threads)) | ||
| 50 | + self.assertEqual(2, len(hottest_threads)) | ||
| 51 | + | ||
| 52 | + def test_dont_see_private_thread_if_logged_out(self): | ||
| 53 | + request = self.client.get('/dashboard') | ||
| 54 | + | ||
| 55 | + latest_threads = request.context['latest_threads'] | ||
| 56 | + hottest_threads = request.context['hottest_threads'] | ||
| 57 | + | ||
| 58 | + self.assertEqual(1, len(latest_threads)) | ||
| 59 | + self.assertEqual(1, len(hottest_threads)) | ||
| 60 | + | ||
| 61 | + def test_dont_see_private_threads_in_profile_if_logged_out(self): | ||
| 62 | + request = self.client.get('/account/gustmax') | ||
| 63 | + | ||
| 64 | + emails = request.context['emails'] | ||
| 65 | + | ||
| 66 | + self.assertEqual(1, len(emails)) |
| @@ -12,7 +12,7 @@ from django.contrib import messages | @@ -12,7 +12,7 @@ from django.contrib import messages | ||
| 12 | from django.db import IntegrityError | 12 | from django.db import IntegrityError |
| 13 | from django.views.generic import View | 13 | from django.views.generic import View |
| 14 | from django.utils.translation import ugettext as _ | 14 | from django.utils.translation import ugettext as _ |
| 15 | -from django.core.exceptions import ObjectDoesNotExist | 15 | +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied |
| 16 | from django.utils.decorators import method_decorator | 16 | from django.utils.decorators import method_decorator |
| 17 | from django.contrib.auth.decorators import login_required | 17 | from django.contrib.auth.decorators import login_required |
| 18 | from django.shortcuts import render, redirect, get_object_or_404 | 18 | from django.shortcuts import render, redirect, get_object_or_404 |
| @@ -31,6 +31,18 @@ class ThreadView(View): | @@ -31,6 +31,18 @@ class ThreadView(View): | ||
| 31 | 31 | ||
| 32 | thread = get_object_or_404(Thread, subject_token=thread_token, | 32 | thread = get_object_or_404(Thread, subject_token=thread_token, |
| 33 | mailinglist__name=mailinglist) | 33 | mailinglist__name=mailinglist) |
| 34 | + | ||
| 35 | + all_privates = dict(mailman.all_lists(private=True)) | ||
| 36 | + if all_privates[thread.mailinglist.name]: | ||
| 37 | + if not request.user.is_authenticated(): | ||
| 38 | + raise PermissionDenied | ||
| 39 | + else: | ||
| 40 | + user = User.objects.get(username=request.user) | ||
| 41 | + emails = user.emails.values_list('address', flat=True) | ||
| 42 | + lists_for_user = mailman.get_user_mailinglists(user) | ||
| 43 | + if thread.mailinglist.name not in lists_for_user: | ||
| 44 | + raise PermissionDenied | ||
| 45 | + | ||
| 34 | thread.hit(request) | 46 | thread.hit(request) |
| 35 | 47 | ||
| 36 | try: | 48 | try: |
| @@ -122,21 +134,31 @@ class ThreadDashboardView(View): | @@ -122,21 +134,31 @@ class ThreadDashboardView(View): | ||
| 122 | def get(self, request): | 134 | def get(self, request): |
| 123 | MAX = 6 | 135 | MAX = 6 |
| 124 | context = {} | 136 | context = {} |
| 125 | - all_lists = mailman.all_lists(description=True) | 137 | + |
| 138 | + all_privates = {} | ||
| 139 | + private_mailinglist = MailingList.objects.filter(is_private=True) | ||
| 140 | + for mailinglist in private_mailinglist: | ||
| 1 |
|
||
| 141 | + all_privates[mailinglist.name] = True | ||
| 126 | 142 | ||
| 127 | context['lists'] = [] | 143 | context['lists'] = [] |
| 128 | 144 | ||
| 145 | + lists_for_user = [] | ||
| 146 | + if request.user.is_authenticated(): | ||
| 147 | + user = User.objects.get(username=request.user) | ||
| 148 | + lists_for_user = mailman.get_user_mailinglists(user) | ||
| 149 | + | ||
| 129 | for list_ in MailingList.objects.order_by('name'): | 150 | for list_ in MailingList.objects.order_by('name'): |
| 130 | - context['lists'].append(( | ||
| 131 | - list_.name, | ||
| 132 | - mailman.get_list_description(list_.name, all_lists), | ||
| 133 | - list_.thread_set.filter(spam=False).order_by( | ||
| 134 | - '-latest_message__received_time' | ||
| 135 | - )[:MAX], | ||
| 136 | - [t.latest_message for t in Thread.highest_score.filter( | ||
| 137 | - mailinglist__name=list_.name)[:MAX]], | ||
| 138 | - len(mailman.list_users(list_.name)), | ||
| 139 | - )) | 151 | + if list_.name not in all_privates or list_.name in lists_for_user: |
| 152 | + context['lists'].append(( | ||
| 153 | + list_.name, | ||
| 154 | + mailman.get_list_description(list_.name), | ||
| 155 | + list_.thread_set.filter(spam=False).order_by( | ||
| 156 | + '-latest_message__received_time' | ||
| 157 | + )[:MAX], | ||
| 158 | + [t.latest_message for t in Thread.highest_score.filter( | ||
| 159 | + mailinglist__name=list_.name)[:MAX]], | ||
| 160 | + len(mailman.list_users(list_.name)), | ||
| 161 | + )) | ||
| 140 | 162 | ||
| 141 | return render(request, 'superarchives/thread-dashboard.html', context) | 163 | return render(request, 'superarchives/thread-dashboard.html', context) |
| 142 | 164 |
-
@rodrigosiqueiramelo @carlospecter
-
Why are you initializing the same variable twice with the same value and no other change between the redefinitions?
-
Why not use address_list function instead of mailing_lists? It returns mailing_lists too, but it's only used once in the file.
-
Same here about the usage of address_lists.
-
ALL done.
| 97 | 102 | return [] |
| 98 | 103 | |
| 99 | 104 | return users.json() |
| 105 | + | |
| 106 | + | |
| 107 | +def get_user_mailinglists(user): | |
| 108 | + lists_for_user = [] | |
| 109 | + emails = '' | |
| 110 | + | |
| 111 | + if user: | |
| 112 | + emails = user.emails.values_list('address', flat=True) | |
| 113 | + | |
| 114 | + lists_for_user = [] | |
| 1 |
|
|
| 38 | 38 | |
| 39 | 39 | |
| 40 | 40 | def update_subscription(address, lists): |
| 41 | - current_lists = address_lists(address) | |
| 41 | + current_lists = mailing_lists(address=address) | |
| 1 |
|
|
| 66 | 67 | |
| 68 | +def is_private_list(name): | |
| 69 | + return dict(all_lists(private=True))[name] | |
| 70 | + | |
| 71 | + | |
| 67 | 72 | def all_lists(*args, **kwargs): |
| 68 | - return address_lists('', *args, **kwargs) | |
| 73 | + return mailing_lists(*args, **kwargs) | |
| 69 | 74 | |
| 70 | 75 | |
| 71 | 76 | def user_lists(user): |
| 72 | 77 | list_set = set() |
| 73 | 78 | |
| 74 | 79 | for email in user.emails.values_list('address', flat=True): |
| 75 | - list_set.update(address_lists(email)) | |
| 80 | + list_set.update(mailing_lists(address=email)) | |
| 1 |
|
|