diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index 1f68ada12b3..2cec645a6ea 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -242,7 +242,6 @@ body.login {
position: absolute;
margin-left: 11px;
margin-top: 12px;
- cursor: text;
}
.login .fields .field label.error {
@@ -2290,7 +2289,7 @@ div.toolbar div.filters select {
max-height: 29px;
background: url(../images/bg-breadcrumbs.png) repeat-x;
overflow: hidden;
- width: 794px;
+ width: 100%;
}
.project-view #breadcrumbs {
@@ -2403,7 +2402,6 @@ div.toolbar div.filters select {
margin-left: -10px;
text-indent: 13px;
font-weight: bold;
- overflow: hidden;
}
#breadcrumbs div.active-project {
@@ -6880,7 +6878,8 @@ div.panel.ui-dialog div.list-view div.fixed-header {
}
.destroy .icon,
-.delete .icon {
+.delete .icon,
+.decline .icon {
background-position: 1px -92px;
}
diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js
index c4743afcb23..b53a5e8470f 100644
--- a/ui/scripts/cloudStack.js
+++ b/ui/scripts/cloudStack.js
@@ -93,6 +93,7 @@
* g_supportELB: "public" - ips are allocated on public network (so use 'forvirtualnetwork' = true)
* g_supportELB: "false" – no ELB support
*/
+ g_capabilities = json.listcapabilitiesresponse.capability;
g_supportELB = json.listcapabilitiesresponse.capability.supportELB.toString(); //convert boolean to string if it's boolean
$.cookie('supportELB', g_supportELB, { expires: 1});
@@ -196,6 +197,7 @@
* g_supportELB: "public" - ips are allocated on public network (so use 'forvirtualnetwork' = true)
* g_supportELB: "false" – no ELB support
*/
+ g_capabilities = json.listcapabilitiesresponse.capability;
g_supportELB = json.listcapabilitiesresponse.capability.supportELB.toString(); //convert boolean to string if it's boolean
$.cookie('supportELB', g_supportELB, { expires: 1});
diff --git a/ui/scripts/projects.js b/ui/scripts/projects.js
index 88b5055f19d..9b20d080488 100644
--- a/ui/scripts/projects.js
+++ b/ui/scripts/projects.js
@@ -3,7 +3,7 @@
requireInvitation: function(args) {
return window.g_projectsInviteRequired;
},
-
+
add: function(args) {
setTimeout(function() {
$.ajax({
@@ -36,16 +36,18 @@
noSelect: true,
fields: {
'email': { edit: true, label: 'E-mail' },
+ 'account': { edit: 'ignore', label: 'Account' },
'state': { edit: 'ignore', label: 'Status' },
'add-user': { addButton: true, label: '' }
},
add: {
- label: 'Invite',
+ label: 'E-mail invite',
action: function(args) {
$.ajax({
url: createURL('addAccountToProject', { ignoreProject: true }),
data: {
projectId: args.context.projects[0].id,
+ account: args.data.account,
email: args.data.email
},
dataType: 'json',
@@ -65,7 +67,7 @@
error: function(data) {
args.response.error('Could not create user');
}
- });
+ });
}
},
actionPreFilter: function(args) {
@@ -78,7 +80,28 @@
return ['destroy'];
},
- actions: {},
+ actions: {
+ destroy: {
+ label: 'Revoke invitation',
+ action: function(args) {
+ $.ajax({
+ url: createURL('deleteProjectInvitation'),
+ data: {
+ id: args.context.multiRule[0].id
+ },
+ success: function(data) {
+ args.response.success({
+ _custom: { jobId: data.deleteprojectinvitationresponse.jobid },
+ notification: {
+ label: 'Un-invited user',
+ poll: pollAsyncJobResult
+ }
+ });
+ }
+ });
+ }
+ }
+ },
// Project users data provider
dataProvider: function(args) {
@@ -95,7 +118,8 @@
data: $.map(invites, function(elem) {
return {
id: elem.id,
- email: elem.email ? elem.email : elem.account,
+ account: elem.account,
+ email: elem.email,
state: elem.state
};
})
@@ -123,7 +147,6 @@
dataType: 'json',
async: true,
success: function(data) {
- data: args.data,
args.response.success({
_custom: {
jobId: data.addaccounttoprojectresponse.jobid
@@ -133,6 +156,10 @@
poll: pollAsyncJobResult
}
});
+
+ if (g_capabilities.projectinviterequired) {
+ cloudStack.dialog.notice({ message: 'Invite sent to user; they will be added to the project once they accept the invitation' });
+ }
}
});
}
@@ -266,91 +293,248 @@
cloudStack.sections.projects = {
title: 'Projects',
id: 'projects',
- listView: {
- fields: {
- name: { label: 'Project Name' },
- displaytext: { label: 'Display Text' },
- domain: { label: 'Domain' },
- account: { label: 'Owner' },
- state: { label: 'Status', indicator: { 'Active': 'on', 'Destroyed': 'off', 'Disabled': 'off' } }
- },
-
- dataProvider: function(args) {
- $.ajax({
- url: createURL('listProjects', { ignoreProject: true }),
- dataType: 'json',
- async: true,
- success: function(data) {
- args.response.success({
- data: data.listprojectsresponse.project,
- actionFilter: projectsActionFilter
- });
- }
- });
- },
-
- actions: {
- add: {
- label: 'New Project',
- action: {
- custom: function(args) {
- $(window).trigger('cloudStack.newProject');
- }
+ sectionSelect: {
+ label: 'Select view'
+ },
+ sections: {
+ projects: {
+ type: 'select',
+ id: 'projects',
+ title: 'Projects',
+ listView: {
+ fields: {
+ name: { label: 'Project Name' },
+ displaytext: { label: 'Display Text' },
+ domain: { label: 'Domain' },
+ account: { label: 'Owner' },
+ state: { label: 'Status', indicator: { 'Active': 'on', 'Destroyed': 'off', 'Disabled': 'off', 'Left Project': 'off' } }
},
- messages: {
- confirm: function(args) {
- return 'Are you sure you want to remove ' + args.name + '?';
- },
- notification: function(args) {
- return 'Removed project';
- }
- },
-
- notification: {
- poll: testData.notifications.testPoll
- }
- },
-
- destroy: {
- label: 'Remove project',
- action: function(args) {
+ dataProvider: function(args) {
$.ajax({
- url: createURL('deleteProject', { ignoreProject: true }),
- data: {
- id: args.data.id
- },
+ url: createURL('listProjects', { ignoreProject: true }),
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
- _custom: {
- getUpdatedItem: function(data) {
- return $.extend(data, { state: 'Destroyed' });
- },
- getActionFilter: function(args) {
- return function() {
- return [];
- };
- },
- jobId: data.deleteprojectresponse.jobid
- }
+ data: data.listprojectsresponse.project,
+ actionFilter: projectsActionFilter
});
}
});
},
- messages: {
- confirm: function(args) {
- return 'Are you sure you want to remove ' + args.name + '?';
+ actions: {
+ add: {
+ label: 'New Project',
+ action: {
+ custom: function(args) {
+ $(window).trigger('cloudStack.newProject');
+ }
+ },
+
+ actions: {
+ add: {
+ label: 'New Project',
+ action: {
+ custom: function(args) {
+ $(window).trigger('cloudStack.newProject');
+ }
+ },
+
+ messages: {
+ confirm: function(args) {
+ return 'Are you sure you want to remove ' + args.name + '?';
+ },
+ notification: function(args) {
+ return 'Removed project';
+ }
+ },
+
+ notification: {
+ poll: testData.notifications.testPoll
+ }
+ },
+
+ destroy: {
+ label: 'Remove project',
+ action: function(args) {
+ $.ajax({
+ url: createURL('deleteProject', { ignoreProject: true }),
+ data: {
+ id: args.data.id
+ },
+ dataType: 'json',
+ async: true,
+ success: function(data) {
+ args.response.success({
+ _custom: {
+ getUpdatedItem: function(data) {
+ return $.extend(data, { state: 'Destroyed' });
+ },
+ getActionFilter: function(args) {
+ return function() {
+ return [];
+ };
+ },
+ jobId: data.deleteprojectresponse.jobid
+ }
+ });
+ }
+ });
+ },
+
+ messages: {
+ confirm: function(args) {
+ return 'Are you sure you want to remove ' + args.name + '?';
+ },
+ notification: function(args) {
+ return 'Removed project';
+ }
+ },
+
+ notification: {
+ poll: pollAsyncJobResult
+ }
+ }
+ }
},
- notification: function(args) {
- return 'Removed project';
+
+ destroy: {
+ label: 'Remove project',
+ action: function(args) {
+ $.ajax({
+ url: createURL('deleteProject', { ignoreProject: true }),
+ data: {
+ id: args.data.id
+ },
+ dataType: 'json',
+ async: true,
+ success: function(data) {
+ args.response.success({
+ _custom: {
+ getUpdatedItem: function(data) {
+ return $.extend(data, { state: 'Destroyed' });
+ },
+ getActionFilter: function(args) {
+ return function() {
+ return [];
+ };
+ },
+ jobId: data.deleteprojectresponse.jobid
+ }
+ });
+ }
+ });
+ },
+
+ messages: {
+ confirm: function(args) {
+ return 'Are you sure you want to remove ' + args.name + '?';
+ },
+ notification: function(args) {
+ return 'Removed project';
+ }
+ },
+
+ notification: {
+ poll: pollAsyncJobResult
+ }
+ }
+ }
+ }
+ },
+
+ invitations: {
+ type: 'select',
+ id: 'invitations',
+ title: 'Invitations',
+ listView: {
+ fields: {
+ project: { label: 'Project' },
+ domain: { label: 'Domain' },
+ state: {
+ label: 'Status',
+ indicator: {
+ 'Accepted': 'on', 'Completed': 'on',
+ 'Pending': 'off', 'Declined': 'off'
+ }
}
},
- notification: {
- poll: pollAsyncJobResult
+ dataProvider: function(args) {
+ $.ajax({
+ url: createURL('listProjectInvitations'),
+ data: {
+ account: cloudStack.context.users[0].account,
+ domainid: cloudStack.context.users[0].domainid,
+ state: 'Pending'
+ },
+ success: function(data) {
+ args.response.success({
+ actionFilter: projectInvitationActionFilter,
+ data: data.listprojectinvitationsresponse.projectinvitation
+ });
+ }
+ });
+ },
+
+ actions: {
+ accept: {
+ label: 'Accept Invitation',
+ action: function(args) {
+ $.ajax({
+ url: createURL('updateProjectInvitation'),
+ data: {
+ projectid: args.context.invitations[0].projectid,
+ account: args.context.users[0].account,
+ domainid: args.context.users[0].domainid,
+ accept: true
+ },
+ success: function(data) {
+ args.response.success({
+ _custom: {
+ jobId: data.updateprojectinvitationresponse.jobid,
+ getUpdatedItem: function() { return { state: 'Accepted' }; }
+ }
+ });
+ }
+ });
+ },
+ messages: {
+ confirm: function() { return 'Please confirm you wish to join this project.'; },
+ notification: function() { return 'Accepted project invitation'; }
+ },
+ notification: { poll: pollAsyncJobResult }
+ },
+
+ decline: {
+ label: 'Decline Invitation',
+ action: function(args) {
+ $.ajax({
+ url: createURL('updateProjectInvitation'),
+ data: {
+ projectid: args.context.invitations[0].projectid,
+ account: args.context.users[0].account,
+ accept: false
+ },
+
+ success: function(data) {
+ args.response.success({
+ _custom: {
+ jobId: data.updateprojectinvitationresponse.jobid,
+ getUpdatedItem: function() { return { state: 'Declined' }; }
+ }
+ });
+ }
+ });
+ },
+ notification: { poll: pollAsyncJobResult },
+ messages: {
+ confirm: function() { return 'Are you sure you want to decline this project invitation?'; },
+ notification: function() { return 'Declined project invitation'; }
+ }
+ }
}
}
}
@@ -358,10 +542,20 @@
};
var projectsActionFilter = function(args) {
- if (args.context.item.account == args.context.users[0].account) {
+ if (args.context.item.account == cloudStack.context.users[0].account) {
return ['destroy'];
}
return [];
};
+
+ var projectInvitationActionFilter = function(args) {
+ var state = args.context.item.state;
+
+ if (state == 'Accepted' || state == 'Completed' || state == 'Declined') {
+ return [];
+ }
+
+ return ['accept', 'decline'];
+ };
} (cloudStack, testData));
diff --git a/ui/scripts/ui-custom/projects.js b/ui/scripts/ui-custom/projects.js
index 1b341da7c71..db61bb281e9 100644
--- a/ui/scripts/ui-custom/projects.js
+++ b/ui/scripts/ui-custom/projects.js
@@ -18,21 +18,23 @@
*/
dashboard: function() {
var tabs = {
- Overview: function() {
- return $('').attr('src', 'images/screens/ProjectDashboard.png');
+ overview: function() {
+ return $('
').attr('src', 'images/screens/ProjectDashboard.png').data('tab-title', 'Overview');
}
};
// Only show management tabs to owner of project
if (cloudStack.context.projects &&
(cloudStack.context.projects[0].account == cloudStack.context.users[0].account)) {
- tabs['Users'] = function() {
- return $('