mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
@@ -230,18 +230,6 @@ def get_modoboa_logo():
|
||||
return logo
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def extra_head_content(user):
|
||||
tpl = template.Template(
|
||||
"{% for sc in static_content %}{{ sc|safe }}{% endfor %}"
|
||||
)
|
||||
return tpl.render(
|
||||
template.Context({
|
||||
'static_content': events.raiseQueryEvent("GetStaticContent", user)
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def load_optionalmenu(user):
|
||||
menu = events.raiseQueryEvent("UserMenuDisplay", "top_menu_middle", user)
|
||||
|
||||
@@ -43,12 +43,13 @@ def password_change(user):
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def get_static_content(user):
|
||||
if not user.mailbox_set.count():
|
||||
def get_static_content(caller, user):
|
||||
if caller != 'top' and not user.mailbox_set.count():
|
||||
return []
|
||||
return """<script type="text/javascript">
|
||||
|
||||
return ["""<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$(document).on('click', 'a[name=sendspam]', simple_ajax_request);
|
||||
$(document).on('click', 'a[name=sendvirus]', simple_ajax_request);
|
||||
});
|
||||
</script>"""
|
||||
</script>"""]
|
||||
|
||||
@@ -15,6 +15,11 @@ admin_events = [
|
||||
"CreateDomain",
|
||||
"DomainModified",
|
||||
"DeleteDomain",
|
||||
"ExtraDomainEntries",
|
||||
"ExtraDomainMenuEntries",
|
||||
"ExtraDomainFilters",
|
||||
"GetDomainActions",
|
||||
"GetDomainModifyLink",
|
||||
|
||||
"DomainAliasCreated",
|
||||
"DomainAliasDeleted",
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# coding: utf-8
|
||||
from functools import wraps
|
||||
from itertools import chain
|
||||
from django.db.models import Q
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
from modoboa.extensions.admin.models import Alias
|
||||
from modoboa.extensions.admin.models import (
|
||||
Domain, Alias
|
||||
)
|
||||
|
||||
|
||||
def needs_mailbox():
|
||||
@@ -65,8 +68,6 @@ def get_identities(user, searchquery=None, idtfilter=None, grpfilter=None):
|
||||
:param list grpfilter: group names filters
|
||||
:return: a queryset
|
||||
"""
|
||||
from itertools import chain
|
||||
|
||||
accounts = []
|
||||
if idtfilter is None or not idtfilter or idtfilter == "account":
|
||||
ids = user.objectaccess_set \
|
||||
@@ -104,3 +105,24 @@ def get_identities(user, searchquery=None, idtfilter=None, grpfilter=None):
|
||||
if idtfilter is not None and idtfilter:
|
||||
aliases = [al for al in aliases if al.type == idtfilter]
|
||||
return chain(accounts, aliases)
|
||||
|
||||
|
||||
def get_domains(user, domfilter=None, searchquery=None, **extrafilters):
|
||||
"""Return all the domains the user can access.
|
||||
|
||||
:param ``User`` user: user object
|
||||
:param str searchquery: filter
|
||||
:rtype: list
|
||||
:return: a list of domains and/or relay domains
|
||||
"""
|
||||
domains = []
|
||||
if domfilter is None or not domfilter or domfilter == 'domain':
|
||||
domains = Domain.objects.get_for_admin(user)
|
||||
if searchquery is not None:
|
||||
q = Q(name__contains=searchquery)
|
||||
q |= Q(domainalias__name__contains=searchquery)
|
||||
domains = domains.filter(q).distinct()
|
||||
extra_domain_entries = events.raiseQueryEvent(
|
||||
'ExtraDomainEntries', user, domfilter, searchquery, **extrafilters
|
||||
)
|
||||
return chain(domains, extra_domain_entries)
|
||||
|
||||
@@ -75,6 +75,13 @@ class Migration(SchemaMigration):
|
||||
'use_domain_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"})
|
||||
},
|
||||
u'admin.mailboxoperation': {
|
||||
'Meta': {'object_name': 'MailboxOperation'},
|
||||
'argument': ('django.db.models.fields.TextField', [], {}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.Mailbox']", 'null': 'True', 'blank': 'True'}),
|
||||
'type': ('django.db.models.fields.CharField', [], {'max_length': '20'})
|
||||
},
|
||||
'admin.objectdates': {
|
||||
'Meta': {'object_name': 'ObjectDates'},
|
||||
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from .base import DatesAware
|
||||
from .domain import Domain
|
||||
from .domain_alias import DomainAlias
|
||||
from .mailbox import Mailbox, Quota, MailboxOperation
|
||||
from .alias import Alias
|
||||
|
||||
__all__ = [
|
||||
'Domain', 'DomainAlias', 'Mailbox', 'Quota', 'Alias', 'MailboxOperation'
|
||||
'Domain', 'DomainAlias', 'Mailbox', 'Quota', 'Alias',
|
||||
'MailboxOperation', 'DatesAware'
|
||||
]
|
||||
|
||||
@@ -56,6 +56,10 @@ class Domain(DatesAware):
|
||||
def mbalias_count(self):
|
||||
return self.alias_set.count()
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return [{"name": "domain", "label": _("Domain"), "type": "dom"}]
|
||||
|
||||
@property
|
||||
def admins(self):
|
||||
"""Return the domain administrators of this domain
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @module admin
|
||||
* @desc Utility classes for the admin application
|
||||
*/
|
||||
|
||||
var Admin = function(options) {
|
||||
Listing.call(this, options);
|
||||
};
|
||||
@@ -12,6 +17,7 @@ Admin.prototype = {
|
||||
Listing.prototype.initialize.call(this, options);
|
||||
this.options = $.extend({}, this.defaults, this.options);
|
||||
this.options.defcallback = $.proxy(this.list_cb, this);
|
||||
this.tag_handlers = {};
|
||||
|
||||
this.navobj = new History(this.options);
|
||||
|
||||
@@ -37,12 +43,61 @@ Admin.prototype = {
|
||||
list_cb: function(data) {
|
||||
$("#listing").html(data.table);
|
||||
this.update_listing(data);
|
||||
$("a.filter").click($.proxy(this.filter_by_tag, this));
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
$("#searchform").submit($.proxy(this.do_search, this));
|
||||
},
|
||||
|
||||
register_tag_handler: function(name, handler) {
|
||||
this.tag_handlers[name] = handler;
|
||||
if (this.navobj.getparam(name + "filter") != undefined) {
|
||||
var text = this.navobj.getparam(name + "filter");
|
||||
$("#searchform").parent().after(this.make_tag(text, name));
|
||||
}
|
||||
},
|
||||
|
||||
generic_tag_handler: function(tag, $link) {
|
||||
if (this.navobj.getparam(tag + "filter") == undefined && $link.hasClass(tag)) {
|
||||
var text = $link.attr("name");
|
||||
this.navobj.setparam(tag + "filter", text).update();
|
||||
$("#searchform").parent().after(this.make_tag(text, tag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
make_tag: function(text, type) {
|
||||
var $tag = $("<a />", {"name": type, "class" : "btn btn-mini", "html": text});
|
||||
var $i = $("<i />", {"class" : "icon-remove"}).prependTo($tag);
|
||||
|
||||
$tag.click($.proxy(this.remove_tag, this));
|
||||
return $tag;
|
||||
},
|
||||
|
||||
remove_tag: function(e) {
|
||||
var $tag = $(e.target);
|
||||
|
||||
if ($tag.is("i")) {
|
||||
$tag = $tag.parent();
|
||||
}
|
||||
e.preventDefault();
|
||||
this.navobj.delparam($tag.attr("name") + "filter").update();
|
||||
$tag.remove();
|
||||
},
|
||||
|
||||
filter_by_tag: function(e) {
|
||||
var $link = $(e.target);
|
||||
e.preventDefault();
|
||||
|
||||
for (var name in this.tag_handlers) {
|
||||
if (this.tag_handlers[name].apply(this, [name, $link])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
do_search: function(e) {
|
||||
e.preventDefault();
|
||||
var squery = $("#searchquery").val();
|
||||
@@ -104,6 +159,11 @@ var Domains = function(options) {
|
||||
};
|
||||
|
||||
Domains.prototype = {
|
||||
initialize: function(options) {
|
||||
Admin.prototype.initialize.call(this, options);
|
||||
this.register_tag_handler("dom", this.generic_tag_handler);
|
||||
},
|
||||
|
||||
list_cb: function(data) {
|
||||
Admin.prototype.list_cb.call(this, data);
|
||||
var deloptions = (data.handle_mailboxes)
|
||||
@@ -120,6 +180,11 @@ Domains.prototype = {
|
||||
checkboxes: deloptions,
|
||||
success_cb: $.proxy(this.reload_listing, this)
|
||||
});
|
||||
$("a[name=delrelaydomain]").confirm({
|
||||
question: function() { return this.$element.attr('title'); },
|
||||
method: "DELETE",
|
||||
success_cb: $.proxy(this.reload_listing, this)
|
||||
});
|
||||
},
|
||||
|
||||
change_inputs_state: function(value) {
|
||||
@@ -193,6 +258,17 @@ Domains.prototype = {
|
||||
});
|
||||
}, this));
|
||||
$(document).trigger('domform_init');
|
||||
},
|
||||
|
||||
rdomainform_cb: function() {
|
||||
$(".submit").one('click', $.proxy(function(e) {
|
||||
simple_ajax_form_post2(e, {
|
||||
formid: "rdomform",
|
||||
error_cb: $.proxy(this.rdomainform_cb, this),
|
||||
reload_on_success: false,
|
||||
success_cb: $.proxy(this.reload_listing, this)
|
||||
});
|
||||
}, this));
|
||||
}
|
||||
|
||||
};
|
||||
@@ -209,14 +285,8 @@ var Identities = function(options) {
|
||||
Identities.prototype = {
|
||||
initialize: function(options) {
|
||||
Admin.prototype.initialize.call(this, options);
|
||||
if (this.navobj.getparam("idtfilter") != undefined) {
|
||||
var text = this.navobj.getparam("idtfilter");
|
||||
$("#searchform").parent().after(this.make_tag(text, "idt"));
|
||||
}
|
||||
if (this.navobj.getparam("grpfilter") != undefined) {
|
||||
var text = this.navobj.getparam("grpfilter");
|
||||
$("#searchform").parent().after(this.make_tag(text, "grp"));
|
||||
}
|
||||
this.register_tag_handler("idt", this.generic_tag_handler);
|
||||
this.register_tag_handler("grp", this.grp_tag_handler);
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
@@ -237,7 +307,6 @@ Identities.prototype = {
|
||||
if (data.handle_mailboxes == "yes") {
|
||||
deloptions = {keepdir: gettext("Do not delete mailbox directory")};
|
||||
}
|
||||
$("a.filter").click($.proxy(this.filter_by_tag, this));
|
||||
|
||||
$("a[name=delaccount]").confirm({
|
||||
question: function() { return this.$element.attr('title'); },
|
||||
@@ -252,46 +321,20 @@ Identities.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
make_tag: function(text, type) {
|
||||
var $tag = $("<a />", {"name": type, "class" : "btn btn-mini", "html": text});
|
||||
var $i = $("<i />", {"class" : "icon-remove"}).prependTo($tag);
|
||||
|
||||
$tag.click($.proxy(this.remove_tag, this));
|
||||
return $tag;
|
||||
},
|
||||
|
||||
remove_tag: function(e) {
|
||||
var $tag = $(e.target);
|
||||
|
||||
if ($tag.is("i")) {
|
||||
$tag = $tag.parent();
|
||||
}
|
||||
e.preventDefault();
|
||||
this.navobj.delparam($tag.attr("name") + "filter").update();
|
||||
$tag.remove();
|
||||
},
|
||||
|
||||
filter_by_tag: function(e) {
|
||||
var $link = $(e.target);
|
||||
e.preventDefault();
|
||||
|
||||
if (this.navobj.getparam("idtfilter") == undefined && $link.hasClass("idt")) {
|
||||
var text = $link.attr("name");
|
||||
this.navobj.setparam("idtfilter", text).update();
|
||||
$("#searchform").parent().after(this.make_tag(text, "idt"));
|
||||
return;
|
||||
}
|
||||
if (this.navobj.getparam("grpfilter") == undefined && $link.hasClass("grp")) {
|
||||
grp_tag_handler: function(tag, $link) {
|
||||
if (this.navobj.getparam(tag + "filter") == undefined && $link.hasClass(tag)) {
|
||||
var text = $link.attr("name");
|
||||
this.navobj
|
||||
.setparam("idtfilter", "account")
|
||||
.setparam("grpfilter", text)
|
||||
.setparam(tag + "filter", text)
|
||||
.update();
|
||||
if ($("a[name=idt]").length == 0) {
|
||||
$("#searchform").parent().after(this.make_tag("account", "idt"));
|
||||
}
|
||||
$("#searchform").parent().after(this.make_tag(text, "grp"));
|
||||
$("#searchform").parent().after(this.make_tag(text, tag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
simpleuser_mode: function() {
|
||||
|
||||
@@ -7,5 +7,6 @@ $(document).ready(function() {
|
||||
admin = new Domains();
|
||||
});
|
||||
</script>
|
||||
{% extra_static_content "domains" user %}
|
||||
{% endblock %}
|
||||
{% block actions %}{% domains_menu "domains" user %}{% endblock %}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{% load i18n admin_tags lib_tags %}
|
||||
<table id="{{ tableid }}" class="table {{ styles }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="identity" class="sortable" data-sort_order="identity" width="">{% trans "Name" %}</th>
|
||||
<th id="name_or_rcpt" class="sortable" data-sort_order="name_or_rcpt" width="">{% trans "Alias(es)" %}</th>
|
||||
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
|
||||
<th id="actions" width="">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in domains %}
|
||||
<tr id="{{ domain.id }}" class="{% if not domain.enabled %}muted{% endif %}" name="{{ forloop.counter }}">
|
||||
<td name="name">{% domain_modify_link domain %}</td>
|
||||
<td name="aliases">{% domain_aliases domain %} </td>
|
||||
<td name="tags">{% render_tags domain.tags %}</td>
|
||||
<td name="actions">{% domain_actions user domain %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -20,5 +20,6 @@ $(document).ready(function() {
|
||||
admin = new Identities();
|
||||
});
|
||||
</script>
|
||||
{% extra_static_content "identities" user %}
|
||||
{% endblock %}
|
||||
{% block actions %}{% identities_menu user %}{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "admin/header.html" %}{% load i18n admin_tags lib_tags %}
|
||||
{% extends "admin/header.html" %}{% load i18n admin_tags %}
|
||||
{% block html_head %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.sortable.css" />
|
||||
|
||||
@@ -15,6 +15,16 @@ genders = {
|
||||
|
||||
@register.simple_tag
|
||||
def domains_menu(selection, user):
|
||||
"""Specific menu for domain related operations.
|
||||
|
||||
Corresponds to the menu visible on the left column when you go to
|
||||
Domains.
|
||||
|
||||
:param str selection: menu entry currently selected
|
||||
:param ``User`` user: connected user
|
||||
:rtype: str
|
||||
:return: rendered menu (as HTML)
|
||||
"""
|
||||
if not user.has_perm("admin.add_domain"):
|
||||
return ""
|
||||
|
||||
@@ -25,6 +35,9 @@ def domains_menu(selection, user):
|
||||
"modal": True,
|
||||
"modalcb": "admin.newdomain_cb",
|
||||
"url": reverse("modoboa.extensions.admin.views.domain.newdomain")},
|
||||
]
|
||||
entries += events.raiseQueryEvent("ExtraDomainMenuEntries", user)
|
||||
entries += [
|
||||
{"name": "import",
|
||||
"label": _("Import"),
|
||||
"img": "icon-folder-open",
|
||||
@@ -111,23 +124,28 @@ def identities_menu(user):
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def domain_actions(user, domid):
|
||||
from modoboa.extensions.admin.models import Domain
|
||||
def domain_actions(user, domain):
|
||||
actions = []
|
||||
if domain.__class__.__name__ == 'Domain':
|
||||
actions = [
|
||||
{"name": "listidentities",
|
||||
"url": reverse("modoboa.extensions.admin.views.identity.identities") + "#list/?searchquery=@%s" % domain.name,
|
||||
"title": _("View the domain's identities"),
|
||||
"img": "icon-user"}
|
||||
]
|
||||
if user.has_perm("admin.delete_domain"):
|
||||
actions.append({
|
||||
"name": "deldomain",
|
||||
"url": reverse(
|
||||
"modoboa.extensions.admin.views.domain.deldomain",
|
||||
args=[domain.id]
|
||||
),
|
||||
"title": _("Delete %s?" % domain.name),
|
||||
"img": "icon-trash"
|
||||
})
|
||||
else:
|
||||
actions = events.raiseQueryEvent('GetDomainActions', user, domain)
|
||||
|
||||
domain = Domain.objects.get(pk=domid)
|
||||
actions = [
|
||||
{"name": "listidentities",
|
||||
"url": reverse("modoboa.extensions.admin.views.identity.identities") + "#list/?searchquery=@%s" % domain.name,
|
||||
"title": _("View the domain's identities"),
|
||||
"img": "icon-user"}
|
||||
]
|
||||
if user.has_perm("admin.delete_domain"):
|
||||
actions.append({
|
||||
"name": "deldomain",
|
||||
"url": reverse("modoboa.extensions.admin.views.domain.deldomain", args=[domid]),
|
||||
"title": _("Delete %s?" % domain.name),
|
||||
"img": "icon-trash"
|
||||
})
|
||||
return render_actions(actions)
|
||||
|
||||
|
||||
@@ -153,8 +171,50 @@ def identity_actions(user, ident):
|
||||
return render_actions(actions)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def domain_modify_link(domain):
|
||||
linkdef = {"label": domain.name, "modal": True}
|
||||
if domain.__class__.__name__ == "Domain":
|
||||
linkdef["url"] = reverse(
|
||||
"modoboa.extensions.admin.views.domain.editdomain",
|
||||
args=[domain.id]
|
||||
)
|
||||
linkdef["modalcb"] = "admin.domainform_cb"
|
||||
else:
|
||||
tmp = events.raiseDictEvent('GetDomainModifyLink', domain)
|
||||
for key in ['url', 'modalcb']:
|
||||
linkdef[key] = tmp[key]
|
||||
return render_link(linkdef)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def domain_aliases(domain):
|
||||
"""Display domain aliases of this domain.
|
||||
|
||||
:param domain:
|
||||
:rtype: str
|
||||
"""
|
||||
if domain.__class__.__name__ == "Domain":
|
||||
if not domain.domainalias_set.count():
|
||||
return '---'
|
||||
res = ""
|
||||
for da in domain.domainalias_set.all():
|
||||
res += "%s<br/>" % da.name
|
||||
return res
|
||||
return '---'
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def identity_modify_link(identity, active_tab='default'):
|
||||
"""Return the appropriate modification link.
|
||||
|
||||
According to the identity type, a specific modification link (URL)
|
||||
must be used.
|
||||
|
||||
:param identity: a ``User`` or ``Alias`` instance
|
||||
:param str active_tab: the tab to display
|
||||
:rtype: str
|
||||
"""
|
||||
linkdef = {"label": identity.identity, "modal": True}
|
||||
if identity.__class__.__name__ == "User":
|
||||
linkdef["url"] = reverse(
|
||||
|
||||
@@ -15,13 +15,13 @@ from modoboa.lib.webutils import (
|
||||
from modoboa.lib.formutils import CreationWizard
|
||||
from modoboa.lib.exceptions import PermDeniedException
|
||||
from modoboa.lib.templatetags.lib_tags import pagination_bar
|
||||
from modoboa.extensions.admin.tables import DomainsTable
|
||||
from modoboa.extensions.admin.lib import get_sort_order, get_listing_page
|
||||
from modoboa.extensions.admin.exceptions import AdminError
|
||||
from modoboa.extensions.admin.models import Domain, Mailbox
|
||||
from modoboa.extensions.admin.forms import (
|
||||
DomainForm, DomainFormGeneral, DomainFormOptions
|
||||
)
|
||||
from modoboa.extensions.admin.lib import get_domains
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -35,17 +35,28 @@ def index(request):
|
||||
)
|
||||
def _domains(request):
|
||||
sort_order, sort_dir = get_sort_order(request.GET, "name")
|
||||
domains = Domain.objects.get_for_admin(request.user)
|
||||
squery = request.GET.get("searchquery", None)
|
||||
if squery is not None:
|
||||
q = Q(name__contains=squery)
|
||||
q |= Q(domainalias__name__contains=squery)
|
||||
domains = domains.filter(q).distinct()
|
||||
filters = dict(
|
||||
(flt, request.GET.get(flt, None))
|
||||
for flt in ['domfilter', 'searchquery']
|
||||
+ events.raiseQueryEvent('ExtraDomainFilters')
|
||||
)
|
||||
domainlist = get_domains(
|
||||
request.user, **filters
|
||||
)
|
||||
if sort_order in ["name", "domainalias__name"]:
|
||||
domains = domains.order_by("%s%s" % (sort_dir, sort_order))
|
||||
page = get_listing_page(domains, request.GET.get("page", 1))
|
||||
domainlist = sorted(
|
||||
domainlist,
|
||||
key=lambda d: getattr(d, sort_order), reverse=sort_dir == '-'
|
||||
)
|
||||
else:
|
||||
domainlist = sorted(domainlist, key=lambda d: d.tags[0],
|
||||
reverse=sort_dir == '-')
|
||||
page = get_listing_page(domainlist, request.GET.get("page", 1))
|
||||
return ajax_simple_response({
|
||||
"table": DomainsTable(request, page.object_list).render(),
|
||||
"table": _render_to_string(request, 'admin/domains_table.html', {
|
||||
'domains': domainlist,
|
||||
'tableid': 'domains'
|
||||
}),
|
||||
"page": page.number,
|
||||
"paginbar": pagination_bar(page),
|
||||
"handle_mailboxes": parameters.get_admin("HANDLE_MAILBOXES",
|
||||
|
||||
@@ -95,13 +95,14 @@ def on_delete_domain(domain):
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def extra_static_content(user):
|
||||
def extra_static_content(caller, user):
|
||||
if user.group == "SimpleUsers":
|
||||
return []
|
||||
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
if caller == 'top' and parameters.get_admin("USER_CAN_RELEASE") == 'no':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
{% if user_can_release == "no" %}var poller = new Poller("{{ url }}", {
|
||||
var poller = new Poller("{{ url }}", {
|
||||
interval: {{ interval }},
|
||||
success_cb: function(data) {
|
||||
var $link = $("#nbrequests");
|
||||
@@ -112,22 +113,27 @@ $(document).ready(function() {
|
||||
$link.parent().addClass('hidden');
|
||||
}
|
||||
}
|
||||
});{% endif %}
|
||||
|
||||
$(document).bind('domform_init', function() {
|
||||
activate_widget.call($('#id_spam_subject_tag2_act'));
|
||||
});
|
||||
});
|
||||
</script>""")
|
||||
</script>
|
||||
""")
|
||||
return [tpl.render(
|
||||
Context({
|
||||
'url': reverse("modoboa.extensions.amavis.views.nbrequests"),
|
||||
'interval': int(parameters.get_admin("CHECK_REQUESTS_INTERVAL")) * 1000,
|
||||
'text': _("pending requests"),
|
||||
})
|
||||
)]
|
||||
|
||||
return [tpl.render(
|
||||
Context(dict(
|
||||
url=reverse("modoboa.extensions.amavis.views.nbrequests"),
|
||||
interval=int(parameters.get_admin("CHECK_REQUESTS_INTERVAL")) * 1000,
|
||||
text=_("pending requests"),
|
||||
user_can_release=parameters.get_admin("USER_CAN_RELEASE")
|
||||
))
|
||||
)]
|
||||
if caller == 'domains':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).bind('domform_init', function() {
|
||||
activate_widget.call($('#id_spam_subject_tag2_act'));
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
|
||||
return [tpl.render(Context({}))]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe("TopNotifications")
|
||||
|
||||
@@ -116,10 +116,12 @@ def fill_account_instances(user, account, instances):
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def get_static_content(user):
|
||||
def get_static_content(caller, user):
|
||||
if caller not in ['domains', 'identities']:
|
||||
return []
|
||||
if user.group == "SimpleUsers":
|
||||
return []
|
||||
return """<style>
|
||||
return ["""<style>
|
||||
.resource {
|
||||
margin: 5px 0;
|
||||
}
|
||||
@@ -137,4 +139,4 @@ $(document).ready(function() {
|
||||
$(".progress").tooltip();
|
||||
});
|
||||
</script>
|
||||
"""
|
||||
"""]
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
# coding: utf-8
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import Template, Context
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.db.models import Q
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.extensions import ModoExtension, exts_pool
|
||||
from .models import RelayDomain, Service
|
||||
|
||||
extension_events = [
|
||||
"RelayDomainCreated",
|
||||
]
|
||||
|
||||
|
||||
class PostfixRelayDomains(ModoExtension):
|
||||
name = "postfix_relay_domains"
|
||||
label = "Postfix relay domains"
|
||||
version = "1.0"
|
||||
description = ugettext_lazy("Relay domains support for Postfix")
|
||||
|
||||
def init(self):
|
||||
"""Initialisation method.
|
||||
|
||||
Only run once, when the extension is enabled. Populates the
|
||||
service table with default entries.
|
||||
"""
|
||||
for service_name in ['relay', 'smtp']:
|
||||
Service.objects.get_or_create(name=service_name)
|
||||
|
||||
def load(self):
|
||||
from .app_settings import AdminParametersForm
|
||||
|
||||
parameters.register(AdminParametersForm, ugettext_lazy("Relay domains"))
|
||||
events.declare(extension_events)
|
||||
|
||||
def destroy(self):
|
||||
events.unregister('GetExtraDomainEntries', extra_domain_entries)
|
||||
|
||||
exts_pool.register_extension(PostfixRelayDomains)
|
||||
|
||||
|
||||
@events.observe('GetStaticContent')
|
||||
def static_content(caller, user):
|
||||
if caller != 'domains':
|
||||
return []
|
||||
|
||||
t = Template("""<script src="{{ STATIC_URL }}postfix_relay_domains/js/relay_domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var rdomain;
|
||||
$(document).ready(function() {
|
||||
rdomain = new RelayDomains({});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [t.render(Context({'STATIC_URL': settings.STATIC_URL}))]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainFilters')
|
||||
def extra_domain_filters():
|
||||
return ['srvfilter']
|
||||
|
||||
|
||||
@events.observe('ExtraDomainMenuEntries')
|
||||
def extra_domain_menu_entries(user):
|
||||
return [
|
||||
{"name": "newrelaydomain",
|
||||
"label": ugettext_lazy("Add relay domain"),
|
||||
"img": "icon-plus",
|
||||
"modal": True,
|
||||
"modalcb": "rdomain.domainform_cb",
|
||||
"url": reverse(
|
||||
"modoboa.extensions.postfix_relay_domains.views.create"
|
||||
)},
|
||||
]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainEntries')
|
||||
def extra_domain_entries(user, domfilter, searchquery, **extrafilters):
|
||||
if domfilter is not None and domfilter and domfilter != 'relaydomain':
|
||||
return []
|
||||
relay_domains = RelayDomain.objects.get_for_admin(user)
|
||||
if searchquery is not None:
|
||||
q = Q(name__contains=searchquery)
|
||||
relay_domains = relay_domains.filter(q).distinct()
|
||||
if 'srvfilter' in extrafilters and extrafilters['srvfilter']:
|
||||
relay_domains = relay_domains.filter(
|
||||
Q(service__name=extrafilters['srvfilter'])
|
||||
)
|
||||
return relay_domains
|
||||
|
||||
|
||||
@events.observe('GetDomainModifyLink')
|
||||
def rdomain_modify_link(domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return {}
|
||||
return {
|
||||
'url': reverse(
|
||||
'modoboa.extensions.postfix_relay_domains.views.edit',
|
||||
args=[domain.id]
|
||||
),
|
||||
'modalcb': 'rdomain.domainform_cb'
|
||||
}
|
||||
|
||||
|
||||
@events.observe('GetDomainActions')
|
||||
def rdomain_actions(user, domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return []
|
||||
if not user.has_perm("postfix_relay_domains.delete_relaydomain"):
|
||||
return []
|
||||
return [{
|
||||
"name": "delrelaydomain",
|
||||
"url": reverse("modoboa.extensions.postfix_relay_domains.views.delete",
|
||||
args=[domain.id]),
|
||||
"title": ugettext_lazy("Delete %s?" % domain.name),
|
||||
"img": "icon-trash"
|
||||
}]
|
||||
@@ -0,0 +1,14 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from modoboa.lib.formutils import SeparatorField
|
||||
from modoboa.lib import parameters
|
||||
|
||||
|
||||
class AdminParametersForm(parameters.AdminParametersForm):
|
||||
app = "postfix_relay_domains"
|
||||
|
||||
master_cf_path = forms.CharField(
|
||||
label=ugettext_lazy("Postfix's master.cf path"),
|
||||
initial="/etc/postfix/master.cf",
|
||||
help_text=ugettext_lazy('Path to the master.cf configuration file')
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
from django import forms
|
||||
from .models import RelayDomain
|
||||
|
||||
|
||||
class RelayDomainForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = RelayDomain
|
||||
exclude = ['dates']
|
||||
widgets = {
|
||||
'service': forms.Select(attrs={'class': 'span2'})
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import reversion
|
||||
from django.db import models
|
||||
from django.db.models.manager import Manager
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.core.models import ObjectAccess
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.permissions import ungrant_access_to_object
|
||||
from modoboa.extensions.admin.models import DatesAware
|
||||
|
||||
|
||||
class RelayDomainManager(Manager):
|
||||
|
||||
def get_for_admin(self, admin):
|
||||
"""Return the relay domains belonging to this admin.
|
||||
|
||||
The result is a ``QuerySet`` object, so this function can be used
|
||||
to fill ``ModelChoiceField`` objects.
|
||||
"""
|
||||
if admin.is_superuser:
|
||||
return self.get_query_set()
|
||||
return self.get_query_set().filter(owners__user=admin)
|
||||
|
||||
|
||||
class ServiceManager(Manager):
|
||||
|
||||
def load_from_master_cf(self):
|
||||
"""Load services from the master.cf file.
|
||||
|
||||
Parse the configuration file to update the service table. New
|
||||
entries are saved and outdated ones (ie. present in the
|
||||
database but not in the file) are removed.
|
||||
"""
|
||||
with open(parameters.get_admin('MASTER_CF_PATH')) as fp:
|
||||
content = fp.read()
|
||||
services = []
|
||||
for line in content.split('\n'):
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
parts = line.strip().split()
|
||||
if len(parts) != 8:
|
||||
continue
|
||||
if parts[7] != 'smtp':
|
||||
continue
|
||||
srv, created = self.get_or_create(name=parts[0])
|
||||
services.append(parts[0])
|
||||
to_delete = []
|
||||
for service in self.all():
|
||||
if not service.name in services:
|
||||
to_delete.append(service.name)
|
||||
Service.objects.filter(name__in=to_delete()).delete()
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
name = models.CharField(
|
||||
ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy('The service name')
|
||||
)
|
||||
|
||||
objects = ServiceManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RelayDomain(DatesAware):
|
||||
"""Relay domain.
|
||||
|
||||
A relay domain differs from a usual domaine because its final
|
||||
destination is not reached yet. It must be accepted by the MTA but
|
||||
it will then be transfered to another one.
|
||||
"""
|
||||
name = models.CharField(
|
||||
ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy('The domain name')
|
||||
)
|
||||
target_host = models.CharField(
|
||||
ugettext_lazy('target host'), max_length=255,
|
||||
help_text=ugettext_lazy('')
|
||||
)
|
||||
service = models.ForeignKey(Service, default='relay')
|
||||
enabled = models.BooleanField(
|
||||
ugettext_lazy('enabled'),
|
||||
help_text=ugettext_lazy('Check to activate this domain')
|
||||
)
|
||||
verify_recipients = models.BooleanField(
|
||||
ugettext_lazy('verify recipients'),
|
||||
help_text=ugettext_lazy('')
|
||||
)
|
||||
|
||||
owners = generic.GenericRelation(ObjectAccess)
|
||||
|
||||
objects = RelayDomainManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return [
|
||||
{"name": "relaydomain", "label": _("Relay Domain"), "type": "dom"},
|
||||
{"name": self.service.name, "label": "%s:" % self.service.name,
|
||||
"type": "srv", "color": "info"}
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def post_create(self, creator):
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
grant_access_to_object(creator, self, is_owner=True)
|
||||
events.raiseEvent("RelayDomainCreated", creator, self)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if "creator" in kwargs:
|
||||
creator = kwargs["creator"]
|
||||
del kwargs["creator"]
|
||||
else:
|
||||
creator = None
|
||||
super(RelayDomain, self).save(*args, **kwargs)
|
||||
if creator is not None:
|
||||
self.post_create(creator)
|
||||
|
||||
def delete(self):
|
||||
events.raiseEvent("RelayDomainDeleted", self)
|
||||
ungrant_access_to_object(self)
|
||||
super(RelayDomain, self).delete()
|
||||
|
||||
reversion.register(RelayDomain)
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @module relay_domains
|
||||
* @desc Utility class to manage relay domains.
|
||||
*/
|
||||
|
||||
var RelayDomains = function(options) {
|
||||
this.initialize(options);
|
||||
};
|
||||
|
||||
RelayDomains.prototype = {
|
||||
constructor: RelayDomains,
|
||||
|
||||
defaults: {
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = $.extend({}, this.defaults, options);
|
||||
admin.register_tag_handler("srv", this.srv_tag_handler);
|
||||
},
|
||||
|
||||
launch_scan: function(e) {
|
||||
var $link = get_target(e);
|
||||
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $link.attr("href")
|
||||
}).done(function(data) {
|
||||
var $widget = $('#id_service');
|
||||
|
||||
$widget.empty();
|
||||
$.each(data, function(label, id) {
|
||||
$widget.append($('<option />', {
|
||||
value: id,
|
||||
html: label
|
||||
}));
|
||||
});
|
||||
$('.modal-body').prepend(
|
||||
build_success_alert(gettext('Services updated'))
|
||||
);
|
||||
}).fail(function(jqxhr) {
|
||||
if (jqxhr.getResponseHeader('Content-Type') != 'application/json') {
|
||||
return;
|
||||
}
|
||||
var result = $.parseJSON(jqxhr.responseText);
|
||||
$('.modal-body').prepend(
|
||||
build_error_alert(result[0])
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
domainform_cb: function() {
|
||||
$('#btnscan').click(this.launch_scan);
|
||||
$(".submit").one('click', $.proxy(function(e) {
|
||||
simple_ajax_form_post2(e, {
|
||||
formid: "rdomform",
|
||||
error_cb: $.proxy(this.domainform_cb, this),
|
||||
reload_on_success: false,
|
||||
success_cb: $.proxy(admin.reload_listing, admin)
|
||||
});
|
||||
}, this));
|
||||
},
|
||||
|
||||
srv_tag_handler: function(tag, $link) {
|
||||
if (this.navobj.getparam(tag + "filter") == undefined && $link.hasClass(tag)) {
|
||||
var text = $link.attr("name");
|
||||
this.navobj
|
||||
.setparam("domfilter", "relaydomain")
|
||||
.setparam(tag + "filter", text)
|
||||
.update();
|
||||
if ($("a[name=dom]").length == 0) {
|
||||
$("#searchform").parent().after(this.make_tag("relaydomain", "dom"));
|
||||
}
|
||||
$("#searchform").parent().after(this.make_tag(text, tag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{% extends "common/generic_modal_form.html" %}{% load lib_tags %}{% block formbody %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.target_host %}
|
||||
{% include "postfix_relay_domains/service_field.html" with field=form.service %}
|
||||
{% render_field form.enabled %}
|
||||
{% render_field form.verify_recipients %}
|
||||
{% endblock %}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{% extends "common/generic_field.html" %}{% load i18n %}
|
||||
{% block extra_controls %}<a id="btnscan" class="btn btn-success" style="margin-bottom: 8px" href="{% url 'modoboa.extensions.postfix_relay_domains.views.scan_for_services' %}">{% trans "Scan" %}</a>{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
@@ -0,0 +1,9 @@
|
||||
from django.conf.urls import patterns
|
||||
|
||||
urlpatterns = patterns(
|
||||
'modoboa.extensions.postfix_relay_domains.views',
|
||||
(r'^relaydomains/new/', 'create'),
|
||||
(r'^relaydomains/(?P<rdom_id>\d+)/edit/$', 'edit'),
|
||||
(r'^relaydomains/(?P<rdom_id>\d+)/delete/$', 'delete'),
|
||||
(r'^relaydomains/scan_services/$', 'scan_for_services'),
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
from django.shortcuts import render
|
||||
from django.db import transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.contrib.auth.decorators import (
|
||||
login_required, permission_required, user_passes_test
|
||||
)
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import ModoboaException, PermDeniedException
|
||||
from modoboa.lib.webutils import render_to_json_response
|
||||
from .models import RelayDomain, Service
|
||||
from .forms import RelayDomainForm
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.add_relaydomain")
|
||||
@transaction.commit_on_success
|
||||
def create(request, tplname="postfix_relay_domains/relaydomain_form.html"):
|
||||
commonctx = {"title": _("New relay domain"),
|
||||
"action_label": _("Create"),
|
||||
"action_classes": "submit",
|
||||
"action": reverse(create),
|
||||
"formid": "rdomform"}
|
||||
if request.method == 'POST':
|
||||
form = RelayDomainForm(request.POST)
|
||||
if form.is_valid():
|
||||
rdom = form.save()
|
||||
rdom.post_create(request.user)
|
||||
return render_to_json_response(
|
||||
{"respmsg": _("Relay domain created")}
|
||||
)
|
||||
return render_to_json_response(form.errors, status=400)
|
||||
commonctx['form'] = RelayDomainForm()
|
||||
return render(request, tplname, commonctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.change_relaydomain")
|
||||
def edit(request, rdom_id, tplname='postfix_relay_domains/relaydomain_form.html'):
|
||||
rdom = RelayDomain.objects.get(pk=rdom_id)
|
||||
if not request.user.can_access(rdom):
|
||||
raise PermDeniedException
|
||||
if request.method == 'POST':
|
||||
form = RelayDomainForm(request.POST, instance=rdom)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
events.raiseEvent('RelayDomainModified', rdom)
|
||||
return render_to_json_response(
|
||||
{'respmsg': _('Relay domain modified')}
|
||||
)
|
||||
return render_to_json_response(form.errors, status=400)
|
||||
ctx = {
|
||||
'action': reverse(edit, args=[rdom.id]),
|
||||
'formid': 'rdomform',
|
||||
'title': rdom.name,
|
||||
'action_label': _("Update"),
|
||||
'action_classes': "submit",
|
||||
'form': RelayDomainForm(instance=rdom)
|
||||
}
|
||||
return render(request, tplname, ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.delete_relaydomain")
|
||||
def delete(request, rdom_id):
|
||||
rdom = RelayDomain.objects.get(pk=rdom_id)
|
||||
if not request.user.can_access(rdom):
|
||||
raise PermDeniedException
|
||||
rdom.delete()
|
||||
return render_to_json_response({
|
||||
'status': 'ok', 'respmsg': _('Relay domain deleted')
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
@require_http_methods(["POST"])
|
||||
def scan_for_services(request):
|
||||
try:
|
||||
Service.objects.load_from_master_cf()
|
||||
except IOError as e:
|
||||
return render_to_json_response([str(e)], status=500)
|
||||
|
||||
return render_to_json_response(
|
||||
dict((srv.name, srv.id) for srv in Service.objects.all())
|
||||
)
|
||||
@@ -132,7 +132,7 @@ def raiseEvent(event, *args, **kwargs):
|
||||
return 1
|
||||
|
||||
|
||||
def raiseQueryEvent(event, *args):
|
||||
def raiseQueryEvent(event, *args, **kwargs):
|
||||
"""Raise a specific event and wait for answers from callbacks
|
||||
|
||||
Any additional keyword argument will be passed to registered
|
||||
@@ -144,7 +144,7 @@ def raiseQueryEvent(event, *args):
|
||||
if not event in events or not event in callbacks.keys():
|
||||
return result
|
||||
for callback in callbacks[event].values():
|
||||
result += callback(*args)
|
||||
result += callback(*args, **kwargs)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from django import template
|
||||
from django.template import Template, Context
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.formutils import SeparatorField
|
||||
|
||||
register = template.Library()
|
||||
@@ -146,3 +147,20 @@ def render_tags(tags):
|
||||
{% endfor %}
|
||||
""")
|
||||
return t.render(Context({"tags": tags}))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def extra_static_content(caller, user):
|
||||
"""Get extra static content from extensions.
|
||||
|
||||
:param str caller: the application (location) responsible for the call
|
||||
:param ``User`` user: connected user
|
||||
"""
|
||||
tpl = template.Template(
|
||||
"{% for sc in static_content %}{{ sc|safe }}{% endfor %}"
|
||||
)
|
||||
return tpl.render(
|
||||
template.Context({
|
||||
'static_content': events.raiseQueryEvent("GetStaticContent", caller, user)
|
||||
})
|
||||
)
|
||||
|
||||
@@ -100,6 +100,17 @@ def ajax_simple_response(content, **response_kwargs):
|
||||
return HttpResponse(simplejson.dumps(content), **response_kwargs)
|
||||
|
||||
|
||||
def render_to_json_response(context, **response_kwargs):
|
||||
"""Simple shortcut to render a JSON response.
|
||||
|
||||
:param dict context: response content
|
||||
:return: ``HttpResponse`` object
|
||||
"""
|
||||
data = simplejson.dumps(context)
|
||||
response_kwargs['content_type'] = 'application/json'
|
||||
return HttpResponse(data, **response_kwargs)
|
||||
|
||||
|
||||
def static_url(path):
|
||||
"""Returns the correct static url for a given file
|
||||
|
||||
|
||||
@@ -80,11 +80,20 @@ function modalbox_autowidth(e) {
|
||||
/*
|
||||
* Simple shorcut do create a bootstrap alert box (error mode)
|
||||
*/
|
||||
function build_error_alert(msg) {
|
||||
return $('<div class="alert alert-error"> \
|
||||
function build_alert_box(msg, level) {
|
||||
return $('<div class="alert alert-' + level + '"> \
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>' + msg + "</div>");
|
||||
}
|
||||
|
||||
function build_error_alert(msg) {
|
||||
return build_alert_box(msg, 'error');
|
||||
}
|
||||
|
||||
function build_success_alert(msg) {
|
||||
return build_alert_box(msg, 'success');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* '.keys()' method support for old browsers :p
|
||||
*/
|
||||
@@ -149,6 +158,70 @@ function simple_ajax_form_post(e, options) {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Replacement
|
||||
*/
|
||||
function simple_ajax_form_post2(e, options) {
|
||||
e.preventDefault();
|
||||
var $form = (options.formid != undefined) ? $("#" + options.formid) : $("form");
|
||||
var defaults = {reload_on_success: true, reload_mode: 'full', modal: true};
|
||||
var opts = $.extend({}, defaults, options);
|
||||
var args = $form.serialize();
|
||||
|
||||
if (options.extradata != undefined) {
|
||||
args += "&" + options.extradata;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $form.attr("action"),
|
||||
data: args
|
||||
}).done(function(data) {
|
||||
$("#modalbox").modal('hide');
|
||||
if (opts.success_cb != undefined) {
|
||||
opts.success_cb(data);
|
||||
return;
|
||||
}
|
||||
if (opts.reload_on_success) {
|
||||
if (opts.reload_mode == 'full') {
|
||||
window.location.reload();
|
||||
} else {
|
||||
history.update(true);
|
||||
}
|
||||
}
|
||||
if (data.respmsg) {
|
||||
$("body").notify('success', data.respmsg, 2000);
|
||||
}
|
||||
}).fail(function(jqxhr) {
|
||||
if (jqxhr.status != 400) {
|
||||
return;
|
||||
}
|
||||
var result = $.parseJSON(jqxhr.responseText);
|
||||
$.each(result, function(id, value) {
|
||||
var fullid = "id_" + id;
|
||||
var $widget = $("#" + fullid);
|
||||
var spanid = fullid + "-error";
|
||||
var $span = $("#" + spanid);
|
||||
|
||||
if (!$widget.parents(".control-group").hasClass("error")) {
|
||||
$widget.parents(".control-group").addClass("error");
|
||||
}
|
||||
if (!$span.length) {
|
||||
$span = $("<span />", {
|
||||
"class": "help-inline",
|
||||
"html": value[0],
|
||||
"id": spanid
|
||||
});
|
||||
$widget.parents(".controls").append($span);
|
||||
} else {
|
||||
$span.html(value[0]);
|
||||
}
|
||||
});
|
||||
if (opts.error_cb) {
|
||||
opts.error_cb(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* The following code prevents a bug under IE7 because fullpath is
|
||||
* returned instead of a relative one. (even if mootools uses
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
<input type="checkbox" class="activator" id="id_{{ field.name }}_act" name="{{ field.name }}_act" {% if activator_value %}checked='checked'{% else %}{% if deactivate_if_empty and field.value == None %}checked='checked'{% endif %}{% endif %}/>{{ activator_label }}
|
||||
</label>{% endif %}
|
||||
{% if field.errors %}<p class="help-block">{% for error in field.errors %}{{ error|safe }}{% endfor %}</p>{% endif %}
|
||||
{% block extra_controls %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<script type="text/javascript">
|
||||
set_static_url("{{ STATIC_URL }}");
|
||||
</script>
|
||||
{% extra_head_content user %}
|
||||
{% extra_static_content "top" user %}
|
||||
{% block html_head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user