forked from DGNum/gestioCOF
Initial import
This commit is contained in:
commit
8e1bf7b705
19 changed files with 970 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
*.swo
|
0
__init__.py
Normal file
0
__init__.py
Normal file
7
apache/django.wsgi
Executable file
7
apache/django.wsgi
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
import os, sys
|
||||||
|
sys.path.append (os.path.expanduser ('~gestion/www'))
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||||
|
|
||||||
|
import django.core.handlers.wsgi
|
||||||
|
|
||||||
|
application = django.core.handlers.wsgi.WSGIHandler()
|
0
gestioncof/__init__.py
Normal file
0
gestioncof/__init__.py
Normal file
65
gestioncof/admin.py
Normal file
65
gestioncof/admin.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer
|
||||||
|
from gestioncof.models import Event, EventOption, EventOptionChoice
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
def add_link_field(target_model = '', field = '', link_text = unicode, desc_text = unicode):
|
||||||
|
def add_link(cls):
|
||||||
|
reverse_name = target_model or cls.model.__name__.lower()
|
||||||
|
def link(self, instance):
|
||||||
|
app_name = instance._meta.app_label
|
||||||
|
reverse_path = "admin:%s_%s_change" % (app_name, reverse_name)
|
||||||
|
link_obj = getattr(instance, field, None) or instance
|
||||||
|
if not link_obj.id:
|
||||||
|
return ""
|
||||||
|
url = reverse(reverse_path, args = (link_obj.id,))
|
||||||
|
return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))
|
||||||
|
link.allow_tags = True
|
||||||
|
link.short_description = desc_text(reverse_name + ' link')
|
||||||
|
cls.link = link
|
||||||
|
cls.readonly_fields = list(getattr(cls, 'readonly_fields', [])) + ['link']
|
||||||
|
return cls
|
||||||
|
return add_link
|
||||||
|
|
||||||
|
class SurveyQuestionAnswerInline(admin.TabularInline):
|
||||||
|
model = SurveyQuestionAnswer
|
||||||
|
|
||||||
|
@add_link_field(desc_text = lambda x: "Réponses", link_text = lambda x: "Éditer les réponses")
|
||||||
|
class SurveyQuestionInline(admin.TabularInline):
|
||||||
|
model = SurveyQuestion
|
||||||
|
|
||||||
|
class SurveyQuestionAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [
|
||||||
|
SurveyQuestionAnswerInline,
|
||||||
|
]
|
||||||
|
|
||||||
|
class SurveyAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [
|
||||||
|
SurveyQuestionInline,
|
||||||
|
]
|
||||||
|
|
||||||
|
@add_link_field()
|
||||||
|
class EventOptionChoiceInline(admin.StackedInline):
|
||||||
|
model = EventOptionChoice
|
||||||
|
|
||||||
|
@add_link_field()
|
||||||
|
class EventOptionInline(admin.StackedInline):
|
||||||
|
model = EventOption
|
||||||
|
|
||||||
|
class EventOptionAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [
|
||||||
|
EventOptionChoiceInline,
|
||||||
|
]
|
||||||
|
|
||||||
|
class EventAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [
|
||||||
|
EventOptionInline,
|
||||||
|
]
|
||||||
|
|
||||||
|
admin.site.register(Survey, SurveyAdmin)
|
||||||
|
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
|
||||||
|
admin.site.register(Event, EventAdmin)
|
||||||
|
admin.site.register(EventOption, EventOptionAdmin)
|
130
gestioncof/models.py
Normal file
130
gestioncof/models.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
|
||||||
|
OCCUPATION_CHOICES = (
|
||||||
|
('exterieur', _(u"Extérieur")),
|
||||||
|
('1A', _(u"1A")),
|
||||||
|
('2A', _(u"2A")),
|
||||||
|
('3A', _(u"3A")),
|
||||||
|
('4A', _(u"4A")),
|
||||||
|
('archicube', _(u"Archicube")),
|
||||||
|
('doctorant', _(u"Doctorant")),
|
||||||
|
('CST', _(u"CST")),
|
||||||
|
)
|
||||||
|
|
||||||
|
TYPE_COTIZ_CHOICES = (
|
||||||
|
('etudiant', _(u"Étudiant")),
|
||||||
|
('normalien', _(u"Normalien")),
|
||||||
|
('exterieur', _(u"Extérieur")),
|
||||||
|
)
|
||||||
|
|
||||||
|
def choices_length (choices):
|
||||||
|
return reduce (lambda m, choice: max (m, len (choice[0])), choices, 0)
|
||||||
|
|
||||||
|
class CofProfile(models.Model):
|
||||||
|
user = models.OneToOneField(User)
|
||||||
|
login_clipper = models.CharField("Login clipper", max_length = 8)
|
||||||
|
is_cof = models.BooleanField("Membre du COF", default = False)
|
||||||
|
occupation = models.CharField (_(u"Occupation"),
|
||||||
|
default = "1A",
|
||||||
|
choices = OCCUPATION_CHOICES,
|
||||||
|
max_length = choices_length (OCCUPATION_CHOICES))
|
||||||
|
type_cotiz = models.CharField (_(u"Type de cotisation"),
|
||||||
|
default = "normalien",
|
||||||
|
choices = TYPE_COTIZ_CHOICES,
|
||||||
|
max_length = choices_length (TYPE_COTIZ_CHOICES))
|
||||||
|
mailing_cof = models.BooleanField("Recevoir les mails COF", default = True)
|
||||||
|
mailing_bda_revente = models.BooleanField("Recevoir les mails de revente de places BDA", default = True)
|
||||||
|
is_buro = models.BooleanField("Membre du Burô", default = False)
|
||||||
|
|
||||||
|
def create_user_profile(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
CofProfile.objects.create(user = instance)
|
||||||
|
post_save.connect(create_user_profile, sender = User)
|
||||||
|
|
||||||
|
class Event(models.Model):
|
||||||
|
title = models.CharField("Titre", max_length = 200)
|
||||||
|
location = models.CharField("Lieu", max_length = 200)
|
||||||
|
start_date = models.DateField("Date de début", blank = True)
|
||||||
|
end_date = models.DateField("Date de fin", blank = True)
|
||||||
|
description = models.TextField("Description", blank = True)
|
||||||
|
registration_open = models.BooleanField("Inscriptions ouvertes", default = True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Événement"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.title)
|
||||||
|
|
||||||
|
class EventOption(models.Model):
|
||||||
|
event = models.ForeignKey(Event)
|
||||||
|
name = models.CharField("Option", max_length = 200)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Option"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.name)
|
||||||
|
|
||||||
|
class EventOptionChoice(models.Model):
|
||||||
|
event_option = models.ForeignKey(EventOption)
|
||||||
|
value = models.CharField("Valeur", max_length = 200)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Choix"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.value)
|
||||||
|
|
||||||
|
class EventRegistration(models.Model):
|
||||||
|
user = models.ForeignKey(User)
|
||||||
|
event = models.ForeignKey(Event)
|
||||||
|
options = models.ManyToManyField(EventOptionChoice)
|
||||||
|
paid = models.BooleanField("A payé", default = False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Inscription"
|
||||||
|
|
||||||
|
class Survey(models.Model):
|
||||||
|
title = models.CharField("Titre", max_length = 200)
|
||||||
|
details = models.TextField("Détails", blank = True)
|
||||||
|
survey_open = models.BooleanField("Sondage ouvert", default = True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Sondage"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.title)
|
||||||
|
|
||||||
|
class SurveyQuestion(models.Model):
|
||||||
|
survey = models.ForeignKey(Survey, related_name = "questions")
|
||||||
|
question = models.CharField("Question", max_length = 200)
|
||||||
|
multi_answers = models.BooleanField("Choix multiples", default = False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Question"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.question)
|
||||||
|
|
||||||
|
class SurveyQuestionAnswer(models.Model):
|
||||||
|
survey_question = models.ForeignKey(SurveyQuestion, related_name = "answers")
|
||||||
|
answer = models.CharField("Réponse", max_length = 200)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Réponse"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.answer)
|
||||||
|
|
||||||
|
class SurveyAnswer(models.Model):
|
||||||
|
user = models.ForeignKey(User)
|
||||||
|
survey = models.ForeignKey(Survey)
|
||||||
|
answers = models.ManyToManyField(SurveyQuestionAnswer)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Réponses"
|
24
gestioncof/shared.py
Normal file
24
gestioncof/shared.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
from django.conf import settings
|
||||||
|
from django_cas.backends import CASBackend
|
||||||
|
|
||||||
|
class COFCASBackend(CASBackend):
|
||||||
|
def authenticate(self, ticket, service):
|
||||||
|
"""Authenticates CAS ticket and retrieves user data"""
|
||||||
|
user = super(COFCASBackend, self).authenticate(ticket, service)
|
||||||
|
profile = user.get_profile()
|
||||||
|
if not profile.login_clipper:
|
||||||
|
profile.login_clipper = user.username
|
||||||
|
profile.save()
|
||||||
|
if not user.email:
|
||||||
|
user.email = settings.CAS_EMAIL_FORMAT % profile.login_clipper
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
def context_processor (request):
|
||||||
|
'''Append extra data to the context of the given request'''
|
||||||
|
data = {
|
||||||
|
"user": request.user,
|
||||||
|
"site": Site.objects.get_current(),
|
||||||
|
}
|
||||||
|
return data
|
16
gestioncof/tests.py
Normal file
16
gestioncof/tests.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
This file demonstrates writing tests using the unittest module. These will pass
|
||||||
|
when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace this with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleTest(TestCase):
|
||||||
|
def test_basic_addition(self):
|
||||||
|
"""
|
||||||
|
Tests that 1 + 1 always equals 2.
|
||||||
|
"""
|
||||||
|
self.assertEqual(1 + 1, 2)
|
108
gestioncof/views.py
Normal file
108
gestioncof/views.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
|
from django.template import RequestContext, loader
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django import forms
|
||||||
|
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
|
||||||
|
from gestioncof.models import Survey, SurveyQuestion, SurveyQuestionAnswer, SurveyAnswer
|
||||||
|
from gestioncof.models import Event, EventOption, EventOptionChoice, EventRegistration
|
||||||
|
|
||||||
|
def render_page (request, data, template):
|
||||||
|
template = loader.get_template (template)
|
||||||
|
context = RequestContext (request, data)
|
||||||
|
return HttpResponse (template.render (context))
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def home(request):
|
||||||
|
data = {"surveys": Survey.objects.filter(survey_open = True).all(),
|
||||||
|
"events": Event.objects.filter(registration_open = True).all()}
|
||||||
|
return render_page(request, data, "home.html")
|
||||||
|
|
||||||
|
def login(request):
|
||||||
|
if request.user.is_authenticated():
|
||||||
|
return redirect("gestioncof.views.home")
|
||||||
|
return render_page(request, {}, "login_switch.html")
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def logout(request):
|
||||||
|
if request.user.get_profile().login_clipper:
|
||||||
|
return redirect("django_cas.views.logout")
|
||||||
|
else:
|
||||||
|
return redirect("django.contrib.auth.views.logout")
|
||||||
|
|
||||||
|
class SurveyForm(forms.Form):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
survey = kwargs.pop("survey")
|
||||||
|
current_answers = kwargs.pop("current_answers", None)
|
||||||
|
super(SurveyForm, self).__init__(*args, **kwargs)
|
||||||
|
answers = {}
|
||||||
|
if current_answers:
|
||||||
|
for answer in current_answers.all():
|
||||||
|
if answer.survey_question.id not in answers:
|
||||||
|
answers[answer.survey_question.id] = [answer.id]
|
||||||
|
else:
|
||||||
|
answers[answer.survey_question.id].append(answer.id)
|
||||||
|
for question in survey.questions.all():
|
||||||
|
choices = [(answer.id, answer.answer) for answer in question.answers.all()]
|
||||||
|
if question.multi_answers:
|
||||||
|
initial = [] if question.id not in answers else answers[question.id]
|
||||||
|
field = forms.MultipleChoiceField(label = question.question,
|
||||||
|
choices = choices,
|
||||||
|
widget = CheckboxSelectMultiple,
|
||||||
|
required = False,
|
||||||
|
initial = initial)
|
||||||
|
else:
|
||||||
|
initial = None if question.id not in answers else answers[question.id][0]
|
||||||
|
field = forms.ChoiceField(label = question.question,
|
||||||
|
choices = choices,
|
||||||
|
widget = RadioSelect,
|
||||||
|
required = False,
|
||||||
|
initial = initial)
|
||||||
|
field.question_id = question.id
|
||||||
|
self.fields["question_%d" % question.id] = field
|
||||||
|
|
||||||
|
def answers(self):
|
||||||
|
for name, value in self.cleaned_data.items():
|
||||||
|
if name.startswith('question_'):
|
||||||
|
yield (self.fields[name].question_id, value)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def survey(request, survey_id):
|
||||||
|
survey = get_object_or_404(Survey, id = survey_id)
|
||||||
|
if not survey.survey_open:
|
||||||
|
raise Http404
|
||||||
|
success = False
|
||||||
|
if request.method == "POST":
|
||||||
|
form = SurveyForm(request.POST, survey = survey)
|
||||||
|
if form.is_valid():
|
||||||
|
all_answers = []
|
||||||
|
for question_id, answers_ids in form.answers():
|
||||||
|
question = get_object_or_404(SurveyQuestion, id = question_id,
|
||||||
|
survey = survey)
|
||||||
|
if type(answers_ids) != list:
|
||||||
|
answers_ids = [answers_ids]
|
||||||
|
if not question.multi_answers and len(answers_ids) > 1:
|
||||||
|
raise Http404
|
||||||
|
for answer_id in answers_ids:
|
||||||
|
answer_id = int(answer_id)
|
||||||
|
answer = SurveyQuestionAnswer.objects.get(
|
||||||
|
id = answer_id,
|
||||||
|
survey_question = question)
|
||||||
|
all_answers.append(answer)
|
||||||
|
try:
|
||||||
|
current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey)
|
||||||
|
except SurveyAnswer.DoesNotExist:
|
||||||
|
current_answer = SurveyAnswer(user = request.user, survey = survey)
|
||||||
|
current_answer.save()
|
||||||
|
current_answer.answers = all_answers
|
||||||
|
current_answer.save()
|
||||||
|
success = True
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
current_answer = SurveyAnswer.objects.get(user = request.user, survey = survey)
|
||||||
|
form = SurveyForm(survey = survey, current_answers = current_answer.answers)
|
||||||
|
except SurveyAnswer.DoesNotExist:
|
||||||
|
form = SurveyForm(survey = survey)
|
||||||
|
return render_page(request, {"survey": survey, "form": form, "success": success}, "survey.html")
|
14
manage.py
Normal file
14
manage.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from django.core.management import execute_manager
|
||||||
|
import imp
|
||||||
|
try:
|
||||||
|
imp.find_module('settings') # Assumed to be in the same directory.
|
||||||
|
except ImportError:
|
||||||
|
import sys
|
||||||
|
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
import settings
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_manager(settings)
|
298
media/cof.css
Normal file
298
media/cof.css
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
html,body {
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Droid Serif';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
src: local('Droid Serif Bold'), local('DroidSerif-Bold'), url('droidserif.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a:link, #cof a:active, #cof a:visited {
|
||||||
|
color: #111;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a:hover {
|
||||||
|
color: #444;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof form {
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof fieldset {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
float: left;
|
||||||
|
clear: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof fieldset legend {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login-container {
|
||||||
|
width: 500px;
|
||||||
|
margin: 7em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login {
|
||||||
|
width: 500px;
|
||||||
|
border: 15px solid #333;
|
||||||
|
-webkit-border-radius: 20px;
|
||||||
|
-moz-border-radius: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-container {
|
||||||
|
width: 800px;
|
||||||
|
margin: 7em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main {
|
||||||
|
width: 800px;
|
||||||
|
border: 15px solid #333;
|
||||||
|
-webkit-border-radius: 20px;
|
||||||
|
-moz-border-radius: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 2em;
|
||||||
|
box-shadow: 0 0 100px #AAA inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main #main-content {
|
||||||
|
font-size: 1.25em;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main #main-content ul {
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main h1 {
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main h1 a {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main h3 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main p {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main form li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof .success {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00B000;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main form ul.errorlist li {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #B00000;
|
||||||
|
background-color: transparent;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login.login_block {
|
||||||
|
padding: 2em;
|
||||||
|
box-shadow: 0 0 100px #AAA inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a#login_clipper, #cof a#login_outsider {
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
width: 250px;
|
||||||
|
height: 200px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Droid Serif', serif;
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 190px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a#login_clipper {
|
||||||
|
background-color: #123E96;
|
||||||
|
box-shadow: 0 0 100px #040C78 inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a#login_clipper:hover {
|
||||||
|
background-color: #164BB6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a#login_outsider {
|
||||||
|
background-color: #961221;
|
||||||
|
box-shadow: 0 0 100px #780411 inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof a#login_outsider:hover {
|
||||||
|
background-color: #B31729;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login label span.accesskey {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login input {
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login .btn-row {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof .btn-submit {
|
||||||
|
float: none;
|
||||||
|
clear: none;
|
||||||
|
display: inline;
|
||||||
|
letter-spacing: 0;
|
||||||
|
color: #333;
|
||||||
|
padding: 5px;
|
||||||
|
text-transform: uppercofe;
|
||||||
|
font-variant: small-caps;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#EEE), to(#CCC));
|
||||||
|
background: -moz-linear-gradient(19% 75% 90deg,#EEE, #CCC);
|
||||||
|
background: linear-gradient(center top, #EEE, #CCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cof #main-login .btn-reset {
|
||||||
|
float: none;
|
||||||
|
clear: none;
|
||||||
|
margin-left: 5px;
|
||||||
|
border: 0;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
background: transparent;
|
||||||
|
color: #777;
|
||||||
|
text-transform: lowercase;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RESET --------------------------------- */
|
||||||
|
/* reset some properties for elements since defaults are not crossbrowser - http: //meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */
|
||||||
|
html,body,div,span,h1,h2,h3,p,a,img,ul,li,fieldset,form,label,legend {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
font-weight: inherit;
|
||||||
|
font-style: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
font-family: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 6px 0 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: normal 400 62.5%/1.0 Verdana, sans-serif;
|
||||||
|
background-color: #eee;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HEADER --------------------------------- */
|
||||||
|
#header h1 {
|
||||||
|
font-family: 'Droid Serif', serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #BB0000;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FORMS --------------------------------- */
|
||||||
|
input {
|
||||||
|
border-width: 1px;
|
||||||
|
font-family: Verdana,sans-serif;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #000;
|
||||||
|
padding: 3px;
|
||||||
|
min-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"], input[type=password] {
|
||||||
|
border: 2px solid #888;
|
||||||
|
-webkit-border-radius: 8px;
|
||||||
|
-moz-border-radius: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 12px #AAA inset;
|
||||||
|
min-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: #333;
|
||||||
|
background: -webkit-gradient(linear, left top, right top, color-stop(0%,hsla(0,0%,70%,0)), color-stop(50%,hsla(0,0%,70%,.75)), color-stop(100%,hsla(0,0%,70%,0)));
|
||||||
|
background: -webkit-linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
|
||||||
|
background: -moz-linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
|
||||||
|
background: -ms-linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
|
||||||
|
background: -o-linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
|
||||||
|
background: linear-gradient(left, hsla(0,0%,70%,0) 0%, hsla(0,0%,70%,.75) 50%, hsla(0,0%,70%,0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fm-v div.row {
|
||||||
|
margin: 0;
|
||||||
|
padding: .5em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fm-v div.row label {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fm-v div.row input.btn-submit {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fm-v div.row.fl-controls-left {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
168
settings.py
Normal file
168
settings.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
# Django settings for cof project.
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
ADMINS = (
|
||||||
|
('Guillaume Seguin', 'guillaume@segu.in'),
|
||||||
|
)
|
||||||
|
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||||
|
'NAME': 'cof_gestion', # Or path to database file if using sqlite3.
|
||||||
|
'USER': 'cof_gestion', # Not used with sqlite3.
|
||||||
|
'PASSWORD': '1OjKotIbmyro', # Not used with sqlite3.
|
||||||
|
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
||||||
|
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Local time zone for this installation. Choices can be found here:
|
||||||
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
|
# although not all choices may be available on all operating systems.
|
||||||
|
# On Unix systems, a value of None will cause Django to use the same
|
||||||
|
# timezone as the operating system.
|
||||||
|
# If running in a Windows environment this must be set to the same as your
|
||||||
|
# system time zone.
|
||||||
|
TIME_ZONE = 'Europe/Paris'
|
||||||
|
|
||||||
|
# Language code for this installation. All choices can be found here:
|
||||||
|
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||||
|
LANGUAGE_CODE = 'fr-fr'
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
# If you set this to False, Django will make some optimizations so as not
|
||||||
|
# to load the internationalization machinery.
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
# If you set this to False, Django will not format dates, numbers and
|
||||||
|
# calendars according to the current locale
|
||||||
|
USE_L10N = True
|
||||||
|
|
||||||
|
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||||
|
# Example: "/home/media/media.lawrence.com/media/"
|
||||||
|
MEDIA_ROOT = ''
|
||||||
|
|
||||||
|
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||||
|
# trailing slash.
|
||||||
|
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
|
||||||
|
MEDIA_URL = '/gestion/media/'
|
||||||
|
|
||||||
|
# Absolute path to the directory static files should be collected to.
|
||||||
|
# Don't put anything in this directory yourself; store your static files
|
||||||
|
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||||
|
# Example: "/home/media/media.lawrence.com/static/"
|
||||||
|
STATIC_ROOT = '/home/gestion/www/static'
|
||||||
|
|
||||||
|
# URL prefix for static files.
|
||||||
|
# Example: "http://media.lawrence.com/static/"
|
||||||
|
STATIC_URL = '/gestion/static/'
|
||||||
|
|
||||||
|
# URL prefix for admin static files -- CSS, JavaScript and images.
|
||||||
|
# Make sure to use a trailing slash.
|
||||||
|
# Examples: "http://foo.com/static/admin/", "/static/admin/".
|
||||||
|
ADMIN_MEDIA_PREFIX = '/gestion/static/grappelli/'
|
||||||
|
GRAPPELLI_ADMIN_HEADLINE = "GestioCOF"
|
||||||
|
GRAPPELLI_ADMIN_TITLE = "GestioCOF"
|
||||||
|
|
||||||
|
# Additional locations of static files
|
||||||
|
STATICFILES_DIRS = (
|
||||||
|
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||||
|
# Always use forward slashes, even on Windows.
|
||||||
|
# Don't forget to use absolute paths, not relative paths.
|
||||||
|
)
|
||||||
|
|
||||||
|
# List of finder classes that know how to find static files in
|
||||||
|
# various locations.
|
||||||
|
STATICFILES_FINDERS = (
|
||||||
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make this unique, and don't share it with anybody.
|
||||||
|
SECRET_KEY = '1d%3zqeyj8dk^@afz9=q12gs&&5k@4qx)5%uc_(&%01)d&74af'
|
||||||
|
|
||||||
|
# List of callables that know how to import templates from various sources.
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
# 'django.template.loaders.eggs.Loader',
|
||||||
|
)
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
"django.core.context_processors.auth",
|
||||||
|
"django.core.context_processors.debug",
|
||||||
|
"django.core.context_processors.i18n",
|
||||||
|
"django.core.context_processors.media",
|
||||||
|
"django.core.context_processors.static",
|
||||||
|
"gestioncof.shared.context_processor",
|
||||||
|
)
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
# 'django_cas.middleware.CASMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'urls'
|
||||||
|
|
||||||
|
TEMPLATE_DIRS = (
|
||||||
|
"/home/gestion/www/templates/gestioncof",
|
||||||
|
)
|
||||||
|
|
||||||
|
LOGIN_URL = "/gestion/login"
|
||||||
|
LOGIN_REDIRECT_URL = "/gestion/"
|
||||||
|
|
||||||
|
CAS_SERVER_URL = 'https://cas.eleves.ens.fr/'
|
||||||
|
CAS_IGNORE_REFERER = True
|
||||||
|
CAS_REDIRECT_URL = '/gestion/'
|
||||||
|
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
|
||||||
|
AUTH_PROFILE_MODULE = 'gestioncof.CofProfile'
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
'gestioncof.shared.COFCASBackend',
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'grappelli',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.admindocs',
|
||||||
|
'gestioncof',
|
||||||
|
)
|
||||||
|
|
||||||
|
# A sample logging configuration. The only tangible logging
|
||||||
|
# performed by this configuration is to send an email to
|
||||||
|
# the site admins on every HTTP 500 error.
|
||||||
|
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||||
|
# more details on how to customize your logging configuration.
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'handlers': {
|
||||||
|
'mail_admins': {
|
||||||
|
'level': 'ERROR',
|
||||||
|
'class': 'django.utils.log.AdminEmailHandler'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'django.request': {
|
||||||
|
'handlers': ['mail_admins'],
|
||||||
|
'level': 'ERROR',
|
||||||
|
'propagate': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
11
templates/gestioncof/base.html
Normal file
11
templates/gestioncof/base.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
|
||||||
|
<head>
|
||||||
|
<title>{{ site.name }}</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="{{ MEDIA_URL }}/cof.css" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
</head>
|
||||||
|
<body id="cof">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
16
templates/gestioncof/base_title.html
Normal file
16
templates/gestioncof/base_title.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="main-container">
|
||||||
|
<div id="main">
|
||||||
|
<div id="header">
|
||||||
|
<h1>{% block title %}<a href="{% url gestioncof.views.home %}">{{ site.name }}</a>{% endblock %}</h1>
|
||||||
|
<hr />
|
||||||
|
<div id="main-content">
|
||||||
|
{% block realcontent %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
28
templates/gestioncof/home.html
Normal file
28
templates/gestioncof/home.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base_title.html" %}
|
||||||
|
|
||||||
|
{% block realcontent %}
|
||||||
|
<h2>Bienvenue, {% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}</h2>
|
||||||
|
{% if events %}
|
||||||
|
<h3>Événements</h3>
|
||||||
|
<ul>
|
||||||
|
{% for event in events %}
|
||||||
|
<li><a href="{% url gestioncof.views.event event.id %}">{{ event.title }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% if surveys %}
|
||||||
|
<h3>Sondages</h3>
|
||||||
|
<ul>
|
||||||
|
{% for survey in surveys %}
|
||||||
|
<li><a href="{% url gestioncof.views.survey survey.id %}">{{ survey.title }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<h3>Divers</h3>
|
||||||
|
<ul>
|
||||||
|
{% if user.is_buro or user.is_staff %}
|
||||||
|
<li><a href="{% url admin:index %}">Administration</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="{% url gestioncof.views.logout %}">Se déconnecter</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
32
templates/gestioncof/login.html
Normal file
32
templates/gestioncof/login.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="main-login-container">
|
||||||
|
<div id="main-login" class="login_block">
|
||||||
|
<div id="header">
|
||||||
|
<h1>{{ site.name }} – Connexion</h1>
|
||||||
|
</div>
|
||||||
|
{% if form.errors %}
|
||||||
|
<p class="error">Identifiants incorrects.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="{% url django.contrib.auth.views.login %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.username.label_tag }}</td>
|
||||||
|
<td>{{ form.username }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.password.label_tag }}</td>
|
||||||
|
<td>{{ form.password }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" class="btn-submit" value="SE CONNECTER" />
|
||||||
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
16
templates/gestioncof/login_switch.html
Normal file
16
templates/gestioncof/login_switch.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="main-login-container">
|
||||||
|
<div id="main-login">
|
||||||
|
<a id="login_clipper" href="{% url django_cas.views.login %}">
|
||||||
|
Compte clipper
|
||||||
|
</a>
|
||||||
|
<a id="login_outsider" href="{% url django.contrib.auth.views.login %}">
|
||||||
|
Extérieur
|
||||||
|
</a>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
16
templates/gestioncof/survey.html
Normal file
16
templates/gestioncof/survey.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base_title.html" %}
|
||||||
|
|
||||||
|
{% block realcontent %}
|
||||||
|
<h2>Sondage: {{ survey.title }}</h2>
|
||||||
|
{% if success %}
|
||||||
|
<p class="success">Votre réponse a bien été enregistrée ! Vous pouvez cependant la modifier jusqu'à la fin du sondage.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if survey.details %}
|
||||||
|
<p>{{ survey.details }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<form method="post" action="{% url gestioncof.views.survey survey.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" class="btn-submit" value="Enregistrer" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
18
urls.py
Normal file
18
urls.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from django.conf.urls.defaults import patterns, include, url
|
||||||
|
from django.contrib import admin
|
||||||
|
admin.autodiscover()
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^$', 'gestioncof.views.home', name='home'),
|
||||||
|
url(r'^cas/login$', 'django_cas.views.login'),
|
||||||
|
url(r'^cas/logout$', 'django_cas.views.logout'),
|
||||||
|
url(r'^outsider/login$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
|
||||||
|
url(r'^outsider/logout$', 'django.contrib.auth.views.logout', {'next_page': '/gestion/'}),
|
||||||
|
url(r'^login$', 'gestioncof.views.login'),
|
||||||
|
url(r'^logout$', 'gestioncof.views.logout'),
|
||||||
|
url(r'^survey/(?P<survey_id>\d+)$', 'gestioncof.views.survey'),
|
||||||
|
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
url(r'^grappelli/', include('grappelli.urls')),
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue