Merge branch 'django2' into 'master'

Migration vers Django 2.0 + tests

See merge request cof-geek/Ernesto!4
This commit is contained in:
Martin Pepin 2018-05-01 13:17:10 +02:00
commit 217e644db3
105 changed files with 429 additions and 14155 deletions

View file

@ -48,12 +48,12 @@ INSTALLED_APPS = [
'pads', 'pads',
] ]
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
@ -62,9 +62,7 @@ ROOT_URLCONF = "Ernestophone.urls"
TEMPLATES = [{ TEMPLATES = [{
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ 'DIRS': [],
os.path.join(BASE_DIR, 'templates/'),
],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -72,16 +70,15 @@ TEMPLATES = [{
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
], ],
} }
}] }]
WSGI_APPLICATION = "Ernestophone.wsgi.application"
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.postgresql_psycopg2", "ENGINE": "django.db.backends.postgresql",
"NAME": DBNAME, "NAME": DBNAME,
"USER": DBUSER, "USER": DBUSER,
"PASSWORD": DBPASSWD, "PASSWORD": DBPASSWD,
@ -89,6 +86,21 @@ DATABASES = {
} }
} }
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# I18n # I18n
LANGUAGE_CODE = 'fr-fr' LANGUAGE_CODE = 'fr-fr'

View file

@ -2,7 +2,7 @@ import os
from .common import * # noqa from .common import * # noqa
from .common import BASE_DIR, INSTALLED_APPS, MIDDLEWARE_CLASSES from .common import BASE_DIR, INSTALLED_APPS, MIDDLEWARE
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
@ -10,13 +10,14 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEBUG = True DEBUG = True
INSTALLED_APPS += ["debug_toolbar"] INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE_CLASSES = ( MIDDLEWARE = (
["debug_toolbar.middleware.DebugToolbarMiddleware"] ["debug_toolbar.middleware.DebugToolbarMiddleware"]
+ MIDDLEWARE_CLASSES + MIDDLEWARE
) )
STATIC_URL = "/static/" STATIC_URL = "/static/"
MEDIA_URL = "/media/" MEDIA_URL = "/media/"
MEDIA_ROOT = "media"
DATABASES = { DATABASES = {
"default": { "default": {

View file

@ -1,36 +1,40 @@
from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from django.urls import include, path
from gestion import views as gestion_views from gestion import views as gestion_views
urlpatterns = [ urlpatterns = [
url(r'^$', gestion_views.home, name='home'), path("", gestion_views.home, name="home"),
url(r'^login/?$', gestion_views.login, name='login'), path("login", gestion_views.login, name="login"),
url(r'^logout/?$', auth_views.logout, {'next_page': '/'}, name='logout'), path("logout", auth_views.logout, {"next_page": "/"}, name="logout"),
url(r'^registration/?$', gestion_views.inscription_membre, path("registration", gestion_views.inscription_membre, name="registration"),
name='registration'), path("change", gestion_views.change_membre, name="change_membre"),
url(r'^change/?', gestion_views.change_membre, name='change_membre'), path("password", gestion_views.change_password, name="change_password"),
url(r'^password/?', gestion_views.change_password, name='change_password'), path("user/password/reset", auth_views.password_reset,
url(r'^user/password/reset/$', auth_views.password_reset, {"post_reset_redirect": "/user/password/reset/done/"},
{'post_reset_redirect': '/user/password/reset/done/'}, name="password_reset"),
name="password_reset"), path("user/password/reset/done", auth_views.password_reset_done),
url(r'^user/password/reset/done/$', path("user/password/reset/<uidb64>/<token>/", auth_views.password_reset_confirm,
auth_views.password_reset_done), {'post_reset_redirect': '/user/password/done/'},
url(r'^user/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', name="password_reset_confirm"),
auth_views.password_reset_confirm, path("user/password/done", auth_views.password_reset_complete),
{'post_reset_redirect': '/user/password/done/'}, path("admin/", admin.site.urls),
name="password_reset_confirm"), path("partitions/", include('partitions.urls')),
url(r'^user/password/done/$', path("pads/", include('pads.urls')),
auth_views.password_reset_complete), path("calendar/", include('calendrier.urls')),
url(r'^admin/', include(admin.site.urls)), path("propositions/", include('propositions.urls')),
url(r'^partitions/', include('partitions.urls')), path("divers/", gestion_views.divers),
url(r'^pads/', include('pads.urls')),
url(r'^calendar/', include('calendrier.urls')),
url(r'^propositions/', include('propositions.urls')),
url(r'^divers/', gestion_views.divers),
] ]
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar
from django.conf.urls import include, url
urlpatterns = [
url(r"^__debug__/", include(debug_toolbar.urls)),
] + urlpatterns
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -36,8 +36,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)), ('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)),
('reponse', models.CharField(max_length=20, choices=[('oui', 'Oui'), ('non', 'Non'), ('pe', 'Peut-être')], default='non', verbose_name='Réponse')), ('reponse', models.CharField(max_length=20, choices=[('oui', 'Oui'), ('non', 'Non'), ('pe', 'Peut-être')], default='non', verbose_name='Réponse')),
('details', models.CharField(blank=True, max_length=50)), ('details', models.CharField(blank=True, max_length=50)),
('event', models.ForeignKey(to='calendrier.Event')), ('event', models.ForeignKey(to='calendrier.Event', on_delete=models.CASCADE)),
('participant', models.ForeignKey(to='gestion.ErnestoUser')), ('participant', models.ForeignKey(to='gestion.ErnestoUser', on_delete=models.CASCADE)),
], ],
), ),
] ]

View file

@ -33,10 +33,8 @@ class Event(models.Model):
class Participants(models.Model): class Participants(models.Model):
event = models.ForeignKey(Event) event = models.ForeignKey(Event, on_delete=models.CASCADE)
participant = models.ForeignKey(ErnestoUser) participant = models.ForeignKey(ErnestoUser, on_delete=models.CASCADE)
reponse = models.CharField("Réponse", max_length=20, default="non", reponse = models.CharField("Réponse", max_length=20, default="non",
choices=ANSWERS) choices=ANSWERS)
details = models.CharField(max_length=50, blank=True) details = models.CharField(max_length=50, blank=True)
# Create your models here.

View file

@ -9,7 +9,7 @@
{% if success %} {% if success %}
<p>Changement enregistré !</p> <p>Changement enregistré !</p>
{% endif %} {% endif %}
<form action="{% url 'change-doodle-name' %}" method="post"> <form action="{% url 'calendrier:change-doodle-name' %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Valider" /> <input type="submit" value="Valider" />

View file

@ -8,7 +8,7 @@
{% if erreur %} {% if erreur %}
{{ erreur }} {{ erreur }}
{% endif %} {% endif %}
<form action="{% url 'calendrier-create_event' %}" method="post"> <form action="{% url 'calendrier:create_event' %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Enregistrer" /> <input type="submit" value="Enregistrer" />

View file

@ -34,7 +34,7 @@ Fanfaron de passage, musicien intrigué, Ernestophoniste en quête de sensations
{{Calendar|translate}} {{Calendar|translate}}
</div> </div>
{% if user.profile.is_chef %} {% if user.profile.is_chef %}
<a href="{% url "calendrier-create_event" %}">Ajouter un évènement</a> <a href="{% url "calendrier:create_event" %}">Ajouter un évènement</a>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -6,10 +6,10 @@
{% if envoi %}<p>Votre réponse a été enregistrée !</p>{% endif %} {% if envoi %}<p>Votre réponse a été enregistrée !</p>{% endif %}
<p><a href="{% url 'view-event' id %}">Retour à l'événement</a></p> <p><a href="{% url 'calendrier:view-event' id %}">Retour à l'événement</a></p>
<div> Voulez vous participer à l'événement {{ ev.nom }}, le {{ ev.date }} à {{ ev.debut|time:"H:i" }} ?</div> <div> Voulez vous participer à l'événement {{ ev.nom }}, le {{ ev.date }} à {{ ev.debut|time:"H:i" }} ?</div>
<form action="{% url 'calendrier-reponse' id %}" method="post"> <form action="{% url 'calendrier:reponse' id %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Valider" /> <input type="submit" value="Valider" />

View file

@ -6,7 +6,7 @@
{% if erreur %} {% if erreur %}
{{ erreur }} {{ erreur }}
{% endif %} {% endif %}
<form action="{% url 'calendrier-resend' id %}" method="post"> <form action="{% url 'calendrier:resend' id %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Renvoyer les mails" /> <input type="submit" value="Renvoyer les mails" />

View file

@ -20,7 +20,7 @@
<p><a href="/calendar/supprimer/{{ev.id}}">Supprimer l'événement</a></p> <p><a href="/calendar/supprimer/{{ev.id}}">Supprimer l'événement</a></p>
<p><a href="/calendar/resend/{{ev.id}}">Renvoyer les mails</a></p> <p><a href="/calendar/resend/{{ev.id}}">Renvoyer les mails</a></p>
{% endif %} {% endif %}
<p><a href="{% url "change-doodle-name" %}">Changer mon nom pour le doodle</a></p> <p><a href="{% url "calendrier:change-doodle-name" %}">Changer mon nom pour le doodle</a></p>
</div> </div>
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
@ -51,7 +51,7 @@
Pas de réponse pour l'instant Pas de réponse pour l'instant
{% endfor %} {% endfor %}
</table> </table>
<p><a href="{% url 'calendrier-reponse' id %}">Répondre à l'événement</a></p> <p><a href="{% url 'calendrier:reponse' id %}">Répondre à l'événement</a></p>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

View file

@ -0,0 +1,159 @@
from datetime import timedelta
from django.contrib.auth import get_user_model
from django.template.defaultfilters import urlencode
from django.test import Client, TestCase
from django.utils import timezone
from gestion.models import ErnestoUser
from ..models import Event
User = get_user_model()
def new_user(username, ernesto=False, chef=False):
u = User.objects.create_user(username=username)
ErnestoUser.objects.create(user=u, slug=username, is_chef=chef, is_ernesto=ernesto)
return u
class TestViews(TestCase):
# TODO: test forms
def setUp(self):
# User with different access level and their clients
chef = new_user("chef", ernesto=True, chef=True)
chef_c = Client()
chef_c.force_login(chef)
ernesto = new_user("ernesto", ernesto=True)
ernesto_c = Client()
ernesto_c.force_login(ernesto)
self.client_matrix = [
(chef, chef_c),
(ernesto, ernesto_c),
(None, Client())
]
# A private and a public event
now = timezone.now()
later = now + timedelta(seconds=3600)
self.priv_event = Event.objects.create(
nom="private event",
nomcourt="privevt",
date=now.date(),
debut=now.time(),
fin=later.time(),
slug="privevt",
lieu="somewhere",
calendrier=False
)
self.pub_event = Event.objects.create(
nom="public event",
nomcourt="pubevt",
date=now.date(),
debut=now.time(),
fin=later.time(),
slug="pubevt",
lieu="somewhere",
calendrier=True
)
# Everyone can see theses pages
def _everyone_can_get(self, url, redirect_url=None):
"""Shorthand for checking that every kind of user can get a page"""
for _, client in self.client_matrix:
resp = client.get(url)
if redirect_url:
self.assertRedirects(resp, redirect_url)
else:
self.assertEqual(200, resp.status_code)
def test_get_home(self):
url = "/calendar/"
self._everyone_can_get(url)
def test_get_calendar(self):
year, month = 2017, 5
url = "/calendar/{}/{}".format(year, month)
self._everyone_can_get(url)
def test_get_public_event(self):
"""Public event, everyone can see"""
evt = self.pub_event
url = "/calendar/{}/{}/{}".format(evt.date.year, evt.date.month, evt.id)
self._everyone_can_get(url)
def test_get_public_event_bis(self):
"""Public event, everyone can see"""
url = "/calendar/{}".format(self.pub_event.id)
self._everyone_can_get(url)
def test_get_reponse_event(self):
# XXX: this view sucks
chef, _ = self.client_matrix[0]
codereps = ["oui", "non", "pe"]
for coderep in codereps:
url = "/calendar/rep/{}/{}/{}".format(chef.profile.slug, self.priv_event.slug, coderep)
self._everyone_can_get(url, redirect_url="/calendar/")
# Only ernesto members can get theses pages
def _get_restricted_page(self, url, chef_only=False, redirect_url=None):
"""Shorthand for testing wether a page in only accessible by ernesto members"""
def user_allowed(user):
if user is None:
return False
if chef_only:
return user.profile.is_chef
return True
for user, client in self.client_matrix:
# If user is not None, it is an ernesto member
resp = client.get(url)
if user_allowed(user):
self.assertEqual(200, resp.status_code)
else:
if redirect_url is None:
redirect_url = "/login?next={}".format(urlencode(urlencode(url)))
self.assertRedirects(resp, redirect_url)
def test_get_private_event(self):
"""Private event, restricted access"""
evt = self.priv_event
url = "/calendar/{}/{}/{}".format(evt.date.year, evt.date.month, evt.id)
self._get_restricted_page(url, redirect_url="/calendar/")
def test_get_private_event_bis(self):
"""Private event, restricted access"""
url = "/calendar/{}".format(self.priv_event.id)
self._get_restricted_page(url, redirect_url="/calendar/")
def test_get_new(self):
"""Only chef can create an event"""
url = "/calendar/new"
self._get_restricted_page(url, chef_only=True)
def test_get_edit(self):
"""Only chef can edit an event"""
url = "/calendar/edition/{}".format(self.pub_event.id)
self._get_restricted_page(url, chef_only=True)
def test_get_delete(self):
"""Only chef can delete an event"""
url = "/calendar/supprimer/{}".format(self.pub_event.id)
self._get_restricted_page(url, chef_only=True)
def test_get_resend(self):
"""Only chef can send event emails"""
url = "/calendar/resend/{}".format(self.pub_event.id)
self._get_restricted_page(url, chef_only=True)
def test_get_changename(self):
"""Only authenticated users can have a doodle name"""
url = "/calendar/changename"
self._get_restricted_page(url)
def test_get_answers(self):
"""Only ernesto members can see who attends an event"""
url = "/calendar/{}/reponse".format(self.priv_event.id)
self._get_restricted_page(url)

View file

@ -1,20 +1,20 @@
from django.conf.urls import url from django.urls import path
from calendrier import views from . import views
from calendrier.views import EventUpdate, EventDelete from .views import EventUpdate, EventDelete
app_name = "calendrier"
urlpatterns = [ urlpatterns = [
url(r'^new$', views.create_event, name='calendrier-create_event'), path("", views.home, name="home"),
url(r'^$', views.home), path("new", views.create_event, name="create_event"),
url(r'^edition/(?P<pk>\d+)$', EventUpdate.as_view()), path("edition/<int:pk>", EventUpdate.as_view()),
url(r'^supprimer/(?P<pk>\d+)$', EventDelete.as_view()), path("supprimer/<int:pk>", EventDelete.as_view()),
url(r'^resend/(?P<id>\d+)$', views.resend, name='calendrier-resend'), path("resend/<int:id>", views.resend, name="resend"),
url(r'^changename/?$', views.changename, name='change-doodle-name'), path("changename", views.changename, name="change-doodle-name"),
url(r'(?P<id>\d+)/reponse/?', views.reponse, name='calendrier-reponse'), path("<int:id>/reponse", views.reponse, name="reponse"),
url(r'(?P<codeus>\w{6})/(?P<codeev>\w{6})/(?P<coderep>\w+)/?', path("rep/<slug:codeus>/<slug:codeev>/<slug:coderep>", views.reponse_event, name="reponse_event"),
views.reponse_event, path("<int:pYear>/<int:pMonth>/<int:id>", views.view_event),
name='calendrier.reponse_event'), path("<int:pYear>/<int:pMonth>", views.calendar),
url(r'(?P<pYear>\d+)/(?P<pMonth>\d+)/(?P<id>\d+)/?', views.view_event), path("<int:id>", views.view_eventbis, name="view-event"),
url(r'(?P<pYear>\d+)/(?P<pMonth>\d+)/?$', views.calendar),
url(r'(?P<id>\d+)/?', views.view_eventbis, name='view-event'),
] ]

View file

@ -4,7 +4,7 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.views.generic import UpdateView, DeleteView from django.views.generic import UpdateView, DeleteView
from django.core.urlresolvers import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.core.mail import send_mail from django.core.mail import send_mail
@ -37,7 +37,7 @@ def calendar(request, pYear, pMonth):
lEvents = Event.objects.filter(date__gte=lCalendarFromMonth, lEvents = Event.objects.filter(date__gte=lCalendarFromMonth,
date__lte=lCalendarToMonth, date__lte=lCalendarToMonth,
calendrier=True) calendrier=True)
if request.user.is_authenticated(): if request.user.is_authenticated:
lEvents = Event.objects.filter(date__gte=lCalendarFromMonth, lEvents = Event.objects.filter(date__gte=lCalendarFromMonth,
date__lte=lCalendarToMonth) date__lte=lCalendarToMonth)
lCalendar = EventCalendar(lEvents).formatmonth(lYear, lMonth) lCalendar = EventCalendar(lEvents).formatmonth(lYear, lMonth)
@ -71,8 +71,8 @@ def calendar(request, pYear, pMonth):
def view_event(request, pYear, pMonth, id): def view_event(request, pYear, pMonth, id):
ev = get_object_or_404(Event, id=id) ev = get_object_or_404(Event, id=id)
if not request.user.is_authenticated() and not ev.calendrier: if not request.user.is_authenticated and not ev.calendrier:
return redirect('calendrier.views.home') return redirect(reverse('calendrier:home'))
nom = ev.nom.capitalize nom = ev.nom.capitalize
fin = False fin = False
desc = False desc = False
@ -89,8 +89,8 @@ def view_event(request, pYear, pMonth, id):
def view_eventbis(request, id): def view_eventbis(request, id):
ev = get_object_or_404(Event, id=id) ev = get_object_or_404(Event, id=id)
if not request.user.is_authenticated() and not ev.calendrier: if not request.user.is_authenticated and not ev.calendrier:
return redirect('calendrier.views.home') return redirect(reverse('calendrier:home'))
part = ev.participants_set.all() part = ev.participants_set.all()
nom = ev.nom.capitalize nom = ev.nom.capitalize
fin = False fin = False
@ -241,6 +241,7 @@ def reponse(request, id):
return render(request, "calendrier/reponse.html", locals()) return render(request, "calendrier/reponse.html", locals())
# XXX: UNSAFE!
def reponse_event(request, codeus, codeev, coderep): def reponse_event(request, codeus, codeev, coderep):
""" """
Inscriptions aux événements via les liens envoyés par mail. Inscriptions aux événements via les liens envoyés par mail.
@ -254,7 +255,7 @@ def reponse_event(request, codeus, codeev, coderep):
Participants.objects.filter(event=ev, participant=part).delete() Participants.objects.filter(event=ev, participant=part).delete()
# Et on écrit la nouvelle inscription # Et on écrit la nouvelle inscription
Participants.objects.create(participant=part, event=ev, reponse=coderep) Participants.objects.create(participant=part, event=ev, reponse=coderep)
return redirect('calendrier.views.home') return redirect(reverse('calendrier:home'))
class EventUpdate(UpdateView): class EventUpdate(UpdateView):

View file

@ -84,7 +84,7 @@ class UserProfileAdmin(UserAdmin):
perm = Permission.objects.get_by_natural_key(*nat_key) perm = Permission.objects.get_by_natural_key(*nat_key)
chef_group.permissions.add(perm) chef_group.permissions.add(perm)
# On met tous les chef dans le groupe # On met tous les chef dans le groupe
chef_group.user_set = User.objects.filter(profile__is_chef=True) chef_group.user_set.set(User.objects.filter(profile__is_chef=True))
# Les chefs sont dans le groupe Chef # Les chefs sont dans le groupe Chef
if user.profile.is_chef: if user.profile.is_chef:
print("J'aime la choucroute") print("J'aime la choucroute")

View file

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
from django.conf import settings from django.conf import settings
@ -23,7 +20,14 @@ class Migration(migrations.Migration):
('slug', models.CharField(max_length=7, editable=False, unique=True)), ('slug', models.CharField(max_length=7, editable=False, unique=True)),
('doodlename', models.CharField(blank=True, max_length=30, verbose_name='Nom pour le doodle')), ('doodlename', models.CharField(blank=True, max_length=30, verbose_name='Nom pour le doodle')),
('mails', models.BooleanField(verbose_name='Recevoir les mails', default=True)), ('mails', models.BooleanField(verbose_name='Recevoir les mails', default=True)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='profile')), (
'user',
models.OneToOneField(
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='profile'
)
),
], ],
options={ options={
'verbose_name': 'Profil Ernestophoniste', 'verbose_name': 'Profil Ernestophoniste',

View file

@ -5,7 +5,7 @@ from django.contrib.auth.models import User
class ErnestoUser(models.Model): class ErnestoUser(models.Model):
user = models.OneToOneField(User, related_name="profile") user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
is_ernesto = models.BooleanField("Membre de l'Ernestophone", default=True) is_ernesto = models.BooleanField("Membre de l'Ernestophone", default=True)
is_chef = models.BooleanField("Chef Fanfare", default=False) is_chef = models.BooleanField("Chef Fanfare", default=False)
phone = models.CharField("Téléphone", max_length=20, blank=True) phone = models.CharField("Téléphone", max_length=20, blank=True)
@ -19,9 +19,6 @@ class ErnestoUser(models.Model):
verbose_name = "Profil Ernestophoniste" verbose_name = "Profil Ernestophoniste"
verbose_name_plural = "Profil Ernestophoniste" verbose_name_plural = "Profil Ernestophoniste"
def __unicode__(self):
return unicode(self.user.username)
def __str__(self): def __str__(self):
return self.user.username return self.user.username
# Create your models here. # Create your models here.

View file

@ -19,7 +19,7 @@
<nav> <nav>
<ul id="menu"> <ul id="menu">
<li><a href="{% url "partitions-liste" %}">Partitions</a></li> <li><a href="{% url "partitions:list" %}">Partitions</a></li>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if user.is_superuser %} {% if user.is_superuser %}
<li><a href = '/admin/'>Administration</a></li> <li><a href = '/admin/'>Administration</a></li>

View file

@ -10,7 +10,7 @@
<p>Changements enregistrés !</p> <p>Changements enregistrés !</p>
{% endif %} {% endif %}
<p><a href="{% url "change_password" %}">Changer mon mot de passe</a></p> <p><a href="{% url "change_password" %}">Changer mon mot de passe</a></p>
<p><a href="{% url "change-doodle-name" %}">Changer mon nom pour les doodles</a></p> <p><a href="{% url "calendrier:change-doodle-name" %}">Changer mon nom pour les doodles</a></p>
<form action="{% url 'change_membre' %}" method="post"> <form action="{% url 'change_membre' %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}

View file

@ -3,7 +3,7 @@
{% block content %} {% block content %}
<h1>Divers</h1> <h1>Divers</h1>
<ul> <ul>
<li><a href="{% url "propositions-liste" %}">Propositions de morceaux</a></li> <li><a href="{% url "propositions:list" %}">Propositions de morceaux</a></li>
<li><a href="{% url "pads-liste" %}">Pads</a></li> <li><a href="{% url "pads:list" %}">Pads</a></li>
</ul> </ul>
{% endblock %} {% endblock %}

View file

@ -1,12 +1,13 @@
from django.conf.urls import url from django.urls import path
from pads import views from pads import views
from pads.views import PadUpdate, PadDelete from pads.views import PadUpdate, PadDelete
app_name = "pads"
urlpatterns = [ urlpatterns = [
url(r'^$', views.liste_pads, name='pads-liste'), path("", views.liste_pads, name="list"),
url(r'^ajouter$', views.add_pad, name='pads-add'), path("ajouter", views.add_pad, name="add"),
url(r'^edition/(?P<pk>\d+)$', PadUpdate.as_view()), path("edition/<int:pk>", PadUpdate.as_view()),
url(r'^supprimer/(?P<pk>\d+)$', PadDelete.as_view()), path("supprimer/<int:pk>", PadDelete.as_view()),
] ]

View file

@ -1,6 +1,6 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy from django.urls import reverse_lazy
from django.views.generic import UpdateView, DeleteView from django.views.generic import UpdateView, DeleteView
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from datetime import date from datetime import date

View file

@ -31,6 +31,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='partition', model_name='partition',
name='morceau', name='morceau',
field=models.ForeignKey(to='partitions.PartitionSet'), field=models.ForeignKey(to='partitions.PartitionSet', on_delete=models.CASCADE),
), ),
] ]

View file

@ -19,7 +19,7 @@ class Category(models.Model):
class Partition(models.Model): class Partition(models.Model):
nom = models.CharField(max_length=100) nom = models.CharField(max_length=100)
part = models.FileField(upload_to="partitions/") part = models.FileField(upload_to="partitions/")
morceau = models.ForeignKey('PartitionSet') morceau = models.ForeignKey('PartitionSet', on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return self.nom return self.nom

View file

@ -2,6 +2,6 @@
{% block titre %}Confirmation de suppression{% endblock %} {% block titre %}Confirmation de suppression{% endblock %}
{% block content %}<h1>Confirmation de suppression</h1> {% block content %}<h1>Confirmation de suppression</h1>
<p>Voulez-vous vraiment supprimer cette partition ?</p> <p>Voulez-vous vraiment supprimer cette partition ?</p>
<p><a href="{% url "partitions-delete" nom auteur id %}">Oui</a></p> <p><a href="{% url "partitions:delete" nom auteur id %}">Oui</a></p>
<p><a href="{% url "partitions-listepart" nom auteur %}">Retour à la liste des partitions</a></p> <p><a href="{% url "partitions:listepart" nom auteur %}">Retour à la liste des partitions</a></p>
{% endblock %} {% endblock %}

View file

@ -2,6 +2,6 @@
{% block titre %}Confirmation de suppression{% endblock %} {% block titre %}Confirmation de suppression{% endblock %}
{% block content %}<h1>Confirmation de suppression</h1> {% block content %}<h1>Confirmation de suppression</h1>
<p>Voulez-vous vraiment supprimer {{ nom }}-{{ auteur }} et toutes les partitions qu'il contient ?</p> <p>Voulez-vous vraiment supprimer {{ nom }}-{{ auteur }} et toutes les partitions qu'il contient ?</p>
<p><a href="{% url "partitions-delete_morc" nom auteur %}">Oui</a></p> <p><a href="{% url "partitions:delete_morc" nom auteur %}">Oui</a></p>
<p><a href="{% url "partitions-liste" %}">Retour à la liste des partitions</a></p> <p><a href="{% url "partitions:list" %}">Retour à la liste des partitions</a></p>
{% endblock %} {% endblock %}

View file

@ -9,7 +9,7 @@
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<h3><a href="{% url "partitions-ajouter_morceau" %}">Ajouter un morceau</a></h3> <h3><a href="{% url "partitions:ajouter_morceau" %}">Ajouter un morceau</a></h3>
{% endif %} {% endif %}
{% for category in categories %} {% for category in categories %}
@ -20,11 +20,11 @@
{% if not user.is_authenticated %} {% if not user.is_authenticated %}
{{ partition.nom }} - {{ partition.auteur }} {{ partition.nom }} - {{ partition.auteur }}
{% else %} {% else %}
<a href="{% url "partitions-listepart" partition.nom partition.auteur %}" <a href="{% url "partitions:listepart" partition.nom partition.auteur %}"
class="fichier">{{ partition.nom }} - {{ partition.auteur }}</a> class="fichier">{{ partition.nom }} - {{ partition.auteur }}</a>
{% endif %} {% endif %}
{% if user.profile.is_chef %} {% if user.profile.is_chef %}
<a href="{% url "partitions-conf_delete_morc" partition.nom partition.auteur %}" <a href="{% url "partitions:conf_delete_morc" partition.nom partition.auteur %}"
class="supprimer">Supprimer</a> class="supprimer">Supprimer</a>
{% endif %} {% endif %}
</li> </li>

View file

@ -4,14 +4,14 @@
{% block content %} {% block content %}
<h1>{{ p }}</h1> <h1>{{ p }}</h1>
{% if suppression %} {% if suppression %}
<p>{{ suppression }}</p> <p>{{ suppression }}</p>
{% endif %} {% endif %}
<div class="info_part"> <div class="info_part">
{% if user.profile.is_chef %} {% if user.profile.is_chef %}
<form action="{% url "partitions-listepart" nom auteur %}" id="chef-edit-form" method="post"> <form action="{% url "partitions:listepart" nom auteur %}" id="chef-edit-form" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Enregister" /> <input type="submit" value="Enregister" />
@ -29,19 +29,19 @@
<ul class="filelist"> <ul class="filelist">
{% for p in part %} {% for p in part %}
{% if user.is_authenticated and ".pdf" in p.part.url %} {% if user.is_authenticated and ".pdf" in p.part.url %}
<li><a href="{% url "partitions-see" nom auteur p.id %}" class="fichier">{{ p.nom }}</a> <li><a href="{% url "partitions:see" nom auteur p.id %}" class="fichier">{{ p.nom }}</a>
{% elif user.is_authenticated and ".mp3" in p.part.url %} {% elif user.is_authenticated and ".mp3" in p.part.url %}
<li><a href="{% url "partitions-see" nom auteur p.id %}" class="fichier">{{ p.nom }}</a> <li><a href="{% url "partitions:see" nom auteur p.id %}" class="fichier">{{ p.nom }}</a>
{% else %} {% else %}
<li><p class="fichier">{{ p.nom }}</p> <li><p class="fichier">{{ p.nom }}</p>
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url "partitions-download" nom auteur p.id %}" class="telecharger">Télécharger</a> <a href="{% url "partitions:download" nom auteur p.id %}" class="telecharger">Télécharger</a>
{% endif %} {% endif %}
{% if user.profile.is_chef %} {% if user.profile.is_chef %}
<a href="{% url "partitions-conf_delete" nom auteur p.pk %}" class="supprimer">Supprimer</a> <a href="{% url "partitions:conf_delete" nom auteur p.pk %}" class="supprimer">Supprimer</a>
{% endif %} {% endif %}
</li> </li>
@ -50,5 +50,5 @@
{% endfor %} {% endfor %}
</ul> </ul>
<p><a href="{% url "partitions-upload" p.nom p.auteur %}">Ajouter un média</a></p> <p><a href="{% url "partitions:upload" p.nom p.auteur %}">Ajouter un média</a></p>
{% endblock %} {% endblock %}

View file

@ -8,11 +8,11 @@
<p> {{ error }}</p> <p> {{ error }}</p>
{% endif %} {% endif %}
<p> <p>
<form method="post" action="{% url 'partitions-ajouter_morceau' %}"> <form method="post" action="">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Ajouter"/> <input type="submit" value="Ajouter"/>
</form> </form>
</p> </p>
<p><a href="{% url 'partitions-liste' %}">Retour à la liste</a></p> <p><a href="{% url 'partitions:list' %}">Retour à la liste</a></p>
{% endblock %} {% endblock %}

View file

@ -8,12 +8,12 @@
<p>{{ error }}</p> <p>{{ error }}</p>
{% endif %} {% endif %}
<p> <p>
<form method ="post" enctype="multipart/form-data" action="{% url 'partitions-upload' nom auteur %}"> <form method ="post" enctype="multipart/form-data" action="{% url 'partitions:upload' nom auteur %}">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<p> <input type="submit" value="Enregistrer"/> </p> <p> <input type="submit" value="Enregistrer"/> </p>
</form> </form>
</p> </p>
<p><a href="{% url "partitions-listepart" nom auteur %}">Retour aux partitions</a></p> <p><a href="{% url "partitions:listepart" nom auteur %}">Retour aux partitions</a></p>
{% endblock %} {% endblock %}

View file

View file

@ -0,0 +1,101 @@
from django.contrib.auth import get_user_model
from django.core.files import File
from django.template.defaultfilters import urlencode
from django.test import Client, TestCase
from gestion.models import ErnestoUser
from ..models import Category, Partition, PartitionSet
User = get_user_model()
def new_user(username, ernesto=False, chef=False):
u = User.objects.create_user(username=username)
ErnestoUser.objects.create(
user=u,
slug=username,
is_chef=chef,
is_ernesto=ernesto
)
return u
class TestViews(TestCase):
# TODO: add tests for upload/deletions
# TODO: test forms
def setUp(self):
# User with different access level and their clients
chef = new_user("chef", ernesto=True, chef=True)
chef_c = Client()
chef_c.force_login(chef)
ernesto = new_user("ernesto", ernesto=True)
ernesto_c = Client()
ernesto_c.force_login(ernesto)
self.client_matrix = [
(chef, chef_c),
(ernesto, ernesto_c),
(None, Client())
]
# A Partition set with 1 partition
self.pset = PartitionSet.objects.create(
category=Category.objects.first(),
nom="My PSet",
auteur="PSet author"
)
file = File(open("media/test_file.txt"), "test file")
Partition.objects.create(nom="part1", part=file, morceau=self.pset)
def tearDown(self):
"""Remove dummy files from media/partitions/"""
for partition in self.pset.partition_set.all():
partition.delete()
def _get_restricted_page(self, url):
"""
Shorthand for testing wether a page in only accessible by ernesto
members
"""
for user, client in self.client_matrix:
# If user is not None, it is an ernesto member
resp = client.get(url)
if user:
self.assertEqual(200, resp.status_code)
else:
encoded_url = urlencode(urlencode(url))
redirection_url = "/login?next={}".format(encoded_url)
self.assertRedirects(resp, redirection_url)
def test_get_partition_sets(self):
"""The list of all partition sets can be fetched by everyone"""
for _, client in self.client_matrix:
resp = client.get("/partitions/")
self.assertEqual(200, resp.status_code)
def test_get_partitions(self):
"""
Only ernesto members can see the partitions inside of a partition set
"""
url = "/partitions/{}/{}".format(self.pset.nom, self.pset.auteur)
self._get_restricted_page(url)
def test_see(self):
"""Only ernesto members can see partitions"""
part = self.pset.partition_set.first()
url = "/partitions/{}/{}/see/{}".format(
self.pset.nom, self.pset.auteur, part.id
)
self._get_restricted_page(url)
def test_download(self):
"""Only ernesto members can download partitions"""
part = self.pset.partition_set.first()
url = "/partitions/{}/{}/{}".format(
self.pset.nom, self.pset.auteur, part.id
)
self._get_restricted_page(url)
def test_new(self):
"""Only ernesto members can create partiton sets"""
url = "/partitions/new"
self._get_restricted_page(url)

View file

@ -1,24 +1,17 @@
from django.conf.urls import url from django.urls import path
from partitions import views from . import views
app_name = "partitions"
urlpatterns = [ urlpatterns = [
url(r'^$', views.liste, name='partitions-liste'), path("", views.liste, name="list"),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/?$', views.listepart, path("<str:nom>/<str:auteur>", views.listepart, name="listepart"),
name='partitions-listepart'), path("<str:nom>/<str:auteur>/upload", views.upload, name="upload"),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/upload/?$', views.upload, path("<str:nom>/<str:auteur>/delete", views.delete_morc, name="delete_morc"),
name='partitions-upload'), path("<str:nom>/<str:auteur>/conf", views.conf_delete_morc, name="conf_delete_morc"),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/delete/?$', views.delete_morc, path("<str:nom>/<str:auteur>/see/<int:partition_id>", views.see, name="see"),
name='partitions-delete_morc'), path("<str:nom>/<str:auteur>/<int:partition_id>", views.download, name="download"),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/conf/?$', views.conf_delete_morc, path("<str:nom>/<str:auteur>/<int:id>/delete", views.delete, name="delete"),
name='partitions-conf_delete_morc'), path("<str:nom>/<str:auteur>/<int:id>/conf", views.conf_delete, name="conf_delete"),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/see/(?P<partition_id>\d+)/?$', path("new", views.ajouter_morceau, name="ajouter_morceau"),
views.see, name='partitions-see'),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/(?P<partition_id>\d+)/$',
views.download, name='partitions-download'),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/(?P<id>[^/]+)/delete/?$',
views.delete, name='partitions-delete'),
url(r'^(?P<nom>[^/]+)/(?P<auteur>[^/]+)/(?P<id>[^/]+)/conf/?$',
views.conf_delete, name='partitions-conf_delete'),
url(r'^new/?$', views.ajouter_morceau, name='partitions-ajouter_morceau'),
] ]

View file

@ -64,8 +64,8 @@ def see(request, nom, auteur, partition_id):
response["Content-Length"] = myfile.size response["Content-Length"] = myfile.size
return response return response
else: else:
partitions = get_object_or_404(PartitionSet, nom=nom, auteur=auteur) p = get_object_or_404(PartitionSet, nom=nom, auteur=auteur)
part = partitions.partition_set.all() part = p.partition_set.all()
return render(request, 'partitions/listepart.html', locals()) return render(request, 'partitions/listepart.html', locals())

View file

@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('lien', models.URLField(blank=True)), ('lien', models.URLField(blank=True)),
('nboui', models.IntegerField(verbose_name='oui', default=0)), ('nboui', models.IntegerField(verbose_name='oui', default=0)),
('nbnon', models.IntegerField(verbose_name='non', default=0)), ('nbnon', models.IntegerField(verbose_name='non', default=0)),
('user', models.ForeignKey(verbose_name='Proposé par', to='gestion.ErnestoUser')), ('user', models.ForeignKey(verbose_name='Proposé par', to='gestion.ErnestoUser', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name': 'Proposition', 'verbose_name': 'Proposition',
@ -31,8 +31,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('reponse', models.CharField(verbose_name='Réponse', choices=[('oui', 'Oui'), ('non', 'Non')], max_length=20, blank=True)), ('reponse', models.CharField(verbose_name='Réponse', choices=[('oui', 'Oui'), ('non', 'Non')], max_length=20, blank=True)),
('part', models.ForeignKey(to='gestion.ErnestoUser')), ('part', models.ForeignKey(to='gestion.ErnestoUser', on_delete=models.CASCADE)),
('prop', models.ForeignKey(to='propositions.Prop')), ('prop', models.ForeignKey(to='propositions.Prop', on_delete=models.CASCADE)),
], ],
), ),
] ]

View file

@ -10,7 +10,7 @@ REP = (
class Prop(models.Model): class Prop(models.Model):
nom = models.CharField(max_length=100) nom = models.CharField(max_length=100)
artiste = models.CharField(blank=True, max_length=100) artiste = models.CharField(blank=True, max_length=100)
user = models.ForeignKey(ErnestoUser, verbose_name="Proposé par") user = models.ForeignKey(ErnestoUser, on_delete=models.CASCADE, verbose_name="Proposé par")
lien = models.URLField(blank=True) lien = models.URLField(blank=True)
nboui = models.IntegerField(default=0, verbose_name="oui") nboui = models.IntegerField(default=0, verbose_name="oui")
nbnon = models.IntegerField(default=0, verbose_name="non") nbnon = models.IntegerField(default=0, verbose_name="non")
@ -23,9 +23,6 @@ class Prop(models.Model):
class Reponses(models.Model): class Reponses(models.Model):
prop = models.ForeignKey(Prop) prop = models.ForeignKey(Prop, on_delete=models.CASCADE)
part = models.ForeignKey(ErnestoUser) part = models.ForeignKey(ErnestoUser, on_delete=models.CASCADE)
reponse = models.CharField("Réponse", max_length=20, blank=True, reponse = models.CharField("Réponse", max_length=20, blank=True, choices=REP)
choices=REP)
# Create your models here.

View file

@ -3,9 +3,9 @@
{%block titre %}Proposition de morceau{% endblock %} {%block titre %}Proposition de morceau{% endblock %}
{% block content %} {% block content %}
<p><a href="{% url "propositions-liste" %}">Retour aux propositions</a></p> <p><a href="{% url "propositions:list" %}">Retour aux propositions</a></p>
{% if envoi %}<p>Votre proposition a été enregistrée.{% endif %} {% if envoi %}<p>Votre proposition a été enregistrée.{% endif %}
<form action="{% url "propositions-create" %}" method="post"> <form action="{% url "propositions:create" %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="Enregistrer" /> <input type="submit" value="Enregistrer" />

View file

@ -3,7 +3,7 @@
{% block titre %}Suppression d'une proposition{% endblock %} {% block titre %}Suppression d'une proposition{% endblock %}
{% block content %}<form action="" method="post"> {% block content %}<form action="" method="post">
{% csrf_token %} {% csrf_token %}
<p><a href="{% url "propositions-liste" %}">Retour aux propositions</a></p> <p><a href="{% url "propositions:list" %}">Retour aux propositions</a></p>
<p>Voulez vous vraiment supprimer la proposition {{ object }}?</p> <p>Voulez vous vraiment supprimer la proposition {{ object }}?</p>
<input type="submit" value="Oui" /> <input type="submit" value="Oui" />
</form> </form>

View file

@ -9,7 +9,7 @@
<p>{{ error }}</p> <p>{{ error }}</p>
{% endif %} {% endif %}
<p><a href="{% url "propositions-create" %}">Proposer un morceau</a></p> <p><a href="{% url "propositions:create" %}">Proposer un morceau</a></p>
{% if n > 0 %} {% if n > 0 %}
<table> <table>
@ -27,10 +27,10 @@
{% if p.lien %}<a href={{ p.lien }}>{% endif %}<b>{{ p.nom }}</b>{% if p.artiste %} - {{ p.artiste }}{% endif %}{% if p.lien %}</a>{% endif %}</td> {% if p.lien %}<a href={{ p.lien }}>{% endif %}<b>{{ p.nom }}</b>{% if p.artiste %} - {{ p.artiste }}{% endif %}{% if p.lien %}</a>{% endif %}</td>
<td>{{ p.nboui }}</td> <td>{{ p.nboui }}</td>
<td>{{ p.nbnon }}</td> <td>{{ p.nbnon }}</td>
<td><a href="{% url "propositions-oui" p.id %}">Oui</a></td> <td><a href="{% url "propositions:oui" p.id %}">Oui</a></td>
<td><a href="{% url "propositions-non" p.id %}">Non</a></td> <td><a href="{% url "propositions:non" p.id %}">Non</a></td>
{% getresponse request.user.profile p %} {% getresponse request.user.profile p %}
<td>{% if p.user == request.user.profile or request.user.profile.is_chef %}<a class="supprimer" href="{% url "propositions-delete" p.id %}">Supprimer</a>{% endif %}</td> <td>{% if p.user == request.user.profile or request.user.profile.is_chef %}<a class="supprimer" href="{% url "propositions:delete" p.id %}">Supprimer</a>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View file

@ -1,13 +1,13 @@
from django.conf.urls import url from django.urls import path
from propositions.views import PropDelete from propositions.views import PropDelete
from propositions import views from propositions import views
app_name = "propositions"
urlpatterns = [ urlpatterns = [
url(r'^$', views.liste, name='propositions-liste'), path("", views.liste, name="list"),
url(r'^new/?$', views.create_prop, name='propositions-create'), path("new", views.create_prop, name="create"),
url(r'^(?P<id>\d+)/oui/?$', views.repoui, name='propositions-oui'), path("<int:id>/oui", views.repoui, name="oui"),
url(r'^(?P<id>\d+)/non/?$', views.repnon, name='propositions-non'), path("<int:id>/non", views.repnon, name="non"),
url(r'^(?P<pk>\d+)/supprimer/?$', PropDelete.as_view(), path("<int:pk>/supprimer", PropDelete.as_view(), name="delete"),
name='propositions-delete'),
] ]

View file

@ -1,5 +1,5 @@
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.core.urlresolvers import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.generic import DeleteView from django.views.generic import DeleteView
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator

View file

@ -1,4 +1,4 @@
Django==1.8.* Django==2.0.*
# Pour la prod # Pour la prod
psycopg2 psycopg2

View file

@ -1,847 +0,0 @@
/*
DJANGO Admin styles
*/
body {
margin: 0;
padding: 0;
font-size: 12px;
font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
color: #333;
background: #fff;
}
/* LINKS */
a:link, a:visited {
color: #5b80b2;
text-decoration: none;
}
a:hover {
color: #036;
}
a img {
border: none;
}
a.section:link, a.section:visited {
color: #fff;
text-decoration: none;
}
/* GLOBAL DEFAULTS */
p, ol, ul, dl {
margin: .2em 0 .8em 0;
}
p {
padding: 0;
line-height: 140%;
}
h1,h2,h3,h4,h5 {
font-weight: bold;
}
h1 {
font-size: 18px;
color: #666;
padding: 0 6px 0 0;
margin: 0 0 .2em 0;
}
h2 {
font-size: 16px;
margin: 1em 0 .5em 0;
}
h2.subhead {
font-weight: normal;
margin-top: 0;
}
h3 {
font-size: 14px;
margin: .8em 0 .3em 0;
color: #666;
font-weight: bold;
}
h4 {
font-size: 12px;
margin: 1em 0 .8em 0;
padding-bottom: 3px;
}
h5 {
font-size: 10px;
margin: 1.5em 0 .5em 0;
color: #666;
text-transform: uppercase;
letter-spacing: 1px;
}
ul li {
list-style-type: square;
padding: 1px 0;
}
ul.plainlist {
margin-left: 0 !important;
}
ul.plainlist li {
list-style-type: none;
}
li ul {
margin-bottom: 0;
}
li, dt, dd {
font-size: 11px;
line-height: 14px;
}
dt {
font-weight: bold;
margin-top: 4px;
}
dd {
margin-left: 0;
}
form {
margin: 0;
padding: 0;
}
fieldset {
margin: 0;
padding: 0;
}
blockquote {
font-size: 11px;
color: #777;
margin-left: 2px;
padding-left: 10px;
border-left: 5px solid #ddd;
}
code, pre {
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
background: inherit;
color: #666;
font-size: 11px;
}
pre.literal-block {
margin: 10px;
background: #eee;
padding: 6px 8px;
}
code strong {
color: #930;
}
hr {
clear: both;
color: #eee;
background-color: #eee;
height: 1px;
border: none;
margin: 0;
padding: 0;
font-size: 1px;
line-height: 1px;
}
/* TEXT STYLES & MODIFIERS */
.small {
font-size: 11px;
}
.tiny {
font-size: 10px;
}
p.tiny {
margin-top: -2px;
}
.mini {
font-size: 9px;
}
p.mini {
margin-top: -3px;
}
.help, p.help {
font-size: 10px !important;
color: #999;
}
img.help-tooltip {
cursor: help;
}
p img, h1 img, h2 img, h3 img, h4 img, td img {
vertical-align: middle;
}
.quiet, a.quiet:link, a.quiet:visited {
color: #999 !important;
font-weight: normal !important;
}
.quiet strong {
font-weight: bold !important;
}
.float-right {
float: right;
}
.float-left {
float: left;
}
.clear {
clear: both;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.example {
margin: 10px 0;
padding: 5px 10px;
background: #efefef;
}
.nowrap {
white-space: nowrap;
}
/* TABLES */
table {
border-collapse: collapse;
border-color: #ccc;
}
td, th {
font-size: 11px;
line-height: 13px;
border-bottom: 1px solid #eee;
vertical-align: top;
padding: 5px;
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
}
th {
text-align: left;
font-size: 12px;
font-weight: bold;
}
thead th,
tfoot td {
color: #666;
padding: 2px 5px;
font-size: 11px;
background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
border-left: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
tfoot td {
border-bottom: none;
border-top: 1px solid #ddd;
}
thead th:first-child,
tfoot td:first-child {
border-left: none !important;
}
thead th.optional {
font-weight: normal !important;
}
fieldset table {
border-right: 1px solid #eee;
}
tr.row-label td {
font-size: 9px;
padding-top: 2px;
padding-bottom: 0;
border-bottom: none;
color: #666;
margin-top: -1px;
}
tr.alt {
background: #f6f6f6;
}
.row1 {
background: #EDF3FE;
}
.row2 {
background: #fff;
}
/* SORTABLE TABLES */
thead th {
padding: 2px 5px;
line-height: normal;
}
thead th a:link, thead th a:visited {
color: #666;
}
thead th.sorted {
background: #c5c5c5 url(../img/nav-bg-selected.gif) top left repeat-x;
}
thead th.sorted .text {
padding-right: 42px;
}
table thead th .text span {
padding: 2px 5px;
display:block;
}
table thead th .text a {
display: block;
cursor: pointer;
padding: 2px 5px;
}
table thead th.sortable:hover {
background: #fff url(../img/nav-bg-reverse.gif) 0 -5px repeat-x;
}
thead th.sorted a.sortremove {
visibility: hidden;
}
table thead th.sorted:hover a.sortremove {
visibility: visible;
}
table thead th.sorted .sortoptions {
display: block;
padding: 4px 5px 0 5px;
float: right;
text-align: right;
}
table thead th.sorted .sortpriority {
font-size: .8em;
min-width: 12px;
text-align: center;
vertical-align: top;
}
table thead th.sorted .sortoptions a {
width: 14px;
height: 12px;
display: inline-block;
}
table thead th.sorted .sortoptions a.sortremove {
background: url(../img/sorting-icons.gif) -4px -5px no-repeat;
}
table thead th.sorted .sortoptions a.sortremove:hover {
background: url(../img/sorting-icons.gif) -4px -27px no-repeat;
}
table thead th.sorted .sortoptions a.ascending {
background: url(../img/sorting-icons.gif) -5px -50px no-repeat;
}
table thead th.sorted .sortoptions a.ascending:hover {
background: url(../img/sorting-icons.gif) -5px -72px no-repeat;
}
table thead th.sorted .sortoptions a.descending {
background: url(../img/sorting-icons.gif) -5px -94px no-repeat;
}
table thead th.sorted .sortoptions a.descending:hover {
background: url(../img/sorting-icons.gif) -5px -115px no-repeat;
}
/* ORDERABLE TABLES */
table.orderable tbody tr td:hover {
cursor: move;
}
table.orderable tbody tr td:first-child {
padding-left: 14px;
background-image: url(../img/nav-bg-grabber.gif);
background-repeat: repeat-y;
}
table.orderable-initalized .order-cell, body>tr>td.order-cell {
display: none;
}
/* FORM DEFAULTS */
input, textarea, select, .form-row p {
margin: 2px 0;
padding: 2px 3px;
vertical-align: middle;
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
font-weight: normal;
font-size: 11px;
}
textarea {
vertical-align: top !important;
}
input[type=text], input[type=password], input[type=email], input[type=url], input[type=number],
textarea, select, .vTextField {
border: 1px solid #ccc;
}
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input {
background: #fff url(../img/nav-bg.gif) bottom repeat-x;
padding: 3px 5px;
color: black;
border: 1px solid #bbb;
border-color: #ddd #aaa #aaa #ddd;
}
.button:active, input[type=submit]:active, input[type=button]:active {
background-image: url(../img/nav-bg-reverse.gif);
background-position: top;
}
.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
background-image: url(../img/nav-bg.gif);
background-position: bottom;
opacity: 0.4;
}
.button.default, input[type=submit].default, .submit-row input.default {
border: 2px solid #5b80b2;
background: #7CA0C7 url(../img/default-bg.gif) bottom repeat-x;
font-weight: bold;
color: #fff;
float: right;
}
.button.default:active, input[type=submit].default:active {
background-image: url(../img/default-bg-reverse.gif);
background-position: top;
}
.button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default {
background-image: url(../img/default-bg.gif);
background-position: bottom;
opacity: 0.4;
}
/* MODULES */
.module {
border: 1px solid #ccc;
margin-bottom: 5px;
background: #fff;
}
.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
padding-left: 10px;
padding-right: 10px;
}
.module blockquote {
margin-left: 12px;
}
.module ul, .module ol {
margin-left: 1.5em;
}
.module h3 {
margin-top: .6em;
}
.module h2, .module caption, .inline-group h2 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 11px;
text-align: left;
font-weight: bold;
background: #7CA0C7 url(../img/default-bg.gif) top left repeat-x;
color: #fff;
}
.module table {
border-collapse: collapse;
}
/* MESSAGES & ERRORS */
ul.messagelist {
padding: 0;
margin: 0;
}
ul.messagelist li {
font-size: 12px;
font-weight: bold;
display: block;
padding: 5px 5px 4px 25px;
margin: 0 0 3px 0;
border-bottom: 1px solid #ddd;
color: #666;
background: #dfd url(../img/icon_success.gif) 5px .3em no-repeat;
}
ul.messagelist li.warning {
background: #ffc url(../img/icon_alert.gif) 5px .3em no-repeat;
}
ul.messagelist li.error {
background: #ffefef url(../img/icon_error.gif) 5px .3em no-repeat;
}
.errornote {
font-size: 12px !important;
font-weight: bold;
display: block;
padding: 5px 5px 4px 25px;
margin: 0 0 3px 0;
border: 1px solid #c22;
color: #c11;
background: #ffefef url(../img/icon_error.gif) 5px .38em no-repeat;
}
.errornote, ul.errorlist {
border-radius: 1px;
}
ul.errorlist {
margin: 0 0 4px !important;
padding: 0 !important;
color: #fff;
background: #c11;
}
ul.errorlist li {
font-size: 12px !important;
display: block;
padding: 5px 5px 4px 7px;
margin: 3px 0 0 0;
}
ul.errorlist li:first-child {
margin-top: 0;
}
ul.errorlist li a {
color: #fff;
text-decoration: underline;
}
td ul.errorlist {
margin: 0 !important;
padding: 0 !important;
}
td ul.errorlist li {
margin: 0 !important;
}
.errors, .form-row.errors {
background: #ffefef;
}
.form-row.errors {
border: 1px solid #c22;
margin: -1px;
}
.errors input, .errors select, .errors textarea {
border: 1px solid #c11;
}
div.system-message {
background: #ffc;
margin: 10px;
padding: 6px 8px;
font-size: .8em;
}
div.system-message p.system-message-title {
padding: 4px 5px 4px 25px;
margin: 0;
color: #c11;
background: #ffefef url(../img/icon_error.gif) 5px .3em no-repeat;
}
.description {
font-size: 12px;
padding: 5px 0 0 12px;
}
/* BREADCRUMBS */
div.breadcrumbs {
background: #fff url(../img/nav-bg-reverse.gif) 0 -10px repeat-x;
padding: 2px 8px 3px 8px;
font-size: 11px;
color: #999;
border-top: 1px solid #fff;
border-bottom: 1px solid #ddd;
text-align: left;
}
/* ACTION ICONS */
.addlink {
padding-left: 12px;
background: url(../img/icon_addlink.gif) 0 .2em no-repeat;
}
.changelink {
padding-left: 12px;
background: url(../img/icon_changelink.gif) 0 .2em no-repeat;
}
.deletelink {
padding-left: 12px;
background: url(../img/icon_deletelink.gif) 0 .25em no-repeat;
}
a.deletelink:link, a.deletelink:visited {
color: #CC3434;
}
a.deletelink:hover {
color: #993333;
}
/* OBJECT TOOLS */
.object-tools {
font-size: 10px;
font-weight: bold;
font-family: Arial,Helvetica,sans-serif;
padding-left: 0;
float: right;
position: relative;
margin-top: -2.4em;
margin-bottom: -2em;
}
.form-row .object-tools {
margin-top: 5px;
margin-bottom: 5px;
float: none;
height: 2em;
padding-left: 3.5em;
}
.object-tools li {
display: block;
float: left;
margin-left: 5px;
height: 16px;
}
.object-tools a {
border-radius: 15px;
}
.object-tools a:link, .object-tools a:visited {
display: block;
float: left;
color: #fff;
padding: .2em 10px;
background: #999;
}
.object-tools a:hover, .object-tools li:hover a {
background-color: #5b80b2;
}
.object-tools a.viewsitelink, .object-tools a.golink {
background: #999 url(../img/tooltag-arrowright.png) 95% center no-repeat;
padding-right: 26px;
}
.object-tools a.addlink {
background: #999 url(../img/tooltag-add.png) 95% center no-repeat;
padding-right: 26px;
}
/* OBJECT HISTORY */
table#change-history {
width: 100%;
}
table#change-history tbody th {
width: 16em;
}
/* PAGE STRUCTURE */
#container {
position: relative;
width: 100%;
min-width: 760px;
padding: 0;
}
#content {
margin: 10px 15px;
}
#content-main {
float: left;
width: 100%;
}
#content-related {
float: right;
width: 18em;
position: relative;
margin-right: -19em;
}
#footer {
clear: both;
padding: 10px;
}
/* COLUMN TYPES */
.colMS {
margin-right: 20em !important;
}
.colSM {
margin-left: 20em !important;
}
.colSM #content-related {
float: left;
margin-right: 0;
margin-left: -19em;
}
.colSM #content-main {
float: right;
}
.popup .colM {
width: 95%;
}
.subcol {
float: left;
width: 46%;
margin-right: 15px;
}
.dashboard #content {
width: 500px;
}
/* HEADER */
#header {
width: 100%;
background: #417690;
color: #ffc;
overflow: hidden;
}
#header a:link, #header a:visited {
color: #fff;
}
#header a:hover {
text-decoration: underline;
}
#branding {
float: left;
}
#branding h1 {
padding: 0 10px;
font-size: 18px;
margin: 8px 0;
font-weight: normal;
}
#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: #f4f379;
}
#branding h2 {
padding: 0 10px;
font-size: 14px;
margin: -8px 0 8px 0;
font-weight: normal;
color: #ffc;
}
#branding a:hover {
text-decoration: none;
}
#user-tools {
float: right;
padding: 1.2em 10px;
font-size: 11px;
text-align: right;
}
/* SIDEBAR */
#content-related h3 {
font-size: 12px;
color: #666;
margin-bottom: 3px;
}
#content-related h4 {
font-size: 11px;
}
#content-related .module h2 {
background: #eee url(../img/nav-bg.gif) bottom left repeat-x;
color: #666;
}

View file

@ -1,293 +0,0 @@
/* CHANGELISTS */
#changelist {
position: relative;
width: 100%;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: 1px solid #ddd;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered {
background: white url(../img/changelist-bg.gif) top right repeat-y !important;
}
.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
margin-right: 160px !important;
width: auto !important;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist-form .results {
overflow-x: auto;
}
#changelist .toplinks {
border-bottom: 1px solid #ccc !important;
}
#changelist .paginator {
color: #666;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
background: white url(../img/nav-bg.gif) 0 180% repeat-x;
overflow: hidden;
}
.change-list .filtered .paginator {
border-right: 1px solid #ddd;
}
/* CHANGELIST TABLES */
#changelist table thead th {
padding: 0;
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td, #changelist table tbody th {
border-left: 1px solid #ddd;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-left: 0;
border-right: 1px solid #ddd;
}
#changelist table tbody td.action-checkbox {
text-align:center;
}
#changelist table tfoot {
color: #666;
}
/* TOOLBAR */
#changelist #toolbar {
padding: 3px;
border-bottom: 1px solid #ddd;
background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
color: #666;
}
#changelist #toolbar form input {
font-size: 11px;
padding: 1px 2px;
}
#changelist #toolbar form #searchbar {
padding: 2px;
}
#changelist #changelist-search img {
vertical-align: middle;
}
/* FILTER COLUMN */
#changelist-filter {
position: absolute;
top: 0;
right: 0;
z-index: 1000;
width: 160px;
border-left: 1px solid #ddd;
background: #efefef;
margin: 0;
}
#changelist-filter h2 {
font-size: 11px;
padding: 2px 5px;
border-bottom: 1px solid #ddd;
}
#changelist-filter h3 {
font-size: 12px;
margin-bottom: 0;
}
#changelist-filter ul {
padding-left: 0;
margin-left: 10px;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
color: #999;
}
#changelist-filter a:hover {
color: #036;
}
#changelist-filter li.selected {
border-left: 5px solid #ccc;
padding-left: 5px;
margin-left: -10px;
}
#changelist-filter li.selected a {
color: #5b80b2 !important;
}
/* DATE DRILLDOWN */
.change-list ul.toplinks {
display: block;
background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x;
border-top: 1px solid white;
float: left;
padding: 0 !important;
margin: 0 !important;
width: 100%;
}
.change-list ul.toplinks li {
padding: 3px 6px;
font-weight: bold;
list-style-type: none;
display: inline-block;
}
.change-list ul.toplinks .date-back a {
color: #999;
}
.change-list ul.toplinks .date-back a:hover {
color: #036;
}
/* PAGINATOR */
.paginator {
font-size: 11px;
padding-top: 10px;
padding-bottom: 10px;
line-height: 22px;
margin: 0;
border-top: 1px solid #ddd;
}
.paginator a:link, .paginator a:visited {
padding: 2px 6px;
border: solid 1px #ccc;
background: white;
text-decoration: none;
}
.paginator a.showall {
padding: 0 !important;
border: none !important;
}
.paginator a.showall:hover {
color: #036 !important;
background: transparent !important;
}
.paginator .end {
border-width: 2px !important;
margin-right: 6px;
}
.paginator .this-page {
padding: 2px 6px;
font-weight: bold;
font-size: 13px;
vertical-align: top;
}
.paginator a:hover {
color: white;
background: #5b80b2;
border-color: #036;
}
/* ACTIONS */
.filtered .actions {
margin-right: 160px !important;
border-right: 1px solid #ddd;
}
#changelist table input {
margin: 0;
}
#changelist table tbody tr.selected {
background-color: #FFFFCC;
}
#changelist .actions {
color: #999;
padding: 3px;
border-top: 1px solid #fff;
border-bottom: 1px solid #ddd;
background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x;
}
#changelist .actions.selected {
background: #fffccf;
border-top: 1px solid #fffee8;
border-bottom: 1px solid #edecd6;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 11px;
margin: 0 0.5em;
display: none;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
border: 1px solid #aaa;
margin-left: 0.5em;
padding: 1px 2px;
}
#changelist .actions label {
font-size: 11px;
margin-left: 0.5em;
}
#changelist #action-toggle {
display: none;
}
#changelist .actions .button {
font-size: 11px;
padding: 1px 2px;
}

View file

@ -1,30 +0,0 @@
/* DASHBOARD */
.dashboard .module table th {
width: 100%;
}
.dashboard .module table td {
white-space: nowrap;
}
.dashboard .module table td a {
display: block;
padding-right: .6em;
}
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
margin-left: 0;
}
ul.actionlist li {
list-style-type: none;
}
ul.actionlist li {
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}

View file

@ -1,376 +0,0 @@
@import url('widgets.css');
/* FORM ROWS */
.form-row {
overflow: hidden;
padding: 8px 12px;
font-size: 11px;
border-bottom: 1px solid #eee;
}
.form-row img, .form-row input {
vertical-align: middle;
}
form .form-row p {
padding-left: 0;
font-size: 11px;
}
.hidden {
display: none;
}
/* FORM LABELS */
form h4 {
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
label {
font-weight: normal !important;
color: #666;
font-size: 12px;
}
.required label, label.required {
font-weight: bold !important;
color: #333 !important;
}
/* RADIO BUTTONS */
form ul.radiolist li {
list-style-type: none;
}
form ul.radiolist label {
float: none;
display: inline;
}
form ul.inline {
margin-left: 0;
padding: 0;
}
form ul.inline li {
float: left;
padding-right: 7px;
}
/* ALIGNED FIELDSETS */
.aligned label {
display: block;
padding: 3px 10px 0 0;
float: left;
width: 8em;
word-wrap: break-word;
}
.aligned ul label {
display: inline;
float: none;
width: auto;
}
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
width: 350px;
}
form .aligned p, form .aligned ul {
margin-left: 7em;
padding-left: 30px;
}
form .aligned table p {
margin-left: 0;
padding-left: 0;
}
form .aligned p.help {
padding-left: 38px;
}
.aligned .vCheckboxLabel {
float: none !important;
display: inline;
padding-left: 4px;
}
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
width: 610px;
}
.checkbox-row p.help {
margin-left: 0;
padding-left: 0 !important;
}
fieldset .field-box {
float: left;
margin-right: 20px;
}
/* WIDE FIELDSETS */
.wide label {
width: 15em !important;
}
form .wide p {
margin-left: 15em;
}
form .wide p.help {
padding-left: 38px;
}
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
width: 450px;
}
/* COLLAPSED FIELDSETS */
fieldset.collapsed * {
display: none;
}
fieldset.collapsed h2, fieldset.collapsed {
display: block !important;
}
fieldset.collapsed h2 {
background-image: url(../img/nav-bg.gif);
background-position: bottom left;
color: #999;
}
fieldset.collapsed .collapse-toggle {
background: transparent;
display: inline !important;
}
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace;
}
/* SUBMIT ROW */
.submit-row {
padding: 5px 7px;
text-align: right;
background: white url(../img/nav-bg.gif) 0 100% repeat-x;
border: 1px solid #ccc;
margin: 5px 0;
overflow: hidden;
}
body.popup .submit-row {
overflow: auto;
}
.submit-row input {
margin: 0 0 0 5px;
}
.submit-row p {
margin: 0.3em;
}
.submit-row p.deletelink-box {
float: left;
}
.submit-row .deletelink {
background: url(../img/icon_deletelink.gif) 0 50% no-repeat;
padding-left: 14px;
}
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
vertical-align: top !important;
}
.vCheckboxField {
border: none;
}
.vDateField, .vTimeField {
margin-right: 2px;
}
.vDateField {
min-width: 6.85em;
}
.vTimeField {
min-width: 4.7em;
}
.vURLField {
width: 30em;
}
.vLargeTextField, .vXMLLargeTextField {
width: 48em;
}
.flatpages-flatpage #id_content {
height: 40.2em;
}
.module table .vPositiveSmallIntegerField {
width: 2.2em;
}
.vTextField {
width: 20em;
}
.vIntegerField {
width: 5em;
}
.vBigIntegerField {
width: 10em;
}
.vForeignKeyRawIdAdminField {
width: 5em;
}
/* INLINES */
.inline-group {
padding: 0;
border: 1px solid #ccc;
margin: 10px 0;
}
.inline-group .aligned label {
width: 8em;
}
.inline-related {
position: relative;
}
.inline-related h3 {
margin: 0;
color: #666;
padding: 3px 5px;
font-size: 11px;
background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
border-bottom: 1px solid #ddd;
}
.inline-related h3 span.delete {
float: right;
}
.inline-related h3 span.delete label {
margin-left: 2px;
font-size: 11px;
}
.inline-related fieldset {
margin: 0;
background: #fff;
border: none;
width: 100%;
}
.inline-related fieldset.module h3 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 11px;
text-align: left;
font-weight: bold;
background: #bcd;
color: #fff;
}
.inline-group .tabular fieldset.module {
border: none;
border-bottom: 1px solid #ddd;
}
.inline-related.tabular fieldset.module table {
width: 100%;
}
.last-related fieldset {
border: none;
}
.inline-group .tabular tr.has_original td {
padding-top: 2em;
}
.inline-group .tabular tr td.original {
padding: 2px 0 0 0;
width: 0;
_position: relative;
}
.inline-group .tabular th.original {
width: 0px;
padding: 0;
}
.inline-group .tabular td.original p {
position: absolute;
left: 0;
height: 1.1em;
padding: 2px 7px;
overflow: hidden;
font-size: 9px;
font-weight: bold;
color: #666;
_width: 700px;
}
.inline-group ul.tools {
padding: 0;
margin: 0;
list-style: none;
}
.inline-group ul.tools li {
display: inline;
padding: 0 5px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
color: #666;
padding: 3px 5px;
border-bottom: 1px solid #ddd;
background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
}
.inline-group .tabular tr.add-row td {
padding: 4px 5px 3px;
border-bottom: none;
}
.inline-group ul.tools a.add,
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
background: url(../img/icon_addlink.gif) 0 50% no-repeat;
padding-left: 14px;
font-size: 11px;
outline: 0; /* Remove dotted border around link */
}
.empty-form {
display: none;
}

View file

@ -1,63 +0,0 @@
/* IE 6 & 7 */
/* Proper fixed width for dashboard in IE6 */
.dashboard #content {
*width: 768px;
}
.dashboard #content-main {
*width: 535px;
}
/* IE 6 ONLY */
/* Keep header from flowing off the page */
#container {
_position: static;
}
/* Put the right sidebars back on the page */
.colMS #content-related {
_margin-right: 0;
_margin-left: 10px;
_position: static;
}
/* Put the left sidebars back on the page */
.colSM #content-related {
_margin-right: 10px;
_margin-left: -115px;
_position: static;
}
.form-row {
_height: 1%;
}
/* Fix right margin for changelist filters in IE6 */
#changelist-filter ul {
_margin-right: -10px;
}
/* IE ignores min-height, but treats height as if it were min-height */
.change-list .filtered {
_height: 400px;
}
/* IE doesn't know alpha transparency in PNGs */
.inline-deletelink {
background: transparent url(../img/inline-delete-8bit.png) no-repeat;
}
/* IE7 doesn't support inline-block */
.change-list ul.toplinks li {
zoom: 1;
*display: inline;
}

View file

@ -1,60 +0,0 @@
/* LOGIN FORM */
body.login {
background: #eee;
}
.login #container {
background: white;
border: 1px solid #ccc;
width: 28em;
min-width: 300px;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
}
.login #content-main {
width: 100%;
}
.login form {
margin-top: 1em;
}
.login .form-row {
padding: 4px 0;
float: left;
width: 100%;
}
.login .form-row label {
padding-right: 0.5em;
line-height: 2em;
font-size: 1em;
clear: both;
color: #333;
}
.login .form-row #id_username, .login .form-row #id_password {
clear: both;
padding: 6px;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.login span.help {
font-size: 10px;
display: block;
}
.login .submit-row {
clear: both;
padding: 1em 0 0 9.4em;
}
.login .password-reset-link {
text-align: center;
}

View file

@ -1,250 +0,0 @@
body {
direction: rtl;
}
/* LOGIN */
.login .form-row {
float: right;
}
.login .form-row label {
float: right;
padding-left: 0.5em;
padding-right: 0;
text-align: left;
}
.login .submit-row {
clear: both;
padding: 1em 9.4em 0 0;
}
/* GLOBAL */
th {
text-align: right;
}
.module h2, .module caption {
text-align: right;
}
.addlink, .changelink {
padding-left: 0px;
padding-right: 12px;
background-position: 100% 0.2em;
}
.deletelink {
padding-left: 0px;
padding-right: 12px;
background-position: 100% 0.25em;
}
.object-tools {
float: left;
}
thead th:first-child,
tfoot td:first-child {
border-left: 1px solid #ddd !important;
}
/* LAYOUT */
#user-tools {
right: auto;
left: 0;
text-align: left;
}
div.breadcrumbs {
text-align: right;
}
#content-main {
float: right;
}
#content-related {
float: left;
margin-left: -19em;
margin-right: auto;
}
.colMS {
margin-left: 20em !important;
margin-right: 10px !important;
}
/* SORTABLE TABLES */
table thead th.sorted .sortoptions {
float: left;
}
thead th.sorted .text {
padding-right: 0;
padding-left: 42px;
}
/* dashboard styles */
.dashboard .module table td a {
padding-left: .6em;
padding-right: 12px;
}
/* changelists styles */
.change-list .filtered {
background: white url(../img/changelist-bg_rtl.gif) top left repeat-y !important;
}
.change-list .filtered table {
border-left: 1px solid #ddd;
border-right: 0px none;
}
#changelist-filter {
right: auto;
left: 0;
border-left: 0px none;
border-right: 1px solid #ddd;
}
.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
margin-right: 0px !important;
margin-left: 160px !important;
}
#changelist-filter li.selected {
border-left: 0px none;
padding-left: 0px;
margin-left: 0;
border-right: 5px solid #ccc;
padding-right: 5px;
margin-right: -10px;
}
.filtered .actions {
border-left:1px solid #DDDDDD;
margin-left:160px !important;
border-right: 0 none;
margin-right:0 !important;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-right: 0;
border-left: 1px solid #ddd;
}
/* FORMS */
.aligned label {
padding: 0 0 3px 1em;
float: right;
}
.submit-row {
text-align: left
}
.submit-row p.deletelink-box {
float: right;
}
.submit-row .deletelink {
background: url(../img/icon_deletelink.gif) 0 50% no-repeat;
padding-right: 14px;
}
.vDateField, .vTimeField {
margin-left: 2px;
}
form ul.inline li {
float: right;
padding-right: 0;
padding-left: 7px;
}
input[type=submit].default, .submit-row input.default {
float: left;
}
fieldset .field-box {
float: right;
margin-left: 20px;
margin-right: 0;
}
.errorlist li {
background-position: 100% .3em;
padding: 4px 25px 4px 5px;
}
.errornote {
background-position: 100% .3em;
padding: 4px 25px 4px 5px;
}
/* WIDGETS */
.calendarnav-previous {
top: 0;
left: auto;
right: 0;
}
.calendarnav-next {
top: 0;
right: auto;
left: 0;
}
.calendar caption, .calendarbox h2 {
text-align: center;
}
.selector {
float: right;
}
.selector .selector-filter {
text-align: right;
}
.inline-deletelink {
float: left;
}
/* MISC */
.inline-related h2, .inline-group h2 {
text-align: right
}
.inline-related h3 span.delete {
padding-right: 20px;
padding-left: inherit;
left: 10px;
right: inherit;
float:left;
}
.inline-related h3 span.delete label {
margin-left: inherit;
margin-right: 2px;
}
/* IE7 specific bug fixes */
div.colM {
position: relative;
}
.submit-row input {
float: left;
}

View file

@ -1,578 +0,0 @@
/* SELECTOR (FILTER INTERFACE) */
.selector {
width: 840px;
float: left;
}
.selector select {
width: 400px;
height: 17.2em;
}
.selector-available, .selector-chosen {
float: left;
width: 400px;
text-align: center;
margin-bottom: 5px;
}
.selector-chosen select {
border-top: none;
}
.selector-available h2, .selector-chosen h2 {
border: 1px solid #ccc;
}
.selector .selector-available h2 {
background: white url(../img/nav-bg.gif) bottom left repeat-x;
color: #666;
}
.selector .selector-filter {
background: white;
border: 1px solid #ccc;
border-width: 0 1px;
padding: 3px;
color: #999;
font-size: 10px;
margin: 0;
text-align: left;
}
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
width: 16px;
padding: 2px;
}
.selector .selector-available input {
width: 360px;
}
.selector ul.selector-chooser {
float: left;
width: 22px;
background-color: #eee;
border-radius: 10px;
margin: 10em 5px 0 5px;
padding: 0;
}
.selector-chooser li {
margin: 0;
padding: 3px;
list-style-type: none;
}
.selector select {
margin-bottom: 10px;
margin-top: 0;
}
.selector-add, .selector-remove {
width: 16px;
height: 16px;
display: block;
text-indent: -3000px;
overflow: hidden;
}
.selector-add {
background: url(../img/selector-icons.gif) 0 -161px no-repeat;
cursor: default;
margin-bottom: 2px;
}
.active.selector-add {
background: url(../img/selector-icons.gif) 0 -187px no-repeat;
cursor: pointer;
}
.selector-remove {
background: url(../img/selector-icons.gif) 0 -109px no-repeat;
cursor: default;
}
.active.selector-remove {
background: url(../img/selector-icons.gif) 0 -135px no-repeat;
cursor: pointer;
}
a.selector-chooseall, a.selector-clearall {
display: inline-block;
text-align: left;
margin-left: auto;
margin-right: auto;
font-weight: bold;
color: #666;
}
a.selector-chooseall {
padding: 3px 18px 3px 0;
}
a.selector-clearall {
padding: 3px 0 3px 18px;
}
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
color: #036;
}
a.selector-chooseall {
background: url(../img/selector-icons.gif) right -263px no-repeat;
cursor: default;
}
a.active.selector-chooseall {
background: url(../img/selector-icons.gif) right -289px no-repeat;
cursor: pointer;
}
a.selector-clearall {
background: url(../img/selector-icons.gif) left -211px no-repeat;
cursor: default;
}
a.active.selector-clearall {
background: url(../img/selector-icons.gif) left -237px no-repeat;
cursor: pointer;
}
/* STACKED SELECTORS */
.stacked {
float: left;
width: 500px;
}
.stacked select {
width: 480px;
height: 10.1em;
}
.stacked .selector-available, .stacked .selector-chosen {
width: 480px;
}
.stacked .selector-available {
margin-bottom: 0;
}
.stacked .selector-available input {
width: 442px;
}
.stacked ul.selector-chooser {
height: 22px;
width: 50px;
margin: 0 0 3px 40%;
background-color: #eee;
border-radius: 10px;
}
.stacked .selector-chooser li {
float: left;
padding: 3px 3px 3px 5px;
}
.stacked .selector-chooseall, .stacked .selector-clearall {
display: none;
}
.stacked .selector-add {
background: url(../img/selector-icons.gif) 0 -57px no-repeat;
cursor: default;
}
.stacked .active.selector-add {
background: url(../img/selector-icons.gif) 0 -83px no-repeat;
cursor: pointer;
}
.stacked .selector-remove {
background: url(../img/selector-icons.gif) 0 -5px no-repeat;
cursor: default;
}
.stacked .active.selector-remove {
background: url(../img/selector-icons.gif) 0 -31px no-repeat;
cursor: pointer;
}
/* DATE AND TIME */
p.datetime {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.datetime span {
font-size: 11px;
color: #ccc;
font-weight: normal;
white-space: nowrap;
}
table p.datetime {
font-size: 10px;
margin-left: 0;
padding-left: 0;
}
/* URL */
p.url {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.url a {
font-weight: normal;
}
/* FILE UPLOADS */
p.file-upload {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.file-upload a {
font-weight: normal;
}
.file-upload .deletelink {
margin-left: 5px;
}
span.clearable-file-input label {
color: #333;
font-size: 11px;
display: inline;
float: none;
}
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
margin: 5px auto;
font-size: 11px;
width: 16em;
text-align: center;
background: white;
position: relative;
}
.clockbox {
width: auto;
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 100%;
}
.calendar caption, .calendarbox h2 {
margin: 0;
font-size: 11px;
text-align: center;
border-top: none;
}
.calendar th {
font-size: 10px;
color: #666;
padding: 2px 3px;
text-align: center;
background: #e1e1e1 url(../img/nav-bg.gif) 0 50% repeat-x;
border-bottom: 1px solid #ddd;
}
.calendar td {
font-size: 11px;
text-align: center;
padding: 0;
border-top: 1px solid #eee;
border-bottom: none;
}
.calendar td.selected a {
background: #C9DBED;
}
.calendar td.nonday {
background: #efefef;
}
.calendar td.today a {
background: #ffc;
}
.calendar td a, .timelist a {
display: block;
font-weight: bold;
padding: 4px;
text-decoration: none;
color: #444;
}
.calendar td a:hover, .timelist a:hover {
background: #5b80b2;
color: white;
}
.calendar td a:active, .timelist a:active {
background: #036;
color: white;
}
.calendarnav {
font-size: 10px;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover {
color: #999;
}
.calendar-shortcuts {
background: white;
font-size: 10px;
line-height: 11px;
border-top: 1px solid #eee;
padding: 3px 0 4px;
color: #ccc;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
display: block;
position: absolute;
font-weight: bold;
font-size: 12px;
background: #C9DBED url(../img/default-bg.gif) bottom left repeat-x;
padding: 1px 4px 2px 4px;
color: white;
}
.calendarnav-previous:hover, .calendarnav-next:hover {
background: #036;
}
.calendarnav-previous {
top: 0;
left: 0;
}
.calendarnav-next {
top: 0;
right: 0;
}
.calendar-cancel {
margin: 0 !important;
padding: 0 !important;
font-size: 10px;
background: #e1e1e1 url(../img/nav-bg.gif) 0 50% repeat-x;
border-top: 1px solid #ddd;
}
.calendar-cancel:hover {
background: #e1e1e1 url(../img/nav-bg-reverse.gif) 0 50% repeat-x;
}
.calendar-cancel a {
color: black;
display: block;
}
ul.timelist, .timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}
/* INLINE ORDERER */
ul.orderer {
position: relative;
padding: 0 !important;
margin: 0 !important;
list-style-type: none;
}
ul.orderer li {
list-style-type: none;
display: block;
padding: 0;
margin: 0;
border: 1px solid #bbb;
border-width: 0 1px 1px 0;
white-space: nowrap;
overflow: hidden;
background: #e2e2e2 url(../img/nav-bg-grabber.gif) repeat-y;
}
ul.orderer li:hover {
cursor: move;
background-color: #ddd;
}
ul.orderer li a.selector {
margin-left: 12px;
overflow: hidden;
width: 83%;
font-size: 10px !important;
padding: 0.6em 0;
}
ul.orderer li a:link, ul.orderer li a:visited {
color: #333;
}
ul.orderer li .inline-deletelink {
position: absolute;
right: 4px;
margin-top: 0.6em;
}
ul.orderer li.selected {
background-color: #f8f8f8;
border-right-color: #f8f8f8;
}
ul.orderer li.deleted {
background: #bbb url(../img/deleted-overlay.gif);
}
ul.orderer li.deleted a:link, ul.orderer li.deleted a:visited {
color: #888;
}
ul.orderer li.deleted .inline-deletelink {
background-image: url(../img/inline-restore.png);
}
ul.orderer li.deleted:hover, ul.orderer li.deleted a.selector:hover {
cursor: default;
}
/* EDIT INLINE */
.inline-deletelink {
float: right;
text-indent: -9999px;
background: transparent url(../img/inline-delete.png) no-repeat;
width: 15px;
height: 15px;
border: 0px none;
outline: 0; /* Remove dotted border around link */
}
.inline-deletelink:hover {
background-position: -15px 0;
cursor: pointer;
}
.editinline button.addlink {
border: 0px none;
color: #5b80b2;
font-size: 100%;
cursor: pointer;
}
.editinline button.addlink:hover {
color: #036;
cursor: pointer;
}
.editinline table .help {
text-align: right;
float: right;
padding-left: 2em;
}
.editinline tfoot .addlink {
white-space: nowrap;
}
.editinline table thead th:last-child {
border-left: none;
}
.editinline tr.deleted {
background: #ddd url(../img/deleted-overlay.gif);
}
.editinline tr.deleted .inline-deletelink {
background-image: url(../img/inline-restore.png);
}
.editinline tr.deleted td:hover {
cursor: default;
}
.editinline tr.deleted td:first-child {
background-image: none !important;
}
/* EDIT INLINE - STACKED */
.editinline-stacked {
min-width: 758px;
}
.editinline-stacked .inline-object {
margin-left: 210px;
background: white;
}
.editinline-stacked .inline-source {
float: left;
width: 200px;
background: #f8f8f8;
}
.editinline-stacked .inline-splitter {
float: left;
width: 9px;
background: #f8f8f8 url(../img/inline-splitter-bg.gif) 50% 50% no-repeat;
border-right: 1px solid #ccc;
}
.editinline-stacked .controls {
clear: both;
background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
padding: 3px 4px;
font-size: 11px;
border-top: 1px solid #ddd;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

View file

@ -1,20 +0,0 @@
Copyright (c) 2010 John Resig, http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,114 +0,0 @@
var SelectBox = {
cache: new Object(),
init: function(id) {
var box = document.getElementById(id);
var node;
SelectBox.cache[id] = new Array();
var cache = SelectBox.cache[id];
for (var i = 0; (node = box.options[i]); i++) {
cache.push({value: node.value, text: node.text, displayed: 1});
}
},
redisplay: function(id) {
// Repopulate HTML select box from cache
var box = document.getElementById(id);
box.options.length = 0; // clear all options
for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {
var node = SelectBox.cache[id][i];
if (node.displayed) {
var new_option = new Option(node.text, node.value, false, false);
// Shows a tooltip when hovering over the option
new_option.setAttribute("title", node.text);
box.options[box.options.length] = new_option;
}
}
},
filter: function(id, text) {
// Redisplay the HTML select box, displaying only the choices containing ALL
// the words in text. (It's an AND search.)
var tokens = text.toLowerCase().split(/\s+/);
var node, token;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
node.displayed = 1;
for (var j = 0; (token = tokens[j]); j++) {
if (node.text.toLowerCase().indexOf(token) == -1) {
node.displayed = 0;
}
}
}
SelectBox.redisplay(id);
},
delete_from_cache: function(id, value) {
var node, delete_index = null;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
if (node.value == value) {
delete_index = i;
break;
}
}
var j = SelectBox.cache[id].length - 1;
for (var i = delete_index; i < j; i++) {
SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
}
SelectBox.cache[id].length--;
},
add_to_cache: function(id, option) {
SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
},
cache_contains: function(id, value) {
// Check if an item is contained in the cache
var node;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
if (node.value == value) {
return true;
}
}
return false;
},
move: function(from, to) {
var from_box = document.getElementById(from);
var to_box = document.getElementById(to);
var option;
for (var i = 0; (option = from_box.options[i]); i++) {
if (option.selected && SelectBox.cache_contains(from, option.value)) {
SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option.value);
}
}
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
move_all: function(from, to) {
var from_box = document.getElementById(from);
var to_box = document.getElementById(to);
var option;
for (var i = 0; (option = from_box.options[i]); i++) {
if (SelectBox.cache_contains(from, option.value)) {
SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option.value);
}
}
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
sort: function(id) {
SelectBox.cache[id].sort( function(a, b) {
a = a.text.toLowerCase();
b = b.text.toLowerCase();
try {
if (a > b) return 1;
if (a < b) return -1;
}
catch (e) {
// silently fail on IE 'unknown' exception
}
return 0;
} );
},
select_all: function(id) {
var box = document.getElementById(id);
for (var i = 0; i < box.options.length; i++) {
box.options[i].selected = 'selected';
}
}
}

View file

@ -1,161 +0,0 @@
/*
SelectFilter2 - Turns a multiple-select box into a filter interface.
Requires core.js, SelectBox.js and addevent.js.
*/
(function($) {
function findForm(node) {
// returns the node of the form containing the given node
if (node.tagName.toLowerCase() != 'form') {
return findForm(node.parentNode);
}
return node;
}
window.SelectFilter = {
init: function(field_id, field_name, is_stacked, admin_static_prefix) {
if (field_id.match(/__prefix__/)){
// Don't intialize on empty forms.
return;
}
var from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID
from_box.className = 'filtered';
var ps = from_box.parentNode.getElementsByTagName('p');
for (var i=0; i<ps.length; i++) {
if (ps[i].className.indexOf("info") != -1) {
// Remove <p class="info">, because it just gets in the way.
from_box.parentNode.removeChild(ps[i]);
} else if (ps[i].className.indexOf("help") != -1) {
// Move help text up to the top so it isn't below the select
// boxes or wrapped off on the side to the right of the add
// button:
from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
}
}
// <div class="selector"> or <div class="selector stacked">
var selector_div = quickElement('div', from_box.parentNode);
selector_div.className = is_stacked ? 'selector stacked' : 'selector';
// <div class="selector-available">
var selector_available = quickElement('div', selector_div);
selector_available.className = 'selector-available';
var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
quickElement('img', title_available, '', 'src', admin_static_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of available %s. You may choose some by selecting them in the box below and then clicking the "Choose" arrow between the two boxes.'), [field_name]));
var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
filter_p.className = 'selector-filter';
var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + "_input");
var search_selector_img = quickElement('img', search_filter_label, '', 'src', admin_static_prefix + 'img/selector-search.gif', 'class', 'help-tooltip', 'alt', '', 'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name]));
filter_p.appendChild(document.createTextNode(' '));
var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
filter_input.id = field_id + '_input';
selector_available.appendChild(from_box);
var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_all_link');
choose_all.className = 'selector-chooseall';
// <ul class="selector-chooser">
var selector_chooser = quickElement('ul', selector_div);
selector_chooser.className = 'selector-chooser';
var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_link');
add_link.className = 'selector-add';
var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_link');
remove_link.className = 'selector-remove';
// <div class="selector-chosen">
var selector_chosen = quickElement('div', selector_div);
selector_chosen.className = 'selector-chosen';
var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
quickElement('img', title_chosen, '', 'src', admin_static_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [field_name]));
var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
to_box.className = 'filtered';
var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_all_link');
clear_all.className = 'selector-clearall';
from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
// Set up the JavaScript event handlers for the select box filter interface
addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
SelectBox.init(field_id + '_from');
SelectBox.init(field_id + '_to');
// Move selected from_box options to to_box
SelectBox.move(field_id + '_from', field_id + '_to');
if (!is_stacked) {
// In horizontal mode, give the same height to the two boxes.
var j_from_box = $(from_box);
var j_to_box = $(to_box);
var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); }
if (j_from_box.outerHeight() > 0) {
resize_filters(); // This fieldset is already open. Resize now.
} else {
// This fieldset is probably collapsed. Wait for its 'show' event.
j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
}
}
// Initial icon refresh
SelectFilter.refresh_icons(field_id);
},
refresh_icons: function(field_id) {
var from = $('#' + field_id + '_from');
var to = $('#' + field_id + '_to');
var is_from_selected = from.find('option:selected').length > 0;
var is_to_selected = to.find('option:selected').length > 0;
// Active if at least one item is selected
$('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
$('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
// Active if the corresponding box isn't empty
$('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
$('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
},
filter_key_up: function(event, field_id) {
var from = document.getElementById(field_id + '_from');
// don't submit form if user pressed Enter
if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
from.selectedIndex = 0;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = 0;
return false;
}
var temp = from.selectedIndex;
SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
from.selectedIndex = temp;
return true;
},
filter_key_down: function(event, field_id) {
var from = document.getElementById(field_id + '_from');
// right arrow -- move across
if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
var old_index = from.selectedIndex;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
return false;
}
// down arrow -- wrap around
if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
}
// up arrow -- wrap around
if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
}
return true;
}
}
})(django.jQuery);

View file

@ -1,144 +0,0 @@
(function($) {
var lastChecked;
$.fn.actions = function(opts) {
var options = $.extend({}, $.fn.actions.defaults, opts);
var actionCheckboxes = $(this);
var list_editable_changed = false;
var checker = function(checked) {
if (checked) {
showQuestion();
} else {
reset();
}
$(actionCheckboxes).prop("checked", checked)
.parent().parent().toggleClass(options.selectedClass, checked);
},
updateCounter = function() {
var sel = $(actionCheckboxes).filter(":checked").length;
// _actions_icnt is defined in the generated HTML
// and contains the total amount of objects in the queryset
$(options.counterContainer).html(interpolate(
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
sel: sel,
cnt: _actions_icnt
}, true));
$(options.allToggle).prop("checked", function() {
var value;
if (sel == actionCheckboxes.length) {
value = true;
showQuestion();
} else {
value = false;
clearAcross();
}
return value;
});
},
showQuestion = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).show();
$(options.allContainer).hide();
},
showClear = function() {
$(options.acrossClears).show();
$(options.acrossQuestions).hide();
$(options.actionContainer).toggleClass(options.selectedClass);
$(options.allContainer).show();
$(options.counterContainer).hide();
},
reset = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).hide();
$(options.allContainer).hide();
$(options.counterContainer).show();
},
clearAcross = function() {
reset();
$(options.acrossInput).val(0);
$(options.actionContainer).removeClass(options.selectedClass);
};
// Show counter by default
$(options.counterContainer).show();
// Check state of checkboxes and reinit state if needed
$(this).filter(":checked").each(function(i) {
$(this).parent().parent().toggleClass(options.selectedClass);
updateCounter();
if ($(options.acrossInput).val() == 1) {
showClear();
}
});
$(options.allToggle).show().click(function() {
checker($(this).prop("checked"));
updateCounter();
});
$("a", options.acrossQuestions).click(function(event) {
event.preventDefault();
$(options.acrossInput).val(1);
showClear();
});
$("a", options.acrossClears).click(function(event) {
event.preventDefault();
$(options.allToggle).prop("checked", false);
clearAcross();
checker(0);
updateCounter();
});
lastChecked = null;
$(actionCheckboxes).click(function(event) {
if (!event) { event = window.event; }
var target = event.target ? event.target : event.srcElement;
if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey === true) {
var inrange = false;
$(lastChecked).prop("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
$(actionCheckboxes).each(function() {
if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {
inrange = (inrange) ? false : true;
}
if (inrange) {
$(this).prop("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
}
});
}
$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
lastChecked = target;
updateCounter();
});
$('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
list_editable_changed = true;
});
$('form#changelist-form button[name="index"]').click(function(event) {
if (list_editable_changed) {
return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
}
});
$('form#changelist-form input[name="_save"]').click(function(event) {
var action_changed = false;
$('select option:selected', options.actionContainer).each(function() {
if ($(this).val()) {
action_changed = true;
}
});
if (action_changed) {
if (list_editable_changed) {
return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action."));
} else {
return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."));
}
}
});
};
/* Setup plugin defaults */
$.fn.actions.defaults = {
actionContainer: "div.actions",
counterContainer: "span.action-counter",
allContainer: "div.actions span.all",
acrossInput: "div.actions input.select-across",
acrossQuestions: "div.actions span.question",
acrossClears: "div.actions span.clear",
allToggle: "#action-toggle",
selectedClass: "selected"
};
})(django.jQuery);

View file

@ -1,6 +0,0 @@
(function(a){var f;a.fn.actions=function(q){var b=a.extend({},a.fn.actions.defaults,q),g=a(this),e=!1,m=function(c){c?k():l();a(g).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){var c=a(g).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).prop("checked",function(){var a;c==g.length?(a=!0,k()):(a=!1,n());return a})},k=function(){a(b.acrossClears).hide();
a(b.acrossQuestions).show();a(b.allContainer).hide()},p=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},n=function(){l();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);
h();1==a(b.acrossInput).val()&&p()});a(b.allToggle).show().click(function(){m(a(this).prop("checked"));h()});a("a",b.acrossQuestions).click(function(c){c.preventDefault();a(b.acrossInput).val(1);p()});a("a",b.acrossClears).click(function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);n();m(0);h()});f=null;a(g).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(f&&a.data(f)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(f).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,
d.checked);a(g).each(function(){if(a.data(this)==a.data(f)||a.data(this)==a.data(d))e=e?!1:!0;e&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);f=d;h()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(a){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});
a('form#changelist-form input[name="_save"]').click(function(c){var d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};
a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);

View file

@ -1,356 +0,0 @@
// Inserts shortcut buttons after all of the following:
// <input type="text" class="vDateField">
// <input type="text" class="vTimeField">
var DateTimeShortcuts = {
calendars: [],
calendarInputs: [],
clockInputs: [],
dismissClockFunc: [],
dismissCalendarFunc: [],
calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
calendarDivName2: 'calendarin', // name of <div> that contains calendar
calendarLinkName: 'calendarlink',// name of the link that is used to toggle
clockDivName: 'clockbox', // name of clock <div> that gets toggled
clockLinkName: 'clocklink', // name of the link that is used to toggle
shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
timezoneOffset: 0,
admin_media_prefix: '',
init: function() {
// Get admin_media_prefix by grabbing it off the window object. It's
// set in the admin/base.html template, so if it's not there, someone's
// overridden the template. In that case, we'll set a clearly-invalid
// value in the hopes that someone will examine HTTP requests and see it.
if (window.__admin_media_prefix__ != undefined) {
DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
} else {
DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
}
if (window.__admin_utc_offset__ != undefined) {
var serverOffset = window.__admin_utc_offset__;
var localOffset = new Date().getTimezoneOffset() * -60;
DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
}
var inputs = document.getElementsByTagName('input');
for (i=0; i<inputs.length; i++) {
var inp = inputs[i];
if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
DateTimeShortcuts.addClock(inp);
DateTimeShortcuts.addTimezoneWarning(inp);
}
else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
DateTimeShortcuts.addCalendar(inp);
DateTimeShortcuts.addTimezoneWarning(inp);
}
}
},
// Return the current time while accounting for the server timezone.
now: function() {
if (window.__admin_utc_offset__ != undefined) {
var serverOffset = window.__admin_utc_offset__;
var localNow = new Date();
var localOffset = localNow.getTimezoneOffset() * -60;
localNow.setTime(localNow.getTime() + 1000 * (serverOffset - localOffset));
return localNow;
} else {
return new Date();
}
},
// Add a warning when the time zone in the browser and backend do not match.
addTimezoneWarning: function(inp) {
var $ = django.jQuery;
var warningClass = DateTimeShortcuts.timezoneWarningClass;
var timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600;
// Only warn if there is a time zone mismatch.
if (!timezoneOffset)
return;
// Check if warning is already there.
if ($(inp).siblings('.' + warningClass).length)
return;
var message;
if (timezoneOffset > 0) {
message = ngettext(
'Note: You are %s hour ahead of server time.',
'Note: You are %s hours ahead of server time.',
timezoneOffset
);
}
else {
timezoneOffset *= -1
message = ngettext(
'Note: You are %s hour behind server time.',
'Note: You are %s hours behind server time.',
timezoneOffset
);
}
message = interpolate(message, [timezoneOffset]);
var $warning = $('<span>');
$warning.attr('class', warningClass);
$warning.text(message);
$(inp).parent()
.append($('<br>'))
.append($warning)
},
// Add clock widget to a given field
addClock: function(inp) {
var num = DateTimeShortcuts.clockInputs.length;
DateTimeShortcuts.clockInputs[num] = inp;
DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; };
// Shortcut links (clock icon and "Now" link)
var shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a');
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", -1);");
now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
clock_link.id = DateTimeShortcuts.clockLinkName + num;
quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/icon_clock.gif', 'alt', gettext('Clock'));
shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(now_link);
shortcuts_span.appendChild(document.createTextNode('\240|\240'));
shortcuts_span.appendChild(clock_link);
// Create clock link div
//
// Markup looks like:
// <div id="clockbox1" class="clockbox module">
// <h2>Choose a time</h2>
// <ul class="timelist">
// <li><a href="#">Now</a></li>
// <li><a href="#">Midnight</a></li>
// <li><a href="#">6 a.m.</a></li>
// <li><a href="#">Noon</a></li>
// </ul>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// </div>
var clock_box = document.createElement('div');
clock_box.style.display = 'none';
clock_box.style.position = 'absolute';
clock_box.className = 'clockbox module';
clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
document.body.appendChild(clock_box);
addEvent(clock_box, 'click', cancelEventPropagation);
quickElement('h2', clock_box, gettext('Choose a time'));
var time_list = quickElement('ul', clock_box);
time_list.className = 'timelist';
quickElement("a", quickElement("li", time_list), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", -1);");
quickElement("a", quickElement("li", time_list), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 0);");
quickElement("a", quickElement("li", time_list), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 6);");
quickElement("a", quickElement("li", time_list), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", 12);");
var cancel_p = quickElement('p', clock_box);
cancel_p.className = 'calendar-cancel';
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
django.jQuery(document).bind('keyup', function(event) {
if (event.which == 27) {
// ESC key closes popup
DateTimeShortcuts.dismissClock(num);
event.preventDefault();
}
});
},
openClock: function(num) {
var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
}
clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';
// Show the clock box
clock_box.style.display = 'block';
addEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
},
dismissClock: function(num) {
document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
removeEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
},
handleClockQuicklink: function(num, val) {
var d;
if (val == -1) {
d = DateTimeShortcuts.now();
}
else {
d = new Date(1970, 1, 1, val, 0, 0, 0)
}
DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
DateTimeShortcuts.clockInputs[num].focus();
DateTimeShortcuts.dismissClock(num);
},
// Add calendar widget to a given field.
addCalendar: function(inp) {
var num = DateTimeShortcuts.calendars.length;
DateTimeShortcuts.calendarInputs[num] = inp;
DateTimeShortcuts.dismissCalendarFunc[num] = function() { DateTimeShortcuts.dismissCalendar(num); return true; };
// Shortcut links (calendar icon and "Today" link)
var shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var today_link = document.createElement('a');
today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
today_link.appendChild(document.createTextNode(gettext('Today')));
var cal_link = document.createElement('a');
cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
cal_link.id = DateTimeShortcuts.calendarLinkName + num;
quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/icon_calendar.gif', 'alt', gettext('Calendar'));
shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(today_link);
shortcuts_span.appendChild(document.createTextNode('\240|\240'));
shortcuts_span.appendChild(cal_link);
// Create calendarbox div.
//
// Markup looks like:
//
// <div id="calendarbox3" class="calendarbox module">
// <h2>
// <a href="#" class="link-previous">&lsaquo;</a>
// <a href="#" class="link-next">&rsaquo;</a> February 2003
// </h2>
// <div class="calendar" id="calendarin3">
// <!-- (cal) -->
// </div>
// <div class="calendar-shortcuts">
// <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
// </div>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// </div>
var cal_box = document.createElement('div');
cal_box.style.display = 'none';
cal_box.style.position = 'absolute';
cal_box.className = 'calendarbox module';
cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
document.body.appendChild(cal_box);
addEvent(cal_box, 'click', cancelEventPropagation);
// next-prev links
var cal_nav = quickElement('div', cal_box);
var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
cal_nav_prev.className = 'calendarnav-previous';
var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
cal_nav_next.className = 'calendarnav-next';
// main box
var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
cal_main.className = 'calendar';
DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
DateTimeShortcuts.calendars[num].drawCurrent();
// calendar shortcuts
var shortcuts = quickElement('div', cal_box);
shortcuts.className = 'calendar-shortcuts';
quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
shortcuts.appendChild(document.createTextNode('\240|\240'));
quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
shortcuts.appendChild(document.createTextNode('\240|\240'));
quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');
// cancel bar
var cancel_p = quickElement('p', cal_box);
cancel_p.className = 'calendar-cancel';
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
django.jQuery(document).bind('keyup', function(event) {
if (event.which == 27) {
// ESC key closes popup
DateTimeShortcuts.dismissCalendar(num);
event.preventDefault();
}
});
},
openCalendar: function(num) {
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
var inp = DateTimeShortcuts.calendarInputs[num];
// Determine if the current value in the input has a valid date.
// If so, draw the calendar with that date's year and month.
if (inp.value) {
var date_parts = inp.value.split('-');
var year = date_parts[0];
var month = parseFloat(date_parts[1]);
var selected = new Date(inp.value);
if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
}
}
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
}
cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';
cal_box.style.display = 'block';
addEvent(document, 'click', DateTimeShortcuts.dismissCalendarFunc[num]);
},
dismissCalendar: function(num) {
document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
removeEvent(document, 'click', DateTimeShortcuts.dismissCalendarFunc[num]);
},
drawPrev: function(num) {
DateTimeShortcuts.calendars[num].drawPreviousMonth();
},
drawNext: function(num) {
DateTimeShortcuts.calendars[num].drawNextMonth();
},
handleCalendarCallback: function(num) {
var format = get_format('DATE_INPUT_FORMATS')[0];
// the format needs to be escaped a little
format = format.replace('\\', '\\\\');
format = format.replace('\r', '\\r');
format = format.replace('\n', '\\n');
format = format.replace('\t', '\\t');
format = format.replace("'", "\\'");
return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
num,
"].value = new Date(y, m-1, d).strftime('",
format,
"');DateTimeShortcuts.calendarInputs[",
num,
"].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
num,
").style.display='none';}"].join('');
},
handleCalendarQuickLink: function(num, offset) {
var d = DateTimeShortcuts.now();
d.setDate(d.getDate() + offset)
DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
DateTimeShortcuts.calendarInputs[num].focus();
DateTimeShortcuts.dismissCalendar(num);
}
}
addEvent(window, 'load', DateTimeShortcuts.init);

View file

@ -1,97 +0,0 @@
// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.
function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
text = text.replace(/&lt;/g, '<');
text = text.replace(/&gt;/g, '>');
text = text.replace(/&quot;/g, '"');
text = text.replace(/&#39;/g, "'");
text = text.replace(/&amp;/g, '&');
return text;
}
// IE doesn't accept periods or dashes in the window name, but the element IDs
// we use to generate popup window names may contain them, therefore we map them
// to allowed characters in a reversible way so that we can locate the correct
// element when the popup window is dismissed.
function id_to_windowname(text) {
text = text.replace(/\./g, '__dot__');
text = text.replace(/\-/g, '__dash__');
return text;
}
function windowname_to_id(text) {
text = text.replace(/__dot__/g, '.');
text = text.replace(/__dash__/g, '-');
return text;
}
function showRelatedObjectLookupPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^lookup_/, '');
name = id_to_windowname(name);
var href;
if (triggeringLink.href.search(/\?/) >= 0) {
href = triggeringLink.href + '&_popup=1';
} else {
href = triggeringLink.href + '?_popup=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissRelatedLookupPopup(win, chosenId) {
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
}
win.close();
}
function showAddAnotherPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^add_/, '');
name = id_to_windowname(name);
var href = triggeringLink.href;
if (href.indexOf('?') == -1) {
href += '?_popup=1';
} else {
href += '&_popup=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissAddAnotherPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
// django.utils.html.escape.
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
var o;
if (elem) {
var elemName = elem.nodeName.toUpperCase();
if (elemName == 'SELECT') {
o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
} else if (elemName == 'INPUT') {
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
} else {
var toId = name + "_to";
o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
}
win.close();
}

View file

@ -1,169 +0,0 @@
/*
calendar.js - Calendar functions by Adrian Holovaty
depends on core.js for utility functions like removeChildren or quickElement
*/
// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
var CalendarNamespace = {
monthsOfYear: gettext('January February March April May June July August September October November December').split(' '),
daysOfWeek: gettext('S M T W T F S').split(' '),
firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
isLeapYear: function(year) {
return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
},
getDaysInMonth: function(month,year) {
var days;
if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) {
days = 31;
}
else if (month==4 || month==6 || month==9 || month==11) {
days = 30;
}
else if (month==2 && CalendarNamespace.isLeapYear(year)) {
days = 29;
}
else {
days = 28;
}
return days;
},
draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
var today = new Date();
var todayDay = today.getDate();
var todayMonth = today.getMonth()+1;
var todayYear = today.getFullYear();
var todayClass = '';
// Use UTC functions here because the date field does not contain time
// and using the UTC function variants prevent the local time offset
// from altering the date, specifically the day field. For example:
//
// ```
// var x = new Date('2013-10-02');
// var day = x.getDate();
// ```
//
// The day variable above will be 1 instead of 2 in, say, US Pacific time
// zone.
var isSelectedMonth = false;
if (typeof selected != 'undefined') {
isSelectedMonth = (selected.getUTCFullYear() == year && (selected.getUTCMonth()+1) == month);
}
month = parseInt(month);
year = parseInt(year);
var calDiv = document.getElementById(div_id);
removeChildren(calDiv);
var calTable = document.createElement('table');
quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year);
var tableBody = quickElement('tbody', calTable);
// Draw days-of-week header
var tableRow = quickElement('tr', tableBody);
for (var i = 0; i < 7; i++) {
quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
}
var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
var days = CalendarNamespace.getDaysInMonth(month, year);
// Draw blanks before first of month
tableRow = quickElement('tr', tableBody);
for (var i = 0; i < startingPos; i++) {
var _cell = quickElement('td', tableRow, ' ');
_cell.className = "nonday";
}
// Draw days of month
var currentDay = 1;
for (var i = startingPos; currentDay <= days; i++) {
if (i%7 == 0 && currentDay != 1) {
tableRow = quickElement('tr', tableBody);
}
if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) {
todayClass='today';
} else {
todayClass='';
}
// use UTC function; see above for explanation.
if (isSelectedMonth && currentDay == selected.getUTCDate()) {
if (todayClass != '') todayClass += " ";
todayClass += "selected";
}
var cell = quickElement('td', tableRow, '', 'class', todayClass);
quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
currentDay++;
}
// Draw blanks after end of month (optional, but makes for valid code)
while (tableRow.childNodes.length < 7) {
var _cell = quickElement('td', tableRow, ' ');
_cell.className = "nonday";
}
calDiv.appendChild(calTable);
}
}
// Calendar -- A calendar instance
function Calendar(div_id, callback, selected) {
// div_id (string) is the ID of the element in which the calendar will
// be displayed
// callback (string) is the name of a JavaScript function that will be
// called with the parameters (year, month, day) when a day in the
// calendar is clicked
this.div_id = div_id;
this.callback = callback;
this.today = new Date();
this.currentMonth = this.today.getMonth() + 1;
this.currentYear = this.today.getFullYear();
if (typeof selected != 'undefined') {
this.selected = selected;
}
}
Calendar.prototype = {
drawCurrent: function() {
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
},
drawDate: function(month, year, selected) {
this.currentMonth = month;
this.currentYear = year;
if(selected) {
this.selected = selected;
}
this.drawCurrent();
},
drawPreviousMonth: function() {
if (this.currentMonth == 1) {
this.currentMonth = 12;
this.currentYear--;
}
else {
this.currentMonth--;
}
this.drawCurrent();
},
drawNextMonth: function() {
if (this.currentMonth == 12) {
this.currentMonth = 1;
this.currentYear++;
}
else {
this.currentMonth++;
}
this.drawCurrent();
},
drawPreviousYear: function() {
this.currentYear--;
this.drawCurrent();
},
drawNextYear: function() {
this.currentYear++;
this.drawCurrent();
}
}

View file

@ -1,24 +0,0 @@
(function($) {
$(document).ready(function() {
// Add anchor tag for Show/Hide link
$("fieldset.collapse").each(function(i, elem) {
// Don't hide if fields in this fieldset have errors
if ($(elem).find("div.errors").length == 0) {
$(elem).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser' +
i +'" class="collapse-toggle" href="#">' + gettext("Show") +
'</a>)');
}
});
// Add toggle to anchor tag
$("fieldset.collapse a.collapse-toggle").click(function(ev) {
if ($(this).closest("fieldset").hasClass("collapsed")) {
// Show
$(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]);
} else {
// Hide
$(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]);
}
return false;
});
});
})(django.jQuery);

View file

@ -1,2 +0,0 @@
(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){a(b).find("div.errors").length==0&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").click(function(){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
[a(this).attr("id")]);return false})})})(django.jQuery);

View file

@ -1,222 +0,0 @@
// Core javascript helper functions
// basic browser identification & version
var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion);
var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]);
// Cross-browser event handlers.
function addEvent(obj, evType, fn) {
if (obj.addEventListener) {
obj.addEventListener(evType, fn, false);
return true;
} else if (obj.attachEvent) {
var r = obj.attachEvent("on" + evType, fn);
return r;
} else {
return false;
}
}
function removeEvent(obj, evType, fn) {
if (obj.removeEventListener) {
obj.removeEventListener(evType, fn, false);
return true;
} else if (obj.detachEvent) {
obj.detachEvent("on" + evType, fn);
return true;
} else {
return false;
}
}
function cancelEventPropagation(e) {
if (!e) e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
// quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]);
function quickElement() {
var obj = document.createElement(arguments[0]);
if (arguments[2]) {
var textNode = document.createTextNode(arguments[2]);
obj.appendChild(textNode);
}
var len = arguments.length;
for (var i = 3; i < len; i += 2) {
obj.setAttribute(arguments[i], arguments[i+1]);
}
arguments[1].appendChild(obj);
return obj;
}
// "a" is reference to an object
function removeChildren(a) {
while (a.hasChildNodes()) a.removeChild(a.lastChild);
}
// ----------------------------------------------------------------------------
// Cross-browser xmlhttp object
// from http://jibbering.com/2002/4/httprequest.html
// ----------------------------------------------------------------------------
var xmlhttp;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
// ----------------------------------------------------------------------------
// Find-position functions by PPK
// See http://www.quirksmode.org/js/findpos.html
// ----------------------------------------------------------------------------
function findPosX(obj) {
var curleft = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft);
obj = obj.offsetParent;
}
// IE offsetParent does not include the top-level
if (isIE && obj.parentElement){
curleft += obj.offsetLeft - obj.scrollLeft;
}
} else if (obj.x) {
curleft += obj.x;
}
return curleft;
}
function findPosY(obj) {
var curtop = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop);
obj = obj.offsetParent;
}
// IE offsetParent does not include the top-level
if (isIE && obj.parentElement){
curtop += obj.offsetTop - obj.scrollTop;
}
} else if (obj.y) {
curtop += obj.y;
}
return curtop;
}
//-----------------------------------------------------------------------------
// Date object extensions
// ----------------------------------------------------------------------------
Date.prototype.getTwelveHours = function() {
hours = this.getHours();
if (hours == 0) {
return 12;
}
else {
return hours <= 12 ? hours : hours-12
}
}
Date.prototype.getTwoDigitMonth = function() {
return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
}
Date.prototype.getTwoDigitDate = function() {
return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
}
Date.prototype.getTwoDigitTwelveHour = function() {
return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
}
Date.prototype.getTwoDigitHour = function() {
return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
}
Date.prototype.getTwoDigitMinute = function() {
return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
}
Date.prototype.getTwoDigitSecond = function() {
return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
}
Date.prototype.getHourMinute = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
}
Date.prototype.getHourMinuteSecond = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
}
Date.prototype.strftime = function(format) {
var fields = {
c: this.toString(),
d: this.getTwoDigitDate(),
H: this.getTwoDigitHour(),
I: this.getTwoDigitTwelveHour(),
m: this.getTwoDigitMonth(),
M: this.getTwoDigitMinute(),
p: (this.getHours() >= 12) ? 'PM' : 'AM',
S: this.getTwoDigitSecond(),
w: '0' + this.getDay(),
x: this.toLocaleDateString(),
X: this.toLocaleTimeString(),
y: ('' + this.getFullYear()).substr(2, 4),
Y: '' + this.getFullYear(),
'%' : '%'
};
var result = '', i = 0;
while (i < format.length) {
if (format.charAt(i) === '%') {
result = result + fields[format.charAt(i + 1)];
++i;
}
else {
result = result + format.charAt(i);
}
++i;
}
return result;
}
// ----------------------------------------------------------------------------
// String object extensions
// ----------------------------------------------------------------------------
String.prototype.pad_left = function(pad_length, pad_string) {
var new_string = this;
for (var i = 0; new_string.length < pad_length; i++) {
new_string = pad_string + new_string;
}
return new_string;
}
// ----------------------------------------------------------------------------
// Get the computed style for and element
// ----------------------------------------------------------------------------
function getStyle(oElm, strCssRule){
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}

View file

@ -1,272 +0,0 @@
/**
* Django admin inlines
*
* Based on jQuery Formset 1.1
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
* @requires jQuery 1.2.6 or later
*
* Copyright (c) 2009, Stanislaus Madueke
* All rights reserved.
*
* Spiced up with Code from Zain Memon's GSoC project 2009
* and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
(function($) {
$.fn.formset = function(opts) {
var options = $.extend({}, $.fn.formset.defaults, opts);
var $this = $(this);
var $parent = $this.parent();
var updateElementIndex = function(el, prefix, ndx) {
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
var replacement = prefix + "-" + ndx;
if ($(el).prop("for")) {
$(el).prop("for", $(el).prop("for").replace(id_regex, replacement));
}
if (el.id) {
el.id = el.id.replace(id_regex, replacement);
}
if (el.name) {
el.name = el.name.replace(id_regex, replacement);
}
};
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off");
var nextIndex = parseInt(totalForms.val(), 10);
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off");
// only show the add button if we are allowed to add more items,
// note that max_num = None translates to a blank string.
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
$this.each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
if ($this.length && showAddButton) {
var addButton;
if ($this.prop("tagName") == "TR") {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = this.eq(-1).children().length;
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
addButton = $parent.find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
addButton = $this.filter(":last").next().find("a");
}
addButton.click(function(e) {
e.preventDefault();
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
}
row.find("*").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Insert the new form when it has been fully edited
row.insertBefore($(template));
// Update number of total forms
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
nextIndex += 1;
// Hide add button in case we've hit the max, except we want to add infinitely
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
row.find("a." + options.deleteCssClass).click(function(e) {
e.preventDefault();
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
nextIndex -= 1;
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) {
options.removed(row);
}
// Update the TOTAL_FORMS form count.
var forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once we drop below max
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
addButton.parent().show();
}
// Also, update names and ids for all remaining form controls
// so they remain in sequence:
for (var i=0, formCount=forms.length; i<formCount; i++)
{
updateElementIndex($(forms).get(i), options.prefix, i);
$(forms.get(i)).find("*").each(function() {
updateElementIndex(this, options.prefix, i);
});
}
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added(row);
}
});
}
return this;
};
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
addText: "add another", // Text for the add link
deleteText: "remove", // Text for the delete link
addCssClass: "add-row", // CSS class applied to the add link
deleteCssClass: "delete-row", // CSS class applied to the delete link
emptyCssClass: "empty-row", // CSS class applied to the empty row
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
};
// Tabular inlines ---------------------------------------------------------
$.fn.tabularFormset = function(options) {
var $rows = $(this);
var alternatingRows = function(row) {
$($rows.selector).not(".add-row").removeClass("row1 row2")
.filter(":even").addClass("row1").end()
.filter(":odd").addClass("row2");
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
var updateSelectFilter = function() {
// If any SelectFilter widgets are a part of the new form,
// instantiate a new SelectFilter instance for it.
if (typeof SelectFilter != 'undefined'){
$('.selectfilter').each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix );
});
$('.selectfilterstacked').each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix );
});
}
};
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this),
input = field.find('input, select, textarea'),
dependency_list = input.data('dependency_list') || [],
dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
};
$rows.formset({
prefix: options.prefix,
addText: options.addText,
formCssClass: "dynamic-" + options.prefix,
deleteCssClass: "inline-deletelink",
deleteText: options.deleteText,
emptyCssClass: "empty-form",
removed: alternatingRows,
added: function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
}
});
return $rows;
};
// Stacked inlines ---------------------------------------------------------
$.fn.stackedFormset = function(options) {
var $rows = $(this);
var updateInlineLabel = function(row) {
$($rows.selector).find(".inline_label").each(function(i) {
var count = i + 1;
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
});
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
var updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix);
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix);
});
}
};
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this),
input = field.find('input, select, textarea'),
dependency_list = input.data('dependency_list') || [],
dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
};
$rows.formset({
prefix: options.prefix,
addText: options.addText,
formCssClass: "dynamic-" + options.prefix,
deleteCssClass: "inline-deletelink",
deleteText: options.deleteText,
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
})
});
return $rows;
};
})(django.jQuery);

View file

@ -1,9 +0,0 @@
(function(a){a.fn.formset=function(g){var b=a.extend({},a.fn.formset.defaults,g),i=a(this);g=i.parent();var m=function(e,k,h){var j=RegExp("("+k+"-(\\d+|__prefix__))");k=k+"-"+h;a(e).prop("for")&&a(e).prop("for",a(e).prop("for").replace(j,k));if(e.id)e.id=e.id.replace(j,k);if(e.name)e.name=e.name.replace(j,k)},l=a("#id_"+b.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),d=parseInt(l.val(),10),c=a("#id_"+b.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off");l=c.val()===""||c.val()-l.val()>0;i.each(function(){a(this).not("."+
b.emptyCssClass).addClass(b.formCssClass)});if(i.length&&l){var f;if(i.prop("tagName")=="TR"){i=this.eq(-1).children().length;g.append('<tr class="'+b.addCssClass+'"><td colspan="'+i+'"><a href="javascript:void(0)">'+b.addText+"</a></tr>");f=g.find("tr:last a")}else{i.filter(":last").after('<div class="'+b.addCssClass+'"><a href="javascript:void(0)">'+b.addText+"</a></div>");f=i.filter(":last").next().find("a")}f.click(function(e){e.preventDefault();var k=a("#id_"+b.prefix+"-TOTAL_FORMS");e=a("#"+
b.prefix+"-empty");var h=e.clone(true);h.removeClass(b.emptyCssClass).addClass(b.formCssClass).attr("id",b.prefix+"-"+d);if(h.is("tr"))h.children(":last").append('<div><a class="'+b.deleteCssClass+'" href="javascript:void(0)">'+b.deleteText+"</a></div>");else h.is("ul")||h.is("ol")?h.append('<li><a class="'+b.deleteCssClass+'" href="javascript:void(0)">'+b.deleteText+"</a></li>"):h.children(":first").append('<span><a class="'+b.deleteCssClass+'" href="javascript:void(0)">'+b.deleteText+"</a></span>");
h.find("*").each(function(){m(this,b.prefix,k.val())});h.insertBefore(a(e));a(k).val(parseInt(k.val(),10)+1);d+=1;c.val()!==""&&c.val()-k.val()<=0&&f.parent().hide();h.find("a."+b.deleteCssClass).click(function(j){j.preventDefault();j=a(this).parents("."+b.formCssClass);j.remove();d-=1;b.removed&&b.removed(j);j=a("."+b.formCssClass);a("#id_"+b.prefix+"-TOTAL_FORMS").val(j.length);if(c.val()===""||c.val()-j.length>0)f.parent().show();for(var n=0,o=j.length;n<o;n++){m(a(j).get(n),b.prefix,n);a(j.get(n)).find("*").each(function(){m(this,
b.prefix,n)})}});b.added&&b.added(h)})}return this};a.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};a.fn.tabularFormset=function(g){var b=a(this),i=function(){a(b.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")},m=function(){if(typeof SelectFilter!="undefined"){a(".selectfilter").each(function(d,
c){var f=c.name.split("-");SelectFilter.init(c.id,f[f.length-1],false,g.adminStaticPrefix)});a(".selectfilterstacked").each(function(d,c){var f=c.name.split("-");SelectFilter.init(c.id,f[f.length-1],true,g.adminStaticPrefix)})}},l=function(d){d.find(".prepopulated_field").each(function(){var c=a(this).find("input, select, textarea"),f=c.data("dependency_list")||[],e=[];a.each(f,function(k,h){e.push("#"+d.find(".field-"+h).find("input, select, textarea").attr("id"))});e.length&&c.prepopulate(e,c.attr("maxlength"))})};
b.formset({prefix:g.prefix,addText:g.addText,formCssClass:"dynamic-"+g.prefix,deleteCssClass:"inline-deletelink",deleteText:g.deleteText,emptyCssClass:"empty-form",removed:i,added:function(d){l(d);if(typeof DateTimeShortcuts!="undefined"){a(".datetimeshortcuts").remove();DateTimeShortcuts.init()}m();i(d)}});return b};a.fn.stackedFormset=function(g){var b=a(this),i=function(){a(b.selector).find(".inline_label").each(function(d){d=d+1;a(this).html(a(this).html().replace(/(#\d+)/g,"#"+d))})},m=function(){if(typeof SelectFilter!=
"undefined"){a(".selectfilter").each(function(d,c){var f=c.name.split("-");SelectFilter.init(c.id,f[f.length-1],false,g.adminStaticPrefix)});a(".selectfilterstacked").each(function(d,c){var f=c.name.split("-");SelectFilter.init(c.id,f[f.length-1],true,g.adminStaticPrefix)})}},l=function(d){d.find(".prepopulated_field").each(function(){var c=a(this).find("input, select, textarea"),f=c.data("dependency_list")||[],e=[];a.each(f,function(k,h){e.push("#"+d.find(".form-row .field-"+h).find("input, select, textarea").attr("id"))});
e.length&&c.prepopulate(e,c.attr("maxlength"))})};b.formset({prefix:g.prefix,addText:g.addText,formCssClass:"dynamic-"+g.prefix,deleteCssClass:"inline-deletelink",deleteText:g.deleteText,emptyCssClass:"empty-form",removed:i,added:function(d){l(d);if(typeof DateTimeShortcuts!="undefined"){a(".datetimeshortcuts").remove();DateTimeShortcuts.init()}m();i(d)}});return b}})(django.jQuery);

View file

@ -1,7 +0,0 @@
/* Puts the included jQuery into our own namespace using noConflict and passing
* it 'true'. This ensures that the included jQuery doesn't pollute the global
* namespace (i.e. this preserves pre-existing values for both window.$ and
* window.jQuery).
*/
var django = django || {};
django.jQuery = jQuery.noConflict(true);

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more