%PDF- %PDF-
Direktori : /backups/router/usr/local/opnsense/mvc/app/views/OPNsense/Core/ |
Current File : //backups/router/usr/local/opnsense/mvc/app/views/OPNsense/Core/firmware.volt |
{# # Copyright (c) 2015-2023 Franco Fichtner <franco@opnsense.org> # Copyright (c) 2015-2018 Deciso B.V. # 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. #} <style> /* "dropdown in responsive table" issue workaround: display dummy road so dropdowns fits the table */ @media screen and (max-width: 767px) { .dropdown_helper { display: block !important; } } .dropdown_helper { display: none; } </style> <script> function generic_search(that, entries) { let search = $(that).val().toLowerCase(); $('.' + entries).each(function () { let row_by_col = $(this).find('td').map(function () { return $(this).text(); }).get(); let name = row_by_col.join(',').toLowerCase(); if (search.length != 0 && name.indexOf(search) == -1) { $(this).hide(); } else { $(this).show(); } }); } function cancel_update() { $('#updatelist').hide(); $('#update_status_container').show(); } /** * retrieve update status from backend */ function updateStatus() { $('#upgrade_maj').hide(); $('#upgrade').hide(); ajaxGet('/api/core/firmware/status', {}, function (data, status){ if (data['status'] == "update") { let show_log = ''; $.status_reboot = data['status_reboot']; // show upgrade list $('#upgrade').show(); $('#updatestatus').html(data['status_msg']); $('#updatelist > tbody').empty(); $('#updatetab > a').tab('show'); $.each(data['all_packages'], function (index, row) { $('#updatelist > tbody').append('<tr><td>'+row['name']+'</td>' + '<td>'+row['old']+'</td><td>'+row['new']+'</td><td>' + row['reason']+'</td><td>'+row['repository'] + '</td></tr>'); if (row['name'] == data['product_target'] && row['new'] != 'N/A') { show_log = row['new'].replace(/[_-].*/, ''); } }); $('#update_status_container').hide(); $('#updatelist').show(); // display the current changelog if one was found if (show_log != '') { changelog(show_log); } packagesInfo(false); } else if (data['status'] == "upgrade") { $.status_reboot = data['status_reboot']; if (data['upgrade_major_message'] != '') { /* we trust this data, it was signed by us and secured by csrf */ stdDialogInform( '{{ lang._('Upgrade instructions') }}', htmlDecode(data['upgrade_major_message']), "{{ lang._('OK') }}", function () { show_upgrade(data) }, 'warning' ); } else { show_upgrade(data); } packagesInfo(false); } else if (data['status'] == "error") { stdDialogInform('{{ lang._('Firmware status') }}', data['status_msg'], "{{ lang._('Close') }}", undefined, 'danger'); packagesInfo(true); } else if (data['status'] == "none") { stdDialogInform('{{ lang._('Firmware status') }}', data['status_msg'], "{{ lang._('Close') }}", undefined, 'success'); packagesInfo(true); } else { // in case new responses are added console.log('Unknown check response'); console.log(data); } }); } /** * perform backend action and install poller to update status */ function backend(type) { $.upgrade_check = type == 'check' $('#update_status').html(''); $('#updatelist').hide(); $('#update_status_container').show(); $('#updatetab > a').tab('show'); $('#updatetab_progress').addClass("fa fa-spinner fa-pulse"); ajaxCall('/api/core/firmware/' + type, {}, function () { setTimeout(trackStatus, 500); }); } /** * read package details from backend */ function details(package) { ajaxCall('/api/core/firmware/details/' + package, {}, function (data, status) { var details = "{{ lang._('Sorry, plugin details are currently not available.') }}"; if (data['details'] != undefined) { details = data['details']; } stdDialogInform("{{ lang._('Plugin details') }}", details, "{{ lang._('Close') }}"); }); } /** * read license from backend */ function license(package) { ajaxCall('/api/core/firmware/license/' + package, {}, function (data, status) { var license = "{{ lang._('Sorry, the package does not have an associated license file.') }}"; if (data['license'] != undefined) { license = data['license']; } stdDialogInform("{{ lang._('License details') }}", license, "{{ lang._('Close') }}"); }); } /** * read changelog from backend */ function changelog(version) { ajaxCall('/api/core/firmware/changelog/' + version, {}, function (data, status) { if (data['html'] != undefined) { /* we trust this data, it was signed by us and secured by csrf */ stdDialogInform(version, htmlDecode(data['html']), "{{ lang._('Close') }}", undefined, 'primary'); } }); } /** * perform package action that requires reboot confirmation */ function action_may_reboot(pkg_act, pkg_name) { if (pkg_act == 'reinstall' && (pkg_name == 'kernel' || pkg_name == 'base')) { // reboot required, inform the user. BootstrapDialog.show({ type:BootstrapDialog.TYPE_WARNING, title: "{{ lang._('Reboot required') }}", message: "{{ lang._('The firewall will reboot directly after this set reinstall.') }}", buttons: [{ label: "{{ lang._('OK') }}", cssClass: 'btn-warning', action: function(dialogRef){ dialogRef.close(); backend(pkg_act + "/" + pkg_name); } },{ label: "{{ lang._('Cancel') }}", action: function(dialogRef){ dialogRef.close(); } }] }); } else { backend(pkg_act + "/" + pkg_name); } } function show_upgrade(data) { $('#upgrade_maj').show(); $('#updatestatus').html(data['status_msg']); $('#updatelist > tbody').empty(); $('#updatetab > a').tab('show'); $.each(data['all_sets'], function (index, row) { $('#updatelist > tbody').append('<tr><td>'+row['name']+'</td>' + '<td>'+row['old']+'</td><td>'+row['new']+'</td><td>' + row['reason']+'</td><td>'+row['repository'] + '</td></tr>'); }); $('#update_status_container').hide(); $('#updatelist').show(); changelog(data['upgrade_major_version']); } /** * check if a reboot is required, warn user or just upgrade */ function upgrade_ui(major) { if (major !== true && $.status_reboot != "1") { backend('update'); } else { let reboot_msg = "{{ lang._('The firewall will reboot directly after this firmware update.') }}"; if (major === true) { reboot_msg = "{{ lang._('The firewall will download all firmware sets and reboot multiple times for this upgrade. All operating system files and packages will be reinstalled as a consequence. This may take several minutes to complete.') }}"; } // reboot required, inform the user. BootstrapDialog.show({ type:BootstrapDialog.TYPE_WARNING, title: "{{ lang._('Reboot required') }}", message: reboot_msg, buttons: [{ label: "{{ lang._('OK') }}", cssClass: 'btn-warning', action: function(dialogRef){ dialogRef.close(); backend(major === true ? 'upgrade' : 'update'); } },{ label: "{{ lang._('Cancel') }}", action: function(dialogRef){ dialogRef.close(); } }] }); } } function rebootWait() { $.ajax({ url: '/', timeout: 2500 }).fail(function () { setTimeout(rebootWait, 2500); }).done(function () { $(location).attr('href', '/'); }); } function trackStatus() { ajaxGet('/api/core/firmware/upgradestatus', { v: Date.now() }, function(data, status) { if (status != 'success') { // recover from temporary errors setTimeout(trackStatus, 1000); return; } if (data['log'] != undefined && data['log'] != '') { var autoscroll = $('#update_status')[0].scrollTop + $('#update_status')[0].clientHeight === $('#update_status')[0].scrollHeight; $('#update_status').html(data['log']); if (autoscroll) { $('#update_status').scrollTop($('#update_status')[0].scrollHeight); } } if (data['status'] == 'done') { $('#updatetab_progress').removeClass("fa fa-spinner fa-pulse"); if ($.upgrade_check === true) { updateStatus(); } else { packagesInfo(true); } } else if (data['status'] == 'reboot') { BootstrapDialog.show({ type:BootstrapDialog.TYPE_INFO, title: "{{ lang._('Your device is rebooting') }}", closable: false, message: "{{ lang._('The upgrade has finished and your device is being rebooted at the moment, please wait...') }}" + ' <i class="fa fa-cog fa-spin"></i>', onshow: function (dialogRef) { setTimeout(rebootWait, 45000); }, }); } else { // schedule next poll setTimeout(trackStatus, 500); } }); } /** * show package info */ function packagesInfo(reset) { $("#statustab_progress").addClass("fa fa-spinner fa-pulse"); ajaxGet('/api/core/firmware/info', {}, function (data, status) { $('#packageslist > tbody').empty(); $('#pluginlist > tbody').empty(); var installed = {}; $.each(data['product'], function(key, value) { if (key == 'product_check') { if (value != null) { $('#product_time_check').text(value['last_check']); } else { $('#product_time_check').text("{{ lang._('N/A') }}"); } } else { $('#' + key).text(value); } }); if (data['product']['product_license'] != undefined) { $.each(data['product']['product_license'], function(key, value) { $('#product_license_' + key).text(value).closest('tr').show(); }); if (!Object.keys(data['product']['product_license']).length) { $('[id^=product_license_]').closest('tr').hide(); } } $("#statustab_progress").removeClass("fa fa-spinner fa-pulse"); if (reset === true) { ajaxGet('/api/core/firmware/upgradestatus', { v: Date.now() }, function(data, status) { if (data['log'] != undefined && data['log'] != '') { $('#update_status').html(data['log']); } else { $('#update_status').html('{{ lang._('No previous action log found.') }}'); } $('#update_status').scrollTop($('#update_status')[0].scrollHeight); }); $('#updatelist').hide(); $('#update_status_container').show(); } var local_count = 0; var plugin_count = 0; var misconfigured_plugins = 0; var missing_plugins = 0; var changelog_count = 0; var changelog_max = 15; if ($.changelog_keep_full != undefined) { changelog_max = 9999; } $.each(data['package'], function(index, row) { if (row['installed'] == "1") { local_count += 1; } else { return 1; } $('#packageslist > tbody').append( '<tr class="package_entry">' + '<td>' + row['name'] + '</td>' + '<td>' + row['version'] + '</td>' + '<td>' + row['flatsize'] + '</td>' + '<td>' + row['repository'] + '</td>' + '<td>' + row['license'] + '</td>' + '<td>' + row['comment'] + '</td>' + '<td style="white-space:nowrap;vertical-align:middle;"><div class="input-group">' + '<button class="btn btn-default btn-xs act_license" data-package="' + row['name'] + '" ' + ' data-toggle="tooltip" title="{{ lang._('License') }}">' + '<i class="fa fa-balance-scale fa-fw"></i></button> ' + '<button class="btn btn-default btn-xs act_reinstall" data-package="' + row['name'] + '" ' + ' data-toggle="tooltip" title="{{ lang._('Reinstall') }}">' + '<i class="fa fa-recycle fa-fw"></i></button> ' + (row['locked'] === '1' ? '<button data-toggle="tooltip" title="{{ lang._('Unlock') }}" class="btn btn-default btn-xs act_unlock" data-package="' + row['name'] + '">' + '<i class="fa fa-lock fa-fw">' + '</i></button>' : '<button class="btn btn-default btn-xs act_lock" data-package="' + row['name'] + '" ' + ' data-toggle="tooltip" title="{{ lang._('Lock') }}" >' + '<i class="fa fa-unlock fa-fw"></i></button>' ) + '</div></td>' + '</tr>' ); }); if (local_count == 0) { $('#packageslist > tbody').append( '<tr><td colspan=6>{{ lang._('No packages were found on your system. Please call for help.') }}</td></tr>' ); } $.each(data['plugin'], function(index, row) { if (row['provided'] == "1") { plugin_count += 1; } let status_text = ''; let bold_on = ''; let bold_off = ''; if (row['installed'] == "1" && row['configured'] == "0") { status_text = ' ({{ lang._('misconfigured') }})'; bold_on = '<b>'; bold_off = '</b>'; misconfigured_plugins = 1; } else if (row['installed'] == "0" && row['configured'] == "1") { status_text = ' ({{ lang._('missing') }})'; bold_on = '<span class="text-danger"><b>'; bold_off = '</b></span>'; missing_plugins = 1; } else if (row['installed'] == "1") { if (row['provided'] == "0") { status_text = ' ({{ lang._('orphaned') }})'; } else { status_text = ' ({{ lang._('installed') }})'; } bold_on = '<b>'; bold_off = '</b>'; } $('#pluginlist > tbody').append( '<tr class="plugin_entry">' + '<td>' + bold_on + row['name'] + status_text + bold_off + '</td>' + '<td>' + bold_on + row['version'] + bold_off + '</td>' + '<td>' + bold_on + row['flatsize'] + bold_off + '</td>' + '<td>' + bold_on + row['tier'] + bold_off + '</td>' + '<td>' + bold_on + row['repository'] + bold_off + '</td>' + '<td>' + bold_on + row['comment'] + bold_off + '</td>' + '<td style="white-space:nowrap;vertical-align:middle;"><div class="input-group">' + '<button class="btn btn-default btn-xs act_details" data-package="' + row['name'] + '" ' + ' data-toggle="tooltip" title="{{ lang._('Info') }}">' + '<i class="fa fa-info-circle fa-fw"></i></button>' + (row['installed'] == "1" ? '<button class="btn btn-default btn-xs act_remove" data-package="' + row['name'] + '" '+ ' data-toggle="tooltip" title="{{ lang._('Remove') }}">' + '<i class="fa fa-trash fa-fw">' + '</i></button>' : '<button class="btn btn-default btn-xs act_install" data-package="' + row['name'] + '" ' + 'data-repository="'+row['repository']+'" data-toggle="tooltip" title="{{ lang._('Install') }}">' + '<i class="fa fa-plus fa-fw">' + '</i></button>' ) + '</div></td>' + '</tr>' ); }); if (plugin_count == 0) { $('#pluginlist > tbody').append( '<tr><td colspan=5>{{ lang._('Check for updates to view available plugins.') }}</td></tr>' ); } if (data['product']['product_log']) { $('#audit_upgrade').parent().show(); } else { $('#audit_upgrade').parent().hide(); } $('#audit_actions').show(); $("#plugin_search").keyup(); $("#package_search").keyup(); if (misconfigured_plugins || missing_plugins) { if (!missing_plugins) { $("#plugin_get").parent().hide(); } else { $("#plugin_get").parent().show(); } $('#plugin_actions').show(); } else { $('#plugin_actions').hide(); } $("#changeloglist > tbody").empty(); $("#changeloglist > thead").html("<tr><th>{{ lang._('Version') }}</th>" + "<th>{{ lang._('Date') }}</th><th></th></tr>"); const installed_version = data['product_version'].replace(/[_-].*/, ''); $.each(data['changelog'], function(index, row) { changelog_count += 1; let status_text = ''; let bold_on = ''; let bold_off = ''; if (installed_version == row['version']) { status_text = ' ({{ lang._('installed') }})'; bold_on = '<b>'; bold_off = '</b>'; } $('#changeloglist > tbody').append( '<tr' + (changelog_count > changelog_max ? ' class="changelog-hidden" style="display: none;" ' : '' ) + '><td>' + bold_on + row['version'] + status_text + bold_off + '</td><td>' + bold_on + row['date'] + bold_off + '</td>' + '<td><button class="btn btn-default btn-xs act_changelog" data-version="' + row['version'] + '" ' + 'data-toggle="tooltip" title="{{ lang._('View') }}">' + '<i class="fa fa-book fa-fw"></i></button></td></tr>' ); }); if (!data['changelog'].length) { $('#changeloglist > tbody').append( '<tr><td colspan=3>{{ lang._('Check for updates to view changelog history.') }}</td></tr>' ); } if (changelog_count > changelog_max) { $('#changeloglist > tbody').append( '<tr class= "changelog-full"><td colspan=3><a id="changelog-act" href="#">{{ lang._('Click to view full changelog history.') }}</a></td></tr>' ); $("#changelog-act").click(function(event) { event.preventDefault(); $(".changelog-hidden").attr('style', ''); $(".changelog-full").attr('style', 'display: none;'); $.changelog_keep_full = 1; }); } // link buttons to actions $(".act_reinstall").click(function(event) { event.preventDefault(); action_may_reboot('reinstall', $(this).data('package')); }); $(".act_unlock").click(function(event) { event.preventDefault(); backend('unlock/' + $(this).data('package')); }); $(".act_lock").click(function(event) { event.preventDefault(); backend('lock/' + $(this).data('package')); }); $(".act_remove").click(function(event) { event.preventDefault(); let plugin_name = $(this).data('package'); BootstrapDialog.show({ type:BootstrapDialog.TYPE_WARNING, title: "{{ lang._('Confirm removal') }}", message: "{{ lang._('Do you really want to remove this plugin?') }}" + " <strong>" + plugin_name + "</strong>", buttons: [{ label: "{{ lang._('OK') }}", cssClass: 'btn-warning', action: function (dialogRef) { dialogRef.close(); backend('remove/' + plugin_name); } },{ label: "{{ lang._('Cancel') }}", action: function(dialogRef){ dialogRef.close(); } }] }); }); $(".act_details").click(function(event) { event.preventDefault(); details($(this).data('package')); }); $(".act_install").click(function(event) { event.preventDefault(); let package_name = $(this).data('package'); /* XXX temporary placeholder to inform the user that he/she is installing from a different (external) source */ if ($(this).data('repository') !== 'OPNsense') { BootstrapDialog.show({ type:BootstrapDialog.TYPE_INFO, title: "{{ lang._('Third party software') }}", message: "{{ lang._('This software package is provided by an external vendor, for more information contact the author')}}", buttons: [{ label: "{{ lang._('Install') }}", action: function(dialogRef){ dialogRef.close(); backend('install/' + package_name); } }, { label: "{{ lang._('Cancel') }}", action: function(dialogRef){ dialogRef.close(); } }] }); } else { backend('install/' + package_name); } }); $(".act_changelog").click(function(event) { event.preventDefault(); changelog($(this).data('version')); }); $(".act_license").click(function(event) { event.preventDefault(); license($(this).data('package')); }); // attach tooltip to generated buttons $('[data-toggle="tooltip"]').tooltip(); }); } $( document ).ready(function() { // link event handlers $('#checkupdate').click(function () { backend('check'); }); $('#upgrade').click(function () { upgrade_ui(false); }); $('#upgrade_maj').click(function () { upgrade_ui(true); }); $('#upgrade_cancel').click(cancel_update); $("#plugin_see").click(function () { $('#plugintab > a').tab('show'); }); $("#plugin_get").click(function () { backend('syncPlugins'); }); $("#plugin_set").click(function () { backend('resyncPlugins'); }); $('#audit_security').click(function () { backend('audit'); }); $('#audit_connection').click(function () { backend('connection'); }); $('#audit_health').click(function () { backend('health'); }); $('#audit_upgrade').click(function () { ajaxCall('/api/core/firmware/log/0', {}, function (data, status) { if (data['log'] != undefined) { stdDialogConfirm("{{ lang._('Upgrade log') }}", data['log'], "{{ lang._('Clear') }}", "{{ lang._('Close') }}", function () { ajaxCall('/api/core/firmware/log/1'); $('#audit_upgrade').parent().hide(); }, 'primary'); } }); }); // populate package information packagesInfo(true); $("#plugin_search").keyup(function () { generic_search(this, 'plugin_entry'); }); $("#package_search").keyup(function () { generic_search(this, 'package_entry'); }); ajaxGet('/api/core/firmware/running', {}, function(data, status) { if (data['status'] == 'busy') { // if action is already running reattach now... backend('audit'); } else if (window.location.hash == '#checkupdate') { // dashboard link: run check automatically after delay setTimeout(function () { backend('check'); }, 2000); } }); // handle firmware config options function fillOptions() { ajaxGet('/api/core/firmware/get_options', {}, function(firmwareoptions, status) { ajaxGet('/api/core/firmware/get', {}, function(config, status) { var firmwareconfig = config['firmware']; var custom_selected = true; $("#firmware_mirror").find('option').remove(); $("#firmware_type").find('option').remove(); $("#firmware_flavour").find('option').remove(); $("#firmware_reboot").prop('checked', firmwareconfig['reboot'] !== ''); $.each(firmwareoptions.mirrors, function(key, value) { var selected = false; if (key == firmwareconfig['mirror']) { selected = true; custom_selected = false; } $("#firmware_mirror").append($("<option/>") .attr("value",key) .text(value) .prop('selected', selected) ); }); if (firmwareoptions['mirrors_allow_custom']) { $("#firmware_mirror :first-child").after($("<option/>") .attr("value", firmwareconfig['mirror']) .text("(custom)") .data("custom", 1) .prop('selected', custom_selected) ); } $("#firmware_subscription").val(firmwareconfig['subscription']); $("#firmware_mirror").selectpicker('refresh'); $("#firmware_mirror").change(); custom_selected = true; $.each(firmwareoptions.flavours, function(key, value) { var selected = false; if (key == firmwareconfig['flavour']) { selected = true; custom_selected = false; } $("#firmware_flavour").append($("<option/>") .attr("value",key) .text(value) .prop('selected', selected) ); }); if (firmwareoptions['flavours_allow_custom']) { $("#firmware_flavour :first-child").after($("<option/>") .attr("value", firmwareconfig['flavour']) .text("(custom)") .data("custom", 1) .prop('selected', custom_selected) ); } $("#firmware_flavour").selectpicker('refresh'); $("#firmware_flavour").change(); if (firmwareconfig['flavour'] !== '' || firmwareconfig['reboot'] !== '') { $("i.fa-toggle-off#show_advanced_firmware").click(); } $.each(firmwareoptions.families, function(key, value) { var selected = false; if (key == firmwareconfig['type']) { selected = true; } $("#firmware_type").append($("<option/>") .attr("value",key) .text(value) .prop('selected', selected) ); }); $("#firmware_type").selectpicker('refresh'); $("#firmware_type").change(); }); }); } fillOptions(); $("#reset_mirror").click(fillOptions); $("#firmware_mirror").change(function(){ $("#firmware_mirror_value").val($(this).val()); if ($(this).find(':selected').data("custom") == 1) { $("#firmware_mirror_custom").show(); } else { $("#firmware_mirror_custom").hide(); } }); $("#firmware_flavour").change(function() { $("#firmware_flavour_value").val($(this).val()); if ($(this).find(':selected').data("custom") == 1) { $("#firmware_flavour_custom").show(); } else { $("#firmware_flavour_custom").hide(); } }); $("#change_mirror").click(function(){ $("#settingstab_progress").addClass("fa fa-spinner fa-pulse"); var confopt = {}; confopt.mirror = $("#firmware_mirror_value").val(); confopt.flavour = $("#firmware_flavour_value").val(); confopt.type = $("#firmware_type").val(); confopt.reboot = $("#firmware_reboot").is(":checked"); confopt.subscription = $("#firmware_subscription").val(); ajaxCall('/api/core/firmware/set', { 'firmware': confopt }, function (data, status) { $("#settingstab_progress").removeClass("fa fa-spinner fa-pulse"); if (data['status'] == 'ok') { packagesInfo(true); } else { let validation_msgs = '<ul>'; for (i = 0; i < data['status_msg'].length; i++) { validation_msgs += '<li>' + $("<textarea/>").html(data['status_msg'][i]).text() + '</li>'; } validation_msgs += '</ul>'; stdDialogInform('{{ lang._('Firmware status') }}', htmlDecode(validation_msgs), "{{ lang._('Close') }}", undefined, 'danger'); } }); }); $("#update_status_copy").click(function () { $("#update_status").select(); document.execCommand("copy"); document.getSelection().removeAllRanges(); setTimeout(function () { $("#update_status").blur(); }, 100); }); // update history on tab state and implement navigation if(window.location.hash != "") { $('a[href="' + window.location.hash + '"]').click() } $('.nav-tabs a').on('shown.bs.tab', function (e) { history.pushState(null, null, e.target.hash); }); $(window).on('hashchange', function(e) { $('a[href="' + window.location.hash + '"]').click() }); }); </script> <div class="container-fluid"> <div class="row"> <div class="col-md-12" id="content"> <ul class="nav nav-tabs" data-tabs="tabs"> <li id="statustab" class="active"><a data-toggle="tab" href="#status">{{ lang._('Status') }} <i id="statustab_progress"></i></a></li> <li id="settingstab"><a data-toggle="tab" href="#settings">{{ lang._('Settings') }} <i id="settingstab_progress"></i></a></li> <li id="changelogtab"><a data-toggle="tab" href="#changelog">{{ lang._('Changelog') }}</a></li> <li id="updatetab"><a data-toggle="tab" href="#updates">{{ lang._('Updates') }} <i id="updatetab_progress"></i></a></li> <li id="plugintab"><a data-toggle="tab" href="#plugins">{{ lang._('Plugins') }}</a></li> <li id="packagestab"><a data-toggle="tab" href="#packages">{{ lang._('Packages') }}</a></li> </ul> <div class="tab-content content-box"> <div id="updates" class="tab-pane table-responsive"> <table class="table table-striped table-condensed" id="updatelist" style="display: none;"> <thead> <tr> <th style="width:20%">{{ lang._('Package name') }}</th> <th style="width:20%">{{ lang._('Current version') }}</th> <th style="width:20%">{{ lang._('New version') }}</th> <th style="width:20%">{{ lang._('Required action') }}</th> <th style="width:20%">{{ lang._('Repository') }}</th> </tr> </thead> <tbody></tbody> <tfoot> <tr> <td></td> <td style="white-space:nowrap;vertical-align:middle;"> <button class="btn btn-info" id="upgrade"><i class="fa fa-check"></i> {{ lang._('Update') }}</button> <button class='btn btn-warning' id="upgrade_maj"><i class="fa fa-check"></i> {{ lang._('Upgrade') }}</button> <button class="btn btn-default" id="upgrade_cancel"><i class="fa fa-times"></i> {{ lang._('Cancel') }}</button> </td> <td colspan="2" style="vertical-align:middle"> <strong><div id="updatestatus"></div></strong> </td> <td></td> </tr> </tfoot> </table> <div id="update_status_container" class="table-responsive"> <textarea name="output" id="update_status" class="form-control" rows="20" wrap="hard" readonly="readonly" style="max-width:100%; font-family: monospace;"></textarea> <table class="table table-striped table-condensed"> <tbody> <tr> <td> {{ lang._('Output shown here for diagnostic purposes. There is no general need for manual system intervention.') }} <a id="update_status_copy">{{ lang._('Click here to copy to clipboard.') }}</a> </td> </tr> </tbody> </table> </div> </div> <div id="status" class="tab-pane active table-responsive"> <table class="table table-striped table-condensed"> <tbody> <tr> <td style="width: 150px;">{{ lang._('Type') }}</td> <td id="product_id"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Version') }}</td> <td id="product_version"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Architecture') }}</td> <td id="product_arch"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Commit') }}</td> <td id="product_hash"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Mirror') }}</td> <td id="product_mirror"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Repositories') }}</td> <td id="product_repos"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Updated on') }}</td> <td id="product_time"></td> <td></td> </tr> <tr> <td style="width: 150px;">{{ lang._('Checked on') }}</td> <td id="product_time_check"></td> <td></td> </tr> <tr style='display:none'> <td style="width: 150px;">{{ lang._('Licensed until') }}</td> <td id="product_license_valid_to"></td> <td></td> </tr> <tr> <td style="width: 150px;"></td> <td style="min-width: 500px;"> <button class="btn btn-primary" id="checkupdate"><i class="fa fa-refresh"></i> {{ lang._('Check for updates') }}</button> <div class="btn-group dropdown" id="audit_actions" style="display:none;"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-lock"></i> {{ lang._('Run an audit') }} <i class="caret"></i> </button> <ul class="dropdown-menu" role="menu"> <li><a id="audit_connection" href="#">{{ lang._('Connectivity') }}</a></li> <li><a id="audit_health" href="#">{{ lang._('Health') }}</a></li> <li><a id="audit_security" href="#">{{ lang._('Security') }}</a></li> <li><a id="audit_upgrade" href="#">{{ lang._('Upgrade') }}</a></li> </ul> </div> <div class="btn-group dropdown" id="plugin_actions" style="display:none;"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-exclamation-triangle"></i> {{ lang._('Resolve plugin conflicts') }} <i class="caret"></i> </button> <ul class="dropdown-menu" role="menu"> <li><a id="plugin_see" href="#">{{ lang._('View and edit local conflicts') }}</a></li> <li><a id="plugin_get" href="#">{{ lang._('Run the automatic resolver') }}</a></li> <li><a id="plugin_set" href="#">{{ lang._('Reset all local conflicts') }}</a></li> </ul> </div> </td> <td></td> </tr> <tr class="dropdown_helper" style="padding-bottom: 120px;"></tr> </tbody> </table> </div> <div id="plugins" class="tab-pane table-responsive"> <table class="table table-striped table-condensed" id="pluginlist"> <thead> <tr> <th style="vertical-align:middle"><input type="text" class="input-sm" autocomplete="off" id="plugin_search" placeholder="{{ lang._('Name') }}"></th> <th style="vertical-align:middle">{{ lang._('Version') }}</th> <th style="vertical-align:middle">{{ lang._('Size') }}</th> <th style="vertical-align:middle">{{ lang._('Tier') }}</th> <th style="vertical-align:middle">{{ lang._('Repository') }}</th> <th style="vertical-align:middle">{{ lang._('Comment') }}</th> <th style="vertical-align:middle"></th> </tr> </thead> <tbody></tbody> </table> </div> <div id="packages" class="tab-pane table-responsive"> <table class="table table-striped table-condensed" id="packageslist"> <thead> <tr> <th style="vertical-align:middle"><input type="text" class="input-sm" autocomplete="off" id="package_search" placeholder="{{ lang._('Name') }}"></th> <th style="vertical-align:middle">{{ lang._('Version') }}</th> <th style="vertical-align:middle">{{ lang._('Size') }}</th> <th style="vertical-align:middle">{{ lang._('Repository') }}</th> <th style="vertical-align:middle">{{ lang._('License') }}</th> <th style="vertical-align:middle">{{ lang._('Comment') }}</th> <th style="vertical-align:middle"></th> </tr> </thead> <tbody></tbody> </table> </div> <div id="changelog" class="tab-pane table-responsive"> <table class="table table-striped table-condensed" id="changeloglist"> <thead></thead> <tbody></tbody> </table> </div> <div id="settings" class="tab-pane table-responsive"> <table class="table table-striped table-condensed"> <tbody> <tr> <td style="text-align:left"><i class="fa fa-toggle-off text-danger" id="show_advanced_firmware"></i></a> <small>{{ lang._('advanced mode') }}</small></td> <td colspan="2" style="text-align:right"> <small>{{ lang._('full help') }}</small> <a href="#"><i class="fa fa-toggle-off text-danger" id="show_all_help_firmware"></i></a> </td> </tr> <tr> <td style="width: 150px;"><a id="help_for_mirror" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {{ lang._('Mirror') }}</td> <td> <select class="selectpicker" id="firmware_mirror" data-size="5" data-live-search="true"> </select> <div style="display:none;" id="firmware_mirror_custom"> <input type="text" id="firmware_mirror_value"> </div> <div class="hidden" data-for="help_for_mirror"> {{ lang._('Select an alternate firmware mirror.') }} </div> </td> <td></td> </tr> <tr data-advanced="true"> <td><a id="help_for_flavour" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {{ lang._('Flavour') }}</td> <td> <select class="selectpicker" id="firmware_flavour"> </select> <div style="display:none;" id="firmware_flavour_custom"> <input type="text" id="firmware_flavour_value"> </div> <div class="hidden" data-for="help_for_flavour"> {{ lang._('Select an alternate firmware flavour.') }} </div> </td> <td></td> </tr> <tr> <td><a id="help_for_type" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {{ lang._('Type') }}</td> <td> <select class="selectpicker" id="firmware_type"> </select> <div class="hidden" data-for="help_for_type"> {{ lang._('Select the release type.') }} </div> </td> <td></td> </tr> <tr> <td style="width: 150px;"><a id="help_for_subscription" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> {{ lang._('Subscription') }}</td> <td> <input type="text" id="firmware_subscription"> <div class="hidden" data-for="help_for_subscription"> {{ lang._('Provide subscription key.') }} </div> </td> <td></td> </tr> <tr data-advanced="true"> <td style="width: 150px;"><i class="fa fa-info-circle text-muted"></i> {{ lang._('Reboot') }}</td> <td> <input type="checkbox" id="firmware_reboot"> {{ lang._('Always reboot after a successful update') }} </td> <td></td> </tr> <tr> <td style="width: 150px;"><i class="fa fa-info-circle text-muted"></i> {{ lang._('Usage') }}</td> <td> {{ lang._('In order to apply these settings a firmware update must be performed after save, which can include a reboot of the system.') }} </td> <td></td> </tr> <tr> <td></td> <td> <button class="btn btn-primary" id="change_mirror" type="button"><i class="fa fa-floppy-o"></i> {{ lang._('Save') }}</button> <button class="btn btn-default" id="reset_mirror" type="button"><i class="fa fa-times"></i> {{ lang._('Cancel') }}</button> </td> <td></td> </tr> </tbody> </table> </div> </div> </div> </div> <div class="row"> <div class="col-md-12"> </div> </div> </div>