Browse Source

Enable basic form saving behaviour

- Saving works
- Some fields are missing
- field-level errors are handled
- Form controls are ugly
- Static content is still 404
Jocelyn Delande 9 years ago
parent
commit
8994f84979

+ 22 - 0
wifiwithme/apps/contribmap/forms.py

@@ -0,0 +1,22 @@
+from django.forms import ModelForm
+
+from .models import Contrib
+
+
+class PublicContribForm(ModelForm):
+    class Meta:
+        model = Contrib
+
+        fields = [
+            'name', 'contrib_type',
+            'latitude', 'longitude',
+            'phone', 'email',
+            'comment',
+            'access_type',
+            'connect_local', 'connect_internet',
+            'bandwidth', 'share_part',
+            'floor', 'floor_total', 'orientations', 'roof',
+            'comment',
+            'privacy_name', 'privacy_email', 'privacy_coordinates',
+            'privacy_place_details', 'privacy_comment',
+        ]

+ 6 - 6
wifiwithme/apps/contribmap/templates/contribmap/base.html

@@ -26,16 +26,16 @@
 
   <header class="main-header jumbotron">
     <div class="container">
-    %if page == 'form':
-    <h1>Réseau wifi expérimental</h1>
-    %else:
-    <h1><a href="./">Réseau wifi expérimental</a></h1>
-    %end
+    <h1>
+    {% block title %}
+      <a href="./">Réseau wifi expérimental</a>
+    {% endblock %}
+    </h1>
     </div>
   </header>
 
   <section role="main" class="container">
-  %include
+    {% block content %}{% endblock %}
   </section>
 
   <footer>

+ 5 - 1
wifiwithme/apps/contribmap/templates/contribmap/thanks.html

@@ -1,4 +1,6 @@
-%rebase base page='thanks'
+{% extends "base.html" %}
+
+{% block content %}
 
 <h1>Merci !</h1>
 
@@ -16,3 +18,5 @@ l'association.
 <p>
 Vous pouvez consulter la <a href="./map">carte publique avec tous les autres contributions</a>.
 </p>
+
+{% endblock %}

+ 37 - 191
wifiwithme/apps/contribmap/templates/contribmap/wifi-form.html

@@ -1,23 +1,12 @@
-%rebase base page='form'
+{% extends "base.html" %}
+
+{% load bootstrap %}
+
+{% block content %}
   <script src="assets/form.js" type="text/javascript"></script>
 
   <header class="jumbotron">
     <div class="container">
-%if errors:
-
-<p>
-  Veuillez corriger les <span class="label label-danger">erreurs</span>
-  suivantes :
-</p>
-<div id="errors" class="bg-danger">
-<ul >
-%for field, err in set(errors):
-  <li><strong>{{field}}</strong> : {{err}}</li>
-%end
-</ul>
-</div>
-
-%else:
     <p>
 L'association <a href="//www.faimaison.net">FAImaison</a> expérimente à
 grande échelle (Nantes et environs) la création d'un réseau sans-fil à
@@ -36,25 +25,31 @@ Renseigner ce formulaire nous permet de définir quelles <strong>zones
 d'expérimentations</strong> (avec une grande densité de volontaires)
 pourraient être intéressantes.
       </p>
-%end
     </div>
   </header>
 
   <section role="main" class="container">
-  <form role="form" method="post">
+  <form role="form" method="post">{% csrf_token %}
+
+{% if form.non_field_errors %}
+    <div id="errors" class="bg-danger">
+      {{ form.non_field_errors }}
+    </div>
+{% endif %}
 
     <h2>Contact</h2>
+
     <div class="form-group">
-    <label for="name">Nom / Pseudo</label>
-    <input name="name" value="{{data.get('name', '')}}"
-           id="name" type="text" class="form-control"/>
+      <label for="name">Nom / Pseudo</label>
+      {{ form.name|formcontrol }}
+      {{ form.name.errors }}
     </div>
 
     <div class="row">
       <div class="form-group col-md-6">
         <label for="email">Email</label>
-        <input name="email" value="{{data.get('email', '')}}"
-               id="email" type="email" class="form-control">
+        {{ form.email|formcontrol }}
+        {{ form.email.errors }}
         <p class="help-block">
           <span class="glyphicon glyphicon-warning-sign"></span>
           Un moyen de contact au moins est nécessaire
@@ -62,50 +57,25 @@ pourraient être intéressantes.
       </div>
       <div class="form-group col-md-6">
         <label for="phone">Téléphone</label>
-        <input name="phone" value="{{data.get('phone', '')}}"
-               id="phone" type="tel" class="form-control"/>
+        {{ form.phone|formcontrol }}
+        {{ form.phone.errors }}
       </div>
     </div>
 
+
     <h2>Je souhaite</h2>
     <p class="radio">
-      <label>
-      <input type="radio" name="contrib-type" value="share"
-             {{'checked' if data.get('contrib-type') == 'share' else ''}}/>
-      Partager une partie de ma connexion
-      </label>
-    </p>
-    <p class="radio">
-      <label>
-      <input type="radio" name="contrib-type" value="connect"
-             {{'checked' if data.get('contrib-type') == 'connect' else ''}}/>
-      Me raccorder au réseau expérimental
-      </label>
+      {{ form.contrib_type }}
+      {{ form.contrib_type.errors }}
     </p>
 
     <div id="contrib-type-share">
     <h2>Partager une connexion</h2>
+
     <h3>Type de connexion</h3>
-    <p class="radio"><label>
-      <input {{'checked' if data.get('access-type') == 'fiber' else ''}}
-         type="radio" name="access-type" value="fiber"/>
-      Fibre
-    </label></p>
-    <p class="radio"><label>
-      <input {{'checked' if data.get('access-type') == 'vdsl' else ''}}
-      type="radio" name="access-type" value="vdsl"/>
-      VDSL
-    </label></p>
-    <p class="radio"><label>
-      <input {{'checked' if data.get('access-type') == 'adsl' else ''}}
-             type="radio" name="access-type" value="adsl"/>
-      ADSL
-    </label></p>
-    <p class="radio"><label>
-      <input {{'checked' if data.get('access-type') == 'cable' else ''}}
-             type="radio" name="access-type" value="cable"/>
-      Câble
-    </label></p>
+    {{ form.access_type }}
+    {{ form.access_type.errors }}
+
     <h3>Débits</h3>
     <p class="help-block">
       Il est possible de limiter techniquement la quantité de bande passante
@@ -114,159 +84,35 @@ pourraient être intéressantes.
     </p>
     <p>
       <label for="bandwidth">Débit total</label>
-      <input name="bandwidth" value="{{data.get('bandwidth', '')}}"
-             id="bandwidth" type="number" min="0" class="form-control"
-             placeholder="(débit descendant, en Mbps)"/>
+      {{ form.bandwidth }}
+      {{ form.bandwidth.errors }}
     </p>
     <p>
       <label for="share-part">Je souhaite partager (au max.)</label>
-      <input name="share-part" value="{{data.get('share-part', '')}}"
-             id="share-part" type="number" min="0" class="form-control"
-             placeholder="(débit descendant, en Mbps)"/>
+      {{ form.share_part }}
+      {{ form.share_part.errors }}
     </p>
     </div>
 
     <div id="contrib-type-connect">
-    <h2>Me raccorder au réseau</h2>
+      <h2>Me raccorder au réseau</h2>
     <h3>J'aimerais pouvoir</h3>
     <p class="checkbox"><label>
-      <input {{'checked' if data.get('connect-type') == 'local' else ''}}
-      type="checkbox" name="connect-type" value="local"/>
+      {{ form.connect_local }}
       Proposer et utiliser des services en local avec les autres utilisateurs du réseau
+      {{ form.connect_local.errors }}
     </label></p>
     <p class="checkbox"><label>
-      <input {{'checked' if data.get('connect-type') == 'internet' else ''}}
-         type="checkbox" name="connect-type" value="internet"/>
+      {{ form.connect_internet }}
       Avoir un accès à Internet
+      {{ form.connect_internet.errors }}
     </label></p>
     </div>
 
-    <h2>Ma localisation</h2>
-
-    <div class="row">
-      <div class="col-sm-6">
-        <div id="map" data-json="{{geojson}}"></div>
-      </div>
-      <div class="form-group col-sm-6">
-        <div class="form-group form-group-lg form-inline">
-          <input type="text" name="search"
-                 id="search" placeholder="rue du calvaire, nantes" class="form-control" />
-          <span id="search-btn" class="btn btn-default btn-lg" data-loading-text="...">Recherche</span>
-
-          <div id="search-results" class=""></div>
-          <p class="help-block">Déplacer le marqueur bleu pour pointer précisément le bâtiment au besoin</p>
-          <p class="help-block">
-            Les ronds verts sont ceux renseignés par d'autres utilisateurs, vous
-            pouvez aussi consulter <a href="map" target="_blank">la carte
-            publique plus détaillée</a>.
-          </p>
-
-        </div>
-        <input name="latitude" value="{{data.get('latitude', '')}}"
-               type="hidden" id="latitude" />
-        <input name="longitude" value="{{data.get('longitude', '')}}"
-               type="hidden"  id="longitude" />
-        </div>
-      </div>
-    </div>
 
-    <p class="help-block">Les antennes peuvent être positionées soit sur le toit soit aux fenêtres/balcons/velux.</p>
 
-    <div class="form-group">
-    <label for="orientation" />Orientation(s) de mes fenêtres, balcons ou velux</label>
-    (<label class="checkbox-inline"><input type="checkbox" name="orientation-all" id="orientation-all" value="" />Vue à 360°</label>)
-    <br>
-%for val, label in orientations:
-    <label class="checkbox-inline">
-      <input type="checkbox" class="orientation" name="orientation" value="{{val}}"
-             {{'checked' if val in data.getall('orientation') else ''}}/>
-      {{label}}
-    </label>
-%end
-    </div>
-
-    <div class="form-group">
-        <label for="roof">Je peux accéder à mon toit
-          <input name="roof" {{'checked' if data.get('roof', False) else ''}}
-                 type="checkbox"/>
-        </label>
-    </div>
-
-    <p class="form-inline">
-      <label for="floor">Mon étage</label>
-      <input name="floor" value="{{data.get('floor', '')}}"
-             id="floor" type="number" class="form-control" placeholder="« 0 » pour le  RDC"/>
-      <span>/</span>
-      <input name="floor_total" value="{{data.get('floor_total', '')}}"
-             id="floor_total" type="number" class="form-control" placeholder="Nb. d'étages du bâtiment"/>
-    </p>
-
-    <h2>Remarque/commentaire</h2>
-    <textarea name="comment" class="form-control" row="5">{{data.get('comment', '').strip()}}</textarea>
-
-    <h2>Mes données</h2>
-
-    <p class="help-block">
-Les données collectées dans ce formulaire sont accessibles
-au bureau de FAImaison.<br />
-
-Vous pouvez cocher ci-dessous celles que vous voulez bien voir <a
-href="./legal">rendues publiques et librement réutilisées</a>.
-    </p>
-
-    <div class="form-group">
-    <label for="privacy" />
-J'autorise qu'apparaissent sur la carte publique :
-    </label><br />
-    </div>
-    <div class="checkbox">
-      <label>
-        <input type="checkbox" name="privacy" value="coordinates"
-        {{'checked' if (('coordinates' in data.getall('privacy')) or not data) else ''}} />
-        Mes coordonnées GPS
-      </label>
-    </div>
-    <div class="checkbox">
-      <label>
-        <input type="checkbox" name="privacy" value="place_details"
-        {{'checked' if (('place_details' in data.getall('privacy')) or not data) else ''}}/>
-
-        Mon étage et mes orientations
-      </label>
-    </div>
-    <div class="checkbox">
-      <label>
-        <input type="checkbox" name="privacy" value="name"
-        {{'checked' if 'name' in data.getall('privacy') else ''}}/>
-        Mon nom/pseudo
-      </label>
-    </div>
-    <div class="checkbox">
-      <label>
-        <input type="checkbox" name="privacy" value="comment"
-        {{'checked' if 'comment' in data.getall('privacy') else ''}}/>
-        Mon commentaire
-      </label>
-    </div>
-    <!-- if you are human, you do not see me: -->
-    <div style="display: none">
-      <input name="url" value="{{data.get('url', '')}}"
-             id="url" class="form-control">
-    </div>
     <input type="submit" value="Envoyer" class="btn btn-primary btn-lg"/>
   </form>
   </section>
 
-
-<div id="modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Resultats" aria-hidden="true">
-  <div class="modal-dialog modal-lg">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Fermer</span></button>
-        <h4 class="modal-title" id="myModalLabel">Résultats</h4>
-      </div>
-      <div class="modal-body">
-      </div>
-    </div>
-  </div>
-</div>
+{% endblock %}

+ 3 - 1
wifiwithme/apps/contribmap/urls.py

@@ -1,8 +1,10 @@
 from django.conf.urls import url
 
-from .views import PublicJSON, PrivateJSON
+from .views import PublicJSON, PrivateJSON, add_contrib, thanks
 
 urlpatterns = [
+    url(r'^contribute/thanks', thanks, name='thanks'),
+    url(r'^contribute', add_contrib, name='add_contrib'),
     url(r'^public.json$', PublicJSON.as_view(), name='public_json'),
     url(r'^private.json$', PrivateJSON.as_view(), name='private_json'),
 ]

+ 20 - 0
wifiwithme/apps/contribmap/views.py

@@ -1,9 +1,29 @@
+from django.core.urlresolvers import reverse
 from django.http import JsonResponse, HttpResponseForbidden
+from django.shortcuts import render, redirect
 from django.views.generic import View
 
+from .forms import PublicContribForm
 from .models import Contrib
 
 
+def add_contrib(request):
+    if request.method == 'GET':
+        form = PublicContribForm()
+    elif request.method == 'POST':
+        form = PublicContribForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return redirect(reverse('thanks'))
+    return render(request, 'contribmap/wifi-form.html', {
+        'form': form,
+    })
+
+
+def thanks(request):
+    return render(request, 'contribmap/thanks.html')
+
+
 class JSONContribView(View):
     def get(self, request):
         return JsonResponse({

+ 0 - 0
wifiwithme/core/templatetags/__init__.py


+ 13 - 0
wifiwithme/core/templatetags/bootstrap.py

@@ -0,0 +1,13 @@
+#
+from django import template
+
+register = template.Library()
+
+
+@register.filter(name='formcontrol')
+def formcontrol(field):
+    """Adds formcontrol class to an inputfield
+
+    For bootstrap form rendering
+    """
+    return field.as_widget(attrs={"class": 'form-control'})

+ 2 - 1
wifiwithme/settings/base.py

@@ -28,7 +28,7 @@ ALLOWED_HOSTS = []
 # Application definition
 
 # Add apps/ to the Python path
-sys.path = [os.path.join(BASE_DIR, 'apps')] + sys.path
+sys.path = [os.path.join(BASE_DIR, 'apps'), BASE_DIR] + sys.path
 
 INSTALLED_APPS = [
     'django.contrib.admin',
@@ -37,6 +37,7 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'core',
     'contribmap',
 ]