Refactored extension API

* Modoboa extensions must now declare themselves
* Now using django 1.3 *staticfiles* module
* Better distinction between *static* content and *media* content

see #197
This commit is contained in:
Antoine Nguyen
2012-05-22 11:40:50 +02:00
parent 184177b405
commit 86f855220a
49 changed files with 441 additions and 436 deletions
+2
View File
@@ -1,6 +1,8 @@
.*~$
^[^\.]+\.pyc
^[^\.]+\.mo
sitestatic
scripts
static/tmp
static/js/ckeditor
settings.py
+22 -29
View File
@@ -10,7 +10,7 @@ from modoboa.lib import parameters, events
from modoboa.lib.exceptions import PermDeniedException
from modoboa.lib.sysutils import exec_cmd, exec_as_vuser
from modoboa.lib.emailutils import split_mailbox
from modoboa.extensions import get_ext_module
from modoboa.extensions import exts_pool
from exceptions import *
import os, sys
import pwd
@@ -806,56 +806,49 @@ class Extension(models.Model):
help_text=ugettext_lazy("Check to enable this extension")
)
def init(self):
module = get_ext_module(self.name)
if hasattr(module, "init"):
module.init()
def __init__(self, *args, **kwargs):
super(Extension, self).__init__(*args, **kwargs)
self.instance = exts_pool.get_extension(self.name)
self.__get_ext_dir()
def load(self):
module = get_ext_module(self.name)
if hasattr(module, "load"):
module.load()
def unload(self):
module = get_ext_module(self.name)
module.destroy()
def __get_ext_dir(self):
modname = self.instance.__module__
path = os.path.realpath(sys.modules[modname].__file__)
self.extdir = os.path.dirname(path)
def on(self):
self.enabled = True
self.save()
self.load()
self.init()
self.instance.load()
self.instance.init()
extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name)
scriptdir = "%s/%s" % (extdir, "scripts")
if self.instance.needs_media:
path = os.path.join(settings.MEDIA_ROOT, self.name)
exec_cmd("mkdir %s" % path)
scriptdir = "%s/%s" % (self.extdir, "scripts")
if os.path.exists(scriptdir):
targetdir = os.path.join(settings.MODOBOA_DIR, "scripts")
exec_cmd("ln -s %s %s/%s" % (scriptdir, targetdir, self.name))
staticpath = "%s/%s" % (extdir, "static")
if os.path.exists(staticpath):
exec_cmd("ln -s %s/static %s/static/%s" % \
(extdir, settings.MODOBOA_DIR, self.name))
events.raiseEvent("ExtEnabled", self)
def off(self):
self.unload()
self.instance.destroy()
self.enabled = False
self.save()
extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name)
scriptdir = "%s/%s" % (extdir, "scripts")
if self.instance.needs_media:
path = os.path.join(settings.MEDIA_ROOT, self.name)
exec_cmd("rm -r %s" % path)
scriptdir = "%s/%s" % (self.extdir, "scripts")
if os.path.exists(scriptdir):
target = "%s/scripts/%s" % (settings.MODOBOA_DIR, self.name)
exec_cmd("rm %s" % target)
staticpath = "%s/%s" % (extdir, "static")
if os.path.exists(staticpath):
exec_cmd("rm -r %s/static/%s" % (settings.MODOBOA_DIR, self.name))
events.raiseEvent("ExtDisabled", self)
def populate_callback(sender, user=None, **kwargs):
+2 -2
View File
@@ -41,11 +41,11 @@ class DomainsTable(tables.Table):
class ExtensionsTable(tables.Table):
idkey = "id"
selection = tables.SelectionColumn("selection", width="4%", header=False)
name = tables.Column("name", label=ugettext_lazy("Name"), width="15%")
label = tables.Column("label", label=ugettext_lazy("Name"), width="15%")
version = tables.Column("version", label=ugettext_lazy("Version"), width="6%")
descr = tables.Column("description", label=ugettext_lazy("Description"))
cols_order = ["selection", "name", "version", "descr"]
cols_order = ["selection", "label", "version", "descr"]
class IdentitiesTable(tables.Table):
idkey = "id"
+2 -2
View File
@@ -494,9 +494,9 @@ def saveparameters(request):
@login_required
@user_passes_test(lambda u: u.is_superuser)
def viewextensions(request, tplname='admin/extensions.html'):
from modoboa.extensions import list_extensions
from modoboa.extensions import exts_pool
exts = list_extensions()
exts = exts_pool.list_all()
for ext in exts:
try:
dbext = Extension.objects.get(name=ext["id"])
+61 -55
View File
@@ -4,65 +4,71 @@ import re
from django.conf import settings
from django.conf.urls.defaults import include
def isinstalled(ext):
return "modoboa.extensions.%s" % ext in settings.INSTALLED_APPS
class ModoExtension(object):
"""Base extension class
def get_ext_module(extname):
modname = "modoboa.extensions.%s" % extname
if not sys.modules.has_key(modname):
__import__(modname)
return sys.modules[modname]
Each Modoboa extension must inherit from this class to be
considered as valid.
"""
name = None
version = "0.1"
description = ""
url = None
needs_media = False
def get_extension_infos(name):
return get_ext_module(name).infos()
def infos(self):
return dict(name=self.name, label=self.label, version=self.version,
description=self.description, url=self.url)
def loadextensions(prefix):
# To avoid a circular import...
from modoboa.admin.models import Extension
def init(self):
pass
basedir = "%s/extensions" % settings.MODOBOA_DIR
result = []
for f in os.listdir(basedir):
if not os.path.isdir("%s/%s" % (basedir, f)):
continue
if not isinstalled(f):
continue
try:
ext = Extension.objects.get(name=f)
if ext.enabled:
ext.load()
except Extension.DoesNotExist:
pass
module = get_ext_module(f)
baseurl = module.baseurl if hasattr(module, "baseurl") \
else f
result += [(r'%s%s/' % (prefix, baseurl),
include("modoboa.extensions.%s.urls" % f))]
return result
def destroy(self):
pass
def load(self):
pass
def list_extensions():
basedir = "%s/extensions" % settings.MODOBOA_DIR
result = []
for d in os.listdir(basedir):
if not os.path.isdir("%s/%s" % (basedir, d)):
continue
if not isinstalled(d):
continue
isvalid = True
for f in ['urls.py', '__init__.py', 'views.py']:
if not os.path.exists("%s/%s/%s" % (basedir, d, f)):
isvalid = False
break
if isvalid:
module = __import__(d, globals(), locals())
class ExtensionsPool(object):
"""The extensions manager"""
def __init__(self):
self.extensions = dict()
def register_extension(self, ext):
self.extensions[ext.name] = dict(cls=ext)
def get_extension(self, name):
if not self.extensions.has_key(name):
return None
if not self.extensions[name].has_key("instance"):
self.extensions[name]["instance"] = self.extensions[name]["cls"]()
return self.extensions[name]["instance"]
def load_all(self, prefix):
from modoboa.admin.models import Extension
result = []
for extname, extdef in self.extensions.iteritems():
extinstance = self.get_extension(extname)
try:
infos = module.infos()
infos["id"] = d
if os.path.isdir("%s/%s/templates" % (basedir, d)):
infos["templates"] = True
else:
infos["templates"] = False
result += [infos]
except AttributeError:
ext = Extension.objects.get(name=extname)
if ext.enabled:
extinstance.load()
except Extension.DoesNotExist:
pass
return result
baseurl = extinstance.url if extinstance.url is not None else extname
result += [(r'%s%s/' % (prefix, baseurl),
include("%s.urls" % extinstance.__module__))]
return result
def list_all(self):
result = []
for extname, extdef in self.extensions.iteritems():
infos = extdef["instance"].infos()
infos["id"] = extname
result += [infos]
return sorted(result, key=lambda i: i["name"])
exts_pool = ExtensionsPool()
+67 -67
View File
@@ -7,78 +7,78 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from django.core.urlresolvers import reverse
from django.template import Template, Context
from modoboa.lib import events, parameters
from modoboa.extensions import ModoExtension, exts_pool
baseurl = "quarantine"
class AmavisQuarantine(ModoExtension):
name = "amavis_quarantine"
label = "Amavis quarantine"
version = "1.0"
description = ugettext_lazy("Simple amavis quarantine management tool")
url = "quarantine"
def infos():
return {
"name" : "Amavis quarantine",
"version" : "1.0",
"description" : _("Simple amavis quarantine management tool"),
"url" : "quarantine"
}
def init(self):
"""Init function
Only run once, when the extension is enabled. We create records
for existing mailboxes in order Amavis considers them local.
"""
from modoboa.admin.models import Mailbox
from models import Users
for mb in Mailbox.objects.all():
try:
u = Users.objects.get(email=mb.full_address)
except Users.DoesNotExist:
u = Users()
u.email = mb.full_address
u.fullname = mb.user.fullname
u.local = "1"
u.priority = 7
u.policy_id = 1
u.save()
def init():
"""Init function
def load(self):
parameters.register_admin(
"MAX_MESSAGES_AGE", type="int",
deflt=14,
help=_("Quarantine messages maximum age (in days) before deletion")
)
parameters.register_admin(
"RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no",
help=_("Remove messages marked as released while cleaning up the database")
)
parameters.register_admin("AM_PDP_MODE", type="list",
deflt="unix",
values=[("inet", "inet"), ("unix", "unix")],
help=ugettext_lazy("Mode used to access the PDP server"))
parameters.register_admin("AM_PDP_HOST", type="string",
deflt="localhost",
help=ugettext_lazy("PDP server address (if inet mode)"))
parameters.register_admin("AM_PDP_PORT", type="int",
deflt=9998,
help=ugettext_lazy("PDP server port (if inet mode)"))
parameters.register_admin("AM_PDP_SOCKET", type="string",
deflt="/var/amavis/amavisd.sock",
help=ugettext_lazy("Path to the PDP server socket (if unix mode)"))
parameters.register_admin("CHECK_REQUESTS_INTERVAL", type="int",
deflt=30,
help=ugettext_lazy("Interval between two release requests checks"))
parameters.register_admin("USER_CAN_RELEASE", type="list_yesno", deflt="no",
help=ugettext_lazy("Allow users to directly release their messages"))
parameters.register_admin("SELF_SERVICE", type="list_yesno", deflt="no",
help=ugettext_lazy("Activate the 'self-service' mode"))
parameters.register_user(
"MESSAGES_PER_PAGE", type="int", deflt=40,
label="Number of displayed emails per page",
help=ugettext_lazy("Sets the maximum number of messages displayed in a page")
)
Only run once, when the extension is enabled. We create records
for existing mailboxes in order Amavis considers them local.
"""
from modoboa.admin.models import Mailbox
from models import Users
def destroy(self):
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("amavis_quarantine")
for mb in Mailbox.objects.all():
try:
u = Users.objects.get(email=mb.full_address)
except Users.DoesNotExist:
u = Users()
u.email = mb.full_address
u.fullname = mb.user.fullname
u.local = "1"
u.priority = 7
u.policy_id = 1
u.save()
def load():
parameters.register_admin(
"MAX_MESSAGES_AGE", type="int",
deflt=14,
help=_("Quarantine messages maximum age (in days) before deletion")
)
parameters.register_admin(
"RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no",
help=_("Remove messages marked as released while cleaning up the database")
)
parameters.register_admin("AM_PDP_MODE", type="list",
deflt="unix",
values=[("inet", "inet"), ("unix", "unix")],
help=ugettext_lazy("Mode used to access the PDP server"))
parameters.register_admin("AM_PDP_HOST", type="string",
deflt="localhost",
help=ugettext_lazy("PDP server address (if inet mode)"))
parameters.register_admin("AM_PDP_PORT", type="int",
deflt=9998,
help=ugettext_lazy("PDP server port (if inet mode)"))
parameters.register_admin("AM_PDP_SOCKET", type="string",
deflt="/var/amavis/amavisd.sock",
help=ugettext_lazy("Path to the PDP server socket (if unix mode)"))
parameters.register_admin("CHECK_REQUESTS_INTERVAL", type="int",
deflt=30,
help=ugettext_lazy("Interval between two release requests checks"))
parameters.register_admin("USER_CAN_RELEASE", type="list_yesno", deflt="no",
help=ugettext_lazy("Allow users to directly release their messages"))
parameters.register_admin("SELF_SERVICE", type="list_yesno", deflt="no",
help=ugettext_lazy("Activate the 'self-service' mode"))
parameters.register_user(
"MESSAGES_PER_PAGE", type="int", deflt=40,
label="Number of displayed emails per page",
help=ugettext_lazy("Sets the maximum number of messages displayed in a page")
)
def destroy():
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("amavis_quarantine")
exts_pool.register_extension(AmavisQuarantine)
@events.observe("UserMenuDisplay")
def menu(target, user):
@@ -1,11 +1,11 @@
{% extends "fixed.html" %}{% load i18n %}
{% block pagetitle %}{% trans "Quarantine management" %}{% endblock %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}amavis_quarantine/css/quarantine.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.htmltable.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/searchbar.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}amavis_quarantine/js/quarantine.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}amavis_quarantine/css/quarantine.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.htmltable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/searchbar.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}amavis_quarantine/js/quarantine.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var qm = new Quarantine({
@@ -3,19 +3,19 @@
<head>
<meta charset="utf-8" />
<title>Modoboa</title>
<link rel="icon" type="image/png" href="{{ MEDIA_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}amavis_quarantine/css/selfservice.css" />
<script src="{{ MEDIA_URL }}js/jquery.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/bootstrap.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/notify.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}amavis_quarantine/js/selfservice.js" type="text/javascript"></script>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}amavis_quarantine/css/selfservice.css" />
<script src="{{ STATIC_URL }}js/jquery.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/notify.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}amavis_quarantine/js/selfservice.js" type="text/javascript"></script>
{% url django.views.i18n.javascript_catalog as catalog_url %}
<script type="text/javascript" src="{{ catalog_url }}"></script>
<script type="text/javascript">
set_media_url("{{ MEDIA_URL }}");
set_media_url("{{ STATIC_URL }}");
</script>
</head>
<body>
@@ -103,7 +103,7 @@ def quar_menu(user):
else:
extraopts = []
extracontent = render_to_string('common/email_searchbar.html', {
"MEDIA_URL" : settings.MEDIA_URL,
"STATIC_URL" : settings.STATIC_URL,
"extraopts" : extraopts
})
+35 -37
View File
@@ -10,51 +10,49 @@ from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from modoboa.lib import events
from modoboa.lib.webutils import static_url
from modoboa.extensions import ModoExtension, exts_pool
from models import *
from forms import ResourcePoolForm
import controls
import views
baseurl = "limits"
class Limits(ModoExtension):
name = "limits"
label = "Limits"
version = "1.0"
description = ugettext_lazy("Per administrator resources to limit the number of objects they can create")
def init():
from modoboa.admin.models import User, Domain
def init(self):
from modoboa.admin.models import User, Domain
ct = ContentType.objects.get(app_label="admin", model="domain")
dagrp = Group.objects.get(name="DomainAdmins")
grp = Group(name="Resellers")
grp.save()
grp.permissions.add(*dagrp.permissions.all())
ct = ContentType.objects.get_for_model(Permission)
for pname in ["view_permissions"]:
perm = Permission.objects.get(content_type=ct, codename=pname)
grp.permissions.add(perm)
ct = ContentType.objects.get_for_model(Domain)
for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]:
perm = Permission.objects.get(content_type=ct, codename=pname)
grp.permissions.add(perm)
grp.save()
for user in User.objects.filter(groups__name='DomainAdmins'):
try:
controls.create_pool(user)
except IntegrityError:
pass
ct = ContentType.objects.get(app_label="admin", model="domain")
dagrp = Group.objects.get(name="DomainAdmins")
def destroy(self):
Group.objects.get(name="Resellers").delete()
grp = Group(name="Resellers")
grp.save()
grp.permissions.add(*dagrp.permissions.all())
ct = ContentType.objects.get_for_model(Permission)
for pname in ["view_permissions"]:
perm = Permission.objects.get(content_type=ct, codename=pname)
grp.permissions.add(perm)
ct = ContentType.objects.get_for_model(Domain)
for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]:
perm = Permission.objects.get(content_type=ct, codename=pname)
grp.permissions.add(perm)
grp.save()
for user in User.objects.filter(groups__name='DomainAdmins'):
try:
controls.create_pool(user)
except IntegrityError:
pass
def destroy():
Group.objects.get(name="Resellers").delete()
def infos():
return {
"name" : "Limits",
"version" : "1.0",
"description" : _("Per administrator resources to limit the number of objects they can create"),
"url" : baseurl
}
exts_pool.register_extension(Limits)
@events.observe("GetExtraRoles")
def get_extra_roles(user):
+37 -37
View File
@@ -10,49 +10,49 @@ from django.conf.urls.defaults import include
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy, get_language
from modoboa.lib import events, parameters
from modoboa.lib.webutils import static_url
from modoboa.extensions import ModoExtension, exts_pool
from models import *
def infos():
return {
"name" : "Postfix autoreply",
"version" : "1.0",
"description" : _("Auto-reply (vacation) functionality using Postfix"),
"url" : "postfix_autoreply"
}
class PostfixAutoreply(ModoExtension):
name = "postfix_autoreply"
label = "Postfix autoreply"
version = "1.0"
description = ugettext_lazy("Auto-reply (vacation) functionality using Postfix")
def init():
from modoboa.admin.models import Domain
for dom in Domain.objects.all():
try:
trans = Transport.objects.get(domain="autoreply.%s" % dom.name)
except Transport.DoesNotExist:
onCreateDomain(None, dom)
else:
continue
for mb in dom.mailbox_set.all():
def init(self):
from modoboa.admin.models import Domain
for dom in Domain.objects.all():
try:
alias = Alias.objects.get(full_address=mb.full_address)
except Alias.DoesNotExist:
onCreateMailbox(None, mb)
trans = Transport.objects.get(domain="autoreply.%s" % dom.name)
except Transport.DoesNotExist:
onCreateDomain(None, dom)
else:
continue
def load():
parameters.register_admin(
"AUTOREPLIES_TIMEOUT",
type="int", deflt=86400,
help=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient")
)
for mb in dom.mailbox_set.all():
try:
alias = Alias.objects.get(full_address=mb.full_address)
except Alias.DoesNotExist:
onCreateMailbox(None, mb)
def destroy():
events.unregister("CreateDomain", onCreateDomain)
events.unregister("DeleteDomain", onDeleteDomain)
events.unregister("CreateMailbox", onCreateMailbox)
events.unregister("DeleteMailbox", onDeleteMailbox)
events.unregister("ModifyMailbox", onModifyMailbox)
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("postfix_autoreply")
def load(self):
parameters.register_admin(
"AUTOREPLIES_TIMEOUT",
type="int", deflt=86400,
help=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient")
)
def destroy(self):
events.unregister("CreateDomain", onCreateDomain)
events.unregister("DeleteDomain", onDeleteDomain)
events.unregister("CreateMailbox", onCreateMailbox)
events.unregister("DeleteMailbox", onDeleteMailbox)
events.unregister("ModifyMailbox", onModifyMailbox)
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("postfix_autoreply")
exts_pool.register_extension(PostfixAutoreply)
@events.observe("UserMenuDisplay")
def menu(target, user):
+32 -33
View File
@@ -2,46 +2,45 @@
from django.utils.translation import ugettext as _, ugettext_lazy
from django.core.urlresolvers import reverse
from modoboa.lib import events, parameters
from modoboa.lib.webutils import static_url
from modoboa.extensions import ModoExtension, exts_pool
from sievelib.managesieve import SUPPORTED_AUTH_MECHS
baseurl = "sfilters"
class SieveFilters(ModoExtension):
name = "sievefilters"
label = "Sieve filters"
version = "1.0"
description = ugettext_lazy("Plugin to easily create server-side filters")
url = "sfilters"
def load():
parameters.register_app(uparams_opts=dict(needs_mailbox=True))
def load(self):
parameters.register_app(uparams_opts=dict(needs_mailbox=True))
parameters.register_admin("SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your MANAGESIEVE server"))
parameters.register_admin("PORT", type="int", deflt="2000",
help=ugettext_lazy("Listening port of your MANAGESIEVE server"))
parameters.register_admin("STARTTLS", type="list_yesno", deflt="no",
help=ugettext_lazy("Use the STARTTLS extension"))
parameters.register_admin("SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your MANAGESIEVE server"))
parameters.register_admin("PORT", type="int", deflt="2000",
help=ugettext_lazy("Listening port of your MANAGESIEVE server"))
parameters.register_admin("STARTTLS", type="list_yesno", deflt="no",
help=ugettext_lazy("Use the STARTTLS extension"))
values = [('AUTO', 'auto')]
for m in SUPPORTED_AUTH_MECHS:
values += [(m, m.lower())]
parameters.register_admin("AUTHENTICATION_MECH", type="list", deflt="auto",
values=values,
help=ugettext_lazy("Prefered authentication mechanism"))
values = [('AUTO', 'auto')]
for m in SUPPORTED_AUTH_MECHS:
values += [(m, m.lower())]
parameters.register_admin("AUTHENTICATION_MECH", type="list", deflt="auto",
values=values,
help=ugettext_lazy("Prefered authentication mechanism"))
# User parameters
parameters.register_user("EDITOR_MODE", type="list", deflt="raw",
label=ugettext_lazy("Editor mode"),
values=[("raw", "raw"), ("gui", "simplified")],
help=ugettext_lazy("Select the mode you want the editor to work in"))
# User parameters
parameters.register_user("EDITOR_MODE", type="list", deflt="raw",
label=ugettext_lazy("Editor mode"),
values=[("raw", "raw"), ("gui", "simplified")],
help=ugettext_lazy("Select the mode you want the editor to work in"))
def destroy():
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("sievefilters")
def destroy(self):
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("sievefilters")
def infos():
return {
"name" : "Sieve filters",
"version" : "1.0",
"description" : _("Plugin to easily create server-side filters"),
"url" : "sfilters"
}
exts_pool.register_extension(SieveFilters)
@events.observe("UserMenuDisplay")
def menu(target, user):
@@ -1,11 +1,11 @@
{% extends "twocols.html" %}{% load i18n sfilters_tags %}
{% block pagetitle %}{% trans "Message filters" %}{% endblock %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}sievefilters/css/style.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}sievefilters/js/lib.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/dynamic_input.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}sievefilters/js/jquery.dynfields.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}sievefilters/css/style.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}sievefilters/js/lib.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/dynamic_input.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}sievefilters/js/jquery.dynfields.js"></script>
<script type="text/javascript">
var history = new History({
deflocation: "{{ default_script }}",
+23 -23
View File
@@ -11,32 +11,32 @@ from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy
from django.conf.urls.defaults import *
from modoboa.lib import events, parameters
from modoboa.lib.webutils import static_url
from modoboa.extensions import ModoExtension, exts_pool
def infos():
return {
"name" : "Statistics",
"version" : "1.0",
"description" : _("Graphical statistics about emails traffic using RRDtool"),
"url" : "stats"
}
class Stats(ModoExtension):
name = "stats"
label = "Statistics"
version = "1.0"
description = ugettext_lazy("Graphical statistics about emails traffic using RRDtool")
def load():
parameters.register_admin("LOGFILE", type="string",
deflt="/var/log/mail.log",
help=ugettext_lazy("Path to log file used to collect statistics"))
parameters.register_admin("RRD_ROOTDIR", type="string",
deflt="/tmp/modoboa",
help=ugettext_lazy("Path to directory where RRD files are stored"))
parameters.register_admin("IMG_ROOTDIR", type="string",
deflt="/tmp/modoboa",
help=ugettext_lazy("Path to directory where PNG files are stored"))
parameters.register_admin("GRAPHS_LOCATION", type="string",
deflt="graphs",
help=ugettext_lazy("Graphics location (to build URLs)"))
def load(self):
parameters.register_admin("LOGFILE", type="string",
deflt="/var/log/mail.log",
help=ugettext_lazy("Path to log file used to collect statistics"))
parameters.register_admin("RRD_ROOTDIR", type="string",
deflt="/tmp/modoboa",
help=ugettext_lazy("Path to directory where RRD files are stored"))
parameters.register_admin("IMG_ROOTDIR", type="string",
deflt="/tmp/modoboa",
help=ugettext_lazy("Path to directory where PNG files are stored"))
parameters.register_admin("GRAPHS_LOCATION", type="string",
deflt="graphs",
help=ugettext_lazy("Graphics location (to build URLs)"))
def destroy():
events.unregister("AdminMenuDisplay", menu)
def destroy(self):
events.unregister("AdminMenuDisplay", menu)
exts_pool.register_extension(Stats)
@events.observe("UserMenuDisplay")
def menu(target, user):
+1 -1
View File
@@ -4,6 +4,6 @@
{% endif %}
{% for elems in graphs %}
<h3>{% trans elems.label %}</h3>
<p><img src="{{ MEDIA_URL }}{{ graphs_loc }}/{{ elems.name }}_{{ domain }}_AVERAGE_{% ifequal period 'custom' %}{{ period_name }}{% else %}{{ period }}{% endifequal %}.png"
<p><img src="{{ STATIC_URL }}{{ graphs_loc }}/{{ elems.name }}_{{ domain }}_AVERAGE_{% ifequal period 'custom' %}{{ period_name }}{% else %}{{ period }}{% endifequal %}.png"
alt="{% trans 'No statistics available' %}" /></p>
{% endfor %}
+3 -3
View File
@@ -1,9 +1,9 @@
{% extends "fixed.html" %}{% load i18n %}
{% block pagetitle %}{% trans "Graphical statistics" %}{% endblock %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}stats/css/stats.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}stats/js/stats.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}stats/css/stats.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}stats/js/stats.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var stats = new Stats({
+69 -67
View File
@@ -3,76 +3,78 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from django.core.urlresolvers import reverse
from modoboa.lib import events, parameters
from modoboa.lib.webutils import static_url
from modoboa.extensions import ModoExtension, exts_pool
def load():
parameters.register_app(uparams_opts=dict(needs_mailbox=True))
class Webmail(ModoExtension):
name = "webmail"
label = "Webmail"
version = "1.0"
description = ugettext_lazy("Simple IMAP webmail")
needs_media = True
def load(self):
parameters.register_app(uparams_opts=dict(needs_mailbox=True))
parameters.register_admin("IMAP_SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your IMAP server"))
parameters.register_admin("IMAP_SECURED", type="list_yesno",
deflt="no",
help=ugettext_lazy("Use a secured connection to access IMAP server"))
parameters.register_admin("IMAP_PORT", type="int", deflt="143",
help=ugettext_lazy("Listening port of your IMAP server"))
parameters.register_admin("SMTP_SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your SMTP server"))
parameters.register_admin("SMTP_SECURED_MODE", type="list",
values=[("none", ugettext_lazy("None")),
("starttls", "STARTTLS"),
("ssl", "SSL/TLS")],
deflt="none",
help=ugettext_lazy("Use a secured connection to access SMTP server"))
parameters.register_admin("SMTP_PORT", type="int", deflt="25",
help=ugettext_lazy("Listening port of your SMTP server"))
parameters.register_admin("SMTP_AUTHENTICATION", type="list_yesno",
deflt="no",
help=ugettext_lazy("Server needs authentication"))
parameters.register_admin("MAX_ATTACHMENT_SIZE", type="int", deflt=2048,
help=ugettext_lazy("Maximum attachment size in bytes (or KB, MB, GB if specified)"))
parameters.register_user("MESSAGES_PER_PAGE", type="int", deflt=40,
label=ugettext_lazy("Number of displayed emails per page"),
help=ugettext_lazy("Sets the maximum number of messages displayed in a page"))
parameters.register_user("REFRESH_INTERVAL", type="int", deflt=300,
label=ugettext_lazy("Listing refresh rate"),
help=ugettext_lazy("Automatic folder refresh rate (in seconds)"))
parameters.register_user("TRASH_FOLDER", type="string", deflt="Trash",
label=ugettext_lazy("Trash folder"),
help=ugettext_lazy("Folder where deleted messages go"))
parameters.register_user("SENT_FOLDER", type="string", deflt="Sent",
label=ugettext_lazy("Sent folder"),
help=ugettext_lazy("Folder where copies of sent messages go"))
parameters.register_user("DRAFTS_FOLDER", type="string", deflt="Drafts",
label=ugettext_lazy("Drafts folder"),
help=ugettext_lazy("Folder where drafts go"))
parameters.register_user("SIGNATURE", type="text", deflt="",
label=ugettext_lazy("Signature text"),
help=ugettext_lazy("User defined email signature"))
parameters.register_user("EDITOR", type="list", deflt="plain",
label=ugettext_lazy("Default editor"),
values=[("html", "html"), ("plain", "text")],
help=ugettext_lazy("The default editor to use when composing a message"))
parameters.register_user("DISPLAYMODE", type="list", deflt="plain",
label=ugettext_lazy("Default message display mode"),
values=[("html", "html"), ("plain", "text")],
help=ugettext_lazy("The default mode used when displaying a message"))
parameters.register_user("ENABLE_LINKS", type="list_yesno", deflt="no",
label=ugettext_lazy("Enable HTML links display"),
help=ugettext_lazy("Enable/Disable HTML links display"))
parameters.register_admin("IMAP_SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your IMAP server"))
parameters.register_admin("IMAP_SECURED", type="list_yesno",
deflt="no",
help=ugettext_lazy("Use a secured connection to access IMAP server"))
parameters.register_admin("IMAP_PORT", type="int", deflt="143",
help=ugettext_lazy("Listening port of your IMAP server"))
parameters.register_admin("SMTP_SERVER", type="string",
deflt="127.0.0.1",
help=ugettext_lazy("Address of your SMTP server"))
parameters.register_admin("SMTP_SECURED_MODE", type="list",
values=[("none", ugettext_lazy("None")),
("starttls", "STARTTLS"),
("ssl", "SSL/TLS")],
deflt="none",
help=ugettext_lazy("Use a secured connection to access SMTP server"))
parameters.register_admin("SMTP_PORT", type="int", deflt="25",
help=ugettext_lazy("Listening port of your SMTP server"))
parameters.register_admin("SMTP_AUTHENTICATION", type="list_yesno",
deflt="no",
help=ugettext_lazy("Server needs authentication"))
parameters.register_admin("MAX_ATTACHMENT_SIZE", type="int", deflt=2048,
help=ugettext_lazy("Maximum attachment size in bytes (or KB, MB, GB if specified)"))
parameters.register_user("MESSAGES_PER_PAGE", type="int", deflt=40,
label=ugettext_lazy("Number of displayed emails per page"),
help=ugettext_lazy("Sets the maximum number of messages displayed in a page"))
parameters.register_user("REFRESH_INTERVAL", type="int", deflt=300,
label=ugettext_lazy("Listing refresh rate"),
help=ugettext_lazy("Automatic folder refresh rate (in seconds)"))
parameters.register_user("TRASH_FOLDER", type="string", deflt="Trash",
label=ugettext_lazy("Trash folder"),
help=ugettext_lazy("Folder where deleted messages go"))
parameters.register_user("SENT_FOLDER", type="string", deflt="Sent",
label=ugettext_lazy("Sent folder"),
help=ugettext_lazy("Folder where copies of sent messages go"))
parameters.register_user("DRAFTS_FOLDER", type="string", deflt="Drafts",
label=ugettext_lazy("Drafts folder"),
help=ugettext_lazy("Folder where drafts go"))
parameters.register_user("SIGNATURE", type="text", deflt="",
label=ugettext_lazy("Signature text"),
help=ugettext_lazy("User defined email signature"))
parameters.register_user("EDITOR", type="list", deflt="plain",
label=ugettext_lazy("Default editor"),
values=[("html", "html"), ("plain", "text")],
help=ugettext_lazy("The default editor to use when composing a message"))
parameters.register_user("DISPLAYMODE", type="list", deflt="plain",
label=ugettext_lazy("Default message display mode"),
values=[("html", "html"), ("plain", "text")],
help=ugettext_lazy("The default mode used when displaying a message"))
parameters.register_user("ENABLE_LINKS", type="list_yesno", deflt="no",
label=ugettext_lazy("Enable HTML links display"),
help=ugettext_lazy("Enable/Disable HTML links display"))
def destroy(self):
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("webmail")
def destroy():
events.unregister("UserMenuDisplay", menu)
parameters.unregister_app("webmail")
def infos():
return {
"name" : "Webmail",
"version" : "1.0",
"description" : _("Simple IMAP webmail"),
"url" : "webmail"
}
exts_pool.register_extension(Webmail)
@events.observe("UserMenuDisplay")
def menu(target, user):
+5 -6
View File
@@ -25,7 +25,7 @@ class WMtable(tables.Table):
idkey = "imapid"
select = tables.ImgColumn(
"select", cssclass="draggable left", width="1%",
defvalue="%spics/grippy.png" % settings.MEDIA_URL,
defvalue="%spics/grippy.png" % settings.STATIC_URL,
header="<input type='checkbox' name='toggleselect' id='toggleselect' />"
)
flags = tables.ImgColumn("flags", width="4%")
@@ -458,9 +458,8 @@ def find_images_in_body(body):
if src is None:
continue
o = urlparse(src)
dirname = os.path.dirname(o.path).replace(settings.MEDIA_URL, "")
fname = os.path.basename(o.path)
path = os.path.join(settings.MEDIA_ROOT, dirname, fname)
path = os.path.join(settings.MEDIA_ROOT, "webmail", fname)
if not os.path.exists(path):
continue
cid = "%s@modoboa" % os.path.splitext(fname)[0]
@@ -504,7 +503,7 @@ def save_attachment(f):
"""
from tempfile import NamedTemporaryFile
dstdir = os.path.join(settings.MEDIA_ROOT, "tmp")
dstdir = os.path.join(settings.MEDIA_ROOT, "webmail")
try:
fp = NamedTemporaryFile(dir=dstdir, delete=False)
except Exception, e:
@@ -521,7 +520,7 @@ def clean_attachments(attlist):
(random name, real name).
"""
for att in attlist:
fullpath = os.path.join(settings.MEDIA_ROOT, "tmp", att["tmpname"])
fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"])
try:
os.remove(fullpath)
except OSError, e:
@@ -569,7 +568,7 @@ def create_mail_attachment(attdef):
maintype, subtype = attdef["content-type"].split("/")
res = MIMEBase(maintype, subtype)
fp = open(os.path.join(settings.MEDIA_ROOT, "tmp", attdef["tmpname"]), "rb")
fp = open(os.path.join(settings.MEDIA_ROOT, "webmail", attdef["tmpname"]), "rb")
res.set_payload(fp.read())
fp.close()
Encoders.encode_base64(res)
@@ -813,7 +813,7 @@ Webmail.prototype = {
CKEDITOR.remove(instance);
}
CKEDITOR.replace(this.editorid, {
customConfig: static_url("js/editor_config.js")
customConfig: get_static_url("js/editor_config.js")
});
CKEDITOR.on("instanceReady", $.proxy(function(evt) {
this.resize_editor();
@@ -6,7 +6,7 @@
<input type="button" class="btn btn-primary" id="submit" name="submit" value="{% trans 'Attach' %}" />
</div>
<div id="upload_status" style="display:none">
<img src="{{ MEDIA_URL }}css/spinner.gif" /> {% trans "Uploading.." %}
<img src="{{ STATIC_URL }}css/spinner.gif" /> {% trans "Uploading.." %}
</div>
<iframe id="upload_target" name="upload_target" src="#"
style="width:0;height:0;border:0px solid #fff;"></iframe>
@@ -12,7 +12,7 @@
{% render_field form.subject %}
<div id="attachments" name="{% url modoboa.extensions.webmail.views.attachments %}">
<div>
<img src="{{ MEDIA_URL }}pics/attachment.png" />
<img src="{{ STATIC_URL }}pics/attachment.png" />
{% trans "Attachments" %}
</div>
<div id="list">
@@ -1,13 +1,13 @@
{% extends "fixed.html" %}{% load i18n libextras %}
{% block pagetitle %}Webmail{% endblock %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}webmail/css/webmail.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery-ui.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.htmltable.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/searchbar.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}webmail/js/webmail.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}webmail/css/webmail.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery-ui.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.htmltable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/searchbar.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}webmail/js/webmail.js"></script>
<script type="text/javascript">
var webmail = undefined;
+1 -1
View File
@@ -106,7 +106,7 @@ def listmailbox_menu(selection, folder, user):
"url" : reverse(webmail.views.empty, args=[folder])}
]
searchbar = render_to_string('common/email_searchbar.html', {
"MEDIA_URL" : settings.MEDIA_URL
"STATIC_URL" : settings.STATIC_URL
})
return render_to_string('common/buttons_list.html', dict(
selection=selection, entries=entries, extracontent=searchbar,
+1 -1
View File
@@ -253,7 +253,7 @@ def delattachment(request):
for att in request.session["compose_mail"]["attachments"]:
if att["tmpname"] == request.GET["name"]:
request.session["compose_mail"]["attachments"].remove(att)
fullpath = os.path.join(settings.MEDIA_ROOT, "tmp", att["tmpname"])
fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"])
try:
os.remove(fullpath)
except OSError, e:
+1 -1
View File
@@ -110,7 +110,7 @@ class EmailListing(object):
def render_navbar(page, baseurl=None):
if page is None:
return ""
context = {"page" : page, "MEDIA_URL" : settings.MEDIA_URL, "baseurl" : baseurl}
context = {"page" : page, "STATIC_URL" : settings.STATIC_URL, "baseurl" : baseurl}
return render_to_string("common/pagination_bar.html", context)
def fetch(self, request, id_start, id_stop):
+2 -2
View File
@@ -17,7 +17,7 @@ def _render(request, tpl, user_context):
"""Custom rendering function
Just a wrapper which automatically adds a RequestContext instance
(useful to use settings variables like MEDIA_URL inside templates)
(useful to use settings variables like STATIC_URL inside templates)
"""
return render_to_response(tpl, user_context,
context_instance=template.RequestContext(request))
@@ -109,7 +109,7 @@ def static_url(path):
"""
if path.startswith("/"):
path = path[1:]
return "%s%s" % (settings.MEDIA_URL, path)
return "%s%s" % (settings.STATIC_URL, path)
def size2integer(value):
"""Try to convert a string representing a size to an integer value
+6 -2
View File
@@ -64,12 +64,15 @@ LOGIN_REDIRECT_URL = '/modoboa/admin/'
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = os.path.join(MODOBOA_DIR, 'static')
MEDIA_ROOT = os.path.join(MODOBOA_DIR, 'media')
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = '/static/'
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(MODOBOA_DIR, 'sitestatic')
STATIC_URL = '/static/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
@@ -112,6 +115,7 @@ INSTALLED_APPS = (
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'south',
'modoboa',
'modoboa.auth',
+5 -5
View File
@@ -2,22 +2,22 @@
* Global javascript utilities
*/
var media_url = "";
var static_url = "";
/*
* A simple function to initialize the value of the global variable
* 'media_url' (corresponding to django's MEDIA_URL variable).
*/
function set_media_url(url) {
media_url = url;
function set_static_url(url) {
static_url = url;
}
/*
* Shortcut function that construct an url from the media_url and the
* given value.
*/
function static_url(value) {
return media_url + value;
function get_static_url(value) {
return static_url + value;
}
function modalbox(e, css, defhref, defcb, defclosecb) {
+2 -2
View File
@@ -1,5 +1,5 @@
{% extends "twocols.html" %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/admin.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/admin.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/admin.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/admin.js"></script>
{% endblock %}
+2 -2
View File
@@ -2,8 +2,8 @@
{% block pagetitle %}{% trans "Identities" %}{% endblock %}
{% block html_head %}
{{ block.super }}
<script type="text/javascript" src="{{ MEDIA_URL }}js/creation_wizard.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/autocompleter.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/creation_wizard.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/autocompleter.js"></script>
<script type="text/javascript">
function get_domains_list(container) {
$.getJSON("{% url modoboa.admin.views.domains_list %}", function(data) {
+1 -1
View File
@@ -9,7 +9,7 @@
<iframe id="import_target" name="import_target" src="#"
style="width:0;height:0;border:0px solid #fff;"></iframe>
<div id="import_status" style="text-align: center;display:none">
<img src="{{MEDIA_URL}}css/spinner.gif" /> {% trans "Importing..." %}
<img src="{{STATIC_URL}}css/spinner.gif" /> {% trans "Importing..." %}
</div>
<div id="import_result"></div>
{% endblock %}
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "admin/header.html" %}{% load i18n admin_extras libextras %}
{% block html_head %}
{{ block.super }}
<script type="text/javascript" src="{{ MEDIA_URL }}js/dynamic_input.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/dynamic_input.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#searchquery").focus(function() {
+1 -1
View File
@@ -7,7 +7,7 @@
<th id="{{ c.name }}" width="{{ c.width }}">
{% endif %}
{{ c|safe }}
<span style="float: right"><img style="display: none" alt="" src="{{ MEDIA_URL }}pics/transparent.png" /></span>
<span style="float: right"><img style="display: none" alt="" src="{{ STATIC_URL }}pics/transparent.png" /></span>
</th>
{% endfor %}
</tr>
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/viewmail.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/viewmail.css" />
</head>
<body>
<div id="table-container">
+14 -14
View File
@@ -3,23 +3,23 @@
<head>
<meta charset="utf-8" />
<title>{% block pagetitle %}{% endblock %}</title>
<link rel="icon" type="image/png" href="{{ MEDIA_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/datepicker.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/custom.css" />
<script src="{{ MEDIA_URL }}js/jquery.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/bootstrap.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/bootstrap-datepicker.js" type="text/javascript"></script>
{% if LANGUAGE_CODE != "en" %}<script src="{{ MEDIA_URL }}js/locales/bootstrap-datepicker.{{ LANGUAGE_CODE }}.js" type="text/javascript"></script>{% endif %}
<script src="{{ MEDIA_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/confirmation.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/notify.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}js/poller.js" type="text/javascript"></script>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/datepicker.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/custom.css" />
<script src="{{ STATIC_URL }}js/jquery.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap-datepicker.js" type="text/javascript"></script>
{% if LANGUAGE_CODE != "en" %}<script src="{{ STATIC_URL }}js/locales/bootstrap-datepicker.{{ LANGUAGE_CODE }}.js" type="text/javascript"></script>{% endif %}
<script src="{{ STATIC_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/confirmation.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/notify.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/poller.js" type="text/javascript"></script>
{% url django.views.i18n.javascript_catalog as catalog_url %}
<script type="text/javascript" src="{{ catalog_url }}"></script>
<script type="text/javascript">
set_media_url("{{ MEDIA_URL }}");
set_static_url("{{ STATIC_URL }}");
</script>
{% extra_head_content user %}
{% block html_head %}{% endblock %}
+7 -7
View File
@@ -3,12 +3,12 @@
<head>
<meta charset="utf-8" />
<title>{% trans "Welcome to Modoboa" %}</title>
<link rel="icon" type="image/png" href="{{ MEDIA_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/login.css" />
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/bootstrap.js"></script>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/login.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/bootstrap.js"></script>
{% url django.views.i18n.javascript_catalog as catalog_url %}
<script type="text/javascript" src="{{ catalog_url }}"></script>
<script type="text/javascript">
@@ -22,7 +22,7 @@
<div class="offset4">
<div class="row">
<div id="logo" class="span3">
<img src="{{ MEDIA_URL }}css/modoboa.png" />
<img src="{{ STATIC_URL }}css/modoboa.png" />
</div>
</div>
<div class="row">
+4 -2
View File
@@ -14,12 +14,14 @@ urlpatterns = patterns('',
(r'^%s$' % prefix, 'modoboa.lib.webutils.topredirection'),
(r'^%sadmin/' % prefix, include('modoboa.admin.urls')),
(r'^%suserprefs/' % prefix, include('modoboa.userprefs.urls')),
(r'^%sdemo/' % prefix, include('modoboa.demo.urls')),
(r'^%sdemo/' % prefix, include('modoboa.demo.urls')),
(r'^%stemplot/' % prefix, include('templot.frontends.modoboa.urls')),
(r'^accounts/login/$', 'modoboa.auth.views.dologin'),
(r'^accounts/logout/$', 'modoboa.auth.views.dologout'),
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog',
{'packages': ('modoboa',),}),
*loadextensions(prefix)
# *loadextensions(prefix)
*exts_pool.load_all(prefix)
)
if settings.DEBUG: