mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Cleanup.
This commit is contained in:
@@ -1,11 +1,47 @@
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from modoboa.lib import parameters, events
|
||||
|
||||
base_events = [
|
||||
"CanCreate",
|
||||
|
||||
"AccountCreated",
|
||||
"AccountAutoCreated",
|
||||
"AccountModified",
|
||||
"AccountDeleted",
|
||||
"AccountExported",
|
||||
"AccountImported",
|
||||
"PasswordUpdated",
|
||||
"ExtraAccountActions",
|
||||
"RoleChanged",
|
||||
"GetExtraRoles",
|
||||
"PasswordChange",
|
||||
|
||||
"UserMenuDisplay",
|
||||
"AdminMenuDisplay",
|
||||
"GetStaticContent",
|
||||
|
||||
"ExtEnabled",
|
||||
"ExtDisabled",
|
||||
|
||||
"UserLogin",
|
||||
"UserLogout",
|
||||
|
||||
"GetAnnouncement",
|
||||
|
||||
"TopNotifications",
|
||||
"ExtraAdminContent",
|
||||
|
||||
"ExtraUprefsRoutes",
|
||||
"ExtraUprefsJS"
|
||||
]
|
||||
|
||||
|
||||
def load_settings():
|
||||
from app_settings import GeneralParametersForm, UserSettings
|
||||
from .app_settings import GeneralParametersForm, UserSettings
|
||||
|
||||
parameters.register(GeneralParametersForm, ugettext_lazy("General"))
|
||||
parameters.register(UserSettings, ugettext_lazy("General"))
|
||||
events.declare(base_events)
|
||||
|
||||
|
||||
@events.observe("ExtDisabled")
|
||||
|
||||
+4
-5
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
import datetime
|
||||
from optparse import make_option
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
from modoboa.core import load_settings
|
||||
from modoboa.core.models import Log
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.models import Log
|
||||
from modoboa.admin import AdminConsole
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -38,8 +37,8 @@ class Command(BaseCommand):
|
||||
l.addHandler(logging.StreamHandler())
|
||||
self.verbose = options["verbose"]
|
||||
|
||||
AdminConsole().load()
|
||||
log_maximum_age = int(parameters.get_admin("LOG_MAXIMUM_AGE", app="admin"))
|
||||
load_settings()
|
||||
log_maximum_age = int(parameters.get_admin("LOG_MAXIMUM_AGE"))
|
||||
self.__vprint("Deleting logs older than %d days..." % log_maximum_age)
|
||||
limit = timezone.now() - datetime.timedelta(log_maximum_age)
|
||||
Log.objects.filter(date_created__lt=limit).delete()
|
||||
@@ -358,6 +358,26 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||
reversion.register(User)
|
||||
|
||||
|
||||
def populate_callback(user):
|
||||
"""Populate callback
|
||||
|
||||
If the LDAP authentication backend is in use, this callback will
|
||||
be called each time a new user authenticates succesfuly to
|
||||
Modoboa. This function is in charge of creating the mailbox
|
||||
associated to the provided ``User`` object.
|
||||
|
||||
:param user: a ``User`` instance
|
||||
"""
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
user.set_role("SimpleUsers")
|
||||
user.post_create(sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, user)
|
||||
events.raiseEvent("AccountAutoCreated", user)
|
||||
|
||||
|
||||
class ObjectAccess(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% load i18n libextras %}
|
||||
{% load i18n core_tags libextras %}
|
||||
<h2>{% trans "Information" %} <small>{% trans "Various information about Modoboa" %}</small></h2><hr>
|
||||
<div class="alert alert-info">Modoboa <strong>{% get_version %}</strong> {% trans "is currently in use." %}</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{% load i18n libextras %}{% load url from future %}
|
||||
{% url "modoboa.admin.views.saveparameters" as action %}
|
||||
<h2>{% trans "Parameters" %} <small>{% trans "Configure Modoboa" %}</small></h2><hr />
|
||||
<form id="settings_form" method="POST" action="{% url 'modoboa.core.views.admin.saveparameters' %}" class="form-horizontal">{% csrf_token %}
|
||||
{% include "common/parameters.html" %}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import re
|
||||
from django import template
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.sessions.models import Session
|
||||
from modoboa.lib import events
|
||||
|
||||
|
||||
@@ -160,3 +162,117 @@ def colorize_level(level):
|
||||
@register.filter
|
||||
def tohtml(message):
|
||||
return re.sub("'(.*?)'", "<strong>\g<1></strong>", message)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def visirule(field):
|
||||
if not hasattr(field.form, "visirules") or not field.html_name in field.form.visirules:
|
||||
return ""
|
||||
rule = field.form.visirules[field.html_name]
|
||||
return " data-visibility-field='%s' data-visibility-value='%s' " \
|
||||
% (rule["field"], rule["value"])
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_version():
|
||||
import pkg_resources
|
||||
return pkg_resources.get_distribution("modoboa").version
|
||||
|
||||
|
||||
class ConnectedUsers(template.Node):
|
||||
def __init__(self, varname):
|
||||
self.varname = varname
|
||||
|
||||
def render(self, context):
|
||||
from modoboa.core.models import User
|
||||
|
||||
sessions = Session.objects.filter(expire_date__gte=timezone.now())
|
||||
uid_list = []
|
||||
# Build a list of user ids from that query
|
||||
for session in sessions:
|
||||
data = session.get_decoded()
|
||||
uid = data.get('_auth_user_id', None)
|
||||
if uid:
|
||||
uid_list.append(uid)
|
||||
|
||||
# Query all logged in users based on id list
|
||||
context[self.varname] = []
|
||||
for uid in uid_list:
|
||||
try:
|
||||
context[self.varname].append(User.objects.get(pk=uid))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
return ''
|
||||
|
||||
|
||||
@register.tag
|
||||
def connected_users(parser, token):
|
||||
try:
|
||||
tag, a, varname = token.split_contents()
|
||||
except ValueError:
|
||||
raise template.TemplateSyntaxError(
|
||||
'connected_users usage: {% connected_users as users %}'
|
||||
)
|
||||
return ConnectedUsers(varname)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_modoboa_logo():
|
||||
import os
|
||||
from django.conf import settings
|
||||
|
||||
try:
|
||||
logo = settings.MODOBOA_CUSTOM_LOGO
|
||||
except AttributeError:
|
||||
logo = None
|
||||
if logo is None:
|
||||
return os.path.join(settings.STATIC_URL, "css/modoboa.png")
|
||||
return logo
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def extra_head_content(user):
|
||||
tpl = Template("{% for sc in static_content %}{{ sc|safe }}{% endfor %}")
|
||||
return tpl.render(
|
||||
Context(
|
||||
dict(static_content=events.raiseQueryEvent("GetStaticContent", user))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def load_optionalmenu(user):
|
||||
menu = events.raiseQueryEvent("UserMenuDisplay", "top_menu_middle", user)
|
||||
return template.loader.render_to_string(
|
||||
'common/menulist.html',
|
||||
{"entries" : menu, "user" : user}
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def load_notifications(user):
|
||||
content = events.raiseQueryEvent("TopNotifications", user)
|
||||
return "".join(content)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def display_messages(msgs):
|
||||
text = ""
|
||||
level = "info"
|
||||
for m in msgs:
|
||||
level = m.tags
|
||||
text += unicode(m) + "\\\n"
|
||||
|
||||
if level == "info":
|
||||
level = "success"
|
||||
timeout = "2000"
|
||||
else:
|
||||
timeout = "undefined"
|
||||
|
||||
return """
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('body').notify('%s', '%s', %s);
|
||||
});
|
||||
</script>
|
||||
""" % (level, text, timeout)
|
||||
|
||||
@@ -157,6 +157,32 @@ def import_account_mailbox(user, account, row):
|
||||
dom.add_admin(account)
|
||||
|
||||
|
||||
@events.observe("AccountAutoCreated")
|
||||
def account_auto_created(user):
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
try:
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
domain = Domain(name=domname, enabled=True, quota=0)
|
||||
domain.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, domain)
|
||||
try:
|
||||
mb = Mailbox.objects.get(domain=domain, address=localpart)
|
||||
except Mailbox.DoesNotExist:
|
||||
mb = Mailbox(
|
||||
address=localpart, domain=domain, user=user, use_domain_quota=True
|
||||
)
|
||||
mb.set_quota(override_rules=True)
|
||||
mb.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, mb)
|
||||
|
||||
|
||||
@events.observe("UserLogin")
|
||||
def user_logged_in(request, username, password):
|
||||
from modoboa.lib.cryptutils import encrypt
|
||||
|
||||
@@ -139,14 +139,6 @@ class Alias(DatesAware):
|
||||
total += len(self.extmboxes.split(','))
|
||||
return total + self.aliases.count() + self.mboxes.count()
|
||||
|
||||
def ui_disabled(self, user):
|
||||
if user.is_superuser:
|
||||
return False
|
||||
for mb in self.mboxes.all():
|
||||
if not user.is_owner(mb.domain):
|
||||
return True
|
||||
return False
|
||||
|
||||
def from_csv(self, user, row, expected_elements=5):
|
||||
"""Create a new alias from a CSV file entry
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{% load i18n %}{% load url from future %}
|
||||
<h2>{% trans "Extensions" %} <small>{% trans "Choose the extension(s) to use" %}</small></h2><hr />
|
||||
<form id="settings_form" method="POST" action="{% url 'modoboa.admin.views.saveextensions' %}">{% csrf_token %}
|
||||
{{ extensions|safe }}
|
||||
</form>
|
||||
<a href="#" class="btn btn-primary" id="update" name="update">{% trans 'Save' %}</a>
|
||||
@@ -1,17 +0,0 @@
|
||||
{% load i18n libextras %}
|
||||
<h2>{% trans "Information" %} <small>{% trans "Various information about Modoboa" %}</small></h2><hr>
|
||||
<div class="alert alert-info">Modoboa <strong>{% get_version %}</strong> {% trans "is currently in use." %}</div>
|
||||
|
||||
<h3>{% trans "Active users" %}</h3>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Group name" %}</th>
|
||||
<th>{% trans "Last login" %}</th>
|
||||
</tr>
|
||||
{% connected_users as users %}{% for u in users %}<tr>
|
||||
<td>{{ u.username }}</td>
|
||||
<td>{{ u.group }}</td>
|
||||
<td>{{ u.last_login }}</td>
|
||||
</tr>{% endfor %}
|
||||
</table>
|
||||
@@ -1,8 +0,0 @@
|
||||
{% load i18n libextras %}{% load url from future %}
|
||||
{% url "modoboa.admin.views.saveparameters" as action %}
|
||||
<h2>{% trans "Parameters" %} <small>{% trans "Configure Modoboa" %}</small></h2><hr />
|
||||
<form id="settings_form" method="POST" action="{% url 'modoboa.admin.views.saveparameters' %}" class="form-horizontal">{% csrf_token %}
|
||||
{% include "common/parameters.html" %}
|
||||
</form>
|
||||
<hr>
|
||||
<a href="#" class="btn btn-primary" id="update" name="update">{% trans 'Save' %}</a>
|
||||
@@ -177,82 +177,6 @@ def domadmin_actions(daid, domid):
|
||||
return render_actions(actions)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def loadadminextmenu(user):
|
||||
menu = events.raiseQueryEvent("AdminMenuDisplay", "admin_menu_box", user)
|
||||
return render_to_string('common/menulist.html',
|
||||
{"entries": menu, "user": user})
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def param(app, definition):
|
||||
name = "%s.%s" % (app, definition["name"])
|
||||
value = definition["value"] \
|
||||
if "value" in definition else definition["deflt"]
|
||||
|
||||
if definition["type"] == "separator":
|
||||
return "<h5>%s</h5>" % (unicode(definition["label"]))
|
||||
|
||||
extratags = ""
|
||||
if "visible_if" in definition:
|
||||
vfield, vvalue = definition["visible_if"].split("=")
|
||||
extratags = "data-visibility-field='%s.%s' data-visibility-value='%s'" \
|
||||
% (app, vfield, vvalue)
|
||||
|
||||
result = """<div class='control-group'%s>
|
||||
<label class="param-label" for="%s">%s</label>
|
||||
""" % (extratags, name,
|
||||
_(definition["label"]) if "label" in definition else definition["name"])
|
||||
|
||||
result += "<div class='param-controls'>"
|
||||
if definition["type"] in ["string", "int"]:
|
||||
result += """
|
||||
<input type='text' name='%s' id='%s' value='%s' />""" % (name, name, value)
|
||||
|
||||
if definition["type"] == "text":
|
||||
result += "<textarea name='%s' id='%s'>%s</textarea>" \
|
||||
% (name, name, value)
|
||||
|
||||
if definition["type"] == "list_yesno":
|
||||
for idx, v in enumerate([("yes", _("Yes")), ("no", _("No"))]):
|
||||
checked = "checked" if value == v[0] else ""
|
||||
result += """<label for="%(id)s" class="radio inline">
|
||||
<input type="radio" name="%(name)s" id="%(id)s" value="%(value)s"%(checked)s />
|
||||
%(label)s
|
||||
</label>
|
||||
""" % dict(id="%s_%d" % (name, idx), name=name, value=v[0],
|
||||
label=v[1], checked=checked)
|
||||
|
||||
if definition["type"] == "list":
|
||||
result += """
|
||||
<select name='%s' id='%s'>""" % (name, name)
|
||||
values = []
|
||||
if "values" in definition:
|
||||
values = definition["values"]
|
||||
for v in values:
|
||||
selected = ""
|
||||
if value == v[0]:
|
||||
selected = " selected='selected'"
|
||||
result += "<option value='%s'%s>%s</option>\n" % (v[0], selected, v[1].decode("utf-8"))
|
||||
result += """
|
||||
</select>
|
||||
"""
|
||||
|
||||
if "help" in definition:
|
||||
result += """<a class="help" rel="popover" data-content="%s" data-original-title="%s">
|
||||
<i class="icon-info-sign"></i>
|
||||
</a>""" % (unicode(definition["help"]), _("Help"))
|
||||
# result += """
|
||||
# <span class="help-block">%s</span>
|
||||
# """ % _(definition["help"])
|
||||
|
||||
result += """
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
return result
|
||||
|
||||
|
||||
@register.filter
|
||||
def gender(value, target):
|
||||
if value in genders:
|
||||
|
||||
@@ -60,7 +60,7 @@ def domains(request, tplname="admin/domains.html"):
|
||||
if not request.user.has_perm("admin.view_domains"):
|
||||
if request.user.has_perm("admin.view_mailboxes"):
|
||||
return HttpResponseRedirect(reverse(identities))
|
||||
return HttpResponseRedirect(reverse("modoboa.userprefs.views.index"))
|
||||
return HttpResponseRedirect(reverse("modoboa.core.views.user.index"))
|
||||
return render(request, tplname, {"selection": "domains"})
|
||||
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.emailutils import sendmail_simple
|
||||
from modoboa.admin.models import User, Domain
|
||||
from modoboa.extensions.admin.models import Domain
|
||||
from modoboa.extensions.amavis import Amavis
|
||||
from modoboa.extensions.amavis.models import (
|
||||
Msgrcpt
|
||||
@@ -68,13 +69,17 @@ class Command(BaseCommand):
|
||||
self.sender = parameters.get_admin("NOTIFICATIONS_SENDER",
|
||||
app="amavis")
|
||||
self.baseurl = self.options["baseurl"].strip("/")
|
||||
self.listingurl = self.baseurl + reverse("modoboa.extensions.amavis.views._listing") + "?viewrequests=1"
|
||||
self.listingurl = self.baseurl \
|
||||
+ reverse("modoboa.extensions.amavis.views._listing") \
|
||||
+ "?viewrequests=1"
|
||||
|
||||
for da in User.objects.filter(groups__name="DomainAdmins"):
|
||||
if not da.mailbox_set.count():
|
||||
continue
|
||||
rcpt = da.mailbox_set.all()[0].full_address
|
||||
reqs = get_wrapper().get_domains_pending_requests(da.get_domains())
|
||||
reqs = get_wrapper().get_domains_pending_requests(
|
||||
Domain.objects.get_for_admin(da)
|
||||
)
|
||||
if reqs.count():
|
||||
self.send_pr_notification(rcpt, reqs)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import string
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from optparse import make_option
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.admin.models import Domain
|
||||
from modoboa.extensions.admin.models import Domain
|
||||
from modoboa.extensions.stats import Stats
|
||||
from modoboa.extensions.stats.grapher import str2Time, Grapher
|
||||
from modoboa.extensions.stats.graph_templates import MailTraffic
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# coding: utf-8
|
||||
from modoboa.lib.webutils import ajax_response, _render_error
|
||||
from modoboa.extensions.webmail.exceptions import WebmailError, ImapError
|
||||
|
||||
|
||||
class WebmailErrorMiddleware(object):
|
||||
def process_exception(self, request, exception):
|
||||
if not isinstance(exception, (WebmailError, ImapError)):
|
||||
return None
|
||||
json = request.GET.get("json", False) if request.method == "GET" \
|
||||
else request.POST.get("json", False)
|
||||
if json:
|
||||
return ajax_response(request, status="ko",
|
||||
respmsg=str(exception), norefresh=True)
|
||||
return _render_error(request, user_context=dict(error=str(exception)))
|
||||
@@ -25,21 +25,22 @@ try:
|
||||
from django_auth_ldap.backend import (
|
||||
LDAPBackend as orig_LDAPBackend, _LDAPUser
|
||||
)
|
||||
from modoboa.admin.models import populate_callback
|
||||
from modoboa.core.models import populate_callback
|
||||
|
||||
class LDAPBackend(orig_LDAPBackend):
|
||||
|
||||
def get_or_create_user(self, username, ldap_user):
|
||||
"""
|
||||
This must return a (User, created) 2-tuple for the given LDAP user.
|
||||
username is the Django-friendly username of the user. ldap_user.dn is
|
||||
the user's DN and ldap_user.attrs contains all of their LDAP attributes.
|
||||
This must return a (User, created) 2-tuple for the given
|
||||
LDAP user. username is the Django-friendly username of
|
||||
the user. ldap_user.dn is the user's DN and
|
||||
ldap_user.attrs contains all of their LDAP attributes.
|
||||
"""
|
||||
lpart, domain = split_mailbox(username)
|
||||
if domain is None:
|
||||
return None
|
||||
user, created = User.objects.get_or_create(
|
||||
username__iexact=username,
|
||||
username__iexact=username,
|
||||
defaults={'username': username.lower(), 'is_local': False}
|
||||
)
|
||||
if created:
|
||||
@@ -50,7 +51,7 @@ try:
|
||||
user = None
|
||||
try:
|
||||
user = User.objects.get(pk=user_id)
|
||||
_LDAPUser(self, user=user) # This sets user.ldap_user
|
||||
_LDAPUser(self, user=user) # This sets user.ldap_user
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
return user
|
||||
|
||||
+22
-20
@@ -1,16 +1,16 @@
|
||||
# coding: utf-8
|
||||
|
||||
import time
|
||||
import re
|
||||
import lxml
|
||||
from django.template import Template, Context
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
import u2u_decode
|
||||
import smtplib
|
||||
from email.header import Header, decode_header
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import make_msgid, formatdate, parseaddr
|
||||
import time
|
||||
from django.template import Template, Context
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
import u2u_decode
|
||||
|
||||
|
||||
class EmailAddress(object):
|
||||
def __init__(self, address):
|
||||
@@ -18,14 +18,15 @@ class EmailAddress(object):
|
||||
(self.name, self.address) = parseaddr(self.fulladdress)
|
||||
if self.name == "":
|
||||
self.fulladdress = self.address
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.fulladdress
|
||||
|
||||
|
||||
class Email(object):
|
||||
def __init__(self, msg, mformat="plain", dformat="plain", links=0):
|
||||
self.attached_map = {}
|
||||
self.contents = {"html" : "", "plain" : ""}
|
||||
self.contents = {"html": "", "plain": ""}
|
||||
self.headers = []
|
||||
self.attachments = {}
|
||||
self.mformat = mformat
|
||||
@@ -34,7 +35,7 @@ class Email(object):
|
||||
|
||||
self.__parse(msg)
|
||||
|
||||
if not self.contents.has_key(mformat) or self.contents[mformat] == "":
|
||||
if not mformat in self.contents or self.contents[mformat] == "":
|
||||
# Fallback
|
||||
self.mformat = mformat == "html" and "plain" or "html"
|
||||
|
||||
@@ -68,7 +69,7 @@ class Email(object):
|
||||
target = "plain"
|
||||
else:
|
||||
target = msg.get_content_subtype()
|
||||
self.contents[target] += decode(msg.get_payload(decode=True),
|
||||
self.contents[target] += decode(msg.get_payload(decode=True),
|
||||
charset=msg.get_content_charset())
|
||||
|
||||
def _parse_image(self, msg, level):
|
||||
@@ -97,7 +98,7 @@ class Email(object):
|
||||
self.attached_map[cid] = re.match("^http:", fname) and fname \
|
||||
or self.__save_image(fname, msg)
|
||||
return
|
||||
self.__parse_default(msg, level)
|
||||
self.__parse_default(msg, level)
|
||||
|
||||
def __parse(self, msg, level=None):
|
||||
"""Recursive email parser
|
||||
@@ -121,7 +122,7 @@ class Email(object):
|
||||
cpt += 1
|
||||
return
|
||||
|
||||
if level is None:
|
||||
if level is None:
|
||||
level = "1"
|
||||
try:
|
||||
getattr(self, "_parse_%s" % msg.get_content_maintype())(msg, level)
|
||||
@@ -148,19 +149,17 @@ class Email(object):
|
||||
return path
|
||||
|
||||
def map_cid(self, url):
|
||||
import re
|
||||
|
||||
m = re.match(".*cid:(.+)", url)
|
||||
if m:
|
||||
if self.attached_map.has_key(m.group(1)):
|
||||
if m.group(1) in self.attached_map:
|
||||
return self.attached_map[m.group(1)]
|
||||
return url
|
||||
|
||||
def render_headers(self, **kwargs):
|
||||
return render_to_string("common/mailheaders.html", {
|
||||
"headers" : self.headers,
|
||||
})
|
||||
|
||||
"headers": self.headers,
|
||||
})
|
||||
|
||||
def viewmail_plain(self, content, **kwargs):
|
||||
return "<pre>%s</pre>" % content
|
||||
|
||||
@@ -181,6 +180,7 @@ class Email(object):
|
||||
body = re.sub("<(/?)body", lambda m: "<%sdiv" % m.group(1), body)
|
||||
return body
|
||||
|
||||
|
||||
def split_mailbox(mailbox):
|
||||
"""Tries to split a mailbox in two parts (local part and domain name)
|
||||
|
||||
@@ -199,6 +199,7 @@ def split_mailbox(mailbox):
|
||||
address = "@".join(parts[:-1])
|
||||
return (address, domain)
|
||||
|
||||
|
||||
def decode(s, encodings=('utf8', 'latin1', 'windows-1252', 'ascii'), charset=None):
|
||||
if charset is not None:
|
||||
try:
|
||||
@@ -270,6 +271,7 @@ def __sendmail(sender, rcpt, msgstring, server='localhost', port=25):
|
||||
return False, "SMTP error: %s" % str(e)
|
||||
return True, None
|
||||
|
||||
|
||||
def sendmail_simple(sender, rcpt, subject="Sample message", content="", **kwargs):
|
||||
"""Simple way to send a text message
|
||||
|
||||
@@ -288,9 +290,10 @@ def sendmail_simple(sender, rcpt, subject="Sample message", content="", **kwargs
|
||||
set_email_headers(msg, subject, sender, rcpt)
|
||||
return __sendmail(sender, rcpt, msg.as_string(), **kwargs)
|
||||
|
||||
|
||||
def sendmail_fromfile(sender, rcpt, fname):
|
||||
"""Send a message contained within a file
|
||||
|
||||
|
||||
The given file name must represent a valid message structure. It
|
||||
must not include the From: and To: headers, they are automatically
|
||||
added by the function.
|
||||
@@ -312,4 +315,3 @@ To: %s
|
||||
fp.close()
|
||||
|
||||
return __sendmail(sender, rcpt, content)
|
||||
|
||||
|
||||
+1
-33
@@ -13,39 +13,7 @@ import inspect
|
||||
import re
|
||||
from django.conf import settings
|
||||
|
||||
events = [
|
||||
"CanCreate",
|
||||
|
||||
"AccountCreated",
|
||||
"AccountModified",
|
||||
"AccountDeleted",
|
||||
"AccountExported",
|
||||
"AccountImported",
|
||||
"PasswordUpdated",
|
||||
"ExtraAccountActions",
|
||||
"RoleChanged",
|
||||
"GetExtraRoles",
|
||||
"PasswordChange",
|
||||
|
||||
"UserMenuDisplay",
|
||||
"AdminMenuDisplay",
|
||||
"GetStaticContent",
|
||||
|
||||
"ExtEnabled",
|
||||
"ExtDisabled",
|
||||
|
||||
"UserLogin",
|
||||
"UserLogout",
|
||||
|
||||
"GetAnnouncement",
|
||||
|
||||
"TopNotifications",
|
||||
"ExtraAdminContent",
|
||||
|
||||
"ExtraUprefsRoutes",
|
||||
"ExtraUprefsJS"
|
||||
]
|
||||
|
||||
events = []
|
||||
callbacks = {}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# coding: utf-8
|
||||
import os
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
from django import template
|
||||
from django.contrib import messages
|
||||
from django.template import Template, Context
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.sessions.models import Session
|
||||
from datetime import datetime
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
@@ -17,10 +13,6 @@ from modoboa.lib.formutils import SeparatorField
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag
|
||||
def get_version():
|
||||
import pkg_resources
|
||||
return pkg_resources.get_distribution("modoboa").version
|
||||
|
||||
@register.simple_tag
|
||||
def join(items, sep=','):
|
||||
@@ -31,53 +23,11 @@ def join(items, sep=','):
|
||||
res += "%s : '%s'" % (k, v)
|
||||
return res
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def tolist(values):
|
||||
return "[%s]" % ",".join(map(lambda v: '"%s"' % v, values))
|
||||
return "[%s]" % ",".join(['"%s"' % v for v in values])
|
||||
|
||||
@register.simple_tag
|
||||
def display_messages(msgs):
|
||||
from django.contrib import messages
|
||||
|
||||
text = ""
|
||||
level = "info"
|
||||
for m in msgs:
|
||||
level = m.tags
|
||||
text += unicode(m) + "\\\n"
|
||||
|
||||
if level == "info":
|
||||
level = "success"
|
||||
timeout = "2000"
|
||||
else:
|
||||
timeout = "undefined"
|
||||
|
||||
return """
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('body').notify('%s', '%s', %s);
|
||||
});
|
||||
</script>
|
||||
""" % (level, text, timeout)
|
||||
|
||||
@register.simple_tag
|
||||
def extra_head_content(user):
|
||||
tpl = Template("{% for sc in static_content %}{{ sc|safe }}{% endfor %}")
|
||||
return tpl.render(
|
||||
Context(
|
||||
dict(static_content=events.raiseQueryEvent("GetStaticContent", user))
|
||||
)
|
||||
)
|
||||
|
||||
@register.simple_tag
|
||||
def load_optionalmenu(user):
|
||||
menu = events.raiseQueryEvent("UserMenuDisplay", "top_menu_middle", user)
|
||||
return template.loader.render_to_string('common/menulist.html',
|
||||
{"entries" : menu, "user" : user})
|
||||
|
||||
@register.simple_tag
|
||||
def load_notifications(user):
|
||||
content = events.raiseQueryEvent("TopNotifications", user)
|
||||
return "".join(content)
|
||||
|
||||
@register.simple_tag
|
||||
def render_form(form, tpl=None):
|
||||
@@ -89,20 +39,25 @@ def render_form(form, tpl=None):
|
||||
ret += "%s\n" % render_field(field)
|
||||
return ret
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_field(field, help_display_mode="tooltip"):
|
||||
from modoboa.core.templatetags.core_tags import visirule
|
||||
|
||||
if type(field.form.fields[field.name]) is SeparatorField:
|
||||
return "<h5%s>%s</h5>" % (visirule(field), unicode(field.label))
|
||||
|
||||
return render_to_string("common/generic_field.html", dict(
|
||||
field=field, help_display_mode=help_display_mode
|
||||
))
|
||||
field=field, help_display_mode=help_display_mode
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_field_appended(field, text):
|
||||
return render_to_string("common/generic_field.html", dict(
|
||||
field=field, help_display_mode="tooltip", appended_text=text
|
||||
))
|
||||
field=field, help_display_mode="tooltip", appended_text=text
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_field_with_activator(field, activator_label=ugettext_lazy("activate")):
|
||||
@@ -111,11 +66,13 @@ def render_field_with_activator(field, activator_label=ugettext_lazy("activate")
|
||||
"activator_label": activator_label, "deactivate_if_empty": True
|
||||
})
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_and_hide_field(field):
|
||||
return render_to_string("common/generic_field.html", dict(
|
||||
field=field, hidden=True
|
||||
))
|
||||
field=field, hidden=True
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_fields_group(form, pattern):
|
||||
@@ -128,7 +85,7 @@ def render_fields_group(form, pattern):
|
||||
haserror = len(first.errors) != 0
|
||||
while True:
|
||||
fname = "%s_%d" % (pattern, cpt)
|
||||
if not form.fields.has_key(fname):
|
||||
if not fname in form.fields:
|
||||
break
|
||||
bfield = forms.BoundField(form, form.fields[fname], fname)
|
||||
if len(bfield.errors):
|
||||
@@ -137,14 +94,16 @@ def render_fields_group(form, pattern):
|
||||
cpt += 1
|
||||
|
||||
return render_to_string("common/generic_fields_group.html", dict(
|
||||
label=label, help_text=first.help_text, group=group, haserror=haserror
|
||||
))
|
||||
label=label, help_text=first.help_text, group=group, haserror=haserror
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def pagination_bar(page):
|
||||
return render_to_string("common/pagination_bar.html", dict(
|
||||
page=page, baseurl="?"
|
||||
))
|
||||
page=page, baseurl="?"
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def alert(msg, typ):
|
||||
@@ -154,6 +113,7 @@ def alert(msg, typ):
|
||||
</div>""")
|
||||
return t.render(Context(dict(type=typ, msg=msg)))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_link(linkdef, mdclass=""):
|
||||
t = Template("""<a href="{{ link.url }}" name="{{ link.name }}" title="{{ link.title }}"
|
||||
@@ -166,6 +126,7 @@ class="{{ mdclass }}{% if link.class %} {{ link.class }}{% endif %}"
|
||||
{{ link.label }}</a>""")
|
||||
return t.render(Context(dict(link=linkdef, mdclass=mdclass)))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def progress_color(value):
|
||||
value = int(value)
|
||||
@@ -175,54 +136,12 @@ def progress_color(value):
|
||||
return "progress-warning"
|
||||
return "progress-danger"
|
||||
|
||||
|
||||
@register.filter
|
||||
def fromunix(value):
|
||||
return datetime.datetime.fromtimestamp(int(value))
|
||||
return datetime.fromtimestamp(int(value))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def visirule(field):
|
||||
if not hasattr(field.form, "visirules") or not field.html_name in field.form.visirules:
|
||||
return ""
|
||||
rule = field.form.visirules[field.html_name]
|
||||
return " data-visibility-field='%s' data-visibility-value='%s' " \
|
||||
% (rule["field"], rule["value"])
|
||||
|
||||
|
||||
class ConnectedUsers(template.Node):
|
||||
def __init__(self, varname):
|
||||
self.varname = varname
|
||||
|
||||
def render(self, context):
|
||||
from modoboa.core.models import User
|
||||
|
||||
sessions = Session.objects.filter(expire_date__gte=timezone.now())
|
||||
uid_list = []
|
||||
|
||||
# Build a list of user ids from that query
|
||||
for session in sessions:
|
||||
data = session.get_decoded()
|
||||
uid = data.get('_auth_user_id', None)
|
||||
if uid:
|
||||
uid_list.append(uid)
|
||||
|
||||
# Query all logged in users based on id list
|
||||
context[self.varname] = []
|
||||
for uid in uid_list:
|
||||
try:
|
||||
context[self.varname].append(User.objects.get(pk=uid))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
return ''
|
||||
|
||||
@register.tag
|
||||
def connected_users(parser, token):
|
||||
try:
|
||||
tag, a, varname = token.split_contents()
|
||||
except ValueError:
|
||||
raise template.TemplateSyntaxError('connected_users usage: {% connected_users as users %}')
|
||||
return ConnectedUsers(varname)
|
||||
|
||||
@register.simple_tag
|
||||
def render_tags(tags):
|
||||
t = Template("""{% for tag in tags %}
|
||||
@@ -231,16 +150,4 @@ def render_tags(tags):
|
||||
</span>
|
||||
{% endfor %}
|
||||
""")
|
||||
return t.render(Context({"tags" : tags}))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_modoboa_logo():
|
||||
try:
|
||||
logo = settings.MODOBOA_CUSTOM_LOGO
|
||||
except AttributeError:
|
||||
logo = None
|
||||
if logo is None:
|
||||
return os.path.join(settings.STATIC_URL, "css/modoboa.png")
|
||||
return logo
|
||||
|
||||
return t.render(Context({"tags": tags}))
|
||||
|
||||
@@ -41,14 +41,6 @@ def render_actions(actions):
|
||||
return t.render(template.Context(dict(actions=actions)))
|
||||
|
||||
|
||||
def _ctx_ok(url):
|
||||
return {"status": "ok", "url": url}
|
||||
|
||||
|
||||
def _ctx_ko(tpl, ctx):
|
||||
return {"status": "ko", "content": render_to_string(tpl, ctx)}
|
||||
|
||||
|
||||
def getctx(status, level=1, callback=None, **kwargs):
|
||||
if not callback:
|
||||
callername = sys._getframe(level).f_code.co_name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% load i18n libextras %}
|
||||
{% load i18n core_tags libextras %}
|
||||
<div class="control-group {% if field.errors %}error{% endif %}"{% visirule field %}{% if hidden %}style="display: none"{% endif %}>
|
||||
{% if field.label %}<label class="control-label" for="id_{{ field.html_name }}">{{ field.label }}</label>{% endif %}{% if help_display_mode == "tooltip" and field.help_text %}<a href="#" class="help pull-left" data-original-title="{% trans 'Help:' %} {{ field.label }}" data-content="{{ field.help_text }}" rel="popover">?</a>{% endif %}
|
||||
<div class="controls spaced">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>{% load i18n libextras %}{% load url from future %}
|
||||
<!DOCTYPE html>{% load i18n core_tags libextras %}{% load url from future %}
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
@@ -8,6 +8,7 @@ from setuptools import setup, find_packages
|
||||
ROOT = os.path.dirname(__file__)
|
||||
PIP_REQUIRES = os.path.join(ROOT, "requirements.txt")
|
||||
|
||||
|
||||
def parse_requirements(*filenames):
|
||||
"""
|
||||
We generate our install_requires from the pip-requires and test-requires
|
||||
@@ -31,6 +32,7 @@ def parse_requirements(*filenames):
|
||||
requirements.append(line)
|
||||
return requirements
|
||||
|
||||
|
||||
def parse_dependency_links(*filenames):
|
||||
"""
|
||||
We generate our dependency_links from the pip-requires and test-requires
|
||||
@@ -46,28 +48,30 @@ def parse_dependency_links(*filenames):
|
||||
dependency_links.append(line)
|
||||
return dependency_links
|
||||
|
||||
|
||||
def read(fname):
|
||||
return open(os.path.join(ROOT, fname)).read()
|
||||
|
||||
setup(name="modoboa",
|
||||
version='1.0.1',
|
||||
url='http://modoboa.org/',
|
||||
license='MIT',
|
||||
description="Mail hosting made simple",
|
||||
long_description=read('README.rst'),
|
||||
author='Antoine Nguyen',
|
||||
author_email='tonio@ngyn.org',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
scripts=["modoboa/bin/modoboa-admin.py"],
|
||||
zip_safe=False,
|
||||
install_requires=parse_requirements(PIP_REQUIRES),
|
||||
dependency_links=parse_dependency_links(PIP_REQUIRES),
|
||||
classifiers=['Development Status :: 4 - Beta',
|
||||
'Framework :: Django',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Internet :: WWW/HTTP']
|
||||
setup(
|
||||
name="modoboa",
|
||||
version='1.0.1',
|
||||
url='http://modoboa.org/',
|
||||
license='MIT',
|
||||
description="Mail hosting made simple",
|
||||
long_description=read('README.rst'),
|
||||
author='Antoine Nguyen',
|
||||
author_email='tonio@ngyn.org',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
scripts=["bin/modoboa-admin.py"],
|
||||
zip_safe=False,
|
||||
install_requires=parse_requirements(PIP_REQUIRES),
|
||||
dependency_links=parse_dependency_links(PIP_REQUIRES),
|
||||
classifiers=['Development Status :: 4 - Beta',
|
||||
'Framework :: Django',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Internet :: WWW/HTTP']
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user