Commit ad6b59eeaa0f6b5e1da59fbc51cd11c366cc164c

Authored by Charles Oliveira
1 parent 1b21cac6

Detached super_archives from colab, searching for crashes now

Showing 45 changed files with 0 additions and 3373 deletions   Show diff stats
colab/super_archives/__init__.py
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 -  
2 -  
3 -default_app_config = 'colab.super_archives.apps.SuperArchivesConfig'  
colab/super_archives/admin.py
@@ -1,64 +0,0 @@ @@ -1,64 +0,0 @@
1 -  
2 -from django.contrib import admin  
3 -from .models import MailingList, Message, Thread, EmailAddress  
4 -  
5 -  
6 -class EmailAddressAdmin(admin.ModelAdmin):  
7 - search_fields = (  
8 - 'address',  
9 - 'user__username',  
10 - 'user__first_name',  
11 - 'user__last_name',  
12 - )  
13 -  
14 -  
15 -class MessageAdmin(admin.ModelAdmin):  
16 - list_filter = ('spam', 'thread__mailinglist', 'received_time', )  
17 - search_fields = (  
18 - 'id',  
19 - 'subject',  
20 - 'subject_clean',  
21 - 'body',  
22 - 'from_address__real_name',  
23 - 'from_address__address',  
24 - 'from_address__user__first_name',  
25 - 'from_address__user__last_name',  
26 - 'from_address__user__username',  
27 - )  
28 - readonly_fields = ('thread', 'from_address', 'mailinglist')  
29 -  
30 -  
31 -class ThreadAdmin(admin.ModelAdmin):  
32 - list_filter = ('spam', 'mailinglist', 'message__received_time',)  
33 - search_fields = (  
34 - 'id',  
35 - 'subject_token',  
36 - 'message__subject',  
37 - 'message__subject_clean',  
38 - 'message__from_address__real_name',  
39 - 'message__from_address__address',  
40 - 'message__from_address__user__first_name',  
41 - 'message__from_address__user__last_name',  
42 - 'message__from_address__user__username',  
43 - )  
44 -  
45 - readonly_fields = (  
46 - 'mailinglist',  
47 - 'subject_token',  
48 - 'latest_message',  
49 - 'score',  
50 - )  
51 -  
52 - fields = (  
53 - 'mailinglist',  
54 - 'subject_token',  
55 - 'latest_message',  
56 - 'score',  
57 - 'spam',  
58 - )  
59 -  
60 -  
61 -admin.site.register(MailingList)  
62 -admin.site.register(Thread, ThreadAdmin)  
63 -admin.site.register(Message, MessageAdmin)  
64 -admin.site.register(EmailAddress, EmailAddressAdmin)  
colab/super_archives/apps.py
@@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
1 -  
2 -from django.apps import AppConfig  
3 -  
4 -  
5 -class SuperArchivesConfig(AppConfig):  
6 - name = 'colab.super_archives'  
7 - verbose_name = 'Super Archives'  
8 -  
9 - def ready(self):  
10 - pass  
11 - # from . import signals  
colab/super_archives/context_processors.py
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -  
2 -from .models import Message  
3 -  
4 -  
5 -def mailarchive(request):  
6 - context = {}  
7 -  
8 - try:  
9 - context['last_imported_message'] = \  
10 - Message.objects.latest('received_time')  
11 - except Message.DoesNotExist:  
12 - pass  
13 -  
14 - return context  
colab/super_archives/fixtures/mailinglistdata.json
@@ -1,186 +0,0 @@ @@ -1,186 +0,0 @@
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": "John",  
14 - "is_superuser": false,  
15 - "last_login": "2015-02-26T17:56:13.378Z",  
16 - "verification_hash": null,  
17 - "role": null,  
18 - "email": "johndoe@example.com",  
19 - "username": "johndoe",  
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": "johndoe@example.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/locale/en/LC_MESSAGES/django.mo
No preview for this file type
colab/super_archives/locale/en/LC_MESSAGES/django.po
@@ -1,269 +0,0 @@ @@ -1,269 +0,0 @@
1 -# SOME DESCRIPTIVE TITLE.  
2 -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER  
3 -# This file is distributed under the same license as the PACKAGE package.  
4 -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.  
5 -#  
6 -#, fuzzy  
7 -msgid ""  
8 -msgstr ""  
9 -"Project-Id-Version: PACKAGE VERSION\n"  
10 -"Report-Msgid-Bugs-To: \n"  
11 -"POT-Creation-Date: 2015-09-01 13:15+0000\n"  
12 -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"  
13 -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"  
14 -"Language-Team: LANGUAGE <LL@li.org>\n"  
15 -"Language: \n"  
16 -"MIME-Version: 1.0\n"  
17 -"Content-Type: text/plain; charset=UTF-8\n"  
18 -"Content-Transfer-Encoding: 8bit\n"  
19 -  
20 -#: super_archives/management/commands/import_emails.py:213  
21 -msgid "[Colab] Warning - Email sent with a blank subject."  
22 -msgstr ""  
23 -  
24 -#: super_archives/models.py:77  
25 -#: super_archives/templates/message-preview.html:62  
26 -#: super_archives/templates/message-thread.html:4  
27 -msgid "Anonymous"  
28 -msgstr ""  
29 -  
30 -#: super_archives/models.py:133  
31 -msgid "Mailing List"  
32 -msgstr ""  
33 -  
34 -#: super_archives/models.py:134  
35 -msgid "The Mailing List where is the thread"  
36 -msgstr ""  
37 -  
38 -#: super_archives/models.py:137  
39 -msgid "Latest message"  
40 -msgstr ""  
41 -  
42 -#: super_archives/models.py:138  
43 -msgid "Latest message posted"  
44 -msgstr ""  
45 -  
46 -#: super_archives/models.py:139  
47 -msgid "Score"  
48 -msgstr ""  
49 -  
50 -#: super_archives/models.py:140  
51 -msgid "Thread score"  
52 -msgstr ""  
53 -  
54 -#: super_archives/models.py:153  
55 -msgid "Thread"  
56 -msgstr ""  
57 -  
58 -#: super_archives/models.py:154  
59 -msgid "Threads"  
60 -msgstr ""  
61 -  
62 -#: super_archives/models.py:267  
63 -msgid "Subject"  
64 -msgstr ""  
65 -  
66 -#: super_archives/models.py:268  
67 -msgid "Please enter a message subject"  
68 -msgstr ""  
69 -  
70 -#: super_archives/models.py:271  
71 -msgid "Message body"  
72 -msgstr ""  
73 -  
74 -#: super_archives/models.py:272  
75 -msgid "Please enter a message body"  
76 -msgstr ""  
77 -  
78 -#: super_archives/models.py:282  
79 -msgid "Message"  
80 -msgstr ""  
81 -  
82 -#: super_archives/models.py:283  
83 -msgid "Messages"  
84 -msgstr ""  
85 -  
86 -#: super_archives/templates/message-preview.html:42  
87 -#: super_archives/templates/message-preview.html:62  
88 -msgid "by"  
89 -msgstr ""  
90 -  
91 -#: super_archives/templates/message-preview.html:65  
92 -#: super_archives/templates/message-thread.html:161  
93 -msgid "ago"  
94 -msgstr ""  
95 -  
96 -#: super_archives/templates/message-thread.html:35  
97 -msgid "You must login before voting."  
98 -msgstr ""  
99 -  
100 -#: super_archives/templates/message-thread.html:132  
101 -msgid "Order by"  
102 -msgstr ""  
103 -  
104 -#: super_archives/templates/message-thread.html:136  
105 -msgid "Votes"  
106 -msgstr ""  
107 -  
108 -#: super_archives/templates/message-thread.html:140  
109 -msgid "Date"  
110 -msgstr ""  
111 -  
112 -#: super_archives/templates/message-thread.html:145  
113 -msgid "Related:"  
114 -msgstr ""  
115 -  
116 -#: super_archives/templates/message-thread.html:156  
117 -msgid "Statistics:"  
118 -msgstr ""  
119 -  
120 -#: super_archives/templates/message-thread.html:160  
121 -msgid "started at"  
122 -msgstr ""  
123 -  
124 -#: super_archives/templates/message-thread.html:166  
125 -msgid "viewed"  
126 -msgstr ""  
127 -  
128 -#: super_archives/templates/message-thread.html:167  
129 -#: super_archives/templates/message-thread.html:172  
130 -#: super_archives/templates/message-thread.html:177  
131 -msgid "times"  
132 -msgstr ""  
133 -  
134 -#: super_archives/templates/message-thread.html:171  
135 -msgid "answered"  
136 -msgstr ""  
137 -  
138 -#: super_archives/templates/message-thread.html:176  
139 -msgid "voted"  
140 -msgstr ""  
141 -  
142 -#: super_archives/templates/message-thread.html:182  
143 -msgid "Tags:"  
144 -msgstr ""  
145 -  
146 -#: super_archives/templates/superarchives/emails/email_blank_subject.txt:2  
147 -msgid "Hello"  
148 -msgstr ""  
149 -  
150 -#: super_archives/templates/superarchives/emails/email_blank_subject.txt:3  
151 -#, python-format  
152 -msgid ""  
153 -"\n"  
154 -"You've sent an email to %(mailinglist)s with a blank subject and the "  
155 -"following content:\n"  
156 -"\n"  
157 -"\"%(body)s\"\n"  
158 -"\n"  
159 -"Please, fill the subject in every email you send it.\n"  
160 -"\n"  
161 -"Thank you.\n"  
162 -msgstr ""  
163 -  
164 -#: super_archives/templates/superarchives/emails/email_verification.txt:2  
165 -#, python-format  
166 -msgid ""  
167 -"Hey, we want to verify that you are indeed \"%(fullname)s (%(username)s)\". "  
168 -"If that's the case, please follow the link below:"  
169 -msgstr ""  
170 -  
171 -#: super_archives/templates/superarchives/emails/email_verification.txt:6  
172 -#, python-format  
173 -msgid ""  
174 -"If you're not %(username)s or didn't request verification you can ignore "  
175 -"this email."  
176 -msgstr ""  
177 -  
178 -#: super_archives/templates/superarchives/includes/message.html:17  
179 -#: super_archives/templates/superarchives/includes/message.html:18  
180 -msgid "Link to this message"  
181 -msgstr ""  
182 -  
183 -#: super_archives/templates/superarchives/includes/message.html:46  
184 -msgid "Reply"  
185 -msgstr ""  
186 -  
187 -#: super_archives/templates/superarchives/includes/message.html:63  
188 -msgid "Send a message"  
189 -msgstr ""  
190 -  
191 -#: super_archives/templates/superarchives/includes/message.html:66  
192 -msgid ""  
193 -"After sending a message it will take few minutes before it shows up in here. "  
194 -"Why don't you grab a coffee?"  
195 -msgstr ""  
196 -  
197 -#: super_archives/templates/superarchives/includes/message.html:69  
198 -msgid "Send"  
199 -msgstr ""  
200 -  
201 -#: super_archives/templates/superarchives/thread-dashboard.html:4  
202 -#: super_archives/templates/superarchives/thread-dashboard.html:7  
203 -msgid "Groups"  
204 -msgstr ""  
205 -  
206 -#: super_archives/templates/superarchives/thread-dashboard.html:14  
207 -#, python-format  
208 -msgid "%(number_of_users)s members"  
209 -msgstr ""  
210 -  
211 -#: super_archives/templates/superarchives/thread-dashboard.html:20  
212 -msgid "latest"  
213 -msgstr ""  
214 -  
215 -#: super_archives/templates/superarchives/thread-dashboard.html:28  
216 -#: super_archives/templates/superarchives/thread-dashboard.html:42  
217 -msgid "more..."  
218 -msgstr ""  
219 -  
220 -#: super_archives/templates/superarchives/thread-dashboard.html:34  
221 -msgid "most relevant"  
222 -msgstr ""  
223 -  
224 -#: super_archives/utils/email.py:14  
225 -msgid "Please verify your email "  
226 -msgstr ""  
227 -  
228 -#: super_archives/views.py:112  
229 -msgid "Error trying to connect to Mailman API"  
230 -msgstr ""  
231 -  
232 -#: super_archives/views.py:115  
233 -msgid "Timeout trying to connect to Mailman API"  
234 -msgstr ""  
235 -  
236 -#: super_archives/views.py:119  
237 -msgid ""  
238 -"Your message was sent to this topic. It may take some minutes before it's "  
239 -"delivered by email to the group. Why don't you breath some fresh air in the "  
240 -"meanwhile?"  
241 -msgstr ""  
242 -  
243 -#: super_archives/views.py:128  
244 -msgid "You cannot send an empty email"  
245 -msgstr ""  
246 -  
247 -#: super_archives/views.py:130  
248 -msgid "Mailing list does not exist"  
249 -msgstr ""  
250 -  
251 -#: super_archives/views.py:133  
252 -msgid "Unknown error trying to connect to Mailman API"  
253 -msgstr ""  
254 -  
255 -#: super_archives/views.py:188  
256 -msgid ""  
257 -"The email address you are trying to verify either has already been verified "  
258 -"or does not exist."  
259 -msgstr ""  
260 -  
261 -#: super_archives/views.py:199  
262 -msgid ""  
263 -"The email address you are trying to verify is already an active email "  
264 -"address."  
265 -msgstr ""  
266 -  
267 -#: super_archives/views.py:213  
268 -msgid "Email address verified!"  
269 -msgstr ""  
colab/super_archives/locale/es/LC_MESSAGES/django.mo
No preview for this file type
colab/super_archives/locale/es/LC_MESSAGES/django.po
@@ -1,224 +0,0 @@ @@ -1,224 +0,0 @@
1 -# colab translation.  
2 -# Copyright (C) 2012, 2013 colab  
3 -# This file is distributed under the same license as the colab package.  
4 -# Leonardo J. Caballero G. <leonardocaballero@gmail.com>, 2012, 2013.  
5 -msgid ""  
6 -msgstr ""  
7 -"Project-Id-Version: colab\n"  
8 -"Report-Msgid-Bugs-To: \n"  
9 -"POT-Creation-Date: 2013-07-18 21:53-0300\n"  
10 -"PO-Revision-Date: 2013-10-14 19:16-0430\n"  
11 -"Last-Translator: Leonardo J. Caballero G. <leonardocaballero@gmail.com>\n"  
12 -"Language-Team: ES <LL@li.org>\n"  
13 -"Language: es\n"  
14 -"MIME-Version: 1.0\n"  
15 -"Content-Type: text/plain; charset=UTF-8\n"  
16 -"Content-Transfer-Encoding: 8bit\n"  
17 -"Plural-Forms: nplurals=2; plural=(n != 1);\n"  
18 -"X-Generator: Virtaal 0.7.1\n"  
19 -  
20 -#: forms.py:18  
21 -msgid "Name"  
22 -msgstr "Nombre"  
23 -  
24 -#: forms.py:19  
25 -msgid "Last name"  
26 -msgstr "Apellido"  
27 -  
28 -#: forms.py:23  
29 -msgid "Institution"  
30 -msgstr "Institución"  
31 -  
32 -#: forms.py:25  
33 -msgid "Role"  
34 -msgstr "Rol"  
35 -  
36 -#: forms.py:26  
37 -msgid "Twitter"  
38 -msgstr "Cuenta Twitter"  
39 -  
40 -#: forms.py:27  
41 -msgid "Facebook"  
42 -msgstr "Cuenta Facebook"  
43 -  
44 -#: forms.py:28  
45 -msgid "Google Talk"  
46 -msgstr "Cuenta Google Talk"  
47 -  
48 -#: forms.py:29  
49 -msgid "Personal Website/Blog"  
50 -msgstr "Sitio Web / Blog personal"  
51 -  
52 -#: models.py:66  
53 -msgid "User Profile"  
54 -msgstr "Perfil de usuario"  
55 -  
56 -#: models.py:67  
57 -msgid "Users Profiles"  
58 -msgstr "Perfiles de usuarios"  
59 -  
60 -#: models.py:100  
61 -msgid "Mailing List"  
62 -msgstr "Lista de correo electrónico"  
63 -  
64 -#: models.py:101  
65 -msgid "The Mailing List where is the thread"  
66 -msgstr "La lista de correo electrónico donde esta el hilo de discusión"  
67 -  
68 -#: models.py:104  
69 -msgid "Latest message"  
70 -msgstr "Últimos mensaje"  
71 -  
72 -#: models.py:105  
73 -msgid "Latest message posted"  
74 -msgstr "Últimos mensajes enviados"  
75 -  
76 -#: models.py:106  
77 -msgid "Score"  
78 -msgstr "Puntuación"  
79 -  
80 -#: models.py:106  
81 -msgid "Thread score"  
82 -msgstr "Puntuación de mensaje"  
83 -  
84 -#: models.py:113  
85 -msgid "Thread"  
86 -msgstr "Hilo"  
87 -  
88 -#: models.py:114  
89 -msgid "Threads"  
90 -msgstr "Hilos"  
91 -  
92 -#: models.py:196  
93 -msgid "Subject"  
94 -msgstr "Asunto"  
95 -  
96 -#: models.py:197  
97 -msgid "Please enter a message subject"  
98 -msgstr "Por favor, ingrese un asunto del mensaje"  
99 -  
100 -#: models.py:200  
101 -msgid "Message body"  
102 -msgstr "Cuerpo de mensaje"  
103 -  
104 -#: models.py:201  
105 -msgid "Please enter a message body"  
106 -msgstr "Por favor, ingrese un cuerpo de contenido del mensaje"  
107 -  
108 -#: models.py:210  
109 -msgid "Message"  
110 -msgstr "Mensaje"  
111 -  
112 -#: models.py:211  
113 -msgid "Messages"  
114 -msgstr "Mensajes"  
115 -  
116 -#: templates/message-list.html:6  
117 -msgid "Discussions"  
118 -msgstr "Discusiones"  
119 -  
120 -#: templates/message-list.html:10  
121 -msgid "Filters"  
122 -msgstr "Filtros"  
123 -  
124 -#: templates/message-list.html:12  
125 -msgid "Sort by"  
126 -msgstr "Ordenado por"  
127 -  
128 -#: templates/message-list.html:14 templates/message-list.html.py:17  
129 -#: templates/message-list.html:27  
130 -msgid "Remove filter"  
131 -msgstr "Remover filtro"  
132 -  
133 -#: templates/message-list.html:16  
134 -msgid "Relevance"  
135 -msgstr "Relevancia"  
136 -  
137 -#: templates/message-list.html:19  
138 -msgid "Recent activity"  
139 -msgstr "Actividad reciente"  
140 -  
141 -#: templates/message-list.html:24  
142 -msgid "Lists"  
143 -msgstr "Listas"  
144 -  
145 -#: templates/message-list.html:41  
146 -msgid "No discussion found"  
147 -msgstr "Sin discusión encontrada"  
148 -  
149 -#: templates/message-list.html:51  
150 -msgid "Previous"  
151 -msgstr "Anterior"  
152 -  
153 -#: templates/message-list.html:55  
154 -msgid "Page"  
155 -msgstr "Página"  
156 -  
157 -#: templates/message-list.html:55  
158 -msgid "of"  
159 -msgstr "de"  
160 -  
161 -#: templates/message-list.html:59  
162 -msgid "Next"  
163 -msgstr "Próxima"  
164 -  
165 -#: templates/message-preview.html:35  
166 -msgid "by"  
167 -msgstr "por"  
168 -  
169 -#: templates/message-preview.html:41  
170 -msgid "anonymous"  
171 -msgstr "anónimo"  
172 -  
173 -#: templates/message-preview.html:47 templates/message-thread.html:59  
174 -msgid "ago"  
175 -msgstr "atrás"  
176 -  
177 -#: templates/message-thread.html:19  
178 -msgid "Anonymous"  
179 -msgstr "Anónimo"  
180 -  
181 -#: templates/message-thread.html:27  
182 -msgid "Vote"  
183 -msgstr "Votar"  
184 -  
185 -#: templates/message-thread.html:31  
186 -msgid "Remove votes"  
187 -msgstr "Remover votos"  
188 -  
189 -#: templates/message-thread.html:47  
190 -msgid "Order by"  
191 -msgstr "Ordenar por"  
192 -  
193 -#: templates/message-thread.html:49  
194 -msgid "Votes"  
195 -msgstr "Votos"  
196 -  
197 -#: templates/message-thread.html:50  
198 -msgid "Date"  
199 -msgstr "Fecha"  
200 -  
201 -#: templates/message-thread.html:55  
202 -msgid "Statistics:"  
203 -msgstr "Estadísticas:"  
204 -  
205 -#: templates/message-thread.html:58  
206 -msgid "started at"  
207 -msgstr "iniciada"  
208 -  
209 -#: templates/message-thread.html:61  
210 -msgid "viewed"  
211 -msgstr "vistos"  
212 -  
213 -#: templates/message-thread.html:62 templates/message-thread.html.py:65  
214 -#: templates/message-thread.html:68  
215 -msgid "times"  
216 -msgstr "veces"  
217 -  
218 -#: templates/message-thread.html:64  
219 -msgid "answered"  
220 -msgstr "respondido"  
221 -  
222 -#: templates/message-thread.html:67  
223 -msgid "voted"  
224 -msgstr "votado"  
colab/super_archives/locale/pt_BR/LC_MESSAGES/django.mo
No preview for this file type
colab/super_archives/locale/pt_BR/LC_MESSAGES/django.po
@@ -1,291 +0,0 @@ @@ -1,291 +0,0 @@
1 -# SOME DESCRIPTIVE TITLE.  
2 -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER  
3 -# This file is distributed under the same license as the PACKAGE package.  
4 -#  
5 -# Translators:  
6 -# Sergio Oliveira <seocam@seocam.com>, 2014  
7 -msgid ""  
8 -msgstr ""  
9 -"Project-Id-Version: colab\n"  
10 -"Report-Msgid-Bugs-To: \n"  
11 -"POT-Creation-Date: 2015-09-01 13:15+0000\n"  
12 -"PO-Revision-Date: 2014-11-21 17:38+0000\n"  
13 -"Last-Translator: Sergio Oliveira <seocam@seocam.com>\n"  
14 -"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/"  
15 -"colab/language/pt_BR/)\n"  
16 -"Language: pt_BR\n"  
17 -"MIME-Version: 1.0\n"  
18 -"Content-Type: text/plain; charset=UTF-8\n"  
19 -"Content-Transfer-Encoding: 8bit\n"  
20 -"Plural-Forms: nplurals=2; plural=(n > 1);\n"  
21 -  
22 -#: super_archives/management/commands/import_emails.py:213  
23 -msgid "[Colab] Warning - Email sent with a blank subject."  
24 -msgstr "[Colab] Atenção - Email enviado sem assunto."  
25 -  
26 -#: super_archives/models.py:77  
27 -#: super_archives/templates/message-preview.html:62  
28 -#: super_archives/templates/message-thread.html:4  
29 -msgid "Anonymous"  
30 -msgstr "Anônimo"  
31 -  
32 -#: super_archives/models.py:133  
33 -msgid "Mailing List"  
34 -msgstr "Lista de discussão"  
35 -  
36 -#: super_archives/models.py:134  
37 -msgid "The Mailing List where is the thread"  
38 -msgstr "Lista de emails da discussão"  
39 -  
40 -#: super_archives/models.py:137  
41 -msgid "Latest message"  
42 -msgstr "Última mensagem"  
43 -  
44 -#: super_archives/models.py:138  
45 -msgid "Latest message posted"  
46 -msgstr "Última mensagem enviada"  
47 -  
48 -#: super_archives/models.py:139  
49 -msgid "Score"  
50 -msgstr "Pontuação"  
51 -  
52 -#: super_archives/models.py:140  
53 -msgid "Thread score"  
54 -msgstr "Pontuação da discussão"  
55 -  
56 -#: super_archives/models.py:153  
57 -msgid "Thread"  
58 -msgstr "Discussão"  
59 -  
60 -#: super_archives/models.py:154  
61 -msgid "Threads"  
62 -msgstr "Discussões"  
63 -  
64 -#: super_archives/models.py:267  
65 -msgid "Subject"  
66 -msgstr "Assuntos"  
67 -  
68 -#: super_archives/models.py:268  
69 -msgid "Please enter a message subject"  
70 -msgstr "Por favor digite um assunto de email"  
71 -  
72 -#: super_archives/models.py:271  
73 -msgid "Message body"  
74 -msgstr "Corpo do email"  
75 -  
76 -#: super_archives/models.py:272  
77 -msgid "Please enter a message body"  
78 -msgstr "Por favor digite o corpo do email"  
79 -  
80 -#: super_archives/models.py:282  
81 -msgid "Message"  
82 -msgstr "Mensagem"  
83 -  
84 -#: super_archives/models.py:283  
85 -msgid "Messages"  
86 -msgstr "Mensagens"  
87 -  
88 -#: super_archives/templates/message-preview.html:42  
89 -#: super_archives/templates/message-preview.html:62  
90 -msgid "by"  
91 -msgstr "por"  
92 -  
93 -#: super_archives/templates/message-preview.html:65  
94 -#: super_archives/templates/message-thread.html:161  
95 -msgid "ago"  
96 -msgstr "atrás"  
97 -  
98 -#: super_archives/templates/message-thread.html:35  
99 -msgid "You must login before voting."  
100 -msgstr "Você deve logar antes de votar."  
101 -  
102 -#: super_archives/templates/message-thread.html:132  
103 -msgid "Order by"  
104 -msgstr "Ordenar por"  
105 -  
106 -#: super_archives/templates/message-thread.html:136  
107 -msgid "Votes"  
108 -msgstr "Votos"  
109 -  
110 -#: super_archives/templates/message-thread.html:140  
111 -msgid "Date"  
112 -msgstr "Data"  
113 -  
114 -#: super_archives/templates/message-thread.html:145  
115 -msgid "Related:"  
116 -msgstr "Relacionado:"  
117 -  
118 -#: super_archives/templates/message-thread.html:156  
119 -msgid "Statistics:"  
120 -msgstr "Estatísticas:"  
121 -  
122 -#: super_archives/templates/message-thread.html:160  
123 -msgid "started at"  
124 -msgstr "iniciada em"  
125 -  
126 -#: super_archives/templates/message-thread.html:166  
127 -msgid "viewed"  
128 -msgstr "vizualizada"  
129 -  
130 -#: super_archives/templates/message-thread.html:167  
131 -#: super_archives/templates/message-thread.html:172  
132 -#: super_archives/templates/message-thread.html:177  
133 -msgid "times"  
134 -msgstr "vezes"  
135 -  
136 -#: super_archives/templates/message-thread.html:171  
137 -msgid "answered"  
138 -msgstr "respondida"  
139 -  
140 -#: super_archives/templates/message-thread.html:176  
141 -msgid "voted"  
142 -msgstr "votada"  
143 -  
144 -#: super_archives/templates/message-thread.html:182  
145 -msgid "Tags:"  
146 -msgstr "Tags:"  
147 -  
148 -#: super_archives/templates/superarchives/emails/email_blank_subject.txt:2  
149 -msgid "Hello"  
150 -msgstr "Olá"  
151 -  
152 -#: super_archives/templates/superarchives/emails/email_blank_subject.txt:3  
153 -#, python-format  
154 -msgid ""  
155 -"\n"  
156 -"You've sent an email to %(mailinglist)s with a blank subject and the "  
157 -"following content:\n"  
158 -"\n"  
159 -"\"%(body)s\"\n"  
160 -"\n"  
161 -"Please, fill the subject in every email you send it.\n"  
162 -"\n"  
163 -"Thank you.\n"  
164 -msgstr ""  
165 -"\n"  
166 -"Você enviou um e-mail para a lista %(mailinglist)s com assunto em branco e "  
167 -"com o seguinte conteúdo:\n"  
168 -"\n"  
169 -"\"%(body)s\"\n"  
170 -"\n"  
171 -"Por favor, preencha o assunto de seus e-mails antes de enviá-los.\n"  
172 -"\n"  
173 -"Obrigado.\n"  
174 -  
175 -#: super_archives/templates/superarchives/emails/email_verification.txt:2  
176 -#, python-format  
177 -msgid ""  
178 -"Hey, we want to verify that you are indeed \"%(fullname)s (%(username)s)\". "  
179 -"If that's the case, please follow the link below:"  
180 -msgstr ""  
181 -"Ei, nós queremos verificar que você é realmente \"%(fullname)s "  
182 -"(%(username)s)\". Se este for o caso, por favor clique no link abaixo:"  
183 -  
184 -#: super_archives/templates/superarchives/emails/email_verification.txt:6  
185 -#, python-format  
186 -msgid ""  
187 -"If you're not %(username)s or didn't request verification you can ignore "  
188 -"this email."  
189 -msgstr ""  
190 -"Se você não é %(username)s ou não solicitou verificação apenas ignore este "  
191 -"email."  
192 -  
193 -#: super_archives/templates/superarchives/includes/message.html:17  
194 -#: super_archives/templates/superarchives/includes/message.html:18  
195 -msgid "Link to this message"  
196 -msgstr "Link para esta mensagem"  
197 -  
198 -#: super_archives/templates/superarchives/includes/message.html:46  
199 -msgid "Reply"  
200 -msgstr "Responder"  
201 -  
202 -#: super_archives/templates/superarchives/includes/message.html:63  
203 -msgid "Send a message"  
204 -msgstr "Enviar mensagem"  
205 -  
206 -#: super_archives/templates/superarchives/includes/message.html:66  
207 -msgid ""  
208 -"After sending a message it will take few minutes before it shows up in here. "  
209 -"Why don't you grab a coffee?"  
210 -msgstr ""  
211 -"Após enviar uma mensagem pode levar alguns minutos até que ela seja exibida "  
212 -"aqui. Por que você não busca um café enquanto isso?"  
213 -  
214 -#: super_archives/templates/superarchives/includes/message.html:69  
215 -msgid "Send"  
216 -msgstr "Enviar"  
217 -  
218 -#: super_archives/templates/superarchives/thread-dashboard.html:4  
219 -#: super_archives/templates/superarchives/thread-dashboard.html:7  
220 -msgid "Groups"  
221 -msgstr "Grupos"  
222 -  
223 -#: super_archives/templates/superarchives/thread-dashboard.html:14  
224 -#, python-format  
225 -msgid "%(number_of_users)s members"  
226 -msgstr "%(number_of_users)s membros"  
227 -  
228 -#: super_archives/templates/superarchives/thread-dashboard.html:20  
229 -msgid "latest"  
230 -msgstr "última"  
231 -  
232 -#: super_archives/templates/superarchives/thread-dashboard.html:28  
233 -#: super_archives/templates/superarchives/thread-dashboard.html:42  
234 -msgid "more..."  
235 -msgstr "mais..."  
236 -  
237 -#: super_archives/templates/superarchives/thread-dashboard.html:34  
238 -msgid "most relevant"  
239 -msgstr "Mais relevante"  
240 -  
241 -#: super_archives/utils/email.py:14  
242 -msgid "Please verify your email "  
243 -msgstr "Por favor verifique seu email"  
244 -  
245 -#: super_archives/views.py:112  
246 -msgid "Error trying to connect to Mailman API"  
247 -msgstr "Erro tentando conectar à API do Mailman"  
248 -  
249 -#: super_archives/views.py:115  
250 -msgid "Timeout trying to connect to Mailman API"  
251 -msgstr "Tempo excedido tentando conectar à API do Mailman"  
252 -  
253 -#: super_archives/views.py:119  
254 -msgid ""  
255 -"Your message was sent to this topic. It may take some minutes before it's "  
256 -"delivered by email to the group. Why don't you breath some fresh air in the "  
257 -"meanwhile?"  
258 -msgstr ""  
259 -"Sua mensagem foi enviada para esta discussão. Pode ser que leve alguns "  
260 -"minutos até que ela seja enviada por email para o grupo. Por que você não "  
261 -"respira um ar fresco enquanto isso?"  
262 -  
263 -#: super_archives/views.py:128  
264 -msgid "You cannot send an empty email"  
265 -msgstr "Você não pode enviar um email vazio"  
266 -  
267 -#: super_archives/views.py:130  
268 -msgid "Mailing list does not exist"  
269 -msgstr "Lista de email inexistente"  
270 -  
271 -#: super_archives/views.py:133  
272 -msgid "Unknown error trying to connect to Mailman API"  
273 -msgstr "Erro desconhecido tentando conectar à API do Mailman"  
274 -  
275 -#: super_archives/views.py:188  
276 -msgid ""  
277 -"The email address you are trying to verify either has already been verified "  
278 -"or does not exist."  
279 -msgstr ""  
280 -"O email que você está tentando verificar não existe ou já foi verificado "  
281 -"anteriormente. "  
282 -  
283 -#: super_archives/views.py:199  
284 -msgid ""  
285 -"The email address you are trying to verify is already an active email "  
286 -"address."  
287 -msgstr "O email que você está tentando verificar já é um email ativo."  
288 -  
289 -#: super_archives/views.py:213  
290 -msgid "Email address verified!"  
291 -msgstr "Endereço de email verificado!"  
colab/super_archives/management/__init__.py
colab/super_archives/management/commands/__init__.py
colab/super_archives/management/commands/import_emails.py
@@ -1,301 +0,0 @@ @@ -1,301 +0,0 @@
1 -#!/usr/bin/env python  
2 -# -*- encoding: utf-8 -*-  
3 -  
4 -"""Import emails from a mailman storage to the django database."""  
5 -  
6 -import os  
7 -import re  
8 -import sys  
9 -import mailbox  
10 -import logging  
11 -from optparse import make_option  
12 -  
13 -from django.conf import settings  
14 -from django.db import transaction  
15 -from django.template.defaultfilters import slugify  
16 -from django.core.management.base import BaseCommand, CommandError  
17 -from django.template.loader import render_to_string  
18 -from django.utils.translation import ugettext as _  
19 -  
20 -from colab.super_archives.utils.email import colab_send_email  
21 -from colab.super_archives.models import (MailingList, Message,  
22 - Thread, EmailAddress)  
23 -from message import Message as CustomMessage  
24 -  
25 -  
26 -class Command(BaseCommand, object):  
27 - """Get emails from mailman archives and import them in the django db. """  
28 -  
29 - help = __doc__  
30 -  
31 - RE_SUBJECT_CLEAN = re.compile('((re|res|fw|fwd|en|enc):)|\[.*?\]',  
32 - re.IGNORECASE)  
33 - THREAD_CACHE = {}  
34 - EMAIL_ADDR_CACHE = {}  
35 -  
36 - lock_file = settings.SUPER_ARCHIVES_LOCK_FILE  
37 -  
38 - # A new command line option to get the dump file to parse.  
39 - option_list = BaseCommand.option_list + (  
40 - make_option(  
41 - '--archives_path',  
42 - dest='archives_path',  
43 - help=('Path of email archives to be imported. '  
44 - '(default: {})').format(settings.SUPER_ARCHIVES_PATH),  
45 - default=settings.SUPER_ARCHIVES_PATH),  
46 -  
47 - make_option(  
48 - '--exclude-list',  
49 - dest='exclude_lists',  
50 - help=("Mailing list that won't be imported. It can be used many"  
51 - "times for more than one list."),  
52 - action='append',  
53 - default=settings.SUPER_ARCHIVES_EXCLUDE),  
54 -  
55 - make_option(  
56 - '--all',  
57 - dest='all',  
58 - help='Import all messages (default: False)',  
59 - action="store_true",  
60 - default=False),  
61 - )  
62 -  
63 - def __init__(self, *args, **kwargs):  
64 - super(Command, self).__init__(*args, **kwargs)  
65 -  
66 - def log(self, msg, error=False):  
67 - """Log message helper."""  
68 - output = self.stdout  
69 - if error:  
70 - output = self.stderr  
71 -  
72 - output.write(msg)  
73 - output.write('\n')  
74 -  
75 - def parse_emails(self, email_filename, index=0):  
76 - """Generator function that parse and extract emails from the file  
77 - `email_filename` starting from the position `index`.  
78 -  
79 - Yield: An instance of `mailbox.mboxMessage` for each email in the  
80 - file.  
81 -  
82 - """  
83 - self.log("Parsing email dump: %s." % email_filename)  
84 - mbox = mailbox.mbox(email_filename, factory=CustomMessage)  
85 -  
86 - # Get each email from mbox file  
87 - #  
88 - # The following implementation was used because the object  
89 - # mbox does not support slicing. Converting the object to a  
90 - # tuple (as represented in the code down here) was a valid  
91 - # option but its performance was too poor.  
92 - #  
93 - # for message in tuple(mbox)[index:]:  
94 - # yield message  
95 - #  
96 - key = index  
97 - while key in mbox:  
98 - key += 1  
99 - yield key-1, mbox[key-1]  
100 -  
101 - def get_emails(self, mailinglist_dir, all, exclude_lists):  
102 - """Generator function that get the emails from each mailing  
103 - list dump dirctory. If `all` is set to True all the emails in the  
104 - mbox will be imported if not it will just resume from the last  
105 - message previously imported. The lists set in `exclude_lists`  
106 - won't be imported.  
107 -  
108 - Yield: A tuple in the form: (mailing list name, email message).  
109 -  
110 - """  
111 - self.log("Getting emails dumps from: %s" % mailinglist_dir)  
112 -  
113 - # Get the list of directories ending with .mbox  
114 - mailing_lists_mboxes = (mbox for mbox in os.listdir(mailinglist_dir)  
115 - if mbox.endswith('.mbox'))  
116 -  
117 - # Get messages from each mbox  
118 - for mbox in mailing_lists_mboxes:  
119 - mbox_path = os.path.join(mailinglist_dir, mbox, mbox)  
120 - mailinglist_name = mbox.split('.')[0]  
121 -  
122 - # Check if the mailinglist is set not to be imported  
123 - if exclude_lists and mailinglist_name in exclude_lists:  
124 - continue  
125 -  
126 - # Find the index of the last imported message  
127 - if all:  
128 - n_msgs = 0  
129 - else:  
130 - try:  
131 - mailinglist = MailingList.objects.get(  
132 - name=mailinglist_name  
133 - )  
134 - n_msgs = mailinglist.last_imported_index  
135 - except MailingList.DoesNotExist:  
136 - n_msgs = 0  
137 -  
138 - for index, msg in self.parse_emails(mbox_path, n_msgs):  
139 - yield mailinglist_name, msg, index  
140 -  
141 - def get_thread(self, email, mailinglist):  
142 - """Group messages by thread looking for similar subjects"""  
143 -  
144 - subject_slug = slugify(email.subject_clean)  
145 - thread = self.THREAD_CACHE.get(subject_slug, {}).get(mailinglist.id)  
146 - if thread is None:  
147 - thread = Thread.all_objects.get_or_create(  
148 - mailinglist=mailinglist,  
149 - subject_token=subject_slug  
150 - )[0]  
151 -  
152 - if self.THREAD_CACHE.get(subject_slug) is None:  
153 - self.THREAD_CACHE[subject_slug] = dict()  
154 - self.THREAD_CACHE[subject_slug][mailinglist.id] = thread  
155 -  
156 - thread.latest_message = email  
157 - thread.update_keywords()  
158 - thread.save()  
159 - return thread  
160 -  
161 - def save_email(self, list_name, email_msg, index):  
162 - """Save email message into the database."""  
163 -  
164 - msg_id = email_msg.get('Message-ID')  
165 - if not msg_id:  
166 - return  
167 -  
168 - # Update last imported message into the DB  
169 - mailinglist, created = MailingList.objects.get_or_create(  
170 - name=list_name  
171 - )  
172 - mailinglist.last_imported_index = index  
173 -  
174 - if created:  
175 - # if the mailinglist is newly created it's sure that the message  
176 - # is not in the DB yet.  
177 - self.create_email(mailinglist, email_msg)  
178 -  
179 - else:  
180 - # If the message is already at the database don't do anything  
181 - try:  
182 - Message.all_objects.get(  
183 - message_id=msg_id,  
184 - thread__mailinglist=mailinglist  
185 - )  
186 -  
187 - except Message.DoesNotExist:  
188 - self.create_email(mailinglist, email_msg)  
189 -  
190 - mailinglist.save()  
191 -  
192 - def create_email(self, mailinglist, email_msg):  
193 - received_time = email_msg.get_received_datetime()  
194 - if not received_time:  
195 - return  
196 -  
197 - real_name, from_ = email_msg.get_from_addr()  
198 -  
199 - email_addr = self.EMAIL_ADDR_CACHE.get(from_)  
200 - if email_addr is None:  
201 - email_addr = EmailAddress.objects.get_or_create(  
202 - address=from_)[0]  
203 - self.EMAIL_ADDR_CACHE[from_] = email_addr  
204 -  
205 - if not email_addr.real_name and real_name:  
206 - email_addr.real_name = real_name[:64]  
207 - email_addr.save()  
208 -  
209 - subject = email_msg.get_subject()  
210 - if not subject:  
211 - colab_send_email(  
212 - subject=_(  
213 - u"[Colab] Warning - Email sent with a blank subject."  
214 - ),  
215 - message=render_to_string(  
216 - u'superarchives/emails/email_blank_subject.txt',  
217 - {  
218 - 'email_body': email_msg.get_body(),  
219 - 'mailinglist': mailinglist.name,  
220 - 'user': email_addr.get_full_name()  
221 - }  
222 - ),  
223 - to=email_addr.address  
224 - )  
225 -  
226 - email = Message.all_objects.create(  
227 - message_id=email_msg.get('Message-ID'),  
228 - from_address=email_addr,  
229 - subject=subject,  
230 - subject_clean=self.RE_SUBJECT_CLEAN.sub('', subject).strip(),  
231 - body=email_msg.get_body(),  
232 - received_time=email_msg.get_received_datetime(),  
233 - )  
234 - email.thread = self.get_thread(email, mailinglist)  
235 - email.save()  
236 - email.update_blocks()  
237 -  
238 - @transaction.commit_manually  
239 - def import_emails(self, archives_path, all, exclude_lists=None):  
240 - """Get emails from the filesystem from the `archives_path`  
241 - and store them into the database. If `all` is set to True all  
242 - the filesystem storage will be imported otherwise the  
243 - importation will resume from the last message previously  
244 - imported. The lists set in `exclude_lists` won't be imported.  
245 -  
246 - """  
247 -  
248 - count = 0  
249 - email_generator = self.get_emails(archives_path, all, exclude_lists)  
250 - for mailinglist_name, msg, index in email_generator:  
251 - try:  
252 - self.save_email(mailinglist_name, msg, index)  
253 - except:  
254 - # This anti-pattern is needed to avoid the transations to  
255 - # get stuck in case of errors.  
256 - transaction.rollback()  
257 - raise  
258 -  
259 - count += 1  
260 - if count % 1000 == 0:  
261 - transaction.commit()  
262 -  
263 - transaction.commit()  
264 -  
265 - def handle(self, *args, **options):  
266 - """Main command method."""  
267 -  
268 - # Already running, so quit  
269 - if os.path.exists(self.lock_file):  
270 - self.log(("This script is already running. "  
271 - "(If your are sure it's not please "  
272 - "delete the lock file in {}')").format(self.lock_file))  
273 - sys.exit(0)  
274 -  
275 - if not os.path.exists(os.path.dirname(self.lock_file)):  
276 - os.mkdir(os.path.dirname(self.lock_file), 0755)  
277 -  
278 - archives_path = options.get('archives_path')  
279 - self.log('Using archives_path `%s`' % settings.SUPER_ARCHIVES_PATH)  
280 -  
281 - if not os.path.exists(archives_path):  
282 - msg = 'archives_path ({}) does not exist'.format(archives_path)  
283 - raise CommandError(msg)  
284 - run_lock = file(self.lock_file, 'w')  
285 - run_lock.close()  
286 -  
287 - try:  
288 - self.import_emails(  
289 - archives_path,  
290 - options.get('all'),  
291 - options.get('exclude_lists'),  
292 - )  
293 - except Exception as e:  
294 - logging.exception(e)  
295 - raise  
296 - finally:  
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
@@ -1,103 +0,0 @@ @@ -1,103 +0,0 @@
1 -  
2 -import re  
3 -import pytz  
4 -import email  
5 -import codecs  
6 -import mailbox  
7 -import datetime  
8 -from email.iterators import typed_subpart_iterator  
9 -  
10 -import chardet  
11 -  
12 -  
13 -def get_charset(message, default='ASCII'):  
14 - """Get the message charset"""  
15 -  
16 - charset = message.get_content_charset()  
17 -  
18 - if not charset:  
19 - charset = message.get_charset()  
20 -  
21 - if not charset:  
22 - charset = default  
23 -  
24 - try:  
25 - codecs.lookup(charset)  
26 - except LookupError:  
27 - charset = default  
28 -  
29 - return charset  
30 -  
31 -  
32 -class Message(mailbox.mboxMessage):  
33 -  
34 - RECEIVED_DELIMITER = re.compile('\n|;')  
35 -  
36 - def get_subject(self):  
37 - subject = email.header.decode_header(self['Subject'])  
38 -  
39 - if isinstance(subject, list):  
40 - new_subject = u''  
41 - for text_part, encoding in subject:  
42 - if not encoding:  
43 - encoding = get_charset(self)  
44 -  
45 - try:  
46 - new_subject += unicode(text_part, encoding)  
47 - except (UnicodeDecodeError, LookupError):  
48 - try:  
49 - new_subject += unicode(text_part, get_charset(self))  
50 - except (UnicodeDecodeError, LookupError):  
51 - encoding = chardet.detect(text_part)['encoding']  
52 - new_subject += unicode(text_part, encoding)  
53 -  
54 - return ''.join(new_subject)  
55 -  
56 - def get_body(self):  
57 - """Get the body of the email message"""  
58 -  
59 - if self.is_multipart():  
60 - # get the plain text version only  
61 - text_parts = [part  
62 - for part in typed_subpart_iterator(self,  
63 - 'text',  
64 - 'plain')]  
65 - body = []  
66 - for part in text_parts:  
67 - charset = get_charset(part, get_charset(self))  
68 - body.append(unicode(part.get_payload(decode=True),  
69 - charset,  
70 - "replace"))  
71 -  
72 - return u"\n".join(body).strip()  
73 -  
74 - else: # if it is not multipart, the payload will be a string  
75 - # representing the message body  
76 - body = unicode(self.get_payload(decode=True),  
77 - get_charset(self),  
78 - "replace")  
79 - return body.strip()  
80 -  
81 - def get_received_datetime(self):  
82 - if 'Received' not in self:  
83 - return None  
84 - # The time received should always be the last element  
85 - # in the `Received` attribute from the message headers  
86 - received_header = self.RECEIVED_DELIMITER.split(self['Received'])  
87 - received_time_header = received_header[-1].strip()  
88 -  
89 - date_tuple = email.utils.parsedate_tz(received_time_header)  
90 - utc_timestamp = email.utils.mktime_tz(date_tuple)  
91 - utc_datetime = datetime.datetime.fromtimestamp(utc_timestamp,  
92 - pytz.utc)  
93 -  
94 - return utc_datetime  
95 -  
96 - def get_from_addr(self):  
97 - real_name_raw, from_ = email.utils.parseaddr(self['From'])  
98 - real_name_str, encoding = email.header.decode_header(real_name_raw)[0]  
99 - if not encoding:  
100 - encoding = 'ascii'  
101 -  
102 - real_name = unicode(real_name_str, encoding, errors='replace')  
103 - return real_name, from_  
colab/super_archives/management/commands/update_blocks.py
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -#!/usr/bin/env python  
2 -  
3 -from django.core.management.base import BaseCommand  
4 -from colab.super_archives.models import Message  
5 -  
6 -  
7 -class Command(BaseCommand):  
8 - help = "Update message blocks (used to hide the reply part messages)"  
9 -  
10 - def handle(self, *args, **kwargs):  
11 - for message in Message.objects.iterator():  
12 - message.update_blocks()  
colab/super_archives/management/commands/update_keywords.py
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -#!/usr/bin/env python  
2 -  
3 -from django.core.management.base import BaseCommand  
4 -from colab.super_archives.models import Thread  
5 -  
6 -  
7 -class Command(BaseCommand):  
8 - help = "Update keywords used in tag cloud and related thread"  
9 -  
10 - def handle(self, *args, **kwargs):  
11 - for thread in Thread.objects.iterator():  
12 - thread.update_keywords()  
colab/super_archives/managers.py
@@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
1 -from django.db import models  
2 -  
3 -from haystack.query import SearchQuerySet  
4 -  
5 -  
6 -class NotSpamManager(models.Manager):  
7 - """Only return objects which are not marked as spam."""  
8 -  
9 - def get_queryset(self):  
10 - return super(NotSpamManager, self).get_queryset().exclude(spam=True)  
11 -  
12 -  
13 -class HighestScore(NotSpamManager):  
14 - def get_queryset(self):  
15 - queryset = super(HighestScore, self).get_queryset()  
16 - return queryset.order_by('-score', '-latest_message__received_time')  
17 -  
18 - def from_haystack(self):  
19 - return SearchQuerySet().filter(type='thread')  
20 -  
21 -  
22 -class MostVotedManager(NotSpamManager):  
23 - def get_queryset(self):  
24 - """Query for the most voted messages sorting by the sum of  
25 - voted and after by date."""  
26 -  
27 - queryset = super(MostVotedManager, self).get_queryset()  
28 -  
29 - sql = """  
30 - SELECT  
31 - count(sav.id)  
32 - FROM  
33 - super_archives_vote AS sav  
34 - WHERE  
35 - super_archives_message.id = sav.message_id  
36 - """  
37 -  
38 - messages = queryset.extra(  
39 - select={  
40 - 'vote_count': sql,  
41 - }  
42 - )  
43 - return messages.order_by('-vote_count', 'received_time')  
colab/super_archives/migrations/0001_initial.py
@@ -1,230 +0,0 @@ @@ -1,230 +0,0 @@
1 -# -*- coding: utf-8 -*-  
2 -from __future__ import unicode_literals  
3 -  
4 -from django.db import models, migrations  
5 -import hitcounter.models  
6 -import taggit.managers  
7 -import django.db.models.deletion  
8 -from django.conf import settings  
9 -import colab.super_archives.models  
10 -  
11 -  
12 -class Migration(migrations.Migration):  
13 -  
14 - dependencies = [  
15 - migrations.swappable_dependency(settings.AUTH_USER_MODEL),  
16 - ('taggit', '0001_initial'),  
17 - ]  
18 -  
19 - operations = [  
20 - migrations.CreateModel(  
21 - name='EmailAddress',  
22 - fields=[  
23 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
24 - auto_created=True, primary_key=True)),  
25 - ('address', models.EmailField(unique=True, max_length=75)),  
26 - ('real_name', models.CharField(db_index=True, max_length=64,  
27 - blank=True)),  
28 - ('md5', models.CharField(max_length=32, null=True)),  
29 - ('user', models.ForeignKey(related_name=b'emails',  
30 - on_delete=django.db.models.deletion.SET_NULL,  
31 - to=settings.AUTH_USER_MODEL, null=True)),  
32 - ],  
33 - options={  
34 - 'ordering': ('id',),  
35 - },  
36 - bases=(models.Model,),  
37 - ),  
38 - migrations.CreateModel(  
39 - name='EmailAddressValidation',  
40 - fields=[  
41 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
42 - auto_created=True, primary_key=True)),  
43 - ('address', models.EmailField(unique=True, max_length=75)),  
44 - ('validation_key',  
45 - models.CharField(  
46 - default=colab.super_archives.models.get_validation_key,  
47 - max_length=32, null=True)),  
48 - ('created', models.DateTimeField(auto_now_add=True)),  
49 - ('user',  
50 - models.ForeignKey(related_name=b'emails_not_validated',  
51 - to=settings.AUTH_USER_MODEL, null=True)),  
52 - ],  
53 - options={  
54 - },  
55 - bases=(models.Model,),  
56 - ),  
57 - migrations.CreateModel(  
58 - name='Keyword',  
59 - fields=[  
60 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
61 - auto_created=True, primary_key=True)),  
62 - ('keyword', models.CharField(max_length=b'128')),  
63 - ('weight', models.IntegerField(default=0)),  
64 - ],  
65 - options={  
66 - 'ordering': ('?',),  
67 - },  
68 - bases=(models.Model,),  
69 - ),  
70 - migrations.CreateModel(  
71 - name='MailingList',  
72 - fields=[  
73 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
74 - auto_created=True, primary_key=True)),  
75 - ('name', models.CharField(max_length=80)),  
76 - ('email', models.EmailField(max_length=75)),  
77 - ('description', models.TextField()),  
78 - ('logo', models.FileField(upload_to=b'list_logo')),  
79 - ('last_imported_index', models.IntegerField(default=0)),  
80 - ],  
81 - options={  
82 - },  
83 - bases=(models.Model,),  
84 - ),  
85 - migrations.CreateModel(  
86 - name='MailingListMembership',  
87 - fields=[  
88 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
89 - auto_created=True, primary_key=True)),  
90 - ('mailinglist',  
91 - models.ForeignKey(to='super_archives.MailingList')),  
92 - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),  
93 - ],  
94 - options={  
95 - },  
96 - bases=(models.Model,),  
97 - ),  
98 - migrations.CreateModel(  
99 - name='Message',  
100 - fields=[  
101 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
102 - auto_created=True, primary_key=True)),  
103 - ('subject',  
104 - models.CharField(help_text='Please enter a message subject',  
105 - max_length=512, verbose_name='Subject',  
106 - db_index=True)),  
107 - ('subject_clean', models.CharField(max_length=512,  
108 - db_index=True)),  
109 - ('body',  
110 - models.TextField(default=b'',  
111 - help_text='Please enter a message body',  
112 - verbose_name='Message body')),  
113 - ('received_time', models.DateTimeField(db_index=True)),  
114 - ('message_id', models.CharField(max_length=512)),  
115 - ('spam', models.BooleanField(default=False)),  
116 - ('from_address',  
117 - models.ForeignKey(to='super_archives.EmailAddress')),  
118 - ],  
119 - options={  
120 - 'ordering': ('received_time',),  
121 - 'verbose_name': 'Message',  
122 - 'verbose_name_plural': 'Messages',  
123 - },  
124 - bases=(models.Model,),  
125 - ),  
126 - migrations.CreateModel(  
127 - name='MessageBlock',  
128 - fields=[  
129 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
130 - auto_created=True, primary_key=True)),  
131 - ('text', models.TextField()),  
132 - ('is_reply', models.BooleanField(default=False)),  
133 - ('order', models.IntegerField()),  
134 - ('message', models.ForeignKey(related_name=b'blocks',  
135 - to='super_archives.Message')),  
136 - ],  
137 - options={  
138 - 'ordering': ('order',),  
139 - },  
140 - bases=(models.Model,),  
141 - ),  
142 - migrations.CreateModel(  
143 - name='MessageMetadata',  
144 - fields=[  
145 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
146 - auto_created=True, primary_key=True)),  
147 - ('name', models.CharField(max_length=512)),  
148 - ('value', models.TextField()),  
149 - ('Message', models.ForeignKey(to='super_archives.Message')),  
150 - ],  
151 - options={  
152 - },  
153 - bases=(models.Model,),  
154 - ),  
155 - migrations.CreateModel(  
156 - name='Thread',  
157 - fields=[  
158 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
159 - auto_created=True, primary_key=True)),  
160 - ('subject_token', models.CharField(max_length=512)),  
161 - ('score', models.IntegerField(default=0,  
162 - help_text='Thread score',  
163 - verbose_name='Score')),  
164 - ('spam', models.BooleanField(default=False)),  
165 - ('latest_message',  
166 - models.OneToOneField(related_name=b'+',  
167 - null=True, to='super_archives.Message',  
168 - help_text='Latest message posted',  
169 - verbose_name='Latest message')),  
170 - ('mailinglist',  
171 - models.ForeignKey(  
172 - verbose_name='Mailing List',  
173 - to='super_archives.MailingList',  
174 - help_text='The Mailing List where is the thread')),  
175 - ('tags',  
176 - taggit.managers.TaggableManager(  
177 - to='taggit.Tag',  
178 - through='taggit.TaggedItem',  
179 - help_text='A comma-separated list of tags.',  
180 - verbose_name='Tags')),  
181 - ],  
182 - options={  
183 - 'ordering': ('-latest_message__received_time',),  
184 - 'verbose_name': 'Thread',  
185 - 'verbose_name_plural': 'Threads',  
186 - },  
187 - bases=(models.Model, hitcounter.models.HitCounterModelMixin),  
188 - ),  
189 - migrations.CreateModel(  
190 - name='Vote',  
191 - fields=[  
192 - ('id', models.AutoField(verbose_name='ID', serialize=False,  
193 - auto_created=True, primary_key=True)),  
194 - ('created', models.DateTimeField(auto_now_add=True)),  
195 - ('message', models.ForeignKey(to='super_archives.Message')),  
196 - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),  
197 - ],  
198 - options={  
199 - },  
200 - bases=(models.Model,),  
201 - ),  
202 - migrations.AlterUniqueTogether(  
203 - name='vote',  
204 - unique_together=set([('user', 'message')]),  
205 - ),  
206 - migrations.AlterUniqueTogether(  
207 - name='thread',  
208 - unique_together=set([('subject_token', 'mailinglist')]),  
209 - ),  
210 - migrations.AddField(  
211 - model_name='message',  
212 - name='thread',  
213 - field=models.ForeignKey(to='super_archives.Thread', null=True),  
214 - preserve_default=True,  
215 - ),  
216 - migrations.AlterUniqueTogether(  
217 - name='message',  
218 - unique_together=set([('thread', 'message_id')]),  
219 - ),  
220 - migrations.AddField(  
221 - model_name='keyword',  
222 - name='thread',  
223 - field=models.ForeignKey(to='super_archives.Thread'),  
224 - preserve_default=True,  
225 - ),  
226 - migrations.AlterUniqueTogether(  
227 - name='emailaddressvalidation',  
228 - unique_together=set([('user', 'address')]),  
229 - ),  
230 - ]  
colab/super_archives/migrations/0002_mailinglist_is_private.py
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
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/migrations/__init__.py
colab/super_archives/models.py
@@ -1,415 +0,0 @@ @@ -1,415 +0,0 @@
1 -# -*- coding: utf-8 -*-  
2 -  
3 -import urllib  
4 -  
5 -from uuid import uuid4  
6 -from hashlib import md5  
7 -  
8 -from django.db import models  
9 -from django.conf import settings  
10 -from django.utils import timezone  
11 -from django.core.urlresolvers import reverse  
12 -from django.utils.translation import ugettext_lazy as _  
13 -  
14 -from html2text import html2text  
15 -from haystack.query import SearchQuerySet  
16 -from taggit.managers import TaggableManager  
17 -from hitcounter.models import HitCounterModelMixin  
18 -  
19 -from .managers import NotSpamManager, MostVotedManager, HighestScore  
20 -from .utils import blocks, email  
21 -from .utils.etiquetador import etiquetador  
22 -from colab.accounts.utils import mailman  
23 -  
24 -  
25 -def get_validation_key():  
26 - return uuid4().hex  
27 -  
28 -  
29 -class EmailAddressValidation(models.Model):  
30 - address = models.EmailField(unique=True)  
31 - user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,  
32 - related_name='emails_not_validated')  
33 - validation_key = models.CharField(max_length=32, null=True,  
34 - default=get_validation_key)  
35 - created = models.DateTimeField(auto_now_add=True)  
36 -  
37 - class Meta:  
38 - unique_together = ('user', 'address')  
39 -  
40 - @classmethod  
41 - def create(cls, address, user):  
42 - email_address_validation = cls.objects.create(address=address,  
43 - user=user)  
44 - return email_address_validation  
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 -  
55 -  
56 -class EmailAddress(models.Model):  
57 - user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,  
58 - related_name='emails', on_delete=models.SET_NULL)  
59 - address = models.EmailField(unique=True)  
60 - real_name = models.CharField(max_length=64, blank=True, db_index=True)  
61 - md5 = models.CharField(max_length=32, null=True)  
62 -  
63 - class Meta:  
64 - ordering = ('id', )  
65 -  
66 - def save(self, *args, **kwargs):  
67 - self.md5 = md5(self.address).hexdigest()  
68 - super(EmailAddress, self).save(*args, **kwargs)  
69 -  
70 - def get_full_name(self):  
71 - if self.user and self.user.get_full_name():  
72 - return self.user.get_full_name()  
73 - else:  
74 - return self.real_name  
75 -  
76 - def get_full_name_or_anonymous(self):  
77 - return self.get_full_name() or _('Anonymous')  
78 -  
79 - def __unicode__(self):  
80 - return '"%s" <%s>' % (self.get_full_name(), self.address)  
81 -  
82 -  
83 -class MailingList(models.Model):  
84 - name = models.CharField(max_length=80)  
85 - email = models.EmailField()  
86 - description = models.TextField()  
87 - logo = models.FileField(upload_to='list_logo') # TODO  
88 - last_imported_index = models.IntegerField(default=0)  
89 - is_private = models.BooleanField(default=False)  
90 -  
91 - def update_privacy(self):  
92 - self.is_private = mailman.is_private_list(self.name)  
93 -  
94 - def get_absolute_url(self):  
95 - params = {  
96 - 'list': self.name,  
97 - 'type': 'thread',  
98 - 'order': 'latest',  
99 - }  
100 - return u'{}?{}'.format(reverse('haystack_search'),  
101 - urllib.urlencode(params))  
102 -  
103 - def __unicode__(self):  
104 - return self.name  
105 -  
106 -  
107 -class MailingListMembership(models.Model):  
108 - user = models.ForeignKey(settings.AUTH_USER_MODEL)  
109 - mailinglist = models.ForeignKey(MailingList)  
110 -  
111 - def __unicode__(self):  
112 - return '%s on %s' % (self.user.email, self.mailinglist.name)  
113 -  
114 -  
115 -class Keyword(models.Model):  
116 - keyword = models.CharField(max_length='128')  
117 - weight = models.IntegerField(default=0)  
118 - thread = models.ForeignKey('Thread')  
119 -  
120 - class Meta:  
121 - ordering = ('?', ) # random order  
122 -  
123 - def __unicode__(self):  
124 - return self.keyword  
125 -  
126 -  
127 -class Thread(models.Model, HitCounterModelMixin):  
128 -  
129 - subject_token = models.CharField(max_length=512)  
130 - mailinglist = \  
131 - models.ForeignKey(  
132 - MailingList,  
133 - verbose_name=_(u"Mailing List"),  
134 - help_text=_(u"The Mailing List where is the thread"))  
135 - latest_message = \  
136 - models.OneToOneField('Message', null=True, related_name='+',  
137 - verbose_name=_(u"Latest message"),  
138 - help_text=_(u"Latest message posted"))  
139 - score = models.IntegerField(default=0, verbose_name=_(u"Score"),  
140 - help_text=_(u"Thread score"))  
141 - spam = models.BooleanField(default=False)  
142 -  
143 - highest_score = HighestScore()  
144 - all_objects = models.Manager()  
145 - objects = NotSpamManager()  
146 - tags = TaggableManager()  
147 -  
148 - # Save this pseudo now to avoid calling the  
149 - # function N times in the loops below  
150 - now = timezone.now()  
151 -  
152 - class Meta:  
153 - verbose_name = _(u"Thread")  
154 - verbose_name_plural = _(u"Threads")  
155 - unique_together = ('subject_token', 'mailinglist')  
156 - ordering = ('-latest_message__received_time', )  
157 -  
158 - @models.permalink  
159 - def get_absolute_url(self):  
160 - return ('thread_view', [self.mailinglist, self.subject_token])  
161 -  
162 - def update_keywords(self):  
163 - blocks = MessageBlock.objects.filter(message__thread__pk=self.pk,  
164 - is_reply=False)  
165 -  
166 - self.tags.clear()  
167 -  
168 - text = u'\n'.join(map(unicode, blocks))  
169 - tags = etiquetador(html2text(text))  
170 -  
171 - for tag, weight in tags:  
172 - keyword, created = Keyword.objects.get_or_create(thread=self,  
173 - keyword=tag)  
174 - if created or keyword.weight != weight:  
175 - keyword.weight = weight  
176 - keyword.save()  
177 -  
178 - if weight >= 3:  
179 - self.tags.add(tag)  
180 -  
181 - # removing old tags not used anylonger  
182 - if tags:  
183 - qs = Keyword.objects.filter(thread=self)  
184 - qs = qs.exclude(keyword__in=zip(*tags)[0])  
185 - qs.delete()  
186 -  
187 - def get_related(self):  
188 - query_string = u' '.join(self.tags.names())  
189 - if query_string:  
190 - query_set = SearchQuerySet().exclude(django_id=self.pk)  
191 - return query_set.filter(content=query_string, type='thread')  
192 -  
193 - return tuple()  
194 -  
195 - def __unicode__(self):  
196 - return '%s - %s (%s)' % (self.id,  
197 - self.subject_token,  
198 - self.message_set.count())  
199 -  
200 - def _days_ago(self, date):  
201 - return (self.now - date).days  
202 -  
203 - def _get_score(self, weight, created):  
204 - return max(weight - (self._days_ago(created) // 3), 5)  
205 -  
206 - def update_score(self):  
207 - """Update the relevance score for this thread.  
208 -  
209 - The score is calculated with the following variables:  
210 -  
211 - * vote_weight: 100 - (minus) 1 for each 3 days since  
212 - voted with minimum of 5.  
213 - * replies_weight: 300 - (minus) 1 for each 3 days since  
214 - replied with minimum of 5.  
215 - * page_view_weight: 10.  
216 -  
217 - * vote_score: sum(vote_weight)  
218 - * replies_score: sum(replies_weight)  
219 - * page_view_score: sum(page_view_weight)  
220 -  
221 - * score = (vote_score + replies_score + page_view_score) // 10  
222 - with minimum of 0 and maximum of 5000  
223 -  
224 - """  
225 -  
226 - if not self.subject_token:  
227 - return  
228 -  
229 - vote_score = 0  
230 - replies_score = 0  
231 - for msg in self.message_set.all():  
232 - # Calculate replies_score  
233 - replies_score += self._get_score(300, msg.received_time)  
234 -  
235 - # Calculate vote_score  
236 - for vote in msg.vote_set.all():  
237 - vote_score += self._get_score(100, vote.created)  
238 -  
239 - # Calculate page_view_score  
240 - page_view_score = self.hits * 10  
241 -  
242 - self.score = (page_view_score + vote_score + replies_score) // 10  
243 - self.save()  
244 -  
245 -  
246 -class Vote(models.Model):  
247 - user = models.ForeignKey(settings.AUTH_USER_MODEL)  
248 - message = models.ForeignKey('Message')  
249 - created = models.DateTimeField(auto_now_add=True)  
250 -  
251 - class Meta:  
252 - unique_together = ('user', 'message')  
253 -  
254 - def __unicode__(self):  
255 - return 'Vote on %s by %s' % (self.message.id,  
256 - self.user.username)  
257 -  
258 -  
259 -class Message(models.Model):  
260 -  
261 - from_address = models.ForeignKey(EmailAddress, db_index=True)  
262 - thread = models.ForeignKey(Thread, null=True, db_index=True)  
263 - # RFC 2822 recommends to use 78 chars + CRLF (so 80 chars) for  
264 - # the max_length of a subject but most of implementations  
265 - # goes for 256. We use 512 just in case.  
266 - subject = models.CharField(max_length=512, db_index=True,  
267 - verbose_name=_(u"Subject"),  
268 - help_text=_(u"Please enter a message subject"))  
269 - subject_clean = models.CharField(max_length=512, db_index=True)  
270 - body = models.TextField(default='',  
271 - verbose_name=_(u"Message body"),  
272 - help_text=_(u"Please enter a message body"))  
273 - received_time = models.DateTimeField(db_index=True)  
274 - message_id = models.CharField(max_length=512)  
275 - spam = models.BooleanField(default=False)  
276 -  
277 - all_objects = models.Manager()  
278 - objects = NotSpamManager()  
279 - most_voted = MostVotedManager()  
280 -  
281 - class Meta:  
282 - verbose_name = _(u"Message")  
283 - verbose_name_plural = _(u"Messages")  
284 - unique_together = ('thread', 'message_id')  
285 - ordering = ('received_time', )  
286 -  
287 - def __unicode__(self):  
288 - return '(%s) %s: %s' % (self.id,  
289 - self.from_address.get_full_name(),  
290 - self.subject_clean)  
291 -  
292 - def update_blocks(self):  
293 - # delete all blocks for that message  
294 - self.blocks.all().delete()  
295 -  
296 - for i, block in enumerate(blocks.EmailBlockParser(self)):  
297 - MessageBlock.from_emailblock(block, self, i)  
298 -  
299 - @property  
300 - def mailinglist(self):  
301 - if not self.thread:  
302 - return None  
303 -  
304 - return self.thread.mailinglist  
305 -  
306 - def vote_list(self):  
307 - """Return a list of user that voted in this message."""  
308 - return [vote.user for vote in self.vote_set.iterator()]  
309 -  
310 - def votes_count(self):  
311 - return len(self.vote_list())  
312 -  
313 - def vote(self, user):  
314 - Vote.objects.create(  
315 - message=self,  
316 - user=user  
317 - )  
318 -  
319 - def unvote(self, user):  
320 - Vote.objects.get(  
321 - message=self,  
322 - user=user  
323 - ).delete()  
324 -  
325 - @property  
326 - def url(self):  
327 - """Shortcut to get thread url"""  
328 - return reverse('thread_view', args=[self.mailinglist.name,  
329 - self.thread.subject_token])  
330 -  
331 - @property  
332 - def description(self):  
333 - """Alias to self.body"""  
334 - return self.body  
335 -  
336 - @property  
337 - def title(self):  
338 - """Alias to self.subject_clean"""  
339 - return self.subject_clean  
340 -  
341 - @property  
342 - def modified(self):  
343 - """Alias to self.modified"""  
344 - return self.received_time  
345 -  
346 - @property  
347 - def tag(self):  
348 - if not self.thread:  
349 - return None  
350 - return self.mailinglist.name  
351 -  
352 - @property  
353 - def author(self):  
354 - return self.fullname  
355 -  
356 - @property  
357 - def author_url(self):  
358 - if self.from_address.user_id:  
359 - return self.from_address.user.get_absolute_url()  
360 - return None  
361 -  
362 - # An alias for author  
363 - @property  
364 - def modified_by(self):  
365 - return self.author  
366 -  
367 - # An alias for author_url  
368 - @property  
369 - def modified_by_url(self):  
370 - return self.author_url  
371 -  
372 - @property  
373 - def fullname(self):  
374 - return self.from_address.get_full_name()  
375 -  
376 - @property  
377 - def icon_name(self):  
378 - return u'envelope'  
379 -  
380 - @property  
381 - def type(self):  
382 - return u'thread'  
383 -  
384 -  
385 -class MessageBlock(models.Model):  
386 - message = models.ForeignKey(Message, related_name='blocks')  
387 - text = models.TextField()  
388 - is_reply = models.BooleanField(default=False)  
389 - order = models.IntegerField()  
390 -  
391 - def __unicode__(self):  
392 - return self.text  
393 -  
394 - class Meta:  
395 - ordering = ('order', )  
396 -  
397 - @classmethod  
398 - def from_emailblock(klass, emailblock, message, order):  
399 - obj = klass.objects.create(text=emailblock.text,  
400 - is_reply=emailblock.is_reply,  
401 - message=message,  
402 - order=order)  
403 - return obj  
404 -  
405 -  
406 -class MessageMetadata(models.Model):  
407 - Message = models.ForeignKey(Message)  
408 - # Same problem here than on subjects. Read comment above  
409 - # on Message.subject  
410 - name = models.CharField(max_length=512)  
411 - value = models.TextField()  
412 -  
413 - def __unicode__(self):  
414 - return 'Email Message Id: %s - %s: %s' % (self.Message.id,  
415 - self.name, self.value)  
colab/super_archives/search_indexes.py
@@ -1,100 +0,0 @@ @@ -1,100 +0,0 @@
1 -# -*- coding: utf-8 -*-  
2 -  
3 -import math  
4 -  
5 -from haystack import indexes  
6 -  
7 -from colab.search.base_indexes import BaseIndex  
8 -from .models import Thread  
9 -  
10 -  
11 -class ThreadIndex(BaseIndex, indexes.Indexable):  
12 - title = indexes.CharField(model_attr='latest_message__subject_clean')  
13 - description = indexes.CharField(use_template=True)  
14 - latest_description = indexes.CharField(  
15 - model_attr='latest_message__description',  
16 - indexed=False,  
17 - )  
18 - created = indexes.DateTimeField()  
19 - modified = indexes.DateTimeField(  
20 - model_attr='latest_message__modified'  
21 - )  
22 - tag = indexes.CharField(model_attr='mailinglist__name')  
23 - collaborators = indexes.CharField(use_template=True, stored=False)  
24 - mailinglist_url = indexes.CharField(  
25 - model_attr='mailinglist__get_absolute_url',  
26 - indexed=False,  
27 - )  
28 - latest_message_pk = indexes.IntegerField(  
29 - model_attr='latest_message__pk', indexed=False  
30 - )  
31 - rating = indexes.IntegerField(model_attr='score')  
32 -  
33 - def get_model(self):  
34 - return Thread  
35 -  
36 - def get_updated_field(self):  
37 - return 'latest_message__received_time'  
38 -  
39 - # def prepare_fullname(self, obj):  
40 - # return obj.message_set.first().from_address.get_full_name()  
41 -  
42 - def prepare_fullname_and_username(self, obj):  
43 - from_address = obj.message_set.first().from_address  
44 - if not from_address.user:  
45 - return from_address.get_full_name()  
46 -  
47 - return u'{}\n{}'.format(  
48 - from_address.get_full_name(),  
49 - from_address.user.username,  
50 - )  
51 -  
52 - def prepare_author(self, obj):  
53 - first_message = obj.message_set.first()  
54 - return first_message.from_address.get_full_name()  
55 -  
56 - def prepare_author_url(self, obj):  
57 - first_message = obj.message_set.first()  
58 - return first_message.author_url  
59 -  
60 - def prepare_modified_by(self, obj):  
61 - modified_by = obj.latest_message.modified_by  
62 - if modified_by:  
63 - return modified_by  
64 - return obj.message_set.first().author  
65 -  
66 - def prepare_modified_by_url(self, obj):  
67 - modified_by_url = obj.latest_message.modified_by_url  
68 - if modified_by_url:  
69 - return modified_by_url  
70 - return None  
71 -  
72 - def prepare_created(self, obj):  
73 - return obj.message_set.first().received_time  
74 -  
75 - def prepare_fullname(self, obj):  
76 - fullname = obj.latest_message.from_address.get_full_name()  
77 - if not fullname:  
78 - fullname = obj.message_set.first().from_address.get_full_name()  
79 - return fullname  
80 -  
81 - def prepare_icon_name(self, obj):  
82 - return u'envelope'  
83 -  
84 - def prepare_type(self, obj):  
85 - return u'thread'  
86 -  
87 - def index_queryset(self, using=None):  
88 - elements = self.get_model().objects.filter(  
89 - spam=False, mailinglist__is_private=False  
90 - ).exclude(subject_token='')  
91 -  
92 - return elements  
93 -  
94 - def get_boost(self, obj):  
95 - boost = super(ThreadIndex, self).get_boost(obj)  
96 -  
97 - if obj.score >= 10:  
98 - boost = boost * math.log(obj.score)  
99 -  
100 - return boost  
colab/super_archives/signals.py
@@ -1,23 +0,0 @@ @@ -1,23 +0,0 @@
1 -  
2 -from django.db.models.signals import post_save  
3 -from django.dispatch import receiver  
4 -from django.conf import settings  
5 -  
6 -from .models import EmailAddress  
7 -  
8 -  
9 -@receiver(post_save, sender=settings.AUTH_USER_MODEL)  
10 -def create_email_address(sender, instance, created, **kwargs):  
11 - if not created:  
12 - return  
13 -  
14 - email, email_created = EmailAddress.objects.get_or_create(  
15 - address=instance.email,  
16 - defaults={  
17 - 'real_name': instance.get_full_name(),  
18 - 'user': instance,  
19 - }  
20 - )  
21 -  
22 - email.user = instance  
23 - email.save()  
colab/super_archives/templates/message-preview.html
@@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
1 -{% load i18n tz highlight %}  
2 -  
3 -<li class="preview-message">  
4 -<span class="glyphicon glyphicon-{{ result.icon_name }}" title="{{ result.type }}"></span>  
5 -  
6 -{% if result.tag %}  
7 -<a href="{% firstof result.mailinglist_url result.mailinglist.get_absolute_url result.url %}">  
8 - <span class="label label-primary">{{ result.tag }}</span>  
9 -</a>  
10 -{% endif %}  
11 -  
12 -{% if result.title %}  
13 - <a href="{{ result.url }}{% if result.type == 'thread' and result.latest_message_pk %}#msg-{{ result.latest_message_pk }}{% elif result.type == 'thread' and result.pk %}#msg-{{ result.pk }}{% endif %}" {% if result.latest_description %}title="{{ result.latest_description|escape|truncatechars:150 }}"{% elif result.description %}title="{{ result.description|escape|truncatechars:150 }}"{% endif %}>  
14 - <span class="subject">  
15 - <!-- a striptags filter was raising an error here because using with highlight -->  
16 - {% if query %}  
17 - {% highlight result.title with query max_length "1000" %}  
18 - {% else %}  
19 - {{ result.title }}  
20 - {% endif %}  
21 - </span>  
22 - </a>  
23 -{% endif %}  
24 -  
25 -{% if result.description %}  
26 - <!-- a striptags filter was raising an error here because using with highlight -->  
27 - <span class="quiet">- {% if query %}  
28 - {% highlight result.description with query max_length "150" %}  
29 - {% else %}  
30 - {% if result.latest_description %}  
31 - {{ result.latest_description|striptags|escape|truncatechars:150 }}  
32 - {% elif result.description %}  
33 - {{ result.description|striptags|escape|truncatechars:150 }}  
34 - {% endif %}  
35 - {% endif %}  
36 - </span>  
37 -{% endif %}  
38 -  
39 -{% if result.fullname or result.modified or result.modified_by %}  
40 - <div class="quiet">  
41 - {% if result.modified_by %}  
42 - <span class="pull-left">{% trans "by" %}  
43 - {% if result.modified_by_url %}  
44 - <a href="{{ result.modified_by_url }}">  
45 - {% else %}  
46 - <span>  
47 - {% endif %}  
48 -  
49 - {% if query %}  
50 - {% highlight result.modified_by with query %}  
51 - {% else %}  
52 - {{ result.modified_by }}  
53 - {% endif %}  
54 -  
55 - {% if result.modified_by_url %}  
56 - </a>  
57 - {% else %}  
58 - </span>  
59 - {% endif %}  
60 - </span>  
61 - {% else %}  
62 - <span class="pull-left">{% trans "by" %} {% trans "Anonymous" %}</span>  
63 - {% endif %}  
64 - {% if result.modified %}  
65 - <span class="pull-right">{{ result.modified|localtime|timesince }} {% trans "ago" %}</span>  
66 - {% endif %}  
67 - </div>  
68 -{% endif %}  
69 -</li>  
colab/super_archives/templates/message-thread.html
@@ -1,193 +0,0 @@ @@ -1,193 +0,0 @@
1 -{% extends "base.html" %}  
2 -{% load i18n tz superarchives %}  
3 -  
4 -{% trans "Anonymous" as anonymous %}  
5 -  
6 -{% block title %}{{ first_msg.subject_clean }}{% endblock %}  
7 -  
8 -{% block head_js %}  
9 -  
10 -<script>  
11 - function vote_done_callback(step) {  
12 - console.debug('(un)vote successfuly (step ' + step + ')');  
13 - var $btn = $(this);  
14 - var step;  
15 -  
16 - if ($btn.hasClass('btn-default')) {  
17 - step = 1;  
18 - } else {  
19 - step = -1;  
20 - }  
21 -  
22 - $btn.prev('.vote-count').text(function(self, count) {  
23 - return parseInt(count) + step;  
24 - });  
25 -  
26 - $btn.toggleClass('btn-success');  
27 - $btn.toggleClass('btn-default');  
28 - $btn.button('reset')  
29 - }  
30 -  
31 - function vote_fail_callback(jqXHR, textStatus, errorThrown) {  
32 - var msg;  
33 -  
34 - if (jqXHR.status === 403) {  
35 - msg = " {% trans 'You must login before voting.' %}"  
36 -  
37 - $('#alert-js #alert-message').html(msg);  
38 - $('#alert-js').show();  
39 - scroll(0, 0);  
40 - }  
41 -  
42 - }  
43 -  
44 - function vote(event) {  
45 - var $ajax;  
46 - var $btn = $(this);  
47 - var $msg = $(this).parents('.email-message');  
48 -  
49 - var method;  
50 - var csrftoken = $.cookie('csrftoken');  
51 - var msg_id = $msg.attr('id').split('-')[1];  
52 -  
53 - if($btn.hasClass('btn-default')) {  
54 - method = 'PUT';  
55 - } else {  
56 - method = 'DELETE';  
57 - }  
58 -  
59 - console.debug('trying to vote');  
60 - $btn.button('loading');  
61 - $ajax = $.ajax({  
62 - url: "/archives/message/" + msg_id + "/vote",  
63 - type: method,  
64 - context: $btn.get(0),  
65 - beforeSend: function(xhr, settings) {  
66 - xhr.setRequestHeader("X-CSRFToken", csrftoken);  
67 - }  
68 - });  
69 - $ajax.done(vote_done_callback);  
70 - $ajax.fail(vote_fail_callback);  
71 - }  
72 -  
73 - function scrollToAnchor(aid){  
74 - var aTag = $(aid);  
75 - console.log(aTag);  
76 - $('html, body').animate({scrollTop: aTag.offset().top}, 800);  
77 - }  
78 -  
79 - {% if user.is_active %}  
80 - function focus_reply(event) {  
81 - scrollToAnchor('#msg-reply');  
82 - $('textarea', '#msg-reply').focus();  
83 - }  
84 - {% endif %}  
85 -  
86 - // Binding functions  
87 - $(function() {  
88 - $(".panel-heading").on('click', function(event) {  
89 - var $target = $(event.target);  
90 - // Do not collapse the the message if the clicked element (target)  
91 - // is a button or a link  
92 - if($target.hasClass('btn') || $target.is('a') || $target.parent().is('a')) {  
93 - return;  
94 - }  
95 -  
96 - $(this).next('.panel-collapse').collapse('toggle');  
97 - });  
98 -  
99 - $('.vote.btn', this).on('click', vote);  
100 - {% if user.is_active %}  
101 - $('.reply.btn', this).on('click', focus_reply);  
102 - {% endif %}  
103 -  
104 - $('.message-link').popover({'placement': 'right'});  
105 - });  
106 -  
107 -</script>  
108 -  
109 -{% endblock %}  
110 -  
111 -{% block main-content %}  
112 -<div class="row">  
113 -  
114 - <div class="col-lg-12">  
115 - <h2>{{ first_msg.subject_clean }}</h2>  
116 - <hr />  
117 - </div>  
118 -  
119 - <div class="col-lg-9 col-md-9 col-sm-12">  
120 - <ul class="unstyled-list">  
121 - {% for email in emails %}  
122 - {% include "superarchives/includes/message.html" with userprofile=email.from_address.user emailaddress=email.from_address fullname=email.from_address.get_full_name_or_anonymous %}  
123 - {% endfor %}  
124 -  
125 - {% if user.is_active %}  
126 - {% include "superarchives/includes/message.html" with userprofile=user emailaddress=user.email fullname=user.get_full_name reply=True %}  
127 - {% endif %}  
128 - </ul>  
129 - </div>  
130 -  
131 - <div class="col-lg-3 col-md-3 hidden-sm hidden-xs">  
132 - <h4><strong>{% trans "Order by" %}:</strong></h4>  
133 - <ul class="unstyled-list">  
134 - <li>  
135 - <span class="glyphicon glyphicon-chevron-right"></span>  
136 - <a href="{% append_to_get order='voted' %}">{% trans "Votes" %}</a>  
137 - </li>  
138 - <li>  
139 - <span class="glyphicon glyphicon-chevron-right"></span>  
140 - <a href="{% append_to_get order='date' %}">{% trans "Date" %}</a>  
141 - </li>  
142 - </ul>  
143 -  
144 - {% if thread.get_related %}  
145 - <h4><strong>{% trans "Related:" %}</strong></h4>  
146 - <ul class="unstyled-list">  
147 - {% for similar in thread.get_related|slice:":10" %}  
148 - <li>  
149 - <span class="label label-primary label-small">{{ similar.tag }}</span>  
150 - <a href="{{ similar.url }}">{{ similar.title|truncatechars:50 }}</a>  
151 - </li>  
152 - {% endfor %}  
153 - </ul>  
154 - {% endif %}  
155 -  
156 - <h4><strong>{% trans "Statistics:" %}</strong></h4>  
157 - <ul class="unstyled-list">  
158 - <li>  
159 - <span class="glyphicon glyphicon-chevron-right"></span>  
160 - {% trans "started at" %}  
161 - <h5>{{ first_msg.received_time|localtime|timesince }} {% trans "ago" %}</h5>  
162 - </li>  
163 -  
164 - <li>  
165 - <span class="glyphicon glyphicon-chevron-right"></span>  
166 - {% trans "viewed" %}  
167 - <h5>{{ pagehits }} {% trans "times" %}</h5>  
168 - </li>  
169 - <li>  
170 - <span class="glyphicon glyphicon-chevron-right"></span>  
171 - {% trans "answered" %}  
172 - <h5>{{ emails|length }} {% trans "times" %}</h5>  
173 - </li>  
174 - <li>  
175 - <span class="glyphicon glyphicon-chevron-right"></span>  
176 - {% trans "voted" %}  
177 - <h5>{{ total_votes }} {% trans "times" %}</h5>  
178 - </li>  
179 - </ul>  
180 -  
181 - {% if thread.keyword_set.count %}  
182 - <h4><strong>{% trans "Tags:" %}</strong></h4>  
183 - <div class="tag-cloud">  
184 - {% for keyword in thread.keyword_set.all %}  
185 - <a class="tag size-{{ keyword.weight }}" href="{% url 'haystack_search' %}?q={{ keyword }}">{{ keyword|escape }}</a>  
186 - {% endfor %}  
187 - </div>  
188 - {% endif %}  
189 -  
190 - </div>  
191 -  
192 -</div>  
193 -{% endblock %}  
colab/super_archives/templates/search/indexes/super_archives/thread_collaborators.txt
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -{% for message in object.message_set.iterator %}  
2 - {{ message.from_address.get_full_name }}  
3 - {{ message.from_address.get_full_name|slugify }}  
4 - {{ message.from_address.user.username }}  
5 -{% endfor %}  
colab/super_archives/templates/search/indexes/super_archives/thread_description.txt
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 -{% for message in object.message_set.iterator %}  
2 - {{ message.body }}  
3 -{% endfor %}  
colab/super_archives/templates/search/indexes/super_archives/thread_text.txt
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -{{ object.subject_token }}  
2 -  
3 -{% for message in object.message_set.iterator %}  
4 - {{ message.title }}  
5 - {{ message.title|slugify }}  
6 - {{ message.author }}  
7 - {{ message.author|slugify }}  
8 - {{ message.body }}  
9 - {{ message.body|slugify }}  
10 -{% endfor %}  
colab/super_archives/templates/superarchives/emails/email_blank_subject.txt
@@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
1 -{% load i18n %}  
2 -{% trans 'Hello' %} {{ user }},  
3 -{% blocktrans with body=email_body mailinglist=mailinglist %}  
4 -You've sent an email to {{ mailinglist }} with a blank subject and the following content:  
5 -  
6 -"{{ body }}"  
7 -  
8 -Please, fill the subject in every email you send it.  
9 -  
10 -Thank you.  
11 -{% endblocktrans %}  
colab/super_archives/templates/superarchives/emails/email_verification.txt
@@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
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 %}  
3 -  
4 -{{ verification_url }}  
5 -  
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/templates/superarchives/includes/message.html
@@ -1,78 +0,0 @@ @@ -1,78 +0,0 @@
1 -{% load gravatar superarchives tz i18n %}  
2 -<li>  
3 - {% spaceless %}  
4 - <div class="email-message" id="msg-{% firstof email.id 'reply' %}">  
5 - <div class="panel panel-default">  
6 - <div class="panel-heading clearfix">  
7 - <div class="col-lg-6 col-md-6 col-sm-6">  
8 - {% if userprofile.get_absolute_url %}  
9 - <a href="{{ userprofile.get_absolute_url }}">  
10 - {% endif %}  
11 - {% gravatar emailaddress 34 %}  
12 - <strong class="user-fullname">{{ fullname }}</strong>  
13 - {% if userprofile.get_absolute_url %}  
14 - </a>  
15 - {% endif %}  
16 - {% if email.id %}  
17 - <div class="hidden-xs hidden-sm div-message-link" title="{% trans 'Link to this message' %}">  
18 - <button class="btn btn-default message-link" data-toggle="popover" data-content="<input type='text' value='{{ request.get_host }}{{ request.path }}#msg-{{ email.id }}' class='form-control' />" data-title="{% trans 'Link to this message' %}" data-html="true" data-container="body">  
19 - <span class="glyphicon glyphicon-link"></span>  
20 - </button>  
21 - </div>  
22 - {% endif %}  
23 - </div>  
24 -  
25 - {% if not reply %}  
26 - <div class="col-lg-6 col-md-6 col-sm-6">  
27 - <div class="pull-right text-right">  
28 - <span class="date">  
29 - {{ email.received_time|localtime|date:'DATETIME_FORMAT' }}  
30 - </span>  
31 -  
32 - <div class="btn-group">  
33 - <button class="btn btn-default vote-count disabled">  
34 - {{ email.votes_count }}  
35 - </button>  
36 - {% if user in email.vote_list %}  
37 - <button class="btn btn-success vote" data-loading-text="...">  
38 - {% else %}  
39 - <button class="btn btn-default vote" data-loading-text="...">  
40 - {% endif %}  
41 - <span class="glyphicon glyphicon-thumbs-up"></span>  
42 - </button>  
43 - </div>  
44 -  
45 - {% if user.is_active %}  
46 - <button class="btn btn-default reply" title="{% trans 'Reply' %}">  
47 - <span class="glyphicon glyphicon-share"></span>  
48 - </button>  
49 - {% endif %}  
50 - </div>  
51 - </div>  
52 - {% endif %}  
53 -  
54 - </div>  
55 - <div class="panel-collapse in">  
56 - <div class="panel-body">  
57 - {% if not reply %}  
58 - {% display_message email %}  
59 - {% else %}  
60 - <form method="POST">  
61 - {% csrf_token %}  
62 - <p>  
63 - <textarea name="emailbody" placeholder="{% trans 'Send a message' %}" rows="5" class="form-control"></textarea>  
64 - </p>  
65 - <div class="col-lg-9 col-md-8 col-sm-8 col-xs-7">  
66 - <p class="quiet">{% trans "After sending a message it will take few minutes before it shows up in here. Why don't you grab a coffee?" %}</p>  
67 - </div>  
68 - <div class="col-lg-3 col-md-4 col-sm-4 col-xs-5 text-right">  
69 - <button class="btn btn-success" type="submit">{% trans 'Send' %}</button>  
70 - </div>  
71 - </form>  
72 - {% endif %}  
73 - </div>  
74 - </div>  
75 - </div>  
76 - </div>  
77 -{% endspaceless %}  
78 -</li>  
colab/super_archives/templates/superarchives/tags/display_message.html
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -{% for block in blocks %}  
2 - {% if block.is_reply %}  
3 - <button class="btn btn-info btn-xs toggle-reply" onclick="$(this).next().toggle();">...</button>  
4 - <div style="display: none; color: #707;">{% else %}<div>{% endif %}{{ block|safe|linebreaksbr }}</div>  
5 -{% endfor %}  
colab/super_archives/templates/superarchives/thread-dashboard.html
@@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
1 -{% extends 'base.html' %}  
2 -{% load i18n %}  
3 -  
4 -{% block title %}{% trans 'Groups'|title %}{% endblock %}  
5 -  
6 -{% block main-content %}  
7 - <h2>{% trans 'Groups'|title %}</h2>  
8 - <hr/>  
9 -  
10 - {% for listname, description, latest, most_relevant, number_of_users in lists %}  
11 - {% if latest or most_relevant %}  
12 - <h3><b>{{ listname|title|lower }} {% if description %} ({{ description }}){% endif %}</b></h3>  
13 - <div class="btn-group btn-group-sm">  
14 - <a href="#" class="btn btn-default" disabled="disabled">{% blocktrans %}{{ number_of_users }} members{% endblocktrans %}</a>  
15 - </div>  
16 - <hr/>  
17 -  
18 - <div class="row">  
19 - <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">  
20 - <h4>{% trans 'latest'|title %}</h4>  
21 - <ul class="message-list">  
22 - {% for thread in latest %}  
23 - {% include "message-preview.html" with result=thread.latest_message %}  
24 - {% endfor %}  
25 - </ul>  
26 - <div class="text-right">  
27 - <a href="{% url 'haystack_search' %}?order=latest&list={{ listname }}&type=thread">  
28 - {% trans "more..." %}  
29 - </a>  
30 - </div>  
31 - </div>  
32 -  
33 - <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">  
34 - <h4>{% trans 'most relevant'|title %}</h4>  
35 - <ul class="message-list">  
36 - {% for thread in most_relevant %}  
37 - {% include "message-preview.html" with result=thread %}  
38 - {% endfor %}  
39 - </ul>  
40 - <div class="text-right">  
41 - <a href="{% url 'haystack_search' %}?list={{ listname }}&type=thread">  
42 - {% trans "more..." %}  
43 - </a>  
44 - </div>  
45 - </div>  
46 - </div>  
47 -  
48 -  
49 - {% endif %}  
50 - {% endfor %}  
51 -  
52 -{% endblock %}  
colab/super_archives/templatetags/__init__.py
colab/super_archives/templatetags/superarchives.py
@@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
1 -  
2 -from django import template  
3 -from colab.super_archives.utils import url  
4 -  
5 -  
6 -register = template.Library()  
7 -TEMPLATE_PATH = 'superarchives/tags/'  
8 -  
9 -  
10 -@register.inclusion_tag(TEMPLATE_PATH + 'display_message.html')  
11 -def display_message(email):  
12 - if not email.blocks.count():  
13 - email.update_blocks()  
14 -  
15 - return {'blocks': email.blocks.all}  
16 -  
17 -  
18 -@register.simple_tag(takes_context=True)  
19 -def append_to_get(context, **kwargs):  
20 - return url.append_to_get(  
21 - context['request'].META['PATH_INFO'],  
22 - context['request'].META['QUERY_STRING'],  
23 - **kwargs  
24 - )  
25 -  
26 -  
27 -@register.simple_tag(takes_context=True)  
28 -def pop_from_get(context, **kwargs):  
29 - return url.pop_from_get(  
30 - context['request'].META['PATH_INFO'],  
31 - context['request'].META['QUERY_STRING'],  
32 - **kwargs  
33 - )  
colab/super_archives/tests/__init__.py
colab/super_archives/tests/test_privatelist.py
@@ -1,71 +0,0 @@ @@ -1,71 +0,0 @@
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='johndoe', password='1234')  
17 -  
18 - def test_see_only_private_list_if_member(self):  
19 - mailman.get_user_mailinglists = mock.Mock(  
20 - return_value="[{'listname': 'privatelist'}]")  
21 - mailman.extract_listname_from_list = mock.Mock(  
22 - return_value="['privatelist']")  
23 - mailman.list_users = mock.Mock(return_value="['johndoe@example.com']")  
24 -  
25 - self.authenticate_user()  
26 - request = self.client.get('/archives/thread/')  
27 -  
28 - list_data = request.context['lists']  
29 -  
30 - self.assertEqual('lista', list_data[0][0])  
31 - self.assertEqual('privatelist', list_data[1][0])  
32 - self.assertEqual(2, len(list_data))  
33 -  
34 - def test_see_only_public_if_not_logged_in(self):  
35 - request = self.client.get('/archives/thread/')  
36 -  
37 - list_data = request.context['lists']  
38 -  
39 - self.assertEqual('lista', list_data[0][0])  
40 - self.assertEqual(1, len(list_data))  
41 -  
42 - def test_see_private_thread_in_dashboard_if_member(self):  
43 - mailman.get_user_mailinglists = mock.Mock(  
44 - return_value="[{'listname': 'privatelist'}]")  
45 - mailman.extract_listname_from_list = mock.Mock(  
46 - return_value="['privatelist']")  
47 -  
48 - self.authenticate_user()  
49 - request = self.client.get('/dashboard')  
50 -  
51 - latest_threads = request.context['latest_threads']  
52 - hottest_threads = request.context['hottest_threads']  
53 -  
54 - self.assertEqual(2, len(latest_threads))  
55 - self.assertEqual(2, len(hottest_threads))  
56 -  
57 - def test_dont_see_private_thread_if_logged_out(self):  
58 - request = self.client.get('/dashboard')  
59 -  
60 - latest_threads = request.context['latest_threads']  
61 - hottest_threads = request.context['hottest_threads']  
62 -  
63 - self.assertEqual(1, len(latest_threads))  
64 - self.assertEqual(1, len(hottest_threads))  
65 -  
66 - def test_dont_see_private_threads_in_profile_if_logged_out(self):  
67 - request = self.client.get('/account/johndoe')  
68 -  
69 - emails = request.context['emails']  
70 -  
71 - self.assertEqual(1, len(emails))  
colab/super_archives/urls.py
@@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
1 -from django.conf.urls import patterns, url  
2 -  
3 -from .views import (EmailView, EmailValidationView, ThreadView,  
4 - ThreadDashboardView, VoteView)  
5 -  
6 -  
7 -urlpatterns = patterns(  
8 - 'super_archives.views',  
9 - url(r'thread/(?P<mailinglist>[-\w]+)/(?P<thread_token>[-\w]+)$',  
10 - ThreadView.as_view(), name="thread_view"),  
11 - url(r'thread/$', ThreadDashboardView.as_view(), name='thread_list'),  
12 - url(r'manage/email/validate/?$', EmailValidationView.as_view(),  
13 - name="archive_email_validation_view"),  
14 - url(r'manage/email/(?P<key>[0-9a-z]{32})?', EmailView.as_view(),  
15 - name="archive_email_view"),  
16 - url(r'message/(?P<msg_id>\d+)/vote$', VoteView.as_view()),  
17 -)  
colab/super_archives/utils/__init__.py
colab/super_archives/utils/blocks.py
@@ -1,101 +0,0 @@ @@ -1,101 +0,0 @@
1 -  
2 -import re  
3 -  
4 -from django.utils.html import strip_tags  
5 -  
6 -from html2text import html2text  
7 -  
8 -  
9 -EXTENDED_PUNCTUATION = '!"#$%&\'()*+,-./:;=?@[\\]^_`{|}~ \t\n\r\x0b\x0c'  
10 -RE_WRAPPED_BY_HTML = re.compile(r'^<[a-z]+[^>]*>.*</[a-z]+[^>]*>$',  
11 - re.MULTILINE | re.IGNORECASE | re.DOTALL)  
12 -RE_LINKS = re.compile(r'(?P<link>https?://[^ \t\r\n\<]+)')  
13 -LINK_MARKUP = u'<a target="_blank" href="\g<link>">\g<link></a>'  
14 -  
15 -RE_REPLY_LINE = re.compile(r'^[\s\t>]*>[\s\t]*')  
16 -  
17 -RE_BR_TO_LINEBREAK = re.compile(r'<\s*/?\s*br\s*/?\s*>')  
18 -  
19 -  
20 -class EmailBlock(list):  
21 - def __init__(self, is_reply=False, mark_links=True, html2text=True):  
22 - self.mark_links = mark_links  
23 - self.html2text = html2text  
24 - self.is_reply = is_reply  
25 -  
26 - def _html2text(self, text):  
27 - if RE_WRAPPED_BY_HTML.match(text.strip()):  
28 - return html2text(text)  
29 -  
30 - text, n = RE_BR_TO_LINEBREAK.subn('\n', text)  
31 - text = strip_tags(text)  
32 - return text  
33 -  
34 - def _mark_links(self, text):  
35 - text, n = RE_LINKS.subn(LINK_MARKUP, text)  
36 - return text  
37 -  
38 - @property  
39 - def text(self):  
40 - block = u''.join(self)  
41 -  
42 - if self.html2text:  
43 - block = self._html2text(block)  
44 -  
45 - if self.mark_links:  
46 - block = self._mark_links(block)  
47 -  
48 - return block  
49 -  
50 - def __unicode__(self):  
51 - return self.text  
52 -  
53 - def __str__(self):  
54 - return self.text  
55 -  
56 -  
57 -class EmailBlockParser(list):  
58 - def __init__(self, email):  
59 - self.email = email  
60 - self.thread_emails = email.thread.message_set  
61 -  
62 - message = email.body  
63 - block = EmailBlock()  
64 -  
65 - for line in message.split('\n'):  
66 - if self.context_switch(line, block):  
67 - self.append(block)  
68 - new_block_context = not block.is_reply  
69 - block = EmailBlock(is_reply=new_block_context)  
70 -  
71 - block.append(line + '\n')  
72 -  
73 - self.append(block)  
74 -  
75 - def context_switch(self, line, block):  
76 - if line.strip(EXTENDED_PUNCTUATION):  
77 - if self.is_reply(line):  
78 - if not block.is_reply:  
79 - return True  
80 -  
81 - else:  
82 - if block.is_reply:  
83 - return True  
84 -  
85 - return False  
86 -  
87 - def is_reply(self, line):  
88 - stripped_line = line.strip()  
89 - if stripped_line.startswith('>') or RE_REPLY_LINE.match(line):  
90 - return True  
91 -  
92 - clean_line = RE_REPLY_LINE.subn('', stripped_line)[0]  
93 - queryset = \  
94 - self.thread_emails.filter(  
95 - received_time__lt=self.email.received_time,  
96 - body__contains=clean_line).order_by('-received_time')  
97 -  
98 - if queryset[:1]:  
99 - return True  
100 -  
101 - return False  
colab/super_archives/utils/email.py
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
1 -  
2 -from django.core import mail  
3 -from django.conf import settings  
4 -from django.template import Context, loader  
5 -from django.utils.translation import ugettext as _  
6 -  
7 -  
8 -def colab_send_email(subject, message, to):  
9 - from_email = settings.COLAB_FROM_ADDRESS  
10 - return mail.send_mail(subject, message, from_email, [to])  
11 -  
12 -  
13 -def send_verification_email(to, user, validation_key, verification_url):  
14 - subject = _('Please verify your email ') + u'{}'.format(to)  
15 - msg_tmpl = \  
16 - loader.get_template('superarchives/emails/email_verification.txt')  
17 - message = msg_tmpl.render(Context({'to': to, 'user': user,  
18 - 'verification_url': verification_url  
19 - }))  
20 - return colab_send_email(subject, message, to)  
colab/super_archives/utils/etiquetador.py
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -  
2 -from etiquetando import Etiquetador  
3 -  
4 -etiquetador = Etiquetador(word_min_size=3, max_tags=24,  
5 - weight_range=(1, 4))  
colab/super_archives/utils/url.py
@@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
1 -# -*- coding: utf-8 -*-  
2 -  
3 -import urllib  
4 -import urlparse  
5 -  
6 -  
7 -def append_to_get(path, query=None, **kwargs):  
8 - query_dict = dict(urlparse.parse_qsl(query))  
9 - for key, value in kwargs.items():  
10 - query_dict[key] = value  
11 - return u'{}?{}'.format(path, urllib.urlencode(query_dict))  
12 -  
13 -  
14 -def pop_from_get(path, query=None, **kwargs):  
15 - query_dict = dict(urlparse.parse_qsl(query))  
16 - for key, value in kwargs.items():  
17 - if query_dict not in (key):  
18 - continue  
19 - if query_dict[key] == value:  
20 - del query_dict[key]  
21 - continue  
22 - if value in query_dict[key]:  
23 - aux = query_dict[key].split(value)  
24 - query_dict[key] = u''.join(aux).strip()  
25 - return u'{}?{}'.format(path, urllib.urlencode(query_dict))  
colab/super_archives/views.py
@@ -1,348 +0,0 @@ @@ -1,348 +0,0 @@
1 -# -*- coding: utf-8 -*-  
2 -  
3 -import smtplib  
4 -import logging  
5 -import urlparse  
6 -  
7 -import requests  
8 -  
9 -from django import http  
10 -from django.conf import settings  
11 -from django.contrib import messages  
12 -from django.core.urlresolvers import reverse  
13 -from django.db import IntegrityError  
14 -from django.views.generic import View  
15 -from django.utils.translation import ugettext as _  
16 -from django.core.exceptions import ObjectDoesNotExist, PermissionDenied  
17 -from django.utils.decorators import method_decorator  
18 -from django.contrib.auth.decorators import login_required  
19 -from django.shortcuts import render, redirect, get_object_or_404  
20 -  
21 -from colab.accounts.utils import mailman  
22 -from colab.accounts.models import User  
23 -from .utils.email import send_verification_email  
24 -from .models import (MailingList, Thread, EmailAddress,  
25 - EmailAddressValidation, Message)  
26 -  
27 -  
28 -class ThreadView(View):  
29 - http_method_names = [u'get', u'post']  
30 -  
31 - def get(self, request, mailinglist, thread_token):  
32 -  
33 - thread = get_object_or_404(Thread, subject_token=thread_token,  
34 - mailinglist__name=mailinglist)  
35 -  
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):  
44 - if not request.user.is_authenticated():  
45 - raise PermissionDenied  
46 - else:  
47 - user = User.objects.get(username=request.user)  
48 - emails = user.emails.values_list('address', flat=True)  
49 - lists_for_user = mailman.get_user_mailinglists(user)  
50 - listnames_for_user = mailman.extract_listname_from_list(  
51 - lists_for_user)  
52 - if thread.mailinglist.name not in listnames_for_user:  
53 - raise PermissionDenied  
54 -  
55 - thread.hit(request)  
56 -  
57 - try:  
58 - first_message = thread.message_set.first()  
59 - except ObjectDoesNotExist:  
60 - raise http.Http404  
61 -  
62 - order_by = request.GET.get('order')  
63 - if order_by == 'voted':  
64 - msgs_query = Message.most_voted  
65 - else:  
66 - msgs_query = Message.objects  
67 -  
68 - msgs_query = msgs_query.filter(thread__subject_token=thread_token)  
69 - msgs_query = msgs_query.filter(thread__mailinglist__name=mailinglist)  
70 - emails = msgs_query.exclude(id=first_message.id)  
71 -  
72 - total_votes = first_message.votes_count()  
73 - for email in emails:  
74 - total_votes += email.votes_count()  
75 -  
76 - # Update relevance score  
77 - thread.update_score()  
78 -  
79 - context = {  
80 - 'first_msg': first_message,  
81 - 'emails': [first_message] + list(emails),  
82 - 'pagehits': thread.hits,  
83 - 'total_votes': total_votes,  
84 - 'thread': thread,  
85 - }  
86 -  
87 - return render(request, 'message-thread.html', context)  
88 -  
89 - def post(self, request, mailinglist, thread_token):  
90 - try:  
91 - thread = Thread.objects.get(subject_token=thread_token,  
92 - mailinglist__name=mailinglist)  
93 - except Thread.DoesNotExist:  
94 - raise http.Http404  
95 -  
96 - data = {  
97 - 'in_reply_to': thread.message_set.last().message_id,  
98 - 'email_from': request.user.email,  
99 - 'name_from': request.user.get_full_name(),  
100 - 'subject': thread.message_set.first().subject_clean,  
101 - 'body': request.POST.get('emailbody', '').strip(),  
102 - }  
103 -  
104 - url = urlparse.urljoin(settings.MAILMAN_API_URL,  
105 - mailinglist + '/sendmail')  
106 -  
107 - error_msg = None  
108 - try:  
109 - resp = requests.post(url, data=data, timeout=2)  
110 - except requests.exceptions.ConnectionError:  
111 - resp = None  
112 - error_msg = _('Error trying to connect to Mailman API')  
113 - except requests.exceptions.Timeout:  
114 - resp = None  
115 - error_msg = _('Timeout trying to connect to Mailman API')  
116 -  
117 - if resp and resp.status_code == 200:  
118 - messages.success(request, _(  
119 - "Your message was sent to this topic. "  
120 - "It may take some minutes before it's delivered by email "  
121 - "to the group. Why don't you breath some fresh air in the "  
122 - "meanwhile?"  
123 - ))  
124 - else:  
125 - if not error_msg:  
126 - if resp is not None:  
127 - if resp.status_code == 400:  
128 - error_msg = _('You cannot send an empty email')  
129 - elif resp.status_code == 404:  
130 - error_msg = _('Mailing list does not exist')  
131 - else:  
132 - error_msg = \  
133 - _('Unknown error trying to connect to Mailman API')  
134 -  
135 - messages.error(request, error_msg)  
136 -  
137 - return self.get(request, mailinglist, thread_token)  
138 -  
139 -  
140 -class ThreadDashboardView(View):  
141 - http_method_names = ['get']  
142 -  
143 - def get(self, request):  
144 - MAX = 6  
145 - context = {}  
146 -  
147 - all_privates = {}  
148 - private_mailinglist = MailingList.objects.filter(is_private=True)  
149 - for mailinglist in private_mailinglist:  
150 - all_privates[mailinglist.name] = True  
151 -  
152 - context['lists'] = []  
153 -  
154 - listnames_for_user = []  
155 - if request.user.is_authenticated():  
156 - user = User.objects.get(username=request.user)  
157 - lists_for_user = mailman.get_user_mailinglists(user)  
158 - listnames_for_user = mailman.extract_listname_from_list(  
159 - lists_for_user)  
160 -  
161 - for list_ in MailingList.objects.order_by('name'):  
162 - if list_.name not in all_privates\  
163 - or list_.name in listnames_for_user:  
164 - context['lists'].append((  
165 - list_.name,  
166 - mailman.get_list_description(list_.name),  
167 - list_.thread_set.filter(spam=False).order_by(  
168 - '-latest_message__received_time'  
169 - )[:MAX],  
170 - [t.latest_message for t in Thread.highest_score.filter(  
171 - mailinglist__name=list_.name)[:MAX]],  
172 - len(mailman.list_users(list_.name)),  
173 - ))  
174 -  
175 - return render(request, 'superarchives/thread-dashboard.html', context)  
176 -  
177 -  
178 -class EmailView(View):  
179 -  
180 - http_method_names = [u'head', u'get', u'post', u'delete', u'update']  
181 -  
182 - def get(self, request, key):  
183 - """Validate an email with the given key"""  
184 -  
185 - try:  
186 - email_val = EmailAddressValidation.objects.get(validation_key=key)  
187 - except EmailAddressValidation.DoesNotExist:  
188 - messages.error(request, _('The email address you are trying to '  
189 - 'verify either has already been verified'  
190 - ' or does not exist.'))  
191 - return redirect('/')  
192 -  
193 - try:  
194 - email = EmailAddress.objects.get(address=email_val.address)  
195 - except EmailAddress.DoesNotExist:  
196 - email = EmailAddress(address=email_val.address)  
197 -  
198 - if email.user and email.user.is_active:  
199 - messages.error(request, _('The email address you are trying to '  
200 - 'verify is already an active email '  
201 - 'address.'))  
202 - email_val.delete()  
203 - return redirect('/')  
204 -  
205 - email.user = email_val.user  
206 - email.save()  
207 - email_val.delete()  
208 -  
209 - user = User.objects.get(username=email.user.username)  
210 - user.is_active = True  
211 - user.save()  
212 -  
213 - messages.success(request, _('Email address verified!'))  
214 - return redirect('user_profile', username=email_val.user.username)  
215 -  
216 - @method_decorator(login_required)  
217 - def post(self, request, key):  
218 - """Create new email address that will wait for validation"""  
219 -  
220 - email = request.POST.get('email')  
221 - user_id = request.POST.get('user')  
222 - if not email:  
223 - return http.HttpResponseBadRequest()  
224 -  
225 - try:  
226 - EmailAddressValidation.objects.create(address=email,  
227 - user_id=user_id)  
228 - except IntegrityError:  
229 - # 409 Conflict  
230 - # duplicated entries  
231 - # email exist and it's waiting for validation  
232 - return http.HttpResponse(status=409)  
233 -  
234 - return http.HttpResponse(status=201)  
235 -  
236 - @method_decorator(login_required)  
237 - def delete(self, request, key):  
238 - """Remove an email address, validated or not."""  
239 -  
240 - request.DELETE = http.QueryDict(request.body)  
241 - email_addr = request.DELETE.get('email')  
242 - user_id = request.DELETE.get('user')  
243 -  
244 - if not email_addr:  
245 - return http.HttpResponseBadRequest()  
246 -  
247 - try:  
248 - email = EmailAddressValidation.objects.get(address=email_addr,  
249 - user_id=user_id)  
250 - except EmailAddressValidation.DoesNotExist:  
251 - pass  
252 - else:  
253 - email.delete()  
254 - return http.HttpResponse(status=204)  
255 -  
256 - try:  
257 - email = EmailAddress.objects.get(address=email_addr,  
258 - user_id=user_id)  
259 - except EmailAddress.DoesNotExist:  
260 - raise http.Http404  
261 -  
262 - email.user = None  
263 - email.save()  
264 - return http.HttpResponse(status=204)  
265 -  
266 - @method_decorator(login_required)  
267 - def update(self, request, key):  
268 - """Set an email address as primary address."""  
269 -  
270 - request.UPDATE = http.QueryDict(request.body)  
271 -  
272 - email_addr = request.UPDATE.get('email')  
273 - user_id = request.UPDATE.get('user')  
274 - if not email_addr:  
275 - return http.HttpResponseBadRequest()  
276 -  
277 - try:  
278 - email = EmailAddress.objects.get(address=email_addr,  
279 - user_id=user_id)  
280 - except EmailAddress.DoesNotExist:  
281 - raise http.Http404  
282 -  
283 - email.user.email = email_addr  
284 - email.user.save()  
285 - return http.HttpResponse(status=204)  
286 -  
287 -  
288 -class EmailValidationView(View):  
289 -  
290 - http_method_names = [u'post']  
291 -  
292 - def post(self, request):  
293 - email_addr = request.POST.get('email')  
294 - user_id = request.POST.get('user')  
295 - try:  
296 - email = EmailAddressValidation.objects.get(address=email_addr,  
297 - user_id=user_id)  
298 - except http.DoesNotExist:  
299 - raise http.Http404  
300 -  
301 - try:  
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)  
307 - except smtplib.SMTPException:  
308 - logging.exception('Error sending validation email')  
309 - return http.HttpResponseServerError()  
310 -  
311 - return http.HttpResponse(status=204)  
312 -  
313 -  
314 -class VoteView(View):  
315 -  
316 - http_method_names = [u'get', u'put', u'delete', u'head']  
317 -  
318 - def put(self, request, msg_id):  
319 - if not request.user.is_authenticated():  
320 - return http.HttpResponseForbidden()  
321 -  
322 - try:  
323 - Message.objects.get(id=msg_id).vote(request.user)  
324 - except IntegrityError:  
325 - # 409 Conflict  
326 - # used for duplicated entries  
327 - return http.HttpResponse(status=409)  
328 -  
329 - # 201 Created  
330 - return http.HttpResponse(status=201)  
331 -  
332 - def get(self, request, msg_id):  
333 - votes = Message.objects.get(id=msg_id).votes_count()  
334 - return http.HttpResponse(votes, content_type='application/json')  
335 -  
336 - def delete(self, request, msg_id):  
337 - if not request.user.is_authenticated():  
338 - return http.HttpResponseForbidden()  
339 -  
340 - try:  
341 - Message.objects.get(id=msg_id).unvote(request.user)  
342 - except ObjectDoesNotExist:  
343 - return http.HttpResponseGone()  
344 -  
345 - # 204 No Content  
346 - # empty body, as per RFC2616.  
347 - # object deleted  
348 - return http.HttpResponse(status=204)