--
-- (C) 2013-24 - ntop.org
--
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
package.path = dirs.installdir .. "/scripts/lua/modules/pools/?.lua;" .. package.path
package.path = dirs.installdir .. "/scripts/lua/modules/vulnerability_scan/?.lua;" .. package.path

require "lua_utils_generic"
require "check_redis_prefs"
local vs_utils = require "vs_utils"

local host_pools_nedge
if ntop.isnEdge() then
    host_pools_nedge = require "host_pools_nedge"
end
local host_pools = require "host_pools"
-- Instantiate host pools
local host_pools_instance = host_pools:create()

local page_utils = require("page_utils")
local custom_column_utils = require("custom_column_utils")
local discover = require("discover_utils")
local template_utils = require("template_utils")
local have_nedge = ntop.isnEdge()
local wheel = nil

local host_ts_available = areHostTimeseriesEnabled()

local function generate_map_url(map, map_type, query, icon)
    local url = ""

    if (ntop.isPro()) then

        local map_available = table.len(map) > 0

        if (map_available) then
            url =
                "<a class='ms-1' href='" .. ntop.getHttpPrefix() .. "/lua/pro/enterprise/network_maps.lua?" .. query ..
                    "&map=" .. map_type .. "'><i class='" .. icon .. "'></i></a>"
        end
    end

    return url
end

sendHTTPContentTypeHeader('text/html')

page_utils.print_header_and_set_active_menu_entry(page_utils.menu_entries.hosts)
local protocol = _GET["protocol"]
local asn = _GET["asn"]
local vlan = _GET["vlan"]
local network = _GET["network"]
local cidr = _GET["network_cidr"]
local country = _GET["country"]
local mac = _GET["mac"]
local os_ = _GET["os"]
local community = _GET["community"]
local pool = _GET["pool"]
local ipversion = _GET["version"]
local traffic_type = _GET["traffic_type"]
local device_ip = _GET["deviceIP"]
local page = _GET["page"] or 'active_hosts'
local base_url = ntop.getHttpPrefix() .. "/lua/hosts_stats.lua"
local page_params = {}
local charts_icon = ""

local mode = _GET["mode"]

if isEmptyString(mode) then
    mode = "all"
else
    page_params["mode"] = mode
end

local hosts_filter = ''

if ((mode ~= "all") or (not isEmptyString(pool))) then
    hosts_filter = '<span class="fas fa-filter"></span>'
end

function getPageTitle(protocol_name, traffic_type_title, device_ip_title, network_name, cidr, ipver_title, os_, country,
    asninfo, mac, pool_, vlan_title, vlan_alias)
    local mode_label = ""

    if mode == "remote" then
        mode_label = i18n("hosts_stats.remote")
    elseif mode == "remote_no_tx" then
        mode_label = i18n("hosts_stats.remote_no_tx")
    elseif mode == "remote_no_tcp_tx" then
        mode_label = i18n("hosts_stats.remote_no_tcp_tx")
    elseif mode == "local" then
        mode_label = i18n("hosts_stats.local")
    elseif mode == "local_no_tx" then
        mode_label = i18n("hosts_stats.local_no_tx")
    elseif mode == "local_no_tcp_tx" then
        mode_label = i18n("hosts_stats.local_no_tcp_tx")
    elseif mode == "filtered" then
        mode_label = i18n("hosts_stats.filtered")
    elseif mode == "blacklisted" then
        mode_label = i18n("hosts_stats.blacklisted")
    elseif mode == "dhcp" then
        mode_label = i18n("nedge.network_conf_dhcp")
    elseif mode == "broadcast_multicast" then
       mode_label = i18n("hosts_stats.broadcast_and_multicast")
    end

    if (network == nil) then
        wheel = ""
        charts_icon = ""
    else
        wheel =
            '<A HREF="' .. ntop.getHttpPrefix() .. '/lua/network_details.lua?network=' .. network .. '&page=config' ..
                '"><i class="fas fa-cog fa-sm"></i></A>'
        charts_icon =
            charts_icon .. "&nbsp; <a href='" .. ntop.getHttpPrefix() .. "/lua/network_details.lua?network=" .. network ..
                "&page=historical'><i class='fas fa-sm fa-chart-area'></i></a>"
    end

    -- Note: we must use the empty string as fallback. Multiple spaces will be collapsed into one automatically.
    return i18n("hosts_stats.hosts_page_title", {
        all = isEmptyString(mode_label) and i18n("hosts_stats.all") or "",
        traffic_type = traffic_type_title or "",
        device_ip = device_ip_title or "",
        local_remote = mode_label,
        protocol = protocol_name or "",
        network = not isEmptyString(network_name) and i18n("hosts_stats.in_network", {
            network = network_name
        }) or "",
        network_cidr = not isEmptyString(cidr) and i18n("hosts_stats.in_network", {
            network = cidr
        }) or "",
        ip_version = ipver_title or "",
        ["os"] = discover.getOsName(os_),
        country_asn_or_mac = country or asninfo or mac or pool_ or "",
        vlan = vlan_title or "",
        vlan_name = vlan_alias or "",
        charts_icon = charts_icon,
        wheel = wheel
    })
end

dofile(dirs.installdir .. "/scripts/lua/inc/menu.lua")

local ifstats = interface.getStats()

-- Parameters necessary for the page title
local protocol_name = nil
local network_name = nil
local traffic_type_title = nil
local device_ip_title = nil
local ipver_title = nil
local asninfo = nil
local os_title = nil
local country_title = nil
local mac_title = nil
local vlan_title = nil
local pool_title = nil
local vlan_alias = nil

if ((protocol ~= nil) and (protocol ~= "")) then
    protocol_name = interface.getnDPIProtoName(tonumber(protocol))
end

if (protocol_name == nil) then
    protocol_name = protocol
end

if not isEmptyString(traffic_type) then
    page_params["traffic_type"] = traffic_type

    if traffic_type == "one_way" then
        traffic_type_title = i18n("hosts_stats.traffic_type_one_way")
    elseif traffic_type == "bidirectional" then
        traffic_type_title = i18n("hosts_stats.traffic_type_two_ways")
    end
else
    traffic_type_title = ""
end

if not isEmptyString(device_ip) then
    page_params["deviceIP"] = device_ip

    device_ip_title = i18n("hosts_stats.probe_traffic", {
        device_ip = device_ip
    })
end

if (tonumber(network) ~= nil) then
    network_name = getLocalNetworkAlias(tonumber(network))

    if isEmptyString(network_name) then
        network_name = i18n("hosts_stats.remote")
    end
else
    network_name = ""
end

if not isEmptyString(ipversion) then
    ipver_title = i18n("hosts_stats.ipver_title", {
        version_num = ipversion
    })
else
    ipver_title = ""
end

if (asn ~= nil) then
    asninfo = " " .. i18n("hosts_stats.asn_title", {
        asn = asn
    }) ..
                  "<small>&nbsp;<i class='fas fa-info-circle fa-sm' aria-hidden='true'></i> <A class='ntopng-external-link' href='https://stat.ripe.net/AS" ..
                  asn .. "'><i class='fas fa-external-link-alt fa-sm' title=\\\"" ..
                  i18n("hosts_stats.more_info_about_as_popup_msg") .. "\\\"></i></A> " .. charts_icon ..
                  "&nbsp; <a href='" .. ntop.getHttpPrefix() .. "/lua/as_details.lua?asn=" .. asn ..
                  "&page=historical'><i class='fas fa-sm fa-chart-area'></i></a> </small>"
end

if (os_ ~= nil) then
    os_title = " " .. os_
end

if (country ~= nil and country ~= '') then
    country_title = " " .. i18n("hosts_stats.country_title", {
        country = country
    })
end

if (mac ~= nil and mac ~= '') then
    mac_title = " " .. i18n("hosts_stats.mac_title", {
        mac = mac
    })
end

if (vlan ~= nil and vlan ~= '') then

    local link_service_map = generate_map_url(interface.serviceMap(nil, tonumber(vlan)), "service_map", "vlan=" .. vlan,
        "fas fa-concierge-bell")
    local link_periodicity_map = generate_map_url(interface.periodicityMap(nil, tonumber(vlan)), "periodicity_map",
        "vlan=" .. vlan, "fas fa-clock")

    local vlan_label = i18n('untagged')
    if (vlan ~= 0 and vlan ~= '0') then
        vlan_label = i18n("hosts_stats.vlan_title", {
            vlan = vlan
        })
    end
    vlan_title = " [" .. vlan_label .. "]"
    local config_button = " <A HREF='" .. ntop.getHttpPrefix() .. "/lua/vlan_details.lua?vlan=" .. vlan .. "&page=config" ..
                     "'><i class='fas fa-cog fa-sm'></i></A>"

    -- in case of untagged traffic is not possible to set a vlan alias
    if (vlan ~= 0 and vlan ~= '0') then
        vlan_title = vlan_title .. config_button
    end

    vlan_title = vlan_title .. " " .. link_service_map .. " " .. link_periodicity_map
    if (vlan == getVlanAlias(vlan)) then
        vlan_alias = ""
    else
        vlan_alias = getVlanAlias(vlan)
    end
end

if (pool ~= nil and pool ~= '') then
    local link_service_map = ""
    local link_periodicity_map = ""
    local charts_available = areHostPoolsTimeseriesEnabled(ifstats.id)

    if (tonumber(pool) ~= host_pools_instance.DEFAULT_POOL_ID) then
        link_service_map = generate_map_url(interface.serviceMap(nil, nil, tonumber(pool)), "service_map",
            "host_pool_id=" .. pool, "fas fa-concierge-bell")
        link_periodicity_map = generate_map_url(interface.periodicityMap(nil, nil, tonumber(pool)), "periodicity_map",
            "host_pool_id=" .. pool, "fas fa-clock")
    end

    local pool_edit = ""
    local pool_link
    local title

    if (tonumber(pool) ~= host_pools_instance.DEFAULT_POOL_ID) or (have_nedge) then
        if have_nedge then
            pool_link = "/lua/pro/nedge/admin/nf_edit_user.lua?username=" ..
                            ternary(tonumber(pool) == host_pools_nedge.DEFAULT_POOL_ID, "",
                    host_pools_nedge.poolIdToUsername(pool))
            title = i18n("nedge.edit_user")
        else
            pool_link = "/lua/admin/manage_host_members.lua?pool=" .. pool
            title = i18n("host_pools.manage_pools")
        end

        pool_edit =
            "&nbsp; <A HREF='" .. ntop.getHttpPrefix() .. pool_link .. "'><i class='fas fa-cog fa-sm' title='" .. title ..
                "'></i></A>"
    end

    pool_title = " " .. i18n(ternary(have_nedge, "hosts_stats.user_title", "hosts_stats.pool_title"), {
        poolname = host_pools_instance:get_pool_name(pool)
    }) .. "<small>" .. pool_edit .. ternary(charts_available,
        " <a href='" .. ntop.getHttpPrefix() .. "/lua/pool_details.lua?page=historical&pool=" .. pool ..
            "'><i class='fas fa-chart-area fa-sm' title='" .. i18n("chart") .. "'></i></a>", "") .. link_service_map ..
                     link_periodicity_map .. "</small>"
end

page_utils.print_navbar(i18n("hosts"), base_url .. "?", {{
    active = page == "active_hosts" or page == nil,
    page_name = "active_hosts",
    label = i18n('active_hosts')
}, {
    hidden = not host_ts_available or not ntop.isEnterpriseXL(),
    active = page == "local_hosts_report" or page == nil,
    page_name = "local_hosts_report",
    label = i18n("local_hosts_report")
}})

if page == 'active_hosts' and ntop.isnEdge() then
    page_utils.print_page_title(getPageTitle(protocol_name, traffic_type_title, device_ip_title, network_name, cidr,
        ipver_title, os_title, country_title, asninfo, mac_title, pool_title, vlan_title, vlan_alias))

    if (_GET["page"] ~= "historical") then
        if (asn ~= nil) then
            print [[
      <div class='card'><div class='card-header'>
   <ul class="nav nav-tabs card-header-tabs">
      <li class="nav-item" class="active"><a class="nav-link active" data-bs-toggle="tab" href="#home">]]
            print(i18n("hosts_stats.hosts"))
            print [[</a></li>
   ]]

            if (asn ~= "0") then
                print [[
      <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#asinfo">]]
                print(i18n("hosts_stats.as_info"))
                print [[</a></li>
      <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#aspath">]]
                print(i18n("hosts_stats.as_path"))
                print [[</a></li>
      <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#prefix">]]
                print(i18n("hosts_stats.as_prefixes"))
                print [[</a></li>
      <li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#bgp">]]
                print(i18n("hosts_stats.bgp_updates"))
                print [[</a></li>
   ]]
            end
        end

        print("</ul>") -- close .card-header

        if (asn ~= nil) then
            print("</div>")
        end

        if (asn ~= nil) then
            print [[
      <div class='card-body'>
   <div class="tab-content">
   <div id="home" class="tab-pane in active">
   ]]
        end

        -- build the current filter url
        page_params["os"] = os_
        page_params["asn"] = asn
        page_params["community"] = community
        page_params["vlan"] = vlan
        page_params["country"] = country
        page_params["mac"] = mac
        page_params["pool"] = pool
        page_params["network_cidr"] = cidr

        if (protocol ~= nil) then
            -- Example HTTP.Facebook
            dot = string.find(protocol, '%.')
            if (dot ~= nil) then
                protocol = string.sub(protocol, dot + 1)
            end

            page_params["protocol"] = protocol
        end

        if (network ~= nil) then
            page_params["network"] = network
            local network_key = ntop.getNetworkNameById(tonumber(network))

            if not isEmptyString(network_name) then
                local charts_available = areInterfaceTimeseriesEnabled(ifstats.id)

                charts_icon = " <small><a href='" .. ntop.getHttpPrefix() .. "/lua/network_details.lua?network=" ..
                                  network .. "&page=config'><i class='fas fa-sm fa-cog'></i></a>"

                if charts_available then
                    charts_icon = charts_icon .. "&nbsp; <a href='" .. ntop.getHttpPrefix() ..
                                      "/lua/network_details.lua?network=" .. network ..
                                      "&page=historical'><i class='fas fa-sm fa-chart-area'></i></a>"
                end

                charts_icon = charts_icon .. "</small>"
            end
        end

        if not isEmptyString(ipversion) then
            page_params["version"] = ipversion
        end

        custom_column_utils.updateCustomColumn()
        local custom_name, custom_key, custom_align = custom_column_utils.getCustomColumnName()

        print [[
         <div id="table-hosts"></div>
      <script>
      var url_update = "]]
        print(getPageUrl(ntop.getHttpPrefix() .. "/lua/get_hosts_data.lua", page_params))
        print [[";]]

        ntop.dumpFile(dirs.installdir .. "/httpdocs/inc/hosts_stats_id.inc")

        if ((ifstats.vlan)) then
            show_vlan = true
        else
            show_vlan = false
        end

        if (show_vlan) then
            print('host_rows_option["vlan"] = true;\n')
        end

        if ((custom_key == "") or (custom_key == nil)) then
            custom_key = "score"
        end

        print [[
      host_rows_option["ip"] = true;
            host_rows_option["custom_column"] = "]]
        print(custom_key)
        print [[";
      $("#table-hosts").datatable({
            title: "]]
        print(i18n("hosts_stats.hosts_list"))
        print [[",
            url: url_update ,
      ]]

        if (protocol == nil) then
            protocol = ""
        end

        if not isEmptyString(protocol_name) then
            charts_icon = " <a href='" .. ntop.getHttpPrefix() .. "/lua/if_stats.lua?ifid=" .. ifstats.id ..
                              "&page=historical&ts_schema=iface:ndpi&protocol=" .. protocol_name ..
                              "'><i class='fas fa-sm fa-chart-area'></i></a>"
        end

        print('title: "",\n')
        print('rowCallback: function ( row ) { return host_table_setID(row); },')

        print [[
         tableCallback: function()  { $("#dt-bottom-details > .float-left > p").first().append('. ]]
        print(i18n('hosts_stats.idle_hosts_not_listed'))
        print [['); },
   ]]

        -- Set the preference table
        preference = tablePreferences("rows_number", _GET["perPage"])
        if (preference ~= "") then
            print('perPage: ' .. preference .. ",\n")
        end

        -- Automatic default sorted. NB: the column must exist.
        print('sort: [ ["' .. getDefaultTableSort("hosts") .. '","' .. getDefaultTableSortOrder("hosts") .. '"] ],')

        print [[    showPagination: true, ]]

        print('buttons: [ ')

        -- IP version selector
        print [['<div class="btn-group float-right">]]
        custom_column_utils.printCustomColumnDropdown(base_url, page_params)
        print [[</div>']]

        -- IP version selector
        print [[, '<div class="btn-group float-right">]]
        printIpVersionDropdown(base_url, page_params)
        print [[</div>']]

        -- VLAN selector
        if ifstats.vlan then
            print [[, '<div class="btn-group float-right">]]
            printVLANFilterDropdown(base_url, page_params)
            print [[</div>']]
        end

        print [[, '<div class="btn-group float-right">]]
        printTrafficTypeFilterDropdown(base_url, page_params)
        print [[</div>']]

        -- Hosts filter
        -- table.clone needed to modify some parameters while keeping the original unchanged
        local hosts_filter_params = table.clone(page_params)

        print(', \'<div class="btn-group"><button class="btn btn-link dropdown-toggle" data-bs-toggle="dropdown">' ..
                  i18n("hosts_stats.filter_hosts") .. hosts_filter ..
                  '<span class="caret"></span></button> <ul class="dropdown-menu scrollable-dropdown" role="menu" style="min-width: 90px;"><li"><a class="dropdown-item" href="')

        hosts_filter_params.mode = nil
        hosts_filter_params.pool = nil
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.all_hosts") .. '</a></li>')


        hosts_filter_params.mode = "blacklisted"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "blacklisted", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.blacklisted_hosts_only") .. '</a></li>')

        if interface.isPacketInterface() and not interface.isPcapDumpInterface() then

            hosts_filter_params.mode = "broadcast_domain"
            print('<li ')
            print('"><a class="dropdown-item ' .. ternary(mode == "broadcast_domain", "active", "") .. '" href="')
            print(getPageUrl(base_url, hosts_filter_params))
            print('">' .. i18n("hosts_stats.broadcast_domain_hosts_only") .. '</a></li>')
        end

        hosts_filter_params.mode = "broadcast_multicast"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "broadcast_multicast", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.broadcast_and_multicast") .. '</a></li>')

        if interface.isPacketInterface() and not interface.isPcapDumpInterface() then
            hosts_filter_params.mode = "dhcp"
            print('<li ')
            print('"><a class="dropdown-item ' .. ternary(mode == "dhcp", "active", "") .. '" href="')
            print(getPageUrl(base_url, hosts_filter_params))
            print('">' .. i18n("mac_stats.dhcp_only") .. '</a></li>')
        end

        -- Host pools
        if (not ifstats.isView) then
            hosts_filter_params.mode = nil
            hosts_filter_params.pool = nil
            local pools = host_pools_instance:get_all_pools()
            if (table.len(pools) > 2) then
                -- Not only the default and jailed (host pools or nEdge users)
                print('<li role="separator" class="divider"></li>')
                for _, _pool in ipairs(pools) do
                    hosts_filter_params.pool = _pool.pool_id
                    print('<li ')
                    print('"><a class="dropdown-item ' .. ternary((tonumber(pool) == _pool.pool_id), 'active', '') .. '" href="')
                    print(getPageUrl(base_url, hosts_filter_params) .. '">' ..
                              i18n(ternary(have_nedge, "hosts_stats.user", "hosts_stats.host_pool"), {
                            pool_name = string.gsub(_pool.name, "'", "\\'")
                        }) .. '</li>')
                end
                hosts_filter_params.pool = nil
            end
        end
	
        hosts_filter_params.mode = "local"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "local", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.local_hosts_only") .. '</a></li>')

        hosts_filter_params.mode = "local_no_tx"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "local_no_tx", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.local_no_tx") .. '</a></li>')

        hosts_filter_params.mode = "local_no_tcp_tx"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "local_no_tcp_tx", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.local_no_tcp_tx") .. '</a></li>')

        hosts_filter_params.mode = "remote"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "remote", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.remote_hosts_only") .. '</a></li>')

        hosts_filter_params.mode = "remote_no_tx"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "remote_no_tx", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.remote_no_tx") .. '</a></li>')

        hosts_filter_params.mode = "remote_no_tcp_tx"
        print('<li ')
        print('"><a class="dropdown-item ' .. ternary(mode == "remote_no_tcp_tx", "active", "") .. '" href="')
        print(getPageUrl(base_url, hosts_filter_params))
        print('">' .. i18n("hosts_stats.remote_no_tcp_tx") .. '</a></li>')

        if isBridgeInterface(ifstats) then
            hosts_filter_params.mode = "filtered"
            print('<li ')
            print('"><a class="dropdown-item ' .. ternary(mode == "filtered", "active", "") .. '" href="')
            print(getPageUrl(base_url, hosts_filter_params))
            print('">' .. i18n("hosts_stats.filtered_hosts_only") .. '</a></li>')
        end

        print('</ul></div>\'')
        local alignment_c_info = 'center'
        if (ntop.isnEdge()) then 
            alignment_c_info = 'nowrap'
        end
        print(' ],')

        print [[
            columns: [
               {
                  title: "Key",
                     field: "key",
                     hidden: true,
                     css: {
                                    textAlign: 'center'
                                 }
                  },{
               title: "",
               field: "column_info",
               sortable: false,
                     css: {
                  textAlign: ']]print(alignment_c_info)print[[',
                     whiteSpace: 'nowrap'

               }
                  },{
               title: "]]
        print(i18n("ip_address"))
        print [[",
               field: "column_ip",
               sortable: true,
                     css: {
                  textAlign: 'left'
               }
                           },
            ]]

        if (show_vlan) then
            print('{ title: "' .. i18n("vlan") .. '",\n')
            print [[
               field: "column_vlan",
               sortable: true,
                     css: {
                  textAlign: 'center'
               }

               },
   ]]
        end

        print [[
               {
               title: "]]
        print(i18n("flows"))
        print [[",
               field: "column_num_flows",
               sortable: true,
                     css: {
                  textAlign: 'center'
               }

               },
               {
               title: "]]
        print(i18n("hosts_stats.page_scan_hosts.title_hosts_page") )
        print [[",
               field: "column_num_vulnerabilities",
               sortable: false,
                     css: {
                  textAlign: 'center'
               },
               hidden: ]]
        local scan_modules = vs_utils.list_scan_modules()
        print(#scan_modules == 0)
        print [[

               }, {
               title: "]]
        print(i18n("if_stats_overview.blocked_flows"))
        print [[",
               field: "column_num_dropped_flows",
               sortable: true,
                                    hidden: ]]
        if isBridgeInterface(ifstats) then
            print("false")
        else
            print("true")
        end
        print [[,
                     css: {
                  textAlign: 'center'
               }

               },  {
               title: "]]
        -- tprint({custom_name = custom_name, custom_key = custom_key, custom_align = custom_align})
        print(custom_name)
        print [[",
               field: "column_]]
        print(custom_key)
        print [[",
               sortable: true,
                     css: {
                  textAlign: ']]
        print(custom_align)
        print [['
               }

               },
               {
               title: "]]
        print(i18n("name"))
        print [[",
               field: "column_name",
               sortable: true,
                     css: {
                  textAlign: 'left'
               }

               },
               {
               title: "]]
        print(i18n("seen_since"))
        print [[",
               field: "column_since",
               sortable: true,
                     css: {
                  textAlign: 'center'
               }
               }, {
               title: "]]
        print(i18n("score"))
        print [[",
               field: "column_score",
               sortable: true,
                     css: {
                  textAlign: 'center'
               }
               }, {
               title: "]]
        print(i18n("breakdown"))
        print [[",
               field: "column_breakdown",
               sortable: false,
                     css: {
                  textAlign: 'center'
               }
               },
               {
               title: "]]
        print(i18n("throughput"))
        print [[",
               field: "column_thpt",
               sortable: true,
                     css: {
                  textAlign: 'right'
               }
               },
               {
               title: "]]
        print(i18n("flows_page.total_bytes"))
        print [[",
               field: "column_traffic",
               sortable: true,
                     css: {
                  textAlign: 'right'
               }
               }
               ]
            });


         </script>

   ]]

        if (have_nedge) then
            print [[
   <script>
   const block_host_csrf = "]]
            print(ntop.getRandomCSRFValue())
            print [[";

   function block_host(host_key, host_url) {
      var url = "]]
            print(ntop.getHttpPrefix())
            print [[/lua/pro/nedge/toggle_block_host.lua?" + host_url;
      $.ajax({
         type: 'GET',
         url: url,
         cache: false,
         data: {
         csrf: block_host_csrf
         },
         success: function(content) {
         var data = jQuery.parseJSON(content);
         if (data.status == "BLOCKED") {
            $('#'+host_key+'_info').find('.block-badge')
               .removeClass('btn-secondary').addClass('btn-danger');
            $('#'+host_key+'_ip').find('a').css("text-decoration", "line-through");
         } else if (data.status == "UNBLOCKED") {
            $('#'+host_key+'_info').find('.block-badge')
               .removeClass('btn-danger').addClass('btn-secondary');
            $('#'+host_key+'_ip').find('a').css("text-decoration", "none");
         }
         },
         error: function(content) {
         console.log("error");
         }
      });
   }
   </script>
   ]]
        end

        if (asn ~= nil) then
            print [[
   </div>
   <script>
   let STAT_WIDGET_API_URL = 'https://stat.ripe.net/widgets/';
   let STAT_DATA_API_URL = 'https://stat.ripe.net/data/';
   let STAT_OTHER_API_URL = 'https://stat.ripe.net/api/';
   let STAT_HOME = "https://stat.ripe.net/";
   // DOM class for auto linking to div element
   let STAT_DOM_CLASS_NAME = 'statwdgtauto';
   // how many seconds to wait for scripts to load
   // 0 ... disables the timeout
   let STAT_REQUIRE_TIMEOUT = 120;
   </script>
   <script src="https://stat.ripe.net/widget-api-config"></script>
   <script src="https://stat.ripe.net/widgets/js/version.js"></script>
   <script src="https://stat.ripe.net/widgets/js/widget_api_main.js"></script>

   <div id="asinfo" class="tab-pane"></div>
   <div id="aspath" class="tab-pane"></div>
   <div id="prefix" class="tab-pane"></div>
   <div id="bgp" class="tab-pane"></div>
   ]]

            if (asn ~= nil) then
                print("</div>")
                print("</div>")
                print("</div>")
            end

            print [[
   <script>
      $(document).ready(function() {
         var tab_id_to_widget = {
            "#asinfo": "iana-registry-info",
            "#aspath": "as-path-length",
            "#prefix": "announced-prefixes",
            "#bgp": "bgp-update-activity",
         };
         var loaded_widgets = {};

         function load_widget(tab_id) {
            var widget = tab_id_to_widget[tab_id];

            if((typeof(widget) === "undefined") || loaded_widgets[widget])
               return;

            var tab = $(tab_id);
            var script = $("<script>")
            var div = $('<div class="statwdgtauto"></div>');
            script.text('ripestat.init("' + widget + '",{"resource":"AS]]
            print(asn)
            print [["},null,{"disable":["controls"]});');
            script.appendTo(div);
            div.appendTo(tab);

            loaded_widgets[widget] = true;
         }

         $('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
            var target = $(e.target).attr("href") // activated tab
            load_widget(target);
         });
      });
   </script>
   ]]
        end -- if(asn ~= nil)
    end
elseif page == "active_hosts" then
    local json = require "dkjson" 
    local vlans = interface.getVLANsList()
    local json_context = json.encode({
        ifid = ifstats.id,
        has_vlans = (vlans ~= nil),
        csrf = ntop.getRandomCSRFValue()
    })
    template_utils.render("pages/vue_page.template", { vue_page_name = "PageHostsList", page_context = json_context })
elseif page == "local_hosts_report" then
    local json = require "dkjson"
    local json_context = json.encode({
        ifid = ifstats.id,
        csrf = ntop.getRandomCSRFValue()
    })
    template_utils.render("pages/vue_page.template", { vue_page_name = "PageLocalHostsReport", page_context = json_context })
    
end

dofile(dirs.installdir .. "/scripts/lua/inc/footer.lua")
