Commit ad6b59eeaa0f6b5e1da59fbc51cd11c366cc164c
1 parent
1b21cac6
Exists in
detach_super_archives
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
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
colab/super_archives/context_processors.py
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
colab/super_archives/templates/search/indexes/super_archives/thread_description.txt
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
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) |