[PATCH v5] UI: Software Update Support

Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available. Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection v4 -> v5: 5a) Disable row selection in software update grid (Thanks to Royce's comment) v3 -> v4: 4a) Added the last update output (Thanks to Aline's comment) 4b) Disabled horizontal resize function of output textarea (Thanks to Aline's comment) 4c) Added "Update Progress" label to the output textarea (Thanks to Aline's comment) 4d) Added refreshing the software grid after updating is finished (Thanks to Aline's comment) 4e) Added software updates grid cleanup when host tab is unloaded v2 -> v3: 3a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo and Aline's comment) 3b) Updated REST API calling according to back-end change 3c) Added in-progress message when system is being updated (Thanks to Aline's comment) v1 -> v2: 2a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo Ricardo Paz Vital's comment) Signed-off-by: Hongliang Wang <hlwang@linux.vnet.ibm.com> --- ui/css/theme-default/host.css | 29 ++++++++++++++ ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++ ui/js/src/kimchi.host.js | 89 +++++++++++++++++++++++++++++++++++++++++++ ui/pages/i18n.html.tmpl | 10 +++++ ui/pages/tabs/host.html.tmpl | 17 +++++++++ 5 files changed, 198 insertions(+) diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css index 470ed1b..0f8b941 100644 --- a/ui/css/theme-default/host.css +++ b/ui/css/theme-default/host.css @@ -224,3 +224,32 @@ width: 300px; } /* End of Debug Report */ + +/* Software Updates */ +.host-panel #software-updates-grid { + border-color: #ddd; + height: 300px; + width: 850px; +} + +.software-update-id { + width: 30px; +} + +.software-update-name, +.software-update-repos { + width: 220px; +} + +.software-update-version, +.software-update-arch { + width: 190px; +} + +.host-panel #software-updates-progress-textarea { + border: 1px solid #ddd; + height: 100px; + resize: vertical; + width: 846px; +} +/* End of Software Updates */ diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index fdd9cfc..beaf314 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -727,5 +727,58 @@ var kimchi = { success : suc, error : err }); + }, + + listSoftwareUpdates : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/packagesupdate', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend: true, + success : suc, + error : err + }); + }, + + updateSoftwares : function(suc, err, progress) { + var taskID = -1; + var onResponse = function(data) { + taskID = data['id']; + trackTask(); + }; + + var trackTask = function() { + kimchi.getTask(taskID, onTaskResponse, err); + }; + + var onTaskResponse = function(result) { + var taskStatus = result['status']; + switch(taskStatus) { + case 'running': + progress && progress(result); + setTimeout(function() { + trackTask(); + }, 200); + break; + case 'finished': + suc(result); + break; + case 'failed': + err(result); + break; + default: + break; + } + }; + + kimchi.requestJSON({ + url : kimchi.url + 'host/swupdate', + type : "POST", + contentType : "application/json", + dataType : "json", + success : onResponse, + error : err + }); } }; diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js index 7974054..e146a75 100644 --- a/ui/js/src/kimchi.host.js +++ b/ui/js/src/kimchi.host.js @@ -133,6 +133,88 @@ kimchi.host_main = function() { }); }; + var softwareUpdatesGridID = 'software-updates-grid'; + var softwareUpdatesGrid = null; + var progressAreaID = 'software-updates-progress-textarea'; + var reloadProgressArea = function(result) { + var progressArea = $('#' + progressAreaID)[0]; + $(progressArea).text(result['message']); + var scrollTop = $(progressArea).prop('scrollHeight'); + $(progressArea).prop('scrollTop', scrollTop); + }; + + var initSoftwareUpdatesGrid = function(softwareUpdates) { + softwareUpdatesGrid = new kimchi.widget.Grid({ + container: 'software-updates-grid-container', + id: softwareUpdatesGridID, + title: i18n['KCHUPD6001M'], + rowSelection: 'disabled', + toolbarButtons: [{ + id: softwareUpdatesGridID + '-update-button', + label: i18n['KCHUPD6006M'], + disabled: true, + onClick: function(event) { + var updateButton = $(this); + var progressArea = $('#' + progressAreaID)[0]; + $('#software-updates-progress-container').removeClass('hidden'); + $(progressArea).text(''); + !kimchi.isElementInViewport(progressArea) && + progressArea.scrollIntoView(); + $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true); + + kimchi.updateSoftwares(function(result) { + reloadProgressArea(result); + $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false); + kimchi.topic('kimchi/softwareUpdated').publish({ + result: result + }); + }, function() {}, reloadProgressArea); + } + }], + frozenFields: [{ + name: 'id', + label: ' ', + 'class': 'software-update-id' + }], + fields: [{ + name: 'package_name', + label: i18n['KCHUPD6002M'], + 'class': 'software-update-name' + }, { + name: 'version', + label: i18n['KCHUPD6003M'], + 'class': 'software-update-version' + }, { + name: 'arch', + label: i18n['KCHUPD6004M'], + 'class': 'software-update-arch' + }, { + name: 'repository', + label: i18n['KCHUPD6005M'], + 'class': 'software-update-repos' + }], + data: softwareUpdates + }); + }; + + var listSoftwareUpdates = function() { + kimchi.listSoftwareUpdates(function(softwareUpdates) { + $.each(softwareUpdates, function(i, item) { + softwareUpdates[i]['id'] = i + 1; + }); + + if(softwareUpdatesGrid) { + softwareUpdatesGrid.setData(softwareUpdates); + } + else { + initSoftwareUpdatesGrid(softwareUpdates); + } + + var updateButton = $('#' + softwareUpdatesGridID + '-update-button'); + $(updateButton).prop('disabled', softwareUpdates.length === 0); + }); + }; + var shutdownButtonID = '#host-button-shutdown'; var restartButtonID = '#host-button-restart'; var shutdownHost = function(params) { @@ -191,6 +273,8 @@ kimchi.host_main = function() { kimchi.keepMonitoringHost = this['checked']; }); + listSoftwareUpdates(); + kimchi.getCapabilities(function(capabilities) { if(!capabilities['system_report_tool']) { return; @@ -204,6 +288,10 @@ kimchi.host_main = function() { listDebugReports(); }); + kimchi.topic('kimchi/softwareUpdated').subscribe(function() { + listSoftwareUpdates(); + }); + kimchi.getHost(function(data) { var htmlTmpl = $('#host-tmpl').html(); data['logo'] = data['logo'] || ''; @@ -469,5 +557,6 @@ kimchi.host_main = function() { delete kimchi.hostTimer; } reportGrid && reportGrid.destroy(); + softwareUpdatesGrid && softwareUpdatesGrid.destroy(); }); }; diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 38f71d9..c96f804 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -89,6 +89,16 @@ var i18n = { 'KCHDR6010M': "$_("Download")", 'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")", + + 'KCHUPD6001M': "$_("Software Updates")", + 'KCHUPD6002M': "$_("Package Name")", + 'KCHUPD6003M': "$_("Version")", + 'KCHUPD6004M': "$_("Architecture")", + 'KCHUPD6005M': "$_("Repository")", + 'KCHUPD6006M': "$_("Update All")", + 'KCHUPD6007M': "$_("Updating...")", + + 'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", 'KCHVM6002M': "$_("Delete Confirmation")", diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl index 23b9853..f6ecd47 100644 --- a/ui/pages/tabs/host.html.tmpl +++ b/ui/pages/tabs/host.html.tmpl @@ -120,6 +120,23 @@ </div> </div> </div> + <div id="software-update-section" class="host-section"> + <h3 class="section-header" + aria-controls="content-software-update"> + $_("Software Updates") + </h3> + <div id="content-software-update" class="section-content"> + <div class="section-row"> + <div class="section-value"> + <div id="software-updates-grid-container"></div> + <div id="software-updates-progress-container" class="hidden"> + <label for="software-updates-progress-textarea">$_("Update Progress")</label> + <textarea id="software-updates-progress-textarea" readonly></textarea> + </div> + </div> + </div> + </div> + </div> <div id="debug-report-section" class="host-section hidden"> <h3 class="section-header" aria-controls="content-sys-reports"> -- 1.8.1.4

I've just noticed when the backend takes some time to check for packages update the "Software Update" appears empty, and after some time the grid is shown. I'd suggest to always display the an empty grid and update it when the request finishes. What do you think? On 03/04/2014 03:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection
v4 -> v5: 5a) Disable row selection in software update grid (Thanks to Royce's comment)
v3 -> v4: 4a) Added the last update output (Thanks to Aline's comment) 4b) Disabled horizontal resize function of output textarea (Thanks to Aline's comment) 4c) Added "Update Progress" label to the output textarea (Thanks to Aline's comment) 4d) Added refreshing the software grid after updating is finished (Thanks to Aline's comment) 4e) Added software updates grid cleanup when host tab is unloaded
v2 -> v3: 3a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo and Aline's comment) 3b) Updated REST API calling according to back-end change 3c) Added in-progress message when system is being updated (Thanks to Aline's comment)
v1 -> v2: 2a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo Ricardo Paz Vital's comment)
Signed-off-by: Hongliang Wang <hlwang@linux.vnet.ibm.com> --- ui/css/theme-default/host.css | 29 ++++++++++++++ ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++ ui/js/src/kimchi.host.js | 89 +++++++++++++++++++++++++++++++++++++++++++ ui/pages/i18n.html.tmpl | 10 +++++ ui/pages/tabs/host.html.tmpl | 17 +++++++++ 5 files changed, 198 insertions(+)
diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css index 470ed1b..0f8b941 100644 --- a/ui/css/theme-default/host.css +++ b/ui/css/theme-default/host.css @@ -224,3 +224,32 @@ width: 300px; } /* End of Debug Report */ + +/* Software Updates */ +.host-panel #software-updates-grid { + border-color: #ddd; + height: 300px; + width: 850px; +} + +.software-update-id { + width: 30px; +} + +.software-update-name, +.software-update-repos { + width: 220px; +} + +.software-update-version, +.software-update-arch { + width: 190px; +} + +.host-panel #software-updates-progress-textarea { + border: 1px solid #ddd; + height: 100px; + resize: vertical; + width: 846px; +} +/* End of Software Updates */ diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index fdd9cfc..beaf314 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -727,5 +727,58 @@ var kimchi = { success : suc, error : err }); + }, + + listSoftwareUpdates : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/packagesupdate', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend: true, + success : suc, + error : err + }); + }, + + updateSoftwares : function(suc, err, progress) { + var taskID = -1; + var onResponse = function(data) { + taskID = data['id']; + trackTask(); + }; + + var trackTask = function() { + kimchi.getTask(taskID, onTaskResponse, err); + }; + + var onTaskResponse = function(result) { + var taskStatus = result['status']; + switch(taskStatus) { + case 'running': + progress && progress(result); + setTimeout(function() { + trackTask(); + }, 200); + break; + case 'finished': + suc(result); + break; + case 'failed': + err(result); + break; + default: + break; + } + }; + + kimchi.requestJSON({ + url : kimchi.url + 'host/swupdate', + type : "POST", + contentType : "application/json", + dataType : "json", + success : onResponse, + error : err + }); } }; diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js index 7974054..e146a75 100644 --- a/ui/js/src/kimchi.host.js +++ b/ui/js/src/kimchi.host.js @@ -133,6 +133,88 @@ kimchi.host_main = function() { }); };
+ var softwareUpdatesGridID = 'software-updates-grid'; + var softwareUpdatesGrid = null; + var progressAreaID = 'software-updates-progress-textarea'; + var reloadProgressArea = function(result) { + var progressArea = $('#' + progressAreaID)[0]; + $(progressArea).text(result['message']); + var scrollTop = $(progressArea).prop('scrollHeight'); + $(progressArea).prop('scrollTop', scrollTop); + }; + + var initSoftwareUpdatesGrid = function(softwareUpdates) { + softwareUpdatesGrid = new kimchi.widget.Grid({ + container: 'software-updates-grid-container', + id: softwareUpdatesGridID, + title: i18n['KCHUPD6001M'], + rowSelection: 'disabled', + toolbarButtons: [{ + id: softwareUpdatesGridID + '-update-button', + label: i18n['KCHUPD6006M'], + disabled: true, + onClick: function(event) { + var updateButton = $(this); + var progressArea = $('#' + progressAreaID)[0]; + $('#software-updates-progress-container').removeClass('hidden'); + $(progressArea).text(''); + !kimchi.isElementInViewport(progressArea) && + progressArea.scrollIntoView(); + $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true); + + kimchi.updateSoftwares(function(result) { + reloadProgressArea(result); + $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false); + kimchi.topic('kimchi/softwareUpdated').publish({ + result: result + }); + }, function() {}, reloadProgressArea); + } + }], + frozenFields: [{ + name: 'id', + label: ' ', + 'class': 'software-update-id' + }], + fields: [{ + name: 'package_name', + label: i18n['KCHUPD6002M'], + 'class': 'software-update-name' + }, { + name: 'version', + label: i18n['KCHUPD6003M'], + 'class': 'software-update-version' + }, { + name: 'arch', + label: i18n['KCHUPD6004M'], + 'class': 'software-update-arch' + }, { + name: 'repository', + label: i18n['KCHUPD6005M'], + 'class': 'software-update-repos' + }], + data: softwareUpdates + }); + }; + + var listSoftwareUpdates = function() { + kimchi.listSoftwareUpdates(function(softwareUpdates) { + $.each(softwareUpdates, function(i, item) { + softwareUpdates[i]['id'] = i + 1; + }); + + if(softwareUpdatesGrid) { + softwareUpdatesGrid.setData(softwareUpdates); + } + else { + initSoftwareUpdatesGrid(softwareUpdates); + } + + var updateButton = $('#' + softwareUpdatesGridID + '-update-button'); + $(updateButton).prop('disabled', softwareUpdates.length === 0); + }); + }; + var shutdownButtonID = '#host-button-shutdown'; var restartButtonID = '#host-button-restart'; var shutdownHost = function(params) { @@ -191,6 +273,8 @@ kimchi.host_main = function() { kimchi.keepMonitoringHost = this['checked']; });
+ listSoftwareUpdates(); + kimchi.getCapabilities(function(capabilities) { if(!capabilities['system_report_tool']) { return; @@ -204,6 +288,10 @@ kimchi.host_main = function() { listDebugReports(); });
+ kimchi.topic('kimchi/softwareUpdated').subscribe(function() { + listSoftwareUpdates(); + }); + kimchi.getHost(function(data) { var htmlTmpl = $('#host-tmpl').html(); data['logo'] = data['logo'] || ''; @@ -469,5 +557,6 @@ kimchi.host_main = function() { delete kimchi.hostTimer; } reportGrid && reportGrid.destroy(); + softwareUpdatesGrid && softwareUpdatesGrid.destroy(); }); }; diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 38f71d9..c96f804 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -89,6 +89,16 @@ var i18n = { 'KCHDR6010M': "$_("Download")", 'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
+ + 'KCHUPD6001M': "$_("Software Updates")", + 'KCHUPD6002M': "$_("Package Name")", + 'KCHUPD6003M': "$_("Version")", + 'KCHUPD6004M': "$_("Architecture")", + 'KCHUPD6005M': "$_("Repository")", + 'KCHUPD6006M': "$_("Update All")", + 'KCHUPD6007M': "$_("Updating...")", + + 'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", 'KCHVM6002M': "$_("Delete Confirmation")",
diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl index 23b9853..f6ecd47 100644 --- a/ui/pages/tabs/host.html.tmpl +++ b/ui/pages/tabs/host.html.tmpl @@ -120,6 +120,23 @@ </div> </div> </div> + <div id="software-update-section" class="host-section"> + <h3 class="section-header" + aria-controls="content-software-update"> + $_("Software Updates") + </h3> + <div id="content-software-update" class="section-content"> + <div class="section-row"> + <div class="section-value"> + <div id="software-updates-grid-container"></div> + <div id="software-updates-progress-container" class="hidden"> + <label for="software-updates-progress-textarea">$_("Update Progress")</label> + <textarea id="software-updates-progress-textarea" readonly></textarea> + </div> + </div> + </div> + </div> + </div> <div id="debug-report-section" class="host-section hidden"> <h3 class="section-header" aria-controls="content-sys-reports">

It has essentially the same issue, though more permanent, when the backend returns an HTTP error. On 03/04/2014 12:29 PM, Aline Manera wrote:
I've just noticed when the backend takes some time to check for packages update the "Software Update" appears empty, and after some time the grid is shown. I'd suggest to always display the an empty grid and update it when the request finishes.
What do you think?
On 03/04/2014 03:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection
-- Adam King <rak@linux.vnet.ibm.com> IBM CSI

On 03/05/2014 07:11 AM, Adam King wrote:
It has essentially the same issue, though more permanent, when the backend returns an HTTP error. Yes. We still have some features to add to Grid Widget. First we need a loading icon for the grid, and then we need some place to show error message when HTTP error occurs.
On 03/04/2014 12:29 PM, Aline Manera wrote:
I've just noticed when the backend takes some time to check for packages update the "Software Update" appears empty, and after some time the grid is shown. I'd suggest to always display the an empty grid and update it when the request finishes.
What do you think?
On 03/04/2014 03:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection

On 03/05/2014 01:29 AM, Aline Manera wrote:
I've just noticed when the backend takes some time to check for packages update the "Software Update" appears empty, and after some time the grid is shown. I'd suggest to always display the an empty grid and update it when the request finishes.
What do you think?
Now grids (report, repository, software) are running in the same way: request data => use response result to initialize the grid. Though it's significant slow for software update grid because it's time-consuming in back-end executing. The common way to improve UX here is to add a "loading" icon when it's waiting for response. It's a new feature of Grid Widget. It will take about one day to complete it. I'll send patch out tomorrow.
On 03/04/2014 03:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection
v4 -> v5: 5a) Disable row selection in software update grid (Thanks to Royce's comment)
v3 -> v4: 4a) Added the last update output (Thanks to Aline's comment) 4b) Disabled horizontal resize function of output textarea (Thanks to Aline's comment) 4c) Added "Update Progress" label to the output textarea (Thanks to Aline's comment) 4d) Added refreshing the software grid after updating is finished (Thanks to Aline's comment) 4e) Added software updates grid cleanup when host tab is unloaded
v2 -> v3: 3a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo and Aline's comment) 3b) Updated REST API calling according to back-end change 3c) Added in-progress message when system is being updated (Thanks to Aline's comment)
v1 -> v2: 2a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo Ricardo Paz Vital's comment)
Signed-off-by: Hongliang Wang <hlwang@linux.vnet.ibm.com> --- ui/css/theme-default/host.css | 29 ++++++++++++++ ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++ ui/js/src/kimchi.host.js | 89 +++++++++++++++++++++++++++++++++++++++++++ ui/pages/i18n.html.tmpl | 10 +++++ ui/pages/tabs/host.html.tmpl | 17 +++++++++ 5 files changed, 198 insertions(+)
diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css index 470ed1b..0f8b941 100644 --- a/ui/css/theme-default/host.css +++ b/ui/css/theme-default/host.css @@ -224,3 +224,32 @@ width: 300px; } /* End of Debug Report */ + +/* Software Updates */ +.host-panel #software-updates-grid { + border-color: #ddd; + height: 300px; + width: 850px; +} + +.software-update-id { + width: 30px; +} + +.software-update-name, +.software-update-repos { + width: 220px; +} + +.software-update-version, +.software-update-arch { + width: 190px; +} + +.host-panel #software-updates-progress-textarea { + border: 1px solid #ddd; + height: 100px; + resize: vertical; + width: 846px; +} +/* End of Software Updates */ diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index fdd9cfc..beaf314 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -727,5 +727,58 @@ var kimchi = { success : suc, error : err }); + }, + + listSoftwareUpdates : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/packagesupdate', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend: true, + success : suc, + error : err + }); + }, + + updateSoftwares : function(suc, err, progress) { + var taskID = -1; + var onResponse = function(data) { + taskID = data['id']; + trackTask(); + }; + + var trackTask = function() { + kimchi.getTask(taskID, onTaskResponse, err); + }; + + var onTaskResponse = function(result) { + var taskStatus = result['status']; + switch(taskStatus) { + case 'running': + progress && progress(result); + setTimeout(function() { + trackTask(); + }, 200); + break; + case 'finished': + suc(result); + break; + case 'failed': + err(result); + break; + default: + break; + } + }; + + kimchi.requestJSON({ + url : kimchi.url + 'host/swupdate', + type : "POST", + contentType : "application/json", + dataType : "json", + success : onResponse, + error : err + }); } }; diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js index 7974054..e146a75 100644 --- a/ui/js/src/kimchi.host.js +++ b/ui/js/src/kimchi.host.js @@ -133,6 +133,88 @@ kimchi.host_main = function() { }); };
+ var softwareUpdatesGridID = 'software-updates-grid'; + var softwareUpdatesGrid = null; + var progressAreaID = 'software-updates-progress-textarea'; + var reloadProgressArea = function(result) { + var progressArea = $('#' + progressAreaID)[0]; + $(progressArea).text(result['message']); + var scrollTop = $(progressArea).prop('scrollHeight'); + $(progressArea).prop('scrollTop', scrollTop); + }; + + var initSoftwareUpdatesGrid = function(softwareUpdates) { + softwareUpdatesGrid = new kimchi.widget.Grid({ + container: 'software-updates-grid-container', + id: softwareUpdatesGridID, + title: i18n['KCHUPD6001M'], + rowSelection: 'disabled', + toolbarButtons: [{ + id: softwareUpdatesGridID + '-update-button', + label: i18n['KCHUPD6006M'], + disabled: true, + onClick: function(event) { + var updateButton = $(this); + var progressArea = $('#' + progressAreaID)[0]; + $('#software-updates-progress-container').removeClass('hidden'); + $(progressArea).text(''); + !kimchi.isElementInViewport(progressArea) && + progressArea.scrollIntoView(); + $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true); + + kimchi.updateSoftwares(function(result) { + reloadProgressArea(result); + $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false); + kimchi.topic('kimchi/softwareUpdated').publish({ + result: result + }); + }, function() {}, reloadProgressArea); + } + }], + frozenFields: [{ + name: 'id', + label: ' ', + 'class': 'software-update-id' + }], + fields: [{ + name: 'package_name', + label: i18n['KCHUPD6002M'], + 'class': 'software-update-name' + }, { + name: 'version', + label: i18n['KCHUPD6003M'], + 'class': 'software-update-version' + }, { + name: 'arch', + label: i18n['KCHUPD6004M'], + 'class': 'software-update-arch' + }, { + name: 'repository', + label: i18n['KCHUPD6005M'], + 'class': 'software-update-repos' + }], + data: softwareUpdates + }); + }; + + var listSoftwareUpdates = function() { + kimchi.listSoftwareUpdates(function(softwareUpdates) { + $.each(softwareUpdates, function(i, item) { + softwareUpdates[i]['id'] = i + 1; + }); + + if(softwareUpdatesGrid) { + softwareUpdatesGrid.setData(softwareUpdates); + } + else { + initSoftwareUpdatesGrid(softwareUpdates); + } + + var updateButton = $('#' + softwareUpdatesGridID + '-update-button'); + $(updateButton).prop('disabled', softwareUpdates.length === 0); + }); + }; + var shutdownButtonID = '#host-button-shutdown'; var restartButtonID = '#host-button-restart'; var shutdownHost = function(params) { @@ -191,6 +273,8 @@ kimchi.host_main = function() { kimchi.keepMonitoringHost = this['checked']; });
+ listSoftwareUpdates(); + kimchi.getCapabilities(function(capabilities) { if(!capabilities['system_report_tool']) { return; @@ -204,6 +288,10 @@ kimchi.host_main = function() { listDebugReports(); });
+ kimchi.topic('kimchi/softwareUpdated').subscribe(function() { + listSoftwareUpdates(); + }); + kimchi.getHost(function(data) { var htmlTmpl = $('#host-tmpl').html(); data['logo'] = data['logo'] || ''; @@ -469,5 +557,6 @@ kimchi.host_main = function() { delete kimchi.hostTimer; } reportGrid && reportGrid.destroy(); + softwareUpdatesGrid && softwareUpdatesGrid.destroy(); }); }; diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 38f71d9..c96f804 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -89,6 +89,16 @@ var i18n = { 'KCHDR6010M': "$_("Download")", 'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
+ + 'KCHUPD6001M': "$_("Software Updates")", + 'KCHUPD6002M': "$_("Package Name")", + 'KCHUPD6003M': "$_("Version")", + 'KCHUPD6004M': "$_("Architecture")", + 'KCHUPD6005M': "$_("Repository")", + 'KCHUPD6006M': "$_("Update All")", + 'KCHUPD6007M': "$_("Updating...")", + + 'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", 'KCHVM6002M': "$_("Delete Confirmation")",
diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl index 23b9853..f6ecd47 100644 --- a/ui/pages/tabs/host.html.tmpl +++ b/ui/pages/tabs/host.html.tmpl @@ -120,6 +120,23 @@ </div> </div> </div> + <div id="software-update-section" class="host-section"> + <h3 class="section-header" + aria-controls="content-software-update"> + $_("Software Updates") + </h3> + <div id="content-software-update" class="section-content"> + <div class="section-row"> + <div class="section-value"> + <div id="software-updates-grid-container"></div> + <div id="software-updates-progress-container" class="hidden"> + <label for="software-updates-progress-textarea">$_("Update Progress")</label> + <textarea id="software-updates-progress-textarea" readonly></textarea> + </div> + </div> + </div> + </div> + </div> <div id="debug-report-section" class="host-section hidden"> <h3 class="section-header" aria-controls="content-sys-reports">

On 03/04/2014 01:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection
v4 -> v5: 5a) Disable row selection in software update grid (Thanks to Royce's comment)
v3 -> v4: 4a) Added the last update output (Thanks to Aline's comment) 4b) Disabled horizontal resize function of output textarea (Thanks to Aline's comment) 4c) Added "Update Progress" label to the output textarea (Thanks to Aline's comment) 4d) Added refreshing the software grid after updating is finished (Thanks to Aline's comment) 4e) Added software updates grid cleanup when host tab is unloaded
v2 -> v3: 3a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo and Aline's comment) 3b) Updated REST API calling according to back-end change 3c) Added in-progress message when system is being updated (Thanks to Aline's comment)
v1 -> v2: 2a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo Ricardo Paz Vital's comment)
Signed-off-by: Hongliang Wang <hlwang@linux.vnet.ibm.com> --- ui/css/theme-default/host.css | 29 ++++++++++++++ ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++ ui/js/src/kimchi.host.js | 89 +++++++++++++++++++++++++++++++++++++++++++ ui/pages/i18n.html.tmpl | 10 +++++ ui/pages/tabs/host.html.tmpl | 17 +++++++++ 5 files changed, 198 insertions(+)
diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css index 470ed1b..0f8b941 100644 --- a/ui/css/theme-default/host.css +++ b/ui/css/theme-default/host.css @@ -224,3 +224,32 @@ width: 300px; } /* End of Debug Report */ + +/* Software Updates */ +.host-panel #software-updates-grid { + border-color: #ddd; + height: 300px; + width: 850px; +} + +.software-update-id { + width: 30px; +} + +.software-update-name, +.software-update-repos { + width: 220px; +} + +.software-update-version, +.software-update-arch { + width: 190px; +} + +.host-panel #software-updates-progress-textarea { + border: 1px solid #ddd; + height: 100px; + resize: vertical; + width: 846px; +} +/* End of Software Updates */
Are these styles any different that the styles we use on other grids? Can we use a common set of styles so that the different grids maintain consistent styles?
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index fdd9cfc..beaf314 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -727,5 +727,58 @@ var kimchi = { success : suc, error : err }); + }, + + listSoftwareUpdates : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/packagesupdate', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend: true, + success : suc, + error : err + }); + }, + + updateSoftwares : function(suc, err, progress) {
Should be updateSoftware Its like sheep. The plural of software is software.
+ var taskID = -1; + var onResponse = function(data) { + taskID = data['id']; + trackTask(); + }; + + var trackTask = function() { + kimchi.getTask(taskID, onTaskResponse, err); + }; + + var onTaskResponse = function(result) { + var taskStatus = result['status']; + switch(taskStatus) { + case 'running': + progress && progress(result); + setTimeout(function() { + trackTask(); + }, 200); + break; + case 'finished': + suc(result); + break; + case 'failed': + err(result); + break; + default: + break; + } + }; + + kimchi.requestJSON({ + url : kimchi.url + 'host/swupdate', + type : "POST", + contentType : "application/json", + dataType : "json", + success : onResponse, + error : err + }); } }; diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js index 7974054..e146a75 100644 --- a/ui/js/src/kimchi.host.js +++ b/ui/js/src/kimchi.host.js @@ -133,6 +133,88 @@ kimchi.host_main = function() { }); };
+ var softwareUpdatesGridID = 'software-updates-grid'; I'd just burn this in. The indirection isn't really saving anything, but will make it harder to search for some of the computed IDs + var softwareUpdatesGrid = null; + var progressAreaID = 'software-updates-progress-textarea'; + var reloadProgressArea = function(result) { + var progressArea = $('#' + progressAreaID)[0]; + $(progressArea).text(result['message']); + var scrollTop = $(progressArea).prop('scrollHeight'); + $(progressArea).prop('scrollTop', scrollTop); + }; + + var initSoftwareUpdatesGrid = function(softwareUpdates) { + softwareUpdatesGrid = new kimchi.widget.Grid({ + container: 'software-updates-grid-container', + id: softwareUpdatesGridID, + title: i18n['KCHUPD6001M'], + rowSelection: 'disabled', + toolbarButtons: [{ + id: softwareUpdatesGridID + '-update-button', + label: i18n['KCHUPD6006M'], + disabled: true, + onClick: function(event) { + var updateButton = $(this); + var progressArea = $('#' + progressAreaID)[0]; + $('#software-updates-progress-container').removeClass('hidden'); + $(progressArea).text(''); + !kimchi.isElementInViewport(progressArea) && + progressArea.scrollIntoView(); + $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true); + + kimchi.updateSoftwares(function(result) { + reloadProgressArea(result); + $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false); + kimchi.topic('kimchi/softwareUpdated').publish({ + result: result + }); + }, function() {}, reloadProgressArea); Do we want this function to be empty? + } + }], + frozenFields: [{ + name: 'id', + label: ' ', + 'class': 'software-update-id' + }], + fields: [{ + name: 'package_name', + label: i18n['KCHUPD6002M'], + 'class': 'software-update-name' + }, { + name: 'version', + label: i18n['KCHUPD6003M'], + 'class': 'software-update-version' + }, { + name: 'arch', + label: i18n['KCHUPD6004M'], + 'class': 'software-update-arch' + }, { + name: 'repository', + label: i18n['KCHUPD6005M'], + 'class': 'software-update-repos' + }], + data: softwareUpdates I think softwareupdates is undefined at this point. Better not to set data, or set it to some explicit value + }); + }; + + var listSoftwareUpdates = function() { + kimchi.listSoftwareUpdates(function(softwareUpdates) { + $.each(softwareUpdates, function(i, item) { + softwareUpdates[i]['id'] = i + 1; + }); + + if(softwareUpdatesGrid) { + softwareUpdatesGrid.setData(softwareUpdates); + } + else { + initSoftwareUpdatesGrid(softwareUpdates); + } + + var updateButton = $('#' + softwareUpdatesGridID + '-update-button'); + $(updateButton).prop('disabled', softwareUpdates.length === 0); + }); + }; + var shutdownButtonID = '#host-button-shutdown'; var restartButtonID = '#host-button-restart'; var shutdownHost = function(params) { @@ -191,6 +273,8 @@ kimchi.host_main = function() { kimchi.keepMonitoringHost = this['checked']; });
+ listSoftwareUpdates(); + kimchi.getCapabilities(function(capabilities) { if(!capabilities['system_report_tool']) { return; @@ -204,6 +288,10 @@ kimchi.host_main = function() { listDebugReports(); });
+ kimchi.topic('kimchi/softwareUpdated').subscribe(function() { + listSoftwareUpdates(); + }); + kimchi.getHost(function(data) { var htmlTmpl = $('#host-tmpl').html(); data['logo'] = data['logo'] || ''; @@ -469,5 +557,6 @@ kimchi.host_main = function() { delete kimchi.hostTimer; } reportGrid && reportGrid.destroy(); + softwareUpdatesGrid && softwareUpdatesGrid.destroy(); }); }; diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 38f71d9..c96f804 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -89,6 +89,16 @@ var i18n = { 'KCHDR6010M': "$_("Download")", 'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
+ + 'KCHUPD6001M': "$_("Software Updates")", + 'KCHUPD6002M': "$_("Package Name")", + 'KCHUPD6003M': "$_("Version")", + 'KCHUPD6004M': "$_("Architecture")", + 'KCHUPD6005M': "$_("Repository")", + 'KCHUPD6006M': "$_("Update All")", + 'KCHUPD6007M': "$_("Updating...")", + + 'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", 'KCHVM6002M': "$_("Delete Confirmation")",
diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl index 23b9853..f6ecd47 100644 --- a/ui/pages/tabs/host.html.tmpl +++ b/ui/pages/tabs/host.html.tmpl @@ -120,6 +120,23 @@ </div> </div> </div> + <div id="software-update-section" class="host-section"> + <h3 class="section-header" + aria-controls="content-software-update"> + $_("Software Updates") + </h3> + <div id="content-software-update" class="section-content"> + <div class="section-row"> + <div class="section-value"> + <div id="software-updates-grid-container"></div> + <div id="software-updates-progress-container" class="hidden"> + <label for="software-updates-progress-textarea">$_("Update Progress")</label> + <textarea id="software-updates-progress-textarea" readonly></textarea> + </div> + </div> + </div> + </div> + </div> <div id="debug-report-section" class="host-section hidden"> <h3 class="section-header" aria-controls="content-sys-reports">
-- Adam King <rak@linux.vnet.ibm.com> IBM CSI

On 03/04/2014 01:18 AM, Hongliang Wang wrote:
Added UI support for software updating. The Host Tab will initially list available updates to user if there are any; or we will disable "Update All" Button if no updates available.
Please apply following patch first: * [PATCH] [UI] Grid Widget - Enable/Disable Row Selection
v4 -> v5: 5a) Disable row selection in software update grid (Thanks to Royce's comment)
v3 -> v4: 4a) Added the last update output (Thanks to Aline's comment) 4b) Disabled horizontal resize function of output textarea (Thanks to Aline's comment) 4c) Added "Update Progress" label to the output textarea (Thanks to Aline's comment) 4d) Added refreshing the software grid after updating is finished (Thanks to Aline's comment) 4e) Added software updates grid cleanup when host tab is unloaded
v2 -> v3: 3a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo and Aline's comment) 3b) Updated REST API calling according to back-end change 3c) Added in-progress message when system is being updated (Thanks to Aline's comment)
v1 -> v2: 2a) Fixed "Update All" Button always being disabled issue (Thanks to Paulo Ricardo Paz Vital's comment)
Signed-off-by: Hongliang Wang <hlwang@linux.vnet.ibm.com> --- ui/css/theme-default/host.css | 29 ++++++++++++++ ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++ ui/js/src/kimchi.host.js | 89 +++++++++++++++++++++++++++++++++++++++++++ ui/pages/i18n.html.tmpl | 10 +++++ ui/pages/tabs/host.html.tmpl | 17 +++++++++ 5 files changed, 198 insertions(+)
diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css index 470ed1b..0f8b941 100644 --- a/ui/css/theme-default/host.css +++ b/ui/css/theme-default/host.css @@ -224,3 +224,32 @@ width: 300px; } /* End of Debug Report */ + +/* Software Updates */ +.host-panel #software-updates-grid { + border-color: #ddd; + height: 300px; + width: 850px; +} + +.software-update-id { + width: 30px; +} + +.software-update-name, +.software-update-repos { + width: 220px; +} + +.software-update-version, +.software-update-arch { + width: 190px; +} + +.host-panel #software-updates-progress-textarea { + border: 1px solid #ddd; + height: 100px; + resize: vertical; + width: 846px; +} +/* End of Software Updates */
Are these styles any different that the styles we use on other grids? Can we use a common set of styles so that the different grids maintain consistent styles? I believe there are redundant style rules besides rules for grids. We need a cleanup to do that. Maybe an item in next release? For this
On 03/05/2014 07:11 AM, Adam King wrote: patch, I'll check the rules and find reusable rules.
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index fdd9cfc..beaf314 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -727,5 +727,58 @@ var kimchi = { success : suc, error : err }); + }, + + listSoftwareUpdates : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/packagesupdate', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend: true, + success : suc, + error : err + }); + }, + + updateSoftwares : function(suc, err, progress) {
Should be updateSoftware Its like sheep. The plural of software is software.
ACK. I was using it as short for software packages :-)
+ var taskID = -1; + var onResponse = function(data) { + taskID = data['id']; + trackTask(); + }; + + var trackTask = function() { + kimchi.getTask(taskID, onTaskResponse, err); + }; + + var onTaskResponse = function(result) { + var taskStatus = result['status']; + switch(taskStatus) { + case 'running': + progress && progress(result); + setTimeout(function() { + trackTask(); + }, 200); + break; + case 'finished': + suc(result); + break; + case 'failed': + err(result); + break; + default: + break; + } + }; + + kimchi.requestJSON({ + url : kimchi.url + 'host/swupdate', + type : "POST", + contentType : "application/json", + dataType : "json", + success : onResponse, + error : err + }); } }; diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js index 7974054..e146a75 100644 --- a/ui/js/src/kimchi.host.js +++ b/ui/js/src/kimchi.host.js @@ -133,6 +133,88 @@ kimchi.host_main = function() { }); };
+ var softwareUpdatesGridID = 'software-updates-grid'; I'd just burn this in. The indirection isn't really saving anything, but will make it harder to search for some of the computed IDs OK. I'll check it. If it can be reused more than 3 times, I'll keep it; or I'll burn it in. It's usually used when there are several references to avoid hand typing errors. + var softwareUpdatesGrid = null; + var progressAreaID = 'software-updates-progress-textarea'; + var reloadProgressArea = function(result) { + var progressArea = $('#' + progressAreaID)[0]; + $(progressArea).text(result['message']); + var scrollTop = $(progressArea).prop('scrollHeight'); + $(progressArea).prop('scrollTop', scrollTop); + }; + + var initSoftwareUpdatesGrid = function(softwareUpdates) { + softwareUpdatesGrid = new kimchi.widget.Grid({ + container: 'software-updates-grid-container', + id: softwareUpdatesGridID, + title: i18n['KCHUPD6001M'], + rowSelection: 'disabled', + toolbarButtons: [{ + id: softwareUpdatesGridID + '-update-button', + label: i18n['KCHUPD6006M'], + disabled: true, + onClick: function(event) { + var updateButton = $(this); + var progressArea = $('#' + progressAreaID)[0]; + $('#software-updates-progress-container').removeClass('hidden'); + $(progressArea).text(''); + !kimchi.isElementInViewport(progressArea) && + progressArea.scrollIntoView(); + $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true); + + kimchi.updateSoftwares(function(result) { + reloadProgressArea(result); + $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false); + kimchi.topic('kimchi/softwareUpdated').publish({ + result: result + }); + }, function() {}, reloadProgressArea); Do we want this function to be empty? I'll fill it. Thanks! + } + }], + frozenFields: [{ + name: 'id', + label: ' ', + 'class': 'software-update-id' + }], + fields: [{ + name: 'package_name', + label: i18n['KCHUPD6002M'], + 'class': 'software-update-name' + }, { + name: 'version', + label: i18n['KCHUPD6003M'], + 'class': 'software-update-version' + }, { + name: 'arch', + label: i18n['KCHUPD6004M'], + 'class': 'software-update-arch' + }, { + name: 'repository', + label: i18n['KCHUPD6005M'], + 'class': 'software-update-repos' + }], + data: softwareUpdates I think softwareupdates is undefined at this point. Better not to set data, or set it to some explicit value It has data at this point. The flow is: request software packages ==> data received, and initialize the grid with the data
$.ajax('/host/packages', function(data) { new kimchi.Grid({ data: data }); }); To improve UX, I'll change the flow to: initialize the grid without any data and show "loading" icon ==> meanwhile, request data ==> data received, feed grid
+ }); + }; + + var listSoftwareUpdates = function() { + kimchi.listSoftwareUpdates(function(softwareUpdates) { + $.each(softwareUpdates, function(i, item) { + softwareUpdates[i]['id'] = i + 1; + }); + + if(softwareUpdatesGrid) { + softwareUpdatesGrid.setData(softwareUpdates); + } + else { + initSoftwareUpdatesGrid(softwareUpdates); + } + + var updateButton = $('#' + softwareUpdatesGridID + '-update-button'); + $(updateButton).prop('disabled', softwareUpdates.length === 0); + }); + }; + var shutdownButtonID = '#host-button-shutdown'; var restartButtonID = '#host-button-restart'; var shutdownHost = function(params) { @@ -191,6 +273,8 @@ kimchi.host_main = function() { kimchi.keepMonitoringHost = this['checked']; });
+ listSoftwareUpdates(); + kimchi.getCapabilities(function(capabilities) { if(!capabilities['system_report_tool']) { return; @@ -204,6 +288,10 @@ kimchi.host_main = function() { listDebugReports(); });
+ kimchi.topic('kimchi/softwareUpdated').subscribe(function() { + listSoftwareUpdates(); + }); + kimchi.getHost(function(data) { var htmlTmpl = $('#host-tmpl').html(); data['logo'] = data['logo'] || ''; @@ -469,5 +557,6 @@ kimchi.host_main = function() { delete kimchi.hostTimer; } reportGrid && reportGrid.destroy(); + softwareUpdatesGrid && softwareUpdatesGrid.destroy(); }); }; diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl index 38f71d9..c96f804 100644 --- a/ui/pages/i18n.html.tmpl +++ b/ui/pages/i18n.html.tmpl @@ -89,6 +89,16 @@ var i18n = { 'KCHDR6010M': "$_("Download")", 'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
+ + 'KCHUPD6001M': "$_("Software Updates")", + 'KCHUPD6002M': "$_("Package Name")", + 'KCHUPD6003M': "$_("Version")", + 'KCHUPD6004M': "$_("Architecture")", + 'KCHUPD6005M': "$_("Repository")", + 'KCHUPD6006M': "$_("Update All")", + 'KCHUPD6007M': "$_("Updating...")", + + 'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", 'KCHVM6002M': "$_("Delete Confirmation")",
diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl index 23b9853..f6ecd47 100644 --- a/ui/pages/tabs/host.html.tmpl +++ b/ui/pages/tabs/host.html.tmpl @@ -120,6 +120,23 @@ </div> </div> </div> + <div id="software-update-section" class="host-section"> + <h3 class="section-header" + aria-controls="content-software-update"> + $_("Software Updates") + </h3> + <div id="content-software-update" class="section-content"> + <div class="section-row"> + <div class="section-value"> + <div id="software-updates-grid-container"></div> + <div id="software-updates-progress-container" class="hidden"> + <label for="software-updates-progress-textarea">$_("Update Progress")</label> + <textarea id="software-updates-progress-textarea" readonly></textarea> + </div> + </div> + </div> + </div> + </div> <div id="debug-report-section" class="host-section hidden"> <h3 class="section-header" aria-controls="content-sys-reports">
participants (3)
-
Adam King
-
Aline Manera
-
Hongliang Wang