commit 1b996627565e6f8468ac8d1f02a5c5b31edb9d52
Author: Leonidas Poulopoulos <leopoul@noc.grnet.gr>
Date:   Wed Feb 22 21:15:00 2012 +0200

    Initial formset effort (with success)

diff --git a/flowspec/forms.py b/flowspec/forms.py
index 8f17e73..c92d36f 100644
--- a/flowspec/forms.py
+++ b/flowspec/forms.py
@@ -10,6 +10,7 @@ from django.contrib.auth.models import User
 from django.conf import settings
 import datetime
 from django.core.mail import mail_admins, mail_managers, send_mail
+from django.forms.models import modelformset_factory
 
 
 class RouteForm(forms.ModelForm):
@@ -211,6 +212,8 @@ class PortPlainForm(forms.ModelForm):
         else:
             raise forms.ValidationError('Cannot be empty')
 
+RuleFormSet = modelformset_factory(Route, extra=2) 
+
 def value_list_to_list(valuelist):
     vl = []
     for val in valuelist:
diff --git a/flowspec/views.py b/flowspec/views.py
index 1502e61..6301ba1 100644
--- a/flowspec/views.py
+++ b/flowspec/views.py
@@ -115,6 +115,14 @@ def add_route(request):
             return render_to_response('apply.html', {'form': form, 'applier':applier},
                                       context_instance=RequestContext(request))
 
+
+@login_required
+@never_cache
+def add_route_set(request):
+    applier = request.user.pk
+    formset = RuleFormSet()
+    return render_to_response('apply_set.html', {'formset': formset, 'applier':applier},
+                                      context_instance=RequestContext(request))
 @login_required
 @never_cache
 def edit_route(request, route_slug):
diff --git a/templates/apply_set.html b/templates/apply_set.html
new file mode 100644
index 0000000..312fc18
--- /dev/null
+++ b/templates/apply_set.html
@@ -0,0 +1,262 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block title %}
+	{% if edit %}
+		{% trans "Edit Rule" %} {{form.data.name}}
+	{% else %}
+		{% trans "Create new Rule" %}
+	{% endif %}
+{% endblock %}
+
+{% block breadcrumbs %}:: 
+	{% if edit %}
+		{% trans "Edit rule" %} {{form.data.name}}
+	{% else %}
+	{% trans "Create set of rules" %}
+	{% endif %}
+		{% endblock %}
+{% block extrahead %}
+<script>
+	$(document).ready( function(){
+		
+		$("#id_sourceport").css('width', '100px').attr('size', '5');
+		$("#id_port").css('width', '100px').attr('size', '5');
+		$("#id_destinationport").css('width', '100px').attr('size', '5');
+		$('#id_then').attr("multiple", "");
+		$( "#id_expires" ).datepicker({ dateFormat: 'yy-mm-dd' , maxDate: '+10d', minDate: '+1d', changeMonth: false, changeYear: false }).datepicker( $.datepicker.regional[ "el" ] );
+		{% comment %}
+		$('#then_diag').dialog({
+			height: 220,
+            width: 340,
+			modal: true,
+			autoOpen: false,
+			buttons: {
+		'Add': function() {
+			console.log($("#add_rl_form").serialize());
+			$.ajax({
+			url:"{% url add-rate-limit %}", 
+			data:$("#add_rl_form").serialize(),
+			type: "POST",
+			cache: false,
+			success:function(data){
+					try {
+						value = data.pk;
+						text = data.value;
+						$('#id_then').append($("<option></option>").attr("value",value).text(text));
+						$('#then_diag').dialog('close');
+					}
+					catch (exception) {
+						$('#then_diag').html(data);
+					}					
+				}
+				});
+		},
+		Cancel: function() {
+			$('#then_diag').dialog('close');
+		}
+	}
+		});
+		{% endcomment %}
+		$('#port_diag').dialog({
+			height: 220,
+            width: 340,
+			modal: true,
+			autoOpen: false,
+			buttons: {
+		'Add': function() {
+			$.ajax({
+			url:"{% url add-port %}", 
+			data:$("#add_port_form").serialize(),
+			type: "POST",
+			cache: false,
+			success:function(data){
+					try {
+						value = data.value;
+						text = data.text;
+						$('#id_port').append($("<option></option>").attr("value",value).text(text));
+						$('#id_destinationport').append($("<option></option>").attr("value",value).text(text));
+						$('#id_sourceport').append($("<option></option>").attr("value",value).text(text));
+						$('#port_diag').dialog('close');
+					}
+					catch (exception) {
+						$('#port_diag').html(data);
+					}					
+				}
+				});
+		},
+		Cancel: function() {
+			$('#port_diag').dialog('close');
+		}
+	}
+		});
+		
+		{% comment %}
+		$("#new_then_actions").button({
+            icons: {
+                primary: "ui-icon-plusthick"
+            },
+			})
+			.click(function(){
+				$.ajax({
+					url: "{% url add-rate-limit %}",
+					cache: false,
+					success: function(data){
+						$("#then_diag").html(data);
+					}
+				});
+				$('#then_diag').dialog('open');
+				return false;
+			});
+			
+			{% endcomment %}
+			$(".new_port").button({
+            icons: {
+                primary: "ui-icon-plusthick"
+            },
+			})
+			.click(function(){
+				$.ajax({
+					url: "{% url add-port %}",
+					cache: false,
+					success: function(data){
+						$("#port_diag").html(data);
+					}
+				});
+				$('#port_diag').dialog('open');
+				return false;
+			});
+			
+		$("#portsacc").accordion({
+			collapsible: true,
+			active: false,
+
+		});
+		
+		$("#applybutton").button();
+
+		});
+		
+</script>
+
+
+<style type="text/css">
+th {
+	text-align: right;
+	padding-right: 0.5em;
+	vertical-align: top;
+}
+
+.accord_wrapper{
+	height: 452px !important;
+} 
+
+.help {
+	font-style: italic;
+
+}
+div.roundbox, #portsacc, #id_comments{
+	-moz-border-radius: 5px; border-radius:5px; -webkit-border-radius: 5px;
+	-moz-box-shadow: 0 0 3px #AAAAAA; box-shadow: 0 0 3px #AAAAAA; -webkit-box-shadow: 0 0 3px #AAAAAA;
+	background-color: #F4F4F4;
+	border: 1px solid #FFFFFF;
+	clear: both;
+	float: left;
+	margin: 5px 0 5px 100px;
+	padding: 10px;
+    width: 620px;
+}
+#rule_form_wrapper{
+    margin: 15px auto;
+    position: relative;
+    text-align: center;
+    width: 800px;
+}
+
+#rule_form_container input:not([type="submit"]), #rule_form_container select {
+    background: none repeat scroll 0 0 #FFFFFF;
+    border: 1px solid #DDDDDD;
+	-moz-border-radius: 3px; border-radius:3px; -webkit-border-radius: 3px; 
+    float: left;
+    font-family: "Century Gothic",Helvetica,sans-serif;
+    font-size: 13px;
+    outline: medium none;
+    padding: 5px;
+    width: 180px;
+}
+
+#id_comments {
+    background: none repeat scroll 0 0 #FFFFFF;
+    border: 1px solid #DDDDDD;
+    border-radius: 3px 3px 3px 3px;
+    float: left;
+    font-family: "Century Gothic",Helvetica,sans-serif;
+    font-size: 13px;
+    outline: medium none;
+    padding: 5px;
+   	width: 622px;
+}
+
+#rule_form_container{
+	-moz-border-radius: 10px 10px 10px 10px; border-radius:10px; -webkit-border-radius: 10px;
+	-moz-box-shadow: 0 0 3px #AAAAAA; box-shadow: 0 0 3px #AAAAAA; -webkit-box-shadow: 0 0 3px #AAAAAA; 
+	background-color: #F9F9F9;
+	border: 2px solid #FFFFFF;
+	overflow: hidden;
+    width: 800px;
+	}
+#rule_form_container div label {
+    color: #666666;
+    float: left;
+    font-family: "Century Gothic",Helvetica,sans-serif;
+    font-size: 15px;
+    font-weight: bold;
+    line-height: 26px;
+    margin-right: 15px;
+    text-align: right;
+    text-shadow: 1px 1px 1px #FFFFFF;
+    width: 180px;
+}
+#rule_form_container p.submit {
+    background: none repeat scroll 0 0 transparent;
+    border: medium none;
+    box-shadow: none;
+}
+input {
+	width:100px;
+}
+
+
+</style>
+{% endblock %}
+{% block content %}
+<div align="center" id="rule_form_wrapper">
+{% load unescape %}
+    <form method="POST">
+{{ formset.non_form_errors.as_ul }}
+            {% csrf_token %}
+<table id="formset" class="form">
+	  <thead><tr>
+    <th>Name</th><th>Source</th><th>Destination</th><th>Then</th><th>Note</th>
+  </tr></thead>
+{% for form in formset.forms %}
+{{form.errors}}
+<tr>
+       <td>{{ form.name }}</td>
+	   <td>{{ form.source }}</td>
+	   <td>{{ form.destination }}</td>
+	   <td>{{ form.then }}</td>
+	   <td>{{ form.comments }}</td>
+    </tr>
+			{% endfor %}
+	</table>
+
+		<input type="submit" id="applybutton" value="{% trans "Apply" %}" />
+    </form>
+</div>
+<div id="then_diag" title="Add new rate-limit value">
+</div>
+<div id="port_diag" title="Add new port">
+</div>
+
+{% endblock %}
