From 253e27d6499df0ddeb81efd9fea9c5a38893cd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Hal=C3=A1sz?= Date: Wed, 18 Jan 2017 13:06:30 +0100 Subject: [PATCH] Unified the layout for VNC/SPICE remote consoles - Created a common HAML view for both console types - Separate JS files with preprocessed dependencies - Removed the old HAML views for VNC/SPICE - Removed some dead code in the related controller --- .../javascripts/remote_consoles/spice.js | 32 ++++++++ app/assets/javascripts/remote_consoles/vnc.js | 39 ++++++++++ app/controllers/vm_common.rb | 27 +++---- app/views/layouts/remote_console.html.haml | 75 ++++++++++++++++++ app/views/vm_common/console_spice.html.haml | 72 ------------------ app/views/vm_common/console_vnc.html.haml | 76 ------------------- config/initializers/assets.rb | 3 + 7 files changed, 163 insertions(+), 161 deletions(-) create mode 100644 app/assets/javascripts/remote_consoles/spice.js create mode 100644 app/assets/javascripts/remote_consoles/vnc.js create mode 100644 app/views/layouts/remote_console.html.haml delete mode 100644 app/views/vm_common/console_spice.html.haml delete mode 100644 app/views/vm_common/console_vnc.html.haml create mode 100644 config/initializers/assets.rb diff --git a/app/assets/javascripts/remote_consoles/spice.js b/app/assets/javascripts/remote_consoles/spice.js new file mode 100644 index 00000000000..f8807b638bf --- /dev/null +++ b/app/assets/javascripts/remote_consoles/spice.js @@ -0,0 +1,32 @@ +//= require jquery +//= require spice-html5-bower/spiceHTML5/spicearraybuffer +//= require spice-html5-bower +//= require_tree ../locale +//= require gettext/all + +$(function() { + var host = window.location.hostname; + var encrypt = window.location.protocol === 'https:'; + var port = encrypt ? 443 : 80; + if (window.location.port) { + port = window.location.port; + } + + $('#ctrlaltdel').click(sendCtrlAltDel); + + var spice = new SpiceMainConn({ + uri: (encrypt ? 'wss://' : 'ws://') + host + ':' + port + '/' + $('#remote-console').attr('data-url'), + screen_id: "remote-console", + password: $('#remote-console').attr('data-secret'), + onerror: function(e) { + spice.stop(); + $('#connection-status').removeClass('label-success label-warning').addClass('label-danger'); + $('#connection-status').text(__('Disconnected')); + console.error("SPICE", e); + }, + onsuccess: function() { + $('#connection-status').removeClass('label-danger label-warning').addClass('label-success'); + $('#connection-status').text(__('Connected')); + }, + }); +}); diff --git a/app/assets/javascripts/remote_consoles/vnc.js b/app/assets/javascripts/remote_consoles/vnc.js new file mode 100644 index 00000000000..c6f3b0bb51d --- /dev/null +++ b/app/assets/javascripts/remote_consoles/vnc.js @@ -0,0 +1,39 @@ +//= require jquery +//= require novnc-rails +//= require_tree ../locale +//= require gettext/all + +$(function() { + var host = window.location.hostname; + var encrypt = window.location.protocol === 'https:'; + var port = encrypt ? 443 : 80; + if (window.location.port) { + port = window.location.port; + } + + // noVNC requires an empty canvas item + var canvas = document.createElement('canvas'); + $('#remote-console').append(canvas); + + var vnc = new RFB({ + target: canvas, + encrypt: encrypt, + true_color: true, + local_cursor: true, + shared: true, + view_only: false, + onUpdateState: function(_, state, _, msg) { + if (['normal', 'loaded'].includes(state)) { + $('#connection-status').removeClass('label-danger label-warning').addClass('label-success'); + $('#connection-status').text(__('Connected')); + } else if (state === 'disconnected') { + $('#connection-status').removeClass('label-success label-warning').addClass('label-danger'); + $('#connection-status').text(__('Disconnected')); + console.error('VNC', msg); + } + }, + }); + + $('#ctrlaltdel').click(vnc.sendCtrlAltDel); + vnc.connect(host, port, $('#remote-console').attr('data-secret'), $('#remote-console').attr('data-url')); +}); diff --git a/app/controllers/vm_common.rb b/app/controllers/vm_common.rb index cb632d8ac69..c110bc2a987 100644 --- a/app/controllers/vm_common.rb +++ b/app/controllers/vm_common.rb @@ -156,22 +156,23 @@ def vm_selected end def launch_html5_console - proto = request.ssl? ? 'wss' : 'ws' + scheme = request.ssl? ? 'wss' : 'ws' override_content_security_policy_directives( - :connect_src => ["'self'", "#{proto}://#{request.env['HTTP_HOST']}"], + :connect_src => ["'self'", "#{scheme}://#{request.env['HTTP_HOST']}"], :img_src => %w(data: 'self') ) - %i(secret url).each { |p| params.require(p) } - @secret = j(params[:secret]) - @url = j(params[:url]) - - case j(params[:proto]) - when 'spice' # spice, vnc - from rhevm - render(:template => 'vm_common/console_spice', :layout => false) - when nil, 'vnc' # nil - from vmware - render(:template => 'vm_common/console_vnc', :layout => false) - when 'novnc_url' # from OpenStack - redirect_to host_address + %i(secret url proto).each { |p| params.require(p) } + + proto = j(params[:proto]) + if %w(vnc spice).include?(proto) # VMWare, RHEV + @console = { + :url => j(params[:url]), + :secret => j(params[:secret]), + :type => proto + } + render(:template => 'layouts/remote_console', :layout => false) + else + raise 'Unsupported protocol' end end diff --git a/app/views/layouts/remote_console.html.haml b/app/views/layouts/remote_console.html.haml new file mode 100644 index 00000000000..9a593a59d4c --- /dev/null +++ b/app/views/layouts/remote_console.html.haml @@ -0,0 +1,75 @@ += render :partial => 'layouts/doctype' +%html{:lang => I18n.locale.to_s.sub('-', '_')} + %head + %title + = _('ManageIQ HTML5 Remote Console') + = favicon_link_tag + = stylesheet_link_tag 'application' + -# Load the required JS based on the console type + = javascript_include_tag 'jquery', "remote_consoles/#{@console[:type]}" + -# Css for the unified look & feel + :css + #remote-console { + position: fixed; + width: 100%; + height: calc(100% - 30px) !important; + overflow: auto; + } + footer { + position: fixed; + line-height: 30px; + vertical-align: middle; + height: 30px; + width: 100%; + top: calc(100% - 32px); + padding-left: 0.2em; + padding-right: 0.2em; + } + -# Handling the fullscreen button + :javascript + $(function () { + $('#fullscreen').click(function () { + switch(true) { + case document.fullScreenEnabled: + if (document.fullscreenElement) { + document.exitFullscreen(); + } else { + document.documentElement.requestFullscreen(); + } + break; + case document.webkitFullscreenEnabled: + if (document.webkitFullscreenElement) { + document.webkitExitFullscreen(); + } else { + document.documentElement.webkitRequestFullscreen(); + } + break; + case document.mozFullScreenEnabled: + if (document.mozFullscreenElement) { + document.mozExitFullscreen(); + } else { + document.documentElement.mozRequestFullscreen(); + } + break; + case document.msFullscreenEnabled: + if (document.msFullscreenElement) { + document.msExitFullscreen(); + } else { + document.documentElement.msRequestFullscreen(); + } + break; + } + }); + + }); + %body + #remote-console{'data-url' => @console[:url], 'data-secret' => @console[:secret]} + %footer + .pull-left + %span#console-type.label.label-info= @console[:type].upcase + %span#connection-status.label.label-warning Connecting + .pull-right + %button#ctrlaltdel.btn.btn-default{:title => _('Send CTRL+ALT+DEL')} + %i.fa.fa-keyboard-o + %button#fullscreen.btn.btn-default{:title => _('Toggle Fullscreen')} + %i.fa.fa-arrows-alt diff --git a/app/views/vm_common/console_spice.html.haml b/app/views/vm_common/console_spice.html.haml deleted file mode 100644 index 7f64f6be2fa..00000000000 --- a/app/views/vm_common/console_spice.html.haml +++ /dev/null @@ -1,72 +0,0 @@ -= render :partial => 'layouts/doctype' -%html{:lang => I18n.locale.to_s.sub('-', '_')} - %head - %title - = _('SPICE Console') - = favicon_link_tag - = stylesheet_link_tag 'application' - = javascript_include_tag 'jquery', 'spice-html5-bower/spiceHTML5/spicearraybuffer', 'spice-html5-bower' - - %body{:style => 'margin: 0px; padding-top: 0px !important;'} - = link_to('Ctrl-Alt-Del', '#', :id => 'sendCtrlAltDelButton', :class => 'btn btn-default', :onclick => 'sendCtrlAltDel()') - #spice-area - .console-status - #spice-status.label{'data-host' => @url} - = _("Connecting (unencrypted) to: %{url}") % {:url => @url} - - #spice-screen.console-screen - - :javascript - var sc = null; - var encrypt; - - $(function () { - var host = window.location.hostname; - var encrypt = window.location.protocol == 'https:'; - var port = encrypt ? 443 : 80; - if (window.location.port) port = window.location.port; - - var secret = "#{j(@secret)}"; - var url = "#{j(@url)}"; - - if (!host || !port || !secret) { - spice_error(__("must set host, port and secret")); - return; - } - - spice_connecting(); - - uri = (encrypt ? "wss://" : "ws://") + host + ":" + port + '/' + url; - - sc = new SpiceMainConn({ - uri: uri, - screen_id: "spice-screen", - password: secret, - onerror: spice_error, - onsuccess: spice_success - }); - }); - - function disconnect() { - if (sc) { sc.stop(); } - } - - function spice_error(e) { - $('#spice-status').text(e); - $('#spice-status').removeClass('label-success').addClass('label-danger'); - disconnect(); - } - - function spice_connecting() { - var enc_text = encrypt ? 'encrypted' : 'unencrypted'; - $('#spice-status').text('Connecting ('+ enc_text + ') to: ' + - $('#spice-status').attr('data-host')); - $('#spice-status').addClass('label-success'); - } - - function spice_success(m) { - var enc_text = encrypt ? 'encrypted' : 'unencrypted'; - $('#spice-status').text('Connected ('+ enc_text + ') to: ' + - $('#spice-status').attr('data-host')); - $('#spice-status').addClass('label-success'); - } diff --git a/app/views/vm_common/console_vnc.html.haml b/app/views/vm_common/console_vnc.html.haml deleted file mode 100644 index 262f316fc87..00000000000 --- a/app/views/vm_common/console_vnc.html.haml +++ /dev/null @@ -1,76 +0,0 @@ -= render :partial => 'layouts/doctype' -%html{:lang => I18n.locale.to_s.sub('-', '_')} - %head - %title - = _('VNC Console') - = favicon_link_tag - = stylesheet_link_tag 'application' - = javascript_include_tag 'jquery', 'novnc-rails' - - %body{:style => 'margin: 0px; padding-top: 0px !important;'} - = link_to('Ctrl-Alt-Del', '#', :id => 'sendCtrlAltDelButton', :class => 'btn btn-default') - #vnc - #noVNC_status_bar.console-status - #noVNC_status.label - = _('Loading ...') - - %canvas#noVNC_canvas.console-screen - = _('Canvas not supported.') - - :javascript - (function(){ - "use strict"; - var rfb; - - window.WEB_SOCKET_SWF_LOCATION = '#{asset_path('noVNC/web-socket-js/WebSocketMain.swf')}'; - - function sendCtrlAltDel() { - rfb.sendCtrlAltDel(); - return false; - } - - function updateState(rfb, state, oldstate, msg) { - var level; - var s = $D('noVNC_status'); - var sb = $D('noVNC_status'); - var cad = $D('sendCtrlAltDelButton'); - switch (state) { - case 'failed': level = "danger"; break; - case 'fatal': level = "danger"; break; - case 'normal': level = "success"; break; - case 'disconnected': level = "default"; break; - case 'loaded': level = "success"; break; - default: level = "warning"; break; - } - - cad.disabled = state !== "normal"; - - if (typeof (msg) !== 'undefined') { - sb.setAttribute("class", "label label-" + level); - s.innerHTML = msg; - } - } - - $(function () { - $D('sendCtrlAltDelButton').style.display = "inline"; // FIXME - $D('sendCtrlAltDelButton').onclick = sendCtrlAltDel; - - var host = window.location.hostname; - var encrypt = window.location.protocol == 'https:'; - var port = encrypt ? 443 : 80; - if (window.location.port) port = window.location.port; - - var secret = "#{j(@secret)}"; - var url = "#{j(@url)}"; - - rfb = new RFB({ - target: $D('noVNC_canvas'), - encrypt: encrypt, - true_color: true, - local_cursor: true, - shared: true, - view_only: false, - onUpdateState: updateState}); - rfb.connect(host, port, secret, url); - }); - })(); diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 00000000000..d66854c04d9 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,3 @@ +Rails.application.config.assets.precompile += %w( + remote_consoles/*.js +)