mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
net/frr - reuse opnsense-treeview.js from core to ease maintanance and offer the same functionality.
This commit is contained in:
@@ -27,6 +27,25 @@ namespace OPNsense\Quagga;
|
||||
|
||||
class DiagnosticsController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function templateJSIncludes()
|
||||
{
|
||||
return array_merge(parent::templateJSIncludes(), [
|
||||
'/ui/js/tree.jquery.min.js',
|
||||
'/ui/js/opnsense-treeview.js'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function templateCSSIncludes()
|
||||
{
|
||||
return array_merge(parent::templateCSSIncludes(), ['/css/jqtree.css']);
|
||||
}
|
||||
|
||||
public function bgpAction()
|
||||
{
|
||||
$this->view->tabs = [
|
||||
|
||||
@@ -29,8 +29,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#}
|
||||
|
||||
<script src="{{ cache_safe('/ui/js/frr/diagnostics_utils.js') }}"></script>
|
||||
|
||||
<style>
|
||||
.searchbox {
|
||||
margin: 8px;
|
||||
@@ -46,9 +44,24 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="{{ cache_safe(theme_file_or_default('/css/jqtree.css', ui_theme|default('opnsense'))) }}">
|
||||
<script src="{{ cache_safe('/ui/js/tree.jquery.min.js') }}"></script>
|
||||
.bootstrap-dialog-body {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.modal-dialog,
|
||||
.modal-content {
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
height: calc(100% - 120px);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.modal-dialog {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
@@ -60,26 +73,26 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
* only display the refresh button on the currently active tab
|
||||
*/
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
$(".tab-icon").removeClass("fa-refresh");
|
||||
$("#"+e.target.id).find(".tab-icon").addClass("fa-refresh");
|
||||
$(".tab-icon").removeClass("fa-refresh");
|
||||
$("#"+e.target.id).find(".tab-icon").addClass("fa-refresh");
|
||||
});
|
||||
|
||||
/**
|
||||
* resize tree widgets on window resize
|
||||
*/
|
||||
$(window).on('resize', resizeTreeWidget);
|
||||
|
||||
/**
|
||||
* delayed search for tree widgets
|
||||
*/
|
||||
$(".tree_search").keyup(treeSearchKeyUp);
|
||||
$(window).on('resize', function(){
|
||||
let new_height = $(".page-foot").offset().top -
|
||||
($(".page-content-head").offset().top + $(".page-content-head").height()) - 160;
|
||||
$(".treewidget").height(new_height);
|
||||
$(".treewidget").css('max-height', new_height + 'px');
|
||||
});
|
||||
|
||||
let all_grids = [];
|
||||
{% for tab in tabs %}
|
||||
/**
|
||||
* register refresh event handler for {{ tab['tabhead'] }}
|
||||
*/
|
||||
$("#refresh-{{ tab['name'] }}").click(function () {
|
||||
$("#{{ tab['name'] }}_tab").click(function () {
|
||||
{% switch tab['type'] %}
|
||||
{% case 'generalroutetable' %}
|
||||
{% case 'bgproutetable' %}
|
||||
@@ -203,46 +216,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
{% break %}
|
||||
{% case 'tree' %}
|
||||
ajaxGet("{{ tab['endpoint'] }}", {}, function (data, status) {
|
||||
if (status == "success") {
|
||||
let $tree = $("#tree-{{ tab['name'] }}");
|
||||
if ($("#tree-{{ tab['name'] }} > ul").length == 0) {
|
||||
$tree.tree({
|
||||
data: dict_to_tree(data['response']),
|
||||
autoOpen: false,
|
||||
dragAndDrop: false,
|
||||
selectable: false,
|
||||
closedIcon: $('<i class="fa fa-plus-square-o"></i>'),
|
||||
openedIcon: $('<i class="fa fa-minus-square-o"></i>'),
|
||||
onCreateLi: function(node, $li) {
|
||||
let n_title = $li.find('.jqtree-title');
|
||||
n_title.text(n_title.text().replace('>','\>').replace('<','\<'));
|
||||
if (node.value !== undefined) {
|
||||
$li.find('.jqtree-element').append(
|
||||
' <strong>:</strong> ' + node.value
|
||||
);
|
||||
}
|
||||
if (node.selected) {
|
||||
$li.addClass("node-selected");
|
||||
} else {
|
||||
$li.removeClass("node-selected");
|
||||
}
|
||||
}
|
||||
});
|
||||
// initial view, collapse first level if there's only one node
|
||||
if (Object.keys(data['response']).length == 1) {
|
||||
for (let key in data['response']) {
|
||||
$tree.tree('openNode', $tree.tree('getNodeById', key));
|
||||
}
|
||||
}
|
||||
//open node on label click
|
||||
$tree.bind('tree.click', function(e) {
|
||||
$tree.tree('toggle', e.node);
|
||||
});
|
||||
} else {
|
||||
let curent_state = $tree.tree('getState');
|
||||
$tree.tree('loadData', dict_to_tree(data['response']));
|
||||
$tree.tree('setState', curent_state);
|
||||
}
|
||||
if (status == "success" && data.response) {
|
||||
update_tree(data.response, "#tree-{{ tab['name'] }}");
|
||||
}
|
||||
});
|
||||
$(window).trigger('resize');
|
||||
@@ -257,14 +232,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
{% endswitch %}
|
||||
});
|
||||
|
||||
/**
|
||||
* perform data fetch via event handler
|
||||
*/
|
||||
$("a[id='{{ tab['name'] }}_tab']").on("shown.bs.tab", function (event) {
|
||||
$("#refresh-{{ tab['name'] }}").click();
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
$(".tree_search").keyup(tree_delayed_live_search);
|
||||
|
||||
/**
|
||||
* add "sub title" heading
|
||||
*/
|
||||
@@ -282,7 +253,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
{% for tab in tabs %}
|
||||
<li>
|
||||
<a data-toggle="tab" href="#{{ tab['name'] }}" id="{{tab['name']}}_tab">
|
||||
{{ tab['tabhead'] }} <span id="refresh-{{ tab['name'] }}" class="fa tab-icon fa-refresh" style="cursor: pointer"></span>
|
||||
{{ tab['tabhead'] }} <span class="fa tab-icon fa-refresh" style="cursor: pointer"></span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
@@ -435,6 +406,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
</div>
|
||||
{% break %}
|
||||
{% case 'tree' %}
|
||||
<section class="col-xs-12">
|
||||
<div class="searchbox">
|
||||
<input
|
||||
id="search-{{ tab['name'] }}"
|
||||
@@ -444,7 +416,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
placeholder="{{ lang._('search') }}"
|
||||
></input>
|
||||
</div>
|
||||
<div class="treewidget" style="padding: 8px; overflow-y: scroll; height:400px;" id="tree-{{ tab['name'] }}"></div>
|
||||
<div class="treewidget" style="padding: 8px; overflow-y: scroll; height:400px;" id="tree-{{tab['name']}}"></div>
|
||||
</section>
|
||||
{% break %}
|
||||
{% case 'text' %}
|
||||
<pre id="text-{{ tab['name'] }}"></pre>
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2023 Deciso B.V.
|
||||
* Copyright (C) 2023 Marc Bartelt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* tree view: resize widget on window resize
|
||||
*/
|
||||
function resizeTreeWidget() {
|
||||
let new_height = $(".page-foot").offset().top -
|
||||
($(".page-content-head").offset().top + $(".page-content-head").height()) - 160;
|
||||
$(".treewidget").height(new_height);
|
||||
$(".treewidget").css('max-height', new_height + 'px');
|
||||
}
|
||||
|
||||
/**
|
||||
* tree view: delayed live-search
|
||||
*/
|
||||
let apply_tree_search_timer = null;
|
||||
function treeSearchKeyUp() {
|
||||
let sender = $(this);
|
||||
|
||||
clearTimeout(apply_tree_search_timer);
|
||||
apply_tree_search_timer = setTimeout(function() {
|
||||
let searchTerm = sender.val().toLowerCase();
|
||||
let target = $("#"+sender.attr('for'));
|
||||
let tree = target.tree("getTree");
|
||||
let selected = [];
|
||||
if (tree !== null) {
|
||||
tree.iterate((node) => {
|
||||
let matched = false;
|
||||
if (searchTerm !== "") {
|
||||
matched = node.name.toLowerCase().includes(searchTerm);
|
||||
if (!matched && typeof node.value === 'string') {
|
||||
matched = node.value.toLowerCase().includes(searchTerm);
|
||||
}
|
||||
}
|
||||
node["selected"] = matched;
|
||||
|
||||
if (matched) {
|
||||
selected.push(node);
|
||||
if (node.isFolder()) {
|
||||
node.is_open = true;
|
||||
}
|
||||
let parent = node.parent;
|
||||
while (parent) {
|
||||
parent.is_open = true;
|
||||
parent = parent.parent;
|
||||
}
|
||||
} else if (node.isFolder()) {
|
||||
node.is_open = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
target.tree("refresh");
|
||||
if (selected.length > 0) {
|
||||
target.tree('scrollToNode', selected[0]);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* jqtree expects a list + dict type structure, transform key value store into expected output
|
||||
* https://mbraak.github.io/jqTree/#general
|
||||
*/
|
||||
function dict_to_tree(node, path) {
|
||||
// some entries are lists, try use a name for the nodes in that case
|
||||
let node_name_keys = ['name', 'interface-name'];
|
||||
let result = [];
|
||||
if ( path === undefined) {
|
||||
path = "";
|
||||
} else {
|
||||
path = path + ".";
|
||||
}
|
||||
for (let key in node) {
|
||||
if (typeof node[key] === "function") {
|
||||
continue;
|
||||
}
|
||||
let item_path = path + key;
|
||||
if (node[key] instanceof Object) {
|
||||
let node_name = key;
|
||||
for (let idx=0; idx < node_name_keys.length; ++idx) {
|
||||
if (/^(0|[1-9]\d*)$/.test(node_name) && node[key][node_name_keys[idx]] !== undefined) {
|
||||
node_name = node[key][node_name_keys[idx]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.push({
|
||||
name: node_name,
|
||||
id: item_path,
|
||||
children: dict_to_tree(node[key], item_path)
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
name: key,
|
||||
value: node[key],
|
||||
id: item_path
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user