| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); 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 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| (function(cloudStack, $) { | |
| var pageElems = cloudStack.uiCustom.projectsTabs = { | |
| /** | |
| * User management multi-edit | |
| */ | |
| userManagement: function(args) { | |
| var multiEdit = !args.useInvites ? | |
| cloudStack.projects.addUserForm : | |
| cloudStack.projects.inviteForm; | |
| var $multi = $('<div>').multiEdit($.extend(true, {}, multiEdit, { | |
| context: args.context | |
| })); | |
| if (args.useInvites) { | |
| var $fields = $multi.find('form table').find('th, td'); | |
| var $accountFields = $fields.filter(function() { | |
| return $(this).hasClass('account'); | |
| }); | |
| var $emailFields = $fields.filter(function() { | |
| return $(this).hasClass('email'); | |
| }); | |
| $multi.prepend( | |
| $('<div>').addClass('add-by') | |
| .append($('<span>').html(_l('label.add.by') + ':')) | |
| .append( | |
| $('<div>').addClass('selection') | |
| .append( | |
| $('<input>').attr({ | |
| type: 'radio', | |
| name: 'add-by', | |
| checked: 'checked' | |
| }).click(function() { | |
| $accountFields.show(); | |
| $emailFields.hide(); | |
| $emailFields.find('input').val(''); | |
| return true; | |
| }).click() | |
| ) | |
| .append($('<label>').html(_l('label.account'))) | |
| .append( | |
| $('<input>').attr({ | |
| type: 'radio', | |
| name: 'add-by' | |
| }).click(function() { | |
| $accountFields.hide(); | |
| $accountFields.find('input').val(''); | |
| $emailFields.show(); | |
| return true; | |
| }) | |
| ) | |
| .append($('<label>').html(_l('label.email'))) | |
| ) | |
| ); | |
| } | |
| return $multi; | |
| }, | |
| dashboardTabs: { | |
| overview: function() { | |
| var $dashboard = $('#template').find('.project-dashboard-view').clone(); | |
| $dashboard.data('tab-title', _l('label.menu.dashboard')); | |
| var getData = function() { | |
| // Populate data | |
| $dashboard.find('[data-item]').hide(); | |
| var $loading = $('<div>').addClass('loading-overlay').prependTo($dashboard); | |
| cloudStack.projects.dashboard({ | |
| response: { | |
| success: function(args) { | |
| $loading.remove(); | |
| var data = args.data; | |
| // Iterate over data; populate corresponding DOM elements | |
| $.each(data, function(key, value) { | |
| var $elem = $dashboard.find('[data-item=' + key + ']'); | |
| // This assumes an array of data | |
| if ($elem.is('ul')) { | |
| $elem.show(); | |
| var $liTmpl = $elem.find('li').remove(); | |
| $(value).each(function() { | |
| var item = this; | |
| var $li = $liTmpl.clone().appendTo($elem).hide(); | |
| $.each(item, function(arrayKey, arrayValue) { | |
| var $arrayElem = $li.find('[data-list-item=' + arrayKey + ']'); | |
| $arrayElem.html(_s(arrayValue)); | |
| }); | |
| $li.attr({ title: item.description }); | |
| $li.fadeIn(); | |
| }); | |
| } else { | |
| $elem.each(function() { | |
| var $item = $(this); | |
| if ($item.hasClass('chart-line')) { | |
| $item.show().animate({ width: value + '%' }); | |
| } else { | |
| $item.hide().html(_s(value)).fadeIn(); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| } | |
| }); | |
| }; | |
| getData(); | |
| $dashboard.find('.button.manage-resources').click(function() { | |
| $('.navigation-item.network').click(); | |
| }); | |
| $dashboard.find('.info-box.events .button').click(function() { | |
| $('.navigation-item.events').click(); | |
| }); | |
| return $dashboard; | |
| }, | |
| users: function() { | |
| return $('<div>').addClass('management').data('tab-title', _l('label.menu.accounts')); | |
| }, | |
| invitations: function() { | |
| return $('<div>').addClass('management-invite').data('tab-title', _l('label.invitations')); | |
| }, | |
| resources: function(options) { | |
| if (!options) options = {}; | |
| var $resources = $('<div>').addClass('resources').data('tab-title', _l('label.resources')); | |
| var $form = $('<form>'); | |
| var $submit = $('<input>').attr({ | |
| type: 'submit' | |
| }).val(_l('label.apply')); | |
| cloudStack.projects.resourceManagement.dataProvider({ | |
| response: { | |
| success: function(args) { | |
| $(args.data).each(function() { | |
| var resource = this; | |
| var $field = $('<div>').addClass('field'); | |
| var $label = $('<label>').attr({ | |
| 'for': resource.type | |
| }).html(_s(resource.label)); | |
| var $input = $('<input>').attr({ | |
| type: 'text', | |
| name: resource.type, | |
| value: resource.value, | |
| id: resource.type | |
| }).addClass('required'); | |
| $field.append($label, $input); | |
| $field.appendTo($form); | |
| }); | |
| $form.validate(); | |
| $form.submit(function() { | |
| if (!$form.valid) { | |
| return false; | |
| } | |
| var $loading = $('<div>').addClass('loading-overlay').appendTo($form); | |
| cloudStack.projects.resourceManagement.update({ | |
| data: cloudStack.serializeForm($form), | |
| response: { | |
| success: function(args) { | |
| $loading.remove(); | |
| $('.notifications').notifications('add', { | |
| section: 'dashboard', | |
| desc: 'label.update.project.resources', | |
| interval: 1000, | |
| poll: function(args) { | |
| args.complete(); | |
| } | |
| }); | |
| } | |
| } | |
| }, options.projectID); | |
| return false; | |
| }); | |
| $submit.appendTo($form); | |
| $form.appendTo($resources); | |
| } | |
| } | |
| }, options.projectID); | |
| return $resources; | |
| } | |
| }, | |
| /** | |
| * Projects dashboard | |
| */ | |
| dashboard: function() { | |
| var tabs = { | |
| dashboard: pageElems.dashboardTabs.overview | |
| }; | |
| // Only show management tabs to owner of project | |
| if (isAdmin() || isDomainAdmin() || ( | |
| cloudStack.context.projects && | |
| (cloudStack.context.projects[0].account == cloudStack.context.users[0].account) | |
| )) { | |
| tabs.users = pageElems.dashboardTabs.users; | |
| if (g_capabilities.projectinviterequired) { | |
| tabs.invitations = pageElems.dashboardTabs.invitations; | |
| } | |
| if (isAdmin() || isDomainAdmin()) { | |
| tabs.resources = pageElems.dashboardTabs.resources; | |
| } | |
| } | |
| var $tabs = $('<div>').addClass('tab-content').append($('<ul>')); | |
| var $toolbar = $('<div>').addClass('toolbar'); | |
| // Make UI tabs | |
| $.each(tabs, function(tabName, tab) { | |
| var $tab = $('<li>').appendTo($tabs.find('ul:first')); | |
| var $tabContent = tab(); | |
| var $tabLink = $('<a>') | |
| .attr({ href: '#project-view-dashboard-' + tabName }) | |
| .html($tabContent.data('tab-title')) | |
| .appendTo($tab); | |
| var $content = $('<div>') | |
| .appendTo($tabs) | |
| .attr({ id: 'project-view-dashboard-' + tabName }) | |
| .append($tabContent); | |
| }); | |
| $tabs.find('ul li:first').addClass('first'); | |
| $tabs.find('ul li:last').addClass('last'); | |
| $tabs.bind('tabsshow', function(event, ui) { | |
| var $panel = $(ui.panel); | |
| var $management = $panel.find('.management'); | |
| var $managementInvite = $panel.find('.management-invite'); | |
| if ($management.size()) { | |
| $management.children().remove(); | |
| $management.append(pageElems.userManagement({ | |
| context: cloudStack.context | |
| })); | |
| return true; | |
| } | |
| if ($managementInvite.size()) { | |
| $managementInvite.children().remove(); | |
| $managementInvite.append(pageElems.userManagement({ | |
| context: cloudStack.context, | |
| useInvites: true | |
| })); | |
| } | |
| return true; | |
| }); | |
| return $('<div>').addClass('project-dashboard') | |
| .append($.merge( | |
| $toolbar, | |
| $tabs.tabs() | |
| )); | |
| }, | |
| /** | |
| * Add new project flow | |
| */ | |
| newProjectForm: function() { | |
| var $newProject = $('<div>').addClass('new-project'); | |
| $newProject.append($('<div>').addClass('title').html(_l('label.create.project'))); | |
| var $form = $('<form>'); | |
| var $formDesc = $('<div>').addClass('form-desc'); | |
| var $projectName = $('<div>').addClass('field name') | |
| .append($('<label>').attr('for', 'project-name').html(_l('label.project.name'))) | |
| .append($('<input>').addClass('required').attr({ | |
| type: 'text', | |
| name: 'project-name', | |
| id: 'project-name' | |
| })); | |
| var $projectDesc = $('<div>').addClass('field desc') | |
| .append($('<label>').attr('for', 'project-desc').html(_l('label.display.text'))) | |
| .append($('<input>').attr({ | |
| type: 'text', | |
| name: 'project-display-text', | |
| id: 'project-desc' | |
| })); | |
| var $submit = $('<input>').attr({ type: 'submit' }).val(_l('label.create.project')); | |
| var $cancel = $('<div>').addClass('button cancel').html(_l('label.cancel')); | |
| var $loading = $('<div>').addClass('loading-overlay'); | |
| // Form events/validation | |
| $form.validate(); | |
| $form.submit(function() { | |
| if (!$form.valid()) return false; | |
| $form.prepend($loading); | |
| cloudStack.projects.add({ | |
| context: cloudStack.context, | |
| data: cloudStack.serializeForm($form), | |
| response: { | |
| success: function(args) { | |
| var project = args.data; | |
| $(window).trigger('cloudStack.fullRefresh'); | |
| $loading.remove(); | |
| // Confirmation | |
| $form.replaceWith(function() { | |
| var $confirm = $('<div>').addClass('confirm'); | |
| // Update title with project name | |
| $newProject.find('.title').html(_s(args.data.name)); | |
| // Show field data | |
| $confirm.append($projectName).find('input').replaceWith( // Name | |
| $('<span>').addClass('value').html(_s( | |
| args.data.name | |
| )) | |
| ); | |
| $confirm.append($projectDesc).find('input').replaceWith( // Display text | |
| $('<span>').addClass('value').html(_s( | |
| args.data.displayText | |
| )) | |
| ); | |
| var $buttons = $('<div>').addClass('buttons'); | |
| var $addAccountButton = $('<div>').addClass('button confirm').html(_l('label.add.accounts')); | |
| $addAccountButton.click(function() { | |
| // Show add user form | |
| $confirm.replaceWith(function() { | |
| var $userManagement = pageElems.userManagement({ | |
| context: $.extend(true, {}, cloudStack.context, { | |
| projects: [project] | |
| }), | |
| useInvites: cloudStack.projects.requireInvitation() | |
| }); | |
| var $nextButton = $('<div>').addClass('button confirm next').html(_l('label.next')); | |
| $newProject.find('.title').html( | |
| cloudStack.projects.requireInvitation() ? | |
| _l('label.invite.to') + ' ' + _s(args.data.name) : | |
| _l('label.add.accounts.to') + ' ' + _s(args.data.name) | |
| ); | |
| $nextButton.appendTo($userManagement).click(function() { | |
| $newProject.find('.title').html(_l('label.review')); | |
| $userManagement.replaceWith(function() { | |
| var $review = $('<div>').addClass('review'); | |
| var $projectData = $('<div>').addClass('project-data'); | |
| // Basic project data | |
| $review.append($projectData); | |
| $projectData.append($projectName).find('input').replaceWith( // Name | |
| $('<span>').addClass('value').html(_s( | |
| args.data.name | |
| )) | |
| ); | |
| $projectData.append($projectDesc).find('input').replaceWith( // Display text | |
| $('<span>').addClass('value').html(_s( | |
| args.data.displayText | |
| )) | |
| ); | |
| // User/resouce tabs | |
| var $tabs = $('<div>').addClass('tabs resources').appendTo($review); | |
| var $ul = $('<ul>').appendTo($tabs) | |
| .append( | |
| // Users tab | |
| $('<li>').addClass('first').append( | |
| $('<a>').attr({ href: '#new-project-review-tabs-users'}).html( | |
| cloudStack.projects.requireInvitation() ? | |
| _l('label.invitations') : _l('label.accounts') | |
| ) | |
| ) | |
| ); | |
| if (isAdmin() || isDomainAdmin()) { | |
| $ul.append( | |
| // Resources tab | |
| $('<li>').addClass('last').append( | |
| $('<a>').attr({ href: '#new-project-review-tabs-resouces'}).html(_l('label.resources')) | |
| ) | |
| ); | |
| } | |
| var $users = $('<div>').attr({ id: 'new-project-review-tabs-users' }).appendTo($tabs); | |
| cloudStack.context.projects = [project]; | |
| var $resources; | |
| if (isAdmin() || isDomainAdmin()) { | |
| $resouces = $('<div>') | |
| .attr({ id: 'new-project-review-tabs-resouces' }) | |
| .appendTo($tabs) | |
| .append(pageElems.dashboardTabs.resources); | |
| } | |
| $tabs.tabs(); | |
| $users.listView({ | |
| listView: { | |
| id: 'project-accounts', | |
| disableInfiniteScrolling: true, | |
| fields: !cloudStack.projects.requireInvitation() ? { | |
| username: { label: _l('label.account') } | |
| } : { | |
| account: { label: _l('label.invited.accounts') } | |
| }, | |
| dataProvider: function(args) { | |
| setTimeout(function() { | |
| args.response.success({ | |
| data: $.map($userManagement.find('.data-item tr'), function(elem) { | |
| // Store previous user data in list table | |
| return !cloudStack.projects.requireInvitation() ? { | |
| username: $(elem).find('td.username span').html() | |
| } : { | |
| account: $(elem).find('td.account span').html() | |
| }; | |
| }) | |
| }); | |
| }, 0); | |
| } | |
| } | |
| }); | |
| // Save button | |
| var $saveButton = $nextButton.clone().appendTo($review); | |
| $saveButton.html(_l('label.save')); | |
| $saveButton.click(function() { | |
| $('#new-project-review-tabs-resouces').find('form').submit(); | |
| $('.ui-dialog, .overlay').remove(); | |
| }); | |
| $laterButton.click(function() { | |
| $(':ui-dialog, .overlay').remove(); | |
| return false; | |
| }); | |
| return $review; | |
| }); | |
| $(':ui-dialog').dialog('option', 'position', 'center'); | |
| }); | |
| $laterButton.html(_l('label.close')).appendTo($userManagement); | |
| return $userManagement; | |
| }); | |
| $(':ui-dialog').dialog('option', 'position', 'center'); | |
| return false; | |
| }); | |
| var $laterButton = $('<div>').addClass('button later').html(_l('label.remind.later')); | |
| $laterButton.click(function() { | |
| $(':ui-dialog, .overlay').remove(); | |
| return false; | |
| }); | |
| $buttons.appendTo($confirm).append($.merge( | |
| $addAccountButton, $laterButton | |
| )); | |
| return $confirm; | |
| }); | |
| }, | |
| error: cloudStack.dialog.error(function() { | |
| $loading.remove(); | |
| }) | |
| } | |
| }); | |
| return false; | |
| }); | |
| $cancel.click(function() { | |
| $(':ui-dialog, .overlay').remove(); | |
| }); | |
| return $newProject | |
| .append( | |
| $form | |
| .append($formDesc) | |
| .append($projectName) | |
| .append($projectDesc) | |
| .append($cancel) | |
| .append($submit) | |
| ); | |
| }, | |
| /** | |
| * Project selection list | |
| */ | |
| selector: function(args) { | |
| var $selector = $('<div>').addClass('project-selector'); | |
| var $toolbar = $('<div>').addClass('toolbar'); | |
| var $list = $('<div>').addClass('listing') | |
| .append($('<div>').addClass('header').html(_l('label.name'))) | |
| .append($('<div>').addClass('data').append($('<ul>'))); | |
| var $searchForm = $('<form>'); | |
| var $search = $('<div>').appendTo($toolbar).addClass('search') | |
| .append( | |
| $searchForm | |
| .append($('<input>').attr({ type: 'text' })) | |
| .append($('<input>').attr({ type: 'submit' }).val('')) | |
| ); | |
| var $projectSelect = args.$projectSelect; | |
| var $cancel = $('<div>').addClass('button cancel').html(_l('label.cancel')); | |
| // Get project data | |
| var loadData = function(complete, options) { | |
| cloudStack.projects.dataProvider({ | |
| projectName: options ? options.projectName : null, | |
| context: cloudStack.context, | |
| response: { | |
| success: function(args) { | |
| var data = args.data; | |
| $projectSelect.find('option').remove(); | |
| $(data).each(function() { | |
| var displayText = this.displayText ? this.displayText : this.name; | |
| $('<li>') | |
| .data('json-obj', this) | |
| .html(_s(displayText)) | |
| .appendTo($list.find('ul')); | |
| // Populate project select | |
| $('<option>') | |
| .appendTo($projectSelect) | |
| .data('json-obj', this) | |
| .html(_s(displayText)); | |
| }); | |
| cloudStack.evenOdd($list, 'li', { | |
| even: function($elem) { | |
| $elem.addClass('even'); | |
| }, | |
| odd: function($elem) { | |
| $elem.addClass('odd'); | |
| } | |
| }); | |
| if (complete) complete(); | |
| } | |
| } | |
| }); | |
| }; | |
| // Search form | |
| $searchForm.submit(function() { | |
| $list.find('li').remove(); | |
| loadData(null, { | |
| projectName: $(this).find('input[type=text]').val() | |
| }); | |
| return false; | |
| }); | |
| //Cancel button | |
| $cancel.click(function() { | |
| $(':ui-dialog, .overlay').remove(); | |
| $('.select.default-view').click(); | |
| }); | |
| // Initial load | |
| loadData(function() { | |
| if (!$list.find('li').size()) { | |
| cloudStack.dialog.notice({ | |
| message: isAdmin() || isDomainAdmin() || g_userProjectsEnabled ? | |
| _l('message.no.projects') : | |
| _l('message.no.projects.adminOnly') | |
| }).closest('.ui-dialog'); | |
| $.merge($selector, $('.overlay')).remove(); | |
| $('.select.default-view').click(); | |
| } else { | |
| $selector.dialog({ | |
| title: _l('label.select.project'), | |
| dialogClass: 'project-selector-dialog', | |
| closeOnEscape: false , | |
| width: 420 | |
| }).closest('.ui-dialog').overlay(); | |
| } | |
| }); | |
| // Project item click event | |
| $selector.click(function(event) { | |
| var $target = $(event.target); | |
| if ($target.is('li')) { | |
| cloudStack.context.projects = [$target.data('json-obj')]; | |
| showDashboard(); | |
| var $switcher = $('.select.project-view'); | |
| var projectName = _s(cloudStack.context.projects[0].name); | |
| $switcher.attr('title', projectName); | |
| if (projectName.length > 10) { | |
| projectName = projectName.substr(0, 10).concat('...'); | |
| } | |
| // Put project name in header | |
| $switcher.html('<span class="icon"> </span>' + projectName); | |
| $.merge($selector, $('.overlay')).remove(); | |
| // Select active project | |
| $projectSelect | |
| .find('option').attr('selected', '') | |
| .filter(function() { | |
| return $(this).data('json-obj').name == _s(cloudStack.context.projects[0].name); | |
| }).attr('selected', 'selected'); | |
| //// | |
| // Hidden for now | |
| //$projectSelect.parent().show(); | |
| } | |
| }); | |
| return $selector | |
| .append($toolbar) | |
| .append($list) | |
| .append($cancel); | |
| } | |
| }; | |
| /** | |
| * Show project-mode appearance on CloudStack UI | |
| */ | |
| var applyProjectStyle = function() { | |
| var $container = $('#cloudStack3-container'); | |
| $container.addClass('project-view'); | |
| }; | |
| /** | |
| * Initiate new project flow | |
| */ | |
| var addProject = function() { | |
| pageElems.newProjectForm().dialog({ | |
| title: 'New Project', | |
| closeOnEscape: false, | |
| width: 760 | |
| }).closest('.ui-dialog').overlay(); | |
| }; | |
| /** | |
| * Show the dashboard, in panel | |
| */ | |
| var showDashboard = function() { | |
| var $browser = $('#browser .container'); | |
| applyProjectStyle($('html body')); | |
| // Cleanup project context | |
| if (cloudStack.context.projects) | |
| cloudStack.context.projects[0].isNew = false; | |
| $browser.cloudBrowser('removeAllPanels'); | |
| $browser.cloudBrowser('addPanel', { | |
| title: _l('label.project.dashboard'), | |
| complete: function($newPanel) { | |
| $('#navigation li.dashboard').addClass('active').siblings().removeClass('active'); | |
| $newPanel.append(pageElems.dashboard); | |
| } | |
| }); | |
| }; | |
| /** | |
| * Projects entry point | |
| */ | |
| cloudStack.uiCustom.projects = function(args) { | |
| var $dashboardNavItem = $('#navigation li.navigation-item.dashboard'); | |
| // Use project dashboard | |
| var event = function() { | |
| if (!$('#cloudStack3-container').hasClass('project-view')) { | |
| // No longer in project view, go back to normal dashboard | |
| $dashboardNavItem.unbind('click', event); | |
| return true; | |
| } | |
| $(this).addClass('active'); | |
| $(this).siblings().removeClass('active'); | |
| if (cloudStack.context.projects && cloudStack.context.projects[0]) { | |
| showDashboard(); | |
| } | |
| return false; | |
| }; | |
| $dashboardNavItem.bind('click', event); | |
| if (args.alreadySelected) { | |
| showDashboard(); | |
| } else { | |
| pageElems.selector(args); | |
| } | |
| }; | |
| /** | |
| * New project event | |
| */ | |
| $(window).bind('cloudStack.newProject', function() { | |
| addProject(); | |
| }); | |
| })(cloudStack, jQuery); |