diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index 90aa02a78f..6729032cc5 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -12,7 +12,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate, - ProjectStatus, FormatDate, Refresh, Wait, Stream, GetChoices, Empty) { + ProjectStatus, FormatDate, Refresh, Wait, Stream, GetChoices, Empty, Find) { ClearScope(); @@ -77,9 +77,11 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, $scope.projects[i].scm_type = $scope.project_scm_type_options[j].label; if ($scope.projects[i].scm_type === 'Manual') { $scope.projects[i].scm_update_tooltip = 'Manaul projects do not require an SCM update'; + $scope.projects[i].scm_schedule_tooltip = 'Manual projects do not require a schedule'; $scope.projects[i].scm_type_class = 'btn-disabled'; } else { $scope.projects[i].scm_update_tooltip = "Start an SCM update"; + $scope.projects[i].scm_schedule_tooltip = "Schedule future SCM updates"; $scope.projects[i].scm_type_class = ""; } break; @@ -339,11 +341,21 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, } } }; + + $scope.editSchedules = function(id) { + var project = Find({ list: $scope.projects, key: 'id', val: id }); + if (project.scm_type === "Manual" || Empty(project.scm_type)) { + // Nothing to do + } + else { + $location.path('/projects/' + id + '/schedules'); + } + }; } ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'GetBasePath', - 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh', 'Wait', 'Stream', 'GetChoices', 'Empty' + 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh', 'Wait', 'Stream', 'GetChoices', 'Empty', 'Find' ]; @@ -475,7 +487,7 @@ function ProjectsAdd($scope, $rootScope, $compile, $location, $log, $routeParams $rootScope.flashMessage = null; generator.reset(); for (fld in master) { - $scope.fld = master.fld; + $scope[fld] = master[fld]; } $scope.scmChange(); }; diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index 289b0e6ad2..ebfe5cc49b 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -185,25 +185,25 @@ angular.module('JobSubmissionHelper', ['RestServices', 'Utilities', 'CredentialF ]) .factory('SubmitJob', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'CredentialList', - 'LookUpInit', 'CredentialForm', 'ProcessErrors', 'JobTemplateForm', 'Wait', + 'LookUpInit', 'CredentialForm', 'ProcessErrors', 'JobTemplateForm', 'Wait', 'Empty', 'PromptForCredential', function (PromptPasswords, $compile, Rest, $location, GetBasePath, CredentialList, LookUpInit, CredentialForm, - ProcessErrors, JobTemplateForm, Wait) { + ProcessErrors, JobTemplateForm, Wait, Empty, PromptForCredential) { return function (params) { + var scope = params.scope, id = params.id, template_name = (params.template) ? params.template : null, base = $location.path().replace(/^\//, '').split('/')[0], url = GetBasePath(base) + id + '/'; - function postJob(data) { - var dt, url, name; - // Create the job record - if (scope.credentialWatchRemove) { - scope.credentialWatchRemove(); - } - dt = new Date().toISOString(); - url = (data.related.jobs) ? data.related.jobs : data.related.job_template + 'jobs/'; - name = (template_name) ? template_name : data.name; + if (scope.removePostTheJob) { + scope.removePostTheJob(); + } + scope.removePostTheJob = scope.$on('PostTheJob', function(e, data) { + var dt = new Date().toISOString(), + url = (data.related.jobs) ? data.related.jobs : data.related.job_template + 'jobs/', + name = (template_name) ? template_name : data.name; + Wait('start'); Rest.setUrl(url); Rest.post({ @@ -251,43 +251,26 @@ angular.module('JobSubmissionHelper', ['RestServices', 'Utilities', 'CredentialF ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to create job. POST returned status: ' + status }); }); + }); + + + if (scope.removePromptForCredential) { + scope.removePromptForCredential(); } + scope.removePromptForCredential = scope.$on('PromptForCredential', function(e, data) { + PromptForCredential({ scope: scope, template: data }); + }); // Get the job or job_template record Wait('start'); Rest.setUrl(url); Rest.get() .success(function (data) { - // Create a job record - scope.credential = ''; - if (data.credential === '' || data.credential === null) { - // Template does not have credential, prompt for one - Wait('stop'); - if (scope.credentialWatchRemove) { - scope.credentialWatchRemove(); - } - scope.credentialWatchRemove = scope.$watch('credential', function (newVal, oldVal) { - if (newVal !== oldVal) { - // After user selects a credential from the modal, - // submit the job - if (scope.credential !== '' && scope.credential !== null && scope.credential !== undefined) { - data.credential = scope.credential; - postJob(data); - } - } - }); - LookUpInit({ - scope: scope, - form: JobTemplateForm, - current_item: null, - list: CredentialList, - field: 'credential', - hdr: 'Credential Required' - }); - scope.lookUpCredential(); + if (Empty(data.credential)) { + scope.$emit('PromptForCredential', data); } else { // We have what we need, submit the job - postJob(data); + scope.$emit('PostTheJob'); } }) .error(function (data, status) { @@ -298,6 +281,38 @@ angular.module('JobSubmissionHelper', ['RestServices', 'Utilities', 'CredentialF } ]) +.factory('PromptForCredential', ['GetBasePath', 'LookUpInit', 'JobTemplateForm', 'CredentialList', 'Empty', +function(GetBasePath, LookUpInit, JobTemplateForm, CredentialList, Empty) { + return function(params) { + + var scope = params.scope, + template = params.template, + launchJob; + + scope.credential = ''; + + launchJob = function () { + if (!Empty(scope.credential)) { + template.credential = scope.credential; + scope.$emit('PostTheJob', template); + } + }; + + LookUpInit({ + url: GetBasePath('credentials') + '?kind=ssh', + scope: scope, + form: JobTemplateForm, + current_item: null, + list: CredentialList, + field: 'credential', + hdr: 'Credential Required', + instructions: "Launching this job requires a machine credential. Please select your machine credential now or Cancel to quit.", + postAction: launchJob + }); + scope.lookUpCredential(); + }; +}]) + // Sumbit SCM Update request .factory('ProjectUpdate', ['PromptPasswords', '$compile', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert', 'ProjectsForm', 'Wait', diff --git a/awx/ui/static/js/helpers/Lookup.js b/awx/ui/static/js/helpers/Lookup.js index 3b0407c7cb..a830a8eabb 100644 --- a/awx/ui/static/js/helpers/Lookup.js +++ b/awx/ui/static/js/helpers/Lookup.js @@ -16,156 +16,204 @@ 'use strict'; -angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'ApiLoader']) - .factory('LookUpInit', ['Alert', 'Rest', 'GenerateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', - function (Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) { - return function (params) { +angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'ApiLoader', 'ModalDialog']) - var scope = params.scope, - form = params.form, - list = params.list, - field = params.field, - postAction = params.postAction, - defaultUrl, name, hdr, watchUrl; + .factory('LookUpInit', ['Alert', 'Rest', 'GenerateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', 'CreateDialog', + function (Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty, CreateDialog) { + return function (params) { - if (params.url) { - // pass in a url value to override the default - defaultUrl = params.url; - } else { - defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name); - } + var parent_scope = params.scope, + form = params.form, + list = params.list, + field = params.field, + instructions = params.instructions, + postAction = params.postAction, + defaultUrl, name, watchUrl; + + if (params.url) { + // pass in a url value to override the default + defaultUrl = params.url; + } else { + defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name); + } + + if ($('#htmlTemplate #lookup-modal-dialog').length > 0) { + $('#htmlTemplate #lookup-modal-dialog').empty(); + } + else { + $('#htmlTemplate').append("
"); + } + + name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1); + + watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&'; + watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value'; + + $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl); + $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field); + + + parent_scope['lookUp' + name] = function () { + + var master = {}, + scope = parent_scope.$new(), + name, hdr, buttons; + + // Generating the search list potentially kills the values held in scope for the field. + // We'll keep a copy in master{} that we can revert back to on cancel; + master[field] = scope[field]; + master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = + scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]; + + GenerateList.inject(list, { + mode: 'lookup', + id: 'lookup-modal-dialog', + scope: scope, + instructions: instructions + }); - // Show pop-up name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1); hdr = (params.hdr) ? params.hdr : 'Select ' + name; - watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&'; - watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value'; - - $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl); - $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field); - - scope['lookUp' + name] = function () { - - var master = {}, listGenerator, listScope; - - // Generating the search list potentially kills the values held in scope for the field. - // We'll keep a copy in master{} that we can revert back to on cancel; - master[field] = scope[field]; - master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]; - - listGenerator = GenerateList; - listScope = listGenerator.inject(list, { mode: 'lookup', hdr: hdr }); - - $('#lookup-modal').on('hidden.bs.modal', function () { - // Restore search settings - if (listScope.searchCleanup) { - listScope.searchCleanup(); - } - // If user clicks cancel without making a selection, restore original values - if (Empty(scope[field])) { - scope[field] = master[field]; - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = - master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]; - } - }); - - listScope.selectAction = function () { - - var i, found = false; - for (i = 0; i < listScope[list.name].length; i++) { - if (listScope[list.name][i].checked === '1') { - found = true; - scope[field] = listScope[list.name][i].id; - if (scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) { - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = - listScope[list.name][i][form.fields[field].sourceField]; - if (scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) { - scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField] - .$setValidity('awlookup', true); - } - } - if (scope[form.name + '_form']) { - scope[form.name + '_form'].$setDirty(); - } - listGenerator.hide(); - } - } - if (found === false) { - Alert('Missing Selection', 'Oops, you failed to make a selection. Click on a row to make your selection, ' + - 'and then click the Select button.'); - } else { - if (postAction) { - postAction(); - } - } - }; - - listScope['toggle_' + list.iterator] = function (id) { - var i; - for (i = 0; i < listScope[list.name].length; i++) { - if (listScope[list.name][i].id === id) { - listScope[list.name][i].checked = '1'; - listScope[list.name][i].success_class = 'success'; - } else { - listScope[list.name][i].checked = '0'; - listScope[list.name][i].success_class = ''; - } - } - }; - - SearchInit({ - scope: listScope, - set: list.name, - list: list, - url: defaultUrl - }); - PaginateInit({ - scope: listScope, - list: list, - url: defaultUrl, - mode: 'lookup' - }); - - // If user made a selection previously, mark it as selected when modal loads - if (listScope.lookupPostRefreshRemove) { - listScope.lookupPostRefreshRemove(); + // Show pop-up + buttons = [{ + label: "Cancel", + icon: "fa-times", + "class": "btn btn-default", + "id": "lookup-cancel-button", + onClick: function() { + $('#lookup-modal-dialog').dialog('close'); } - listScope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () { - var fld, i; - for (fld in list.fields) { - if (list.fields[fld].type && list.fields[fld].type === 'date') { - //convert dates to our standard format - for (i = 0; i < scope[list.name].length; i++) { - scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld])); + },{ + label: "Select", + onClick: function() { + scope.selectAction(); + }, + icon: "fa-check", + "class": "btn btn-primary", + "id": "lookup-save-button" + }]; + + if (scope.removeModalReady) { + scope.removeModalReady(); + } + scope.removeModalReady = scope.$on('ModalReady', function() { + $('#lookup-modal-dialog').dialog('open'); + }); + + CreateDialog({ + scope: scope, + buttons: buttons, + width: 600, + height: (instructions) ? 625 : 500, + minWidth: 500, + title: hdr, + id: 'lookup-modal-dialog', + onClose: function() { + setTimeout( function() { + scope.$apply( function() { + if (Empty(scope[field])) { + scope[field] = master[field]; + scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = + master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]; } + }); + }, 300); + }, + callback: 'ModalReady' + }); + + SearchInit({ + scope: scope, + set: list.name, + list: list, + url: defaultUrl + }); + + PaginateInit({ + scope: scope, + list: list, + url: defaultUrl, + mode: 'lookup' + }); + + if (scope.lookupPostRefreshRemove) { + scope.lookupPostRefreshRemove(); + } + scope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () { + var fld, i; + for (fld in list.fields) { + if (list.fields[fld].type && list.fields[fld].type === 'date') { + //convert dates to our standard format + for (i = 0; i < scope[list.name].length; i++) { + scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld])); } } + } - // List generator creates the form, resetting it and losing the previously selected value. - // If it's in the current set, find it and marke it as selected. - if (scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== '' && - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] !== null) { - for (i = 0; i < listScope[list.name].length; i++) { - if (listScope[list.name][i][form.fields[field].sourceField] === - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) { - scope[field] = listScope[list.name][i].id; - break; + // List generator creates the list, resetting it and losing the previously selected value. + // If the selected value is in the current set, find it and mark selected. + if (!Empty(parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField])) { + scope[list.name].forEach(function(elem) { + if (elem[form.fields[field].sourceField] === + parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) { + scope[field] = elem.id; + } + }); + + } + + if (!Empty(scope[field])) { + scope['toggle_' + list.iterator](scope[field]); + } + + }); + + scope.search(list.iterator); + + scope.selectAction = function () { + var i, found = false; + for (i = 0; i < scope[list.name].length; i++) { + if (scope[list.name][i].checked === '1') { + found = true; + parent_scope[field] = scope[list.name][i].id; + if (parent_scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) { + parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] = + scope[list.name][i][form.fields[field].sourceField]; + if (parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) { + parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField] + .$setValidity('awlookup', true); } } - + if (parent_scope[form.name + '_form']) { + parent_scope[form.name + '_form'].$setDirty(); + } } - - if (!Empty(scope[field])) { - listScope['toggle_' + list.iterator](scope[field]); + } + if (found === false) { + Alert('Missing Selection', 'Oops, you failed to make a selection. Click on a row to make your selection, ' + + 'and then click the Select button.'); + } else { + $('#lookup-modal-dialog').dialog('close'); + if (postAction) { + postAction(); } + } + }; - }); - - listScope.search(list.iterator); + scope['toggle_' + list.iterator] = function (id) { + var i; + for (i = 0; i < scope[list.name].length; i++) { + if (scope[list.name][i].id === id) { + scope[list.name][i].checked = '1'; + scope[list.name][i].success_class = 'success'; + } else { + scope[list.name][i].checked = '0'; + scope[list.name][i].success_class = ''; + } + } }; }; - } - ]); + }; + }]); diff --git a/awx/ui/static/js/lists/Credentials.js b/awx/ui/static/js/lists/Credentials.js index 3e649127c3..829b46b040 100644 --- a/awx/ui/static/js/lists/Credentials.js +++ b/awx/ui/static/js/lists/Credentials.js @@ -29,7 +29,7 @@ angular.module('CredentialsListDefinition', []) }, description: { label: 'Description', - excludeModal: false + excludeModal: true }, kind: { label: 'Type', diff --git a/awx/ui/static/js/lists/Projects.js b/awx/ui/static/js/lists/Projects.js index 7309dbd2fd..642c85fa78 100644 --- a/awx/ui/static/js/lists/Projects.js +++ b/awx/ui/static/js/lists/Projects.js @@ -106,8 +106,9 @@ angular.module('ProjectsListDefinition', []) }, schedule: { mode: 'all', - ngHref: '#/projects/{{ project.id }}/schedules', - awToolTip: 'Schedule future SCM updates', + ngClick: "editSchedules(project.id)", + awToolTip: "{{ project.scm_schedule_tooltip }}", + ngClass: "project.scm_type_class", dataPlacement: 'top' }, edit: { diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 853e2be5d2..6d4f924322 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -657,6 +657,11 @@ legend { } +#lookup-modal-dialog .instructions { + margin-top: 0; + margin-bottom: 20px; +} + .related-footer { margin: 10px 0 0 0; } @@ -1053,15 +1058,11 @@ input[type="checkbox"].checkbox-no-label { font-weight: normal; line-height: 1; } - + + .job-list.ui-accordion-content { + padding: 25px 15px 25px 15px; + } .job-list { - margin-top: 20px; - .title { - margin-left: 3px; - font-weight: bold; - margin-bottom: 6px; - color: #666; - } thead >tr >th, .page-row { font-size: 12px; color: #666; diff --git a/awx/ui/static/lib/ansible/directives.js b/awx/ui/static/lib/ansible/directives.js index 1776ba7f80..b805ceddab 100644 --- a/awx/ui/static/lib/ansible/directives.js +++ b/awx/ui/static/lib/ansible/directives.js @@ -629,4 +629,14 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job }); } }; - }]); + }]) + + + .directive('awAccordion', function() { + return function(scope, element) { + $(element).accordion({ + collapsible: true, + heightStyle: "content" + }); + }; + }); diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index ecf73bec33..eb2a8b58e7 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -49,9 +49,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) // var element; - if (options.mode === 'lookup') { - element = angular.element(document.getElementById('lookup-modal-body')); - } else if (options.id) { + if (options.id) { element = angular.element(document.getElementById(options.id)); } else { element = angular.element(document.getElementById('htmlTemplate')); @@ -97,7 +95,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) //ignore any errors should the dialog not be initialized } - if (options.mode === 'lookup') { + /*if (options.mode === 'lookup') { // options should include {hdr: , action: } this.scope.formModalActionDisabled = false; this.scope.lookupHeader = options.hdr; @@ -111,7 +109,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) $('#lookup-modal').modal('hide'); } }); - } + }*/ return this.scope; }, @@ -151,6 +149,13 @@ angular.module('ListGenerator', ['GeneratorHelpers']) html += "\n"; } + if (options.instructions) { + html += "
" + options.instructions + "
\n"; + } + else if (list.instructions) { + html += "
" + list.instructions + "
\n"; + } + if (options.mode !== 'lookup' && (list.well === undefined || list.well)) { html += "
\n"; } @@ -164,7 +169,6 @@ angular.module('ListGenerator', ['GeneratorHelpers']) html += "
\n"; } - if (options.showSearch=== undefined || options.showSearch === true) { html += "
\n"; if (list.name !== 'groups') { diff --git a/awx/ui/static/partials/jobs.html b/awx/ui/static/partials/jobs.html index bc51d27b7e..8020a7215e 100644 --- a/awx/ui/static/partials/jobs.html +++ b/awx/ui/static/partials/jobs.html @@ -5,22 +5,30 @@
-
-
-
Completed
-
+
+
+

Completed

+
+
+
-
-
Active
-
+
+

Active

+
+
+
-
-
Queued
-
+
+

Queued

+
+
+
-
-
Scheduled
-
+
+

Scheduled

+
+
+
diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index a2bb8d1609..0e3919562a 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -299,24 +299,6 @@
- - -