diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index 5497e034344..9549157e1e1 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -6835,3 +6835,50 @@ div.panel.ui-dialog div.list-view div.fixed-header {
background-position: -229px -586px;
}
+.moveTop .icon {
+ background-position: -24px -161px;
+}
+
+.moveTop:hover .icon {
+ background-position: -24px -734px;
+}
+
+.moveBottom .icon {
+ background-position: -98px -161px;
+}
+
+.moveBottom:hover .icon {
+ background-position: -98px -734px;
+}
+
+.moveUp .icon {
+ background-position: -2px -161px;
+}
+
+.moveUp:hover .icon {
+ background-position: -2px -734px;
+}
+
+.moveDown .icon {
+ background-position: -55px -161px;
+}
+
+.moveDown:hover .icon {
+ background-position: -55px -734px;
+}
+
+.moveDrag .icon {
+ cursor: move;
+ /*+border-radius:10px;*/
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ -khtml-border-radius: 10px;
+ border-radius: 10px 10px 10px 10px;
+ background-position: -74px -162px;
+}
+
+.moveDrag:hover .icon {
+ background-color: #FFFFFF;
+ cursor: move !important;
+}
+
diff --git a/ui/images/sprites.png b/ui/images/sprites.png
index 4babfd498b7..4804598d526 100644
Binary files a/ui/images/sprites.png and b/ui/images/sprites.png differ
diff --git a/ui/scripts-test/configuration.js b/ui/scripts-test/configuration.js
index 67f56e62616..4c6d1a407e4 100644
--- a/ui/scripts-test/configuration.js
+++ b/ui/scripts-test/configuration.js
@@ -68,6 +68,45 @@
}
}
},
+
+ reorder: {
+ moveTop: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveBottom: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveUp: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDown: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDrag: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ }
+ },
+
dataProvider: function(args) {
setTimeout(function() {
args.response.success({
@@ -90,6 +129,45 @@
memory: { label: 'Memory' },
domain: { label: 'Domain'}
},
+
+ reorder: {
+ moveTop: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveBottom: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveUp: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDown: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDrag: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ }
+ },
+
actions: {
add: {
label: 'Add system service offering',
@@ -168,6 +246,44 @@
});
},
+ reorder: {
+ moveTop: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveBottom: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveUp: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDown: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDrag: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ }
+ },
+
actions: {
add: {
label: 'Add disk offering',
@@ -251,6 +367,44 @@
traffictype: { label: 'Traffic Type'}
},
+ reorder: {
+ moveTop: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveBottom: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveUp: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDown: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ },
+ moveDrag: {
+ action: function(args) {
+ setTimeout(function() {
+ args.response.success();
+ }, 500);
+ }
+ }
+ },
+
actions: {
add: {
label: 'Add network offering',
@@ -327,7 +481,7 @@
vlanId: { label: 'VLAN ID', isHidden: true, dependsOn: 'specifyVlan'},
- supportedServices: {
+ supportedServices: {
label: 'Supported Services',
dynamic: function(args) {
@@ -343,7 +497,7 @@
};
fields[id.isEnabled] = { label: this, isBoolean: true };
- fields[id.provider] = {
+ fields[id.provider] = {
label: this + ' Provider',
isHidden: true,
dependsOn: id.isEnabled,
@@ -368,7 +522,7 @@
tags: { label: 'Tags' }
}
},
-
+
notification: {
poll: testData.notifications.testPoll
},
diff --git a/ui/scripts-test/network.js b/ui/scripts-test/network.js
index 9b8f91c9692..18eb509b716 100644
--- a/ui/scripts-test/network.js
+++ b/ui/scripts-test/network.js
@@ -6,6 +6,49 @@
label: 'Select view'
},
sections: {
+ networks: {
+ type: 'select',
+ title: 'Networks',
+ listView: {
+ filters: {
+ all: { label: 'All' },
+ mine: { label: 'My network' }
+ },
+ fields: {
+ displaytext: { label: 'Name' },
+ traffictype: { label: 'Traffic Type' },
+ gateway: { label: 'Gateway' },
+ vlan: { label: 'VLAN' }
+ },
+ dataProvider: testData.dataProvider.listView('networks'),
+
+ detailView: {
+ name: 'Network details',
+ viewAll: { path: 'network.ipAddresses', label: 'IP Addresses' },
+ tabs: {
+ details: {
+ title: 'Details',
+ fields: [
+ {
+ displaytext: { label: 'Name' }
+ },
+ {
+ name: { label: 'Short name' },
+ traffictype: { label: 'Traffic Type' },
+ gateway: { label: 'Gateway' },
+ vlan: { label: 'VLAN' }
+ },
+ {
+ startip: { label: 'Start IP' },
+ endip: { label: 'End IP' }
+ }
+ ],
+ dataProvider: testData.dataProvider.detailView('networks')
+ }
+ }
+ }
+ }
+ },
ipAddresses: {
type: 'select',
title: 'IP Addresses',
@@ -86,9 +129,6 @@
})
},
messages: {
- confirm: function(args) {
- return 'Are you sure you want to enable static NAT?';
- },
notification: function(args) {
return 'Enabled Static NAT';
}
@@ -113,48 +153,6 @@
notification: {
poll: testData.notifications.customPoll({ isstaticnat: false })
}
- },
- enableVPN: {
- label: 'Enable VPN',
- action: function(args) {
- args.response.success();
- },
- messages: {
- confirm: function(args) {
- return 'Please confirm that you want VPN enabled for this IP address.';
- },
- notification: function(args) {
- return 'Enabled VPN';
- },
- complete: function(args) {
- return 'VPN is now enabled for IP ' + args.publicip + '.'
- + '
Your IPsec pre-shared key is:
' + args.presharedkey;
- }
- },
- notification: {
- poll: testData.notifications.customPoll({
- publicip: '10.2.2.1',
- presharedkey: '23fudh881ssx88199488PP!#Dwdw',
- vpnenabled: true
- })
- }
- },
- disableVPN: {
- label: 'Disable VPN',
- action: function(args) {
- args.response.success();
- },
- messages: {
- confirm: function(args) {
- return 'Are you sure you want to disable VPN?';
- },
- notification: function(args) {
- return 'Disabled VPN';
- }
- },
- notification: {
- poll: testData.notifications.customPoll({ vpnenabled: false })
- }
}
},
dataProvider: testData.dataProvider.listView('network'),
@@ -174,6 +172,87 @@
return disabledTabs;
},
+ actions: {
+ enableStaticNAT: {
+ label: 'Enable static NAT',
+ action: {
+ noAdd: true,
+ custom: cloudStack.uiCustom.enableStaticNAT({
+ listView: cloudStack.sections.instances,
+ action: function(args) {
+ args.response.success();
+ }
+ })
+ },
+ messages: {
+ notification: function(args) {
+ return 'Enabled Static NAT';
+ }
+ },
+ notification: {
+ poll: testData.notifications.customPoll({ isstaticnat: true })
+ }
+ },
+ disableStaticNAT: {
+ label: 'Disable static NAT',
+ action: function(args) {
+ args.response.success();
+ },
+ messages: {
+ confirm: function(args) {
+ return 'Are you sure you want to disable static NAT?';
+ },
+ notification: function(args) {
+ return 'Disabled Static NAT';
+ }
+ },
+ notification: {
+ poll: testData.notifications.customPoll({ isstaticnat: false })
+ }
+ },
+ enableVPN: {
+ label: 'Enable VPN',
+ action: function(args) {
+ args.response.success();
+ },
+ messages: {
+ confirm: function(args) {
+ return 'Please confirm that you want VPN enabled for this IP address.';
+ },
+ notification: function(args) {
+ return 'Enabled VPN';
+ },
+ complete: function(args) {
+ return 'VPN is now enabled for IP ' + args.publicip + '.'
+ + '
Your IPsec pre-shared key is:
' + args.presharedkey;
+ }
+ },
+ notification: {
+ poll: testData.notifications.customPoll({
+ publicip: '10.2.2.1',
+ presharedkey: '23fudh881ssx88199488PP!#Dwdw',
+ vpnenabled: true
+ })
+ }
+ },
+ disableVPN: {
+ label: 'Disable VPN',
+ action: function(args) {
+ args.response.success();
+ },
+ messages: {
+ confirm: function(args) {
+ return 'Are you sure you want to disable VPN?';
+ },
+ notification: function(args) {
+ return 'Disabled VPN';
+ }
+ },
+ notification: {
+ poll: testData.notifications.customPoll({ vpnenabled: false })
+ }
+ }
+ },
tabs: {
details: {
title: 'Details',
diff --git a/ui/scripts-test/test-data.js b/ui/scripts-test/test-data.js
index 040a9dc720d..7f96e4293dc 100644
--- a/ui/scripts-test/test-data.js
+++ b/ui/scripts-test/test-data.js
@@ -111,31 +111,15 @@
detailView: function(section) {
return function(args) {
setTimeout(function() {
- var item = $.grep(testData.data[section], function(elem) {
+ var pulledData = $.grep(testData.data[section], function(elem) {
return elem.id == args.id;
- });
+ })[0];
+
+ var item = $.extend(true, {}, pulledData, args.jsonObj);
args.response.success({
- actionFilter: function(args) {
- var allowedActions = args.context.actions;
- var disallowedActions = [];
- var item = args.context.item;
- var status = item.state;
-
- if (status == 'Running' || status == 'Starting') {
- disallowedActions.push('start');
- } else if (status == 'Stopped' || status == 'Stopping') {
- disallowedActions.push('stop');
- disallowedActions.push('restart');
- }
-
- allowedActions = $.grep(allowedActions, function(item) {
- return $.inArray(item, disallowedActions) == -1;
- });
-
- return allowedActions;
- },
- data: item[0]
+ actionFilter: testData.actionFilter,
+ data: item
});
}, 300);
};
diff --git a/ui/scripts/ui/widgets/dataTable.js b/ui/scripts/ui/widgets/dataTable.js
index 4dc96d77380..0b2d4e6d554 100644
--- a/ui/scripts/ui/widgets/dataTable.js
+++ b/ui/scripts/ui/widgets/dataTable.js
@@ -14,7 +14,7 @@
*/
var withinResizeBounds = function($elem, posX) {
var leftBound = $elem.offset().left + $elem.width() / 1.2;
-
+
return posX > leftBound;
};
@@ -165,7 +165,7 @@
computeEvenOddRows();
};
-
+
var resizeHeaders = function() {
var $thead = $table.closest('div.data-table').find('thead');
var $tbody = $table.find('tbody');
@@ -209,6 +209,13 @@
refresh: function() {
resizeHeaders();
computeEvenOddRows();
+ },
+
+ selectRow: function(rowIndex) {
+ var $row = $($table.find('tbody tr')[rowIndex]);
+
+ $row.siblings().removeClass('selected');
+ $row.addClass('selected');
}
};
diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js
index a95fdec34c1..5a5165b8451 100644
--- a/ui/scripts/ui/widgets/detailView.js
+++ b/ui/scripts/ui/widgets/detailView.js
@@ -67,6 +67,116 @@
var id = args.id;
var context = $detailView.data('view-args').context;
var _custom = $detailView.data('_custom');
+ var customAction = action.action.custom;
+ var noAdd = action.noAdd;
+
+ var updateTabContent = function(newData) {
+ var $detailViewElems = $detailView.find('ul.ui-tabs-nav, .detail-group').remove();
+ $detailView.tabs('destroy');
+ $detailView.data('view-args').jsonObj = newData;
+
+ makeTabs(
+ $detailView,
+ $detailView.data('view-args').tabs,
+ {
+ context: context,
+ tabFilter: $detailView.data('view-args').tabFilter,
+ newData: newData
+ }
+ ).appendTo($detailView);
+
+ $detailView.tabs();
+ };
+
+ var performAction = function(data, options) {
+ if (!options) options = {};
+
+ var $form = options.$form;
+
+ if (customAction && !noAdd) {
+ customAction({
+ context: context,
+ complete: function(args) {
+ // Set loading appearance
+ var $loading = $('