diff --git a/app/assets/javascripts/controllers/ansible_repository/repository_form_controller.js b/app/assets/javascripts/controllers/ansible_repository/repository_form_controller.js new file mode 100644 index 00000000000..fef99810cdb --- /dev/null +++ b/app/assets/javascripts/controllers/ansible_repository/repository_form_controller.js @@ -0,0 +1,123 @@ +ManageIQ.angular.app.controller('repositoryFormController', ['$scope', 'repositoryId', 'miqService', 'API', function($scope, repositoryId, miqService, API) { + var vm = this; + + var init = function() { + vm.afterGet = false; + + vm.repositoryModel = { + name: '', + description: '', + scm_type: 'git', + manager_resource: {}, + scm_url: '', + authentication_id: null, + scm_branch: '', + scm_clean: false, + scm_delete_on_update: false, + scm_update_on_launch: false, + }; + + API.get('/api/providers?collection_class=ManageIQ::Providers::EmbeddedAutomationManager') + .then(getManagerResource) + .catch(miqService.handleFailure); + + vm.model = 'repositoryModel'; + + ManageIQ.angular.scope = vm; + + $scope.newRecord = repositoryId === 'new'; + + vm.scm_credentials = [{name: __('Select credentials'), value: null}]; + API.get('/api/authentications?collection_class=ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ScmCredential&expand=resources&sort_by=name&sort_order=ascending') + .then(getCredentials) + .catch(miqService.handleFailure); + + if (repositoryId !== 'new') { + API.get('/api/configuration_script_sources/' + repositoryId) + .then(getRepositoryFormData) + .catch(miqService.handleFailure); + } else { + vm.afterGet = true; + vm.modelCopy = angular.copy( vm.repositoryModel ); + miqService.sparkleOff(); + } + }; + + $scope.cancelClicked = function() { + miqService.sparkleOn(); + var message = $scope.newRecord ? __('Add of Repository cancelled by user.') : sprintf(__('Edit of Repository \"%s\" cancelled by user.'), vm.repositoryModel.name); + var url = '/ansible_repository/show_list' + '?flash_msg=' + message + '&escape=true&flash_warning=true&flash_error=false'; + window.location.href = url; + }; + + $scope.resetClicked = function() { + vm.repositoryModel = angular.copy( vm.modelCopy ); + $scope.angularForm.$setPristine(true); + miqService.miqFlash('warn', __('All changes have been reset')); + }; + + $scope.saveClicked = function() { + miqService.sparkleOn(); + API.put('/api/configuration_script_sources/' + repositoryId, vm.repositoryModel) + .then(getBack) + .catch(miqService.handleFailure); + }; + + $scope.addClicked = function() { + miqService.sparkleOn(); + API.post('/api/configuration_script_sources/', vm.repositoryModel) + .then(getBack) + .catch(miqService.handleFailure); + }; + + var getRepositoryFormData = function(response) { + var data = response; + Object.assign(vm.repositoryModel, data); + vm.modelCopy = angular.copy( vm.repositoryModel ); + vm.afterGet = true; + miqService.sparkleOff(); + }; + + var getBack = function(response) { + var message = ''; + var error = false; + if (response.hasOwnProperty('results')) { + error = ! response.results[0].success; + if (error) { + message = __('Unable to add Repository ') + vm.repositoryModel.name + ' .' + response.results[0].message; + } else { + message = sprintf(__('Add of Repository \"%s\" was successfully initialized.'), vm.repositoryModel.name); + } + } else { + error = ! response.success; + if (error) { + message = __('Unable to edit Repository') + vm.repositoryModel.name + ' .' + response.message; + } else { + message = sprintf(__('Edit of Repository \"%s\" was successfully initialized.'), vm.repositoryModel.name); + } + } + var url = '/ansible_repository/show_list' + '?flash_msg=' + message + '&escape=true'; + if (error) { + miqService.miqFlash('error', message); + miqService.sparkleOff(); + } else { + window.location.href = url; + } + }; + + var getCredentials = function(response) { + response.resources.forEach( function(resource) { + vm.scm_credentials.push({name: resource.name, value: resource.id}); + }); + }; + + var getManagerResource = function(response) { + if (!response.resources.length) { + miqService.miqFlash('error', __('Embedded Ansible Provider not found.')); + } else { + vm.repositoryModel.manager_resource = {'href': response.resources[0].href}; + } + }; + init(); +}]); + diff --git a/app/assets/javascripts/directives/url_validation.js b/app/assets/javascripts/directives/url_validation.js new file mode 100644 index 00000000000..e44fa241cbf --- /dev/null +++ b/app/assets/javascripts/directives/url_validation.js @@ -0,0 +1,17 @@ +ManageIQ.angular.app.directive('urlValidation', function() { + return { + require: 'ngModel', + link: function (_scope, _elem, _attrs, ctrl) { + ctrl.$validators.urlValidation = function (modelValue, viewValue) { + if (!viewValue) { + return true; + } + return validUrl(viewValue); + }; + + var validUrl = function(s) { + return s.substring(0, 8) === 'https://' || s.substring(0, 7) === 'http://'; + }; + } + } +}); diff --git a/app/controllers/ansible_repository_controller.rb b/app/controllers/ansible_repository_controller.rb index 519e9066e2e..7eb20d4cb6d 100644 --- a/app/controllers/ansible_repository_controller.rb +++ b/app/controllers/ansible_repository_controller.rb @@ -10,6 +10,7 @@ class AnsibleRepositoryController < ApplicationController include Mixins::GenericShowMixin menu_section :ansible + toolbar :ansible_repository def self.display_methods %w(playbooks) @@ -19,6 +20,57 @@ def self.model ManageIQ::Providers::EmbeddedAutomationManager::ConfigurationScriptSource end + def title + _("Repository") + end + + def button + if params[:pressed] == "embedded_configuration_script_source_edit" + id = from_cid(params[:miq_grid_checks]) + javascript_redirect :action => 'edit', :id => id + elsif params[:pressed] == "embedded_configuration_script_source_add" + javascript_redirect :action => 'new' + elsif params[:pressed] == "embedded_configuration_script_source_delete" + delete_repositories + end + end + + def delete_repositories + assert_privileges('embedded_configuration_script_source_delete') + checked = find_checked_items + checked[0] = params[:id] if checked.blank? && params[:id] + AnsibleRepositoryController.model.where(:id => checked).each do |repo| + begin + repo.delete_in_provider_queue + add_flash(_("Delete of Repository \"%{name}\" was successfully initiated.") % {:name => repo.name}) + rescue => ex + add_flash(_("Unable to delete Repository \"%{name}\": %{details}") % {:name => repo.name, + :details => ex}, + :error) + end + end + session[:flash_msgs] = @flash_array + javascript_redirect :action => 'show_list' + end + + def edit + assert_privileges('embedded_configuration_script_source_edit') + @record = AnsibleRepositoryController.model.find(params[:id]) + drop_breadcrumb(:name => _("Edit a Repository \"%{name}\"") % {:name => @record.name}, + :url => "/ansible_repository/edit/#{@record.id}") + @title = _("Edit Repository \"%{name}\"") % {:name => @record.name} + @id = @record.id + @in_a_form = true + end + + def new + assert_privileges('embedded_configuration_script_source_add') + drop_breadcrumb(:name => _("Add a new Repository"), :url => "/ansible_repository/new") + @title = _("Add new Repository") + @id = 'new' + @in_a_form = true + end + def display_playbooks nested_list("ansible_playbook", ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook) end diff --git a/app/helpers/application_helper/toolbar/ansible_repositories_center.rb b/app/helpers/application_helper/toolbar/ansible_repositories_center.rb new file mode 100644 index 00000000000..9cca40087ee --- /dev/null +++ b/app/helpers/application_helper/toolbar/ansible_repositories_center.rb @@ -0,0 +1,35 @@ +class ApplicationHelper::Toolbar::AnsibleRepositoriesCenter < ApplicationHelper::Toolbar::Basic + button_group('ansible_repositories', [ + select( + :ansible_repositories_configuration, + 'fa fa-cog fa-lg', + t = N_('Configuration'), + t, + :items => [ + button( + :embedded_configuration_script_source_add, + 'pficon pficon-edit fa-lg', + t = N_('Add New Repository'), + t, + :url_parms => "unused_div"), + button( + :embedded_configuration_script_source_edit, + 'pficon pficon-edit fa-lg', + t = N_('Edit this Repository'), + t, + :enabled => false, + :onwhen => "1", + :url_parms => "unused_div"), + button( + :embedded_configuration_script_source_delete, + 'pficon pficon-delete fa-lg', + t = N_('Remove selected Repositories'), + t, + :url_parms => "unused_div", + :enabled => false, + :onwhen => "1+", + :confirm => N_("Warning: The selected Repository will be permanently removed!")), + ] + ) + ]) +end diff --git a/app/helpers/application_helper/toolbar/ansible_repository_center.rb b/app/helpers/application_helper/toolbar/ansible_repository_center.rb new file mode 100644 index 00000000000..ee290467f85 --- /dev/null +++ b/app/helpers/application_helper/toolbar/ansible_repository_center.rb @@ -0,0 +1,25 @@ +class ApplicationHelper::Toolbar::AnsibleRepositoryCenter < ApplicationHelper::Toolbar::Basic + button_group('ansible_repository', [ + select( + :ansible_repository_configuration, + 'fa fa-cog fa-lg', + t = N_('Configuration'), + t, + :items => [ + button( + :embedded_configuration_script_source_edit, + 'pficon pficon-edit fa-lg', + t = N_('Edit this Repository'), + t, + :url => "/edit"), + button( + :embedded_configuration_script_source_delete, + 'pficon pficon-delete fa-lg', + t = N_('Remove this Repository'), + t, + :url_parms => "&refresh=y", + :confirm => N_("Warning: The selected Repository will be permanently removed!")), + ] + ), + ]) +end diff --git a/app/views/ansible_repository/_repository_form.html.haml b/app/views/ansible_repository/_repository_form.html.haml new file mode 100644 index 00000000000..dc53f9b0cf3 --- /dev/null +++ b/app/views/ansible_repository/_repository_form.html.haml @@ -0,0 +1,97 @@ +%br +%br +.form-horizontal + %form#form_div{:name => "angularForm", + 'ng-controller' => "repositoryFormController as vm", + 'ng-show' => "vm.afterGet", + 'ng-cloak' => '', + "miq-form" => true, + "model" => "vm.repositoryModel", + "model-copy" => 'vm.modelCopy', + :novalidate => true} + = render :partial => "layouts/flash_msg" + .form-group{"ng-class" => "{'has-error': angularForm.name.$invalid}"} + %label.col-md-2.control-label + = _('Name') + .col-md-8 + %input.form-control{:type => "text", + :name => "name", + "id" => "name", + 'ng-model' => "vm.repositoryModel.name", + :maxlength => MAX_NAME_LEN, + :required => "", + :checkchange => true} + %span.help-block{"ng-show" => "angularForm.name.$error.required"} + = _("Required") + .form-group + %label.col-md-2.control-label + = _('Description') + .col-md-8 + %input.form-control{:type => "text", + :name => "description", + "id" => "description", + 'ng-model' => "vm.repositoryModel.description", + :checkchange => true} + .form-group + %label.col-md-2.control-label + = _('SCM type') + .col-md-8 + = select_tag('scm_type', + options_for_select([[_("GIT"), "git"]], 'git'), + "ng-model" => "vm.repositoryModel.scm_type", + :disabled => true, + :checkchange => true, + 'pf-select' => true) + .form-group{"ng-class" => "{'has-error': angularForm.scm_url.$error.required || angularForm.scm_url.$error.urlValidation}"} + %label.col-md-2.control-label + = _('URL') + .col-md-8 + %input.form-control{:type => "text", + :name => "scm_url", + :id => "scm_url", + 'ng-model' => "vm.repositoryModel.scm_url", + :required => "", + :checkchange => true, + 'url-validation' => true} + %span.help-block{"ng-show" => "angularForm.scm_url.$error.required"} + = _("Required") + %span.help-block{"ng-show" => "angularForm.scm_url.$error.urlValidation"} + = _("URL must include a protocol (http:// or https://)") + .form-group + %label.col-md-2.control-label + = _('SCM credentials') + .col-md-8 + %select{'name' => 'authentication_id', + 'ng-model' => 'vm.repositoryModel.authentication_id', + 'ng-options' => 'scm_credential.value as scm_credential.name for scm_credential in vm.scm_credentials', + :checkchange => true, + 'pf-select' => true} + .form-group + %label.col-md-2.control-label + = _('SCM Branch') + .col-md-8 + %input.form-control{:type => "text", + :name => "scm_branch", + "id" => "scm_branch", + 'ng-model' => "vm.repositoryModel.scm_branch", + :checkchange => true} + .form-group + %label.col-md-2.control-label + = _('SCM Update Options') + .col-md-8 + %div + %label + = check_box_tag("clean", "1", false, 'ng-model' => "vm.repositoryModel.scm_clean") + = _('Clean') + %div + %label + = check_box_tag("scm_delete_on_update", "1", false, 'ng-model' => 'vm.repositoryModel.scm_delete_on_update') + = _('Delete on Update') + %div + %label + = check_box_tag("scm_update_on_launch", "1", false, 'ng-model' => 'vm.repositoryModel.scm_update_on_launch') + = _('Update on Launch') + = render :partial => "layouts/angular/x_edit_buttons_angular" +:javascript + ManageIQ.angular.app.value('repositoryId', '#{@id}'); + miq_bootstrap('#form_div'); diff --git a/app/views/ansible_repository/edit.html.haml b/app/views/ansible_repository/edit.html.haml new file mode 100644 index 00000000000..44c829a0672 --- /dev/null +++ b/app/views/ansible_repository/edit.html.haml @@ -0,0 +1,2 @@ +#main_div + = render :partial => 'repository_form' diff --git a/app/views/ansible_repository/new.html.haml b/app/views/ansible_repository/new.html.haml new file mode 100644 index 00000000000..44c829a0672 --- /dev/null +++ b/app/views/ansible_repository/new.html.haml @@ -0,0 +1,2 @@ +#main_div + = render :partial => 'repository_form' diff --git a/config/routes.rb b/config/routes.rb index 1cfcda86f9b..854394af2d3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2032,11 +2032,17 @@ :get => %w( download_data download_summary_pdf + edit + new show show_list ), :post => %w( - show_list) + button + edit + new + show_list + ) }, :miq_ae_class => {