Merge branch 'master' into feature/score-and-sorting-in-quarantine

Conflicts:
	modoboa/extensions/amavis/views.py
This commit is contained in:
Antoine Nguyen
2013-11-25 12:19:56 +01:00
86 changed files with 1116 additions and 1325 deletions
+10 -10
View File
@@ -26,12 +26,12 @@ frameworks.
Main features
*************
* Administration panel
* `Amavisd-new <http://www.amavis.org>`_ frontend
* Webmail
* Per-user Sieve filters
* Autoreply messages for Postfix
* Graphical statistics about email traffic
* Administration panel
* `Amavisd-new <http://www.amavis.org>`_ frontend
* Webmail
* Per-user Sieve filters
* Autoreply messages for Postfix
* Graphical statistics about email traffic
*************
Documentation
@@ -54,10 +54,10 @@ External code
The following external libraries are provided with Modoboa:
* jQuery version 1.9.1 (http://www.jquery.org/)
* jQuery-UI 1.10+ (http://jqueryui.com/)
* Bootstrap, from Twitter version 2.3.1 (http://twitter.github.com/bootstrap/)
* Bootstrap datepicker (https://bitbucket.org/eternicode/bootstrap-datepicker)
* `jQuery version 1.9.1 <http://www.jquery.org/>`_
* `jQuery-UI 1.10+ <http://jqueryui.com/>`_
* `Bootstrap, from Twitter version 2.3.2 <http://twitter.github.com/bootstrap/>`_
* `Bootstrap datetimepicker <http://www.malot.fr/bootstrap-datetimepicker/index.php>`_
.. |latest-version| image:: https://pypip.in/v/modoboa/badge.png
:alt: Latest version on Pypi
+44 -12
View File
@@ -129,18 +129,6 @@ Raised when an existing account is modified.
* ``newaccount`` is the account after the modification
ExtraAccountActions
===================
Raised when the account list is displayed. Let developers define new
actions to act on a specific user.
*Callback prototype*::
def callback(account): pass
* ``account`` is the account being listed
AdminMenuDisplay
================
@@ -298,6 +286,18 @@ Raised just after an extension has been activated.
* ``extension`` is an ``Extension`` instance
ExtraAccountActions
===================
Raised when the account list is displayed. Let developers define new
actions to act on a specific user.
*Callback prototype*::
def callback(account): pass
* ``account`` is the account being listed
ExtraAccountForm
================
@@ -425,6 +425,25 @@ least three keys::
"label": "<menu label>",
"url": "<menu url>"}
.. _extraformfields:
ExtraFormFields
===============
Raised to request extra fields to include in a django form.
*Callback prototype*::
def callback(form_name, instance=None): pass
* ``form_name`` is a string used to distinguish a specific form
* ``instance`` is a django model instance related to ``form_name``
Must return a list of 2uple, each one containing the following
information::
('field name', <Django form field instance>)
ExtraRelayDomainForm
====================
@@ -841,6 +860,19 @@ Raised when the role of an account is about to be changed.
* ``account`` is the account being modified
* ``role`` is the new role (string)
SaveExtraFormFields
===================
Raised to save extra fields declared using :ref:`extraformfields`.
*Callback prototype*::
def callback(form_name, instance, values): pass
* ``form_name`` is a string used to distinguish a specific form
* ``instance`` is a django model instance related to ``form_name``
* ``values`` is a dictionary containing the form's values
UserCanSetRole
==============
+1 -2
View File
@@ -53,8 +53,7 @@ Finally, follow the common procedure::
1.1.0: FIXME
============
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to
``MODOBOA_APPS``
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to ``MODOBOA_APPS``
Due to code refactoring, some modifications need to be done into
:file:`settings.py`:
+3 -1
View File
@@ -35,7 +35,9 @@ base_events = [
"ExtraUprefsRoutes",
"ExtraUprefsJS",
"GetExtraParameters"
"GetExtraParameters",
"ExtraFormFields",
"SaveExtraFormFields"
]
+1 -1
View File
@@ -202,7 +202,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
tpl = self.cleaned_data["ldap_user_dn_template"]
try:
test = tpl % {"user": "toto"}
except ValueError:
except (KeyError, ValueError):
raise forms.ValidationError(_("Invalid syntax"))
return tpl
+8
View File
@@ -2,6 +2,8 @@
import getpass
import inspect
import os
import sys
import datetime
from django.template import Context, Template
from . import Command
@@ -46,6 +48,12 @@ query = {{ query|safe }}
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
+1 -1
View File
@@ -1 +1 @@
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26T15:19:26+01:00", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26T15:19:26+01:00"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
+10 -2
View File
@@ -304,11 +304,13 @@ class User(PermissionsMixin):
self.post_create(creator)
def from_csv(self, user, row, crypt_password=True):
"""Create a new account from a CSV file entry
"""Create a new account from a CSV file entry.
The expected order is the following::
"account", loginname, password, first name, last name, enabled, group, address[, domain, ...]
"account", loginname, password, first name, last name, enabled, group
Additional fields can be added using the *AccountImported* event.
:param user: a ``core.User`` instance
:param row: a list containing the expected information
@@ -346,6 +348,12 @@ class User(PermissionsMixin):
events.raiseEvent("AccountImported", user, self, row[7:])
def to_csv(self, csvwriter):
"""Export this account.
The CSV format is used to export.
:param csvwriter: csv object
"""
row = ["account", self.username.encode("utf-8"), self.password.encode("utf-8"),
self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
self.is_active, self.group, self.email.encode("utf-8")]
+3 -1
View File
@@ -136,7 +136,9 @@ def tohtml(message):
@register.simple_tag
def visirule(field):
if not hasattr(field.form, "visirules") or not field.html_name in field.form.visirules:
if not hasattr(field, 'form') or \
not hasattr(field.form, "visirules") or \
not field.html_name in field.form.visirules:
return ""
rule = field.form.visirules[field.html_name]
return " data-visibility-field='%s' data-visibility-value='%s' " \
+19 -5
View File
@@ -133,13 +133,25 @@ def grant_access_to_all_objects(user, role):
@events.observe("AccountExported")
def export_admin_domains(admin):
result = [admin.mailbox_set.all()[0].quota] \
if admin.mailbox_set.count() else ['']
if admin.group != "DomainAdmins":
return []
return [dom.name for dom in Domain.objects.get_for_admin(admin)]
return result
return result + [dom.name for dom in Domain.objects.get_for_admin(admin)]
@events.observe("AccountImported")
def import_account_mailbox(user, account, row):
"""Handle extra fields when an account is imported.
Expected fields:
email address; quota; [domain; ...]
:param User user: user importing the account
:param User account: account being imported
:param list rom: list of fields (strings)
"""
account.email = row[0].strip()
if account.email != "":
account.save()
@@ -152,12 +164,14 @@ def import_account_mailbox(user, account, row):
)
if not user.can_access(domain):
raise PermDeniedException
quota = int(row[1].strip())
use_domain_quota = True if not quota else False
mb = Mailbox(address=mailbox, domain=domain,
user=account, use_domain_quota=True)
mb.set_quota(override_rules=user.has_perm("admin.change_domain"))
user=account, use_domain_quota=use_domain_quota)
mb.set_quota(quota, override_rules=user.has_perm("admin.change_domain"))
mb.save(creator=user)
if account.group == "DomainAdmins":
for domname in row[1:]:
for domname in row[2:]:
try:
dom = Domain.objects.get(name=domname.strip())
except Domain.DoesNotExist:
+2
View File
@@ -13,11 +13,13 @@ class MailboxFactory(PermissionFactory):
FACTORY_FOR = models.Mailbox
quota = 10
enabled = True
class AliasFactory(PermissionFactory):
FACTORY_FOR = models.Alias
enabled = True
def populate_database():
"""Create test data.
@@ -1 +0,0 @@
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "is_local": true, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
@@ -1 +0,0 @@
[{"pk": 2, "model": "admin.user", "fields": {"username": "user@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [2]}},{"pk": 3, "model": "admin.user", "fields": {"username": "admin@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [1]}},{"pk": 4, "model": "admin.user", "fields": {"username": "superadmin@test.com", "is_active": "True", "is_superuser": "True", "password": "{PLAIN}toto", "groups": []}},{"pk": 1, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 2, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 3, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 1, "model": "admin.Domain", "fields": {"name" : "test.com", "quota" : "100", "dates": "1"}}, {"pk": 1, "model": "admin.Mailbox", "fields": {"address": "user", "domain": "1", "dates": "1", "quota": "10", "user": "2"}}, {"pk": 2, "model": "admin.Mailbox", "fields": {"address": "admin", "domain": "1", "dates": "2", "quota": "10", "user": "3"}}, {"pk": 3, "model": "admin.Mailbox", "fields": {"address": "superadmin", "domain": "1", "dates": "3", "quota": "10", "user": "4"}}, {"pk": "user@test.com", "model": "admin.Quota", "fields": {"mbox": 1, "username": "user@test.com", "bytes": 0, "messages": 0}}, {"pk": "admin@test.com", "model": "admin.Quota", "fields": {"mbox": 2, "username": "admin@test.com", "bytes": 0, "messages": 0}}, {"pk": "superadmin@test.com", "model": "admin.Quota", "fields": {"mbox": 3, "username": "superadmin@test.com", "bytes": 0, "messages": 0}},{"pk": 2, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 2}},{"pk": 3, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 3}}]
+13 -2
View File
@@ -6,7 +6,7 @@ from modoboa.lib.exceptions import PermDeniedException, Conflict, NotFound
from modoboa.lib.permissions import get_account_roles
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.formutils import (
DomainNameField, DynamicForm, TabForms
DomainNameField, DynamicForm, TabForms, YesNoField
)
from modoboa.core.models import User
from modoboa.extensions.admin.models import (
@@ -132,8 +132,14 @@ class AccountFormMail(forms.Form, DynamicForm):
if "instance" in kwargs:
self.mb = kwargs["instance"]
del kwargs["instance"]
else:
self.mb = None
super(AccountFormMail, self).__init__(*args, **kwargs)
if hasattr(self, "mb") and self.mb is not None:
self.extra_fields = []
for fname, field in events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb):
self.fields[fname] = field
self.extra_fields.append(fname)
if self.mb is not None:
self.fields["email"].required = True
cpt = 1
for alias in self.mb.alias_set.all():
@@ -186,6 +192,7 @@ class AccountFormMail(forms.Form, DynamicForm):
elif account.group == "SimpleUsers" and account.username != self.mb.full_address:
newaddress = account.username
if newaddress is not None:
self.mb.old_full_address = self.mb.full_address
local_part, domname = split_mailbox(newaddress)
try:
domain = Domain.objects.get(name=domname)
@@ -201,6 +208,7 @@ class AccountFormMail(forms.Form, DynamicForm):
else False
self.mb.set_quota(self.cleaned_data["quota"], override_rules)
self.mb.save()
events.raiseEvent('MailboxModified', self.mb)
def save(self, user, account):
if self.cleaned_data["email"] == "":
@@ -213,6 +221,9 @@ class AccountFormMail(forms.Form, DynamicForm):
self.create_mailbox(user, account)
else:
self.update_mailbox(user, account)
events.raiseEvent(
'SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data
)
account.email = self.cleaned_data["email"]
account.save()
@@ -1,4 +1,5 @@
{% load lib_tags %}
{% render_field form.email %}
{% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") %}
{% render_extra_fields form %}
{% render_fields_group form "aliases" %}
+7 -7
View File
@@ -22,7 +22,7 @@ class ExportTestCase(ModoTestCase):
def test_export_identities(self):
response = self.__export_identities()
self.assertEqual(response.content, "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com\r\nalias;alias@test.com;False;user@test.com\r\nforward;forward@test.com;False;user@external.com\r\ndlist;postmaster@test.com;False;toto@titi.com;test@truc.fr\r\n")
self.assertEqual(response.content, "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nforward;forward@test.com;True;user@external.com\r\ndlist;postmaster@test.com;True;toto@titi.com;test@truc.fr\r\n")
def test_export_simpleusers(self):
response = self.__export_identities(
@@ -30,7 +30,7 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com"
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10"
)
def test_export_superadmins(self):
@@ -39,7 +39,7 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;"
"account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;"
)
def test_export_domainadmins(self):
@@ -48,26 +48,26 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;test2.com"
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com"
)
def test_export_aliases(self):
response = self.__export_identities(idtfilter="alias")
self.assertEqual(
response.content.strip(),
"alias;alias@test.com;False;user@test.com"
"alias;alias@test.com;True;user@test.com"
)
def test_export_forwards(self):
response = self.__export_identities(idtfilter="forward")
self.assertEqual(
response.content.strip(),
"forward;forward@test.com;False;user@external.com"
"forward;forward@test.com;True;user@external.com"
)
def test_export_dlists(self):
response = self.__export_identities(idtfilter="dlist")
self.assertEqual(
response.content.strip(),
"dlist;postmaster@test.com;False;toto@titi.com;test@truc.fr"
"dlist;postmaster@test.com;True;toto@titi.com;test@truc.fr"
)
+26 -8
View File
@@ -53,8 +53,8 @@ domainalias;test.alias;test.com;True
def test_identities_import(self):
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; test.com
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 5; test.com
alias; alias1@test.com; True; user1@test.com
forward; fwd1@test.com; True; user@extdomain.com
dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
@@ -65,20 +65,26 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
)
admin = User.objects.get(username="admin")
u1 = User.objects.get(username="user1@test.com")
mb1 = u1.mailbox_set.all()[0]
self.assertTrue(admin.is_owner(u1))
self.assertEqual(u1.email, "user1@test.com")
self.assertEqual(u1.first_name, "User")
self.assertEqual(u1.last_name, "One")
self.assertTrue(u1.is_active)
self.assertEqual(u1.group, "SimpleUsers")
self.assertTrue(admin.is_owner(u1.mailbox_set.all()[0]))
self.assertEqual(u1.mailbox_set.all()[0].full_address, "user1@test.com")
self.assertTrue(mb1.use_domain_quota)
self.assertEqual(mb1.quota, 0)
self.assertTrue(admin.is_owner(mb1))
self.assertEqual(mb1.full_address, "user1@test.com")
self.assertTrue(self.clt.login(username="user1@test.com", password="toto"))
da = User.objects.get(username="truc@test.com")
damb = da.mailbox_set.all()[0]
self.assertEqual(da.first_name, u"René")
self.assertEqual(da.group, "DomainAdmins")
self.assertEqual(da.mailbox_set.all()[0].full_address, "truc@test.com")
self.assertEqual(damb.quota, 5)
self.assertFalse(damb.use_domain_quota)
self.assertEqual(damb.full_address, "truc@test.com")
dom = Domain.objects.get(name="test.com")
self.assertIn(da, dom.admins)
u = User.objects.get(username="user@test.com")
@@ -97,10 +103,22 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
self.assertIn("user@extdomain.com", dlist.extmboxes)
self.assertTrue(admin.is_owner(dlist))
def test_import_invalid_quota(self):
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 20
""", name="identities.csv")
resp = self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn('Quota is greater than the allowed', resp.content)
def test_import_duplicate(self):
f = ContentFile(b"""
account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; test.com
account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; 0; test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0; test.com
""", name="identities.csv")
self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
@@ -119,7 +137,7 @@ account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; te
self.clt.logout()
self.assertTrue(self.clt.login(username="admin@test.com", password="toto"))
f = ContentFile(b"""
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com; 50
""", name="identities.csv")
self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
+1 -1
View File
@@ -152,7 +152,7 @@ def import_identities(request):
helptext = _("""Provide a CSV file where lines respect one of the following formats:
<ul>
<li><em>account; loginname; password; first name; last name; enabled; group; address[, domain, ...]</em></li>
<li><em>account; loginname; password; first name; last name; enabled; group; address; quota; [, domain, ...]</em></li>
<li><em>alias; address; enabled; internal recipient</em></li>
<li><em>forward; address; enabled; external recipient</em></li>
<li><em>dlist; address; enabled; recipient; recipient; ...</em></li>
+14 -4
View File
@@ -1,9 +1,8 @@
# coding: utf-8
import email
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.http import HttpResponseRedirect, Http404
from django.template import Template, Context
from django.utils import simplejson
from django.utils.translation import ugettext as _, ungettext
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators \
@@ -26,8 +25,8 @@ from .models import Msgrcpt
def empty_quarantine(request):
content = "<div class='alert alert-info'>%s</div>" % _("Empty quarantine")
ctx = getctx("ok", level=2, listing=content, paginbar="")
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
ctx = getctx("ok", level=2, listing=content, navbar="")
return render_to_json_response(ctx)
@login_required
@@ -76,12 +75,19 @@ def _listing(request):
return empty_quarantine(request)
content = lst.fetch(request, page.id_start, page.id_stop)
<<<<<<< HEAD
paginbar = pagination_bar(page)
ctx = getctx("ok", listing=content, paginbar=paginbar, page=page.number)
if request.session.get('location', 'listing') != 'listing':
ctx['menu'] = quar_menu()
request.session['location'] = 'listing'
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
=======
navbar = lst.render_navbar(page, "listing/?")
ctx = getctx("ok", listing=content, navbar=navbar,
menu=quar_menu(request.user))
return render_to_json_response(ctx)
>>>>>>> master
@login_required
@@ -149,8 +155,12 @@ def viewmail(request, mail_id):
""").render(Context({"url": reverse(getmailcontent, args=[mail_id])}))
menu = viewm_menu(mail_id, rcpt)
ctx = getctx("ok", menu=menu, listing=content)
<<<<<<< HEAD
request.session['location'] = 'viewmail'
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
=======
return render_to_json_response(ctx)
>>>>>>> master
@login_required
@@ -16,3 +16,39 @@ class ParametersForm(AdminParametersForm):
help_text=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient")
)
default_subject = forms.CharField(
label=ugettext_lazy("Default subject"),
initial=ugettext_lazy("I'm off"),
help_text=ugettext_lazy(
"Default subject used when an auto-reply message is created automatically"
)
)
default_content = forms.CharField(
label=ugettext_lazy("Default content"),
initial=ugettext_lazy(
"""I'm currently off. I'll answer as soon as I come back.
Best regards,
%(name)s
"""),
help_text=ugettext_lazy(
"Default content user when an auto-reply message is created "
"automatically. The '%(name)s' macro will be replaced by the "
"user's full name."
),
widget=forms.widgets.Textarea
)
def clean_default_content(self):
"""Check if the provided value is valid.
Must be a valid format string which will be used with the %
operator.
"""
tpl = self.cleaned_data["default_content"]
try:
test = tpl % {"name": "Antoine Nguyen"}
except (KeyError, ValueError):
raise forms.ValidationError(ugettext_lazy("Invalid syntax"))
return tpl
+41 -7
View File
@@ -2,14 +2,26 @@
import datetime
from django import forms
from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils import timezone
from .models import ARmessage
class ARmessageForm(forms.ModelForm):
untildate = forms.DateField(
fromdate = forms.DateTimeField(
label=ugettext_lazy('From'),
required=False,
help_text=ugettext_lazy("Activate your auto reply from this date"),
widget=forms.TextInput(
attrs={'class': 'datefield', 'readonly': 'readonly'}
)
)
untildate = forms.DateTimeField(
label=ugettext_lazy('Until'),
required=False,
help_text=ugettext_lazy("Activate your auto reply until this date")
help_text=ugettext_lazy("Activate your auto reply until this date"),
widget=forms.TextInput(
attrs={'class': 'datefield', 'readonly': 'readonly'}
)
)
class Meta:
@@ -18,10 +30,32 @@ class ARmessageForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ARmessageForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['subject', 'content', 'untildate', 'enabled']
self.fields.keyOrder = ['subject', 'content', 'fromdate', 'untildate', 'enabled']
if 'instance' in kwargs and kwargs['instance'] is not None:
self.fields['fromdate'].initial = kwargs['instance'].fromdate
self.fields['untildate'].initial = kwargs['instance'].untildate
def clean_untildate(self):
def clean(self):
"""Custom fields validaton.
We want to be sure that fromdate < untildate and that they are
both in the future.
"""
super(ARmessageForm, self).clean()
if self._errors:
raise forms.ValidationError(self._errors)
if self.cleaned_data["fromdate"] is not None:
if self.cleaned_data["fromdate"] < timezone.now():
self._errrors["fromdate"] = self.error_class([_("This date is over")])
del self.cleaned_data['fromdate']
else:
self.cleaned_data['fromdate'] = timezone.now()
if self.cleaned_data["untildate"] is not None:
if self.cleaned_data["untildate"] < datetime.date.today():
raise forms.ValidationError(_("This date is over"))
return self.cleaned_data['untildate']
if self.cleaned_data["untildate"] < timezone.now():
self._errors["untildate"] = self.error_class([_("This date is over")])
del self.cleaned_data['untildate']
elif self.cleaned_data['untildate'] < self.cleaned_data['fromdate']:
self._errors["untildate"] = \
self.error_class([_("Must be greater than start date")])
del self.cleaned_data['untildate']
return self.cleaned_data
@@ -1,14 +1,22 @@
from django.utils import timezone
from django.utils.translation import ugettext_lazy
from modoboa.lib import events, parameters
from modoboa.extensions.postfix_autoreply.models import Transport, Alias
from .models import ARmessage
@events.observe("ExtraUprefsJS")
def extra_js(user):
return ["""function autoreply_cb() {
$('#id_untildate').datepicker({format: 'yyyy-mm-dd', language: '%s'});
$('.datefield').datetimepicker({
format: 'yyyy-mm-dd hh:ii:ss',
language: '%(lang)s',
autoclose: true,
todayHighlight: true,
todayBtn: 'linked'
});
}
""" % parameters.get_user(user, "LANG", app="core")
""" % {'lang': parameters.get_user(user, "LANG", app="core")}
]
@@ -77,11 +85,61 @@ def onMailboxDeleted(mailboxes):
@events.observe("MailboxModified")
def onModifyMailbox(mailbox, oldmailbox):
if oldmailbox.full_address == mailbox.full_address:
def onMailboxModified(mailbox):
if mailbox.full_address == mailbox.old_full_address:
return
alias = Alias.objects.get(full_address=oldmailbox.full_address)
alias = Alias.objects.get(full_address=mailbox.old_full_address)
alias.full_address = mailbox.full_address
alias.autoreply_address = \
"%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name)
alias.save()
@events.observe("ExtraFormFields")
def extra_mailform_fields(form_name, mailbox=None):
"""Define extra fields to include in mail forms.
For now, only the auto-reply state can be modified.
:param str form_name: form name (must be 'mailform')
:param Mailbox mailbox: mailbox
"""
from modoboa.lib.formutils import YesNoField
if form_name != "mailform":
return []
status = False
if mailbox is not None and mailbox.armessage_set.count():
status = mailbox.armessage_set.all()[0].enabled
return [
('autoreply', YesNoField(
label=ugettext_lazy("Enable auto-reply"),
initial="yes" if status else "no",
help_text=ugettext_lazy("Enable or disable Postfix auto-reply")
))
]
@events.observe("SaveExtraFormFields")
def save_extra_mailform_fields(form_name, mailbox, values):
"""Set the auto-reply status for a mailbox.
If a corresponding auto-reply message exists, we update its
status. Otherwise, we create a message using default values.
:param str form_name: form name (must be 'mailform')
:param Mailbox mailbox: mailbox
:param dict values: form values
"""
if form_name != 'mailform':
return
if mailbox.armessage_set.count():
arm = mailbox.armessage_set.all()[0]
else:
arm = ARmessage(mbox=mailbox)
arm.subject = parameters.get_admin("DEFAULT_SUBJECT")
arm.content = parameters.get_admin("DEFAULT_CONTENT") \
% {'name': mailbox.user.fullname}
arm.fromdate = timezone.now()
arm.enabled = True if values['autoreply'] == 'yes' else False
arm.save()
@@ -12,8 +12,10 @@ from modoboa.extensions.postfix_autoreply.models import ARmessage, ARhistoric
def send_autoreply(sender, mailbox, armessage):
if armessage.fromdate < timezone.now():
return
if armessage.untildate is not None \
and armessage.untildate < datetime.date.today():
and armessage.untildate < timezone.now():
armessage.enabled = False
armessage.save()
return
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
from django.utils import timezone
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ARmessage.fromdate'
db.add_column(u'postfix_autoreply_armessage', 'fromdate',
self.gf('django.db.models.fields.DateTimeField')(default=timezone.now),
keep_default=False)
def backwards(self, orm):
# Deleting field 'ARmessage.fromdate'
db.delete_column(u'postfix_autoreply_armessage', 'fromdate')
models = {
'admin.domain': {
'Meta': {'ordering': "['name']", 'object_name': 'Domain'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'quota': ('django.db.models.fields.IntegerField', [], {})
},
'admin.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '252'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'quota': ('django.db.models.fields.PositiveIntegerField', [], {}),
'use_domain_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"})
},
'admin.objectdates': {
'Meta': {'object_name': 'ObjectDates'},
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'core.objectaccess': {
'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"})
},
u'core.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_local': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '254'})
},
u'postfix_autoreply.alias': {
'Meta': {'object_name': 'Alias'},
'autoreply_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'full_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'postfix_autoreply.arhistoric': {
'Meta': {'unique_together': "(('armessage', 'sender'),)", 'object_name': 'ARhistoric'},
'armessage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['postfix_autoreply.ARmessage']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'sender': ('django.db.models.fields.CharField', [], {'max_length': '254'})
},
u'postfix_autoreply.armessage': {
'Meta': {'object_name': 'ARmessage'},
'content': ('django.db.models.fields.TextField', [], {}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'fromdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mbox': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Mailbox']"}),
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'untildate': ('django.db.models.fields.DateTimeField', [], {})
},
u'postfix_autoreply.transport': {
'Meta': {'object_name': 'Transport'},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'method': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['postfix_autoreply']
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'ARmessage.untildate'
db.alter_column(u'postfix_autoreply_armessage', 'untildate', self.gf('django.db.models.fields.DateTimeField')(null=True))
def backwards(self, orm):
# User chose to not deal with backwards NULL issues for 'ARmessage.untildate'
raise RuntimeError("Cannot reverse this migration. 'ARmessage.untildate' and its values cannot be restored.")
models = {
'admin.domain': {
'Meta': {'ordering': "['name']", 'object_name': 'Domain'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'quota': ('django.db.models.fields.IntegerField', [], {})
},
'admin.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '252'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'quota': ('django.db.models.fields.PositiveIntegerField', [], {}),
'use_domain_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"})
},
'admin.objectdates': {
'Meta': {'object_name': 'ObjectDates'},
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'core.objectaccess': {
'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['core.User']"})
},
u'core.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_local': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '254'})
},
u'postfix_autoreply.alias': {
'Meta': {'object_name': 'Alias'},
'autoreply_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'full_address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'postfix_autoreply.arhistoric': {
'Meta': {'unique_together': "(('armessage', 'sender'),)", 'object_name': 'ARhistoric'},
'armessage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['postfix_autoreply.ARmessage']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_sent': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'sender': ('django.db.models.fields.CharField', [], {'max_length': '254'})
},
u'postfix_autoreply.armessage': {
'Meta': {'object_name': 'ARmessage'},
'content': ('django.db.models.fields.TextField', [], {}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'fromdate': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mbox': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Mailbox']"}),
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'untildate': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
},
u'postfix_autoreply.transport': {
'Meta': {'object_name': 'Transport'},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'method': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['postfix_autoreply']
@@ -1,5 +1,6 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from modoboa.extensions.admin.models import Mailbox
@@ -27,7 +28,8 @@ class ARmessage(models.Model):
_('enabled'),
help_text=_("Activate/Deactivate your auto reply")
)
untildate = models.DateTimeField()
fromdate = models.DateTimeField(default=timezone.now)
untildate = models.DateTimeField(null=True, blank=True)
class ARhistoric(models.Model):
@@ -0,0 +1,129 @@
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib.tests import ExtTestCase
from modoboa.extensions.admin import factories
from modoboa.extensions.admin.models import (
Domain
)
from modoboa.extensions.postfix_autoreply.models import (
Transport, Alias, ARmessage
)
class EventsTestCase(ExtTestCase):
fixtures = ['initial_users.json']
def setUp(self):
super(EventsTestCase, self).setUp()
self.activate_extensions('postfix_autoreply')
factories.populate_database()
def test_domain_created_event(self):
values = {
"name": "domain.tld", "quota": 100, "create_dom_admin": "no",
"stepid": 'step2'
}
self.ajax_post(
reverse("modoboa.extensions.admin.views.domain.newdomain"),
values
)
trans = Transport.objects.get(domain='autoreply.domain.tld')
def test_domain_deleted_event(self):
dom = Domain.objects.get(name="test.com")
trans = Transport.objects.get(domain='autoreply.test.com')
self.ajax_post(
reverse("modoboa.extensions.admin.views.domain.deldomain",
args=[dom.id]),
{}
)
with self.assertRaises(Transport.DoesNotExist):
Transport.objects.get(domain='autoreply.test.com')
def test_domain_modified_event(self):
values = {
"name": "test.fr", "quota": 100, "enabled": True
}
dom = Domain.objects.get(name="test.com")
self.ajax_post(
reverse("modoboa.extensions.admin.views.domain.editdomain",
args=[dom.id]),
values
)
trans = Transport.objects.get(domain='autoreply.test.fr')
self.assertEqual(
Alias.objects.filter(full_address__contains='@test.fr').count(), 2
)
for al in Alias.objects.filter(full_address__contains='@test.fr'):
self.assertIn('autoreply.test.fr', al.autoreply_address)
def test_mailbox_created_event(self):
values = {
'username': "tester@test.com", 'first_name': 'Tester', 'last_name': 'Toto',
'password1': 'toto', 'password2': 'toto', 'role': 'SimpleUsers',
'quota_act': True, 'is_active': True, 'email': 'tester@test.com',
'stepid': 'step2', 'autoreply': 'no'
}
self.ajax_post(
reverse("modoboa.extensions.admin.views.identity.newaccount"),
values
)
al = Alias.objects.get(full_address='tester@test.com')
self.assertEqual(al.autoreply_address, 'tester@test.com@autoreply.test.com')
arm = ARmessage.objects.get(mbox__address='tester')
def test_mailbox_deleted_event(self):
account = User.objects.get(username="user@test.com")
self.ajax_post(
reverse("modoboa.extensions.admin.views.identity.delaccount",
args=[account.id]),
{}
)
with self.assertRaises(Alias.DoesNotExist):
Alias.objects.get(full_address='user@test.com')
with self.assertRaises(ARmessage.DoesNotExist):
ARmessage.objects.get(mbox__address='user', mbox__domain__name='test.com')
def test_modify_mailbox_event(self):
values = {
'username': "leon@test.com", 'first_name': 'Tester', 'last_name': 'Toto',
'role': 'SimpleUsers', 'quota_act': True, 'is_active': True,
'email': 'leon@test.com', 'autoreply': 'no'
}
account = User.objects.get(username="user@test.com")
self.ajax_post(
reverse("modoboa.extensions.admin.views.identity.editaccount",
args=[account.id]),
values
)
with self.assertRaises(Alias.DoesNotExist):
Alias.objects.get(full_address='user@test.com')
al = Alias.objects.get(full_address='leon@test.com')
class FormTestCase(ExtTestCase):
fixtures = ['initial_users.json']
def setUp(self):
super(FormTestCase, self).setUp()
self.activate_extensions('postfix_autoreply')
factories.populate_database()
# def test_set_autoreply(self):
# self.clt.get(
# reverse('modoboa.core.views.user.index')
# )
# values = {
# 'subject': 'test', 'content': "I'm off", "enabled": True
# }
# self.ajax_post(
# reverse('modoboa.extensions.postfix_autoreply.views.autoreply'),
# values
# )
# account = User.objects.get(username="user@test.com")
# arm = ARmessage.objects.get(mbox=account.mailbox_set.all()[0])
# self.assertEqual(arm.subject, 'test')
# self.assertTrue(arm.enabled)
# self.assertFalse(arm.untildate)
# self.assertTrue(arm.fromdate)
@@ -26,6 +26,7 @@ def autoreply(request, tplname="postfix_autoreply/autoreply.html"):
form = ARmessageForm(request.POST)
if form.is_valid():
arm = form.save(commit=False)
arm.fromdate = form.cleaned_data["fromdate"]
arm.untildate = form.cleaned_data["untildate"]
arm.mbox = mb
arm.save()
@@ -38,10 +39,6 @@ def autoreply(request, tplname="postfix_autoreply/autoreply.html"):
)
form = ARmessageForm(instance=arm)
if arm is not None:
form.fields['untildate'].initial = arm.untildate
else:
form.fields['untildate'].initial = date.today()
return render_to_json_response({
"content": _render_to_string(request, tplname, {"form": form}),
"onload_cb": "autoreply_cb"
@@ -44,10 +44,11 @@ Stats.prototype = {
if (navobj.params.end) {
$("#id_to").val(navobj.params.end);
}
$("#custom-period input").datepicker({
format: 'yyyy-mm-dd',
$("#custom-period input").datetimepicker({
format: 'yyyy-mm-dd hh:ii:ss',
autoclose: true,
todayHighlight: true,
todayBtn: 'linked',
language: this.options.language
});
$("#searchquery").autocompleter({
+5 -3
View File
@@ -1,4 +1,5 @@
# coding: utf-8
import re
from django.shortcuts import render
from django.utils.translation import ugettext as _
from django.contrib.auth.decorators import (
@@ -70,12 +71,13 @@ def graphs(request):
start = request.GET["start"]
end = request.GET["end"]
G = Grapher()
period_name = "%s_%s" % (start.replace('-', ''), end.replace('-', ''))
expr = re.compile(r'[:\- ]')
period_name = "%s_%s" % (expr.sub('', start), expr.sub('', end))
for tpl in gsets[gset].get_graphs():
tplvars['graphs'].append(tpl.display_name)
G.process(
tplvars["domain"], period_name, str2Time(*start.split('-')),
str2Time(*end.split('-')), tpl
tplvars["domain"], period_name, str2Time(*expr.split(start)),
str2Time(*expr.split(end)), tpl
)
tplvars["period_name"] = period_name
tplvars["start"] = start
+2 -1
View File
@@ -1,8 +1,9 @@
# coding: utf-8
import re
from django.utils.translation import ugettext as _, ugettext_lazy
from django.forms import ChoiceField
from django.forms.widgets import RadioSelect, RadioInput
from django.forms.fields import CharField, Field, ChoiceField
from django.forms.fields import CharField, Field
from django.core.exceptions import ValidationError
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
+6 -3
View File
@@ -58,6 +58,9 @@ class GenericParametersForm(forms.Form):
def _load_initial_values(self):
raise NotImplementedError
def _decode_value(self, value):
return value.decode('unicode_escape').replace('\\r\\n', '\n')
def _load_extra_parameters(self, level):
params = events.raiseDictEvent('GetExtraParameters', self.app, level)
for pname, pdef in params.items():
@@ -85,7 +88,7 @@ class AdminParametersForm(GenericParametersForm):
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in Parameter.objects.filter(name__in=names):
self.fields[p.shortname].initial = p.value
self.fields[p.shortname].initial = self._decode_value(p.value)
def save(self):
from .models import Parameter
@@ -127,7 +130,7 @@ class UserParametersForm(GenericParametersForm):
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in UserParameter.objects.filter(user=self.user, name__in=names):
self.fields[p.shortname].initial = p.value
self.fields[p.shortname].initial = self._decode_value(p.value)
@staticmethod
def has_access(user):
@@ -256,7 +259,7 @@ def get_admin(name, app=None, raise_error=True):
p = Parameter.objects.get(name="%s.%s" % (app, name))
except Parameter.DoesNotExist:
return _params["A"][app]["defaults"][name]
return p.value.decode("unicode_escape")
return p.value.decode("unicode_escape").replace('\\r\\n', '\n')
def get_user(user, name, app=None, raise_error=True):
+10
View File
@@ -94,6 +94,16 @@ def render_fields_group(form, pattern):
))
@register.simple_tag
def render_extra_fields(form):
result = ''
for fname in form.extra_fields:
result += render_to_string("common/generic_field.html", {
'field': form[fname], 'help_display_mode': 'tooltip'
})
return result
@register.simple_tag
def pagination_bar(page):
return render_to_string("common/pagination_bar.html", dict(
+2 -2
View File
@@ -1,7 +1,7 @@
# coding: utf-8
import json
from django.test import TestCase
from django.test.client import Client
from django.utils import simplejson
from django import forms
from django.core.urlresolvers import reverse
from modoboa.lib import parameters
@@ -17,7 +17,7 @@ class ModoTestCase(TestCase):
response = getattr(self.clt, method) \
(url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, status)
return simplejson.loads(response.content)
return json.loads(response.content)
def ajax_post(self, *args, **kwargs):
return self.ajax_request('post', *args, **kwargs)
+3 -3
View File
@@ -5,12 +5,12 @@ This module contains extra functions/shortcuts used to render HTML.
"""
import sys
import re
import json
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response
from django.template.loader import render_to_string
from django import template
from django.utils import simplejson
from django.conf import settings
@@ -86,7 +86,7 @@ def ajax_response(request, status="ok", respmsg=None,
if url is not None:
jsonctx["url"] = url
jsonctx["norefresh"] = norefresh
return HttpResponse(simplejson.dumps(jsonctx), mimetype="application/json")
return HttpResponse(json.dumps(jsonctx), mimetype="application/json")
def render_to_json_response(context, **response_kwargs):
@@ -95,7 +95,7 @@ def render_to_json_response(context, **response_kwargs):
:param dict context: response content
:return: ``HttpResponse`` object
"""
data = simplejson.dumps(context)
data = json.dumps(context)
response_kwargs['content_type'] = 'application/json'
return HttpResponse(data, **response_kwargs)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4 -4
View File
File diff suppressed because one or more lines are too long
-224
View File
@@ -1,224 +0,0 @@
/*!
* Datepicker for Bootstrap
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
.datepicker {
top: 0;
left: 0;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
/*.dow {
border-top: 1px solid #ddd !important;
}*/
}
.datepicker:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
top: -7px;
left: 6px;
}
.datepicker:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #ffffff;
position: absolute;
top: -6px;
left: 7px;
}
.datepicker > div {
display: none;
}
.datepicker.days div.datepicker-days {
display: block;
}
.datepicker.months div.datepicker-months {
display: block;
}
.datepicker.years div.datepicker-years {
display: block;
}
.datepicker table {
width: 100%;
margin: 0;
}
.datepicker td,
.datepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datepicker td.day:hover {
background: #eeeeee;
cursor: pointer;
}
.datepicker td.old,
.datepicker td.new {
color: #999999;
}
.datepicker td.disabled,
.datepicker td.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datepicker td.active,
.datepicker td.active:hover,
.datepicker td.active.disabled,
.datepicker td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:dximagetransform.microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker td.active:hover,
.datepicker td.active:hover:hover,
.datepicker td.active.disabled:hover,
.datepicker td.active.disabled:hover:hover,
.datepicker td.active:active,
.datepicker td.active:hover:active,
.datepicker td.active.disabled:active,
.datepicker td.active.disabled:hover:active,
.datepicker td.active.active,
.datepicker td.active:hover.active,
.datepicker td.active.disabled.active,
.datepicker td.active.disabled:hover.active,
.datepicker td.active.disabled,
.datepicker td.active:hover.disabled,
.datepicker td.active.disabled.disabled,
.datepicker td.active.disabled:hover.disabled,
.datepicker td.active[disabled],
.datepicker td.active:hover[disabled],
.datepicker td.active.disabled[disabled],
.datepicker td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker td.active:active,
.datepicker td.active:hover:active,
.datepicker td.active.disabled:active,
.datepicker td.active.disabled:hover:active,
.datepicker td.active.active,
.datepicker td.active:hover.active,
.datepicker td.active.disabled.active,
.datepicker td.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker td span {
display: block;
width: 47px;
height: 54px;
line-height: 54px;
float: left;
margin: 2px;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datepicker td span:hover {
background: #eeeeee;
}
.datepicker td span.disabled,
.datepicker td span.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datepicker td span.active,
.datepicker td span.active:hover,
.datepicker td span.active.disabled,
.datepicker td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:dximagetransform.microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker td span.active:hover,
.datepicker td span.active:hover:hover,
.datepicker td span.active.disabled:hover,
.datepicker td span.active.disabled:hover:hover,
.datepicker td span.active:active,
.datepicker td span.active:hover:active,
.datepicker td span.active.disabled:active,
.datepicker td span.active.disabled:hover:active,
.datepicker td span.active.active,
.datepicker td span.active:hover.active,
.datepicker td span.active.disabled.active,
.datepicker td span.active.disabled:hover.active,
.datepicker td span.active.disabled,
.datepicker td span.active:hover.disabled,
.datepicker td span.active.disabled.disabled,
.datepicker td span.active.disabled:hover.disabled,
.datepicker td span.active[disabled],
.datepicker td span.active:hover[disabled],
.datepicker td span.active.disabled[disabled],
.datepicker td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datepicker td span.active:active,
.datepicker td span.active:hover:active,
.datepicker td span.active.disabled:active,
.datepicker td span.active.disabled:hover:active,
.datepicker td span.active.active,
.datepicker td span.active:hover.active,
.datepicker td span.active.disabled.active,
.datepicker td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker td span.old {
color: #999999;
}
.datepicker th.switch {
width: 145px;
}
.datepicker thead tr:first-child th {
cursor: pointer;
}
.datepicker thead tr:first-child th:hover {
background: #eeeeee;
}
.input-append.date .add-on i,
.input-prepend.date .add-on i {
display: block;
cursor: pointer;
width: 16px;
height: 16px;
}
-884
View File
@@ -1,884 +0,0 @@
/* =========================================================
* bootstrap-datepicker.js
* http://www.eyecon.ro/bootstrap-datepicker
* =========================================================
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
!function( $ ) {
function UTCDate(){
return new Date(Date.UTC.apply(Date, arguments));
}
function UTCToday(){
var today = new Date();
return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
}
// Picker object
var Datepicker = function(element, options) {
var that = this;
this.element = $(element);
this.language = options.language||this.element.data('date-language')||"en";
this.language = this.language in dates ? this.language : "en";
this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
this.picker = $(DPGlobal.template)
.appendTo('body')
.on({
click: $.proxy(this.click, this)
});
this.isInput = this.element.is('input');
this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
this.hasInput = this.component && this.element.find('input').length;
if(this.component && this.component.length === 0)
this.component = false;
this._attachEvents();
this.forceParse = true;
if ('forceParse' in options) {
this.forceParse = options.forceParse;
} else if ('dateForceParse' in this.element.data()) {
this.forceParse = this.element.data('date-force-parse');
}
$(document).on('mousedown', function (e) {
// Clicked outside the datepicker, hide it
if ($(e.target).closest('.datepicker').length == 0) {
that.hide();
}
});
this.autoclose = false;
if ('autoclose' in options) {
this.autoclose = options.autoclose;
} else if ('dateAutoclose' in this.element.data()) {
this.autoclose = this.element.data('date-autoclose');
}
this.keyboardNavigation = true;
if ('keyboardNavigation' in options) {
this.keyboardNavigation = options.keyboardNavigation;
} else if ('dateKeyboardNavigation' in this.element.data()) {
this.keyboardNavigation = this.element.data('date-keyboard-navigation');
}
switch(options.startView || this.element.data('date-start-view')){
case 2:
case 'decade':
this.viewMode = this.startViewMode = 2;
break;
case 1:
case 'year':
this.viewMode = this.startViewMode = 1;
break;
case 0:
case 'month':
default:
this.viewMode = this.startViewMode = 0;
break;
}
this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
this.weekEnd = ((this.weekStart + 6) % 7);
this.startDate = -Infinity;
this.endDate = Infinity;
this.setStartDate(options.startDate||this.element.data('date-startdate'));
this.setEndDate(options.endDate||this.element.data('date-enddate'));
this.fillDow();
this.fillMonths();
this.update();
this.showMode();
};
Datepicker.prototype = {
constructor: Datepicker,
_events: [],
_attachEvents: function(){
this._detachEvents();
if (this.isInput) {
this._events = [
[this.element, {
focus: $.proxy(this.show, this),
keyup: $.proxy(this.update, this),
keydown: $.proxy(this.keydown, this)
}]
];
}
else if (this.component && this.hasInput){
this._events = [
// For components that are not readonly, allow keyboard nav
[this.element.find('input'), {
focus: $.proxy(this.show, this),
keyup: $.proxy(this.update, this),
keydown: $.proxy(this.keydown, this),
}],
[this.component, {
click: $.proxy(this.show, this)
}]
];
}
else {
this._events = [
[this.element, {
click: $.proxy(this.show, this)
}]
];
}
for (var i=0, el, ev; i<this._events.length; i++){
el = this._events[i][0];
ev = this._events[i][1];
el.on(ev);
}
},
_detachEvents: function(){
for (var i=0, el, ev; i<this._events.length; i++){
el = this._events[i][0];
ev = this._events[i][1];
el.off(ev);
}
this._events = [];
},
show: function(e) {
this.picker.show();
this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
this.update();
this.place();
$(window).on('resize', $.proxy(this.place, this));
if (e ) {
e.stopPropagation();
e.preventDefault();
}
this.element.trigger({
type: 'show',
date: this.date
});
},
hide: function(e){
this.picker.hide();
$(window).off('resize', this.place);
this.viewMode = this.startViewMode;
this.showMode();
if (!this.isInput) {
$(document).off('mousedown', this.hide);
}
if (
this.forceParse &&
(
this.isInput && this.element.val() ||
this.hasInput && this.element.find('input').val()
)
)
this.setValue();
this.element.trigger({
type: 'hide',
date: this.date
});
},
remove: function() {
this._detachEvents();
this.picker.remove();
delete this.element.data().datepicker;
},
getDate: function() {
var d = this.getUTCDate();
return new Date(d.getTime() + (d.getTimezoneOffset()*60000))
},
getUTCDate: function() {
return this.date;
},
setDate: function(d) {
this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
},
setUTCDate: function(d) {
this.date = d;
this.setValue();
},
setValue: function() {
var formatted = DPGlobal.formatDate(this.date, this.format, this.language);
if (!this.isInput) {
if (this.component){
this.element.find('input').prop('value', formatted);
}
this.element.data('date', formatted);
} else {
this.element.prop('value', formatted);
}
},
setStartDate: function(startDate){
this.startDate = startDate||-Infinity;
if (this.startDate !== -Infinity) {
this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
}
this.update();
this.updateNavArrows();
},
setEndDate: function(endDate){
this.endDate = endDate||Infinity;
if (this.endDate !== Infinity) {
this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
}
this.update();
this.updateNavArrows();
},
place: function(){
var zIndex = parseInt(this.element.parents().filter(function() {
return $(this).css('z-index') != 'auto';
}).first().css('z-index'))+10;
var offset = this.component ? this.component.offset() : this.element.offset();
this.picker.css({
top: offset.top + this.height,
left: offset.left,
zIndex: zIndex
});
},
update: function(){
this.date = DPGlobal.parseDate(
this.isInput ? this.element.prop('value') : this.element.data('date') || this.element.find('input').prop('value'),
this.format, this.language
);
if (this.date < this.startDate) {
this.viewDate = new Date(this.startDate);
} else if (this.date > this.endDate) {
this.viewDate = new Date(this.endDate);
} else {
this.viewDate = new Date(this.date);
}
this.fill();
},
fillDow: function(){
var dowCnt = this.weekStart;
var html = '<tr>';
while (dowCnt < this.weekStart + 7) {
html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
}
html += '</tr>';
this.picker.find('.datepicker-days thead').append(html);
},
fillMonths: function(){
var html = '';
var i = 0
while (i < 12) {
html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
}
this.picker.find('.datepicker-months td').html(html);
},
fill: function() {
var d = new Date(this.viewDate),
year = d.getUTCFullYear(),
month = d.getUTCMonth(),
startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
currentDate = this.date.valueOf(),
today = new Date();
this.picker.find('.datepicker-days thead th:eq(1)')
.text(dates[this.language].months[month]+' '+year);
this.picker.find('tfoot th.today')
.text(dates[this.language].today)
.toggle(this.todayBtn);
this.updateNavArrows();
this.fillMonths();
var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
prevMonth.setUTCDate(day);
prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
var nextMonth = new Date(prevMonth);
nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
nextMonth = nextMonth.valueOf();
var html = [];
var clsName;
while(prevMonth.valueOf() < nextMonth) {
if (prevMonth.getUTCDay() == this.weekStart) {
html.push('<tr>');
}
clsName = '';
if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
clsName += ' old';
} else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
clsName += ' new';
}
// Compare internal UTC date with local today, not UTC today
if (this.todayHighlight &&
prevMonth.getUTCFullYear() == today.getFullYear() &&
prevMonth.getUTCMonth() == today.getMonth() &&
prevMonth.getUTCDate() == today.getDate()) {
clsName += ' today';
}
if (prevMonth.valueOf() == currentDate) {
clsName += ' active';
}
if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
clsName += ' disabled';
}
html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
if (prevMonth.getUTCDay() == this.weekEnd) {
html.push('</tr>');
}
prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
}
this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
var currentYear = this.date.getUTCFullYear();
var months = this.picker.find('.datepicker-months')
.find('th:eq(1)')
.text(year)
.end()
.find('span').removeClass('active');
if (currentYear == year) {
months.eq(this.date.getUTCMonth()).addClass('active');
}
if (year < startYear || year > endYear) {
months.addClass('disabled');
}
if (year == startYear) {
months.slice(0, startMonth).addClass('disabled');
}
if (year == endYear) {
months.slice(endMonth+1).addClass('disabled');
}
html = '';
year = parseInt(year/10, 10) * 10;
var yearCont = this.picker.find('.datepicker-years')
.find('th:eq(1)')
.text(year + '-' + (year + 9))
.end()
.find('td');
year -= 1;
for (var i = -1; i < 11; i++) {
html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
year += 1;
}
yearCont.html(html);
},
updateNavArrows: function() {
var d = new Date(this.viewDate),
year = d.getUTCFullYear(),
month = d.getUTCMonth();
switch (this.viewMode) {
case 0:
if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
this.picker.find('.prev').css({visibility: 'hidden'});
} else {
this.picker.find('.prev').css({visibility: 'visible'});
}
if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
this.picker.find('.next').css({visibility: 'hidden'});
} else {
this.picker.find('.next').css({visibility: 'visible'});
}
break;
case 1:
case 2:
if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
this.picker.find('.prev').css({visibility: 'hidden'});
} else {
this.picker.find('.prev').css({visibility: 'visible'});
}
if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
this.picker.find('.next').css({visibility: 'hidden'});
} else {
this.picker.find('.next').css({visibility: 'visible'});
}
break;
}
},
click: function(e) {
e.stopPropagation();
e.preventDefault();
var target = $(e.target).closest('span, td, th');
if (target.length == 1) {
switch(target[0].nodeName.toLowerCase()) {
case 'th':
switch(target[0].className) {
case 'switch':
this.showMode(1);
break;
case 'prev':
case 'next':
var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
switch(this.viewMode){
case 0:
this.viewDate = this.moveMonth(this.viewDate, dir);
break;
case 1:
case 2:
this.viewDate = this.moveYear(this.viewDate, dir);
break;
}
this.fill();
break;
case 'today':
var date = new Date();
date.setUTCHours(0);
date.setUTCMinutes(0);
date.setUTCSeconds(0);
date.setUTCMilliseconds(0);
this.showMode(-2);
var which = this.todayBtn == 'linked' ? null : 'view';
this._setDate(date, which);
break;
}
break;
case 'span':
if (!target.is('.disabled')) {
this.viewDate.setUTCDate(1);
if (target.is('.month')) {
var month = target.parent().find('span').index(target);
this.viewDate.setUTCMonth(month);
this.element.trigger({
type: 'changeMonth',
date: this.viewDate
});
} else {
var year = parseInt(target.text(), 10)||0;
this.viewDate.setUTCFullYear(year);
this.element.trigger({
type: 'changeYear',
date: this.viewDate
});
}
this.showMode(-1);
this.fill();
}
break;
case 'td':
if (target.is('.day') && !target.is('.disabled')){
var day = parseInt(target.text(), 10)||1;
var year = this.viewDate.getUTCFullYear(),
month = this.viewDate.getUTCMonth();
if (target.is('.old')) {
if (month == 0) {
month = 11;
year -= 1;
} else {
month -= 1;
}
} else if (target.is('.new')) {
if (month == 11) {
month = 0;
year += 1;
} else {
month += 1;
}
}
this._setDate(UTCDate(year, month, day,0,0,0,0));
}
break;
}
}
},
_setDate: function(date, which){
if (!which || which == 'date')
this.date = date;
if (!which || which == 'view')
this.viewDate = date;
this.fill();
this.setValue();
this.element.trigger({
type: 'changeDate',
date: this.date
});
var element;
if (this.isInput) {
element = this.element;
} else if (this.component){
element = this.element.find('input');
}
if (element) {
element.change();
if (this.autoclose) {
this.hide();
}
}
},
moveMonth: function(date, dir){
if (!dir) return date;
var new_date = new Date(date.valueOf()),
day = new_date.getUTCDate(),
month = new_date.getUTCMonth(),
mag = Math.abs(dir),
new_month, test;
dir = dir > 0 ? 1 : -1;
if (mag == 1){
test = dir == -1
// If going back one month, make sure month is not current month
// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
? function(){ return new_date.getUTCMonth() == month; }
// If going forward one month, make sure month is as expected
// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
: function(){ return new_date.getUTCMonth() != new_month; };
new_month = month + dir;
new_date.setUTCMonth(new_month);
// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
if (new_month < 0 || new_month > 11)
new_month = (new_month + 12) % 12;
} else {
// For magnitudes >1, move one month at a time...
for (var i=0; i<mag; i++)
// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
new_date = this.moveMonth(new_date, dir);
// ...then reset the day, keeping it in the new month
new_month = new_date.getUTCMonth();
new_date.setUTCDate(day);
test = function(){ return new_month != new_date.getUTCMonth(); };
}
// Common date-resetting loop -- if date is beyond end of month, make it
// end of month
while (test()){
new_date.setUTCDate(--day);
new_date.setUTCMonth(new_month);
}
return new_date;
},
moveYear: function(date, dir){
return this.moveMonth(date, dir*12);
},
dateWithinRange: function(date){
return date >= this.startDate && date <= this.endDate;
},
keydown: function(e){
if (this.picker.is(':not(:visible)')){
if (e.keyCode == 27) // allow escape to hide and re-show picker
this.show();
return;
}
var dateChanged = false,
dir, day, month,
newDate, newViewDate;
switch(e.keyCode){
case 27: // escape
this.hide();
e.preventDefault();
break;
case 37: // left
case 39: // right
if (!this.keyboardNavigation) break;
dir = e.keyCode == 37 ? -1 : 1;
if (e.ctrlKey){
newDate = this.moveYear(this.date, dir);
newViewDate = this.moveYear(this.viewDate, dir);
} else if (e.shiftKey){
newDate = this.moveMonth(this.date, dir);
newViewDate = this.moveMonth(this.viewDate, dir);
} else {
newDate = new Date(this.date);
newDate.setUTCDate(this.date.getUTCDate() + dir);
newViewDate = new Date(this.viewDate);
newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
}
if (this.dateWithinRange(newDate)){
this.date = newDate;
this.viewDate = newViewDate;
this.setValue();
this.update();
e.preventDefault();
dateChanged = true;
}
break;
case 38: // up
case 40: // down
if (!this.keyboardNavigation) break;
dir = e.keyCode == 38 ? -1 : 1;
if (e.ctrlKey){
newDate = this.moveYear(this.date, dir);
newViewDate = this.moveYear(this.viewDate, dir);
} else if (e.shiftKey){
newDate = this.moveMonth(this.date, dir);
newViewDate = this.moveMonth(this.viewDate, dir);
} else {
newDate = new Date(this.date);
newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
newViewDate = new Date(this.viewDate);
newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
}
if (this.dateWithinRange(newDate)){
this.date = newDate;
this.viewDate = newViewDate;
this.setValue();
this.update();
e.preventDefault();
dateChanged = true;
}
break;
case 13: // enter
this.hide();
e.preventDefault();
break;
case 9: // tab
this.hide();
break;
}
if (dateChanged){
this.element.trigger({
type: 'changeDate',
date: this.date
});
var element;
if (this.isInput) {
element = this.element;
} else if (this.component){
element = this.element.find('input');
}
if (element) {
element.change();
}
}
},
showMode: function(dir) {
if (dir) {
this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
}
this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
this.updateNavArrows();
}
};
$.fn.datepicker = function ( option ) {
var args = Array.apply(null, arguments);
args.shift();
return this.each(function () {
var $this = $(this),
data = $this.data('datepicker'),
options = typeof option == 'object' && option;
if (!data) {
$this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
}
if (typeof option == 'string' && typeof data[option] == 'function') {
data[option].apply(data, args);
}
});
};
$.fn.datepicker.defaults = {
};
$.fn.datepicker.Constructor = Datepicker;
var dates = $.fn.datepicker.dates = {
en: {
days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
today: "Today"
}
}
var DPGlobal = {
modes: [
{
clsName: 'days',
navFnc: 'Month',
navStep: 1
},
{
clsName: 'months',
navFnc: 'FullYear',
navStep: 1
},
{
clsName: 'years',
navFnc: 'FullYear',
navStep: 10
}],
isLeapYear: function (year) {
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
},
getDaysInMonth: function (year, month) {
return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
},
validParts: /dd?|mm?|MM?|yy(?:yy)?/g,
nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g,
parseFormat: function(format){
// IE treats \0 as a string end in inputs (truncating the value),
// so it's a bad format delimiter, anyway
var separators = format.replace(this.validParts, '\0').split('\0'),
parts = format.match(this.validParts);
if (!separators || !separators.length || !parts || parts.length == 0){
throw new Error("Invalid date format.");
}
return {separators: separators, parts: parts};
},
parseDate: function(date, format, language) {
if (date instanceof Date) return date;
if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) {
var part_re = /([-+]\d+)([dmwy])/,
parts = date.match(/([-+]\d+)([dmwy])/g),
part, dir;
date = new Date();
for (var i=0; i<parts.length; i++) {
part = part_re.exec(parts[i]);
dir = parseInt(part[1]);
switch(part[2]){
case 'd':
date.setUTCDate(date.getUTCDate() + dir);
break;
case 'm':
date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
break;
case 'w':
date.setUTCDate(date.getUTCDate() + dir * 7);
break;
case 'y':
date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
break;
}
}
return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
}
var parts = date && date.match(this.nonpunctuation) || [],
date = new Date(),
parsed = {},
setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
setters_map = {
yyyy: function(d,v){ return d.setUTCFullYear(v); },
yy: function(d,v){ return d.setUTCFullYear(2000+v); },
m: function(d,v){
v -= 1;
while (v<0) v += 12;
v %= 12;
d.setUTCMonth(v);
while (d.getUTCMonth() != v)
d.setUTCDate(d.getUTCDate()-1);
return d;
},
d: function(d,v){ return d.setUTCDate(v); }
},
val, filtered, part;
setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
setters_map['dd'] = setters_map['d'];
date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
if (parts.length == format.parts.length) {
for (var i=0, cnt = format.parts.length; i < cnt; i++) {
val = parseInt(parts[i], 10);
part = format.parts[i];
if (isNaN(val)) {
switch(part) {
case 'MM':
filtered = $(dates[language].months).filter(function(){
var m = this.slice(0, parts[i].length),
p = parts[i].slice(0, m.length);
return m == p;
});
val = $.inArray(filtered[0], dates[language].months) + 1;
break;
case 'M':
filtered = $(dates[language].monthsShort).filter(function(){
var m = this.slice(0, parts[i].length),
p = parts[i].slice(0, m.length);
return m == p;
});
val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
break;
}
}
parsed[part] = val;
}
for (var i=0, s; i<setters_order.length; i++){
s = setters_order[i];
if (s in parsed && !isNaN(parsed[s]))
setters_map[s](date, parsed[s])
}
}
return date;
},
formatDate: function(date, format, language){
var val = {
d: date.getUTCDate(),
m: date.getUTCMonth() + 1,
M: dates[language].monthsShort[date.getUTCMonth()],
MM: dates[language].months[date.getUTCMonth()],
yy: date.getUTCFullYear().toString().substring(2),
yyyy: date.getUTCFullYear()
};
val.dd = (val.d < 10 ? '0' : '') + val.d;
val.mm = (val.m < 10 ? '0' : '') + val.m;
var date = [],
seps = $.extend([], format.separators);
for (var i=0, cnt = format.parts.length; i < cnt; i++) {
if (seps.length)
date.push(seps.shift())
date.push(val[format.parts[i]]);
}
return date.join('');
},
headTemplate: '<thead>'+
'<tr>'+
'<th class="prev"><i class="icon-arrow-left"/></th>'+
'<th colspan="5" class="switch"></th>'+
'<th class="next"><i class="icon-arrow-right"/></th>'+
'</tr>'+
'</thead>',
contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
};
DPGlobal.template = '<div class="datepicker dropdown-menu">'+
'<div class="datepicker-days">'+
'<table class=" table-condensed">'+
DPGlobal.headTemplate+
'<tbody></tbody>'+
DPGlobal.footTemplate+
'</table>'+
'</div>'+
'<div class="datepicker-months">'+
'<table class="table-condensed">'+
DPGlobal.headTemplate+
DPGlobal.contTemplate+
DPGlobal.footTemplate+
'</table>'+
'</div>'+
'<div class="datepicker-years">'+
'<table class="table-condensed">'+
DPGlobal.headTemplate+
DPGlobal.contTemplate+
DPGlobal.footTemplate+
'</table>'+
'</div>'+
'</div>';
}( window.jQuery );
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
-13
View File
@@ -1,13 +0,0 @@
/**
* Brazilian translation for bootstrap-datepicker
* Cauan Cabral <cauan@radig.com.br>
*/
;(function($){
$.fn.datepicker.dates['br'] = {
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
};
}(jQuery));
-14
View File
@@ -1,14 +0,0 @@
/**
* Polish translation for bootstrap-datepicker
* Robert <rtpm@gazeta.pl>
*/
;(function($){
$.fn.datepicker.dates['pl'] = {
days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"],
daysShort: ["Nie", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nie"],
daysMin: ["N", "Pn", "Wt", "Śr", "Cz", "Pt", "So", "N"],
months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
monthsShort: ["Sty", "Lu", "Mar", "Kw", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa", "Lis", "Gru"],
today: "Dzisiaj"
};
}(jQuery));
-13
View File
@@ -1,13 +0,0 @@
/**
* Traditional Chinese translation for bootstrap-datepicker
* Rung-Sheng Jang <daniel@i-trend.co.cc>
*/
;(function($){
$.fn.datepicker.dates['zh-TW'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Bulgarian translation for bootstrap-datepicker
* Bulgarian translation for bootstrap-datetimepicker
* Apostol Apostolov <apostol.s.apostolov@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['bg'] = {
$.fn.datetimepicker.dates['bg'] = {
days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб", "Нед"],
daysMin: ["Н", "П", "В", "С", "Ч", "П", "С", "Н"],
months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
today: "днес"
today: "днес",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Catalan translation for bootstrap-datetimepicker
* J. Garcia <jogaco.en@gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates['ca'] = {
days: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"],
daysShort: ["Diu", "Dil", "Dmt", "Dmc", "Dij", "Div", "Dis", "Diu"],
daysMin: ["dg", "dl", "dt", "dc", "dj", "dv", "ds", "dg"],
months: ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"],
monthsShort: ["Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"],
today: "Avui",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,17 @@
/**
* Czech translation for bootstrap-datepicker
* Czech translation for bootstrap-datetimepicker
* Matěj Koubík <matej@koubik.name>
* Fixes by Michal Remiš <michal.remis@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['cs'] = {
$.fn.datetimepicker.dates['cs'] = {
days: ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"],
daysShort: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"],
daysMin: ["N", "P", "Ú", "St", "Č", "P", "So", "N"],
daysShort: ["Ned", "Pon", "Úte", "Stř", "Čtv", "Pát", "Sob", "Ned"],
daysMin: ["Ne", "Po", "Út", "St", "Čt", "Pá", "So", "Ne"],
months: ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"],
monthsShort: ["Led", "Úno", "Bře", "Dub", "Kvě", "Čer", "Čnc", "Srp", "Zář", "Říj", "Lis", "Pro"],
today: "Dnes"
today: "Dnes",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Danish translation for bootstrap-datepicker
* Danish translation for bootstrap-datetimepicker
* Christian Pedersen <http://github.com/chripede>
*/
;(function($){
$.fn.datepicker.dates['da'] = {
$.fn.datetimepicker.dates['da'] = {
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
months: ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "I Dag"
today: "I Dag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,18 @@
/**
* German translation for bootstrap-datepicker
* German translation for bootstrap-datetimepicker
* Sam Zurcher <sam@orelias.ch>
*/
;(function($){
$.fn.datepicker.dates['de'] = {
$.fn.datetimepicker.dates['de'] = {
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"],
daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
today: "Heute"
today: "Heute",
suffix: [],
meridiem: [],
weekStart: 1,
format: "dd.mm.yyyy"
};
}(jQuery));
@@ -0,0 +1,15 @@
/**
* Greek translation for bootstrap-datetimepicker
*/
;(function($){
$.fn.datetimepicker.dates['el'] = {
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"],
daysShort: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ"],
daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σα", "Κυ"],
months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ"],
today: "Σήμερα",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Spanish translation for bootstrap-datepicker
* Spanish translation for bootstrap-datetimepicker
* Bruno Bonamin <bruno.bonamin@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['es'] = {
$.fn.datetimepicker.dates['es'] = {
days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"],
daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"],
daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"],
months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
today: "Hoy"
today: "Hoy",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Finnish translation for bootstrap-datepicker
* Finnish translation for bootstrap-datetimepicker
* Jaakko Salonen <https://github.com/jsalonen>
*/
;(function($){
$.fn.datepicker.dates['fi'] = {
$.fn.datetimepicker.dates['fi'] = {
days: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai"],
daysShort: ["sun", "maa", "tii", "kes", "tor", "per", "lau", "sun"],
daysMin: ["su", "ma", "ti", "ke", "to", "pe", "la", "su"],
months: ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"],
monthsShort: ["tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mar", "jou"],
today: "tänään"
today: "tänään",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,18 @@
/**
* French translation for bootstrap-datepicker
* French translation for bootstrap-datetimepicker
* Nico Mollet <nico.mollet@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['fr'] = {
$.fn.datetimepicker.dates['fr'] = {
days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"],
daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"],
daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"],
months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"],
today: "Aujourd'hui"
today: "Aujourd'hui",
suffix: [],
meridiem: ["am", "pm"],
weekStart: 1,
format: "dd/mm/yyyy"
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Hebrew translation for bootstrap-datetimepicker
* Sagie Maoz <sagie@maoz.info>
*/
;(function($){
$.fn.datetimepicker.dates['he'] = {
days: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"],
daysShort: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
daysMin: ["א", "ב", "ג", "ד", "ה", "ו", "ש", "א"],
months: ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"],
monthsShort: ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"],
today: "היום",
suffix: [],
meridiem: [],
rtl: true
};
}(jQuery));
@@ -0,0 +1,15 @@
/**
* Croatian localisation
*/
;(function($){
$.fn.datetimepicker.dates['hr'] = {
days: ["Nedjelja", "Ponedjelja", "Utorak", "Srijeda", "Četrtak", "Petak", "Subota", "Nedjelja"],
daysShort: ["Ned", "Pon", "Uto", "Srr", "Čet", "Pet", "Sub", "Ned"],
daysMin: ["Ne", "Po", "Ut", "Sr", "Če", "Pe", "Su", "Ne"],
months: ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"],
monthsShort: ["Sije", "Velj", "Ožu", "Tra", "Svi", "Lip", "Jul", "Kol", "Ruj", "Lis", "Stu", "Pro"],
today: "Danas",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Hungarian translation for bootstrap-datetimepicker
* darevish <http://github.com/darevish>
*/
;(function($){
$.fn.datetimepicker.dates['hu'] = {
days: ["Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat", "Vasárnap"],
daysShort: ["Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo", "Vas"],
daysMin: ["V", "H", "K", "Sze", "Cs", "P", "Szo", "V"],
months: ["Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"],
monthsShort: ["Jan", "Feb", "Már", "Ápr", "Máj", "Jún", "Júl", "Aug", "Sze", "Okt", "Nov", "Dec"],
today: "Ma",
suffix: [],
meridiem: [],
weekStart: 1
};
}(jQuery));
@@ -1,13 +1,15 @@
/**
* Bahasa translation for bootstrap-datepicker
* Bahasa translation for bootstrap-datetimepicker
* Azwar Akbar <azwar.akbar@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['id'] = {
$.fn.datetimepicker.dates['id'] = {
days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"],
daysShort: ["Mgu", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Mgu"],
daysMin: ["Mg", "Sn", "Sl", "Ra", "Ka", "Ju", "Sa", "Mg"],
months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"]
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"],
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Icelandic translation for bootstrap-datepicker
* Icelandic translation for bootstrap-datetimepicker
* Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['is'] = {
$.fn.datetimepicker.dates['is'] = {
days: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur", "Sunnudagur"],
daysShort: ["Sun", "Mán", "Þri", "Mið", "Fim", "Fös", "Lau", "Sun"],
daysMin: ["Su", "Má", "Þr", "Mi", "Fi", "Fö", "La", "Su"],
months: ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maí", "Jún", "Júl", "Ágú", "Sep", "Okt", "Nóv", "Des"],
today: "Í Dag"
today: "Í Dag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,18 @@
/**
* Italian translation for bootstrap-datepicker
* Italian translation for bootstrap-datetimepicker
* Enrico Rubboli <rubboli@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['it'] = {
$.fn.datetimepicker.dates['it'] = {
days: ["Domenica", "Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato", "Domenica"],
daysShort: ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"],
daysMin: ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"],
months: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"],
monthsShort: ["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"],
today: "Oggi"
today: "Oggi",
suffix: [],
meridiem: [],
weekStart: 1,
format: "dd.mm.yyyy"
};
}(jQuery));
@@ -1,13 +1,16 @@
/**
* Japanese translation for bootstrap-datepicker
* Japanese translation for bootstrap-datetimepicker
* Norio Suzuki <https://github.com/suzuki/>
*/
;(function($){
$.fn.datepicker.dates['ja'] = {
$.fn.datetimepicker.dates['ja'] = {
days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜", "日曜"],
daysShort: ["日", "月", "火", "水", "木", "金", "土", "日"],
daysMin: ["日", "月", "火", "水", "木", "金", "土", "日"],
months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
today: "今日",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,13 +1,15 @@
/**
* Korean translation for bootstrap-datepicker
* Korean translation for bootstrap-datetimepicker
* Gu Youn <http://github.com/guyoun>
*/
;(function($){
$.fn.datepicker.dates['kr'] = {
$.fn.datetimepicker.dates['kr'] = {
days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"],
daysShort: ["일", "월", "화", "수", "목", "금", "토", "일"],
daysMin: ["일", "월", "화", "수", "목", "금", "토", "일"],
months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,15 +1,18 @@
/**
* Lithuanian translation for bootstrap-datepicker
* Lithuanian translation for bootstrap-datetimepicker
* Šarūnas Gliebus <ssharunas@yahoo.co.uk>
*/
;(function($){
$.fn.datepicker.dates['lt'] = {
$.fn.datetimepicker.dates['lt'] = {
days: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis", "Sekmadienis"],
daysShort: ["S", "Pr", "A", "T", "K", "Pn", "Š", "S"],
daysMin: ["Sk", "Pr", "An", "Tr", "Ke", "Pn", "Št", "Sk"],
months: ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"],
monthsShort: ["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gru"],
today: "Šiandien",
suffix: [],
meridiem: [],
weekStart: 1
};
}(jQuery));
@@ -1,16 +1,18 @@
/**
* Latvian translation for bootstrap-datepicker
* Latvian translation for bootstrap-datetimepicker
* Artis Avotins <artis@apit.lv>
*/
;(function($){
$.fn.datepicker.dates['lv'] = {
$.fn.datetimepicker.dates['lv'] = {
days: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena", "Svētdiena"],
daysShort: ["Sv", "P", "O", "T", "C", "Pk", "S", "Sv"],
daysMin: ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "St", "Sv"],
months: ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jūn", "Jūl", "Aug", "Sep", "Okt", "Nov", "Dec."],
today: "Šodien",
suffix: [],
meridiem: [],
weekStart: 1
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Malay translation for bootstrap-datepicker
* Malay translation for bootstrap-datetimepicker
* Ateman Faiz <noorulfaiz@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['ms'] = {
$.fn.datetimepicker.dates['ms'] = {
days: ["Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu", "Ahad"],
daysShort: ["Aha", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab", "Aha"],
daysMin: ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa", "Ah"],
months: ["Januari", "Februari", "Mac", "April", "Mei", "Jun", "Julai", "Ogos", "September", "Oktober", "November", "Disember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis"],
today: "Hari Ini"
today: "Hari Ini",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Norwegian (bokmål) translation for bootstrap-datepicker
* Norwegian (bokmål) translation for bootstrap-datetimepicker
* Fredrik Sundmyhr <http://github.com/fsundmyhr>
*/
;(function($){
$.fn.datepicker.dates['nb'] = {
$.fn.datetimepicker.dates['nb'] = {
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
months: ["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"],
today: "I Dag"
today: "I Dag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Dutch translation for bootstrap-datepicker
* Dutch translation for bootstrap-datetimepicker
* Reinier Goltstein <mrgoltstein@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['nl'] = {
$.fn.datetimepicker.dates['nl'] = {
days: ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"],
daysShort: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
daysMin: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"],
months: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "Vandaag"
today: "Vandaag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Norwegian translation for bootstrap-datetimepicker
* Rune Warhuus <rune@dinkdonkd.no>
*/
;(function($){
$.fn.datetimepicker.dates['no'] = {
days: ["Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"],
daysShort: ["Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør", "Søn"],
daysMin: ["Sø", "Ma", "Ti", "On", "To", "Fr", "Lø", "Sø"],
months: ["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"],
today: "I Dag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Polish translation for bootstrap-datetimepicker
* Robert <rtpm@gazeta.pl>
*/
;(function($){
$.fn.datetimepicker.dates['pl'] = {
days: ["Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"],
daysShort: ["Nie", "Pn", "Wt", "Śr", "Czw", "Pt", "So", "Nie"],
daysMin: ["N", "Pn", "Wt", "Śr", "Cz", "Pt", "So", "N"],
months: ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"],
monthsShort: ["Sty", "Lu", "Mar", "Kw", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa", "Lis", "Gru"],
today: "Dzisiaj",
suffix: [],
meridiem: [],
weekStart: 1
};
}(jQuery));
@@ -1,14 +1,17 @@
/**
* Brazilian translation for bootstrap-datepicker
* Brazilian translation for bootstrap-datetimepicker
* Cauan Cabral <cauan@radig.com.br>
*/
;(function($){
$.fn.datepicker.dates['pt-BR'] = {
$.fn.datetimepicker.dates['pt-BR'] = {
format: 'dd/mm/yyyy',
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
today: "Hoje"
today: "Hoje",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,17 @@
/**
* Portuguese translation for bootstrap-datepicker
* Portuguese translation for bootstrap-datetimepicker
* Original code: Cauan Cabral <cauan@radig.com.br>
* Tiago Melo <tiago.blackcode@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['pt'] = {
$.fn.datetimepicker.dates['pt'] = {
days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"],
daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"],
daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"],
months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
suffix: [],
meridiem: ["am","pm"],
today: "Hoje"
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Romanian translation for bootstrap-datetimepicker
* Cristian Vasile <cristi.mie@gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates['ro'] = {
days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă", "Duminică"],
daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm", "Dum"],
daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ", "Du"],
months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"],
monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"],
today: "Astăzi",
suffix: [],
meridiem: [],
weekStart: 1
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Serbian latin translation for bootstrap-datetimepicker
* Bojan Milosavlević <milboj@gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates['rs'] = {
days: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota", "Nedelja"],
daysShort: ["Ned", "Pon", "Uto", "Sre", "Čet", "Pet", "Sub", "Ned"],
daysMin: ["N", "Po", "U", "Sr", "Č", "Pe", "Su", "N"],
months: ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
today: "Danas",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Serbian cyrillic translation for bootstrap-datetimepicker
* Bojan Milosavlević <milboj@gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates['rs'] = {
days: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота", "Недеља"],
daysShort: ["Нед", "Пон", "Уто", "Сре", "Чет", "Пет", "Суб", "Нед"],
daysMin: ["Н", "По", "У", "Ср", "Ч", "Пе", "Су", "Н"],
months: ["Јануар", "Фебруар", "Март", "Април", "Мај", "Јун", "Јул", "Август", "Септембар", "Октобар", "Новембар", "Децембар"],
monthsShort: ["Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", "Нов", "Дец"],
today: "Данас",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Russian translation for bootstrap-datepicker
* Russian translation for bootstrap-datetimepicker
* Victor Taranenko <darwin@snowdale.com>
*/
;(function($){
$.fn.datepicker.dates['ru'] = {
$.fn.datetimepicker.dates['ru'] = {
days: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"],
daysShort: ["Вск", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Вск"],
daysMin: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"],
months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
monthsShort: ["Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"],
today: "Сегодня"
today: "Сегодня",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Slovak translation for bootstrap-datetimepicker
* Marek Lichtner <marek@licht.sk>
* Fixes by Michal Remiš <michal.remis@gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates["sk"] = {
days: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"],
daysShort: ["Ned", "Pon", "Uto", "Str", "Štv", "Pia", "Sob", "Ned"],
daysMin: ["Ne", "Po", "Ut", "St", "Št", "Pia", "So", "Ne"],
months: ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Máj", "Jún", "Júl", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "Dnes",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Slovene translation for bootstrap-datepicker
* Slovene translation for bootstrap-datetimepicker
* Gregor Rudolf <gregor.rudolf@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['sl'] = {
$.fn.datetimepicker.dates['sl'] = {
days: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota", "Nedelja"],
daysShort: ["Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob", "Ned"],
daysMin: ["Ne", "Po", "To", "Sr", "Če", "Pe", "So", "Ne"],
months: ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"],
today: "Danes"
today: "Danes",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Swedish translation for bootstrap-datepicker
* Swedish translation for bootstrap-datetimepicker
* Patrik Ragnarsson <patrik@starkast.net>
*/
;(function($){
$.fn.datepicker.dates['sv'] = {
$.fn.datetimepicker.dates['sv'] = {
days: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"],
daysShort: ["Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör", "Sön"],
daysMin: ["Sö", "Må", "Ti", "On", "To", "Fr", "Lö", "Sö"],
months: ["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
today: "I Dag"
today: "I Dag",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,17 @@
/**
* Swahili translation for bootstrap-datetimepicker
* Edwin Mugendi <https://github.com/edwinmugendi>
* Source: http://scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=xnfaqyzcku
*/
;(function($){
$.fn.datetimepicker.dates['sw'] = {
days: ["Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi", "Jumapili"],
daysShort: ["J2", "J3", "J4", "J5", "Alh", "Ij", "J1", "J2"],
daysMin: ["2", "3", "4", "5", "A", "I", "1", "2"],
months: ["Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba", "Oktoba", "Novemba", "Desemba"],
monthsShort: ["Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des"],
today: "Leo",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Thai translation for bootstrap-datepicker
* Thai translation for bootstrap-datetimepicker
* Suchau Jiraprapot <seroz24@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['th'] = {
$.fn.datetimepicker.dates['th'] = {
days: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"],
daysShort: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
daysMin: ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส", "อา"],
months: ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"],
monthsShort: ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."],
today: "วันนี้"
today: "วันนี้",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,15 +1,17 @@
/**
* Turkish translation for bootstrap-datepicker
* Turkish translation for bootstrap-datetimepicker
* Serkan Algur <kaisercrazy_2@hotmail.com>
*/
;(function($){
$.fn.datepicker.dates['tr'] = {
$.fn.datetimepicker.dates['tr'] = {
days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"],
daysShort: ["Pz", "Pzt", "Sal", "Çrş", "Prş", "Cu", "Cts", "Pz"],
daysMin: ["Pz", "Pzt", "Sa", "Çr", "Pr", "Cu", "Ct", "Pz"],
months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"],
monthsShort: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"],
today: "Bugün"
today: "Bugün",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,15 @@
/**
* Ukrainian translation for bootstrap-datepicker
* Igor Polynets
*/
;(function($){
$.fn.datetimepicker.dates['ua'] = {
days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четверг", "П'ятниця", "Субота", "Неділя"],
daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Нед"],
daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд"],
months: ["Cічень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"],
monthsShort: ["Січ", "Лют", "Бер", "Квт", "Трв", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Грд"],
today: "Сьогодні",
weekStart: 1
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Ukrainian translation for bootstrap-datetimepicker
* Andrey Vityuk <andrey [dot] vityuk [at] gmail.com>
*/
;(function($){
$.fn.datetimepicker.dates['uk'] = {
days: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота", "Неділя"],
daysShort: ["Нед", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Суб", "Нед"],
daysMin: ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд"],
months: ["Січень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"],
monthsShort: ["Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"],
today: "Сьогодні",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -1,14 +1,16 @@
/**
* Simplified Chinese translation for bootstrap-datepicker
* Simplified Chinese translation for bootstrap-datetimepicker
* Yuan Cheung <advanimal@gmail.com>
*/
;(function($){
$.fn.datepicker.dates['zh-CN'] = {
$.fn.datetimepicker.dates['zh-CN'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
today: "今日"
today: "今日",
suffix: [],
meridiem: []
};
}(jQuery));
@@ -0,0 +1,16 @@
/**
* Traditional Chinese translation for bootstrap-datetimepicker
* Rung-Sheng Jang <daniel@i-trend.co.cc>
*/
;(function($){
$.fn.datetimepicker.dates['zh-TW'] = {
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
today: "今天",
suffix: [],
meridiem: ["上午", "下午"]
};
}(jQuery));
+3 -3
View File
@@ -7,12 +7,12 @@
<link rel="icon" type="image/png" href="{{ STATIC_URL }}css/logo-icon.png" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap-responsive.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/datepicker.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap-datetimepicker.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/custom.css" />
<script src="{{ STATIC_URL }}js/jquery.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap-datepicker.js" type="text/javascript"></script>
{% if LANGUAGE_CODE != "en" %}<script src="{{ STATIC_URL }}js/locales/bootstrap-datepicker.{{ LANGUAGE_CODE }}.js" type="text/javascript"></script>{% endif %}
<script src="{{ STATIC_URL }}js/bootstrap-datetimepicker.js" type="text/javascript"></script>
{% if LANGUAGE_CODE != "en" %}<script src="{{ STATIC_URL }}js/locales/bootstrap-datetimepicker.{{ LANGUAGE_CODE }}.js" type="text/javascript"></script>{% endif %}
<script src="{{ STATIC_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/csrf.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/confirmation.js" type="text/javascript"></script>