WIP: Upload volume UI

Adds new action, upload volume, to the storage->volumes list view
header. This will present a dialog to specify URL & name of volume,
and select a VM to attach the volume to.

Server API calls are still incomplete and rely on hardcoded values.
This commit is contained in:
bfederle 2012-05-02 14:29:29 -07:00
parent 310453f993
commit 57cc1ea378
6 changed files with 323 additions and 1 deletions

View File

@ -1,4 +1,8 @@
#new labels (begin) **********************************************************************************************
message.specify.url=Please specify URL
label.select.instance.to.attach.volume.to=Select instance to attach volume to
label.upload=Upload
label.upload.volume=Upload volume
label.virtual.routers=Virtual Routers
label.primary.storage.count=Primary Storage Pools
label.secondary.storage.count=Secondary Storage Pools

View File

@ -7079,6 +7079,90 @@ div.panel.ui-dialog div.list-view div.fixed-header {
display: none;
}
/*Upload volume*/
.upload-volume {
}
.upload-volume .list-view {
margin-top: 5px !important;
}
.upload-volume .listView-container {
background: #FFFFFF;
width: 823px;
margin: 71px 0px 20px 28px;
border: 1px solid #DADADA;
/*+border-radius:4px;*/
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
-khtml-border-radius: 4px;
border-radius: 4px 4px 4px 4px;
}
.upload-volume div.list-view .data-table div.fixed-header {
top: 115px !important;
left: 56px !important;
background: #FFFFFF !important;
}
.upload-volume .data-table table.body {
margin-top: 66px !important;
margin-left: 19px;
}
.upload-volume .list-view .toolbar {
top: 118px;
width: 801px;
left: 43px;
background: transparent;
border: none;
}
.upload-volume .top-fields {
float: left;
clear: none;
margin-left: 24px;
}
.upload-volume .top-fields .field {
float: left;
margin-right: 50px;
}
.upload-volume .top-fields input {
float: right;
/*+border-radius:3px;*/
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
-khtml-border-radius: 3px;
border-radius: 3px 3px 3px 3px;
padding: 2px;
width: 186px;
}
.upload-volume .top-fields label,
.upload-volume .desc {
display: block;
float: left;
padding: 6px;
font-size: 12px;
color: #4C5D6C;
/*+text-shadow:0px 0px #FFFFFF;*/
-moz-text-shadow: 0px 0px #FFFFFF;
-webkit-text-shadow: 0px 0px #FFFFFF;
-o-text-shadow: 0px 0px #FFFFFF;
text-shadow: 0px 0px #FFFFFF;
}
.upload-volume .desc {
position: absolute;
width: 825px;
text-align: left;
top: 79px;
left: 32px;
border-top: 1px solid #CFCFCF;
}
/*Network detail chat*/
.network-chart {
width: 100%;
@ -8886,3 +8970,11 @@ div.panel.ui-dialog div.list-view div.fixed-header {
cursor: move !important;
}
.uploadVolume .icon {
background-position: -232px -34px;
}
.uploadVolume:hover .icon {
background-position: -230px -615px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@ -1639,6 +1639,7 @@
<script src="scripts/ui-custom/securityRules.js" type="text/javascript"></script>
<script src="scripts/network.js" type="text/javascript"></script>
<script src="scripts/ui-custom/recurringSnapshots.js" type="text/javascript"></script>
<script src="scripts/ui-custom/uploadVolume.js" type="text/javascript"></script>
<script src="scripts/storage.js" type="text/javascript"></script>
<script src="scripts/templates.js" type="text/javascript"></script>
<script src="scripts/accounts.js" type="text/javascript"></script>
@ -1668,6 +1669,10 @@
<script language="javascript">
dictionary = {
'message.specify.url': '<fmt:message key="message.specify.url"/>',
'label.select.instance.to.attach.volume.to': '<fmt:message key="label.select.instance.to.attach.volume.to"/>',
'label.upload': '<fmt:message key="label.upload"/>',
'label.upload.volume': '<fmt:message key="label.upload.volume"/>',
'label.virtual.routers': '<fmt:message key="label.virtual.routers"/>',
'label.primary.storage.count': '<fmt:message key="label.primary.storage.count"/>',
'label.secondary.storage.count': '<fmt:message key="label.secondary.storage.count"/>',

View File

@ -171,8 +171,74 @@
notification: {
poll: pollAsyncJobResult
}
}
},
uploadVolume: {
isHeader: true,
label: 'label.upload.volume',
messages: {
notification: function() { return 'label.upload.volume'; }
},
notification: { poll: function(args) { args.complete(); } },
action: {
custom: cloudStack.uiCustom.uploadVolume({
listView: $.extend(true, {}, cloudStack.sections.instances, {
listView: {
filters: false,
dataProvider: function(args) {
var searchByArgs = args.filterBy.search.value &&
args.filterBy.search.value.length ?
'&name=' + args.filterBy.search.value : '';
$.ajax({
url: createURL('listVirtualMachines' + searchByArgs),
data: {
page: args.page,
pageSize: pageSize,
listAll: true
},
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
data: $.grep(
data.listvirtualmachinesresponse.virtualmachine ?
data.listvirtualmachinesresponse.virtualmachine : [],
function(instance) {
return $.inArray(instance.state, [
'Destroyed', 'Error', 'Stopping', 'Starting'
]) == -1;
}
)
});
},
error: function(data) {
args.response.error(parseXMLHttpResponse(data));
}
});
}
}
}),
action: function(args) {
$.ajax({
url: createURL('uploadVolume'),
data: {
hypervisor: 'XenServer', // Replace with instances' hypervisor
format: 'VHD', // Replace with format of uploaded volume
name: args.data.name,
url: args.data.url,
zoneid: 1 // Replace with instances' zone ID
},
success: function(json) {
args.response.success();
},
error: function(json) {
args.response.error(parseXMLHttpResponse(json));
}
});
}
})
}
}
},
dataProvider: function(args) {

View File

@ -0,0 +1,155 @@
(function(cloudStack, $) {
cloudStack.uiCustom.uploadVolume = function(args) {
var listView = args.listView;
var action = args.action;
var validate = function($uploadVolume) {
if (!$uploadVolume.find('input[type=text]').val()) {
cloudStack.dialog.notice({ message: _l('message.specify.url')});
return false;
}
if (!$uploadVolume.find(
'input[type=radio]:checked, input[type=checkbox]:checked'
).size()) {
cloudStack.dialog.notice({ message: _l('message.select.instance')});
return false;
}
return true;
};
return function(args) {
var $uploadVolume = $('<div>').addClass('upload-volume');
var context = args.context;
var topFields = function() {
var $form = $('<form>').addClass('top-fields');
var $urlLabel = $('<label>').html(_l('label.url') + ':');
var $urlField = $('<div>').addClass('field url');
var $nameLabel = $('<label>').html(_l('label.name') + ':');
var $nameField = $('<div>').addClass('field name');
var $urlInput = $('<input>').attr({
type: 'text',
name: 'url'
}).addClass('required');
var $nameInput = $('<input>').attr({
type: 'text',
name: 'name'
}).addClass('required');
$urlField.append($urlLabel, $urlInput);
$nameField.append($nameLabel, $nameInput);
$form.append($nameField, $urlField);
return $form;
};
var vmList = function(args) {
// Create a listing of instances, based on limited information
// from main instances list view
var $listView;
var instances = $.extend(true, {}, args.listView, {
context: context,
uiCustom: true
});
instances.listView.actions = {
select: {
label: _l('label.select.instance'),
type: 'radio',
action: {
uiCustom: function(args) {
var $item = args.$item;
var $input = $item.find('td.actions input:visible');
if ($input.attr('type') == 'checkbox') {
if ($input.is(':checked'))
$item.addClass('multi-edit-selected');
else
$item.removeClass('multi-edit-selected');
} else {
$item.siblings().removeClass('multi-edit-selected');
$item.addClass('multi-edit-selected');
}
}
}
}
};
$listView = $('<div>').listView(instances);
// Change action label
$listView.find('th.actions').html(_l('label.select'));
return $listView;
};
$uploadVolume.append(
topFields,
$('<div>').addClass('desc').html(_l('label.select.instance.to.attach.volume.to') + ':'),
$('<div>').addClass('listView-container').append(
vmList({ listView: listView })
)
);
$uploadVolume.dialog({
dialogClass: 'multi-edit-add-list panel',
width: 900,
title: _l('label.upload.volume'),
buttons: [
{
text: _l('label.upload'),
'class': 'ok',
click: function() {
if (!validate($uploadVolume)) return false;
var complete = args.complete;
var $loading = $('<div>').addClass('loading-overlay');
$loading.appendTo($uploadVolume);
action({
data: cloudStack.serializeForm($uploadVolume.find('form')),
context: $.extend(true, {}, context, {
instances: [
$uploadVolume.find('tr.multi-edit-selected').data('json-obj')
]
}),
response: {
success: function(args) {
$('.ui-dialog').fadeOut(function() {
$('.ui-dialog').remove();
$(window).trigger('cloudStack.fullRefresh');
});
$('div.overlay').fadeOut(function() {
$('div.overlay').remove();
});
complete({
$item: $('<div>'),
_custom: args._custom
});
},
error: function(args) {
$loading.remove();
cloudStack.dialog.notice({ message: args });
}
}
});
}
},
{
text: _l('label.cancel'),
'class': 'cancel',
click: function() {
$('.ui-dialog').fadeOut(function() {
$('.ui-dialog').remove();
});
$('div.overlay').fadeOut(function() {
$('div.overlay').remove();
});
}
}
]
}).closest('.ui-dialog').overlay();
};
};
}(cloudStack, jQuery));