summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-16 03:05:58 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-16 03:05:58 -0400
commitfb7de73d27d28378028aa2ae5d8e7b969865211b (patch)
tree23721494c225c19e11ea7a081bfaf392cb84780f
parent731dfdf5d343f1599597d45fad2128d4da2ea55f (diff)
downloadaskbot-fb7de73d27d28378028aa2ae5d8e7b969865211b.tar.gz
askbot-fb7de73d27d28378028aa2ae5d8e7b969865211b.tar.bz2
askbot-fb7de73d27d28378028aa2ae5d8e7b969865211b.zip
first pass for posting question by email via Lamson
-rw-r--r--askbot/lamson_handlers.py17
-rw-r--r--askbot/management/commands/post_emailed_questions.py123
-rw-r--r--askbot/models/reply_by_email.py34
-rw-r--r--askbot/utils/mail.py174
4 files changed, 183 insertions, 165 deletions
diff --git a/askbot/lamson_handlers.py b/askbot/lamson_handlers.py
index 1d2cb220..ee37ff0b 100644
--- a/askbot/lamson_handlers.py
+++ b/askbot/lamson_handlers.py
@@ -2,9 +2,10 @@ import re
from lamson.routing import route, stateless
from lamson.server import Relay
from django.utils.translation import ugettext as _
-from askbot.models import ReplyAddress
-from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
+from django.conf import settings
+from askbot.models import ReplyAddress
+from askbot.utils import mail
#we might end up needing to use something like this
@@ -90,9 +91,19 @@ def get_attachments(message):
attachments.append(process_attachment(part))
return attachments
+@route('ask@(host)')
+@stateless
+def ASK(message, host = None):
+ body = get_body(message)
+ attachments = get_attachments(message)
+ subject = '[test; one; two;] this is a question title'#get_subject(message)
+ from_address = message.From
+ import pdb
+ pdb.set_trace()
+ mail.process_emailed_question(from_address, subject, body, attachments)
-@route("(address)@(host)", address=".+")
+@route('(address)@(host)', address='.+')
@stateless
def PROCESS(message, address = None, host = None):
"""handler to process the emailed message
diff --git a/askbot/management/commands/post_emailed_questions.py b/askbot/management/commands/post_emailed_questions.py
index 0a038b62..cc77b57f 100644
--- a/askbot/management/commands/post_emailed_questions.py
+++ b/askbot/management/commands/post_emailed_questions.py
@@ -21,75 +21,14 @@ import quopri
import base64
from django.conf import settings as django_settings
from django.core.management.base import NoArgsCommand, CommandError
-from django.core import exceptions
-from django.utils.translation import ugettext_lazy as _
-from django.utils.translation import string_concat
-from django.core.urlresolvers import reverse
from askbot.conf import settings as askbot_settings
from askbot.utils import mail
-from askbot.utils import url_utils
-from askbot import models
-from askbot.forms import AskByEmailForm
-
-USAGE = _(
-"""<p>To ask by email, please:</p>
-<ul>
- <li>Format the subject line as: [Tag1; Tag2] Question title</li>
- <li>Type details of your question into the email body</li>
-</ul>
-<p>Note that tags may consist of more than one word, and tags
-may be separated by a semicolon or a comma</p>
-"""
-)
-
-def bounce_email(email, subject, reason = None, body_text = None):
- """sends a bounce email at address ``email``, with the subject
- line ``subject``, accepts several reasons for the bounce:
-
- * ``'problem_posting'``, ``unknown_user`` and ``permission_denied``
- * ``body_text`` in an optional parameter that allows to append
- extra text to the message
- """
- if reason == 'problem_posting':
- error_message = _(
- '<p>Sorry, there was an error posting your question '
- 'please contact the %(site)s administrator</p>'
- ) % {'site': askbot_settings.APP_SHORT_NAME}
- error_message = string_concat(error_message, USAGE)
- elif reason == 'unknown_user':
- error_message = _(
- '<p>Sorry, in order to post questions on %(site)s '
- 'by email, please <a href="%(url)s">register first</a></p>'
- ) % {
- 'site': askbot_settings.APP_SHORT_NAME,
- 'url': url_utils.get_login_url()
- }
- elif reason == 'permission_denied':
- error_message = _(
- '<p>Sorry, your question could not be posted '
- 'due to insufficient privileges of your user account</p>'
- )
- else:
- raise ValueError('unknown reason to bounce an email: "%s"' % reason)
-
- if body_text != None:
- error_message = string_concat(error_message, body_text)
-
- #print 'sending email'
- #print email
- #print subject
- #print error_message
- mail.send_mail(
- recipient_list = (email,),
- subject_line = 'Re: ' + subject,
- body_text = error_message
- )
class CannotParseEmail(Exception):
"""This exception will bounce the email"""
- def __init__(self, email, subject):
+ def __init__(self, email_address, subject):
super(CannotParseEmail, self).__init__()
- bounce_email(email, subject, reason = 'problem_posting')
+ mail.bounce_email(email_address, subject, reason = 'problem_posting')
def parse_message(msg):
"""returns a tuple
@@ -105,7 +44,7 @@ def parse_message(msg):
if isinstance(msg, list):
raise CannotParseEmail(sender, subject)
- ctype = msg.get_content_type()#text/plain only
+ #ctype = msg.get_content_type()#text/plain only
raw_body = msg.get_payload()#text/plain only
encoding = msg.get('Content-Transfer-Encoding')
if encoding == 'base64':
@@ -118,6 +57,7 @@ def parse_message(msg):
class Command(NoArgsCommand):
+ """the django management command class"""
def handle_noargs(self, **options):
"""reads all emails from the INBOX of
imap server and posts questions based on
@@ -145,7 +85,7 @@ class Command(NoArgsCommand):
imap.select('INBOX')
#get message ids
- status, ids = imap.search(None, 'ALL')
+ junk, ids = imap.search(None, 'ALL')
if len(ids[0].strip()) == 0:
#with empty inbox - close and exit
@@ -154,58 +94,19 @@ class Command(NoArgsCommand):
return
#for each id - read a message, parse it and post a question
- for id in ids[0].split(' '):
- t, data = imap.fetch(id, '(RFC822)')
- message_body = data[0][1]
+ for msg_id in ids[0].split(' '):
+ junk, data = imap.fetch(msg_id, '(RFC822)')
+ #message_body = data[0][1]
msg = email.message_from_string(data[0][1])
- imap.store(id, '+FLAGS', '\\Deleted')
+ imap.store(msg_id, '+FLAGS', '\\Deleted')
try:
(sender, subject, body) = parse_message(msg)
- except CannotParseEmail, e:
+ except CannotParseEmail:
continue
- data = {
- 'sender': sender,
- 'subject': subject,
- 'body_text': body
- }
- form = AskByEmailForm(data)
- print data
- if form.is_valid():
- email_address = form.cleaned_data['email']
- try:
- user = models.User.objects.get(
- email__iexact = email_address
- )
- except models.User.DoesNotExist:
- bounce_email(email_address, subject, reason = 'unknown_user')
- except models.User.MultipleObjectsReturned:
- bounce_email(email_address, subject, reason = 'problem_posting')
- tagnames = form.cleaned_data['tagnames']
- title = form.cleaned_data['title']
- body_text = form.cleaned_data['body_text']
+ sender = mail.extract_first_email_address(sender)
+ mail.process_emailed_question(sender, subject, body)
- try:
- user.post_question(
- title = title,
- tags = tagnames,
- body_text = body_text
- )
- except exceptions.PermissionDenied, e:
- bounce_email(
- email_address,
- subject,
- reason = 'permission_denied',
- body_text = unicode(e)
- )
- else:
- email_address = mail.extract_first_email_address(sender)
- if email_address:
- bounce_email(
- email_address,
- subject,
- reason = 'problem_posting'
- )
imap.expunge()
imap.close()
imap.logout()
diff --git a/askbot/models/reply_by_email.py b/askbot/models/reply_by_email.py
index 62242367..26c53999 100644
--- a/askbot/models/reply_by_email.py
+++ b/askbot/models/reply_by_email.py
@@ -1,35 +1,13 @@
from datetime import datetime
import random
import string
-import os
from django.db import models
from django.contrib.auth.models import User
+from django.utils.translation import ugettext_lazy as _
from askbot.models.post import Post
from askbot.models.base import BaseQuerySetManager
-from askbot.utils.file_utils import store_file
from askbot.conf import settings as askbot_settings
-from django.utils.translation import ugettext_lazy as _
-
-def process_attachments(attachments):
- """saves file attachments and adds
-
- cheap way of dealing with the attachments
- just insert them inline, however it might
- be useful to keep track of the uploaded files separately
- and deal with them as with resources of their own value"""
- if attachments:
- content = ''
- for att in attachments:
- file_storage, file_name, file_url = store_file(att)
- chunk = '[%s](%s) ' % (att.name, file_url)
- file_extension = os.path.splitext(att.name)
- #todo: this is a hack - use content type
- if file_extension.lower() in ('png', 'jpg', 'gif'):
- chunk = '\n\n!' + chunk
- content += '\n\n' + chunk
- return content
- else:
- return ''
+from askbot.utils import mail
class ReplyAddressManager(BaseQuerySetManager):
@@ -86,7 +64,7 @@ class ReplyAddress(models.Model):
"""edits the created post upon repeated response
to the same address"""
assert self.was_used == True
- content += process_attachments(attachments)
+ content += mail.process_attachments(attachments)
self.user.edit_post(
post = self.response_post,
body_text = content,
@@ -99,7 +77,7 @@ class ReplyAddress(models.Model):
to the user
"""
result = None
- content += process_attachments(attachments)
+ content += mail.process_attachments(attachments)
if self.post.post_type == 'answer':
result = self.user.post_comment(self.post, content)
@@ -116,7 +94,3 @@ class ReplyAddress(models.Model):
self.used_at = datetime.now()
self.save()
return result
-
-
-
-
diff --git a/askbot/utils/mail.py b/askbot/utils/mail.py
index 005743fc..8b5d2663 100644
--- a/askbot/utils/mail.py
+++ b/askbot/utils/mail.py
@@ -1,13 +1,19 @@
"""functions that send email in askbot
these automatically catch email-related exceptions
"""
+import os
import smtplib
import logging
from django.core import mail
from django.conf import settings as django_settings
-from askbot.conf import settings as askbot_settings
+from django.core.exceptions import PermissionDenied
+from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import string_concat
from askbot import exceptions
from askbot import const
+from askbot.conf import settings as askbot_settings
+from askbot.utils import url_utils
+from askbot.utils.file_utils import store_file
#todo: maybe send_mail functions belong to models
#or the future API
def prefix_the_subject_line(subject):
@@ -32,38 +38,40 @@ def extract_first_email_address(text):
return None
def thread_headers(post, orig_post, update):
+ """modify headers for email messages, so
+ that emails appear as threaded conversations in gmail"""
suffix_id = django_settings.SERVER_EMAIL
if update == const.TYPE_ACTIVITY_ASK_QUESTION:
- id = "NQ-%s-%s" % (post.id, suffix_id)
- headers = {'Message-ID': id}
+ msg_id = "NQ-%s-%s" % (post.id, suffix_id)
+ headers = {'Message-ID': msg_id}
elif update == const.TYPE_ACTIVITY_ANSWER:
- id = "NA-%s-%s" % (post.id, suffix_id)
- orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
- headers = {'Message-ID': id,
+ msg_id = "NA-%s-%s" % (post.id, suffix_id)
+ orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
+ headers = {'Message-ID': msg_id,
'In-Reply-To': orig_id}
elif update == const.TYPE_ACTIVITY_UPDATE_QUESTION:
- id = "UQ-%s-%s-%s" % (post.id, post.last_edited_at, suffix_id)
- orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
- headers = {'Message-ID': id,
+ msg_id = "UQ-%s-%s-%s" % (post.id, post.last_edited_at, suffix_id)
+ orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
+ headers = {'Message-ID': msg_id,
'In-Reply-To': orig_id}
elif update == const.TYPE_ACTIVITY_COMMENT_QUESTION:
- id = "CQ-%s-%s" % (post.id, suffix_id)
- orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
- headers = {'Message-ID': id,
+ msg_id = "CQ-%s-%s" % (post.id, suffix_id)
+ orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
+ headers = {'Message-ID': msg_id,
'In-Reply-To': orig_id}
elif update == const.TYPE_ACTIVITY_UPDATE_ANSWER:
- id = "UA-%s-%s-%s" % (post.id, post.last_edited_at, suffix_id)
- orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
- headers = {'Message-ID': id,
+ msg_id = "UA-%s-%s-%s" % (post.id, post.last_edited_at, suffix_id)
+ orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
+ headers = {'Message-ID': msg_id,
'In-Reply-To': orig_id}
elif update == const.TYPE_ACTIVITY_COMMENT_ANSWER:
- id = "CA-%s-%s" % (post.id, suffix_id)
- orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
- headers = {'Message-ID': id,
+ msg_id = "CA-%s-%s" % (post.id, suffix_id)
+ orig_id = "NQ-%s-%s" % (orig_post.id, suffix_id)
+ headers = {'Message-ID': msg_id,
'In-Reply-To': orig_id}
else:
- # Unknown type -> Can't set headers
- return {}
+ # Unknown type -> Can't set headers
+ return {}
return headers
@@ -130,8 +138,132 @@ def mail_moderators(
try:
mail.send_mail(subject_line, body_text, from_email, recipient_list)
- pass
except smtplib.SMTPException, error:
logging.critical(unicode(error))
if raise_on_failure == True:
raise exceptions.EmailNotSent(unicode(error))
+
+ASK_BY_EMAIL_USAGE = _(
+"""<p>To ask by email, please:</p>
+<ul>
+ <li>Format the subject line as: [Tag1; Tag2] Question title</li>
+ <li>Type details of your question into the email body</li>
+</ul>
+<p>Note that tags may consist of more than one word, and tags
+may be separated by a semicolon or a comma</p>
+"""
+)
+
+def bounce_email(email, subject, reason = None, body_text = None):
+ """sends a bounce email at address ``email``, with the subject
+ line ``subject``, accepts several reasons for the bounce:
+
+ * ``'problem_posting'``, ``unknown_user`` and ``permission_denied``
+ * ``body_text`` in an optional parameter that allows to append
+ extra text to the message
+ """
+ if reason == 'problem_posting':
+ error_message = _(
+ '<p>Sorry, there was an error posting your question '
+ 'please contact the %(site)s administrator</p>'
+ ) % {'site': askbot_settings.APP_SHORT_NAME}
+ error_message = string_concat(error_message, ASK_BY_EMAIL_USAGE)
+ elif reason == 'unknown_user':
+ error_message = _(
+ '<p>Sorry, in order to post questions on %(site)s '
+ 'by email, please <a href="%(url)s">register first</a></p>'
+ ) % {
+ 'site': askbot_settings.APP_SHORT_NAME,
+ 'url': url_utils.get_login_url()
+ }
+ elif reason == 'permission_denied':
+ error_message = _(
+ '<p>Sorry, your question could not be posted '
+ 'due to insufficient privileges of your user account</p>'
+ )
+ else:
+ raise ValueError('unknown reason to bounce an email: "%s"' % reason)
+
+ if body_text != None:
+ error_message = string_concat(error_message, body_text)
+
+ #print 'sending email'
+ #print email
+ #print subject
+ #print error_message
+ send_mail(
+ recipient_list = (email,),
+ subject_line = 'Re: ' + subject,
+ body_text = error_message
+ )
+
+def process_attachments(attachments):
+ """saves file attachments and adds
+
+ cheap way of dealing with the attachments
+ just insert them inline, however it might
+ be useful to keep track of the uploaded files separately
+ and deal with them as with resources of their own value"""
+ if attachments:
+ content = ''
+ for att in attachments:
+ file_storage, file_name, file_url = store_file(att)
+ chunk = '[%s](%s) ' % (att.name, file_url)
+ file_extension = os.path.splitext(att.name)[1]
+ #todo: this is a hack - use content type
+ if file_extension.lower() in ('png', 'jpg', 'gif'):
+ chunk = '\n\n!' + chunk
+ content += '\n\n' + chunk
+ return content
+ else:
+ return ''
+
+
+
+def process_emailed_question(from_address, subject, body, attachments = None):
+ """posts question received by email or bounces the message"""
+ #a bunch of imports here, to avoid potential circular import issues
+ from askbot.forms import AskByEmailForm
+ from askbot.models import User
+ data = {
+ 'sender': from_address,
+ 'subject': subject,
+ 'body_text': body
+ }
+ form = AskByEmailForm(data)
+ if form.is_valid():
+ email_address = form.cleaned_data['email']
+ try:
+ user = User.objects.get(
+ email__iexact = email_address
+ )
+ except User.DoesNotExist:
+ bounce_email(email_address, subject, reason = 'unknown_user')
+ except User.MultipleObjectsReturned:
+ bounce_email(email_address, subject, reason = 'problem_posting')
+
+ tagnames = form.cleaned_data['tagnames']
+ title = form.cleaned_data['title']
+ body_text = form.cleaned_data['body_text']
+
+ try:
+ body_text += process_attachments(attachments)
+ user.post_question(
+ title = title,
+ tags = tagnames,
+ body_text = body_text
+ )
+ except PermissionDenied, error:
+ bounce_email(
+ email_address,
+ subject,
+ reason = 'permission_denied',
+ body_text = unicode(error)
+ )
+ else:
+ if email_address:
+ bounce_email(
+ email_address,
+ subject,
+ reason = 'problem_posting'
+ )