Commit f59cc89813f4d24fe99873e80b451770775aacea

Authored by Sergio Oliveira
2 parents 256ed785 058dc7b0

Merge branch 'private_mailman_lists' into 'master'

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.
colab/accounts/utils/mailman.py
@@ -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)
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
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
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))
  115 +
  116 + return lists_for_user
colab/accounts/views.py
@@ -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
colab/home/views.py
@@ -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 }
colab/search/tests.py
@@ -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')
colab/search/utils.py
@@ -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
  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)
  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
colab/super_archives/fixtures/mailinglistdata.json 0 → 100644
@@ -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 +]
colab/super_archives/management/commands/import_emails.py
@@ -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()
colab/super_archives/management/commands/message.py
@@ -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:
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
colab/super_archives/migrations/0002_mailinglist_is_private.py 0 → 100644
@@ -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 + ]
colab/super_archives/models.py
@@ -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 = {
colab/super_archives/search_indexes.py
@@ -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
colab/super_archives/tests/__init__.py 0 → 100644
colab/super_archives/tests/test_privatelist.py 0 → 100644
@@ -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))
colab/super_archives/views.py
@@ -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:
  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