Commit 5046caafa4eb9f665b3629469485d8f4d962c177

Authored by Sergio Oliveira
2 parents 0d143d6a a3ff3d5d

Merge branch 'master' of portal.softwarepublico.gov.br:softwarepublico/colab

colab/accounts/forms.py
@@ -141,8 +141,9 @@ class UserUpdateForm(UserForm): @@ -141,8 +141,9 @@ class UserUpdateForm(UserForm):
141 141
142 class ListsForm(forms.Form): 142 class ListsForm(forms.Form):
143 LISTS_NAMES = (( 143 LISTS_NAMES = ((
144 - listname, u'{} ({})'.format(listname, description)  
145 - ) for listname, description in mailman.all_lists(description=True)) 144 + mlist.get('listname'), u'{} ({})'.format(mlist.get('listname'),
  145 + mlist.get('description'))
  146 + ) for mlist in mailman.all_lists())
146 147
147 lists = forms.MultipleChoiceField(label=_(u'Mailing lists'), 148 lists = forms.MultipleChoiceField(label=_(u'Mailing lists'),
148 required=False, 149 required=False,
colab/accounts/models.py
@@ -60,7 +60,7 @@ class User(AbstractUser): @@ -60,7 +60,7 @@ class User(AbstractUser):
60 return mailman.user_lists(self) 60 return mailman.user_lists(self)
61 61
62 def update_subscription(self, email, lists): 62 def update_subscription(self, email, lists):
63 - mailman.update_subscription(email, lists) 63 + return mailman.update_subscription(email, lists)
64 64
65 def save(self, *args, **kwargs): 65 def save(self, *args, **kwargs):
66 66
colab/accounts/templates/accounts/user_detail.html
@@ -84,7 +84,6 @@ @@ -84,7 +84,6 @@
84 {% endif %} 84 {% endif %}
85 {% endif %} 85 {% endif %}
86 </ul> 86 </ul>
87 -  
88 {% if user_.mailinglists %} 87 {% if user_.mailinglists %}
89 <b>{% trans 'Groups: ' %}</b> 88 <b>{% trans 'Groups: ' %}</b>
90 {% for list in user_.mailinglists %} 89 {% for list in user_.mailinglists %}
colab/accounts/utils/mailman.py
@@ -4,66 +4,99 @@ import requests @@ -4,66 +4,99 @@ import requests
4 import logging 4 import logging
5 5
6 from django.conf import settings 6 from django.conf import settings
  7 +from django.contrib import messages
7 8
8 TIMEOUT = 1 9 TIMEOUT = 1
9 10
10 LOGGER = logging.getLogger('colab.mailman') 11 LOGGER = logging.getLogger('colab.mailman')
11 12
12 -  
13 -def get_url(listname=None): 13 +S = 'success'
  14 +I = 'info'
  15 +E = 'error'
  16 +
  17 +MAILMAN_MSGS = {
  18 + 0: (S, '%s: Success!'),
  19 + 1: (S, '%s: An email confirmation was sent to you, please check your inbox.'),
  20 + 2: (I, '%s: Your subscription was sent successfully! Please wait for the list\'s admin approval.'),
  21 + 3: (I, '%s: You are already a member of this list.'),
  22 + 4: (E, '%s: You are banned from this list!'),
  23 + 5: (E, '%s: You appear to have an invalid email address.'),
  24 + 6: (E, '%s: Your email address is considered to be hostile.'),
  25 + 7: (E, '%s: You are not a member of this list.'),
  26 + 8: (E, 'Missing information: `email_from`, `subject` and `body` are mandatory.'),
  27 +}
  28 +
  29 +
  30 +def get_url(path, listname=None):
  31 + url = urlparse.urljoin(settings.MAILMAN_API_URL, path)
14 if listname: 32 if listname:
15 - return urlparse.urljoin(settings.MAILMAN_API_URL, '/' + listname)  
16 -  
17 - return settings.MAILMAN_API_URL 33 + return urlparse.urljoin(url, listname)
  34 + return url
18 35
19 36
20 def subscribe(listname, address): 37 def subscribe(listname, address):
21 - url = get_url(listname) 38 + url = get_url('subscribe/', listname=listname)
22 try: 39 try:
23 - requests.put(url, timeout=TIMEOUT, data={'address': address}) 40 + result = requests.put(url, timeout=TIMEOUT, data={'address': address})
  41 + msg_type, message = MAILMAN_MSGS[result.json()]
  42 + return msg_type, message % listname
24 except: 43 except:
25 LOGGER.exception('Unable to subscribe user') 44 LOGGER.exception('Unable to subscribe user')
26 - return False  
27 - return True 45 + return E, 'Error: Unable to subscribe user'
28 46
29 47
30 def unsubscribe(listname, address): 48 def unsubscribe(listname, address):
31 - url = get_url(listname) 49 + url = get_url('subscribe/', listname)
32 try: 50 try:
33 - requests.delete(url, timeout=TIMEOUT, data={'address': address}) 51 + result = requests.delete(url, timeout=TIMEOUT, data={'address': address})
  52 + msg_type, message = MAILMAN_MSGS[result.json()]
  53 + return msg_type, message % listname
34 except: 54 except:
35 LOGGER.exception('Unable to unsubscribe user') 55 LOGGER.exception('Unable to unsubscribe user')
36 - return False  
37 - return True 56 + return E, 'Error: Unable to subscribe user'
38 57
39 58
40 def update_subscription(address, lists): 59 def update_subscription(address, lists):
41 - current_lists = mailing_lists(address=address) 60 + current_lists = mailing_lists(address=address, names_only=True)
  61 + info_messages = []
42 62
43 for maillist in current_lists: 63 for maillist in current_lists:
44 if maillist not in lists: 64 if maillist not in lists:
45 - unsubscribe(maillist, address) 65 + info_messages.append(unsubscribe(maillist, address))
46 66
47 for maillist in lists: 67 for maillist in lists:
48 if maillist not in current_lists: 68 if maillist not in current_lists:
49 - subscribe(maillist, address) 69 + info_messages.append(subscribe(maillist, address))
  70 +
  71 + return info_messages
50 72
51 73
52 def mailing_lists(**kwargs): 74 def mailing_lists(**kwargs):
53 - url = get_url() 75 + url = get_url('lists/')
54 76
55 try: 77 try:
56 - lists = requests.get(url, timeout=TIMEOUT, params=kwargs) 78 + lists = requests.get(url, timeout=TIMEOUT, params=kwargs).json()
  79 + if not isinstance(lists, (list, tuple)):
  80 + raise
57 except: 81 except:
58 LOGGER.exception('Unable to list mailing lists') 82 LOGGER.exception('Unable to list mailing lists')
59 return [] 83 return []
60 84
61 - return lists.json() 85 + if kwargs.get('names_only'):
  86 + names_only = []
  87 + for l in lists:
  88 + names_only.append(l['listname'])
  89 + return names_only
  90 + else:
  91 + return lists
62 92
63 93
64 def is_private_list(name): 94 def is_private_list(name):
65 try: 95 try:
66 - return dict(all_lists(private=True))[name] 96 + privacy = {}
  97 + privacy.update({mlist.get('listname'): mlist.get('archive_private')
  98 + for mlist in all_lists()})
  99 + return privacy[name]
67 except KeyError: 100 except KeyError:
68 return [] 101 return []
69 102
@@ -76,22 +109,25 @@ def user_lists(user): @@ -76,22 +109,25 @@ def user_lists(user):
76 list_set = set() 109 list_set = set()
77 110
78 for email in user.emails.values_list('address', flat=True): 111 for email in user.emails.values_list('address', flat=True):
79 - list_set.update(mailing_lists(address=email)) 112 + mlists = mailing_lists(address=email)
  113 + list_set.update(mlist.get('listname') for mlist in mlists)
80 114
81 return tuple(list_set) 115 return tuple(list_set)
82 116
83 117
84 def get_list_description(listname, lists=None): 118 def get_list_description(listname, lists=None):
85 if not lists: 119 if not lists:
86 - lists = dict(all_lists(description=True))  
87 - elif not isinstance(lists, dict):  
88 - lists = dict(lists) 120 + lists = all_lists()
  121 +
  122 + desc = "".join(mlist.get('description') for mlist in lists
  123 + if isinstance(mlist, dict) and
  124 + mlist.get('listname') == listname)
89 125
90 - return lists.get(listname) 126 + return desc
91 127
92 128
93 def list_users(listname): 129 def list_users(listname):
94 - url = get_url(listname) 130 + url = get_url('members/', listname)
95 131
96 params = {} 132 params = {}
97 133
@@ -114,3 +150,10 @@ def get_user_mailinglists(user): @@ -114,3 +150,10 @@ def get_user_mailinglists(user):
114 lists_for_user.extend(mailing_lists(address=email)) 150 lists_for_user.extend(mailing_lists(address=email))
115 151
116 return lists_for_user 152 return lists_for_user
  153 +
  154 +
  155 +def extract_listname_from_list(lists):
  156 + try:
  157 + return [mlist.get('listname') for mlist in lists]
  158 + except ValueError:
  159 + return []
colab/accounts/views.py
@@ -103,7 +103,12 @@ def signup(request): @@ -103,7 +103,12 @@ def signup(request):
103 103
104 user.is_active = False 104 user.is_active = False
105 user.save() 105 user.save()
106 - EmailAddressValidation.create(user.email, user) 106 + email = EmailAddressValidation.create(user.email, user)
  107 +
  108 + location = reverse('archive_email_view',
  109 + kwargs={'key': email.validation_key})
  110 + verification_url = request.build_absolute_uri(location)
  111 + EmailAddressValidation.verify_email(email, verification_url)
107 112
108 # Check if the user's email have been used previously 113 # Check if the user's email have been used previously
109 # in the mainling lists to link the user to old messages 114 # in the mainling lists to link the user to old messages
@@ -139,7 +144,11 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView): @@ -139,7 +144,11 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
139 user = self.get_object() 144 user = self.get_object()
140 for email in user.emails.values_list('address', flat=True): 145 for email in user.emails.values_list('address', flat=True):
141 lists = self.request.POST.getlist(email) 146 lists = self.request.POST.getlist(email)
142 - user.update_subscription(email, lists) 147 + info_messages = user.update_subscription(email, lists)
  148 + for msg_type, message in info_messages:
  149 + show_message = getattr(messages, msg_type)
  150 + show_message(request, _(message))
  151 +
143 152
144 return redirect('user_profile', username=user.username) 153 return redirect('user_profile', username=user.username)
145 154
@@ -149,18 +158,19 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView): @@ -149,18 +158,19 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
149 158
150 user = self.get_object() 159 user = self.get_object()
151 emails = user.emails.values_list('address', flat=True) 160 emails = user.emails.values_list('address', flat=True)
152 - all_lists = mailman.all_lists(description=True) 161 + all_lists = mailman.all_lists()
153 162
154 for email in emails: 163 for email in emails:
155 lists = [] 164 lists = []
156 - lists_for_address = mailman.mailing_lists(address=email)  
157 - for listname, description in all_lists:  
158 - if listname in lists_for_address: 165 + lists_for_address = mailman.mailing_lists(address=email, names_only=True)
  166 + for mlist in all_lists:
  167 + if mlist.get('listname') in lists_for_address:
159 checked = True 168 checked = True
160 else: 169 else:
161 checked = False 170 checked = False
162 lists.append(( 171 lists.append((
163 - {'listname': listname, 'description': description}, 172 + {'listname': mlist.get('listname'),
  173 + 'description': mlist.get('description')},
164 checked 174 checked
165 )) 175 ))
166 176
colab/home/views.py
@@ -10,9 +10,10 @@ from colab.accounts.models import User @@ -10,9 +10,10 @@ from colab.accounts.models import User
10 10
11 def get_user_threads(threads, lists_for_user, key): 11 def get_user_threads(threads, lists_for_user, key):
12 visible_threads = [] 12 visible_threads = []
  13 + listnames_for_user = mailman.extract_listname_from_list(lists_for_user)
13 for t in threads: 14 for t in threads:
14 if not t.mailinglist.is_private or \ 15 if not t.mailinglist.is_private or \
15 - t.mailinglist.name in lists_for_user: 16 + t.mailinglist.name in listnames_for_user:
16 visible_threads.append(key(t)) 17 visible_threads.append(key(t))
17 18
18 return visible_threads 19 return visible_threads
colab/plugins/gitlab/migrations/0005_auto_20150806_1230.py 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  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 + ('gitlab', '0004_auto_20150630_1149'),
  11 + ]
  12 +
  13 + operations = [
  14 + migrations.RemoveField(
  15 + model_name='gitlabcomment',
  16 + name='iid',
  17 + ),
  18 + migrations.AddField(
  19 + model_name='gitlabissue',
  20 + name='iid',
  21 + field=models.IntegerField(null=True),
  22 + preserve_default=True,
  23 + ),
  24 + ]
colab/plugins/gitlab/models.py
@@ -64,6 +64,7 @@ class GitlabMergeRequest(Collaboration): @@ -64,6 +64,7 @@ class GitlabMergeRequest(Collaboration):
64 class GitlabIssue(Collaboration): 64 class GitlabIssue(Collaboration):
65 65
66 id = models.IntegerField(primary_key=True) 66 id = models.IntegerField(primary_key=True)
  67 + iid = models.IntegerField(null=True)
67 project = models.ForeignKey(GitlabProject, null=True, 68 project = models.ForeignKey(GitlabProject, null=True,
68 on_delete=models.SET_NULL) 69 on_delete=models.SET_NULL)
69 title = models.TextField() 70 title = models.TextField()
@@ -82,7 +83,7 @@ class GitlabIssue(Collaboration): @@ -82,7 +83,7 @@ class GitlabIssue(Collaboration):
82 @property 83 @property
83 def url(self): 84 def url(self):
84 return u'/gitlab/{}/issues/{}'.format( 85 return u'/gitlab/{}/issues/{}'.format(
85 - self.project.path_with_namespace, self.id) 86 + self.project.path_with_namespace, self.iid)
86 87
87 class Meta: 88 class Meta:
88 verbose_name = _('Gitlab Issue') 89 verbose_name = _('Gitlab Issue')
@@ -92,7 +93,6 @@ class GitlabIssue(Collaboration): @@ -92,7 +93,6 @@ class GitlabIssue(Collaboration):
92 class GitlabComment(Collaboration): 93 class GitlabComment(Collaboration):
93 94
94 id = models.IntegerField(primary_key=True) 95 id = models.IntegerField(primary_key=True)
95 - iid = models.IntegerField(null=True)  
96 body = models.TextField() 96 body = models.TextField()
97 created_at = models.DateTimeField(blank=True, null=True) 97 created_at = models.DateTimeField(blank=True, null=True)
98 issue_comment = models.BooleanField(default=True) 98 issue_comment = models.BooleanField(default=True)
@@ -139,7 +139,7 @@ class GitlabComment(Collaboration): @@ -139,7 +139,7 @@ class GitlabComment(Collaboration):
139 url_str = u'/gitlab/{}/merge_requests/{}#notes_{}' 139 url_str = u'/gitlab/{}/merge_requests/{}#notes_{}'
140 140
141 return url_str.format(self.project.path_with_namespace, 141 return url_str.format(self.project.path_with_namespace,
142 - self.parent_id, self.iid) 142 + self.parent_id, self.id)
143 143
144 class Meta: 144 class Meta:
145 verbose_name = _('Gitlab Comments') 145 verbose_name = _('Gitlab Comments')
colab/plugins/gitlab/tests/test_gitlab.py
@@ -24,9 +24,9 @@ class GitlabTest(TestCase): @@ -24,9 +24,9 @@ class GitlabTest(TestCase):
24 pass 24 pass
25 25
26 def test_data_integrity(self): 26 def test_data_integrity(self):
27 - self.assertEqual(GitlabProject.objects.all().count(), 1)  
28 - self.assertEqual(GitlabMergeRequest.objects.all().count(), 1)  
29 - self.assertEqual(GitlabIssue.objects.all().count(), 1) 27 + self.assertEqual(GitlabProject.objects.all().count(), 2)
  28 + self.assertEqual(GitlabMergeRequest.objects.all().count(), 2)
  29 + self.assertEqual(GitlabIssue.objects.all().count(), 2)
30 self.assertEqual(GitlabComment.objects.all().count(), 2) 30 self.assertEqual(GitlabComment.objects.all().count(), 2)
31 31
32 def test_project_url(self): 32 def test_project_url(self):
@@ -34,55 +34,92 @@ class GitlabTest(TestCase): @@ -34,55 +34,92 @@ class GitlabTest(TestCase):
34 '/gitlab/softwarepublico/colab') 34 '/gitlab/softwarepublico/colab')
35 35
36 def test_merge_request_url(self): 36 def test_merge_request_url(self):
37 - self.assertEqual(GitlabMergeRequest.objects.get(iid=1).url, 37 + self.assertEqual(GitlabMergeRequest.objects.get(id=1).url,
38 '/gitlab/softwarepublico/colab/merge_requests/1') 38 '/gitlab/softwarepublico/colab/merge_requests/1')
  39 + self.assertEqual(GitlabMergeRequest.objects.get(id=2).url,
  40 + '/gitlab/softwarepublico/colabinc/merge_requests/1')
39 41
40 def test_issue_url(self): 42 def test_issue_url(self):
41 self.assertEqual(GitlabIssue.objects.get(id=1).url, 43 self.assertEqual(GitlabIssue.objects.get(id=1).url,
42 '/gitlab/softwarepublico/colab/issues/1') 44 '/gitlab/softwarepublico/colab/issues/1')
  45 + self.assertEqual(GitlabIssue.objects.get(id=2).url,
  46 + '/gitlab/softwarepublico/colabinc/issues/1')
43 47
44 def test_comment_on_mr_url(self): 48 def test_comment_on_mr_url(self):
45 url = '/gitlab/softwarepublico/colab/merge_requests/1#notes_1' 49 url = '/gitlab/softwarepublico/colab/merge_requests/1#notes_1'
46 - self.assertEqual(GitlabComment.objects.get(iid=1).url, url) 50 + self.assertEqual(GitlabComment.objects.get(id=1).url, url)
47 51
48 def test_comment_on_issue_url(self): 52 def test_comment_on_issue_url(self):
49 self.assertEqual(GitlabComment.objects.get(id=2).url, 53 self.assertEqual(GitlabComment.objects.get(id=2).url,
50 '/gitlab/softwarepublico/colab/issues/1#notes_2') 54 '/gitlab/softwarepublico/colab/issues/1#notes_2')
51 55
52 def create_gitlab_data(self): 56 def create_gitlab_data(self):
53 - g = GitlabProject()  
54 - g.id = 1  
55 - g.name = "colab"  
56 - g.name_with_namespace = "Software Public / Colab"  
57 - g.path_with_namespace = "softwarepublico/colab"  
58 - g.created_at = datetime.now()  
59 - g.last_activity_at = datetime.now()  
60 - g.save()  
61 -  
62 - mr = GitlabMergeRequest()  
63 - mr.iid = 1  
64 - mr.project = g  
65 - mr.title = "Include plugin support"  
66 - mr.description = "Merge request for plugin support"  
67 - mr.state = "Closed"  
68 - mr.created_at = datetime.now()  
69 - mr.update_user(self.user.username)  
70 - mr.save()  
71 -  
72 - i = GitlabIssue()  
73 - i.id = 1  
74 - i.project = g  
75 - i.title = "Issue for colab"  
76 - i.description = "Issue reported to colab"  
77 - i.created_at = datetime.now()  
78 - i.state = "Open"  
79 - i.update_user(self.user.username)  
80 - i.save() 57 + g1 = GitlabProject()
  58 + g1.id = 1
  59 + g1.name = "colab"
  60 + g1.name_with_namespace = "Software Public / Colab"
  61 + g1.path_with_namespace = "softwarepublico/colab"
  62 + g1.created_at = datetime.now()
  63 + g1.last_activity_at = datetime.now()
  64 + g1.save()
  65 +
  66 + g2 = GitlabProject()
  67 + g2.id = 2
  68 + g2.name = "colabinc"
  69 + g2.name_with_namespace = "Software Public / ColabInc"
  70 + g2.path_with_namespace = "softwarepublico/colabinc"
  71 + g2.created_at = datetime.now()
  72 + g2.last_activity_at = datetime.now()
  73 + g2.save()
  74 +
  75 + mr1 = GitlabMergeRequest()
  76 + mr1.id = 1
  77 + mr1.iid = 1
  78 + mr1.project = g1
  79 + mr1.title = "Include plugin support"
  80 + mr1.description = "Merge request for plugin support"
  81 + mr1.state = "Closed"
  82 + mr1.created_at = datetime.now()
  83 + mr1.update_user(self.user.username)
  84 + mr1.save()
  85 +
  86 + mr2 = GitlabMergeRequest()
  87 + mr2.id = 2
  88 + mr2.iid = 1
  89 + mr2.project = g2
  90 + mr2.title = "Include test support"
  91 + mr2.description = "Merge request for test support"
  92 + mr2.state = "Closed"
  93 + mr2.created_at = datetime.now()
  94 + mr2.update_user(self.user.username)
  95 + mr2.save()
  96 +
  97 + i1 = GitlabIssue()
  98 + i1.id = 1
  99 + i1.iid = 1
  100 + i1.project = g1
  101 + i1.title = "Issue for colab"
  102 + i1.description = "Issue reported to colab"
  103 + i1.created_at = datetime.now()
  104 + i1.state = "Open"
  105 + i1.update_user(self.user.username)
  106 + i1.save()
  107 +
  108 + i2 = GitlabIssue()
  109 + i2.id = 2
  110 + i2.iid = 1
  111 + i2.project = g2
  112 + i2.title = "Issue for colab"
  113 + i2.description = "Issue reported to colab"
  114 + i2.created_at = datetime.now()
  115 + i2.state = "Open"
  116 + i2.update_user(self.user.username)
  117 + i2.save()
81 118
82 c1 = GitlabComment() 119 c1 = GitlabComment()
83 - c1.iid = 1  
84 - c1.parent_id = mr.iid  
85 - c1.project = g 120 + c1.id = 1
  121 + c1.parent_id = mr1.iid
  122 + c1.project = g1
86 c1.body = "Comment to merge request" 123 c1.body = "Comment to merge request"
87 c1.created_at = datetime.now() 124 c1.created_at = datetime.now()
88 c1.issue_comment = False 125 c1.issue_comment = False
@@ -90,9 +127,9 @@ class GitlabTest(TestCase): @@ -90,9 +127,9 @@ class GitlabTest(TestCase):
90 c1.save() 127 c1.save()
91 128
92 c2 = GitlabComment() 129 c2 = GitlabComment()
93 - c2.iid = 2  
94 - c2.parent_id = i.id  
95 - c2.project = g 130 + c2.id = 2
  131 + c2.parent_id = i1.id
  132 + c2.project = g1
96 c2.body = "Comment to issue" 133 c2.body = "Comment to issue"
97 c2.created_at = datetime.now() 134 c2.created_at = datetime.now()
98 c2.issue_comment = True 135 c2.issue_comment = True
colab/search/utils.py
@@ -15,11 +15,12 @@ from colab.accounts.utils import mailman @@ -15,11 +15,12 @@ from colab.accounts.utils import mailman
15 15
16 def get_visible_threads_queryset(logged_user): 16 def get_visible_threads_queryset(logged_user):
17 queryset = Thread.objects 17 queryset = Thread.objects
18 - lists_for_user = [] 18 + listnames_for_user = []
19 if logged_user: 19 if logged_user:
20 lists_for_user = mailman.get_user_mailinglists(logged_user) 20 lists_for_user = mailman.get_user_mailinglists(logged_user)
  21 + listnames_for_user = mailman.extract_listname_from_list(lists_for_user)
21 22
22 - user_lists = Condition(mailinglist__name__in=lists_for_user) 23 + user_lists = Condition(mailinglist__name__in=listnames_for_user)
23 public_lists = Condition(mailinglist__is_private=False) 24 public_lists = Condition(mailinglist__is_private=False)
24 queryset = Thread.objects.filter(user_lists | public_lists) 25 queryset = Thread.objects.filter(user_lists | public_lists)
25 26
@@ -28,6 +29,7 @@ def get_visible_threads_queryset(logged_user): @@ -28,6 +29,7 @@ def get_visible_threads_queryset(logged_user):
28 29
29 def get_visible_threads(logged_user, filter_by_user=None): 30 def get_visible_threads(logged_user, filter_by_user=None):
30 thread_qs = get_visible_threads_queryset(logged_user) 31 thread_qs = get_visible_threads_queryset(logged_user)
  32 +
31 if filter_by_user: 33 if filter_by_user:
32 message_qs = Message.objects.filter(thread__in=thread_qs) 34 message_qs = Message.objects.filter(thread__in=thread_qs)
33 messages = message_qs.filter( 35 messages = message_qs.filter(
@@ -57,10 +59,8 @@ def get_collaboration_data(logged_user, filter_by_user=None): @@ -57,10 +59,8 @@ def get_collaboration_data(logged_user, filter_by_user=None):
57 59
58 latest_results.extend(messages) 60 latest_results.extend(messages)
59 61
60 - app_names = settings.COLAB_APPS.keys()  
61 -  
62 - for app_name in app_names:  
63 - module = importlib.import_module('{}.models'.format(app_name)) 62 + for app in settings.COLAB_APPS.values():
  63 + module = importlib.import_module('{}.models'.format(app.get('name')))
64 64
65 for module_item_name in dir(module): 65 for module_item_name in dir(module):
66 module_item = getattr(module, module_item_name) 66 module_item = getattr(module, module_item_name)
colab/settings.py
@@ -239,7 +239,7 @@ SUPER_ARCHIVES_EXCLUDE = [] @@ -239,7 +239,7 @@ SUPER_ARCHIVES_EXCLUDE = []
239 SUPER_ARCHIVES_LOCK_FILE = '/var/lock/colab/import_emails.lock' 239 SUPER_ARCHIVES_LOCK_FILE = '/var/lock/colab/import_emails.lock'
240 240
241 # Mailman API settings 241 # Mailman API settings
242 -MAILMAN_API_URL = 'http://localhost:8124' 242 +MAILMAN_API_URL = 'http://localhost:8124/v2/'
243 243
244 LOGIN_URL = '/user/login' 244 LOGIN_URL = '/user/login'
245 LOGIN_REDIRECT_URL = '/' 245 LOGIN_REDIRECT_URL = '/'
@@ -266,8 +266,8 @@ for app_name, app in COLAB_APPS.items(): @@ -266,8 +266,8 @@ for app_name, app in COLAB_APPS.items():
266 if dep not in INSTALLED_APPS: 266 if dep not in INSTALLED_APPS:
267 INSTALLED_APPS += (dep,) 267 INSTALLED_APPS += (dep,)
268 268
269 - if app_name not in INSTALLED_APPS:  
270 - INSTALLED_APPS += (app_name,) 269 + if app.get('name') not in INSTALLED_APPS:
  270 + INSTALLED_APPS += (app.get('name'),)
271 271
272 if 'middlewares' in app: 272 if 'middlewares' in app:
273 for middleware in app.get('middlewares'): 273 for middleware in app.get('middlewares'):
colab/super_archives/fixtures/mailinglistdata.json
@@ -6,23 +6,23 @@ @@ -6,23 +6,23 @@
6 "twitter": null, 6 "twitter": null,
7 "is_staff": false, 7 "is_staff": false,
8 "user_permissions": [ 8 "user_permissions": [
9 - 9 +
10 ], 10 ],
11 "date_joined": "2015-02-24T21:10:35.004Z", 11 "date_joined": "2015-02-24T21:10:35.004Z",
12 "google_talk": null, 12 "google_talk": null,
13 - "first_name": "Gust", 13 + "first_name": "John",
14 "is_superuser": false, 14 "is_superuser": false,
15 "last_login": "2015-02-26T17:56:13.378Z", 15 "last_login": "2015-02-26T17:56:13.378Z",
16 "verification_hash": null, 16 "verification_hash": null,
17 "role": null, 17 "role": null,
18 - "email": "gustmax@hotmail.com",  
19 - "username": "gustmax", 18 + "email": "johndoe@example.com",
  19 + "username": "johndoe",
20 "bio": null, 20 "bio": null,
21 "needs_update": false, 21 "needs_update": false,
22 "is_active": true, 22 "is_active": true,
23 "facebook": null, 23 "facebook": null,
24 "groups": [ 24 "groups": [
25 - 25 +
26 ], 26 ],
27 "password": "pbkdf2_sha256$12000$ez83ccNOUQZk$vYT/QcYMukXZ7D7L1qQPyYlzCUEEEF20J7/Xjef0Rqg=", 27 "password": "pbkdf2_sha256$12000$ez83ccNOUQZk$vYT/QcYMukXZ7D7L1qQPyYlzCUEEEF20J7/Xjef0Rqg=",
28 "institution": null, 28 "institution": null,
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 "real_name": "", 37 "real_name": "",
38 "user": 1, 38 "user": 1,
39 "md5": "ed8f47ae6048f8d4456c0554578f53ff", 39 "md5": "ed8f47ae6048f8d4456c0554578f53ff",
40 - "address": "gustmax@hotmail.com" 40 + "address": "johndoe@example.com"
41 }, 41 },
42 "model": "super_archives.emailaddress", 42 "model": "super_archives.emailaddress",
43 "pk": 1 43 "pk": 1
colab/super_archives/models.py
@@ -41,11 +41,17 @@ class EmailAddressValidation(models.Model): @@ -41,11 +41,17 @@ class EmailAddressValidation(models.Model):
41 def create(cls, address, user): 41 def create(cls, address, user):
42 email_address_validation = cls.objects.create(address=address, 42 email_address_validation = cls.objects.create(address=address,
43 user=user) 43 user=user)
44 - email.send_verification_email(email_address_validation.address,  
45 - email_address_validation.user,  
46 - email_address_validation.validation_key)  
47 return email_address_validation 44 return email_address_validation
48 45
  46 + @classmethod
  47 + def verify_email(cls, email_address_validation, verification_url):
  48 + return email.send_verification_email(
  49 + email_address_validation.address,
  50 + email_address_validation.user,
  51 + email_address_validation.validation_key,
  52 + verification_url
  53 + )
  54 +
49 55
50 class EmailAddress(models.Model): 56 class EmailAddress(models.Model):
51 user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, 57 user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
colab/super_archives/templates/superarchives/emails/email_verification.txt
1 {% load i18n %} 1 {% load i18n %}
2 {% blocktrans with fullname=user.get_full_name|title username=user.username|lower %}Hey, we want to verify that you are indeed "{{ fullname }} ({{ username }})". If that's the case, please follow the link below:{% endblocktrans %} 2 {% blocktrans with fullname=user.get_full_name|title username=user.username|lower %}Hey, we want to verify that you are indeed "{{ fullname }} ({{ username }})". If that's the case, please follow the link below:{% endblocktrans %}
3 3
4 -{{ SITE_URL }}{% url 'archive_email_view' key %} 4 +{{ verification_url }}
5 5
6 {% blocktrans with username=user.username %}If you're not {{ username }} or didn't request verification you can ignore this email.{% endblocktrans %} 6 {% blocktrans with username=user.username %}If you're not {{ username }} or didn't request verification you can ignore this email.{% endblocktrans %}
colab/super_archives/tests/test_privatelist.py
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 import mock 2 import mock
3 3
4 from colab.accounts.utils import mailman 4 from colab.accounts.utils import mailman
5 -from django.test import TestCase, Client 5 +from django.test import TestCase, Client
6 6
7 7
8 class ArchivesViewTest(TestCase): 8 class ArchivesViewTest(TestCase):
@@ -13,11 +13,14 @@ class ArchivesViewTest(TestCase): @@ -13,11 +13,14 @@ class ArchivesViewTest(TestCase):
13 self.client = Client() 13 self.client = Client()
14 14
15 def authenticate_user(self): 15 def authenticate_user(self):
16 - self.client.login(username='gustmax', password='1234') 16 + self.client.login(username='johndoe', password='1234')
17 17
18 def test_see_only_private_list_if_member(self): 18 def test_see_only_private_list_if_member(self):
19 mailman.get_user_mailinglists = mock.Mock( 19 mailman.get_user_mailinglists = mock.Mock(
  20 + return_value="[{'listname': 'privatelist'}]")
  21 + mailman.extract_listname_from_list = mock.Mock(
20 return_value="['privatelist']") 22 return_value="['privatelist']")
  23 + mailman.list_users = mock.Mock(return_value="['johndoe@example.com']")
21 24
22 self.authenticate_user() 25 self.authenticate_user()
23 request = self.client.get('/archives/thread/') 26 request = self.client.get('/archives/thread/')
@@ -26,7 +29,7 @@ class ArchivesViewTest(TestCase): @@ -26,7 +29,7 @@ class ArchivesViewTest(TestCase):
26 29
27 self.assertEqual('lista', list_data[0][0]) 30 self.assertEqual('lista', list_data[0][0])
28 self.assertEqual('privatelist', list_data[1][0]) 31 self.assertEqual('privatelist', list_data[1][0])
29 - self.assertEqual(2, len(list_data)) 32 + self.assertEqual(2, len(list_data))
30 33
31 def test_see_only_public_if_not_logged_in(self): 34 def test_see_only_public_if_not_logged_in(self):
32 request = self.client.get('/archives/thread/') 35 request = self.client.get('/archives/thread/')
@@ -34,10 +37,12 @@ class ArchivesViewTest(TestCase): @@ -34,10 +37,12 @@ class ArchivesViewTest(TestCase):
34 list_data = request.context['lists'] 37 list_data = request.context['lists']
35 38
36 self.assertEqual('lista', list_data[0][0]) 39 self.assertEqual('lista', list_data[0][0])
37 - self.assertEqual(1, len(list_data)) 40 + self.assertEqual(1, len(list_data))
38 41
39 def test_see_private_thread_in_dashboard_if_member(self): 42 def test_see_private_thread_in_dashboard_if_member(self):
40 mailman.get_user_mailinglists = mock.Mock( 43 mailman.get_user_mailinglists = mock.Mock(
  44 + return_value="[{'listname': 'privatelist'}]")
  45 + mailman.extract_listname_from_list = mock.Mock(
41 return_value="['privatelist']") 46 return_value="['privatelist']")
42 47
43 self.authenticate_user() 48 self.authenticate_user()
@@ -59,7 +64,7 @@ class ArchivesViewTest(TestCase): @@ -59,7 +64,7 @@ class ArchivesViewTest(TestCase):
59 self.assertEqual(1, len(hottest_threads)) 64 self.assertEqual(1, len(hottest_threads))
60 65
61 def test_dont_see_private_threads_in_profile_if_logged_out(self): 66 def test_dont_see_private_threads_in_profile_if_logged_out(self):
62 - request = self.client.get('/account/gustmax') 67 + request = self.client.get('/account/johndoe')
63 68
64 emails = request.context['emails'] 69 emails = request.context['emails']
65 70
colab/super_archives/utils/email.py
@@ -10,11 +10,11 @@ def colab_send_email(subject, message, to): @@ -10,11 +10,11 @@ def colab_send_email(subject, message, to):
10 return mail.send_mail(subject, message, from_email, [to]) 10 return mail.send_mail(subject, message, from_email, [to])
11 11
12 12
13 -def send_verification_email(to, user, validation_key): 13 +def send_verification_email(to, user, validation_key, verification_url):
14 subject = _('Please verify your email ') + u'{}'.format(to) 14 subject = _('Please verify your email ') + u'{}'.format(to)
15 msg_tmpl = \ 15 msg_tmpl = \
16 loader.get_template('superarchives/emails/email_verification.txt') 16 loader.get_template('superarchives/emails/email_verification.txt')
17 message = msg_tmpl.render(Context({'to': to, 'user': user, 17 message = msg_tmpl.render(Context({'to': to, 'user': user,
18 - 'key': validation_key,  
19 - 'SITE_URL': settings.SITE_URL})) 18 + 'verification_url': verification_url
  19 + }))
20 return colab_send_email(subject, message, to) 20 return colab_send_email(subject, message, to)
colab/super_archives/views.py
@@ -9,6 +9,7 @@ import requests @@ -9,6 +9,7 @@ import requests
9 from django import http 9 from django import http
10 from django.conf import settings 10 from django.conf import settings
11 from django.contrib import messages 11 from django.contrib import messages
  12 +from django.core.urlresolvers import reverse
12 from django.db import IntegrityError 13 from django.db import IntegrityError
13 from django.views.generic import View 14 from django.views.generic import View
14 from django.utils.translation import ugettext as _ 15 from django.utils.translation import ugettext as _
@@ -32,17 +33,23 @@ class ThreadView(View): @@ -32,17 +33,23 @@ class ThreadView(View):
32 thread = get_object_or_404(Thread, subject_token=thread_token, 33 thread = get_object_or_404(Thread, subject_token=thread_token,
33 mailinglist__name=mailinglist) 34 mailinglist__name=mailinglist)
34 35
35 - # TODO: Refactor this code  
36 - # Use local flag is_private instead of always check the API!!  
37 - all_privates = dict(mailman.all_lists(private=True))  
38 - if all_privates[thread.mailinglist.name]: 36 + all_privates = []
  37 + all_privates.extend(
  38 + [mlist.get('listname')
  39 + for mlist in mailman.all_lists()
  40 + if mlist.get('archive_private')]
  41 + )
  42 +
  43 + if all_privates.count(thread.mailinglist.name):
39 if not request.user.is_authenticated(): 44 if not request.user.is_authenticated():
40 raise PermissionDenied 45 raise PermissionDenied
41 else: 46 else:
42 user = User.objects.get(username=request.user) 47 user = User.objects.get(username=request.user)
43 emails = user.emails.values_list('address', flat=True) 48 emails = user.emails.values_list('address', flat=True)
44 lists_for_user = mailman.get_user_mailinglists(user) 49 lists_for_user = mailman.get_user_mailinglists(user)
45 - if thread.mailinglist.name not in lists_for_user: 50 + listnames_for_user = mailman.extract_listname_from_list(
  51 + lists_for_user)
  52 + if thread.mailinglist.name not in listnames_for_user:
46 raise PermissionDenied 53 raise PermissionDenied
47 54
48 thread.hit(request) 55 thread.hit(request)
@@ -144,13 +151,16 @@ class ThreadDashboardView(View): @@ -144,13 +151,16 @@ class ThreadDashboardView(View):
144 151
145 context['lists'] = [] 152 context['lists'] = []
146 153
147 - lists_for_user = [] 154 + listnames_for_user = []
148 if request.user.is_authenticated(): 155 if request.user.is_authenticated():
149 user = User.objects.get(username=request.user) 156 user = User.objects.get(username=request.user)
150 lists_for_user = mailman.get_user_mailinglists(user) 157 lists_for_user = mailman.get_user_mailinglists(user)
  158 + listnames_for_user = mailman.extract_listname_from_list(
  159 + lists_for_user)
151 160
152 for list_ in MailingList.objects.order_by('name'): 161 for list_ in MailingList.objects.order_by('name'):
153 - if list_.name not in all_privates or list_.name in lists_for_user: 162 + if list_.name not in all_privates\
  163 + or list_.name in listnames_for_user:
154 context['lists'].append(( 164 context['lists'].append((
155 list_.name, 165 list_.name,
156 mailman.get_list_description(list_.name), 166 mailman.get_list_description(list_.name),
@@ -177,7 +187,7 @@ class EmailView(View): @@ -177,7 +187,7 @@ class EmailView(View):
177 except EmailAddressValidation.DoesNotExist: 187 except EmailAddressValidation.DoesNotExist:
178 messages.error(request, _('The email address you are trying to ' 188 messages.error(request, _('The email address you are trying to '
179 'verify either has already been verified' 189 'verify either has already been verified'
180 - 'or does not exist.')) 190 + ' or does not exist.'))
181 return redirect('/') 191 return redirect('/')
182 192
183 try: 193 try:
@@ -289,8 +299,11 @@ class EmailValidationView(View): @@ -289,8 +299,11 @@ class EmailValidationView(View):
289 raise http.Http404 299 raise http.Http404
290 300
291 try: 301 try:
292 - send_verification_email(email_addr, email.user,  
293 - email.validation_key) 302 + location = reverse('archive_email_view',
  303 + kwargs={'key': email.validation_key})
  304 + verification_url = request.build_absolute_uri(location)
  305 + send_verification_email(request, email_addr, email.user,
  306 + email.validation_key, verification_url)
294 except smtplib.SMTPException: 307 except smtplib.SMTPException:
295 logging.exception('Error sending validation email') 308 logging.exception('Error sending validation email')
296 return http.HttpResponseServerError() 309 return http.HttpResponseServerError()
colab/utils/conf.py
@@ -95,6 +95,7 @@ def load_colab_apps(): @@ -95,6 +95,7 @@ def load_colab_apps():
95 return {'COLAB_APPS': COLAB_APPS} 95 return {'COLAB_APPS': COLAB_APPS}
96 96
97 for file_name in os.listdir(plugins_dir): 97 for file_name in os.listdir(plugins_dir):
  98 + app_name = ""
98 file_module = file_name.split('.')[0] 99 file_module = file_name.split('.')[0]
99 100
100 logger.info('Loaded plugin settings: %s%s', plugins_dir, file_name) 101 logger.info('Loaded plugin settings: %s%s', plugins_dir, file_name)
@@ -104,7 +105,7 @@ def load_colab_apps(): @@ -104,7 +105,7 @@ def load_colab_apps():
104 app_name = file_name 105 app_name = file_name
105 106
106 elif file_name.endswith('.py'): 107 elif file_name.endswith('.py'):
107 - app_name = py_settings_d.get('name') 108 + app_name = py_settings_d.get('name').split('.')[-1]
108 109
109 if not app_name: 110 if not app_name:
110 logger.warning("Plugin missing name variable (%s)", file_name) 111 logger.warning("Plugin missing name variable (%s)", file_name)
@@ -121,7 +122,7 @@ def load_colab_apps(): @@ -121,7 +122,7 @@ def load_colab_apps():
121 122
122 fields = ['verbose_name', 'upstream', 'urls', 123 fields = ['verbose_name', 'upstream', 'urls',
123 'menu_urls', 'middlewares', 'dependencies', 124 'menu_urls', 'middlewares', 'dependencies',
124 - 'context_processors', 'private_token'] 125 + 'context_processors', 'private_token', 'name']
125 126
126 for key in fields: 127 for key in fields:
127 value = py_settings_d.get(key) 128 value = py_settings_d.get(key)