From: Samuel GuimarĂ£es <sguimaraes943@gmail.com> Added a warning icon when a template has invalid parameters in Templates list and when editing a template. Signed-off-by: peterpennings <peterpnns@gmail.com> Signed-off-by: Samuel GuimarĂ£es <sguimaraes943@gmail.com> --- ui/css/kimchi.css | 97 ++++++++++++++++++++++++++++++++-- ui/css/src/modules/_guests.scss | 6 +++ ui/css/src/modules/_iso-list.scss | 22 +++++++- ui/css/src/modules/_templates.scss | 53 +++++++++++++++++-- ui/js/src/kimchi.guest_add_main.js | 7 +++ ui/js/src/kimchi.template_edit_main.js | 52 ++++++++++++++---- ui/js/src/kimchi.template_main.js | 9 +++- ui/pages/guest-add.html.tmpl | 11 ++-- ui/pages/i18n.json.tmpl | 1 + ui/pages/tabs/templates.html.tmpl | 24 +++++---- ui/pages/template-edit.html.tmpl | 6 ++- 11 files changed, 255 insertions(+), 33 deletions(-) diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css index 1d33d45..e7bed2e 100644 --- a/ui/css/kimchi.css +++ b/ui/css/kimchi.css @@ -121,6 +121,38 @@ transition: all .1s ease-in-out; } +#template-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-inner, +#guest-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-inner { + font-size: 10pt; + padding: 6px 8px; +} + +#template-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-inner, +#guest-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-inner { + max-width: 300px; +} + +#template-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip[style], +#guest-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip[style] { + left: 7px !important; + right: 7px !important; +} + +#template-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-arrow[style], +#guest-add-window.modal-content label.box-iso-outer span.box-iso-inner .tooltip-arrow[style] { + left: 216px !important; +} + +#template-add-window.modal-content li[data-invalid="invalid"] label.box-iso-outer, +#guest-add-window.modal-content li[data-invalid="invalid"] label.box-iso-outer { + color: #999 !important; +} + +#template-add-window.modal-content li[data-invalid="invalid"] label.box-iso-outer span.box-iso-inner, +#guest-add-window.modal-content li[data-invalid="invalid"] label.box-iso-outer span.box-iso-inner { + background: rgba(255, 255, 255, 0.4); +} + #template-add-window.modal-content ul.list-template, #template-add-window.modal-content ul.list-iso, #guest-add-window.modal-content ul.list-template, @@ -255,6 +287,13 @@ padding: 10px 30px; } +.guests-modal .template-status { + position: absolute; + right: 15px; + bottom: 15px; + font-size: 32px; +} + #guest-add-window .modal-body { margin: 0; padding: 0; @@ -1807,16 +1846,20 @@ body.wok-gallery { } #templates-root-container .wok-vm-list .column-version { - width: 14.2435%; + width: 12.2435%; font-weight: bold; } #templates-root-container .wok-vm-list .column-processors { - width: 12.8413%; + width: 10.8413%; } #templates-root-container .wok-vm-list .column-memory { - width: 7.3800%; + width: 9.6900%; +} + +#templates-root-container .wok-vm-list .column-status { + width: 1.6900%; } #templates-root-container .wok-vm-list .column-action { @@ -1875,6 +1918,7 @@ body.wok-gallery { } #templates-root-container .wok-vm-gallery .wok-vm-body { + position: relative; padding: 0 20px 0 20px; width: 240px; display: inline-block; @@ -1911,6 +1955,19 @@ body.wok-gallery { cursor: default; } +#templates-root-container .wok-vm-gallery span.template-status { + position: absolute; + bottom: 10px; + right: 10px; + width: 32px; + height: 32px; +} + +#templates-root-container .wok-vm-gallery .invalid-icon { + position: absolute; + font-size: 32px; +} + #templates-root-container .wok-vm-gallery .item-hidden.column-type, #templates-root-container .wok-vm-gallery .item-hidden.column-version, #templates-root-container .wok-vm-gallery .item-hidden.column-processors { padding-bottom: 11px; } @@ -1979,6 +2036,40 @@ body.wok-gallery { background-image: url("/images/theme-default/icon-unknown.png"); } +#templates-root-container span.template-status { + color: #999; +} + +#templates-root-container .tooltip-inner { + font-size: 10pt; + padding: 6px 8px; +} + +#templates-root-container .wok-vm-list .tooltip-inner { + max-width: 300px; +} + +#templates-root-container .wok-vm-list .tooltip[style] { + left: 79px !important; +} + +#templates-root-container .wok-vm-list .tooltip-arrow[style] { + left: 10px !important; +} + +#templates-root-container .wok-vm-gallery .tooltip-inner { + max-width: 320px; +} + +#templates-root-container .wok-vm-gallery .tooltip[style] { + left: 7px !important; + right: 7px !important; +} + +#templates-root-container .wok-vm-gallery .tooltip-arrow[style] { + left: 207px !important; +} + .network-config input.invalid-field[type="text"] { border-color: #FF4444; } diff --git a/ui/css/src/modules/_guests.scss b/ui/css/src/modules/_guests.scss index 6f5dbf0..6c24800 100644 --- a/ui/css/src/modules/_guests.scss +++ b/ui/css/src/modules/_guests.scss @@ -38,6 +38,12 @@ overflow: auto; padding: 10px 30px; } + .template-status { + position: absolute; + right: 15px; + bottom: 15px; + font-size: 32px; + } } #guest-add-window { diff --git a/ui/css/src/modules/_iso-list.scss b/ui/css/src/modules/_iso-list.scss index 222dcdb..355f590 100644 --- a/ui/css/src/modules/_iso-list.scss +++ b/ui/css/src/modules/_iso-list.scss @@ -46,9 +46,29 @@ label.box-iso-outer span.box-iso-inner { display: block; border: 1px solid transparent; - background: $gray-lighter; + background: $body-bg; transition: all .1s ease-in-out; } + label.box-iso-outer span.box-iso-inner .tooltip-inner { + font-size: 10pt; + padding: 6px 8px; + } + label.box-iso-outer span.box-iso-inner .tooltip-inner { + max-width: 300px; + } + label.box-iso-outer span.box-iso-inner .tooltip[style] { + left: 7px !important; + right: 7px !important; + } + label.box-iso-outer span.box-iso-inner .tooltip-arrow[style] { + left: 216px !important; + } + li[data-invalid="invalid"] label.box-iso-outer { + color: $gray-light !important; + } + li[data-invalid="invalid"] label.box-iso-outer span.box-iso-inner { + background: rgba($body-bg, 0.4); + } ul.list-template, ul.list-iso { display: block; diff --git a/ui/css/src/modules/_templates.scss b/ui/css/src/modules/_templates.scss index a9f0e8f..ad76d0f 100644 --- a/ui/css/src/modules/_templates.scss +++ b/ui/css/src/modules/_templates.scss @@ -370,14 +370,17 @@ font-weight: bold; } .column-version { - width: 14.2435%; + width: 12.2435%; font-weight: bold; } .column-processors { - width: 12.8413%; + width: 10.8413%; } .column-memory { - width: 7.3800%; + width: 9.6900%; + } + .column-status { + width: 1.6900%; } .column-action { width: 33.8000%; @@ -423,6 +426,7 @@ display: none; } .wok-vm-body { + position: relative;; padding: 0 20px 0 20px; width: 240px; display: inline-block; @@ -455,6 +459,17 @@ white-space: nowrap; cursor: default; } + span.template-status { + position: absolute; + bottom: 10px; + right: 10px; + width: 32px; + height: 32px; + } + .invalid-icon { + position: absolute; + font-size: 32px; + } .item-hidden { &.column-type, &.column-version, @@ -515,4 +530,36 @@ } } } + span.template-status { + color: #999; + } + .tooltip-inner { + font-size: 10pt; + padding: 6px 8px; + } + + .wok-vm-list .tooltip-inner { + max-width: 300px; + } + + .wok-vm-list .tooltip[style] { + left: 79px !important; + } + + .wok-vm-list .tooltip-arrow[style] { + left: 10px !important; + } + + .wok-vm-gallery .tooltip-inner { + max-width: 320px; + } + + .wok-vm-gallery .tooltip[style] { + left: 7px !important; + right: 7px !important; + } + + .wok-vm-gallery .tooltip-arrow[style] { + left: 207px !important; + } } diff --git a/ui/js/src/kimchi.guest_add_main.js b/ui/js/src/kimchi.guest_add_main.js index 87ad6fa..2b311fc 100644 --- a/ui/js/src/kimchi.guest_add_main.js +++ b/ui/js/src/kimchi.guest_add_main.js @@ -29,9 +29,16 @@ kimchi.guest_add_main = function() { var html = ''; var tmpl = $('#tmpl-template').html(); $.each(result, function(index, value) { + value.invalid_indicator = "invalid"; + if ($.isEmptyObject(value.invalid)) { + value.invalid_indicator = "valid"; + } html += wok.substitute(tmpl, value); }); $('#templateTile').html(html); + $('.iso-radio-hidden[data-invalid="invalid"]').attr("disabled", true); + $('.template-status[data-invalid="valid"]').hide(); + $('[data-toggle="tooltip"]').tooltip(); return; } diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js index bc3c2ef..f639e0a 100644 --- a/ui/js/src/kimchi.template_edit_main.js +++ b/ui/js/src/kimchi.template_edit_main.js @@ -133,9 +133,14 @@ kimchi.template_edit_main = function() { storageOptions += '<option value="' + poolName + '">' + poolName + '</option>'; }); - $(storageRow + ' #selectStorageName').append(storageOptions); - $(storageRow + ' #selectStorageName').val(storageData.storageName); - $(storageRow + ' #selectStorageName').selectpicker(); + $(storageRow + ' .selectStorageName').append(storageOptions); + if(!$(storageRow + ' .selectStorageName option[value="'+storageData.storageName+'"]').length){ + var invalidOption = '<option disabled="disabled" selected="selected" value="' + storageData.storageName + '">' + storageData.storageName + '</option>'; + $(storageRow + ' .selectStorageName').prepend(invalidOption); + $(storageRow + ' .selectStorageName').parent().addClass('has-error') + } + $(storageRow + ' .selectStorageName').val(storageData.storageName); + $(storageRow + ' .selectStorageName').selectpicker(); if (storageData.storageType === 'iscsi' || storageData.storageType === 'scsi') { $(storageRow + ' .template-storage-disk').attr('readonly', true).prop('disabled', true); @@ -164,7 +169,8 @@ kimchi.template_edit_main = function() { $(this).parent().parent().remove(); }); - $(storageRow + ' #selectStorageName').change(function() { + $(storageRow + ' select.selectStorageName').change(function() { + $(this).parent().parent().removeClass('has-error'); var poolType = storagePoolsInfo[$(this).val()].type; $(storageRow + ' .template-storage-name').val($(this).val()); $(storageRow + ' .template-storage-type').val(poolType); @@ -319,9 +325,24 @@ kimchi.template_edit_main = function() { $('#guest-show-max-processor i.fa').toggleClass('fa-plus-circle fa-minus-circle'); }); }; + + var checkInvalids = function(){ + $.each(template.invalid, function(key, value) { + if(key === 'cdrom' || key === 'vm-image'){ + $('.tab-content input[name="'+key+'"]').attr('disabled',false).parent().addClass('has-error has-feedback'); + return true; + }else if(key === 'storagepools'){ + return true; + }else { + return false; + } + }); + } + kimchi.listNetworks(initInterface); kimchi.listStoragePools(initStorage); initProcessor(); + checkInvalids(); }; kimchi.retrieveTemplate(kimchi.selectedTemplate, initTemplate); @@ -415,16 +436,27 @@ kimchi.template_edit_main = function() { data.networks = []; } - kimchi.updateTemplate($('#template-name').val(), data, function() { - kimchi.doListTemplates(); - wok.window.close(); - }, function(err) { + if($('.has-error', '#form-template-storage').length){ + // Workaround to check if invalid storage wasn't changed + $('a[href="#storage"]','#edit-template-tabs').tab('show'); $button.html(i18n['KCHAPI6007M']); $('.modal input[type="text"]').prop('disabled', false); $('.modal input[type="checkbox"]').prop('disabled', false); $('.modal select').prop('disabled', false); $('.modal .selectpicker').removeClass('disabled'); - wok.message.error(err.responseJSON.reason,'#alert-modal-container'); - }); + wok.message.error(i18n['KCHTMPL6007M'],'#alert-modal-container'); + }else { + kimchi.updateTemplate($('#template-name').val(), data, function() { + kimchi.doListTemplates(); + wok.window.close(); + }, function(err) { + $button.html(i18n['KCHAPI6007M']); + $('.modal input[type="text"]').prop('disabled', false); + $('.modal input[type="checkbox"]').prop('disabled', false); + $('.modal select').prop('disabled', false); + $('.modal .selectpicker').removeClass('disabled'); + wok.message.error(err.responseJSON.reason,'#alert-modal-container'); + }); + } }); }; diff --git a/ui/js/src/kimchi.template_main.js b/ui/js/src/kimchi.template_main.js index 5787d77..22a3509 100644 --- a/ui/js/src/kimchi.template_main.js +++ b/ui/js/src/kimchi.template_main.js @@ -7,7 +7,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0invalid_indicator_template * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -24,6 +24,10 @@ kimchi.doListTemplates = function() { var listHtml = ''; var templateHtml = $('#templateTmpl').html(); $.each(result, function(index, value) { + value.invalid_indicator = "invalid"; + if ($.isEmptyObject(value.invalid)) { + value.invalid_indicator = "valid"; + } listHtml += wok.substitute(templateHtml, value); }); $('.wok-vm-list').removeClass('hidden'); @@ -31,6 +35,7 @@ kimchi.doListTemplates = function() { $('#templateList').html(listHtml); kimchi.templateBindClick(); $('.wok-mask').fadeOut(300, function() {}); + $('.template-status[data-invalid="valid"]').hide(); } else { $('#templateList').html(''); $('#noTemplates').show(); @@ -43,7 +48,7 @@ kimchi.doListTemplates = function() { valueNames: ['name-filter', 'os-type-filter', 'os-version-filter', 'cpus-filter', 'memory-filter'] }; var templatesList = new List('templates-container', options); - + $('[data-invalid="invalid"][data-toggle="tooltip"]').tooltip(); }, function(err) { wok.message.error(err.responseJSON.reason); $('.wok-mask').fadeOut(300, function() { diff --git a/ui/pages/guest-add.html.tmpl b/ui/pages/guest-add.html.tmpl index 33472ce..8289a7f 100644 --- a/ui/pages/guest-add.html.tmpl +++ b/ui/pages/guest-add.html.tmpl @@ -51,12 +51,17 @@ <ul id="templateTile" class="list-template tile-check tile-template"> </ul> <script type="html/text" id="tmpl-template" class="tmpl-html"> - <li class="col-md-3"> + <li class="col-md-3 template-opacity" data-invalid="{invalid_indicator}"> <label class="box-iso-outer"> - <input type="radio" name="template" value="/plugins/kimchi/templates/{name}" class="iso-radio-hidden"> + <input type="radio" name="template" value="/plugins/kimchi/templates/{name}" class="iso-radio-hidden" data-invalid="{invalid_indicator}"> <span class="box-iso-border"> <span class="box-iso-inner"> - <h3 class="iso-title {os_distro}" title="{name}">{name}</h3> + + <h3 class="iso-title {os_distro}" title="{name}"> + <span data-invalid="{invalid_indicator}" data-placement="top" class="template-status" data-toggle="tooltip" title="$_("This template has invalid parameters.")"> + <i role="presentation" class="fa fa-exclamation-triangle invalid-icon"></i> + <span class="sr-only">$_("This template has invalid parameters")</span> + </span>{name}</h3> <dl class="iso-info"> <dt>{os_distro}</dt> <dd>$_("OS")</dd> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl index 8da568a..95c5600 100644 --- a/ui/pages/i18n.json.tmpl +++ b/ui/pages/i18n.json.tmpl @@ -51,6 +51,7 @@ "KCHTMPL6004M": "$_("View Table")", "KCHTMPL6005M": "$_("View Gallery")", "KCHTMPL6006M": "$_("Not Available")", + "KCHTMPL6007M": "$_("Please check the invalid Storage Pools")", "KCHVM6001M": "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")", "KCHVM6002M": "$_("Power off Confirmation")", diff --git a/ui/pages/tabs/templates.html.tmpl b/ui/pages/tabs/templates.html.tmpl index 50e2b22..7616796 100644 --- a/ui/pages/tabs/templates.html.tmpl +++ b/ui/pages/tabs/templates.html.tmpl @@ -60,11 +60,11 @@ <button type="button" class="btn dropdown-toggle form-control selectpicker btn-default sort-button" data-toggle="dropdown" title="Status" aria-expanded="false"><span class="filter-option pull-left">$_("Status")</span> <span class="caret"></span></button> <div class="dropdown-menu open" style="max-height: 668px; overflow: hidden; min-height: 0px;padding: 0px;"> <ul class="dropdown-menu inner selectpicker" role="menu" style="max-height: 656px; overflow-y: auto; min-height: 0px;"> - <li role="presentation" nwAct="sort"><a href="#" class="sort" data-sort="name-filter">$_("Name")</a></li> - <li role="presentation" nwAct="clone"><a href="#" class="sort" data-sort="os-type-filter">$_("OS")</a></li> - <li role="presentation" nwAct="clone"><a href="#" class="sort" data-sort="os-version-filter">$_("Version")</a></li> - <li role="presentation" nwAct="clone"><a href="#" class="sort" data-sort="cpus-filter">$_("Current CPUs")</a></li> - <li role="presentation" nwAct="clone"><a href="#" class="sort" data-sort="memory-filter">$_("Memory")</a></li> + <li nwAct="sort"><a href="#" class="sort" data-sort="name-filter">$_("Name")</a></li> + <li nwAct="clone"><a href="#" class="sort" data-sort="os-type-filter">$_("OS")</a></li> + <li nwAct="clone"><a href="#" class="sort" data-sort="os-version-filter">$_("Version")</a></li> + <li nwAct="clone"><a href="#" class="sort" data-sort="cpus-filter">$_("Current CPUs")</a></li> + <li nwAct="clone"><a href="#" class="sort" data-sort="memory-filter">$_("Memory")</a></li> </ul> </div> </div> @@ -108,17 +108,21 @@ kimchi.template_main(); </script> <script id="templateTmpl" type="html/text"> - <li class="wok-vm-body"> - <span class='column-name name-filter name-distro-icon icon-{os_distro}' title="{name}" val="{name}">{name}</span><!-- + <li class="wok-vm-body" data-invalid="{invalid_indicator}"> + <span class='column-name name-filter name-distro-icon icon-{os_distro}' title="{name}" val="{name}"> + <span class="template-status" data-invalid="{invalid_indicator}" data-toggle="tooltip" title="$_("This template has invalid parameters.")"> + <i role="presentation" class="fa fa-exclamation-triangle invalid-icon"></i> + <span class="sr-only">$_("This template has invalid parameters")</span> + </span>{name}</span><!-- --><span class='column-action pull-right'> <span class="pull-right"> <div class="dropdown menu-flat"> <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false"><span class="edit-alt"></span>$_("Actions")<span class="caret"></span> </button> <ul class="dropdown-menu" role="menu"> - <li role="presentation" nwAct="edit" class='template-edit'><a href="#" data-template='{name}'><i class="fa fa-pencil"></i>$_("Edit")</a></li> - <li role="presentation" nwAct="clone" class='template-clone'><a href="#" data-template='{name}'><i class="fa fa-files-o"></i>$_("Clone")</a></li> - <li role="presentation" nwAct="delete" class='critical template-delete'><a href="#" data-template='{name}'><i class="fa fa-minus-circle"></i>$_("Delete")</a></li> + <li nwAct="edit" class='template-edit'><a href="#" data-template='{name}'><i class="fa fa-pencil"></i>$_("Edit")</a></li> + <li nwAct="clone" class='template-clone'><a href="#" data-template='{name}'><i class="fa fa-files-o"></i>$_("Clone")</a></li> + <li nwAct="delete" class='critical template-delete'><a href="#" data-template='{name}'><i class="fa fa-minus-circle"></i>$_("Delete")</a></li> </ul> </div> </span> diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl index d094e66..8fb4a56 100644 --- a/ui/pages/template-edit.html.tmpl +++ b/ui/pages/template-edit.html.tmpl @@ -93,9 +93,13 @@ </div> <div class="template-edit-wrapper-controls templ-edit-cdrom"> <input id="template-edit-cdrom-textbox" class="form-control" name="cdrom" type="text" disabled="disabled" /> + <span class="form-control-feedback"> + <i class="fa fa-times"></i> + </span> </div> <div class="template-edit-wrapper-controls templ-edit-vm-image hide"> <input id="template-edit-vmimage-textbox" class="form-control" name="vm-image" type="text" disabled="disabled" /> + <span class="fa fa-times form-control-feedback"></span> </div> <div class="template-edit-wrapper-controls"> <select id="template-edit-graphics" name="graphics" class="form-control" /> @@ -172,7 +176,7 @@ <div id="storageRow{storageIndex}" class='item'> <span class="template-storage-cell storage-pool"> <input class="template-storage-name" value={storageName} type="text" style="display:none" /> - <select id="selectStorageName"></select> + <select id="selectStorageName-{storageIndex}" class="selectStorageName"></select> </span> <span class="template-storage-cell type"> <input class="template-storage-type form-control" value={storageType} readonly=true type="text" />