diff --git a/README.md b/README.md index 15f0ec0d..db2cc93b 100644 --- a/README.md +++ b/README.md @@ -47,28 +47,49 @@ gérer la machine virtuelle : - `vagrant ssh` vous connecte en SSH à la machine virtuelle, dans le dossier où est installé GestioCOF. Vous pouvez utiliser les commandes Django - habituelles (`manage.py runserver` etc.); toutefois pour lancer le serveur il faut faire - - python manage.py runserver 0.0.0.0:8000 - - car par défaut Django n'écoute que sur l'adresse locale de la machine - virtuelle - or vous voudrez accéder à GestioCOF depuis votre machine - physique. + habituelles (`manage.py runserver` etc.) pour lancer + [le serveur en dev](#lancer-le-serveur-de-développement-standard) par + exemple **Le dossier avec le code de GestioCOF est partagé entre la machine virtuelle et votre machine physique : vous pouvez donc utiliser votre éditeur favori pour coder depuis l'extérieur de la machine virtuelle, et les changements seront répercutés dans la machine virtuelle.** +#### Lancer le serveur de développement standard + +Pour lancer le serveur de développement, il faut faire + + python manage.py runserver 0.0.0.0:8000 + +car par défaut Django n'écoute que sur l'adresse locale de la machine virtuelle +or vous voudrez accéder à GestioCOF depuis votre machine physique. L'url à +entrer dans le navigateur est `localhost:8000`. + +#### Serveur de développement type production + +Sur la VM Vagrant, un serveur apache est configuré pour servir GestioCOF de +façon similaire à la version en production : on utilise +[Daphne](https://github.com/django/daphne/) et `python manage.py runworker` +derrière un reverse-proxy apache. Le tout est monitoré par +[supervisor](http://supervisord.org/). + +Ce serveur se lance tout seul et est accessible en dehors de la VM à l'url +`localhost:8080`. Toutefois il ne se recharge pas tout seul lorsque le code +change, il faut relancer le worker avec `sudo supervisorctl restart worker` pour +visualiser la dernière version du code. + ### Installation manuelle Si vous optez pour une installation manuelle plutôt que d'utiliser Vagrant, il est fortement conseillé d'utiliser un environnement virtuel pour Python. Il vous faudra installer mercurial, pip, les librairies de développement de -python, ainsi qu'un client et un serveur MySQL ; sous Debian et dérivées (Ubuntu, ...) : +python, un client et un serveur MySQL ainsi qu'un serveur redis ; sous Debian et +dérivées (Ubuntu, ...) : sudo apt-get install mercurial python-pip python-dev libmysqlclient-dev + redis-server Si vous décidez d'utiliser un environnement virtuel Python (virtualenv; fortement conseillé), déplacez-vous dans le dossier où est installé GestioCOF @@ -87,7 +108,7 @@ Vous pouvez maintenant installer les dépendances Python depuis les fichiers pip install -r requirements.txt -r requirements-devel.txt -Enfin, copiez le fichier `cof/settings_dev.py` dans `cof/settings.py`. +Copiez le fichier `cof/settings_dev.py` dans `cof/settings.py`. #### Installation avec MySQL diff --git a/apache/django.wsgi b/apache/django.wsgi deleted file mode 100644 index 7b9c271d..00000000 --- a/apache/django.wsgi +++ /dev/null @@ -1,7 +0,0 @@ -import os, sys -sys.path.append (os.path.expanduser ('~gestion/www')) -os.environ['DJANGO_SETTINGS_MODULE'] = 'cof.settings' - -import django.core.handlers.wsgi - -application = django.core.handlers.wsgi.WSGIHandler() diff --git a/apache/wsgi.py b/apache/wsgi.py deleted file mode 100644 index 177542a8..00000000 --- a/apache/wsgi.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -WSGI config for myproject project. -It exposes the WSGI callable as a module-level variable named ``application``. -For more information on this file, see -https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ -""" - -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import os -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings") -application = get_wsgi_application() diff --git a/bda/migrations/0008_py3.py b/bda/migrations/0008_py3.py index 42718255..fe6a8eaf 100644 --- a/bda/migrations/0008_py3.py +++ b/bda/migrations/0008_py3.py @@ -14,12 +14,21 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='choixspectacle', name='double_choice', - field=models.CharField(verbose_name='Nombre de places', choices=[('1', '1 place'), ('autoquit', '2 places si possible, 1 sinon'), ('double', '2 places sinon rien')], default='1', max_length=10), + field=models.CharField( + verbose_name='Nombre de places', + choices=[('1', '1 place'), + ('autoquit', '2 places si possible, 1 sinon'), + ('double', '2 places sinon rien')], + max_length=10, default='1'), ), migrations.AlterField( model_name='participant', name='paymenttype', - field=models.CharField(verbose_name='Moyen de paiement', choices=[('cash', 'Cash'), ('cb', 'CB'), ('cheque', 'Chèque'), ('autre', 'Autre')], max_length=6, blank=True), + field=models.CharField( + blank=True, + choices=[('cash', 'Cash'), ('cb', 'CB'), + ('cheque', 'Chèque'), ('autre', 'Autre')], + max_length=6, verbose_name='Moyen de paiement'), ), migrations.AlterField( model_name='salle', @@ -44,7 +53,8 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='spectacle', name='listing', - field=models.BooleanField(verbose_name='Les places sont sur listing'), + field=models.BooleanField( + verbose_name='Les places sont sur listing'), ), migrations.AlterField( model_name='spectacle', @@ -59,7 +69,8 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='spectacle', name='slots_description', - field=models.TextField(verbose_name='Description des places', blank=True), + field=models.TextField(verbose_name='Description des places', + blank=True), ), migrations.AlterField( model_name='spectacle', @@ -69,17 +80,20 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='tirage', name='active', - field=models.BooleanField(verbose_name='Tirage actif', default=False), + field=models.BooleanField(verbose_name='Tirage actif', + default=False), ), migrations.AlterField( model_name='tirage', name='fermeture', - field=models.DateTimeField(verbose_name='Date et heure de fermerture du tirage'), + field=models.DateTimeField( + verbose_name='Date et heure de fermerture du tirage'), ), migrations.AlterField( model_name='tirage', name='ouverture', - field=models.DateTimeField(verbose_name="Date et heure d'ouverture du tirage"), + field=models.DateTimeField( + verbose_name="Date et heure d'ouverture du tirage"), ), migrations.AlterField( model_name='tirage', diff --git a/bda/models.py b/bda/models.py index 8a7d0814..270921be 100644 --- a/bda/models.py +++ b/bda/models.py @@ -32,7 +32,8 @@ class Tirage(models.Model): default=False) def date_no_seconds(self): - return self.fermeture.strftime('%d %b %Y %H:%M') + return self.fermeture.astimezone(timezone.get_current_timezone()) \ + .strftime('%d %b %Y %H:%M') def __str__(self): return "%s - %s" % (self.title, self.date_no_seconds()) @@ -89,7 +90,8 @@ class Spectacle(models.Model): return "%d" % calendar.timegm(self.date.utctimetuple()) def date_no_seconds(self): - return self.date.strftime('%d %b %Y %H:%M') + return self.date.astimezone(timezone.get_current_timezone()) \ + .strftime('%d %b %Y %H:%M') def __str__(self): return "%s - %s, %s, %.02f€" % (self.title, self.date_no_seconds(), diff --git a/bda/templates/descriptions.html b/bda/templates/descriptions.html index 3ab514f2..26da76b6 100644 --- a/bda/templates/descriptions.html +++ b/bda/templates/descriptions.html @@ -3,6 +3,7 @@ + + {% for show in shows %} @@ -46,26 +53,61 @@ - + - - - - + {% if show.vips %} + + + + {% endif %} + {% if show.image %} - + {% endif %}

{{ show.location }}

{{ show.category }}

{{ show.date }}

{{ show.slots }} place{{ show.slots|pluralize}} {% if show.slots_description != "" %}({{ show.slots_description }}){% endif %}- {{ show.price }}

{{ show.date|date:"l j F Y - H\hi" }}

{{ show.slots }} place{{ show.slots|pluralize}} {% if show.slots_description != "" %}({{ show.slots_description }}){% endif %} - {{ show.price }} euro{{ show.price|pluralize}}

{{ show.category }}

{{ show.vips }}

{{ show.description }}

{% for quote in show.quote_set.all %} -

«{{ quote.text }}»{% if show.quote.author %} - {{ quote.author }}{% endif %}

+

«{{ quote.text }}»{% if quote.author %} - {{ quote.author }}{% endif %}

{% endfor %}

{{ show.title }}

{{ show.title }}

{% endfor %} + diff --git a/bda/views.py b/bda/views.py index 4ea0df32..4cf3dd03 100644 --- a/bda/views.py +++ b/bda/views.py @@ -66,7 +66,7 @@ def etat_places(request, tirage_id): def _hash_queryset(queryset): - data = serializers.serialize("json", queryset).encode() + data = serializers.serialize("json", queryset).encode('utf-8') hasher = hashlib.sha256() hasher.update(data) return hasher.hexdigest() diff --git a/cof/asgi.py b/cof/asgi.py new file mode 100644 index 00000000..a34621c7 --- /dev/null +++ b/cof/asgi.py @@ -0,0 +1,7 @@ +import os +from channels.asgi import get_channel_layer + +if "DJANGO_SETTINGS_MODULE" not in os.environ: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings") + +channel_layer = get_channel_layer() diff --git a/cof/settings_dev.py b/cof/settings_dev.py index 54797fc5..38d36390 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -29,7 +29,7 @@ SECRET_KEY = 'q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['127.0.0.1'] # Application definition @@ -123,6 +123,7 @@ USE_TZ = True # https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = '/static/' +STATIC_ROOT = '/var/www/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static/'), @@ -151,12 +152,12 @@ PETITS_COURS_REPLYTO = "cof@ens.fr" RAPPEL_FROM = 'Le BdA ' RAPPEL_REPLY_TO = RAPPEL_FROM -LOGIN_URL = "/login" -LOGIN_REDIRECT_URL = "/" +LOGIN_URL = "/gestion/login" +LOGIN_REDIRECT_URL = "/gestion/" CAS_SERVER_URL = 'https://cas.eleves.ens.fr/' CAS_IGNORE_REFERER = True -CAS_REDIRECT_URL = '/' +CAS_REDIRECT_URL = '/gestion/' CAS_EMAIL_FORMAT = "%s@clipper.ens.fr" AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', @@ -175,12 +176,13 @@ CHANNEL_LAYERS = { "default": { "BACKEND": "asgi_redis.RedisChannelLayer", "CONFIG": { - "hosts": [("redis://:password_redis@127.0.0.1:6379/0")], + "hosts": [("localhost", 6379)], }, "ROUTING": "cof.routing.channel_routing", } } + def show_toolbar(request): """ On ne veut pas la vérification de INTERNAL_IPS faite par la debug-toolbar diff --git a/cof/urls.py b/cof/urls.py index ca7ea247..263fc3a0 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -24,7 +24,7 @@ from gestioncof.autocomplete import autocomplete autocomplete_light.autodiscover() admin.autodiscover() -urlpatterns = [ +my_urlpatterns = [ # Page d'accueil url(r'^$', gestioncof_views.home, name='home'), # Le BdA @@ -88,3 +88,7 @@ urlpatterns = [ else []) # Si on est en production, MEDIA_ROOT est servi par Apache. # Il faut dire à Django de servir MEDIA_ROOT lui-même en développement. + +urlpatterns = [ + url(r'^gestion/', include(my_urlpatterns)) +] diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 7aedf089..58b37115 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -6,16 +6,18 @@ from __future__ import unicode_literals from django import forms from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \ CofProfile, EventOption, EventOptionChoice, Event, Club, CustomMail, \ Survey, EventCommentField, EventRegistration from gestioncof.petits_cours_models import PetitCoursDemande, \ PetitCoursSubject, PetitCoursAbility, PetitCoursAttribution, \ PetitCoursAttributionCounter -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.admin import UserAdmin from django.core.urlresolvers import reverse from django.utils.safestring import mark_safe +from django.db.models import Q import django.utils.six as six import autocomplete_light @@ -163,6 +165,7 @@ class UserProfileAdmin(UserAdmin): return False is_cof.short_description = 'Membre du COF' is_cof.boolean = True + list_display = ('profile_num',) + UserAdmin.list_display \ + ('profile_login_clipper', 'profile_phone', 'profile_occupation', 'profile_mailing_cof', 'profile_mailing_bda', @@ -176,6 +179,40 @@ class UserProfileAdmin(UserAdmin): CofProfileInline, ] + staff_fieldsets = [ + (None, {'fields': ['username', 'password']}), + (_('Personal info'), {'fields': ['first_name', 'last_name', 'email']}), + ] + + def get_fieldsets(self, request, user=None): + if not request.user.is_superuser: + return self.staff_fieldsets + return super(UserProfileAdmin, self).get_fieldsets(request, user) + + def save_model(self, request, user, form, change): + cof_group, created = Group.objects.get_or_create(name='COF') + if created: + # Si le groupe COF n'était pas déjà dans la bdd + # On lui assigne les bonnes permissions + perms = Permission.objects.filter( + Q(content_type__app_label='gestioncof') + | Q(content_type__app_label='bda') + | (Q(content_type__app_label='auth') + & Q(content_type__model='user'))) + cof_group.permissions = perms + # On y associe les membres du Burô + cof_group.user_set = User.objects.filter(profile__is_buro=True) + # Sauvegarde + cof_group.save() + # le Burô est staff et appartient au groupe COF + if user.profile.is_buro: + user.is_staff = True + user.groups.add(cof_group) + else: + user.is_staff = False + user.groups.remove(cof_group) + user.save() + # FIXME: This is absolutely horrible. def user_unicode(self): diff --git a/gestioncof/autocomplete.py b/gestioncof/autocomplete.py index 5b4616be..ed0a1e5a 100644 --- a/gestioncof/autocomplete.py +++ b/gestioncof/autocomplete.py @@ -10,8 +10,10 @@ from django.db.models import Q from django.contrib.auth.models import User from gestioncof.models import CofProfile, Clipper +from gestioncof.decorators import buro_required +@buro_required def autocomplete(request): if "q" not in request.GET: raise Http404 diff --git a/gestioncof/migrations/0008_py3.py b/gestioncof/migrations/0008_py3.py index 57a20c81..7d94d7ce 100644 --- a/gestioncof/migrations/0008_py3.py +++ b/gestioncof/migrations/0008_py3.py @@ -4,6 +4,11 @@ from __future__ import unicode_literals from django.db import models, migrations +def forwards(apps, schema_editor): + Profile = apps.get_model("gestioncof", "CofProfile") + Profile.objects.update(comments="") + + class Migration(migrations.Migration): dependencies = [ @@ -24,57 +29,85 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='cofprofile', name='comments', - field=models.TextField(verbose_name='Commentaires visibles uniquement par le Buro', blank=True), + field=models.TextField( + verbose_name="Commentaires visibles par l'utilisateur", + blank=True), ), migrations.AlterField( model_name='cofprofile', name='is_cof', - field=models.BooleanField(verbose_name='Membre du COF', default=False), + field=models.BooleanField(verbose_name='Membre du COF', + default=False), ), migrations.AlterField( model_name='cofprofile', name='login_clipper', - field=models.CharField(verbose_name='Login clipper', max_length=8, blank=True), + field=models.CharField(verbose_name='Login clipper', max_length=8, + blank=True), ), migrations.AlterField( model_name='cofprofile', name='mailing_bda', - field=models.BooleanField(verbose_name='Recevoir les mails BdA', default=False), + field=models.BooleanField(verbose_name='Recevoir les mails BdA', + default=False), ), migrations.AlterField( model_name='cofprofile', name='mailing_bda_revente', - field=models.BooleanField(verbose_name='Recevoir les mails de revente de places BdA', default=False), + field=models.BooleanField( + verbose_name='Recevoir les mails de revente de places BdA', + default=False), ), migrations.AlterField( model_name='cofprofile', name='mailing_cof', - field=models.BooleanField(verbose_name='Recevoir les mails COF', default=False), + field=models.BooleanField(verbose_name='Recevoir les mails COF', + default=False), ), migrations.AlterField( model_name='cofprofile', name='occupation', - field=models.CharField(verbose_name='Occupation', choices=[('exterieur', 'Extérieur'), ('1A', '1A'), ('2A', '2A'), ('3A', '3A'), ('4A', '4A'), ('archicube', 'Archicube'), ('doctorant', 'Doctorant'), ('CST', 'CST')], default='1A', max_length=9), + field=models.CharField(verbose_name='Occupation', + choices=[('exterieur', 'Extérieur'), + ('1A', '1A'), + ('2A', '2A'), + ('3A', '3A'), + ('4A', '4A'), + ('archicube', 'Archicube'), + ('doctorant', 'Doctorant'), + ('CST', 'CST')], + max_length=9, default='1A'), ), migrations.AlterField( model_name='cofprofile', name='petits_cours_accept', - field=models.BooleanField(verbose_name='Recevoir des petits cours', default=False), + field=models.BooleanField(verbose_name='Recevoir des petits cours', + default=False), ), migrations.AlterField( model_name='cofprofile', name='petits_cours_remarques', - field=models.TextField(verbose_name='Remarques et précisions pour les petits cours', default='', blank=True), + field=models.TextField( + blank=True, + verbose_name='Remarques et précisions pour les petits cours', + default=''), ), migrations.AlterField( model_name='cofprofile', name='type_cotiz', - field=models.CharField(verbose_name='Type de cotisation', choices=[('etudiant', 'Normalien étudiant'), ('normalien', 'Normalien élève'), ('exterieur', 'Extérieur')], default='normalien', max_length=9), + field=models.CharField( + verbose_name='Type de cotisation', + choices=[('etudiant', 'Normalien étudiant'), + ('normalien', 'Normalien élève'), + ('exterieur', 'Extérieur')], + max_length=9, default='normalien'), ), migrations.AlterField( model_name='custommail', name='comments', - field=models.TextField(verbose_name='Informations contextuelles sur le mail', blank=True), + field=models.TextField( + verbose_name='Informations contextuelles sur le mail', + blank=True), ), migrations.AlterField( model_name='custommail', @@ -94,12 +127,14 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='end_date', - field=models.DateTimeField(verbose_name='Date de fin', null=True, blank=True), + field=models.DateTimeField(null=True, verbose_name='Date de fin', + blank=True), ), migrations.AlterField( model_name='event', name='image', - field=models.ImageField(verbose_name='Image', upload_to='imgs/events/', null=True, blank=True), + field=models.ImageField(upload_to='imgs/events/', null=True, + verbose_name='Image', blank=True), ), migrations.AlterField( model_name='event', @@ -109,7 +144,8 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='event', name='registration_open', - field=models.BooleanField(verbose_name='Inscriptions ouvertes', default=True), + field=models.BooleanField(verbose_name='Inscriptions ouvertes', + default=True), ), migrations.AlterField( model_name='event', @@ -119,7 +155,10 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='eventcommentfield', name='fieldtype', - field=models.CharField(verbose_name='Type', choices=[('text', 'Texte long'), ('char', 'Texte court')], default='text', max_length=10), + field=models.CharField(verbose_name='Type', + choices=[('text', 'Texte long'), + ('char', 'Texte court')], + max_length=10, default='text'), ), migrations.AlterField( model_name='eventcommentfield', @@ -129,12 +168,14 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='eventcommentvalue', name='content', - field=models.TextField(verbose_name='Contenu', null=True, blank=True), + field=models.TextField(null=True, verbose_name='Contenu', + blank=True), ), migrations.AlterField( model_name='eventoption', name='multi_choices', - field=models.BooleanField(verbose_name='Choix multiples', default=False), + field=models.BooleanField(verbose_name='Choix multiples', + default=False), ), migrations.AlterField( model_name='eventoption', @@ -149,7 +190,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='petitcoursability', name='niveau', - field=models.CharField(verbose_name='Niveau', choices=[('college', 'Collège'), ('lycee', 'Lycée'), ('prepa1styear', 'Prépa 1ère année / L1'), ('prepa2ndyear', 'Prépa 2ème année / L2'), ('licence3', 'Licence 3'), ('other', 'Autre (préciser dans les commentaires)')], max_length=12), + field=models.CharField( + choices=[('college', 'Collège'), ('lycee', 'Lycée'), + ('prepa1styear', 'Prépa 1ère année / L1'), + ('prepa2ndyear', 'Prépa 2ème année / L2'), + ('licence3', 'Licence 3'), + ('other', 'Autre (préciser dans les commentaires)')], + max_length=12, verbose_name='Niveau'), ), migrations.AlterField( model_name='petitcoursattribution', @@ -159,22 +206,32 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='petitcoursattributioncounter', name='count', - field=models.IntegerField(verbose_name="Nombre d'envois", default=0), + field=models.IntegerField(verbose_name="Nombre d'envois", + default=0), ), migrations.AlterField( model_name='petitcoursdemande', name='niveau', - field=models.CharField(verbose_name='Niveau', choices=[('college', 'Collège'), ('lycee', 'Lycée'), ('prepa1styear', 'Prépa 1ère année / L1'), ('prepa2ndyear', 'Prépa 2ème année / L2'), ('licence3', 'Licence 3'), ('other', 'Autre (préciser dans les commentaires)')], default='', max_length=12), + field=models.CharField( + verbose_name='Niveau', + choices=[('college', 'Collège'), ('lycee', 'Lycée'), + ('prepa1styear', 'Prépa 1ère année / L1'), + ('prepa2ndyear', 'Prépa 2ème année / L2'), + ('licence3', 'Licence 3'), + ('other', 'Autre (préciser dans les commentaires)')], + max_length=12, default=''), ), migrations.AlterField( model_name='survey', name='old', - field=models.BooleanField(verbose_name='Archiver (sondage fini)', default=False), + field=models.BooleanField(verbose_name='Archiver (sondage fini)', + default=False), ), migrations.AlterField( model_name='survey', name='survey_open', - field=models.BooleanField(verbose_name='Sondage ouvert', default=True), + field=models.BooleanField(verbose_name='Sondage ouvert', + default=True), ), migrations.AlterField( model_name='survey', @@ -184,11 +241,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='surveyquestion', name='multi_answers', - field=models.BooleanField(verbose_name='Choix multiples', default=False), + field=models.BooleanField(verbose_name='Choix multiples', + default=False), ), migrations.AlterField( model_name='surveyquestion', name='question', field=models.CharField(verbose_name='Question', max_length=200), ), + migrations.RunPython(forwards, migrations.RunPython.noop), ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 382a5750..19590aff 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -62,7 +62,7 @@ class CofProfile(models.Model): mailing_bda_revente = models.BooleanField( "Recevoir les mails de revente de places BdA", default=False) comments = models.TextField( - "Commentaires visibles uniquement par le Buro", blank=True) + "Commentaires visibles par l'utilisateur", blank=True) is_buro = models.BooleanField("Membre du Burô", default=False) petits_cours_accept = models.BooleanField( "Recevoir des petits cours", default=False) diff --git a/gestioncof/petits_cours_views.py b/gestioncof/petits_cours_views.py index 7d54766e..1a31115d 100644 --- a/gestioncof/petits_cours_views.py +++ b/gestioncof/petits_cours_views.py @@ -135,7 +135,7 @@ def _finalize_traitement(request, demande, proposals, proposed_for, unsatisfied, attribdata, redo=False, errors=None): proposals = proposals.items() proposed_for = proposed_for.items() - attribdata = attribdata.items() + attribdata = list(attribdata.items()) proposed_mails = _generate_eleve_email(demande, proposed_for) mainmail = render_template("petits-cours-mail-demandeur.txt", {"proposals": proposals, @@ -153,7 +153,8 @@ def _finalize_traitement(request, demande, proposals, proposed_for, "proposed_mails": proposed_mails, "mainmail": mainmail, "attribdata": - base64.b64encode(simplejson.dumps(attribdata)), + base64.b64encode(simplejson.dumps(attribdata) + .encode('utf_8')), "redo": redo, "errors": errors, }) diff --git a/gestioncof/shared.py b/gestioncof/shared.py index fc901d50..87021a00 100644 --- a/gestioncof/shared.py +++ b/gestioncof/shared.py @@ -58,8 +58,7 @@ class COFCASBackend(CASBackend): if not user.email: user.email = settings.CAS_EMAIL_FORMAT % profile.login_clipper user.save() - if profile.is_buro and not user.is_superuser: - user.is_superuser = True + if profile.is_buro and not user.is_staff: user.is_staff = True user.save() return user diff --git a/gestioncof/templates/base.html b/gestioncof/templates/base.html index 185ebd44..2a718951 100644 --- a/gestioncof/templates/base.html +++ b/gestioncof/templates/base.html @@ -4,10 +4,12 @@ {{ site.name }} - - + + + + {% block extra_head %}{% endblock %} diff --git a/gestioncof/templates/profile.html b/gestioncof/templates/profile.html index 6ba101e9..7b185150 100644 --- a/gestioncof/templates/profile.html +++ b/gestioncof/templates/profile.html @@ -13,10 +13,18 @@ {% csrf_token %} {% for field in form %} - {{ field | bootstrap}} + {{ field | bootstrap }} {% endfor %} + {% if user.profile.comments %} +
+

Commentaires

+

+ {{ user.profile.comments }} +

+
+ {% endif %}
diff --git a/gestioncof/views.py b/gestioncof/views.py index 3148308a..80f528f7 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -346,7 +346,7 @@ def registration_form2(request, login_clipper=None, username=None): registration_set_ro_fields(user_form, profile_form) # events & clubs event_formset = EventFormset(events=events, prefix='events') - clubs_form = ClubsForm(initial={'clubs': member.clubs.all()}) + clubs_form = ClubsForm() if username: member = get_object_or_404(User, username=username) (profile, _) = CofProfile.objects.get_or_create(user=member) diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index ca401ccb..0eceebd2 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -1240,9 +1240,10 @@ $(document).ready(function() { case 72: if (e.ctrlKey) { // Ctrl+H - Display help + e.preventDefault(); $('.help').show('fast'); } - return false; + return true; case 112: // F1 - Cool reset coolReset(); diff --git a/provisioning/apache.conf b/provisioning/apache.conf new file mode 100644 index 00000000..001c6ec9 --- /dev/null +++ b/provisioning/apache.conf @@ -0,0 +1,29 @@ + + ServerName default + DocumentRoot /var/www/html + + ProxyPreserveHost On + ProxyRequests Off + ProxyPass /static/ ! + ProxyPass /media/ ! + ProxyPass /ws/ ws://127.0.0.1:8001/ws/ + ProxyPass / http://127.0.0.1:8001/ + ProxyPassReverse / http://127.0.0.1:8001/ + + Alias /media /vagrant/media + Alias /static /var/www/static + + Order deny,allow + Allow from all + + + Order deny,allow + Allow from all + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/provisioning/bootstrap.sh b/provisioning/bootstrap.sh index 304d8cd0..f072e6fc 100644 --- a/provisioning/bootstrap.sh +++ b/provisioning/bootstrap.sh @@ -9,7 +9,7 @@ DBPASSWD="4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" # Installation de paquets utiles apt-get update && apt-get install -y mercurial python-pip python-dev \ - libmysqlclient-dev libjpeg-dev git + libmysqlclient-dev libjpeg-dev git redis-server # Configuration et installation de mysql. Le mot de passe root est le même que # le mot de passe pour l'utilisateur local - pour rappel, ceci est une instance @@ -21,8 +21,15 @@ apt-get install -y mysql-server mysql -uroot -p$DBPASSWD -e "CREATE DATABASE $DBNAME; GRANT ALL PRIVILEGES ON $DBNAME.* TO '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASSWD'" -# Installation de redis-server. Todo: lui mettre un mot de passe -apt-get install -y redis-server +# Installation et configuration d'Apache +apt-get install -y apache2 +a2enmod proxy proxy_http +cp /vagrant/provisioning/apache.conf /etc/apache2/sites-available/gestiocof.conf +a2ensite gestiocof +a2dissite 000-default +service apache2 restart +mkdir /var/www/static +chown -R vagrant:www-data /var/www/static # Mise en place du .bash_profile pour tout configurer lors du `vagrant ssh` cat > ~vagrant/.bash_profile <