mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Merge branch 'master' into feature/resizable_mailbox_list
This commit is contained in:
+3
-11
@@ -1,6 +1,6 @@
|
||||
#######
|
||||
Modoboa
|
||||
#######
|
||||
################################
|
||||
`Modoboa <http://modoboa.org/>`_
|
||||
################################
|
||||
|
||||
|travis| |latest-version| |downloads|
|
||||
|
||||
@@ -40,14 +40,6 @@ Documentation
|
||||
A detailled `documentation <https://modoboa.readthedocs.org/>`_ will help you
|
||||
to install, use or extend Modoboa.
|
||||
|
||||
******
|
||||
Issues
|
||||
******
|
||||
|
||||
Modoboa is currently using redmine for project management and issues
|
||||
tracking. Report issues and/or ask for enhancements on
|
||||
`dev.modoboa.org <http://dev.modoboa.org/>`_.
|
||||
|
||||
*************
|
||||
External code
|
||||
*************
|
||||
|
||||
@@ -36,16 +36,27 @@ the *Modoboa > Parameters > Limits* page.
|
||||
Postfix relay domains support
|
||||
*****************************
|
||||
|
||||
This plugin adds the support for relay domains using postfix. This
|
||||
feature is useful when the MTA managed by Modoboa is not the final
|
||||
destination of one or many domains.
|
||||
This plugin adds the support for relay domains using postfix. You can
|
||||
use it when the MTA managed by Modoboa is not the final destination of
|
||||
one or several domains.
|
||||
|
||||
If activated, two new objects will be available from the *domains*
|
||||
If activated, two new objects will be available from the *Domains*
|
||||
listing page: *relay domain* and *relay domain alias*.
|
||||
|
||||
This extension is compatible with the *amavis* and *limits*
|
||||
The extension is compatible with the *amavis* and *limits*
|
||||
ones. Resellers will be able to create both new objects.
|
||||
|
||||
To tell Postfix this feature exists, you must generate two new map
|
||||
files and then update your configuration.
|
||||
|
||||
To generate the map files, run the following command::
|
||||
|
||||
$ modoboa-admin.py postfix_maps --categories relaydomains --dbtype <the database you use> <path>
|
||||
|
||||
Replace values between ``<>`` by yours.
|
||||
|
||||
Edit the :file:`/etc/postfix/main.cf` file.
|
||||
|
||||
.. _amavis_frontend:
|
||||
|
||||
********************
|
||||
|
||||
@@ -53,8 +53,6 @@ Finally, follow the common procedure::
|
||||
1.1.0: FIXME
|
||||
============
|
||||
|
||||
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to ``MODOBOA_APPS``
|
||||
|
||||
Due to code refactoring, some modifications need to be done into
|
||||
:file:`settings.py`:
|
||||
|
||||
@@ -74,6 +72,8 @@ Due to code refactoring, some modifications need to be done into
|
||||
'modoboa.extensions.sievefilters',
|
||||
)
|
||||
|
||||
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to ``MODOBOA_APPS``
|
||||
|
||||
#. ``AUTH_USER_MODEL`` must be set to ``core.User``
|
||||
|
||||
#. Into ``LOGGING``, replace ``modoboa.lib.logutils.SQLHandler`` by
|
||||
@@ -85,6 +85,8 @@ Then, run the following commands to migrate your installation::
|
||||
$ python manage.py migrate
|
||||
$ python manage.py collectstatic
|
||||
|
||||
Finally, update both :ref:`Dovecot <dovecot_authentication>` and
|
||||
:ref:`Postfix <postfix>` queries.
|
||||
|
||||
1.0.1: operations on mailboxes
|
||||
==============================
|
||||
|
||||
@@ -89,6 +89,7 @@ And add the following line inside::
|
||||
The result of each order is recorded into Modoboa's log. Go to
|
||||
*Modoboa > Logs* to consult them.
|
||||
|
||||
.. _dovecot_authentication:
|
||||
|
||||
Authentication
|
||||
==============
|
||||
|
||||
@@ -37,7 +37,7 @@ base_events = [
|
||||
|
||||
"GetExtraParameters",
|
||||
"ExtraFormFields",
|
||||
"SaveExtraFormFields"
|
||||
"SaveExtraFormFields",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -322,6 +322,7 @@ class UserSettings(parameters.UserParametersForm):
|
||||
choices=[("cs", "čeština"), ("de", "deutsch"),
|
||||
("en", "english"), ("es", "español"),
|
||||
("fr", "français"), ("it", "italiano"),
|
||||
("pt", "português"), ("sv", "svenska")],
|
||||
("pt", "português"), ("ru", "русский"),
|
||||
("sv", "svenska")],
|
||||
help_text=ugettext_lazy("Prefered language to display pages")
|
||||
)
|
||||
|
||||
@@ -26,6 +26,43 @@ query = {{ query|safe }}
|
||||
if not self.tpl_context['dbhost']:
|
||||
self.tpl_context['dbhost'] = '127.0.0.1'
|
||||
|
||||
def __render_map(self, args, mapobject):
|
||||
"""Render a map file.
|
||||
|
||||
:param args: command line arguments
|
||||
:param mapobject: a ``MapFile`` subclass
|
||||
"""
|
||||
content = self.template.render(
|
||||
Context(dict(
|
||||
self.tpl_context.items(),
|
||||
query=getattr(mapobject, args.dbtype)
|
||||
))
|
||||
)
|
||||
with open("%s/%s" % (args.destdir, mapobject.filename), "w") as fp:
|
||||
print >> fp, """# This file was generated on %s by running:
|
||||
# %s %s
|
||||
# DO NOT EDIT!
|
||||
""" % (datetime.datetime.now().isoformat(),
|
||||
os.path.basename(sys.argv[0]),
|
||||
' '.join(sys.argv[1:]))
|
||||
print >> fp, content
|
||||
|
||||
def __render_std_maps(self, args):
|
||||
"""Render standard map files.
|
||||
|
||||
(ie. the ones belonging to the 'std' category)
|
||||
|
||||
:param args: command line arguments
|
||||
"""
|
||||
for v in globals().values():
|
||||
if not inspect.isclass(v) or not issubclass(v, MapFile):
|
||||
continue
|
||||
if v is MapFile:
|
||||
continue
|
||||
if not v.category in args.categories:
|
||||
continue
|
||||
self.__render_map(args, v)
|
||||
|
||||
def render(self, args):
|
||||
"""Render all map files.
|
||||
|
||||
@@ -35,26 +72,7 @@ query = {{ query|safe }}
|
||||
os.mkdir(args.destdir)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
for v in globals().values():
|
||||
if not inspect.isclass(v) or not issubclass(v, MapFile):
|
||||
continue
|
||||
if v is MapFile:
|
||||
continue
|
||||
if not v.category in args.categories:
|
||||
continue
|
||||
content = self.template.render(
|
||||
Context(dict(self.tpl_context.items(),
|
||||
query=getattr(v, args.dbtype)))
|
||||
)
|
||||
with open("%s/%s" % (args.destdir, v.filename), "w") as fp:
|
||||
print >> fp, """# This file was generated on %s by running:
|
||||
# %s %s
|
||||
# DO NOT EDIT!
|
||||
""" % (datetime.datetime.now().isoformat(),
|
||||
os.path.basename(sys.argv[0]),
|
||||
' '.join(sys.argv[1:]))
|
||||
print >> fp, content
|
||||
self.__render_std_maps(args)
|
||||
|
||||
|
||||
class SQLiteMapFilesGenerator(MapFilesGenerator):
|
||||
@@ -88,9 +106,9 @@ class DomainsAliasesMap(MapFile):
|
||||
|
||||
class MailboxesMap(MapFile):
|
||||
filename = 'sql-mailboxes.cf'
|
||||
mysql = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
|
||||
postgres = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user u ON mb.user_id=u.id WHERE dom.enabled AND dom.name='%d' AND u.is_active AND mb.address='%u'"
|
||||
sqlite = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN admin_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
|
||||
mysql = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
|
||||
postgres = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON mb.user_id=u.id WHERE dom.enabled AND dom.name='%d' AND u.is_active AND mb.address='%u'"
|
||||
sqlite = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
|
||||
|
||||
|
||||
class AliasesMap(MapFile):
|
||||
@@ -137,6 +155,22 @@ class AutoRepliesMap(MapFile):
|
||||
sqlite = "SELECT full_address, autoreply_address FROM postfix_autoreply_alias WHERE full_address='%s'"
|
||||
|
||||
|
||||
class RelayDomainsTransportMap(MapFile):
|
||||
category = "relaydomains"
|
||||
filename = "sql-relaydomains-transport.cf"
|
||||
mysql = "SELECT CONCAT(srv.name, ':') FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
|
||||
postgres = "SELECT srv.name || ':' FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled AND rdom.name='%s'"
|
||||
sqlite = "SELECT srv.name || ':' FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
|
||||
|
||||
|
||||
class RelayDomainAliasesTransportMap(MapFile):
|
||||
category = "relaydomains"
|
||||
filename = "sql-relaydomain-aliases-transport.cf"
|
||||
mysql = "SELECT CONCAT(srv.name, ':') FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relaydomains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
|
||||
postgres = "SELECT srv.name || ':' FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relaydomains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled AND rdomalias.enabled AND rdomalias.name='%s'"
|
||||
sqlite = "SELECT srv.name || ':' FROM postfix_relaydomains_service AS srv INNER JOIN postfix_relaydomains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relaydomains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
|
||||
|
||||
|
||||
class PostfixMapsCommand(Command):
|
||||
help = "Generate ready-to-use postfix map files"
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class PasswordHasher(object):
|
||||
:return: True if passwords match, False otherwise
|
||||
"""
|
||||
return constant_time_compare(
|
||||
self._encrypt(clearvalue, hashed_value),
|
||||
self._b64encode(self._encrypt(clearvalue, hashed_value)),
|
||||
hashed_value
|
||||
)
|
||||
|
||||
|
||||
@@ -22,10 +22,20 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
help_text=ugettext_lazy("The UNIX account who owns mailboxes on the filesystem")
|
||||
)
|
||||
|
||||
default_domain_quota = forms.IntegerField(
|
||||
label=ugettext_lazy("Default domain quota"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Default quota (in MB) applied to freshly created domains with no "
|
||||
"value specified. A value of 0 means no quota."
|
||||
),
|
||||
widget=forms.TextInput(attrs={'class': 'span2'})
|
||||
)
|
||||
|
||||
auto_account_removal = YesNoField(
|
||||
label=ugettext_lazy("Automatic account removal"),
|
||||
initial="no",
|
||||
help_text=ugettext_lazy("When a mailbox is removed, also remove the associated account"),
|
||||
help_text=ugettext_lazy("When a mailbox is removed, also remove the associated account")
|
||||
)
|
||||
|
||||
# Visibility rules
|
||||
@@ -36,13 +46,26 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AdminParametersForm, self).__init__(*args, **kwargs)
|
||||
hide_fields = False
|
||||
try:
|
||||
code, version = exec_cmd("dovecot --version")
|
||||
except OSError:
|
||||
hide_fields = True
|
||||
else:
|
||||
if code or not version.strip().startswith("2"):
|
||||
code, output = exec_cmd("which dovecot")
|
||||
if not code:
|
||||
dpath = output.strip()
|
||||
try:
|
||||
code, version = exec_cmd("%s --version" % dpath)
|
||||
except OSError:
|
||||
hide_fields = True
|
||||
else:
|
||||
if code or not version.strip().startswith("2"):
|
||||
hide_fields = True
|
||||
else:
|
||||
hide_fields = True
|
||||
if hide_fields:
|
||||
del self.fields["handle_mailboxes"]
|
||||
del self.fields["mailboxes_owner"]
|
||||
|
||||
def clean_default_domain_quota(self):
|
||||
"""Ensure quota is a positive integer."""
|
||||
if self.cleaned_data['default_domain_quota'] < 0:
|
||||
raise forms.ValidationError(
|
||||
ugettext_lazy('Must be a positive integer')
|
||||
)
|
||||
return self.cleaned_data['default_domain_quota']
|
||||
|
||||
@@ -162,6 +162,22 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
"""Ensure lower case emails"""
|
||||
return self.cleaned_data["email"].lower()
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validation.
|
||||
|
||||
Check if quota is >= 0 only when the domain value is not used.
|
||||
"""
|
||||
super(AccountFormMail, self).clean()
|
||||
if self._errors:
|
||||
raise forms.ValidationError(self._errors)
|
||||
if not self.cleaned_data["quota_act"] \
|
||||
and self.cleaned_data['quota'] is not None:
|
||||
if self.cleaned_data["quota"] < 0:
|
||||
self._errors["quota"] = self.error_class(
|
||||
[_("Must be a positive integer")])
|
||||
del self.cleaned_data["quota"]
|
||||
return self.cleaned_data
|
||||
|
||||
def create_mailbox(self, user, account):
|
||||
locpart, domname = split_mailbox(self.cleaned_data["email"])
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django import forms
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.exceptions import Conflict
|
||||
from modoboa.lib.formutils import (
|
||||
DomainNameField, YesNoField, DynamicForm, TabForms
|
||||
@@ -13,6 +13,15 @@ from modoboa.extensions.admin.models import (
|
||||
|
||||
|
||||
class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
quota = forms.IntegerField(
|
||||
label=ugettext_lazy("Quota"),
|
||||
required=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Default quota in MB applied to mailboxes. Leave empty to use the "
|
||||
"default value."
|
||||
),
|
||||
widget=forms.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
aliases = DomainNameField(
|
||||
label=ugettext_lazy("Alias(es)"),
|
||||
required=False,
|
||||
@@ -25,9 +34,6 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
class Meta:
|
||||
model = Domain
|
||||
fields = ("name", "quota", "aliases", "enabled")
|
||||
widgets = dict(
|
||||
quota=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.oldname = None
|
||||
@@ -43,6 +49,13 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
name = "aliases_%d" % (pos + 1)
|
||||
self._create_field(forms.CharField, name, dalias.name, 3)
|
||||
|
||||
def clean_quota(self):
|
||||
if self.cleaned_data['quota'] is None:
|
||||
return int(parameters.get_admin('DEFAULT_DOMAIN_QUOTA'))
|
||||
if self.cleaned_data['quota'] < 0:
|
||||
raise forms.ValidationError(_("Must be a positive integer"))
|
||||
return self.cleaned_data['quota']
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validation.
|
||||
|
||||
|
||||
@@ -117,14 +117,18 @@ class Alias(AdminObject):
|
||||
if not t in int_rcpts:
|
||||
self.mboxes.remove(t)
|
||||
|
||||
def get_recipients(self):
|
||||
"""Return the recipients list
|
||||
def get_recipients(self, with_external=True):
|
||||
"""Return the recipients list.
|
||||
|
||||
Internal and external addresses are mixed into a single list.
|
||||
|
||||
:param bool with_external: include external addresses or not
|
||||
:rtype: list
|
||||
:return: a list of addresses
|
||||
"""
|
||||
result = [al.full_address for al in self.aliases.all()]
|
||||
result += [mb.full_address for mb in self.mboxes.all()]
|
||||
if self.extmboxes != "":
|
||||
if with_external and self.extmboxes != "":
|
||||
result += self.extmboxes.split(',')
|
||||
return result
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ class Domain(AdminObject):
|
||||
"""
|
||||
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy("The domain name"))
|
||||
quota = models.IntegerField(
|
||||
help_text=ugettext_lazy("Default quota in MB applied to mailboxes")
|
||||
)
|
||||
quota = models.IntegerField()
|
||||
enabled = models.BooleanField(
|
||||
ugettext_lazy('enabled'),
|
||||
help_text=ugettext_lazy("Check to activate this domain")
|
||||
|
||||
@@ -114,6 +114,17 @@ class Mailbox(AdminObject):
|
||||
self.__mail_home = output.strip()
|
||||
return self.__mail_home
|
||||
|
||||
@property
|
||||
def alias_addresses(self):
|
||||
"""Return all alias address of this mailbox.
|
||||
|
||||
:rtype: list of string
|
||||
"""
|
||||
aliases = []
|
||||
for alias in self.alias_set.all():
|
||||
aliases += [alias.full_address]
|
||||
return aliases
|
||||
|
||||
def rename_dir(self, old_mail_home):
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
|
||||
@@ -85,7 +85,8 @@ class PermissionsTestCase(ModoTestCase):
|
||||
self.user = User.objects.get(username='user@test.com')
|
||||
self.values = dict(
|
||||
username=self.user.username, role="DomainAdmins",
|
||||
is_active=self.user.is_active, email="user@test.com"
|
||||
is_active=self.user.is_active, email="user@test.com",
|
||||
quota_act=True
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.extensions.admin.models import (
|
||||
Domain, Alias
|
||||
@@ -66,6 +67,22 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertIn(da.mailbox_set.all()[0], al.mboxes.all())
|
||||
self.assertTrue(da.can_access(al))
|
||||
|
||||
def test_create_using_default_quota(self):
|
||||
parameters.save_admin('DEFAULT_DOMAIN_QUOTA', 50, app='admin')
|
||||
values = {
|
||||
"name": "pouet.com", "create_dom_admin": "yes",
|
||||
"dom_admin_username": "toto", "create_aliases": "yes",
|
||||
"stepid": 'step2'
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse("modoboa.extensions.admin.views.domain.newdomain"),
|
||||
values
|
||||
)
|
||||
dom = Domain.objects.get(name="pouet.com")
|
||||
self.assertEqual(dom.quota, 50)
|
||||
da = User.objects.get(username="toto@pouet.com")
|
||||
self.assertEqual(da.mailbox_set.all()[0].quota, 50)
|
||||
|
||||
def test_modify(self):
|
||||
"""Test the modification of a domain
|
||||
|
||||
|
||||
@@ -60,11 +60,14 @@ $(document).ready(function() {
|
||||
interval: {{ interval }},
|
||||
success_cb: function(data) {
|
||||
var $link = $("#nbrequests");
|
||||
var $maincounter = $("#alerts-counter");
|
||||
|
||||
if (data.requests > 0) {
|
||||
$link.html(data.requests + " " + "{{ text }}");
|
||||
$link.parent().removeClass('hidden');
|
||||
$maincounter.html(data.requests);
|
||||
$link.children("span").html(data.requests);
|
||||
$maincounter.closest('div').removeClass('hidden');
|
||||
} else {
|
||||
$link.parent().addClass('hidden');
|
||||
$maincounter.closest('div').addClass('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -102,10 +105,24 @@ def display_requests(user):
|
||||
|
||||
url = reverse("modoboa.extensions.amavis.views.index")
|
||||
url += "#listing/?viewrequests=1"
|
||||
tpl = Template('<div class="btn-group {{ css }}"><a id="nbrequests" href="{{ url }}" class="btn btn-danger">{{ label }}</a></div>')
|
||||
tpl = Template("""<ul class="nav pull-right {{ css }}">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-white icon-bell"></i> <span id="alerts-counter" class="label label-important">{{ nbrequests }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a id="nbrequests" href="{{ url }}">
|
||||
<span class="label label-important">{{ nbrequests }}</span> {{ label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>""")
|
||||
css = "hidden" if nbrequests == 0 else ""
|
||||
return [tpl.render(Context(dict(
|
||||
label=_("%d pending requests" % nbrequests), url=url, css=css
|
||||
label=_("Pending requests"), url=url, css=css,
|
||||
nbrequests=nbrequests
|
||||
)))]
|
||||
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
from modoboa.lib.webutils import NavigationParameters
|
||||
from modoboa.extensions.amavis.models import Users, Policy
|
||||
|
||||
|
||||
|
||||
def selfservice(ssfunc=None):
|
||||
"""Decorator used to expose views to the 'self-service' feature
|
||||
|
||||
@@ -79,6 +81,38 @@ recipient=%s
|
||||
return False
|
||||
|
||||
|
||||
class QuarantineNavigationParameters(NavigationParameters):
|
||||
"""
|
||||
Specific NavigationParameters subclass for the quarantine.
|
||||
"""
|
||||
def __init__(self, request):
|
||||
super(QuarantineNavigationParameters, self).__init__(
|
||||
request, 'quarantine_navparams'
|
||||
)
|
||||
self.parameters += [('msgtype', None, False)]
|
||||
|
||||
def back_to_listing(self):
|
||||
"""Return the current listing URL.
|
||||
|
||||
Looks into the user's session and the current request to build
|
||||
the URL.
|
||||
|
||||
:return: a string
|
||||
"""
|
||||
url = "listing"
|
||||
params = []
|
||||
navparams = self.request.session[self.sessionkey]
|
||||
if "page" in navparams:
|
||||
params += ["page=%s" % navparams["page"]]
|
||||
if "order" in navparams:
|
||||
params += ["sort_order=%s" % navparams["order"]]
|
||||
params += ["%s=%s" % (p[0], navparams[p[0]])
|
||||
for p in self.parameters if p[0] in navparams]
|
||||
if params:
|
||||
url += "?%s" % ("&".join(params))
|
||||
return url
|
||||
|
||||
|
||||
def create_user_and_policy(name):
|
||||
"""Create records.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from modoboa.lib.webutils import static_url
|
||||
from modoboa.lib.email_listing import MBconnector, EmailListing
|
||||
from modoboa.lib.emailutils import Email
|
||||
from modoboa.lib.dbutils import db_type
|
||||
from modoboa.extensions.admin.models import Domain
|
||||
from modoboa.extensions.admin.models import Domain, Alias
|
||||
from .models import Quarantine, Msgrcpt
|
||||
|
||||
|
||||
@@ -18,30 +18,60 @@ class Qtable(tables.Table):
|
||||
styles = "table-condensed"
|
||||
idkey = "mailid"
|
||||
|
||||
type = tables.Column(
|
||||
"type", align="center", width="30px",
|
||||
selection = tables.SelectionColumn(
|
||||
"selection", safe=True, width='5px', header=None, sortable=False
|
||||
)
|
||||
rstatus = tables.ImgColumn("rstatus", width='25px')
|
||||
from_ = tables.Column("from", label=ugettext_lazy("From"), limit=30)
|
||||
subject = tables.Column("subject", label=ugettext_lazy("Subject"), limit=40)
|
||||
time = tables.Column("date", label=ugettext_lazy("Date"))
|
||||
to = tables.Column("to", label=ugettext_lazy("To"), sortable=False)
|
||||
type = tables.Column(
|
||||
"type", align="center", width="30px", sort_order="type", safe=True
|
||||
)
|
||||
rstatus = tables.ImgColumn(
|
||||
"rstatus", width='25px', sortable=False
|
||||
)
|
||||
score = tables.Column(
|
||||
"score", label=ugettext_lazy("Score"), limit=6,
|
||||
sort_order="score", cssclass="openable"
|
||||
)
|
||||
to = tables.Column(
|
||||
"to", label=ugettext_lazy("To"), sort_order="to", cssclass="openable"
|
||||
)
|
||||
from_ = tables.Column(
|
||||
"from", label=ugettext_lazy("From"), limit=30,
|
||||
sort_order="from", cssclass="openable"
|
||||
)
|
||||
subject = tables.Column(
|
||||
"subject", label=ugettext_lazy("Subject"), limit=40,
|
||||
sort_order="subject", cssclass="openable"
|
||||
)
|
||||
time = tables.Column(
|
||||
"date", label=ugettext_lazy("Date"), sort_order="date",
|
||||
cssclass="openable"
|
||||
)
|
||||
|
||||
cols_order = [
|
||||
'selection', 'type', 'rstatus', 'score', 'to', 'from_', 'subject', 'time'
|
||||
]
|
||||
|
||||
cols_order = []
|
||||
def parse_type(self, value):
|
||||
color = 'important' if value in ['S', 'V'] else 'warning'
|
||||
return '<span class="label label-%s">%s</span>' % (color, value)
|
||||
|
||||
def parse_date(self, value):
|
||||
return datetime.fromtimestamp(value)
|
||||
|
||||
|
||||
class SQLconnector(MBconnector):
|
||||
orders = {
|
||||
"from": "mail__from_addr",
|
||||
order_translation_table = {
|
||||
"type": "mail__msgrcpt__content",
|
||||
"score": "mail__msgrcpt__bspam_level",
|
||||
"date": "mail__time_num",
|
||||
"subject": "mail__subject",
|
||||
"date": "mail__time_num"
|
||||
"from": "mail__from_addr",
|
||||
"to": "mail__msgrcpt__rid__email"
|
||||
}
|
||||
|
||||
def __init__(self, mail_ids=None, filter=None):
|
||||
self.count = None
|
||||
self.messages = None
|
||||
self.mail_ids = mail_ids
|
||||
self.filter = filter
|
||||
|
||||
@@ -52,35 +82,46 @@ class SQLconnector(MBconnector):
|
||||
filter &= Q(mail__in=self.mail_ids)
|
||||
if self.filter:
|
||||
filter &= self.filter
|
||||
self.messages = Quarantine.objects.filter(filter)
|
||||
self.messages = Quarantine.objects.filter(filter).values(
|
||||
"mail__from_addr",
|
||||
"mail__msgrcpt__rid__email",
|
||||
"mail__subject",
|
||||
"mail__mail_id",
|
||||
"mail__time_num",
|
||||
"mail__msgrcpt__content",
|
||||
"mail__msgrcpt__bspam_level",
|
||||
"mail__msgrcpt__rs"
|
||||
)
|
||||
if "order" in kwargs:
|
||||
totranslate = kwargs["order"][1:]
|
||||
sign = kwargs["order"][:1]
|
||||
if sign == " ":
|
||||
sign = ""
|
||||
order = sign + self.orders[totranslate]
|
||||
self.messages = self.messages.order_by(order)
|
||||
self.count = self.messages.count()
|
||||
order = kwargs["order"]
|
||||
sign = ""
|
||||
if order[0] == "-":
|
||||
sign = "-"
|
||||
order = order[1:]
|
||||
order = self.order_translation_table[order]
|
||||
self.messages = self.messages.order_by(sign + order)
|
||||
|
||||
self.count = len(self.messages)
|
||||
return self.count
|
||||
|
||||
def fetch(self, start=None, stop=None, **kwargs):
|
||||
messages = self.messages[start - 1:stop]
|
||||
emails = []
|
||||
for qm in messages:
|
||||
for rcpt in qm.mail.msgrcpt_set.all():
|
||||
m = {"from": qm.mail.from_addr,
|
||||
"to": rcpt.rid.email,
|
||||
"subject": qm.mail.subject,
|
||||
"mailid": qm.mail_id,
|
||||
"date": qm.mail.time_num,
|
||||
"type": rcpt.content}
|
||||
if rcpt.rs == '':
|
||||
m["class"] = "unseen"
|
||||
elif rcpt.rs == 'R':
|
||||
m["img_rstatus"] = static_url("pics/release.png")
|
||||
elif rcpt.rs == 'p':
|
||||
m["class"] = "pending"
|
||||
emails.append(m)
|
||||
for qm in self.messages[start - 1:stop]:
|
||||
m = {"from": qm["mail__from_addr"],
|
||||
"to": qm["mail__msgrcpt__rid__email"],
|
||||
"subject": qm["mail__subject"],
|
||||
"mailid": qm["mail__mail_id"],
|
||||
"date": qm["mail__time_num"],
|
||||
"type": qm["mail__msgrcpt__content"],
|
||||
"score": qm["mail__msgrcpt__bspam_level"]}
|
||||
rs = qm["mail__msgrcpt__rs"]
|
||||
if rs == '':
|
||||
m["class"] = "unseen"
|
||||
elif rs == 'R':
|
||||
m["img_rstatus"] = static_url("pics/release.png")
|
||||
elif rs == 'p':
|
||||
m["class"] = "pending"
|
||||
emails.append(m)
|
||||
return emails
|
||||
|
||||
|
||||
@@ -98,22 +139,25 @@ class SQLWrapper(object):
|
||||
"""
|
||||
|
||||
def get_mails(self, request, rcptfilter=None):
|
||||
if request.GET.get("viewrequests", None) == "1":
|
||||
q = Q(rs='p')
|
||||
else:
|
||||
q = ~Q(rs='D')
|
||||
"""Retrieve all messages visible by a user.
|
||||
|
||||
Simple users can only see the messages
|
||||
|
||||
:rtype: QuerySet
|
||||
"""
|
||||
q = Q(rs='p') \
|
||||
if request.GET.get("viewrequests", None) == "1" else ~Q(rs='D')
|
||||
if request.user.group == 'SimpleUsers':
|
||||
q &= Q(rid__email=request.user.email)
|
||||
else:
|
||||
if not request.user.is_superuser:
|
||||
doms = Domain.objects.get_for_admin(request.user)
|
||||
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
|
||||
doms_q = Q(rid__email__regex=regexp)
|
||||
q &= doms_q
|
||||
if rcptfilter is not None:
|
||||
q &= Q(rid__email__contains=rcptfilter)
|
||||
|
||||
rcpts = [request.user.email] \
|
||||
+ request.user.mailbox_set.all()[0].alias_addresses
|
||||
q &= Q(rid__email__in=rcpts)
|
||||
elif not request.user.is_superuser:
|
||||
doms = Domain.objects.get_for_admin(request.user)
|
||||
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
|
||||
doms_q = Q(rid__email__regex=regexp)
|
||||
q &= doms_q
|
||||
if rcptfilter is not None:
|
||||
q &= Q(rid__email__contains=rcptfilter)
|
||||
return Msgrcpt.objects.filter(q).values("mail_id")
|
||||
|
||||
def get_recipient_message(self, address, mailid):
|
||||
@@ -153,24 +197,23 @@ class PgWrapper(SQLWrapper):
|
||||
"""
|
||||
|
||||
def get_mails(self, request, rcptfilter=None):
|
||||
if request.GET.get("viewrequests", None) == "1":
|
||||
q = Q(rs='p')
|
||||
else:
|
||||
q = ~Q(rs='D')
|
||||
q = Q(rs='p') \
|
||||
if request.GET.get("viewrequests", None) == "1" else ~Q(rs='D')
|
||||
where = ["U0.rid=maddr.id"]
|
||||
if request.user.group == 'SimpleUsers':
|
||||
where.append("convert_from(maddr.email, 'UTF8') = '%s'" % request.user.email)
|
||||
return Msgrcpt.objects.filter(q).extra(
|
||||
where=where, tables=['maddr']
|
||||
)
|
||||
|
||||
if not request.user.is_superuser:
|
||||
rcpts = [request.user.email] \
|
||||
+ request.user.mailbox_set.all()[0].alias_addresses
|
||||
where.append("convert_from(maddr.email, 'UTF8') IN (%s)" \
|
||||
% (','.join(["'%s'" % rcpt for rcpt in rcpts])))
|
||||
elif not request.user.is_superuser:
|
||||
doms = Domain.objects.get_for_admin(request.user)
|
||||
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
|
||||
where.append("convert_from(maddr.email, 'UTF8') ~ '%s'" % regexp)
|
||||
if rcptfilter is not None:
|
||||
where.append("convert_from(maddr.email, 'UTF8') LIKE '%%%s%%'" % rcptfilter)
|
||||
return Msgrcpt.objects.filter(q).extra(where=where, tables=['maddr']).values("mail_id")
|
||||
where.append("convert_from(maddr.email, 'UTF8') LIKE '%%%s%%'"
|
||||
% rcptfilter)
|
||||
return Msgrcpt.objects.filter(q)\
|
||||
.extra(where=where, tables=['maddr']).values("mail_id")
|
||||
|
||||
def get_recipient_message(self, address, mailid):
|
||||
qset = Msgrcpt.objects.filter(mail=mailid).extra(
|
||||
@@ -229,10 +272,6 @@ class SQLlisting(EmailListing):
|
||||
reset_wm_url = True
|
||||
|
||||
def __init__(self, user, msgs, filter, **kwargs):
|
||||
if user.group == 'SimpleUsers':
|
||||
Qtable.cols_order = ['type', 'rstatus', 'from_', 'subject', 'time']
|
||||
else:
|
||||
Qtable.cols_order = ['type', 'rstatus', 'to', 'from_', 'subject', 'time']
|
||||
self.mbc = SQLconnector(msgs, filter)
|
||||
super(SQLlisting, self).__init__(**kwargs)
|
||||
self.show_listing_headers = True
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#menubar input[type="checkbox"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#listing {
|
||||
position: fixed;
|
||||
padding: 0 15px;
|
||||
@@ -27,15 +31,32 @@
|
||||
color: #0088cc;
|
||||
}
|
||||
|
||||
#filters_bar {
|
||||
/*padding-top: 5px;*/
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
thead tr {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
td > input[type=checkbox] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
td[name=type] span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td.unseen {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.pending {
|
||||
td[class*=pending] {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
td[class*=openable] {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,21 +1,42 @@
|
||||
var Quarantine = function(options) {
|
||||
this.initialize(options);
|
||||
Listing.call(this, options);
|
||||
};
|
||||
|
||||
Quarantine.prototype = {
|
||||
constructor: Quarantine,
|
||||
|
||||
defaults: {
|
||||
deflocation: "listing",
|
||||
defcallback: "listing_cb"
|
||||
deflocation: "listing"
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = $.extend({}, this.defaults, options);
|
||||
Listing.prototype.initialize.call(this, options);
|
||||
this.options = $.extend({}, this.defaults, this.options);
|
||||
this.options.defcallback = $.proxy(this.listing_cb, this);
|
||||
this.navobj = new History(this.options);
|
||||
|
||||
this.register_navcallbacks();
|
||||
this.listen();
|
||||
this.set_msgtype();
|
||||
|
||||
$(document).on('click', '#selectall', this.toggle_selection);
|
||||
$("#searchfield").searchbar({navobj: this.navobj});
|
||||
},
|
||||
|
||||
/*
|
||||
* Activate the button corresponding to the current message type
|
||||
* filter. If no message type is found, the 'All' button is
|
||||
* selected.
|
||||
*/
|
||||
set_msgtype: function() {
|
||||
var msgtype = this.navobj.getparam('msgtype');
|
||||
|
||||
$("button[name*=msgtype]").removeClass('active');
|
||||
if (msgtype != undefined) {
|
||||
$("button[name=msgtype_" + msgtype + "]").addClass('active');
|
||||
} else {
|
||||
$("button[name=msgtype_all]").addClass('active');
|
||||
}
|
||||
},
|
||||
|
||||
update_page: function(data) {
|
||||
@@ -23,9 +44,6 @@ Quarantine.prototype = {
|
||||
$("#menubar").html(data.menu);
|
||||
$("#searchfield").searchbar({navobj: this.navobj});
|
||||
}
|
||||
if (data.navbar) {
|
||||
$("#bottom-bar-right").html(data.navbar);
|
||||
}
|
||||
if (data.listing != undefined) {
|
||||
$("#listing").html(data.listing);
|
||||
$("#listing").css({
|
||||
@@ -34,10 +52,11 @@ Quarantine.prototype = {
|
||||
overflow: "auto"
|
||||
});
|
||||
}
|
||||
this.update_listing(data);
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
$(document).on("dblclick", "tbody>tr", $.proxy(this.viewmail_loader, this));
|
||||
$(document).on("click", "td[class*=openable]", $.proxy(this.viewmail_loader, this));
|
||||
$(document).on("click", "a[name=selectmsgs]", $.proxy(this.selectmsgs, this));
|
||||
$(document).on("click", "a[name=release-multi]",
|
||||
$.proxy(this.release_selection, this));
|
||||
@@ -45,19 +64,16 @@ Quarantine.prototype = {
|
||||
$.proxy(this.delete_selection, this));
|
||||
$(document).on("click", "a[name=viewrequests]",
|
||||
$.proxy(this.view_requests, this));
|
||||
$(document).on("click", "#bottom-bar a", $.proxy(this.load_page, this));
|
||||
|
||||
$(document).on("click", "a[name=release]", $.proxy(this.release, this));
|
||||
$(document).on("click", "a[name=delete]", $.proxy(this.delete, this));
|
||||
$(document).on("click", "a[name=headers]", $.proxy(this.headers, this));
|
||||
$(document).on("click", "td[name=type] span", $.proxy(this.filter_by_type, this));
|
||||
$(document).on("click", "#filters button", $.proxy(this.filter_by_type, this));
|
||||
},
|
||||
|
||||
load_page: function(e) {
|
||||
e.preventDefault();
|
||||
var $link = $(e.target).parent();
|
||||
|
||||
this.navobj.delparam("rcpt");
|
||||
this.navobj.parse_string($link.attr("href")).update();
|
||||
Listing.prototype.load_page.apply(this, arguments);
|
||||
this.navobj.delparam("rcpt").update();
|
||||
},
|
||||
|
||||
view_requests: function(e) {
|
||||
@@ -77,20 +93,59 @@ Quarantine.prototype = {
|
||||
this.navobj.update();
|
||||
},
|
||||
|
||||
selectmsgs: function(e) {
|
||||
e.preventDefault();
|
||||
var type = $(e.target).attr("href");
|
||||
|
||||
if (type == "") {
|
||||
/*
|
||||
* Toggle message selection when the top checkbox's state is
|
||||
* modified.
|
||||
*
|
||||
* If it is checked, all messages are selected. Otherwise, the
|
||||
* current selection is resetted.
|
||||
*/
|
||||
toggle_selection: function(e) {
|
||||
if (!$('#selectall').prop('checked')) {
|
||||
$("#emails").htmltable("clear_selection");
|
||||
return;
|
||||
}
|
||||
$("td[name*=selection]").each(function() {
|
||||
var $input = $(this).children('input');
|
||||
$input.prop('checked', true);
|
||||
$('#emails').htmltable('select_row', $(this).parent());
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Select all messages of a specific type.
|
||||
*/
|
||||
selectmsgs: function(e) {
|
||||
e.preventDefault();
|
||||
var type = get_target(e, 'a').attr("href");
|
||||
var counter = 0;
|
||||
|
||||
$("td[name=type]").each(function() {
|
||||
var $this = $(this);
|
||||
if ($this.html().trim() == type) {
|
||||
$("#emails").htmltable("select_row", $this.parent());
|
||||
if ($this.find('span').html().trim() == type) {
|
||||
var $input = $this.parent().children('td[name=selection]').children('input');
|
||||
$input.prop('checked', true);
|
||||
$('#emails').htmltable('select_row', $this.parent());
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
if (counter) {
|
||||
$("#selectall").prop('checked', true);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Filter listing by message type (spam, virus, etc.)
|
||||
*/
|
||||
filter_by_type: function(evt) {
|
||||
var msgtype = $(evt.target).html().trim();
|
||||
|
||||
if (msgtype != 'All') {
|
||||
this.navobj.setparam('msgtype', msgtype);
|
||||
} else {
|
||||
this.navobj.delparam('msgtype');
|
||||
}
|
||||
this.navobj.update();
|
||||
},
|
||||
|
||||
_send_selection: function(e, name, message) {
|
||||
@@ -138,7 +193,9 @@ Quarantine.prototype = {
|
||||
}
|
||||
$.ajax({
|
||||
url: $link.attr("href"),
|
||||
dataType: 'json'
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
data: {rcpt: get_parameter_by_name($link.attr("href"), 'rcpt')}
|
||||
}).done($.proxy(this.action_cb, this));
|
||||
},
|
||||
|
||||
@@ -196,10 +253,30 @@ Quarantine.prototype = {
|
||||
$.proxy(this.viewmail_cb, this));
|
||||
},
|
||||
|
||||
activate_buttons: function($tr) {
|
||||
$("a[name=release-multi]").removeClass('disabled');
|
||||
$("a[name=delete-multi]").removeClass('disabled');
|
||||
$("#selectall").prop('checked', true);
|
||||
},
|
||||
|
||||
deactivate_buttons: function($tr) {
|
||||
if (!this.htmltable || !this.htmltable.current_selection().length) {
|
||||
$("a[name=release-multi]").addClass('disabled');
|
||||
$("a[name=delete-multi]").addClass('disabled');
|
||||
$("#selectall").prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
listing_cb: function(data) {
|
||||
this.update_page(data);
|
||||
$("#emails").htmltable();
|
||||
this.navobj.delparam("rcpt").update();
|
||||
this.set_msgtype();
|
||||
$("#emails").htmltable({
|
||||
tr_selected_event: this.activate_buttons,
|
||||
tr_unselected_event: $.proxy(this.deactivate_buttons, this)
|
||||
});
|
||||
this.htmltable = $("#emails").data("htmltable");
|
||||
this.deactivate_buttons();
|
||||
},
|
||||
|
||||
viewmail_cb: function(data) {
|
||||
@@ -215,4 +292,6 @@ Quarantine.prototype = {
|
||||
$("body").notify("error", data.respmsg);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Quarantine.prototype = $.extend({}, Listing.prototype, Quarantine.prototype);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{% extends "fixed.html" %}{% load i18n %}
|
||||
{% extends "fixed.html" %}{% load i18n amavis_tags %}
|
||||
{% block pagetitle %}{% trans "Quarantine management" %}{% endblock %}
|
||||
{% block html_head %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}amavis/css/quarantine.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.sortable.css" />
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.htmltable.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.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 }}js/listing.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}amavis/js/quarantine.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
@@ -16,6 +19,6 @@ $(document).ready(function() {
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block fixed_content %}
|
||||
<div id="menubar"></div>
|
||||
<div id="menubar">{% quar_menu %}</div>
|
||||
<div id="listing"></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{% load lib_tags i18n %}
|
||||
<div class="btn-toolbar pull-left">
|
||||
<div class="btn-group" title="{% trans 'Select' %}">
|
||||
<a class="btn"><input type="checkbox" id="selectall" /></a>
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a name="selectmsgs" href="S">{% trans "Spam" %}</a></li>
|
||||
<li><a name="selectmsgs" href="V">{% trans "Virus" %}</a></li>
|
||||
<li><a name="selectmsgs" href="H">{% trans "Bad Header" %}</a></li>
|
||||
<li><a name="selectmsgs" href="M">{% trans "Bad MIME" %}</a></li>
|
||||
<li><a name="selectmsgs" href="B">{% trans "Banned" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a name="release-multi" href="{% url 'modoboa.extensions.amavis.views.process' %}" class="btn btn-success disabled">
|
||||
<i class="icon-white icon-ok"></i> {% trans "Release" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a name="delete-multi" href="{% url 'modoboa.extensions.amavis.views.process' %}" class="btn btn-danger disabled">
|
||||
<i class="icon-white icon-trash"></i> {% trans "Delete" %}
|
||||
</a>
|
||||
</div>
|
||||
{% include "common/email_searchbar.html" with extraopts=extraopts %}
|
||||
</div>
|
||||
<div id="filters_bar" class="btn-toolbar pull-right">
|
||||
<span class="label label-title">{% trans "Display" %}</span>
|
||||
<div id="filters" class="btn-group" data-toggle="buttons-radio">
|
||||
<button name="msgtype_all" class="btn" title="{% trans 'All messages' %}">{% trans "All" %}</button>
|
||||
<button name="msgtype_S" class="btn btn-danger" title="{% trans 'Spam only' %}">S</button>
|
||||
<button name="msgtype_V" class="btn btn-danger" title="{% trans 'Virus only' %}">V</button>
|
||||
<button name="msgtype_H" class="btn btn-warning" title="{% trans 'Bad Header only' %}">H</button>
|
||||
<button name="msgtype_M" class="btn btn-warning" title="{% trans 'Bad MIME only' %}">M</button>
|
||||
<button name="msgtype_B" class="btn btn-warning" title="{% trans 'Banned only' %}">B</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,12 +3,13 @@ from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from modoboa.lib.webutils import _render_to_string
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def viewm_menu(user, mail_id, rcpt):
|
||||
def viewm_menu(mail_id, rcpt):
|
||||
entries = [
|
||||
{"name": "release",
|
||||
"img": "icon-white icon-ok",
|
||||
@@ -65,49 +66,13 @@ def viewm_menu_simple(user, mail_id, rcpt, secret_id=""):
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def quar_menu(user):
|
||||
entries = [
|
||||
{"name": "release-multi",
|
||||
"url": reverse('modoboa.extensions.amavis.views.process'),
|
||||
"img": "icon-white icon-ok",
|
||||
"class": "btn-success",
|
||||
"label": _("Release")},
|
||||
{"name": "delete-multi",
|
||||
"img": "icon-white icon-trash",
|
||||
"class": "btn-danger",
|
||||
"url": reverse('modoboa.extensions.amavis.views.process'),
|
||||
"label": _("Delete")},
|
||||
{"name": "select",
|
||||
"url": "",
|
||||
"label": _("Content Class"),
|
||||
"menu": [
|
||||
{"name": "selectmsgs",
|
||||
"url": "",
|
||||
"label": _("Nothing")},
|
||||
{"name": "selectmsgs",
|
||||
"url": "S",
|
||||
"label": _("Spam")},
|
||||
{"name": "selectmsgs",
|
||||
"url": "V",
|
||||
"label": _("Virus")},
|
||||
{"name": "selectmsgs",
|
||||
"url": "H",
|
||||
"label": _("Bad header")},
|
||||
{"name": "selectmsgs",
|
||||
"url": "M",
|
||||
"label": _("Bad MIME")}
|
||||
]}
|
||||
]
|
||||
def quar_menu():
|
||||
"""Render the quarantine listing menu.
|
||||
|
||||
if user.group != 'SimpleUsers':
|
||||
extraopts = [{"name": "to", "label": _("To")}]
|
||||
else:
|
||||
extraopts = []
|
||||
extracontent = render_to_string('common/email_searchbar.html', {
|
||||
"STATIC_URL": settings.STATIC_URL,
|
||||
"extraopts": extraopts
|
||||
:rtype: str
|
||||
:return: resulting HTML
|
||||
"""
|
||||
extraopts = [{"name": "to", "label": _("To")}]
|
||||
return render_to_string('amavis/main_action_bar.html', {
|
||||
'extraopts': extraopts
|
||||
})
|
||||
|
||||
return render_to_string('common/buttons_list.html', dict(
|
||||
entries=entries, extracontent=extracontent
|
||||
))
|
||||
|
||||
@@ -13,41 +13,19 @@ from modoboa.lib.exceptions import BadRequest
|
||||
from modoboa.lib.webutils import (
|
||||
getctx, ajax_response, render_to_json_response
|
||||
)
|
||||
from modoboa.lib.email_listing import parse_search_parameters
|
||||
from modoboa.lib.templatetags.lib_tags import pagination_bar
|
||||
from modoboa.extensions.admin.models import Mailbox, Domain
|
||||
from modoboa.extensions.amavis.templatetags.amavis_tags import (
|
||||
quar_menu, viewm_menu
|
||||
)
|
||||
from .lib import selfservice, AMrelease
|
||||
from .lib import selfservice, AMrelease, QuarantineNavigationParameters
|
||||
from .sql_listing import SQLlisting, SQLemail, get_wrapper
|
||||
from .models import Msgrcpt
|
||||
|
||||
|
||||
def __back_to_listing(request):
|
||||
"""Return the current listing URL.
|
||||
|
||||
Looks into the user's session and the current request to build the
|
||||
URL.
|
||||
|
||||
:param request: a ``Request`` object
|
||||
:return: a string
|
||||
"""
|
||||
url = "listing"
|
||||
params = []
|
||||
if "page" in request.session:
|
||||
params += ["page=%s" % request.session["page"]]
|
||||
|
||||
params += ["%s=%s" % (p, request.session[p])
|
||||
for p in ["criteria", "pattern"] if p in request.session]
|
||||
if params:
|
||||
url += "?%s" % ("&".join(params))
|
||||
return url
|
||||
|
||||
|
||||
def empty_quarantine(request):
|
||||
content = "<div class='alert alert-info'>%s</div>" % _("Empty quarantine")
|
||||
ctx = getctx("ok", level=2, listing=content, navbar="",
|
||||
menu=quar_menu(request.user))
|
||||
ctx = getctx("ok", level=2, listing=content)
|
||||
return render_to_json_response(ctx)
|
||||
|
||||
|
||||
@@ -61,58 +39,56 @@ def _listing(request):
|
||||
if not Domain.objects.get_for_admin(request.user).count():
|
||||
return empty_quarantine(request)
|
||||
|
||||
order = request.GET.get("order", "-date")
|
||||
if not "navparams" in request.session:
|
||||
request.session["navparams"] = {}
|
||||
request.session["navparams"]["order"] = order
|
||||
|
||||
parse_search_parameters(request)
|
||||
if "pattern" in request.session:
|
||||
criteria = request.session["criteria"]
|
||||
navparams = QuarantineNavigationParameters(request)
|
||||
navparams.store()
|
||||
pattern = navparams.get('pattern', '')
|
||||
if pattern:
|
||||
criteria = navparams.get('criteria')
|
||||
if criteria == "both":
|
||||
criteria = "from_addr,subject,to"
|
||||
for c in criteria.split(","):
|
||||
if c == "from_addr":
|
||||
nfilter = Q(mail__from_addr__contains=request.session["pattern"])
|
||||
nfilter = Q(mail__from_addr__contains=pattern)
|
||||
elif c == "subject":
|
||||
nfilter = Q(mail__subject__contains=request.session["pattern"])
|
||||
nfilter = Q(mail__subject__contains=pattern)
|
||||
elif c == "to":
|
||||
rcptfilter = request.session["pattern"]
|
||||
rcptfilter = pattern
|
||||
continue
|
||||
else:
|
||||
raise BadRequest("unsupported search criteria %s" % c)
|
||||
flt = nfilter if flt is None else flt | nfilter
|
||||
|
||||
msgtype = navparams.get('msgtype', None)
|
||||
if msgtype is not None:
|
||||
nfilter = Q(mail__msgrcpt__content=msgtype)
|
||||
flt = flt | nfilter if flt is not None else nfilter
|
||||
|
||||
msgs = get_wrapper().get_mails(request, rcptfilter)
|
||||
|
||||
if "page" in request.GET:
|
||||
request.session["page"] = request.GET["page"]
|
||||
pageid = int(request.session["page"])
|
||||
else:
|
||||
if "page" in request.session:
|
||||
del request.session["page"]
|
||||
pageid = 1
|
||||
|
||||
page = navparams.get('page')
|
||||
lst = SQLlisting(
|
||||
request.user, msgs, flt,
|
||||
navparams=request.session["navparams"],
|
||||
navparams=request.session["quarantine_navparams"],
|
||||
elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE"))
|
||||
)
|
||||
page = lst.paginator.getpage(pageid)
|
||||
page = lst.paginator.getpage(page)
|
||||
if not page:
|
||||
return empty_quarantine(request)
|
||||
|
||||
content = lst.fetch(request, page.id_start, page.id_stop)
|
||||
navbar = lst.render_navbar(page, "listing/?")
|
||||
ctx = getctx("ok", listing=content, navbar=navbar,
|
||||
menu=quar_menu(request.user))
|
||||
ctx = getctx(
|
||||
"ok", listing=content, paginbar=pagination_bar(page), page=page.number
|
||||
)
|
||||
if request.session.get('location', 'listing') != 'listing':
|
||||
ctx['menu'] = quar_menu()
|
||||
request.session['location'] = 'listing'
|
||||
return render_to_json_response(ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
return render(request, "amavis/index.html", dict(
|
||||
deflocation="listing/", defcallback="listing_cb", selection="quarantine"
|
||||
deflocation="listing/?sort_order=-date", defcallback="listing_cb",
|
||||
selection="quarantine"
|
||||
))
|
||||
|
||||
|
||||
@@ -160,23 +136,20 @@ def viewmail_selfservice(request, mail_id,
|
||||
|
||||
@selfservice(viewmail_selfservice)
|
||||
def viewmail(request, mail_id):
|
||||
if request.user.group != 'SimpleUsers':
|
||||
rcpt = request.GET["rcpt"]
|
||||
else:
|
||||
rcpt = None
|
||||
|
||||
rcpt = request.GET["rcpt"]
|
||||
if request.user.mailbox_set.count():
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
if not rcpt or rcpt == mb.full_address:
|
||||
msgrcpt = get_wrapper().get_recipient_message(mb.full_address, mail_id)
|
||||
if rcpt in mb.alias_addresses:
|
||||
msgrcpt = get_wrapper().get_recipient_message(rcpt, mail_id)
|
||||
msgrcpt.rs = 'V'
|
||||
msgrcpt.save()
|
||||
|
||||
content = Template("""
|
||||
<iframe src="{{ url }}" id="mailcontent"></iframe>
|
||||
""").render(Context({"url": reverse(getmailcontent, args=[mail_id])}))
|
||||
menu = viewm_menu(request.user, mail_id, rcpt)
|
||||
menu = viewm_menu(mail_id, rcpt)
|
||||
ctx = getctx("ok", menu=menu, listing=content)
|
||||
request.session['location'] = 'viewmail'
|
||||
return render_to_json_response(ctx)
|
||||
|
||||
|
||||
@@ -193,8 +166,8 @@ def viewheaders(request, mail_id):
|
||||
|
||||
def check_mail_id(request, mail_id):
|
||||
if type(mail_id) in [str, unicode]:
|
||||
if "rcpt" in request.GET:
|
||||
mail_id = ["%s %s" % (request.GET["rcpt"], mail_id)]
|
||||
if "rcpt" in request.POST:
|
||||
mail_id = ["%s %s" % (request.POST["rcpt"], mail_id)]
|
||||
else:
|
||||
mail_id = [mail_id]
|
||||
return mail_id
|
||||
@@ -215,27 +188,29 @@ def delete_selfservice(request, mail_id):
|
||||
|
||||
@selfservice(delete_selfservice)
|
||||
def delete(request, mail_id):
|
||||
mail_id = check_mail_id(request, mail_id)
|
||||
if request.user.group == 'SimpleUsers':
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
msgrcpts = get_wrapper().get_recipient_messages(mb.full_address, mail_id)
|
||||
#msgrcpts.update(rs='D')
|
||||
for msgrcpt in msgrcpts:
|
||||
msgrcpt.rs = 'D'
|
||||
msgrcpt.save()
|
||||
else:
|
||||
wrapper = get_wrapper()
|
||||
for mid in mail_id:
|
||||
r, i = mid.split()
|
||||
msgrcpt = wrapper.get_recipient_message(r, i)
|
||||
msgrcpt.rs = 'D'
|
||||
msgrcpt.save()
|
||||
"""Delete message selection.
|
||||
|
||||
:param str mail_id: message unique identifier
|
||||
"""
|
||||
mail_id = check_mail_id(request, mail_id)
|
||||
wrapper = get_wrapper()
|
||||
mb = Mailbox.objects.get(user=request.user) \
|
||||
if request.user.group == 'SimpleUsers' else None
|
||||
for mid in mail_id:
|
||||
r, i = mid.split()
|
||||
if mb is not None and r != mb.full_address \
|
||||
and not r in mb.alias_addresses:
|
||||
continue
|
||||
msgrcpt = wrapper.get_recipient_message(r, i)
|
||||
msgrcpt.rs = 'D'
|
||||
msgrcpt.save()
|
||||
message = ungettext("%(count)d message deleted successfully",
|
||||
"%(count)d messages deleted successfully",
|
||||
len(mail_id)) % {"count": len(mail_id)}
|
||||
return ajax_response(request, respmsg=message,
|
||||
url=__back_to_listing(request))
|
||||
return ajax_response(
|
||||
request, respmsg=message,
|
||||
url=QuarantineNavigationParameters(request).back_to_listing()
|
||||
)
|
||||
|
||||
|
||||
def release_selfservice(request, mail_id):
|
||||
@@ -266,30 +241,37 @@ def release_selfservice(request, mail_id):
|
||||
|
||||
@selfservice(release_selfservice)
|
||||
def release(request, mail_id):
|
||||
"""Release message selection.
|
||||
|
||||
:param str mail_id: message unique identifier
|
||||
"""
|
||||
mail_id = check_mail_id(request, mail_id)
|
||||
if request.user.group == 'SimpleUsers':
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
msgrcpts = get_wrapper().get_recipient_messages(mb.full_address, mail_id)
|
||||
if parameters.get_admin("USER_CAN_RELEASE") == "no":
|
||||
# FIXME : can't use this syntax because extra SQL (using
|
||||
# .extra() for postgres) is not propagated (the 'tables'
|
||||
# parameter is lost somewhere...)
|
||||
#
|
||||
# msgrcpts.update(rs='p')
|
||||
for msgrcpt in msgrcpts:
|
||||
msgrcpt.rs = 'p'
|
||||
msgrcpt.save()
|
||||
message = ungettext("%(count)d request sent",
|
||||
"%(count)d requests sent",
|
||||
len(mail_id)) % {"count": len(mail_id)}
|
||||
return ajax_response(request, "ok", respmsg=message,
|
||||
url=__back_to_listing(request))
|
||||
else:
|
||||
msgrcpts = []
|
||||
wrapper = get_wrapper()
|
||||
for mid in mail_id:
|
||||
r, i = mid.split()
|
||||
msgrcpts += [wrapper.get_recipient_message(r, i)]
|
||||
msgrcpts = []
|
||||
wrapper = get_wrapper()
|
||||
mb = Mailbox.objects.get(user=request.user) \
|
||||
if request.user.group == 'SimpleUsers' else None
|
||||
for mid in mail_id:
|
||||
r, i = mid.split()
|
||||
if mb is not None and r != mb.full_address \
|
||||
and not r in mb.alias_addresses:
|
||||
continue
|
||||
msgrcpts += [wrapper.get_recipient_message(r, i)]
|
||||
if mb is not None and parameters.get_admin("USER_CAN_RELEASE") == "no":
|
||||
# FIXME : can't use this syntax because extra SQL (using
|
||||
# .extra() for postgres) is not propagated (the 'tables'
|
||||
# parameter is lost somewhere...)
|
||||
#
|
||||
# msgrcpts.update(rs='p')
|
||||
for msgrcpt in msgrcpts:
|
||||
msgrcpt.rs = 'p'
|
||||
msgrcpt.save()
|
||||
message = ungettext("%(count)d request sent",
|
||||
"%(count)d requests sent",
|
||||
len(mail_id)) % {"count": len(mail_id)}
|
||||
return ajax_response(
|
||||
request, "ok", respmsg=message,
|
||||
url=QuarantineNavigationParameters(request).back_to_listing()
|
||||
)
|
||||
|
||||
amr = AMrelease()
|
||||
error = None
|
||||
@@ -308,8 +290,10 @@ def release(request, mail_id):
|
||||
len(mail_id)) % {"count": len(mail_id)}
|
||||
else:
|
||||
message = error
|
||||
return ajax_response(request, "ko" if error else "ok", respmsg=message,
|
||||
url=__back_to_listing(request))
|
||||
return ajax_response(
|
||||
request, "ko" if error else "ok", respmsg=message,
|
||||
url=QuarantineNavigationParameters(request).back_to_listing()
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -33,7 +33,7 @@ Best regards,
|
||||
%(name)s
|
||||
"""),
|
||||
help_text=ugettext_lazy(
|
||||
"Default content user when an auto-reply message is created "
|
||||
"Default content used when an auto-reply message is created "
|
||||
"automatically. The '%(name)s' macro will be replaced by the "
|
||||
"user's full name."
|
||||
),
|
||||
|
||||
@@ -86,6 +86,8 @@ def onMailboxDeleted(mailboxes):
|
||||
|
||||
@events.observe("MailboxModified")
|
||||
def onMailboxModified(mailbox):
|
||||
if not hasattr(mailbox, 'old_full_address'):
|
||||
return
|
||||
if mailbox.full_address == mailbox.old_full_address:
|
||||
return
|
||||
alias = Alias.objects.get(full_address=mailbox.old_full_address)
|
||||
|
||||
@@ -47,10 +47,12 @@ $(document).ready(function() {
|
||||
<form class="form-inline">
|
||||
<span id="custom-period">
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-indent-left"></i></span><input id="id_from" class="span10" name="date_from" value="{{ start }}" type="text" />
|
||||
<span class="add-on"><i class="icon-indent-left"></i></span>
|
||||
<input id="id_from" class="span10" name="date_from" value="{{ start }}" type="text" readonly="readonly" />
|
||||
</div>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-indent-right"></i></span><input id="id_to" class="span10" name="date_to" value="{{ end }}" type="text" />
|
||||
<span class="add-on"><i class="icon-indent-right"></i></span>
|
||||
<input id="id_to" class="span10" name="date_to" value="{{ end }}" type="text" readonly="readonly" />
|
||||
</div>
|
||||
<button class="btn" id="customsend">{% trans "Apply" %}</button>
|
||||
</span>
|
||||
|
||||
@@ -10,8 +10,8 @@ from django.core.files.uploadhandler import FileUploadHandler, SkipFile
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.conf import settings
|
||||
from modoboa.lib import u2u_decode, tables, parameters
|
||||
from modoboa.lib.webutils import size2integer, NavigationParameters
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
from modoboa.lib.webutils import size2integer
|
||||
from modoboa.lib.email_listing import EmailListing
|
||||
from modoboa.lib.emailutils import (
|
||||
EmailAddress, Email, prepare_addresses, set_email_headers
|
||||
@@ -26,22 +26,29 @@ class WMtable(tables.Table):
|
||||
tableid = "emails"
|
||||
styles = "table-condensed"
|
||||
idkey = "imapid"
|
||||
select = tables.ImgColumn(
|
||||
"select", cssclass="draggable left", width="2%",
|
||||
drag = tables.ImgColumn(
|
||||
"drag", cssclass="draggable left", width="1%",
|
||||
defvalue="%spics/grippy.png" % settings.STATIC_URL,
|
||||
header="<input type='checkbox' name='toggleselect' id='toggleselect' />"
|
||||
)
|
||||
flags = tables.ImgColumn("flags", width="4%")
|
||||
selection = tables.SelectionColumn(
|
||||
"selection", safe=True, width='1%', header=None, sortable=False
|
||||
)
|
||||
flags = tables.ImgColumn("flags", width="3%")
|
||||
withatts = tables.ImgColumn("withatts", width="2%")
|
||||
subject = tables.Column(
|
||||
"subject", label=ugettext_lazy("Subject"), width="50%", limit=60
|
||||
"subject", label=ugettext_lazy("Subject"), width="50%", limit=60,
|
||||
cssclass="openable"
|
||||
)
|
||||
from_ = tables.Column(
|
||||
"from", width="20%", label=ugettext_lazy("From"), limit=30
|
||||
"from", width="20%", label=ugettext_lazy("From"), limit=30,
|
||||
cssclass="openable"
|
||||
)
|
||||
date = tables.Column(
|
||||
"date", width="15%", label=ugettext_lazy("Date"), cssclass="openable"
|
||||
)
|
||||
date = tables.Column("date", width="15%", label=ugettext_lazy("Date"))
|
||||
|
||||
cols_order = ["select", "withatts", "flags", "subject", "from_", "date"]
|
||||
cols_order = ["drag", "selection", "withatts", "flags", "subject", "from_", "date"]
|
||||
|
||||
def parse(self, header, value):
|
||||
if value is None:
|
||||
@@ -143,9 +150,10 @@ class ImapListing(EmailListing):
|
||||
def __init__(self, user, password, **kwargs):
|
||||
self.user = user
|
||||
self.mbc = IMAPconnector(user=user.username, password=password)
|
||||
if "pattern" in kwargs:
|
||||
self.parse_search_parameters(kwargs["criteria"],
|
||||
kwargs["pattern"])
|
||||
if "pattern" in kwargs and kwargs["pattern"]:
|
||||
self.parse_search_parameters(
|
||||
kwargs["criteria"], kwargs["pattern"]
|
||||
)
|
||||
else:
|
||||
self.mbc.criterions = []
|
||||
|
||||
@@ -605,17 +613,6 @@ def html2plaintext(content):
|
||||
return plaintext
|
||||
|
||||
|
||||
def get_current_url(request):
|
||||
if not "folder" in request.session:
|
||||
return ""
|
||||
|
||||
res = "%s?page=%s" % (request.session["folder"], request.session["page"])
|
||||
for p in ["criteria", "pattern", "order"]:
|
||||
if p in request.session.keys():
|
||||
res += "&%s=%s" % (p, request.session[p])
|
||||
return res
|
||||
|
||||
|
||||
def create_mail_attachment(attdef):
|
||||
"""Create the MIME part corresponding to the given attachment.
|
||||
|
||||
@@ -726,7 +723,7 @@ def send_mail(request, posturl=None):
|
||||
password=request.session["password"]).push_mail(sentfolder, msg)
|
||||
clean_attachments(request.session["compose_mail"]["attachments"])
|
||||
del request.session["compose_mail"]
|
||||
return True, dict(url=get_current_url(request))
|
||||
return True, {}
|
||||
|
||||
listing = _render_to_string(request, "webmail/compose.html",
|
||||
{"form": form, "noerrors": True,
|
||||
@@ -756,3 +753,15 @@ class AttachmentUploadHandler(FileUploadHandler):
|
||||
|
||||
def file_complete(self, file_size):
|
||||
return None
|
||||
|
||||
|
||||
class WebmailNavigationParameters(NavigationParameters):
|
||||
"""
|
||||
Specific NavigationParameters subclass for the webmail.
|
||||
"""
|
||||
def __init__(self, request, defmailbox=None):
|
||||
super(WebmailNavigationParameters, self).__init__(
|
||||
request, 'webmail_navparams'
|
||||
)
|
||||
if defmailbox is not None:
|
||||
self.parameters += [('mbox', defmailbox, False)]
|
||||
|
||||
@@ -228,4 +228,15 @@ div.expanded {
|
||||
|
||||
#folders2 li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*
|
||||
* Table
|
||||
*/
|
||||
td > input[type=checkbox] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
td[class*=openable] {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -76,7 +76,6 @@ Webmail.prototype = {
|
||||
this.navobject.register_callback("reply", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("replyall", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("forward", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("delete", $.proxy(this.delete_callback, this));
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
@@ -96,7 +95,7 @@ Webmail.prototype = {
|
||||
$(document).on("click", "a[name=editmbox]", $.proxy(this.edit_mbox, this));
|
||||
$(document).on("click", "a[name=removembox]", $.proxy(this.remove_mbox, this));
|
||||
|
||||
$(document).on("dblclick", "tbody>tr", $.proxy(this.viewmail_loader, this));
|
||||
$(document).on("click", "td[class*=openable]", $.proxy(this.viewmail_loader, this));
|
||||
|
||||
$(document).on("click", "a[name=reply]", $.proxy(this.reply_loader, this));
|
||||
$(document).on("click", "a[name=replyall]", $.proxy(this.reply_loader, this));
|
||||
@@ -871,13 +870,11 @@ Webmail.prototype = {
|
||||
},
|
||||
|
||||
delete_callback: function(data) {
|
||||
var msg = (data.respmsg) ? data.respmsg : gettext("Message deleted");
|
||||
|
||||
this.go_back_to_listing();
|
||||
if (this.get_current_mailbox() != this.options.trash) {
|
||||
$("a[name=totrash]").removeClass("disabled");
|
||||
}
|
||||
$("body").notify("success", msg, 2000);
|
||||
$("body").notify("success", data, 2000);
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -1009,14 +1006,16 @@ Webmail.prototype = {
|
||||
init_draggables: function() {
|
||||
var plug = this;
|
||||
|
||||
$("td[name=select]").draggable({
|
||||
$("td[name=drag]").draggable({
|
||||
opacity: 0.8,
|
||||
helper: function(e) {
|
||||
var $this = $(this);
|
||||
var $tr = $this.parent();
|
||||
|
||||
if (!plug.htmltable.is_selected($tr)) {
|
||||
var $input = $tr.find('#selection');
|
||||
plug.htmltable.select_row($tr);
|
||||
$input.prop('checked', true);
|
||||
}
|
||||
|
||||
var nmsgs = plug.htmltable.current_selection().length;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{% extends "common/buttons_list.html" %}{% load i18n %}
|
||||
{% block extracontent %}{% include "common/email_searchbar.html" with extraopts=extraopts %}
|
||||
{% endblock %}
|
||||
@@ -114,13 +114,10 @@ def listmailbox_menu(selection, folder, user):
|
||||
"label": _("Empty folder"),
|
||||
"url": reverse(webmail.views.empty, args=[folder])}
|
||||
]
|
||||
searchbar = render_to_string('common/email_searchbar.html', {
|
||||
"STATIC_URL": settings.STATIC_URL
|
||||
return render_to_string('webmail/main_action_bar.html', {
|
||||
'selection': selection, 'entries': entries, 'user': user, 'css': "nav",
|
||||
'STATIC_URL': settings.STATIC_URL
|
||||
})
|
||||
return render_to_string('common/buttons_list.html', dict(
|
||||
selection=selection, entries=entries, extracontent=searchbar,
|
||||
user=user, css="nav"
|
||||
))
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
||||
@@ -23,7 +23,7 @@ from .lib import (
|
||||
decode_payload, AttachmentUploadHandler,
|
||||
save_attachment, ImapListing, EmailSignature,
|
||||
clean_attachments, set_compose_session, send_mail,
|
||||
ImapEmail, ReplyModifier, ForwardModifier
|
||||
ImapEmail, WebmailNavigationParameters, ReplyModifier, ForwardModifier
|
||||
)
|
||||
from templatetags import webmail_tags
|
||||
|
||||
@@ -64,8 +64,9 @@ def move(request):
|
||||
if not arg in request.GET:
|
||||
raise BadRequest(_("Invalid request"))
|
||||
mbc = get_imapconnector(request)
|
||||
mbc.move(request.GET["msgset"], request.session["mbox"], request.GET["to"])
|
||||
resp = listmailbox(request, request.session["mbox"], update_session=False)
|
||||
navparams = WebmailNavigationParameters(request)
|
||||
mbc.move(request.GET["msgset"], navparams.get('mbox'), request.GET["to"])
|
||||
resp = listmailbox(request, navparams.get('mbox'), update_session=False)
|
||||
return render_to_json_response(resp)
|
||||
|
||||
|
||||
@@ -189,8 +190,7 @@ def editfolder(request, tplname="webmail/folder.html"):
|
||||
res["newmb"] = form.cleaned_data["name"]
|
||||
res["oldparent"] = oldparent
|
||||
res["newparent"] = pf
|
||||
if "mbox" in request.session:
|
||||
del request.session["mbox"]
|
||||
WebmailNavigationParameters(request).remove('mbox')
|
||||
return render_to_json_response(res)
|
||||
|
||||
return render_to_json_response({'form_errors': form.errors}, status=400)
|
||||
@@ -224,8 +224,7 @@ def delfolder(request):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
mbc.delete_folder(name)
|
||||
if "mbox" in request.session:
|
||||
del request.session["mbox"]
|
||||
WebmailNavigationParameters(request).remove('mbox')
|
||||
return ajax_response(request)
|
||||
|
||||
|
||||
@@ -307,7 +306,7 @@ def render_mboxes_list(request, imapc):
|
||||
:param imapc: an ``IMAPconnector` object
|
||||
:return: a string
|
||||
"""
|
||||
curmbox = request.session.get("mbox", "INBOX")
|
||||
curmbox = WebmailNavigationParameters(request).get("mbox", "INBOX")
|
||||
return _render_to_string(request, "webmail/folders.html", {
|
||||
"selected": curmbox,
|
||||
"mboxes": imapc.getmboxes(request.user),
|
||||
@@ -315,25 +314,6 @@ def render_mboxes_list(request, imapc):
|
||||
})
|
||||
|
||||
|
||||
def set_nav_params(request):
|
||||
if not "navparams" in request.session:
|
||||
request.session["navparams"] = {}
|
||||
|
||||
request.session["pageid"] = \
|
||||
int(request.GET.get("page", 1))
|
||||
if not request.GET.get("order", False):
|
||||
if not "order" in request.session["navparams"]:
|
||||
request.session["navparams"]["order"] = "-date"
|
||||
else:
|
||||
request.session["navparams"]["order"] = request.GET.get("order")
|
||||
|
||||
for p in ["pattern", "criteria"]:
|
||||
if request.GET.get(p, False):
|
||||
request.session["navparams"][p] = request.GET[p]
|
||||
elif p in request.session["navparams"]:
|
||||
del request.session["navparams"][p]
|
||||
|
||||
|
||||
def listmailbox(request, defmailbox="INBOX", update_session=True):
|
||||
"""Mailbox content listing
|
||||
|
||||
@@ -345,19 +325,18 @@ def listmailbox(request, defmailbox="INBOX", update_session=True):
|
||||
:param defmailbox: the default mailbox (when not present inside request arguments)
|
||||
:return: a dictionnary
|
||||
"""
|
||||
mbox = request.GET.get("mbox", defmailbox)
|
||||
navparams = WebmailNavigationParameters(request, defmailbox)
|
||||
if update_session:
|
||||
set_nav_params(request)
|
||||
request.session["mbox"] = mbox
|
||||
|
||||
navparams.store()
|
||||
mbox = navparams.get('mbox')
|
||||
lst = ImapListing(
|
||||
request.user, request.session["password"],
|
||||
baseurl="?action=listmailbox&mbox=%s&" % mbox,
|
||||
folder=mbox,
|
||||
elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")),
|
||||
**request.session["navparams"]
|
||||
**request.session["webmail_navparams"]
|
||||
)
|
||||
return lst.render(request, request.session["pageid"])
|
||||
return lst.render(request, navparams.get('page'))
|
||||
|
||||
|
||||
def render_compose(request, form, posturl, email=None, insert_signature=False):
|
||||
@@ -524,7 +503,6 @@ def index(request):
|
||||
|
||||
"""
|
||||
action = request.GET.get("action", None)
|
||||
|
||||
if action is not None:
|
||||
if not action in globals():
|
||||
raise UnknownAction
|
||||
@@ -534,7 +512,7 @@ def index(request):
|
||||
raise BadRequest(_("Invalid request"))
|
||||
response = dict(selection="webmail")
|
||||
|
||||
curmbox = request.session.get("mbox", "INBOX")
|
||||
curmbox = WebmailNavigationParameters(request).get("mbox", "INBOX")
|
||||
if not request.is_ajax():
|
||||
request.session["lastaction"] = None
|
||||
imapc = get_imapconnector(request)
|
||||
|
||||
@@ -139,16 +139,3 @@ class EmailListing(object):
|
||||
else:
|
||||
listing = self.fetch(request, page.id_start, page.id_stop)
|
||||
return dict(listing=listing, navbar=self.render_navbar(page, self.baseurl))
|
||||
|
||||
|
||||
def parse_search_parameters(request):
|
||||
if 'pattern' in request.GET:
|
||||
request.session["pattern"] = re.escape(request.GET["pattern"])
|
||||
if 'criteria' in request.GET:
|
||||
request.session["criteria"] = request.GET["criteria"]
|
||||
else:
|
||||
request.session["criteria"] = ["from_addr"]
|
||||
else:
|
||||
for p in ["pattern", "criteria"]:
|
||||
if p in request.session.keys():
|
||||
del request.session[p]
|
||||
|
||||
@@ -41,8 +41,11 @@ class Column(object):
|
||||
col["value"] = row[self.name]
|
||||
try:
|
||||
limit = getattr(self, "limit")
|
||||
if len(col["value"]) > limit:
|
||||
col["value"] = col["value"][0:limit] + "..."
|
||||
if limit:
|
||||
# The value is not necessarily a string, so...
|
||||
col["value"] = unicode(col["value"], "utf-8") if isinstance(col["value"], str) else unicode(col["value"])
|
||||
if len(col["value"]) > limit:
|
||||
col["value"] = col["value"][0:limit] + "..."
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
|
||||
@@ -155,3 +155,55 @@ def topredirection(request):
|
||||
infos = exts_pool.get_extension_infos(topredir)
|
||||
path = infos["url"] if infos["url"] else infos["name"]
|
||||
return HttpResponseRedirect(path)
|
||||
|
||||
|
||||
class NavigationParameters(object):
|
||||
"""
|
||||
Just a simple object to manipulate navigation parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, request, sessionkey):
|
||||
self.request = request
|
||||
self.sessionkey = sessionkey
|
||||
self.parameters = [('pattern', '', True),
|
||||
('criteria', 'from_addr', False)]
|
||||
|
||||
def store(self):
|
||||
"""Store navigation parameters into session.
|
||||
"""
|
||||
if not self.sessionkey in self.request.session:
|
||||
self.request.session[self.sessionkey] = {}
|
||||
navparams = self.request.session[self.sessionkey]
|
||||
navparams["order"] = self.request.GET.get("sort_order", "-date")
|
||||
navparams["page"] = int(self.request.GET.get("page", 1))
|
||||
for param, defvalue, escape in self.parameters:
|
||||
value = self.request.GET.get(param, defvalue)
|
||||
if value is None:
|
||||
if param in navparams:
|
||||
del navparams[param]
|
||||
continue
|
||||
navparams[param] = re.escape(value) if escape else value
|
||||
self.request.session.modified = True
|
||||
|
||||
def get(self, param, default_value=None):
|
||||
"""Retrieve a navigation parameter.
|
||||
|
||||
Just a simple getter to avoid using the full key name to
|
||||
access a parameter.
|
||||
|
||||
:param str param: parameter name
|
||||
:param defaultvalue: default value if none is found
|
||||
:return: parameter's value
|
||||
"""
|
||||
if not self.sessionkey in self.request.session:
|
||||
return default_value
|
||||
return self.request.session[self.sessionkey].get(param, default_value)
|
||||
|
||||
def remove(self, param):
|
||||
"""Remove a navigation parameter from session.
|
||||
|
||||
:param str param: parameter name
|
||||
"""
|
||||
navparams = self.request.session[self.sessionkey]
|
||||
if param in navparams:
|
||||
del navparams[param]
|
||||
|
||||
+1624
-1205
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: modoboa-1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: miris@kunago.com\n"
|
||||
"POT-Creation-Date: 2013-09-20 22:30+0200\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: 2013-09-29 22:13+0100\n"
|
||||
"Last-Translator: Miroslav Abrahám <miris@kunago.com>\n"
|
||||
"Language-Team: CS <miris@kunago.com>\n"
|
||||
@@ -18,97 +18,81 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Poedit 1.5.7\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Hledat"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Nemazat adresář domény"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Tento krok odstraní VŠECHNA data související s touto doménou."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr ""
|
||||
"Tento krok odstraní VŠECHNA data související s touto doménou, vyjma účtů."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "Smazat tuto doménu?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Není určen správce domény"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Nemazat adresář schránky"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "Smazat tento účet?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "Smazat tento seznam?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "Smazat toto přeposílání?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "Smazat tento alias?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Odblokovat tento výběr?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Smazat tento výběr?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Odblokovat tuto zprávu?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Smazat tuto zprávu?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Zobrazit úplné záhlaví"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Skrýt záhlaví"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Odstranit tento filtr?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Uložit změny?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Smazat sadu filtrů?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Aktivovat tuto sadu filtrů?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "aktivní"
|
||||
|
||||
@@ -116,19 +100,19 @@ msgstr "aktivní"
|
||||
msgid "Search a domain"
|
||||
msgstr "Vyhledat doménu"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Odstranit vybranou schránku?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Schránka odstraněna"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Zpráva odeslána"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Zpráva smazána"
|
||||
|
||||
@@ -148,18 +132,34 @@ msgstr[0] "Přesunuji %s zprávu"
|
||||
msgstr[1] "Přesunuji %s zprávy"
|
||||
msgstr[2] "Přesunuji %s zpráv"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Upozornění"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Zrušit"
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
#, fuzzy
|
||||
msgid "Internal error"
|
||||
msgstr "Interní chyba"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Hledat..."
|
||||
|
||||
#~ msgid "Delete this domain?"
|
||||
#~ msgstr "Smazat tuto doménu?"
|
||||
|
||||
#~ msgid "Delete this account?"
|
||||
#~ msgstr "Smazat tento účet?"
|
||||
|
||||
#~ msgid "Delete this list?"
|
||||
#~ msgstr "Smazat tento seznam?"
|
||||
|
||||
#~ msgid "Delete this forward?"
|
||||
#~ msgstr "Smazat toto přeposílání?"
|
||||
|
||||
#~ msgid "Delete this alias?"
|
||||
#~ msgstr "Smazat tento alias?"
|
||||
|
||||
+1585
-1243
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa-0.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"PO-Revision-Date: 2013-05-24 22:38+0100\n"
|
||||
"POT-Creation-Date: 2013-12-03 10:33+0100\n"
|
||||
"PO-Revision-Date: 2013-12-08 23:34+0100\n"
|
||||
"Last-Translator: Patrick Ben Koetter <p@sys4.de>\n"
|
||||
"Language-Team: DE <p@state-of-mind.fr>\n"
|
||||
"Language: de\n"
|
||||
@@ -17,94 +17,78 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Verzeichnis der Domain nicht löschen"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Diese Aktion wird ALLE mit der Domain verbundenen Daten löschen."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid "This operation will remove all data associated to this domain, excepting accounts."
|
||||
msgstr "Diese Aktion wird ALLE mit der Domain verbundenen Daten löschen - ausser Mailkonten."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "Diese Domain löschen?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Domain-Administrator fehlt"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Verzeichnis der Mailbox nicht löschen"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "Diese Konto löschen?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "Diese Liste löschen?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "Diese Weiterleitung löschen?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "Diesen Alias löschen?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Diese Auswahl freilassen?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Diese Auswahl löschen?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Diese Nachricht freilassen?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Diese Nachricht löschen?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Vollansicht Header anzeigen"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Vollansicht Header ausblenden"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr "'service'-Liste aktualisiert"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Diesen Filter entfernen?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Änderungen speichern?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Diesen Filter entfernen?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Diesen Filter aktivieren?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "aktiv"
|
||||
|
||||
@@ -112,19 +96,19 @@ msgstr "aktiv"
|
||||
msgid "Search a domain"
|
||||
msgstr "Domain suchen"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Ausgewählte Mailbox entfernen?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Mailbox entfernt"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Nachricht gesendet"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Nachricht gelöscht"
|
||||
|
||||
@@ -142,22 +126,36 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] "Verschiebe %s Nachricht"
|
||||
msgstr[1] "Verschiebe %s Nachrichten"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Warnung"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: static/js/confirmation.js:99
|
||||
#: static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
msgid "Internal error"
|
||||
msgstr "Interner Fehler"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Suchen..."
|
||||
|
||||
#~ msgid "Delete this domain?"
|
||||
#~ msgstr "Diese Domain löschen?"
|
||||
|
||||
#~ msgid "Delete this account?"
|
||||
#~ msgstr "Diese Konto löschen?"
|
||||
|
||||
#~ msgid "Delete this list?"
|
||||
#~ msgstr "Diese Liste löschen?"
|
||||
|
||||
#~ msgid "Delete this forward?"
|
||||
#~ msgstr "Diese Weiterleitung löschen?"
|
||||
|
||||
#~ msgid "Delete this alias?"
|
||||
#~ msgstr "Diesen Alias löschen?"
|
||||
|
||||
#~ msgid "Loading..."
|
||||
#~ msgstr "Lade..."
|
||||
|
||||
+1482
-1152
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,96 +17,80 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr ""
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
@@ -114,19 +98,19 @@ msgstr ""
|
||||
msgid "Search a domain"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr ""
|
||||
|
||||
@@ -144,16 +128,16 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
msgid "Internal error"
|
||||
msgstr ""
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
|
||||
+1702
-1188
File diff suppressed because it is too large
Load Diff
@@ -1,114 +1,101 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# Pedro Gracia <lasarux@neuroomante.com>, 2011,2013
|
||||
# Pedro Gracia <pedro.gracia@impulzia.com>, 2010, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: http://dev.modoboa.org/\n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: 2013-06-03 16:32+0000\n"
|
||||
"Last-Translator: Pedro Gracia <lasarux@neuroomante.com>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/modoboa/language/es/)\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/projects/p/modoboa/language/"
|
||||
"es/)\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "No eleminar el directorio del dominio"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Esta operación eliminará TODOS los datos asociados a este dominio."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Esta operación eliminará todos los datos asociados a este dominio, excepto las cuentas."
|
||||
msgstr ""
|
||||
"Esta operación eliminará todos los datos asociados a este dominio, excepto "
|
||||
"las cuentas."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "¿Eliminar este dominio?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "No hay un administrador de dominio definido"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "No eliminar el directorio del mailbox"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "¿Eliminar esta cuenta?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "¿Eliminar esta lista?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "¿Eliminar este reenvío?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "¿Eliminar este alias?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "¿Publicar esta selección?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "¿Eliminar esta selección?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "¿Publicar este mensaje?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "¿Eliminar este mensaje?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Ver todas las cabeceras"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Esconder todas las cabeceras"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "¿Eliminar este filtro?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "¿Guardar cambios?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "¿Eliminar este conjunto de filtros?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "¿Activar este conjunto de filtros?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "activo"
|
||||
|
||||
@@ -116,19 +103,19 @@ msgstr "activo"
|
||||
msgid "Search a domain"
|
||||
msgstr "Buscar un dominio"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "¿Eliminar el mailbox seleccionado?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Mailbox eliminado"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Mensaje enviado"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Mensaje eliminado"
|
||||
|
||||
@@ -146,18 +133,34 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] "Moviendo %s mensaje"
|
||||
msgstr[1] "Moviendo %s mensajes"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Aviso"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
#, fuzzy
|
||||
msgid "Internal error"
|
||||
msgstr "Error interno"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Buscar..."
|
||||
|
||||
#~ msgid "Delete this domain?"
|
||||
#~ msgstr "¿Eliminar este dominio?"
|
||||
|
||||
#~ msgid "Delete this account?"
|
||||
#~ msgstr "¿Eliminar esta cuenta?"
|
||||
|
||||
#~ msgid "Delete this list?"
|
||||
#~ msgstr "¿Eliminar esta lista?"
|
||||
|
||||
#~ msgid "Delete this forward?"
|
||||
#~ msgstr "¿Eliminar este reenvío?"
|
||||
|
||||
#~ msgid "Delete this alias?"
|
||||
#~ msgstr "¿Eliminar este alias?"
|
||||
|
||||
+1577
-1217
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: modoboa-1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: 2011-01-07 10:00+0100\n"
|
||||
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
|
||||
"Language-Team: FR <tonio@ngyn.org>\n"
|
||||
@@ -17,97 +17,82 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Chercher"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Ne pas supprimer le répertoire du domaine"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Cette opération supprimera TOUTES les données associées à ce domaine."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Cette opération supprimera toutes les données associées à ce domaine "
|
||||
"à l'excéption des comptes."
|
||||
msgstr ""
|
||||
"Cette opération supprimera toutes les données associées à ce domaine à "
|
||||
"l'excéption des comptes."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "Supprimer ce domaine?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Aucun administrateur de domaine défini"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Ne pas supprimer le répertoire de la boite aux lettres"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "Supprimer ce compte?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "Supprimer cette liste?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "Supprimer ce transfert?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "Supprimer cet alias?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Relâcher cette sélection?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Supprimer cette sélection?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Relâcher cette message?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Supprimer ce message?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Voir les entêtes complets"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Cacher les entêtes complets"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr "Services mis à jour"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Supprimer ce filtre?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Enregistrer les changements?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Supprimer ce jeu de filtres?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Activer ce jeu de filtres?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "actif"
|
||||
|
||||
@@ -115,19 +100,19 @@ msgstr "actif"
|
||||
msgid "Search a domain"
|
||||
msgstr "Checher un domaine"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Supprimer la boite sélectionnée?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Boite aux lettres supprimées"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Message envoyé"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Message supprimé"
|
||||
|
||||
@@ -145,16 +130,16 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] "Déplacement de %s message"
|
||||
msgstr[1] "Déplacement de %s messages"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Avertissement"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
msgid "Internal error"
|
||||
msgstr "Erreur interne"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
|
||||
+1623
-1205
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-09-26 23:44+0200\n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: 2013-09-27 00:07+0200\n"
|
||||
"Last-Translator: Giancarlo Gaifas <lallo@artiemestieri.tn.it>\n"
|
||||
"Language-Team: Italiano <LL@li.org>\n"
|
||||
@@ -17,19 +17,19 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Ricerca"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Non eliminare la cartella dei domini"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Questa operazione eliminerà TUTTI i dati associati a questo dominio."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
@@ -37,58 +37,62 @@ msgstr ""
|
||||
"Questa operazione eliminerà tutti i dati associati a questo dominio tranne "
|
||||
"gli account"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Non è stato definito un amministratore di dominio"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Non eliminare la cartella della casella posta"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Rilasciare questa selezione?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Eliminare questa selezione?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Rilasciare questo messaggio?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Eliminare questo messaggio?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Visualizza tutte le intestazioni"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Nascondi le intestazioni"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Rimuovere questo filtro?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Salvare le modifiche?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Rimuovere il gruppo di filtri?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Attivare il gruppo di filtri?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "attivo"
|
||||
|
||||
@@ -96,30 +100,30 @@ msgstr "attivo"
|
||||
msgid "Search a domain"
|
||||
msgstr "Cerca un dominio"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:476
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Rimuovere la casella di posta selezionata?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:490
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Casella di posta rimossa"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:846
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Messaggio inviato"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:878
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Messaggio eliminato"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1008
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:981
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s file"
|
||||
msgstr[1] "%s file"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1040
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1013
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
@@ -134,8 +138,9 @@ msgstr "Attenzione"
|
||||
msgid "Cancel"
|
||||
msgstr "Elimina"
|
||||
|
||||
#: static/js/confirmation.js:101 static/js/history.js:235
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
#, fuzzy
|
||||
msgid "Internal error"
|
||||
msgstr "Errore interno"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
|
||||
+1697
-1186
File diff suppressed because it is too large
Load Diff
@@ -1,114 +1,101 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# mikec <miguelmclara@gmail.com>, 2012
|
||||
# Sandra Ribeiro <sandrarib@gmail.com>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: http://dev.modoboa.org/\n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-03 10:33+0100\n"
|
||||
"PO-Revision-Date: 2013-06-03 17:44+0000\n"
|
||||
"Last-Translator: Sandra Ribeiro <sandrarib@gmail.com>\n"
|
||||
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/modoboa/language/pt_PT/)\n"
|
||||
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
|
||||
"modoboa/language/pt_PT/)\n"
|
||||
"Language: pt_PT\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_PT\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Procurar"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Não excluir o diretório do domínio"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Esta operação irá remover TODOS os dados associados a este domínio."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Esta operação irá remover TODOS os dados associados a este domínio, excepto as contas."
|
||||
msgstr ""
|
||||
"Esta operação irá remover TODOS os dados associados a este domínio, excepto "
|
||||
"as contas."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "Apagar este domínio?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Não está definido nenhum administrador para o domínio"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Não excluir o diretório da caixa de correio"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "Apagar esta conta?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "Apagar esta lista?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "Apagar este reencaminhamento?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "Apagar este alias?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Libertar itens seleccionados?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Eliminar itens seleccionados?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Libertar esta mensagem?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Eliminar esta mensagem?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Ver cabeçalho completo"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Esconder cabeçalho completo"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Remover este filtro?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Guardar alterações?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Limpar filtros definidos?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Activar estes filtros?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "activo"
|
||||
|
||||
@@ -116,19 +103,19 @@ msgstr "activo"
|
||||
msgid "Search a domain"
|
||||
msgstr "Pesquisar domínio"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Remover a caixa de correio seleccionada"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Caixa removida"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Mensagem enviada"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Mensagem apagada"
|
||||
|
||||
@@ -146,18 +133,34 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] "A mover %s mensagem"
|
||||
msgstr[1] "A mover %s mensagens"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Aviso"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
#, fuzzy
|
||||
msgid "Internal error"
|
||||
msgstr "Erro Interno"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Pesquisar..."
|
||||
|
||||
#~ msgid "Delete this domain?"
|
||||
#~ msgstr "Apagar este domínio?"
|
||||
|
||||
#~ msgid "Delete this account?"
|
||||
#~ msgstr "Apagar esta conta?"
|
||||
|
||||
#~ msgid "Delete this list?"
|
||||
#~ msgstr "Apagar esta lista?"
|
||||
|
||||
#~ msgid "Delete this forward?"
|
||||
#~ msgstr "Apagar este reencaminhamento?"
|
||||
|
||||
#~ msgid "Delete this alias?"
|
||||
#~ msgstr "Apagar este alias?"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-03 10:32+0100\n"
|
||||
"PO-Revision-Date: 2013-12-03 09:35+0000\n"
|
||||
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
|
||||
"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Не удаляйте каталог домена"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Эта операция удалит ВСЕ данные, связанные с этим доменом."
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Эта операция удалит ВСЕ данные, связанные с этим доменом, за исключением аккаунтов."
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Не указан администратор домена"
|
||||
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Не удаляйте каталог почтовых ящиков"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Деблокировать отмеченные?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Удалить отмеченные?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Деблокировать сообщение?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Удалить сообщение?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Просмотр всех заголовков"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Скрыть все заголовки"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Удалить фильтр?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Сохранить изменения?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Удалить набор фильтров?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Активировать набор фильтров?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "активно"
|
||||
|
||||
#: extensions/stats/static/stats/js/stats.js:37
|
||||
msgid "Search a domain"
|
||||
msgstr "Найти домен"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Удалить выбранные почтовые ящики?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Почтовый ящик удален"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Сообщение послано"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Сообщение удалено"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:981
|
||||
#, c-format
|
||||
msgid "%s file"
|
||||
msgid_plural "%s files"
|
||||
msgstr[0] "%s файл"
|
||||
msgstr[1] "%s файла"
|
||||
msgstr[2] "%s файлов"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:1013
|
||||
#, c-format
|
||||
msgid "Moving %s message"
|
||||
msgid_plural "Moving %s messages"
|
||||
msgstr[0] "Перемещается %s сообщение"
|
||||
msgstr[1] "Перемещается %s сообщения"
|
||||
msgstr[2] "Перемещается %s сообщений"
|
||||
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Внимание"
|
||||
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
#: static/js/global.js:314
|
||||
msgid "Internal error"
|
||||
msgstr ""
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Поиск..."
|
||||
+1680
-1181
File diff suppressed because it is too large
Load Diff
@@ -1,113 +1,100 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
#
|
||||
# Translators:
|
||||
# ogg1980 <olle@dalnix.se>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Modoboa\n"
|
||||
"Report-Msgid-Bugs-To: http://dev.modoboa.org/\n"
|
||||
"POT-Creation-Date: 2013-05-22 17:39+0200\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-12-03 10:33+0100\n"
|
||||
"PO-Revision-Date: 2013-05-23 08:22+0000\n"
|
||||
"Last-Translator: ogg1980 <olle@dalnix.se>\n"
|
||||
"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/sv/)\n"
|
||||
"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
|
||||
"sv/)\n"
|
||||
"Language: sv\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sv\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: admin/static/admin/js/admin.js:26
|
||||
#: extensions/admin/static/admin/js/admin.js:32
|
||||
msgid "Search"
|
||||
msgstr "Sök"
|
||||
|
||||
#: admin/static/admin/js/admin.js:110
|
||||
#: extensions/admin/static/admin/js/admin.js:170
|
||||
msgid "Do not delete domain directory"
|
||||
msgstr "Radera inte domän katalog"
|
||||
|
||||
#: admin/static/admin/js/admin.js:113
|
||||
#: extensions/admin/static/admin/js/admin.js:173
|
||||
msgid "This operation will remove ALL data associated to this domain."
|
||||
msgstr "Denna åtgärd kommer radera ALL data associerad med den här domänen."
|
||||
|
||||
#: admin/static/admin/js/admin.js:114
|
||||
#: extensions/admin/static/admin/js/admin.js:174
|
||||
msgid ""
|
||||
"This operation will remove all data associated to this domain, excepting "
|
||||
"accounts."
|
||||
msgstr "Den här operationen raderar all data som är associerad med denna domän, förutom konton."
|
||||
msgstr ""
|
||||
"Den här operationen raderar all data som är associerad med denna domän, "
|
||||
"förutom konton."
|
||||
|
||||
#: admin/static/admin/js/admin.js:117
|
||||
msgid "Delete this domain?"
|
||||
msgstr "Radera denna domän?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:161
|
||||
#: extensions/admin/static/admin/js/admin.js:222
|
||||
msgid "No domain administrator defined"
|
||||
msgstr "Ingen domänadministratör definierad"
|
||||
|
||||
#: admin/static/admin/js/admin.js:238
|
||||
#: extensions/admin/static/admin/js/admin.js:291
|
||||
msgid "Do not delete mailbox directory"
|
||||
msgstr "Radera inte brevlådans katalog"
|
||||
|
||||
#: admin/static/admin/js/admin.js:243
|
||||
msgid "Delete this account?"
|
||||
msgstr "Radera detta konto?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:249
|
||||
msgid "Delete this list?"
|
||||
msgstr "Radera denna lista?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:254
|
||||
msgid "Delete this forward?"
|
||||
msgstr "Radera denna vidarebefodring?"
|
||||
|
||||
#: admin/static/admin/js/admin.js:259
|
||||
msgid "Delete this alias?"
|
||||
msgstr "Radera detta alias?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:126
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:180
|
||||
msgid "Release this selection?"
|
||||
msgstr "Släpp markerat urval?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:130
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:184
|
||||
msgid "Delete this selection?"
|
||||
msgstr "Radera markerat urval?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:148
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:22
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:203
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:17
|
||||
msgid "Release this message?"
|
||||
msgstr "Släpp detta meddelande?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:152
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:26
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:207
|
||||
#: extensions/amavis/static/amavis/js/selfservice.js:21
|
||||
msgid "Delete this message?"
|
||||
msgstr "Radera detta meddelande?"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:169
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:224
|
||||
msgid "View full headers"
|
||||
msgstr "Visa meddelandehuvud"
|
||||
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:174
|
||||
#: extensions/amavis/static/amavis/js/quarantine.js:229
|
||||
msgid "Hide full headers"
|
||||
msgstr "Göm meddelandehuvud"
|
||||
|
||||
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
|
||||
msgid "Services updated"
|
||||
msgstr ""
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
|
||||
msgid "Remove this filter?"
|
||||
msgstr "Radera detta filter?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:117
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:107
|
||||
msgid "Save changes?"
|
||||
msgstr "Spara ändringar?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:144
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:129
|
||||
msgid "Remove filters set?"
|
||||
msgstr "Radera filteruppsättning?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:161
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:146
|
||||
msgid "Activate this filters set?"
|
||||
msgstr "Aktivera filteruppsättning?"
|
||||
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:169
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:224
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:154
|
||||
#: extensions/sievefilters/static/sievefilters/js/lib.js:203
|
||||
msgid "active"
|
||||
msgstr "aktivera"
|
||||
|
||||
@@ -115,19 +102,19 @@ msgstr "aktivera"
|
||||
msgid "Search a domain"
|
||||
msgstr "Sök domän"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:472
|
||||
msgid "Remove the selected mailbox?"
|
||||
msgstr "Radera markerad brevlåda?"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:499
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:485
|
||||
msgid "Mailbox removed"
|
||||
msgstr "Brevlåda raderad"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:823
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:727
|
||||
msgid "Message sent"
|
||||
msgstr "Meddelande sänt"
|
||||
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:856
|
||||
#: extensions/webmail/static/webmail/js/webmail.js:854
|
||||
msgid "Message deleted"
|
||||
msgstr "Meddelande raderat"
|
||||
|
||||
@@ -145,18 +132,34 @@ msgid_plural "Moving %s messages"
|
||||
msgstr[0] "Flyttar %s meddelande"
|
||||
msgstr[1] "Flyttar %s meddelanden"
|
||||
|
||||
#: static/js/confirmation.js:48
|
||||
#: static/js/confirmation.js:50
|
||||
msgid "Warning"
|
||||
msgstr "Varning"
|
||||
|
||||
#: static/js/confirmation.js:57
|
||||
#: static/js/confirmation.js:59
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
|
||||
#: static/js/confirmation.js:99 static/js/history.js:239
|
||||
msgid "Internal Error"
|
||||
#: static/js/global.js:314
|
||||
#, fuzzy
|
||||
msgid "Internal error"
|
||||
msgstr "Internt fel"
|
||||
|
||||
#: static/js/searchbar.js:20
|
||||
msgid "Search..."
|
||||
msgstr "Sök..."
|
||||
|
||||
#~ msgid "Delete this domain?"
|
||||
#~ msgstr "Radera denna domän?"
|
||||
|
||||
#~ msgid "Delete this account?"
|
||||
#~ msgstr "Radera detta konto?"
|
||||
|
||||
#~ msgid "Delete this list?"
|
||||
#~ msgstr "Radera denna lista?"
|
||||
|
||||
#~ msgid "Delete this forward?"
|
||||
#~ msgstr "Radera denna vidarebefodring?"
|
||||
|
||||
#~ msgid "Delete this alias?"
|
||||
#~ msgstr "Radera detta alias?"
|
||||
|
||||
@@ -116,3 +116,13 @@ body {
|
||||
.table-fixed {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.menubar-text {
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.label-title {
|
||||
vertical-align: middle;
|
||||
margin: 0 10px;
|
||||
}
|
||||
@@ -233,6 +233,19 @@ function parse_qs(raw) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a specific URL parameter using its name.
|
||||
*
|
||||
* Ref: http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript
|
||||
*/
|
||||
function get_parameter_by_name(url, name) {
|
||||
var name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(url);
|
||||
|
||||
return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the target associated to an event object.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, $.fn.htmltable.defaults, options);
|
||||
this.shift_pressed = false;
|
||||
this.ctrl_pressed = false;
|
||||
this.last_selection = null;
|
||||
this.prev_dir = null;
|
||||
this.listen();
|
||||
@@ -25,29 +26,50 @@
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
|
||||
$(document)
|
||||
.off("click", "tbody>tr")
|
||||
.on("click", "tbody>tr",
|
||||
.off("change", "tbody input[type=checkbox]")
|
||||
.on("change", "tbody input[type=checkbox]",
|
||||
$.proxy(this.toggle_select, this));
|
||||
$(document).on("keydown", "body", $.proxy(this.keydown, this));
|
||||
$(document).on("keyup", "body", $.proxy(this.keyup, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Effective change of a table row's state.
|
||||
*/
|
||||
_toggle_select: function($tr) {
|
||||
this.last_selection = $tr;
|
||||
if ($tr.hasClass(this.options.tr_selected_class)) {
|
||||
$tr.children('td[name=selection]').children('input').prop('checked', false);
|
||||
$tr.removeClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_unselected_event != undefined) {
|
||||
this.options.tr_unselected_event($tr);
|
||||
}
|
||||
} else {
|
||||
$tr.children('td[name=selection]').children('input').prop('checked', true);
|
||||
$tr.addClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_selected_event != undefined) {
|
||||
this.options.tr_selected_event($tr);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the selection state of a given table row.
|
||||
*
|
||||
* Holding the shift key between two selections let users
|
||||
* select all rows between those two locations.
|
||||
*
|
||||
* Holding the ctrl key between two selections let users make
|
||||
* an additive selection.
|
||||
*/
|
||||
toggle_select: function(e) {
|
||||
e.preventDefault();
|
||||
var $tr = $(e.target).parent();
|
||||
var $tr = $(e.target).parents('tr');
|
||||
|
||||
if (this.shift_pressed && this.last_selection) {
|
||||
var start = this.last_selection;
|
||||
|
||||
this.clear_selection();
|
||||
this.last_selection = start;
|
||||
var itfunc = ($tr.index() >= this.last_selection.index())
|
||||
? "next" : "prev";
|
||||
var $curtr = null;
|
||||
@@ -55,7 +77,7 @@
|
||||
if (this.prev_dir && itfunc != this.prev_dir) {
|
||||
$curtr = this.last_selection;
|
||||
} else {
|
||||
$curtr = this.last_selection[itfunc]("tr");
|
||||
$curtr = this.last_selection;
|
||||
}
|
||||
for (; $curtr.length; $curtr = $curtr[itfunc]("tr")) {
|
||||
this._toggle_select($curtr);
|
||||
@@ -65,30 +87,50 @@
|
||||
}
|
||||
this.prev_dir = itfunc;
|
||||
return;
|
||||
} else if (!this.ctrl_pressed && (!this.last_selection || !this.last_selection.is($tr))) {
|
||||
this.clear_selection();
|
||||
}
|
||||
this.last_selection = $tr;
|
||||
this._toggle_select($tr);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if $row is selected or not.
|
||||
*/
|
||||
is_selected: function($row) {
|
||||
return $row.hasClass(this.options.tr_selected_class);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current selection. (jQuery object)
|
||||
*/
|
||||
current_selection: function() {
|
||||
return $("tr[class*=" + this.options.tr_selected_class + "]");
|
||||
},
|
||||
|
||||
/*
|
||||
* Cancel the current selection
|
||||
/**
|
||||
* Cancel the current selection.
|
||||
*/
|
||||
clear_selection: function() {
|
||||
this.last_selection = null;
|
||||
this.current_selection().removeClass(this.options.tr_selected_class);
|
||||
$("tbody input[type=checkbox]").prop('checked', false);
|
||||
if (this.options.tr_unselected_event != undefined) {
|
||||
this.options.tr_unselected_event();
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Manually select a table row
|
||||
/**
|
||||
* Manually select a table row.
|
||||
*/
|
||||
select_row: function($row) {
|
||||
if (!this.last_selection) {
|
||||
this.last_selection = $row;
|
||||
}
|
||||
$row.addClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_selected_event != undefined) {
|
||||
this.options.tr_selected_event($row);
|
||||
}
|
||||
},
|
||||
|
||||
keydown: function(e) {
|
||||
@@ -97,6 +139,9 @@
|
||||
e.preventDefault();
|
||||
this.shift_pressed = true;
|
||||
break;
|
||||
case 17:
|
||||
this.ctrl_pressed = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -106,6 +151,9 @@
|
||||
e.preventDefault();
|
||||
this.shift_pressed = false;
|
||||
break;
|
||||
case 17:
|
||||
this.ctrl_pressed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ Listing.prototype = {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
if (data.page != this.navobj.getparam("page")) {
|
||||
if (data.page && data.page != this.navobj.getparam("page")) {
|
||||
this.navobj.setparam("page", data.page).update(false, true);
|
||||
}
|
||||
var $sortables = $(this.options.sortable_selector);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{% load lib_tags %}
|
||||
<div class="btn-toolbar {{ extraclasses }}">{% for entry in entries %}
|
||||
<div class="btn-group">{% if entry.menu %}
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown">{% if entry.img %}<i class="{{ entry.img}}"></i>{% endif %}{{ entry.label }}<span class="caret"></span></a>
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown">{% if entry.img %}<i class="{{ entry.img}}"></i>{% endif %}{{ entry.label }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">{% for sentry in entry.menu %}
|
||||
<li>{% render_link sentry %}</li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}{% render_link entry "btn" %}{% endif %}
|
||||
</div>
|
||||
{% endfor %}{{ extracontent }}</div>
|
||||
{% endfor %}{% block extracontent %}{% endblock %}
|
||||
</div>
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
django
|
||||
django>=1.5,<1.6
|
||||
django-reversion>=1.8.0
|
||||
dj-database-url
|
||||
south
|
||||
|
||||
Reference in New Issue
Block a user