diff --git a/circle/bower.json b/circle/bower.json index 7be9709..458c729 100644 --- a/circle/bower.json +++ b/circle/bower.json @@ -19,6 +19,7 @@ "jquery-simple-slider": "https://github.com/BME-IK/jquery-simple-slider.git", "bootbox": "~4.3.0", "intro.js": "0.9.0", - "favico.js": "~0.3.5" + "favico.js": "~0.3.5", + "datatables": "~1.10.4" } } diff --git a/circle/circle/settings/base.py b/circle/circle/settings/base.py index 7fa1d38..e98a1b3 100644 --- a/circle/circle/settings/base.py +++ b/circle/circle/settings/base.py @@ -193,6 +193,7 @@ PIPELINE_JS = { "jquery-knob/dist/jquery.knob.min.js", "jquery-simple-slider/js/simple-slider.js", "favico.js/favico.js", + "datatables/media/js/jquery.dataTables.js", "dashboard/dashboard.js", "dashboard/activity.js", "dashboard/group-details.js", @@ -210,6 +211,7 @@ PIPELINE_JS = { "js/host.js", "js/network.js", "js/switch-port.js", + "js/host-list.js", "autocomplete_light/autocomplete.js", "autocomplete_light/widget.js", "autocomplete_light/addanother.js", diff --git a/circle/network/static/js/host-list.js b/circle/network/static/js/host-list.js new file mode 100644 index 0000000..49df838 --- /dev/null +++ b/circle/network/static/js/host-list.js @@ -0,0 +1,42 @@ +$(function() { + if($("#network-host-list-table").length) { + var order = ["hostname", "vlan", "mac", "ipv4", "ipv6", "external_ipv4", "created_at", "owner"] + var options = { + paging: false, + columnDefs: [ + { type: 'cloud-hostname', targets: 0}, + { type: 'ip-address', targets: [3,6]}, + ], + language: { + zeroRecords: gettext("No host found.") + } + } + table = createDataTable($("#network-host-list-table"), options, "sort", order); + + $("#network-host-list-input").keyup(function() { + table.search($(this).val()).draw(); + }); + $("#network-host-list-input").trigger("keyup"); + + $("#network-host-list-table_filter, #network-host-list-table_info").remove(); + } +}); + + +function createDataTable(table_element, options, sort_parameter_name, sort_order) { + var table = $(table_element).DataTable(options); + + var sort = getParameterByName(sort_parameter_name); + + var dir = "asc"; + var index = 0; + if(sort.length > 0 && sort[0] == "-") { + dir = "desc" + sort = sort.substr(1, sort.length - 1); + } + if(sort) + index = sort_order.indexOf(sort); + + table.order([[index, dir]]); + return table; +} diff --git a/circle/network/static/js/network.js b/circle/network/static/js/network.js index cef0875..f037d0e 100644 --- a/circle/network/static/js/network.js +++ b/circle/network/static/js/network.js @@ -74,3 +74,50 @@ $("#ipv6-tpl-magic").click(function() { }}); }); }); + +/* sort methods for DataTables */ +var hostname_max_0_len = 10; +var hostname_zeros = new Array(hostname_max_0_len).join("0"); +jQuery.extend( jQuery.fn.dataTableExt.oSort, { + "cloud-hostname-pre": function ( a ) { + var x = String(a).replace( /<[\s\S]*?>/g, "" ).replace(/^cloud\-/i, ""); + if(parseFloat(x) && x.length < hostname_max_0_len) { + x = hostname_zeros.substring(0, 10-x.length) + x; + } + return x; + }, + + "cloud-hostname-asc": function ( a, b ) { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + }, + + "cloud-hostname-desc": function ( a, b ) { + return ((a < b) ? 1 : ((a > b) ? -1 : 0)); + } +} ); + +jQuery.extend( jQuery.fn.dataTableExt.oSort, { + "ip-address-pre": function ( a ) { + var m = a.split("."), x = ""; + + for(var i = 0; i < m.length; i++) { + var item = m[i]; + if(item.length == 1) { + x += "00" + item; + } else if(item.length == 2) { + x += "0" + item; + } else { + x += item; + } + } + + return x; + }, + + "ip-address-asc": function ( a, b ) { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + }, + "ip-address-desc": function ( a, b ) { + return ((a < b) ? 1 : ((a > b) ? -1 : 0)); + } +} ); diff --git a/circle/network/tables.py b/circle/network/tables.py index b966ab6..9a0e397 100644 --- a/circle/network/tables.py +++ b/circle/network/tables.py @@ -79,10 +79,11 @@ class HostTable(Table): class Meta: model = Host - attrs = {'class': 'table table-striped table-condensed'} + attrs = {'class': "table table-striped table-condensed", + 'id': "network-host-list-table"} fields = ('hostname', 'vlan', 'mac', 'ipv4', 'ipv6', 'external_ipv4', 'created_at', 'owner', ) - order_by = ('vlan', 'hostname', ) + order_by = ("hostname", ) class SmallRuleTable(Table): diff --git a/circle/network/templates/network/host-list.html b/circle/network/templates/network/host-list.html index f3e073a..ade295a 100644 --- a/circle/network/templates/network/host-list.html +++ b/circle/network/templates/network/host-list.html @@ -8,24 +8,38 @@ {% block content %} <div class="page-header"> - <a href="{% url "network.host_create" %}" class="btn btn-success pull-right"><i class="fa fa-plus-circle"></i> {% trans "Create a new host" %}</a> - <h1> - {% trans "Hosts" %} - <small> - {% trans "list of all hosts" %} - </small> - </h1> + <a href="{% url "network.host_create" %}" class="btn btn-success pull-right"> + <i class="fa fa-plus-circle"></i> + {% trans "Create a new host" %} + </a> + <h1> + {% trans "Hosts" %} + <small>{% trans "list of all hosts" %}</small> + </h1> </div> <ul class="nav nav-pills" style="margin: 5px 0 20px 0;"> - <li class="disabled"><a href="#">{% trans "Filter by vlans" %}</a></li> - <li {% if not request.GET.vlan %} class="active"{% endif %}><a href="{{ request.path }}">{% trans "ALL" %}</a></li> - {% for vlan in vlans %} - <li{% if request.GET.vlan|add:"0" == vlan.id %} class="active"{% endif %}><a href="?vlan={{ vlan.id }}">{{ vlan.name }}</a></li> - {% endfor %} + <li class="disabled"><a href="#">{% trans "Filter by vlans" %}</a></li> + <li {% if not request.GET.vlan %} class="active"{% endif %}> + <a href="{{ request.path }}">{% trans "ALL" %}</a> + </li> + {% for vlan in vlans %} + <li{% if request.GET.vlan|add:"0" == vlan.id %} class="active"{% endif %}> + <a href="?vlan={{ vlan.id }}">{{ vlan.name }}</a> + </li> + {% endfor %} + <form action="" method="GET"> + <div class="input-group pull-right" style="max-width: 300px;"> + <input type="text" id="network-host-list-input" name="s" class="form-control" + value="{{ request.GET.s }}"/> + <span class="input-group-btn"> + <button class="btn btn-primary"><i class="fa fa-search"></i></button> + </span> + </div> + </form> </ul> <div class="table-responsive"> - {% render_table table %} + {% render_table table %} </div> {% endblock %} diff --git a/circle/network/views.py b/circle/network/views.py index bcb1c7a..2dfede1 100644 --- a/circle/network/views.py +++ b/circle/network/views.py @@ -22,6 +22,7 @@ from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse_lazy from django.shortcuts import render, redirect, get_object_or_404 from django.http import HttpResponse, Http404 +from django.db.models import Q from django_tables2 import SingleTableView @@ -375,6 +376,11 @@ class HostList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): data = Host.objects.filter(vlan=vlan_id).select_related() else: data = Host.objects.select_related() + + search = self.request.GET.get("s") + if search: + data = data.filter(Q(hostname__icontains=search) | + Q(ipv4=search)) # ipv4 does not work TODO return data