Merge Request #82

Merged
softwarepublico/colab!82
Created by Charles Oliveira

Added moderated email lists subscriptions/unsubscriptions notifications

Merged by Lucas Kanashiro

Source branch has been removed
Commits (12)
5 participants
colab/accounts/forms.py
... ... @@ -141,8 +141,9 @@ class UserUpdateForm(UserForm):
141 141  
142 142 class ListsForm(forms.Form):
143 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 148 lists = forms.MultipleChoiceField(label=_(u'Mailing lists'),
148 149 required=False,
... ...
colab/accounts/models.py
... ... @@ -60,7 +60,7 @@ class User(AbstractUser):
60 60 return mailman.user_lists(self)
61 61  
62 62 def update_subscription(self, email, lists):
63   - mailman.update_subscription(email, lists)
  63 + return mailman.update_subscription(email, lists)
64 64  
65 65 def save(self, *args, **kwargs):
66 66  
... ...
colab/accounts/templates/accounts/user_detail.html
... ... @@ -84,7 +84,6 @@
84 84 {% endif %}
85 85 {% endif %}
86 86 </ul>
87   -
88 87 {% if user_.mailinglists %}
89 88 <b>{% trans 'Groups: ' %}</b>
90 89 {% for list in user_.mailinglists %}
... ...
colab/accounts/utils/mailman.py
... ... @@ -4,66 +4,99 @@ import requests
4 4 import logging
5 5  
6 6 from django.conf import settings
  7 +from django.contrib import messages
7 8  
8 9 TIMEOUT = 1
9 10  
10 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 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 37 def subscribe(listname, address):
21   - url = get_url(listname)
  38 + url = get_url('subscribe/', listname=listname)
22 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 43 except:
25 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 48 def unsubscribe(listname, address):
31   - url = get_url(listname)
  49 + url = get_url('subscribe/', listname)
32 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 54 except:
35 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 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 63 for maillist in current_lists:
44 64 if maillist not in lists:
45   - unsubscribe(maillist, address)
  65 + info_messages.append(unsubscribe(maillist, address))
46 66  
47 67 for maillist in lists:
48 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 74 def mailing_lists(**kwargs):
53   - url = get_url()
  75 + url = get_url('lists/')
54 76  
55 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 81 except:
58 82 LOGGER.exception('Unable to list mailing lists')
59 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 94 def is_private_list(name):
65 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 100 except KeyError:
68 101 return []
69 102  
... ... @@ -76,22 +109,25 @@ def user_lists(user):
76 109 list_set = set()
77 110  
78 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 115 return tuple(list_set)
82 116  
83 117  
84 118 def get_list_description(listname, lists=None):
85 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 129 def list_users(listname):
94   - url = get_url(listname)
  130 + url = get_url('members/', listname)
95 131  
96 132 params = {}
97 133  
... ... @@ -114,3 +150,10 @@ def get_user_mailinglists(user):
114 150 lists_for_user.extend(mailing_lists(address=email))
115 151  
116 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
... ... @@ -144,7 +144,11 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
144 144 user = self.get_object()
145 145 for email in user.emails.values_list('address', flat=True):
146 146 lists = self.request.POST.getlist(email)
147   - 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 +
148 152  
149 153 return redirect('user_profile', username=user.username)
150 154  
... ... @@ -154,18 +158,19 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView):
154 158  
155 159 user = self.get_object()
156 160 emails = user.emails.values_list('address', flat=True)
157   - all_lists = mailman.all_lists(description=True)
  161 + all_lists = mailman.all_lists()
158 162  
159 163 for email in emails:
160 164 lists = []
161   - lists_for_address = mailman.mailing_lists(address=email)
162   - for listname, description in all_lists:
163   - 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:
164 168 checked = True
165 169 else:
166 170 checked = False
167 171 lists.append((
168   - {'listname': listname, 'description': description},
  172 + {'listname': mlist.get('listname'),
  173 + 'description': mlist.get('description')},
169 174 checked
170 175 ))
171 176  
... ...
colab/home/views.py
... ... @@ -10,9 +10,10 @@ from colab.accounts.models import User
10 10  
11 11 def get_user_threads(threads, lists_for_user, key):
12 12 visible_threads = []
  13 + listnames_for_user = mailman.extract_listname_from_list(lists_for_user)
13 14 for t in threads:
14 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 17 visible_threads.append(key(t))
17 18  
18 19 return visible_threads
... ...
colab/search/utils.py
... ... @@ -15,11 +15,12 @@ from colab.accounts.utils import mailman
15 15  
16 16 def get_visible_threads_queryset(logged_user):
17 17 queryset = Thread.objects
18   - lists_for_user = []
  18 + listnames_for_user = []
19 19 if logged_user:
20 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 24 public_lists = Condition(mailinglist__is_private=False)
24 25 queryset = Thread.objects.filter(user_lists | public_lists)
25 26  
... ... @@ -28,6 +29,7 @@ def get_visible_threads_queryset(logged_user):
28 29  
29 30 def get_visible_threads(logged_user, filter_by_user=None):
30 31 thread_qs = get_visible_threads_queryset(logged_user)
  32 +
31 33 if filter_by_user:
32 34 message_qs = Message.objects.filter(thread__in=thread_qs)
33 35 messages = message_qs.filter(
... ...
colab/settings.py
... ... @@ -239,7 +239,7 @@ SUPER_ARCHIVES_EXCLUDE = []
239 239 SUPER_ARCHIVES_LOCK_FILE = '/var/lock/colab/import_emails.lock'
240 240  
241 241 # Mailman API settings
242   -MAILMAN_API_URL = 'http://localhost:8124'
  242 +MAILMAN_API_URL = 'http://localhost:8124/v2/'
243 243  
244 244 LOGIN_URL = '/user/login'
245 245 LOGIN_REDIRECT_URL = '/'
... ...
colab/super_archives/fixtures/mailinglistdata.json
... ... @@ -6,23 +6,23 @@
6 6 "twitter": null,
7 7 "is_staff": false,
8 8 "user_permissions": [
9   -
  9 +
10 10 ],
11 11 "date_joined": "2015-02-24T21:10:35.004Z",
12 12 "google_talk": null,
13   - "first_name": "Gust",
  13 + "first_name": "John",
14 14 "is_superuser": false,
15 15 "last_login": "2015-02-26T17:56:13.378Z",
16 16 "verification_hash": null,
17 17 "role": null,
18   - "email": "gustmax@hotmail.com",
19   - "username": "gustmax",
  18 + "email": "johndoe@example.com",
  19 + "username": "johndoe",
20 20 "bio": null,
21 21 "needs_update": false,
22 22 "is_active": true,
23 23 "facebook": null,
24 24 "groups": [
25   -
  25 +
26 26 ],
27 27 "password": "pbkdf2_sha256$12000$ez83ccNOUQZk$vYT/QcYMukXZ7D7L1qQPyYlzCUEEEF20J7/Xjef0Rqg=",
28 28 "institution": null,
... ... @@ -37,7 +37,7 @@
37 37 "real_name": "",
38 38 "user": 1,
39 39 "md5": "ed8f47ae6048f8d4456c0554578f53ff",
40   - "address": "gustmax@hotmail.com"
  40 + "address": "johndoe@example.com"
41 41 },
42 42 "model": "super_archives.emailaddress",
43 43 "pk": 1
... ...
colab/super_archives/tests/test_privatelist.py
... ... @@ -2,7 +2,7 @@
2 2 import mock
3 3  
4 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 8 class ArchivesViewTest(TestCase):
... ... @@ -13,11 +13,14 @@ class ArchivesViewTest(TestCase):
13 13 self.client = Client()
14 14  
15 15 def authenticate_user(self):
16   - self.client.login(username='gustmax', password='1234')
  16 + self.client.login(username='johndoe', password='1234')
17 17  
18 18 def test_see_only_private_list_if_member(self):
19 19 mailman.get_user_mailinglists = mock.Mock(
  20 + return_value="[{'listname': 'privatelist'}]")
  21 + mailman.extract_listname_from_list = mock.Mock(
20 22 return_value="['privatelist']")
  23 + mailman.list_users = mock.Mock(return_value="['johndoe@example.com']")
21 24  
22 25 self.authenticate_user()
23 26 request = self.client.get('/archives/thread/')
... ... @@ -26,7 +29,7 @@ class ArchivesViewTest(TestCase):
26 29  
27 30 self.assertEqual('lista', list_data[0][0])
28 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 34 def test_see_only_public_if_not_logged_in(self):
32 35 request = self.client.get('/archives/thread/')
... ... @@ -34,10 +37,12 @@ class ArchivesViewTest(TestCase):
34 37 list_data = request.context['lists']
35 38  
36 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 42 def test_see_private_thread_in_dashboard_if_member(self):
40 43 mailman.get_user_mailinglists = mock.Mock(
  44 + return_value="[{'listname': 'privatelist'}]")
  45 + mailman.extract_listname_from_list = mock.Mock(
41 46 return_value="['privatelist']")
42 47  
43 48 self.authenticate_user()
... ... @@ -59,7 +64,7 @@ class ArchivesViewTest(TestCase):
59 64 self.assertEqual(1, len(hottest_threads))
60 65  
61 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 69 emails = request.context['emails']
65 70  
... ...
colab/super_archives/views.py
... ... @@ -33,17 +33,23 @@ class ThreadView(View):
33 33 thread = get_object_or_404(Thread, subject_token=thread_token,
34 34 mailinglist__name=mailinglist)
35 35  
36   - # TODO: Refactor this code
37   - # Use local flag is_private instead of always check the API!!
38   - all_privates = dict(mailman.all_lists(private=True))
39   - 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):
40 44 if not request.user.is_authenticated():
41 45 raise PermissionDenied
42 46 else:
43 47 user = User.objects.get(username=request.user)
44 48 emails = user.emails.values_list('address', flat=True)
45 49 lists_for_user = mailman.get_user_mailinglists(user)
46   - 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:
47 53 raise PermissionDenied
48 54  
49 55 thread.hit(request)
... ... @@ -145,13 +151,16 @@ class ThreadDashboardView(View):
145 151  
146 152 context['lists'] = []
147 153  
148   - lists_for_user = []
  154 + listnames_for_user = []
149 155 if request.user.is_authenticated():
150 156 user = User.objects.get(username=request.user)
151 157 lists_for_user = mailman.get_user_mailinglists(user)
  158 + listnames_for_user = mailman.extract_listname_from_list(
  159 + lists_for_user)
152 160  
153 161 for list_ in MailingList.objects.order_by('name'):
154   - 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:
155 164 context['lists'].append((
156 165 list_.name,
157 166 mailman.get_list_description(list_.name),
... ...
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    #84 describes more about these tasks

    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    Added 34 new commits:

    • 7c84af70 - Add celery to colab
    • 18990d74 - Add celery service
    • 50b085db - Fixing for tests
    • 692253bb - Merge branch 'add_celery'
    • 7f68454c - Remove Sorl from script.
    • 5e7a172c - Added epel to centos, because of celery.
    • 75f3d7a6 - Improved menu.
    • df127fc2 - updated revproxy and removed browserid
    • 13d0938f - Removed browserid left-overs
    • 6f406e45 - Added requires
    • e32b47e8 - Fixed search.
    • 166a69fb - Merge branch 'fix_master'
    • cbf39547 - 1.10.1 Release
    • 31499efd - Merge remote-tracking branch 'origin/match_release'
    • 2b5b7cce - Fix super_archives pt_BR translations
    • 5505fb3b - Include super_archives translations in MANIFEST.in
    • 1c074167 - Merge branch 'fix_locale' into 'master'
    • d9198a01 - Fix gitlab links
    • 9a166399 - Refactor url method of mr/issue
    • a70fae9a - Merge branch 'fix_gitlab_links' into 'master'
    • 1ba623cd - Merge branch 'master' of portal.softwarepublico.gov.br:softwarepublico/colab
    • 2a43c881 - No need for postgres nor solr
    • 3329a8d7 - We don't use apt on tests
    • 080cb1eb - Moving to new infrastructure
    • fd24ee4c - Fixed flake8
    • 97c80b74 - Updated setuptools
    • 83ef0de7 - Set colab_settings for tests
    • c72f8481 - Fixed pep8
    • f1e92a83 - Fixed Gitlab tests
    • c57dd273 - Added subscribe messages returned from Mailman
    • 11f37eda - Added informative messages for subscription and unsubscription on email lists
    • 46a0d475 - Added MAILMANAPI_URL ending slash to settings
    • 47ad4827 - Added code errors for subscription errors
    • 8d1f236c - Added code errors for subscription errors
    Choose File ...   File name...
    Cancel
  • 08d286e4ed2d9bc51fc10057fc32a3d4?s=40&d=identicon
    Carlos Coêlho @carlos

    Added 1 new commit:

    • f3b2dec1 - Updated mailing_lists to mailman-api v2
    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    Added 1 new commit:

    • 6002dcdb - Added support for mailman apiv2
    Choose File ...   File name...
    Cancel
  • 08d286e4ed2d9bc51fc10057fc32a3d4?s=40&d=identicon
    Carlos Coêlho @carlos

    Added 1 new commit:

    • 92cb599e - Replaced way to check dict for variable
    Choose File ...   File name...
    Cancel
  • 08d286e4ed2d9bc51fc10057fc32a3d4?s=40&d=identicon
    Carlos Coêlho @carlos

    Added 3 new commits:

    • 731470ba - Got description from attrs of list
    • c2179e5c - Updated to Mailman API v2 usage
    • 5c406cf7 - Fixed Mailman API v2 usage
    Choose File ...   File name...
    Cancel
  • 9fe63c7bd60deeb55e409a1d7dd173f5?s=40&d=identicon
    Sergio Oliveira @seocam (Edited )
    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    Added 1 new commit:

    • 5a65c125 - Making sure 'lists' is not just a string
    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    Added 1 new commit:

    • 4099dece - Making sure 'lists' is not just a string
    Choose File ...   File name...
    Cancel
  • Def69c998857099b7bc246389e6ad936?s=40&d=identicon
    Lucas Kanashiro @kanashiro

    Revisei esse PR e a moderação/subscribe/unsubscribe de listas está OK. Aguardando o release da versão 2 do mailman-api para realizar esse merge.

    Choose File ...   File name...
    Cancel
  • F1c4e7359f83d87a9b8ffc5a003e9cc9?s=40&d=identicon
    Alexandre Barbosa @alexandre

    Milestone changed to Versão 2 da mailman api

    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    Added 118 new commits:

    • 3e43c7db - Refactored colab-admin command
    • 13321ea3 - Reducing celery concurrency on local env
    • 4a4ddcb1 - Added init script for celery beat
    • 7ba7f4e6 - Importing data asynchronously
    • a12566bc - Data import moved to celery
    • 57470863 - Fixed pep8
    • e0dc6a6c - Removed PROXIED_APPS (replaced by COLAB_APPS)
    • 31e49bf3 - Renamed ColabProxiedAppConfig to ColabPluginAppConfig
    • d19c324f - Set task name manually
    • 1efa7b87 - Splitted gitlab imports to allow async workers
    • 540555ad - Refactored plugins to remove reference to proxy
    • 9c502ce5 - Fixed to only call tasks if fetch_data is not abstract
    • 1a166d1d - Removed unused import
    • 64781e7e - Allow full plugins on /etc/colab/plugins.d
    • 34773388 - Moved from rabbitmq to redis
    • 8a883b80 - Implemented locking to allow only one importing task at time
    • c548b5aa - Fix redis installation on centos
    • 0b348292 - Change settings.yaml to settings.py
    • 4cb38792 - Change periodic task to 60 seconds
    • 8440c81e - Merge pull request #58 from colab/refactor-data-import
    • b642d938 - Added better strategy for bootstrap hide elements
    • 709bf66a - Merge branch 'fix_merge_button' into 'master'
    • 152ebd04 - Add colab documentation for installation
    • 5037930f - Update user documentation
    • fe35cecc - Merge branch 'documentation' into 'master'
    • 935957af - Fixing database path
    • 0d5e9faf - Set colab_settings for tests
    • 58ec1d8d - Fixed pep8
    • 47ca4d8d - Fixed Gitlab tests
    • b71fe737 - Don't allow auto db conf if debug is False
    • 65a70b23 - Test settings validation for db
    • 5498660e - Removing old deps from badger
    • a2965d3c - remove legacy converse.js stuff
    • 121feb9a - Removed i18n_model (no longer needed)
    • 6c3931c0 - Reorganized deps on setup.py
    • ef767871 - Removed django-mobile
    • e373e29d - Readded tastypie
    • f83576eb - Removed open-data api
    • 87c02305 - Removed old TODO file
    • 9c0fc2b4 - Moved voting to super_archives
    • 70e66881 - Refactored vagrant install scripts
    • f1daeea2 - Ignore whoosh_index
    • aaaff48e - Added TODO
    • 2f9c5931 - Added missing slash
    • 6cb1ec46 - Removed old cron jobs left behind
    • 60d43389 - Removed tastypie dep
    • 9c651342 - Merge branch 'cleanup-old-deps' into fix_master
    • d1bc4536 - Removed precise64 support
    • cb01d435 - Added travis notifications on IRC
    • 8068038a - Merge branch 'fix_master'
    • 52ba4161 - Add support to noosfero data.
    • 6befc78f - Noosfero data to haystack.
    • 226f9140 - Change location of save model of noosfero import data
    • 1d7c2fa4 - Fixed flake8 checks
    • ce7c9854 - Merge branch 'noosfero_data' into 'master'
    • 84df3d2c - Added script to insert html content
    • c27c16e1 - Change colab_integration.js to use jquery
    • 2bd8d4af - Merge branch 'insert_tag_html'
    • 1b8059fc - Removed old exception
    • ed995b74 - Removed return before load settings.d
    • 39c37442 - Behave initial setup
    • f758f049 - Running behave with setup.py test
    • 399b7dd8 - Added behave to tests deps
    • f0596cb4 - Running behave on travis
    • 5bc7faa7 - Fixed travis env vars
    • 7b82e8f3 - Removed old confs for plugins
    • 698f2840 - Add celery to colab
    • 9773eddc - Add celery service
    • 5975ae7b - Create structure for colab signals
    • f1ff2fc7 - colab.signals: raise exception in send method
    • 508468fc - colab.signals: Making signal methods global
    • 4423a4f6 - Overwrite reduce parameter of Signal
    • 3c820045 - Create example of plugin using signals structure
    • d1286481 - Creating documentation for signals
    • c811c353 - Create abstract signal class for plugins
    • 7b743f36 - Update plugins dev documentation
    • 5af82b0a - Fixing signals test
    • 9f9d3466 - Solr not needed
    • 2f08f0d5 - Fixed PEP8
    • fe71a2b1 - Using custom exception
    • 502ba6f4 - Moved abstractsignal methods to abstractmethod
    • d6f2d2fb - Extending object
    • 29117f5a - django-celery not needed
    • 15e7387a - Fixed flake8 warnings/errors
    • 011124d4 - Fixed celery imports
    • 5971df7b - Add comment to reducer function on colab.signals
    • fac44a85 - Fix colab.signals tests
    • c30d7a00 - Added logging for settings initialization
    • 96c9b143 - Fixed signals implementation
    • b883fd46 - Removed "leftovers"
    • bf316bce - Refactored signals connections
    • 8570d70e - Create test for signals on colab.plugins.utils
    • e90428a9 - Update doc of plugins signals
    • 7fe0d78a - Remove unused import in test signals
    • 6c215efe - Remove signals.py from plugin dev documentation
    • 169689f7 - Fix signals in plugins.utils and plugins.gitlab
    • 60aef4af - Merge pull request #56 from colab/colab-signals
    • fd747671 - Merge branch 'master' of github.com:colab/colab
    • 721677c8 - Added warning when trying to import plugin from wrong path
    • 37c091d6 - Fixed way to build verification url
    • 566ca024 - Made sending verification email return status
    • 85cc285d - Merge branch 'fix_verification_url' into 'master'
    • d877c831 - Fix app_label in COLAB_APPS
    • 2c6621f8 - Merge branch 'fix_plugins_app_label' into 'master'
    • 86609bf9 - Fixing app_name in colab/utils/conf.py
    • bdd65848 - Merge branch 'fixing_app_name' into 'master'
    • 345b2731 - Added subscribe messages returned from Mailman
    • a7e544d0 - Added informative messages for subscription and unsubscription on email lists
    • 975fe726 - Added MAILMANAPI_URL ending slash to settings
    • 0f909e81 - Added code errors for subscription errors
    • 4b043b7a - Added code errors for subscription errors
    • a2b437aa - Updated mailing_lists to mailman-api v2
    • 5332878a - Added support for mailman apiv2
    • d921a387 - Replaced way to check dict for variable
    • 87e9e4a6 - Got description from attrs of list
    • b216ed6c - Updated to Mailman API v2 usage
    • b086563a - Fixed Mailman API v2 usage
    • 9acafb94 - Making sure 'lists' is not just a string
    Choose File ...   File name...
    Cancel
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira started a discussion on commit 9acafb94
    last updated by Charles Oliveira
  • 80bf7e338265f7a91f4144eaeded1f29?s=40&d=identicon
    Charles Oliveira @chaws

    mentioned in issue #55

    Choose File ...   File name...
    Cancel
  • Def69c998857099b7bc246389e6ad936?s=40&d=identicon
    Lucas Kanashiro @kanashiro
    Choose File ...   File name...
    Cancel