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 $('
').addClass('management'); + tabs.users = function() { + return $('
').addClass('management').data('tab-title', 'Users'); }; - tabs['Invitations'] = function() { - return $('
').addClass('management-invite'); - }; + if (g_capabilities.projectinviterequired) { + tabs.invitations = function() { + return $('
').addClass('management-invite').data('tab-title', 'Invitations'); + }; + } } var $tabs = $('
').addClass('tab-content').append($('