mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Compose, Reply, Reply all.
New paramaters to configure connections to IMAP and SMTP servers.
This commit is contained in:
@@ -57,7 +57,7 @@ AND quarantine.chunk_ind=1
|
||||
ORDER BY msgs.time_num DESC
|
||||
""" % self.filter
|
||||
|
||||
def messages_count(self, folder=None):
|
||||
def messages_count(self, **kwargs):
|
||||
return self.count
|
||||
|
||||
def fetch(self, start=None, stop=None, **kwargs):
|
||||
|
||||
@@ -4,6 +4,6 @@ from django.utils.translation import ugettext as _
|
||||
class ComposeMailForm(forms.Form):
|
||||
from_ = forms.CharField(label=_("From"))
|
||||
to = forms.CharField(label=_("To"), widget=forms.Textarea())
|
||||
cc = forms.CharField(label=_("Cc"), widget=forms.Textarea())
|
||||
cc = forms.CharField(label=_("Cc"), widget=forms.Textarea(), required=False)
|
||||
subject = forms.CharField(label=_("Subject"), max_length=255)
|
||||
|
||||
|
||||
@@ -31,21 +31,36 @@ class WMtable(tables.Table):
|
||||
except AttributeError:
|
||||
return value
|
||||
|
||||
class IMAPheader(object):
|
||||
addr_exp = re.compile("([^<\(]+)<|\(([^>\)]+)>|\)")
|
||||
name_exp = re.compile("\"(.+)\"")
|
||||
class EmailAddress(object):
|
||||
addr_exp = re.compile("([^<\(]+)[<(]([^>)]+)[>)]")
|
||||
name_exp = re.compile('"([^"]+)"')
|
||||
|
||||
def __init__(self, address):
|
||||
self.fulladdress = address
|
||||
self.name = self.address = None
|
||||
m = EmailAddress.addr_exp.match(self.fulladdress)
|
||||
if m is None:
|
||||
return
|
||||
name = m.group(1).strip()
|
||||
self.address = m.group(2).strip()
|
||||
m2 = EmailAddress.name_exp.match(name)
|
||||
if m2:
|
||||
name = m2.group(1)
|
||||
self.name = ""
|
||||
for part in decode_header(name):
|
||||
if self.name != "":
|
||||
self.name += " "
|
||||
if part[1] is None:
|
||||
self.name += part[0]
|
||||
else:
|
||||
self.name += unicode(part[0], part[1])
|
||||
self.fulladdress = "%s <%s>" % (self.name, self.address)
|
||||
|
||||
class IMAPheader(object):
|
||||
@staticmethod
|
||||
def parse_address(value):
|
||||
m = IMAPheader.addr_exp.match(value)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
name = name.strip()
|
||||
m2 = IMAPheader.name_exp.match(name)
|
||||
if m2:
|
||||
return IMAPheader.parse_subject(m2.group(1))
|
||||
return IMAPheader.parse_subject(name)
|
||||
return value
|
||||
addr = EmailAddress(value)
|
||||
return addr.name and addr.name or value
|
||||
|
||||
@staticmethod
|
||||
def parse_address_list(values):
|
||||
@@ -126,16 +141,27 @@ class IMAPconnector(object):
|
||||
return "INBOX"
|
||||
return folder.encode("imap4-utf-7")
|
||||
|
||||
def messages_count(self, folder=None, order=None):
|
||||
if order:
|
||||
sign = order[:1]
|
||||
criterion = order[1:].upper()
|
||||
def messages_count(self, **kwargs):
|
||||
"""An enhanced version of messages_count
|
||||
|
||||
With IMAP, to know how many messages a mailbox contains, we
|
||||
have to make a request to the server. To avoid requests
|
||||
multiplications, we sort messages in the same time. This will
|
||||
be usefull for other methods.
|
||||
|
||||
:param order: sorting order
|
||||
:param folder: mailbox to scan
|
||||
"""
|
||||
if "order" in kwargs.keys() and kwargs["order"]:
|
||||
sign = kwargs["order"][:1]
|
||||
criterion = kwargs["order"][1:].upper()
|
||||
if sign == '-':
|
||||
criterion = "REVERSE %s" % criterion
|
||||
else:
|
||||
criterion = "REVERSE DATE"
|
||||
print criterion
|
||||
|
||||
folder = kwargs.has_key("folder") and kwargs["folder"] or None
|
||||
(status, data) = self.m.select(self._encodefolder(folder))
|
||||
(status, data) = self.m.sort("(%s)" % criterion, "UTF-8", "(NOT DELETED)")
|
||||
self.messages = data[0].split()
|
||||
@@ -210,9 +236,8 @@ class ImapListing(EmailListing):
|
||||
tpl = "webmail/index.html"
|
||||
tbltype = WMtable
|
||||
|
||||
def __init__(self, user, password, baseurl=None, navparams={}, **kwargs):
|
||||
def __init__(self, user, password, baseurl=None, **kwargs):
|
||||
self.mbc = IMAPconnector(user=user, password=password)
|
||||
self.navparams = navparams
|
||||
EmailListing.__init__(self, **kwargs)
|
||||
if baseurl:
|
||||
self.paginator.baseurl = baseurl
|
||||
@@ -232,7 +257,7 @@ class ImapEmail(Email):
|
||||
def __init__(self, msg, **kwargs):
|
||||
Email.__init__(self, msg, **kwargs)
|
||||
|
||||
fields = ["Subject", "From", "To", "Cc", "Date"]
|
||||
fields = ["Subject", "From", "To", "Reply-To", "Cc", "Date"]
|
||||
for f in fields:
|
||||
label = f
|
||||
if not f in msg.keys():
|
||||
|
||||
+61
-23
@@ -22,13 +22,18 @@ def init():
|
||||
parameters.register("webmail", "IMAP_SERVER", "string", "127.0.0.1",
|
||||
help=_("Address of your IMAP server"))
|
||||
parameters.register("webmail", "IMAP_SECURED", "list_yesno", "no",
|
||||
help=_("Use a secure connexion to access IMAP server"))
|
||||
help=_("Use a secured connection to access IMAP server"))
|
||||
parameters.register("webmail", "IMAP_PORT", "int", "143",
|
||||
help=_("Listening port of your IMAP server"))
|
||||
parameters.register("webmail", "SMTP_SERVER", "string", "127.0.0.1",
|
||||
help=_("Address of your SMTP server"))
|
||||
parameters.register("webmail", "SMTP_PORT", "int", "25",
|
||||
help=_("Listening port of your SMTP server"))
|
||||
parameters.register("webmail", "SMTP_AUTHENTICATION", "list_yesno", "no",
|
||||
help=_("Server needs authentication"))
|
||||
parameters.register("webmail", "SMTP_SECURED", "list_yesno", "no",
|
||||
help=_("Use a secured connection to access SMTP server"))
|
||||
|
||||
|
||||
def urls():
|
||||
return (r'^mailng/webmail/',
|
||||
@@ -57,7 +62,7 @@ def getctx(status, level=1, **kwargs):
|
||||
def folder(request, name, updatenav=True):
|
||||
if not name:
|
||||
name = "INBOX"
|
||||
order = request.GET.has_key("order") and request.GET["order"] or None
|
||||
order = request.GET.has_key("order") and request.GET["order"] or "-date"
|
||||
if updatenav:
|
||||
pageid = request.GET.has_key("page") and int(request.GET["page"]) or 1
|
||||
if not "navparams" in request.session.keys():
|
||||
@@ -183,23 +188,40 @@ def render_compose(request, form, bodyheader=None, body=None):
|
||||
ctx = getctx("ok", level=2, menu=menu, listing=content)
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
def send_mail(request):
|
||||
form = ComposeMailForm(request.POST)
|
||||
if form.is_valid():
|
||||
from email.mime.text import MIMEText
|
||||
import smtplib
|
||||
|
||||
msg = MIMEText(request.POST["id_body"].encode("utf-8"),
|
||||
_charset="utf-8")
|
||||
msg["Subject"] = request.POST["subject"]
|
||||
msg["From"] = request.POST["from_"]
|
||||
msg["To"] = request.POST["to"]
|
||||
rcpts = msg['To'].split(',')
|
||||
if "cc" in request.POST.keys():
|
||||
msg["Cc"] = request.POST["cc"]
|
||||
rcpts += msg["Cc"].split(",")
|
||||
try:
|
||||
s = smtplib.SMTP(parameters.get("webmail", "SMTP_SERVER"))
|
||||
if parameters.get("webmail", "SMTP_SECURED") == "yes":
|
||||
s.starttls()
|
||||
except (smtplib.SMTPException, ssl.SSLError), error:
|
||||
print error
|
||||
|
||||
if parameters.get("webmail", "SMTP_AUTHENTICATION") == "yes":
|
||||
s.login(request.user.username, request.session["password"])
|
||||
s.sendmail(msg['From'], rcpts, msg.as_string())
|
||||
s.quit()
|
||||
ctx = getctx("ok", url="%s?page=%s" % (request.session["folder"],
|
||||
request.session["page"]))
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
def compose(request):
|
||||
if request.method == "POST":
|
||||
form = ComposeMailForm(request.POST)
|
||||
if form.is_valid():
|
||||
from email.mime.text import MIMEText
|
||||
import smtplib
|
||||
|
||||
msg = MIMEText(request.POST["id_body"].encode("utf-8"),
|
||||
_charset="utf-8")
|
||||
msg["Subject"] = request.POST["subject"]
|
||||
msg["From"] = request.POST["from_"]
|
||||
msg["to"] = request.POST["to"]
|
||||
s = smtplib.SMTP(parameters.get("webmail", "SMTP_SERVER"))
|
||||
s.sendmail(msg['From'], [msg['To']], msg.as_string())
|
||||
s.quit()
|
||||
return folder(request, request.session["navparams"]["folder"], False)
|
||||
return send_mail(request)
|
||||
|
||||
form = ComposeMailForm()
|
||||
form.fields["from_"].initial = request.user.username
|
||||
@@ -208,7 +230,7 @@ def compose(request):
|
||||
@login_required
|
||||
def reply(request, folder, mail_id):
|
||||
if request.method == "POST":
|
||||
return
|
||||
return send_mail(request)
|
||||
msg = fetchmail(request, folder, mail_id, True)
|
||||
email = ImapEmail(msg)
|
||||
lines = email.body.split('\n')
|
||||
@@ -219,11 +241,27 @@ def reply(request, folder, mail_id):
|
||||
body += ">%s" % l
|
||||
form = ComposeMailForm()
|
||||
form.fields["from_"].initial = request.user.username
|
||||
form.fields["to"].initial = msg["From"]
|
||||
m = re.match("re\s*:\s*.+", msg["Subject"].lower())
|
||||
if m:
|
||||
form.fields["subject"].initial = msg["Subject"]
|
||||
if not "Reply-To" in msg.keys():
|
||||
form.fields["to"].initial = msg["From"]
|
||||
else:
|
||||
form.fields["subject"].initial = "Re: %s" % msg["Subject"]
|
||||
textheader = msg["From"] + " " + _("wrote:")
|
||||
form.fields["to"].initial = msg["Reply-To"]
|
||||
if request.GET.has_key("all"):
|
||||
form.fields["cc"].initial = ""
|
||||
toparse = msg["To"].split(",")
|
||||
if "Cc" in msg.keys():
|
||||
toparse += msg["Cc"].split(",")
|
||||
for addr in toparse:
|
||||
tmp = EmailAddress(addr)
|
||||
if tmp.address and tmp.address == request.user.username:
|
||||
continue
|
||||
if form.fields["cc"].initial != "":
|
||||
form.fields["cc"].initial += ", "
|
||||
form.fields["cc"].initial += tmp.fulladdress
|
||||
subject = IMAPheader.parse_subject(msg["Subject"])
|
||||
m = re.match("re\s*:\s*.+", subject.lower())
|
||||
if m:
|
||||
form.fields["subject"].initial = subject
|
||||
else:
|
||||
form.fields["subject"].initial = "Re: %s" % subject
|
||||
textheader = EmailAddress(msg["From"]).fulladdress + " " + _("wrote:")
|
||||
return render_compose(request, form, textheader, body)
|
||||
|
||||
@@ -43,8 +43,13 @@
|
||||
<div id="mailheader">
|
||||
{% include "common/genericform.html" %}
|
||||
</div>
|
||||
{% if body %}
|
||||
<textarea id="id_body" name="id_body">
|
||||
{% if bodyheader %}{{ bodyheader }}{% endif %}
|
||||
{% if body %}{{ body }}{% endif %}</textarea>
|
||||
{{ body }}
|
||||
</textarea>
|
||||
{% else %}
|
||||
<textarea id="id_body" name="id_body"></textarea>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -160,6 +160,7 @@ tr.selected {
|
||||
updatelisting(resp, "hidden");
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
$$("a[name=reply]").addEvent("click", reply);
|
||||
$$("a[name=replyall]").addEvent("click", replyall);
|
||||
$$("a[name=delete]").addEvent("click", function(event) {
|
||||
var lnk = event.target;
|
||||
|
||||
@@ -173,16 +174,16 @@ tr.selected {
|
||||
compose_callback = function(resp) {
|
||||
updatelisting(resp);
|
||||
window.addEvent("resize", function(evt) {
|
||||
var bodysize = $("listing").getSize().y - $("mailheader").getSize().y;
|
||||
$("id_body").setStyle("height", bodysize + "px");
|
||||
setDivHeight("id_body", $("mailheader").getSize().y, 0);
|
||||
});
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
$$("a[name=sendmail]").addEvent("click", function(evt) {
|
||||
evt.stop();
|
||||
$("composemail").set("send", {
|
||||
onSuccess: function(res) {
|
||||
updatelisting(JSON.decode(res));
|
||||
$$("tr").addEvent("dblclick", viewmail);
|
||||
onSuccess: function(resp) {
|
||||
resp = JSON.decode(resp);
|
||||
current_anchor.parse_string(resp.url, true).setparams(navparams);
|
||||
current_anchor.update();
|
||||
}
|
||||
});
|
||||
$("composemail").send();
|
||||
@@ -201,6 +202,11 @@ tr.selected {
|
||||
location.hash += event.target.get("href");
|
||||
}
|
||||
register_callback("reply", compose_callback);
|
||||
replyall = function(event) {
|
||||
event.stop();
|
||||
location.hash += event.target.get("href") + "?all=1";
|
||||
}
|
||||
register_callback("replyall", compose_callback);
|
||||
|
||||
loadFolder = function(event, obj) {
|
||||
if (!$defined(obj)) {
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
#emailheaders a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
pre {
|
||||
font-size: medium;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -34,8 +34,8 @@ def viewm_menu(selection, session, mail_id, perms):
|
||||
"url" : "reply/",
|
||||
"img" : "/static/pics/reply.png",
|
||||
"label" : _("Reply")},
|
||||
{"name" : "reply-all",
|
||||
"url" : "",
|
||||
{"name" : "replyall",
|
||||
"url" : "reply/",
|
||||
"img" : "/static/pics/reply-all.png",
|
||||
"label" : _("Reply all")},
|
||||
{"name" : "forward",
|
||||
|
||||
@@ -17,6 +17,9 @@ class MBconnector(object):
|
||||
self.address = address
|
||||
self.port = port
|
||||
|
||||
def messages_count(self, **kwargs):
|
||||
pass
|
||||
|
||||
class Page(object):
|
||||
def __init__(self, pageid, id_start, id_stop, items,
|
||||
items_per_page, has_previous, has_next,
|
||||
@@ -80,11 +83,13 @@ class Paginator(object):
|
||||
return p
|
||||
|
||||
class EmailListing:
|
||||
def __init__(self, folder=None, elems_per_page=40, **kwargs):
|
||||
def __init__(self, folder=None, elems_per_page=40, navparams={}, **kwargs):
|
||||
self.folder = folder
|
||||
self.elems_per_page = elems_per_page
|
||||
self.navparams = navparams
|
||||
order = "order" in kwargs.keys() and kwargs["order"] or None
|
||||
self.paginator = Paginator(self.mbc.messages_count(self.folder, order),
|
||||
self.paginator = Paginator(self.mbc.messages_count(folder=self.folder,
|
||||
order=order),
|
||||
elems_per_page)
|
||||
|
||||
def render_navbar(self, page):
|
||||
@@ -212,3 +217,4 @@ class Email(object):
|
||||
body = re.sub("<(/?)body", lambda m: "<%sdiv" % m.group(1), body)
|
||||
body = Template(decode(body)).render({})
|
||||
return (False, body)
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 629 B |
@@ -32,7 +32,7 @@
|
||||
{% loadextmenu perms %}
|
||||
{% if user.is_superuser or perms.admin.view_domains %}
|
||||
<li class="dropdown topdropdown">
|
||||
<img src="/static/pics/settings.png" border="0" />
|
||||
<img src="/static/pics/admin.png" border="0" />
|
||||
<label>{% trans "Admin" %}</label>
|
||||
<ul class="links">
|
||||
<li>
|
||||
|
||||
Reference in New Issue
Block a user