diff --git a/netbox_topology_views/api/views.py b/netbox_topology_views/api/views.py
index 2c9b3f2..5d545d2 100644
--- a/netbox_topology_views/api/views.py
+++ b/netbox_topology_views/api/views.py
@@ -83,7 +83,7 @@ class SearchViewSet(ReadOnlyModelViewSet):
queryset = Device.objects.all()
serializer_class = TopologyDummySerializer
- def _filter(self, site, role, name, tags):
+ def _filter(self, site, role, name, tags, region):
filter_devices = Device.objects.all()
if name is not None:
filter_devices = filter_devices.filter(name__contains=name)
@@ -93,6 +93,8 @@ class SearchViewSet(ReadOnlyModelViewSet):
filter_devices = filter_devices.filter(device_role__id__in=role)
if tags is not None:
filter_devices = filter_devices.filter(tags__id__in=tags)
+ if region is not None:
+ filter_devices = filter_devices.filter(site__region__id__in=region)
return filter_devices
@@ -114,7 +116,11 @@ class SearchViewSet(ReadOnlyModelViewSet):
if tags == []:
tags = None
- devices = self._filter(sites, devicerole, name, tags)
+ regions = request.query_params.getlist('regions[]', None)
+ if regions == []:
+ regions = None
+
+ devices = self._filter(sites, devicerole, name, tags, regions)
nodes = []
edges = []
diff --git a/netbox_topology_views/static/netbox_topology_views/js/app.js b/netbox_topology_views/static/netbox_topology_views/js/app.js
index 19d8026..39ad013 100644
--- a/netbox_topology_views/static/netbox_topology_views/js/app.js
+++ b/netbox_topology_views/static/netbox_topology_views/js/app.js
@@ -1 +1,304 @@
-var graph=null,container=null,downloadButton=null,MIME_TYPE="image/png",canvas=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!1},nodes:{shape:"image",brokenImage:"../../static/netbox_topology_views/img/role-unknown.png",size:35,font:{multi:"md",face:"helvetica"}},edges:{length:100,width:2,font:{face:"helvetica"}},physics:{solver:"forceAtlas2Based"}};function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),csrftoken=jQuery("[name=csrfmiddlewaretoken]").val(),startLoadSearchBar(),handleButtonPress(),downloadButton=document.getElementById("btnDownloadImage")},!1)}function startLoadSearchBar(){$("#device-roles").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/device-roles/?brief=true",dataType:"json",type:"GET",data:function(e){return{q:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#sites").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/sites/?brief=true",dataType:"json",type:"GET",data:function(e){return{q:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#tags").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/extras/tags/?brief=true",dataType:"json",type:"GET",data:function(e){return{q:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}});var n=$("#device-roles");$.ajax({type:"GET",url:"../../api/plugins/topology-views/preselectdeviceroles/"}).then(function(e){$.each(e.results,function(e,t){var a=new Option(t.name,t.id,!0,!0);n.append(a).trigger("change"),n.trigger({type:"select2:select",params:{data:t}})})});var s=$("#tags");$.ajax({type:"GET",url:"../../api/plugins/topology-views/preselecttags/"}).then(function(e){$.each(e.results,function(e,t){var a=new Option(t.name,t.id,!0,!0);s.append(a).trigger("change"),s.trigger({type:"select2:select",params:{data:t}})})})}function handleButtonPress(){$("#search-form").submit(function(e){$("#status").html('Loading data'),e.preventDefault();var t=$("#name").val(),a=$("#device-roles").val(),n=$("#sites").val(),s=$("#tags").val();$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/",data:{name:t,"devicerole[]":a,"sites[]":n,"tags[]":s},headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",dataType:"json",success:function(e){$("#status").html('Drawing network'),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),canvas=document.getElementById("visgraph").getElementsByTagName("canvas")[0],graph.on("afterDrawing",function(){$("#status").html('Ready');var e=canvas.toDataURL(MIME_TYPE);downloadButton.href=e,downloadButton.download="topology"}),graph.on("dragEnd",function(e){dragged=this.getPositions(e.nodes),$.each(dragged,function(e,t){$("#coordstatus").html(""),$("#checkSaveCoordinates").is(":checked")&&$.ajax({url:"../../api/plugins/topology-views/save-coords/save_coords/",type:"PATCH",dataType:"json",headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",processData:!1,data:JSON.stringify({node_id:e,x:t.x,y:t.y}),error:function(e){$("#coordstatus").html('Failed to update coordinates')},success:function(e){$("#coordstatus").html('Updated coordinates')}})})})},error:function(e){$("#status").html('Something went wrong')}})})}
\ No newline at end of file
+var graph = null;
+var container = null;
+var downloadButton = null;
+var MIME_TYPE = "image/png";
+var canvas = null;
+var csrftoken = null;
+var nodes = new vis.DataSet();
+var edges = new vis.DataSet();
+var options = {
+ interaction: {
+ hover: true,
+ hoverConnectedEdges: true,
+ multiselect: false
+ },
+ nodes: {
+ shape: 'image',
+ brokenImage: '../../static/netbox_topology_views/img/role-unknown.png',
+ size: 35,
+ font: {
+ multi: 'md',
+ face: 'helvetica',
+ },
+ },
+ edges: {
+ length: 100,
+ width: 2,
+ font: {
+ face: 'helvetica',
+ },
+ },
+ physics: {
+ solver: 'forceAtlas2Based'
+ }
+};
+var selected_regions = [];
+
+function iniPlotboxIndex() {
+ document.addEventListener('DOMContentLoaded', function () {
+ container = document.getElementById('visgraph');
+ csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
+ startLoadSearchBar();
+ handleButtonPress();
+ downloadButton = document.getElementById('btnDownloadImage');
+ }, false);
+}
+
+function startLoadSearchBar() {
+ $('#device-roles').select2({
+ allowClear: true,
+ placeholder: "---------",
+ theme: "bootstrap",
+ multiple: true,
+ ajax: {
+ url: "../../api/dcim/device-roles/?brief=true",
+ dataType: "json",
+ type: "GET",
+ data: function (params) {
+ var queryParameters = {
+ q: params.term
+ }
+ return queryParameters;
+ },
+ processResults: function (data) {
+ return {
+ results: $.map(data.results, function (item) {
+ return {
+ text: item.name,
+ id: item.id
+ }
+ })
+ };
+ }
+ }
+ });
+ $('#sites').select2({
+ allowClear: true,
+ placeholder: "---------",
+ theme: "bootstrap",
+ multiple: true,
+ ajax: {
+ url: function (params) {
+ var base_url = "../../api/dcim/sites/?brief=true";
+ if (selected_regions.length == 0) {
+ return base_url;
+ }
+ else {
+ selected_regions.forEach(element => {
+ var tmp = "®ion_id=" + element;
+ base_url = base_url + tmp;
+ });
+ return base_url;
+ }
+
+ },
+ dataType: "json",
+ type: "GET",
+ data: function (params) {
+ var queryParameters = {
+ q: params.term
+ }
+ return queryParameters;
+ },
+ processResults: function (data) {
+ return {
+ results: $.map(data.results, function (item) {
+ return {
+ text: item.name,
+ id: item.id
+ }
+ })
+ };
+ }
+ }
+ });
+ $('#tags').select2({
+ allowClear: true,
+ placeholder: "---------",
+ theme: "bootstrap",
+ multiple: true,
+ ajax: {
+ url: "../../api/extras/tags/?brief=true",
+ dataType: "json",
+ type: "GET",
+ data: function (params) {
+ var queryParameters = {
+ q: params.term
+ }
+ return queryParameters;
+ },
+ processResults: function (data) {
+ return {
+ results: $.map(data.results, function (item) {
+ return {
+ text: item.name,
+ id: item.id
+ }
+ })
+ };
+ }
+ }
+ });
+ $('#regions').select2({
+ allowClear: true,
+ placeholder: "---------",
+ theme: "bootstrap",
+ multiple: true,
+ ajax: {
+ url: "../../api/dcim/regions/?brief=true",
+ dataType: "json",
+ type: "GET",
+ data: function (params) {
+ var queryParameters = {
+ q: params.term
+ }
+ return queryParameters;
+ },
+ processResults: function (data) {
+ return {
+ results: $.map(data.results, function (item) {
+ return {
+ text: item.name,
+ id: item.id
+ }
+ })
+ };
+ }
+ }
+ });
+
+ $('#regions').on('select2:select', function (e) {
+ var data = e.params.data;
+ var index = selected_regions.indexOf(data.id.toString());
+ if (index == -1) {
+ selected_regions.push(data.id.toString());
+ }
+ });
+ $('#regions').on('select2:unselect', function (e) {
+ var data = e.params.data;
+ var index = selected_regions.indexOf(data.id.toString());
+ console.log(index);
+ if (index > -1) {
+ selected_regions.splice(index, 1);
+ }
+ });
+ $('#regions').on('select2:clear', function (e) {
+ selected_regions = [];
+ });
+
+
+ var deviceRolesSelect = $('#device-roles');
+ $.ajax({
+ type: 'GET',
+ url: '../../api/plugins/topology-views/preselectdeviceroles/'
+ }).then(function (data) {
+ $.each(data.results, function (index, device_role_to_preload) {
+ var option = new Option(device_role_to_preload.name, device_role_to_preload.id, true, true);
+ deviceRolesSelect.append(option).trigger('change');
+ deviceRolesSelect.trigger({
+ type: 'select2:select',
+ params: {
+ data: device_role_to_preload
+ }
+ });
+ });
+ });
+
+ var tagsSelect = $('#tags');
+ $.ajax({
+ type: 'GET',
+ url: '../../api/plugins/topology-views/preselecttags/'
+ }).then(function (data) {
+ $.each(data.results, function (index, tags_to_preload) {
+ var option = new Option(tags_to_preload.name, tags_to_preload.id, true, true);
+ tagsSelect.append(option).trigger('change');
+ tagsSelect.trigger({
+ type: 'select2:select',
+ params: {
+ data: tags_to_preload
+ }
+ });
+ });
+ });
+}
+
+function handleButtonPress() {
+ $("#search-form").submit(function (event) {
+ $("#status").html('Loading data');
+ event.preventDefault();
+ var value = $("#name").val();
+ var value2 = $("#device-roles").val();
+ var value3 = $("#sites").val();
+ var value4 = $("#tags").val();
+ var value5 = $("#regions").val();
+ $.ajax({
+ type: "GET",
+ url: "../../api/plugins/topology-views/search/search/",
+ data: {
+ 'name': value,
+ 'devicerole[]': value2,
+ 'sites[]': value3,
+ 'tags[]': value4,
+ 'regions[]': value5
+ },
+ headers: { "X-CSRFToken": csrftoken },
+ contentType: "application/json; charset=utf-8",
+ dataType: "json",
+ success: function (data_result) {
+ $("#status").html('Drawing network');
+ graph = null;
+ nodes = new vis.DataSet();
+ edges = new vis.DataSet();
+ graph = new vis.Network(container, { nodes: nodes, edges: edges }, options);
+ $.each(data_result["nodes"], function (index, device) {
+ nodes.add(device);
+ });
+ $.each(data_result["edges"], function (index, edge) {
+ edges.add(edge);
+ });
+ graph.fit();
+ canvas = document.getElementById('visgraph').getElementsByTagName('canvas')[0];
+
+ graph.on('afterDrawing', function () {
+ $("#status").html('Ready');
+ var image = canvas.toDataURL(MIME_TYPE);
+ downloadButton.href = image;
+ downloadButton.download = "topology";
+ });
+
+ graph.on("dragEnd", function (params) {
+ dragged = this.getPositions(params.nodes);
+ $.each(dragged, function (node_id, coordinates) {
+ $("#coordstatus").html('');
+ if ($('#checkSaveCoordinates').is(":checked")) {
+ //nodes.update({ id: node_id, physics: false });
+ $.ajax({
+ url: "../../api/plugins/topology-views/save-coords/save_coords/",
+ type: 'PATCH',
+ dataType: 'json',
+ headers: { "X-CSRFToken": csrftoken },
+ contentType: "application/json; charset=utf-8",
+ processData: false,
+ data: JSON.stringify({
+ 'node_id': node_id,
+ 'x': coordinates.x,
+ 'y': coordinates.y
+ }),
+ error: function (error_result) {
+ $("#coordstatus").html('Failed to update coordinates');
+ },
+ success: function (data_result) {
+ $("#coordstatus").html('Updated coordinates');
+ },
+ });
+ }
+ });
+ });
+
+ },
+ error: function (error_result) {
+ $("#status").html('Something went wrong');
+ },
+ });
+ });
+}
diff --git a/netbox_topology_views/static/netbox_topology_views/js/vendor.js b/netbox_topology_views/static/netbox_topology_views/js/vendor.js
index 5c1e3e3..f8188ae 100644
--- a/netbox_topology_views/static/netbox_topology_views/js/vendor.js
+++ b/netbox_topology_views/static/netbox_topology_views/js/vendor.js
@@ -1 +1,85 @@
-!function(g,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((g=g||self).vis=g.vis||{})}(this,function(g){"use strict";var eC="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}function e(g,t){return g(t={exports:{}},t.exports),t.exports}function A(g){return g&&g.default||g}function I(g){return g&&g.Math==Math&&g}function a(g){try{return!!g()}catch(g){return!0}}function v(g,t){return{enumerable:!(1&g),configurable:!(2&g),writable:!(4&g),value:t}}function C(g){return k.call(g).slice(8,-1)}function o(g){if(null==g)throw TypeError("Can't call method on "+g);return g}function r(g){return O(o(g))}function d(g){return"object"==typeof g?null!==g:"function"==typeof g}function i(g,t){if(!d(g))return g;var e,A;if(t&&"function"==typeof(e=g.toString)&&!d(A=e.call(g)))return A;if("function"==typeof(e=g.valueOf)&&!d(A=e.call(g)))return A;if(!t&&"function"==typeof(e=g.toString)&&!d(A=e.call(g)))return A;throw TypeError("Can't convert object to primitive value")}function p(g,t){return N.call(g,t)}function n(g){return M?Z.createElement(g):{}}function s(g,t){var e=F[R(g)];return e==L||e!=S&&("function"==typeof t?a(t):!!t)}function l(g){if("function"!=typeof g)throw TypeError(String(g)+" is not a function");return g}function y(A,I,g){if(l(A),void 0===I)return A;switch(g){case 0:return function(){return A.call(I)};case 1:return function(g){return A.call(I,g)};case 2:return function(g,t){return A.call(I,g,t)};case 3:return function(g,t,e){return A.call(I,g,t,e)}}return function(){return A.apply(I,arguments)}}function h(g){if(!d(g))throw TypeError(String(g)+" is not an object");return g}function f(A){function g(g,t,e){if(this instanceof A){switch(arguments.length){case 0:return new A;case 1:return new A(g);case 2:return new A(g,t)}return new A(g,t,e)}return A.apply(this,arguments)}return g.prototype=A.prototype,g}function m(g,t){var e,A,I,C,i,n,o,r,s=g.target,a=g.global,d=g.stat,l=g.proto,h=a?b:d?b[s]:(b[s]||{}).prototype,c=a?Y:Y[s]||(Y[s]={}),u=c.prototype;for(I in t)e=!Q(a?I:s+(d?".":"#")+I,g.forced)&&h&&p(h,I),i=c[I],e&&(n=g.noTargetGet?(r=j(h,I))&&r.value:h[I]),C=e&&n?n:t[I],e&&typeof i==typeof C||(o=g.bind&&e?y(C,b):g.wrap&&e?f(C):l&&"function"==typeof C?y(Function.call,C):C,(g.sham||C&&C.sham||i&&i.sham)&&P(o,"sham",!0),c[I]=o,l&&(p(Y,A=s+"Prototype")||P(Y,A,{}),Y[A][I]=C,g.real&&u&&!u[I]&&P(u,I,C)))}var b=I("object"==typeof globalThis&&globalThis)||I("object"==typeof window&&window)||I("object"==typeof self&&self)||I("object"==typeof eC&&eC)||Function("return this")(),c=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),u={}.propertyIsEnumerable,w=Object.getOwnPropertyDescriptor,x={f:w&&!u.call({1:2},1)?function(g){var t=w(this,g);return!!t&&t.enumerable}:u},k={}.toString,D="".split,O=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(g){return"String"==C(g)?D.call(g,""):Object(g)}:Object,N={}.hasOwnProperty,Z=b.document,M=d(Z)&&d(Z.createElement),E=!c&&!a(function(){return 7!=Object.defineProperty(n("div"),"a",{get:function(){return 7}}).a}),G=Object.getOwnPropertyDescriptor,T={f:c?G:function(g,t){if(g=r(g),t=i(t,!0),E)try{return G(g,t)}catch(g){}if(p(g,t))return v(!x.f.call(g,t),g[t])}},B=/#|\.prototype\./,R=s.normalize=function(g){return String(g).replace(B,".").toLowerCase()},F=s.data={},S=s.NATIVE="N",L=s.POLYFILL="P",Q=s,Y={},W=Object.defineProperty,z={f:c?W:function(g,t,e){if(h(g),t=i(t,!0),h(e),E)try{return W(g,t,e)}catch(g){}if("get"in e||"set"in e)throw TypeError("Accessors not supported");return"value"in e&&(g[t]=e.value),g}},P=c?function(g,t,e){return z.f(g,t,v(1,e))}:function(g,t,e){return g[t]=e,g},j=T.f,V=[].slice,X={},U=Function.bind||function(t){var e=l(this),A=V.call(arguments,1),I=function(){var g=A.concat(V.call(arguments));return this instanceof I?function(g,t,e){if(!(t in X)){for(var A=[],I=0;Is&&(h=s),l=Math.sqrt(h*h/(1+r*r)),t+=l=n<0?-l:l,e+=r*l,!0===d?g.lineTo(t,e):g.moveTo(t,e),s-=h,d=!d}var Ag={circle:q,dashedLine:eg,database:tg,diamond:function(g,t,e,A){g.beginPath(),g.lineTo(t,e+A),g.lineTo(t+A,e),g.lineTo(t,e-A),g.lineTo(t-A,e),g.closePath()},ellipse:gg,ellipse_vis:gg,hexagon:function(g,t,e,A){g.beginPath();var I=2*Math.PI/6;g.moveTo(t+A,e);for(var C=1;C<6;C++)g.lineTo(t+A*Math.cos(I*C),e+A*Math.sin(I*C));g.closePath()},roundRect:$,square:function(g,t,e,A){g.beginPath(),g.rect(t-A,e-A,2*A,2*A),g.closePath()},star:function(g,t,e,A){g.beginPath(),e+=.1*(A*=.82);for(var I=0;I<10;I++){var C=I%2==0?1.3*A:.5*A;g.lineTo(t+C*Math.sin(2*I*Math.PI/10),e-C*Math.cos(2*I*Math.PI/10))}g.closePath()},triangle:function(g,t,e,A){g.beginPath(),e+=.275*(A*=1.15);var I=2*A,C=I/2,i=Math.sqrt(3)/6*I,n=Math.sqrt(I*I-C*C);g.moveTo(t,e-(n-i)),g.lineTo(t+C,e+i),g.lineTo(t-C,e+i),g.lineTo(t,e-(n-i)),g.closePath()},triangleDown:function(g,t,e,A){g.beginPath(),e-=.275*(A*=1.15);var I=2*A,C=I/2,i=Math.sqrt(3)/6*I,n=Math.sqrt(I*I-C*C);g.moveTo(t,e+(n-i)),g.lineTo(t+C,e-i),g.lineTo(t-C,e-i),g.lineTo(t,e+(n-i)),g.closePath()}},Ig=e(function(g){function e(g){if(g)return function(g){for(var t in e.prototype)g[t]=e.prototype[t];return g}(g)}(g.exports=e).prototype.on=e.prototype.addEventListener=function(g,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+g]=this._callbacks["$"+g]||[]).push(t),this},e.prototype.once=function(g,t){function e(){this.off(g,e),t.apply(this,arguments)}return e.fn=t,this.on(g,e),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(g,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var e,A=this._callbacks["$"+g];if(!A)return this;if(1==arguments.length)return delete this._callbacks["$"+g],this;for(var I=0;II;)p(A,e=t[I++])&&(~it(C,e)||C.push(e));return C}function _g(g){return"function"==typeof g?g:void 0}function Hg(g,t){return arguments.length<2?_g(Y[g])||_g(b[g]):Y[g]&&Y[g][t]||b[g]&&b[g][t]}function Jg(){}function Kg(g){return"