This commit is contained in:
Antoine Nguyen
2013-10-09 15:13:52 +02:00
parent fca83ba439
commit eb43ee2c0a
27 changed files with 299 additions and 354 deletions
+37 -1
View File
@@ -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")
@@ -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()
+20
View File
@@ -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 -1
View File
@@ -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" %}
+116
View File
@@ -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)
+26
View File
@@ -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
-8
View File
@@ -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:
+1 -1
View File
@@ -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
-15
View File
@@ -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)))
+7 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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 = {}
+28 -121
View File
@@ -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}))
-8
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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" />
+25 -21
View File
@@ -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']
)