diff options
author | Matthew Jones <matburt@redhat.com> | 2016-07-19 03:16:01 +0200 |
---|---|---|
committer | Matthew Jones <matburt@redhat.com> | 2016-07-19 03:16:01 +0200 |
commit | 7b1fd964e3bb1deb62b22c977fddd362da7fac19 (patch) | |
tree | 5469f011471ba5c79c0c8df13df3ff968a91476a | |
parent | Merge branch 'release_3.0.0' into devel (diff) | |
parent | remove aws ask at runtime prompt from cred form config, resolves #3055 (#3058) (diff) | |
download | awx-7b1fd964e3bb1deb62b22c977fddd362da7fac19.tar.xz awx-7b1fd964e3bb1deb62b22c977fddd362da7fac19.zip |
Merge branch 'release_3.0.0' into devel
* release_3.0.0: (22 commits)
remove aws ask at runtime prompt from cred form config, resolves #3055 (#3058)
fix missing URI encoding in event summary serch, kickback on #2980 (#3050)
Switch base class for StateConflict
Fixed password show/hide on enter for survey maker password type previews
Fixed password show/hide on enter for survey taker survey questions where type is password
Prevent populate_user from being registered multiple times.
Fixing iterator used when jobs list refreshes
Explicit super user check for JT can_delete
Fix for populating teams for LDAP user.
Update hubspot template for marketting
Fix up flake8
Rolled back the onExit solution previously implemented to handle the backspace navigation on the job launch modal. New solution listens for state changes within the directive and cleans itself up.
Switch disallowed object delete to 409
Password enter show/hide fix
add test for CustomInventoryScript serializer
Fixed bug where hitting enter in a password field in the job launch/survey maker modal would toggle the show/hide.
Setting the local var CredentialList to the deep clone seems to be problematic. Moving this out so that the original object itself is overwritten which is how it's done in other places. (#3017)
Jobs list page size (#3019)
resolves kickback on #2980 (#3008)
add read_role to organization select_related
...
27 files changed, 129 insertions, 144 deletions
diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 1487fe17e3..679d23aeee 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1283,7 +1283,9 @@ class CustomInventoryScriptSerializer(BaseSerializer): if obj is None: return ret request = self.context.get('request', None) - if request.user not in obj.admin_role: + if request.user not in obj.admin_role and \ + not request.user.is_superuser and \ + not request.user.is_system_auditor: ret['script'] = None return ret diff --git a/awx/api/views.py b/awx/api/views.py index 64ea980f66..11ae8f6766 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -653,7 +653,7 @@ class OrganizationList(OrganizationCountsMixin, ListCreateAPIView): def get_queryset(self): qs = Organization.accessible_objects(self.request.user, 'read_role') - qs = qs.select_related('admin_role', 'auditor_role', 'member_role') + qs = qs.select_related('admin_role', 'auditor_role', 'member_role', 'read_role') return qs def create(self, request, *args, **kwargs): @@ -2184,14 +2184,6 @@ class JobTemplateDetail(RetrieveUpdateDestroyAPIView): serializer_class = JobTemplateSerializer always_allow_superuser = False - def destroy(self, request, *args, **kwargs): - obj = self.get_object() - can_delete = request.user.can_access(JobTemplate, 'delete', obj) - if not can_delete: - raise PermissionDenied("Cannot delete job template.") - return super(JobTemplateDetail, self).destroy(request, *args, **kwargs) - - class JobTemplateLaunch(RetrieveAPIView, GenericAPIView): model = JobTemplate diff --git a/awx/main/access.py b/awx/main/access.py index af05c58be6..5b2ee91851 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -25,7 +25,7 @@ from awx.main.conf import tower_settings __all__ = ['get_user_queryset', 'check_user_access', 'user_accessible_objects', - 'user_admin_role',] + 'user_admin_role', 'StateConflict',] PERMISSION_TYPES = [ PERM_INVENTORY_ADMIN, @@ -57,6 +57,8 @@ access_registry = { # ... } +class StateConflict(ValidationError): + status_code = 409 def register_access(model_class, access_class): access_classes = access_registry.setdefault(model_class, []) @@ -315,11 +317,15 @@ class OrganizationAccess(BaseAccess): if not is_change_possible: return False active_jobs = [] - active_jobs.extend(Job.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)) - active_jobs.extend(ProjectUpdate.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)) - active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__inventory__organization=obj, status__in=ACTIVE_STATES)) + active_jobs.extend([dict(type="job", id=o.id) + for o in Job.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)]) + active_jobs.extend([dict(type="project_update", id=o.id) + for o in ProjectUpdate.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)]) + active_jobs.extend([dict(type="inventory_update", id=o.id) + for o in InventoryUpdate.objects.filter(inventory_source__inventory__organization=obj, status__in=ACTIVE_STATES)]) if len(active_jobs) > 0: - raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs))) + raise StateConflict({"conflict": "Resource is being used by running jobs", + "active_jobs": active_jobs}) return True class InventoryAccess(BaseAccess): @@ -387,10 +393,13 @@ class InventoryAccess(BaseAccess): if not is_can_admin: return False active_jobs = [] - active_jobs.extend(Job.objects.filter(inventory=obj, status__in=ACTIVE_STATES)) - active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__inventory=obj, status__in=ACTIVE_STATES)) + active_jobs.extend([dict(type="job", id=o.id) + for o in Job.objects.filter(inventory=obj, status__in=ACTIVE_STATES)]) + active_jobs.extend([dict(type="inventory_update", id=o.id) + for o in InventoryUpdate.objects.filter(inventory_source__inventory=obj, status__in=ACTIVE_STATES)]) if len(active_jobs) > 0: - raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs))) + raise StateConflict({"conflict": "Resource is being used by running jobs", + "active_jobs": active_jobs}) return True def can_run_ad_hoc_commands(self, obj): @@ -508,9 +517,11 @@ class GroupAccess(BaseAccess): if not is_delete_allowed: return False active_jobs = [] - active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__in=obj.inventory_sources.all(), status__in=ACTIVE_STATES)) + active_jobs.extend([dict(type="inventory_update", id=o.id) + for o in InventoryUpdate.objects.filter(inventory_source__in=obj.inventory_sources.all(), status__in=ACTIVE_STATES)]) if len(active_jobs) > 0: - raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs))) + raise StateConflict({"conflict": "Resource is being used by running jobs", + "active_jobs": active_jobs}) return True class InventorySourceAccess(BaseAccess): @@ -765,10 +776,13 @@ class ProjectAccess(BaseAccess): if not is_change_allowed: return False active_jobs = [] - active_jobs.extend(Job.objects.filter(project=obj, status__in=ACTIVE_STATES)) - active_jobs.extend(ProjectUpdate.objects.filter(project=obj, status__in=ACTIVE_STATES)) + active_jobs.extend([dict(type="job", id=o.id) + for o in Job.objects.filter(project=obj, status__in=ACTIVE_STATES)]) + active_jobs.extend([dict(type="project_update", id=o.id) + for o in ProjectUpdate.objects.filter(project=obj, status__in=ACTIVE_STATES)]) if len(active_jobs) > 0: - raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs))) + raise StateConflict({"conflict": "Resource is being used by running jobs", + "active_jobs": active_jobs}) return True @check_superuser @@ -989,14 +1003,15 @@ class JobTemplateAccess(BaseAccess): return True - @check_superuser def can_delete(self, obj): - is_delete_allowed = self.user in obj.admin_role + is_delete_allowed = self.user.is_superuser or self.user in obj.admin_role if not is_delete_allowed: return False - active_jobs = obj.jobs.filter(status__in=ACTIVE_STATES) + active_jobs = [dict(type="job", id=o.id) + for o in obj.jobs.filter(status__in=ACTIVE_STATES)] if len(active_jobs) > 0: - raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs))) + raise StateConflict({"conflict": "Resource is being used by running jobs", + "active_jobs": active_jobs}) return True class JobAccess(BaseAccess): diff --git a/awx/main/migrations/0026_v300_credential_unique.py b/awx/main/migrations/0026_v300_credential_unique.py index b354ce3d62..3c1d714327 100644 --- a/awx/main/migrations/0026_v300_credential_unique.py +++ b/awx/main/migrations/0026_v300_credential_unique.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='credential', name='read_role', - field=awx.main.fields.ImplicitRoleField(related_name='+', parent_role=[b'singleton:system_auditor', b'use_role', b'admin_role', b'organization.auditor_role'], to='main.Role', null=b'True'), + field=awx.main.fields.ImplicitRoleField(related_name='+', parent_role=[b'singleton:system_auditor', b'organization.auditor_role', b'use_role', b'admin_role'], to='main.Role', null=b'True'), ), migrations.RunPython(migration_utils.set_current_apps_for_migrations), migrations.RunPython(rbac.rebuild_role_hierarchy), diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index 14c92c4a54..cab2e53731 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -335,3 +335,15 @@ def test_jt_without_project(inventory): data["job_type"] = "scan" serializer = JobTemplateSerializer(data=data) assert serializer.is_valid() + +@pytest.mark.django_db +def test_disallow_template_delete_on_running_job(job_template_factory, delete, admin_user): + objects = job_template_factory('jt', + credential='c', + job_type="run", + project='p', + inventory='i', + organization='o') + objects.job_template.create_unified_job() + delete_response = delete(reverse('api:job_template_detail', args=[objects.job_template.pk]), user=admin_user) + assert delete_response.status_code == 409 diff --git a/awx/main/tests/unit/api/test_serializers.py b/awx/main/tests/unit/api/test_serializers.py index a8fb25d005..2496ba9a2d 100644 --- a/awx/main/tests/unit/api/test_serializers.py +++ b/awx/main/tests/unit/api/test_serializers.py @@ -1,14 +1,31 @@ # Python import pytest import mock +from mock import PropertyMock import json # AWX -from awx.api.serializers import JobTemplateSerializer, JobSerializer, JobOptionsSerializer -from awx.main.models import Label, Job +from awx.api.serializers import ( + JobTemplateSerializer, + JobSerializer, + JobOptionsSerializer, + CustomInventoryScriptSerializer, +) +from awx.main.models import ( + Label, + Job, + CustomInventoryScript, + User, +) #DRF +from rest_framework.request import Request from rest_framework import serializers +from rest_framework.test import ( + APIRequestFactory, + force_authenticate, +) + def mock_JT_resource_data(): return ({}, []) @@ -189,3 +206,30 @@ class TestJobTemplateSerializerValidation(object): for ev in self.bad_extra_vars: with pytest.raises(serializers.ValidationError): serializer.validate_extra_vars(ev) + +class TestCustomInventoryScriptSerializer(object): + + @pytest.mark.parametrize("superuser,sysaudit,admin_role,value", + ((True, False, False, '#!/python'), + (False, True, False, '#!/python'), + (False, False, True, '#!/python'), + (False, False, False, None))) + def test_to_representation_orphan(self, superuser, sysaudit, admin_role, value): + with mock.patch.object(CustomInventoryScriptSerializer, 'get_summary_fields', return_value={}): + User.add_to_class('is_system_auditor', sysaudit) + user = User(username="root", is_superuser=superuser) + roles = [user] if admin_role else [] + + with mock.patch('awx.main.models.CustomInventoryScript.admin_role', new_callable=PropertyMock, return_value=roles): + cis = CustomInventoryScript(pk=1, script='#!/python') + serializer = CustomInventoryScriptSerializer() + + factory = APIRequestFactory() + wsgi_request = factory.post("/inventory_script/1", {'id':1}, format="json") + force_authenticate(wsgi_request, user) + + request = Request(wsgi_request) + serializer.context['request'] = request + + representation = serializer.to_representation(cis) + assert representation['script'] == value diff --git a/awx/sso/backends.py b/awx/sso/backends.py index 3d5edb7ec9..91999034d5 100644 --- a/awx/sso/backends.py +++ b/awx/sso/backends.py @@ -201,7 +201,7 @@ def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=True): rel.remove(user) -@receiver(populate_user) +@receiver(populate_user, dispatch_uid='populate-ldap-user') def on_populate_user(sender, **kwargs): ''' Handle signal from LDAP backend to populate the user object. Update user @@ -239,7 +239,7 @@ def on_populate_user(sender, **kwargs): team, created = Team.objects.get_or_create(name=team_name, organization=org) users_opts = team_opts.get('users', None) remove = bool(team_opts.get('remove', True)) - _update_m2m_from_groups(user, ldap_user, team.member_role.users, users_opts, + _update_m2m_from_groups(user, ldap_user, team.member_role.members, users_opts, remove) # Update user profile to store LDAP DN. diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 0698a2fdbb..e25d53ac51 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -255,16 +255,6 @@ var tower = angular.module('Tower', [ }); }); }] - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }). @@ -274,16 +264,6 @@ var tower = angular.module('Tower', [ controller: JobsListController, ncyBreadcrumb: { label: "JOBS" - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }). diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index 27cbd6f8f5..a7408259a2 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -66,6 +66,7 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $statePa scope: jobs_scope, list: AllJobsList, id: 'active-jobs', + pageSize: 20, url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled&order_by=-finished', searchParams: search_params, spinner: false @@ -77,15 +78,14 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $statePa parent_scope: $scope, scope: scheduled_scope, list: ScheduledJobsList, + pageSize: 20, id: 'scheduled-jobs-tab', searchSize: 'col-lg-4 col-md-4 col-sm-4 col-xs-12', url: GetBasePath('schedules') + '?next_run__isnull=false' }); $scope.refreshJobs = function() { - jobs_scope.search('queued_job'); - jobs_scope.search('running_job'); - jobs_scope.search('completed_job'); + jobs_scope.search('all_job'); scheduled_scope.search('schedule'); }; diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js index f23852cebc..fe61186cce 100644 --- a/awx/ui/client/src/forms/Credentials.js +++ b/awx/ui/client/src/forms/Credentials.js @@ -107,11 +107,6 @@ export default init: false }, autocomplete: false, - subCheckbox: { - variable: 'secret_key_ask', - text: 'Ask at runtime?', - ngChange: 'ask(\'secret_key\', \'undefined\')' - }, clear: false, hasShowInputButton: true, apiField: 'password', diff --git a/awx/ui/client/src/helpers/JobDetail.js b/awx/ui/client/src/helpers/JobDetail.js index dde9264109..3b7dcca1ce 100644 --- a/awx/ui/client/src/helpers/JobDetail.js +++ b/awx/ui/client/src/helpers/JobDetail.js @@ -687,7 +687,7 @@ export default scope.plays = []; url = scope.job.url + 'job_plays/?page_size=' + scope.playsMaxRows + '&order=id'; - url += (scope.search_play_name) ? '&play__icontains=' + scope.search_play_name : ''; + url += (scope.search_play_name) ? '&play__icontains=' + encodeURIComponent(scope.search_play_name) : ''; url += (scope.search_play_status === 'failed') ? '&failed=true' : ''; scope.playsLoading = true; Rest.setUrl(url); @@ -786,7 +786,7 @@ export default scope.tasks = []; if (scope.selectedPlay) { url = scope.job.url + 'job_tasks/?event_id=' + scope.selectedPlay; - url += (scope.search_task_name) ? '&task__icontains=' + scope.search_task_name : ''; + url += (scope.search_task_name) ? '&task__icontains=' + encodeURIComponent(scope.search_task_name) : ''; url += (scope.search_task_status === 'failed') ? '&failed=true' : ''; url += '&page_size=' + scope.tasksMaxRows + '&order=id'; scope.plays.every(function(p, idx) { diff --git a/awx/ui/client/src/helpers/JobTemplates.js b/awx/ui/client/src/helpers/JobTemplates.js index 55a4a9aa5f..2947c05546 100644 --- a/awx/ui/client/src/helpers/JobTemplates.js +++ b/awx/ui/client/src/helpers/JobTemplates.js @@ -25,7 +25,6 @@ angular.module('JobTemplatesHelper', ['Utilities']) return function(params) { var scope = params.scope, - CredentialList = _.cloneDeep(CredentialList), defaultUrl = GetBasePath('job_templates'), // generator = GenerateForm, form = JobTemplateForm(), @@ -37,6 +36,8 @@ angular.module('JobTemplatesHelper', ['Utilities']) // checkSCMStatus, getPlaybooks, callback, // choicesCount = 0; + CredentialList = _.cloneDeep(CredentialList); + // The form uses awPopOverWatch directive to 'watch' scope.callback_help for changes. Each time the // popover is activated, a function checks the value of scope.callback_help before constructing the content. scope.setCallbackHelp = function() { diff --git a/awx/ui/client/src/job-detail/host-events/host-events.controller.js b/awx/ui/client/src/job-detail/host-events/host-events.controller.js index 55abf51e04..f56111bdb6 100644 --- a/awx/ui/client/src/job-detail/host-events/host-events.controller.js +++ b/awx/ui/client/src/job-detail/host-events/host-events.controller.js @@ -25,8 +25,8 @@ host_name: $scope.hostName, }; if ($scope.searchStr && $scope.searchStr !== ''){ - params.or__play__icontains = $scope.searchStr; - params.or__task__icontains = $scope.searchStr; + params.or__play__icontains = encodeURIComponent($scope.searchStr); + params.or__task__icontains = encodeURIComponent($scope.searchStr); } switch($scope.activeFilter){ diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js b/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js index 34763c838e..d5c826ac35 100644 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js +++ b/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js @@ -104,7 +104,7 @@ Wait('start'); JobDetailService.getJobHostSummaries($stateParams.id, { page_size: page_size, - host_name__icontains: $scope.searchTerm, + host_name__icontains: encodeURIComponent($scope.searchTerm), }).success(function(res){ $scope.hosts = res.results; $scope.next = res.next; diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.route.js b/awx/ui/client/src/job-detail/host-summary/host-summary.route.js index f7722462a0..a2de70e5d4 100644 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.route.js +++ b/awx/ui/client/src/job-detail/host-summary/host-summary.route.js @@ -17,15 +17,5 @@ export default { }, ncyBreadcrumb: { skip: true // Never display this state in breadcrumb. - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }; diff --git a/awx/ui/client/src/job-detail/job-detail.route.js b/awx/ui/client/src/job-detail/job-detail.route.js index bf21fb8e5a..dda2722511 100644 --- a/awx/ui/client/src/job-detail/job-detail.route.js +++ b/awx/ui/client/src/job-detail/job-detail.route.js @@ -30,15 +30,5 @@ export default { }] }, templateUrl: templateUrl('job-detail/job-detail'), - controller: 'JobDetailController', - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); - } + controller: 'JobDetailController' }; diff --git a/awx/ui/client/src/job-submission/job-submission.directive.js b/awx/ui/client/src/job-submission/job-submission.directive.js index bd1c90ff92..26a3d9c826 100644 --- a/awx/ui/client/src/job-submission/job-submission.directive.js +++ b/awx/ui/client/src/job-submission/job-submission.directive.js @@ -89,6 +89,12 @@ export default [ 'templateUrl', 'CreateDialog', 'Wait', 'CreateSelect2', 'ParseT } }; + scope.$on("$stateChangeStart", function() { + scope.$evalAsync(function( scope ) { + scope.clearDialog(); + }); + }); + scope.init(); } diff --git a/awx/ui/client/src/job-submission/job-submission.partial.html b/awx/ui/client/src/job-submission/job-submission.partial.html index 95ca9df877..3f69234d91 100644 --- a/awx/ui/client/src/job-submission/job-submission.partial.html +++ b/awx/ui/client/src/job-submission/job-submission.partial.html @@ -54,9 +54,9 @@ </label> <div class="input-group"> <span class="input-group-btn"> - <button class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-password')" data-original-title="" title="">Show</button> + <button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-password')" data-original-title="" title="">Show</button> </span> - <input id="job-submission-ssh-password" type="password" ng-model="passwords.ssh_password" ng-keydown="keydown($event)" name="ssh_password" class="password-field form-control input-sm Form-textInput" required> + <input id="job-submission-ssh-password" type="password" ng-model="passwords.ssh_password" name="ssh_password" class="password-field form-control input-sm Form-textInput" required> </div> <div class="error" ng-show="forms.credentialpasswords.ssh_password.$dirty && forms.credentialpasswords.ssh_password.$error.required">Please enter a password.</div> <div class="error api-error" ng-bind="ssh_password_api_error"></div> @@ -67,9 +67,9 @@ </label> <div class="input-group"> <span class="input-group-btn"> - <button class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-key-unlock_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-key-unlock')" data-original-title="" title="">Show</button> + <button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-ssh-key-unlock_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-ssh-key-unlock')" data-original-title="" title="">Show</button> </span> - <input id="job-submission-ssh-key-unlock" type="password" ng-model="passwords.ssh_key_unlock" ng-keydown="keydown($event)" name="ssh_key_unlock" class="password-field form-control input-sm Form-textInput" required> + <input id="job-submission-ssh-key-unlock" type="password" ng-model="passwords.ssh_key_unlock" name="ssh_key_unlock" class="password-field form-control input-sm Form-textInput" required> </div> <div class="error" ng-show="forms.credentialpasswords.ssh_key_unlock.$dirty && forms.credentialpasswords.ssh_key_unlock.$error.required">Please enter a password.</div> <div class="error api-error" ng-bind="ssh_key_unlock_api_error"></div> @@ -80,9 +80,9 @@ </label> <div class="input-group"> <span class="input-group-btn"> - <button class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-become-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-become-password')" data-original-title="" title="">Show</button> + <button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-become-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-become-password')" data-original-title="" title="">Show</button> </span> - <input id="job-submission-become-password" type="password" ng-model="passwords.become_password" ng-keydown="keydown($event)" name="become_password" class="password-field form-control input-sm Form-textInput" required> + <input id="job-submission-become-password" type="password" ng-model="passwords.become_password" name="become_password" class="password-field form-control input-sm Form-textInput" required> </div> <div class="error" ng-show="forms.credentialpasswords.become_password.$dirty && forms.credentialpasswords.become_password.$error.required">Please enter a password.</div> <div class="error api-error" ng-bind="become_password_api_error"></div> @@ -93,9 +93,9 @@ </label> <div class="input-group"> <span class="input-group-btn"> - <button class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-vault-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-vault-password')" data-original-title="" title="">Show</button> + <button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="job-submission-vault-password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#job-submission-vault-password')" data-original-title="" title="">Show</button> </span> - <input id="job-submission-vault-password" type="password" ng-model="passwords.vault_password" ng-keydown="keydown($event)" name="vault_password" class="password-field form-control input-sm Form-textInput" required> + <input id="job-submission-vault-password" type="password" ng-model="passwords.vault_password" name="vault_password" class="password-field form-control input-sm Form-textInput" required> </div> <div class="error" ng-show="forms.credentialpasswords.vault_password.$dirty && forms.credentialpasswords.vault_password.$error.required">Please enter a password.</div> <div class="error api-error" ng-bind="vault_password_api_error"></div> @@ -176,7 +176,7 @@ <div ng-if="question.type === 'password'"> <div class="input-group"> <span class="input-group-btn"> - <button class="btn btn-default show_input_button JobSubmission-passwordButton" id="survey_question_{{$index}}_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#survey_question_' + $index)" data-original-title="" title="">Show</button> + <button type="button" class="btn btn-default show_input_button JobSubmission-passwordButton" id="survey_question_{{$index}}_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="togglePassword('#survey_question_' + $index)" data-original-title="" title="">Show</button> </span> <input id="survey_question_{{$index}}" type="password" ng-model="question.model" name="survey_question_{{$index}}" ng-required="question.required" ng-minlength="question.minlength" ng-maxlength="question.maxlength" class="form-control Form-textInput" autocomplete="false"> </div> diff --git a/awx/ui/client/src/job-templates/add/job-templates-add.route.js b/awx/ui/client/src/job-templates/add/job-templates-add.route.js index 8218cabc32..68275c0f22 100644 --- a/awx/ui/client/src/job-templates/add/job-templates-add.route.js +++ b/awx/ui/client/src/job-templates/add/job-templates-add.route.js @@ -16,14 +16,8 @@ export default { label: "CREATE JOB TEMPLATE" }, onExit: function(){ - // close the job launch modal + // close the survey maker modal // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); if($("#survey-modal-dialog").hasClass('ui-dialog-content')) { $('#survey-modal-dialog').dialog('destroy'); diff --git a/awx/ui/client/src/job-templates/edit/job-templates-edit.route.js b/awx/ui/client/src/job-templates/edit/job-templates-edit.route.js index 78d383fa42..b2dffa229b 100644 --- a/awx/ui/client/src/job-templates/edit/job-templates-edit.route.js +++ b/awx/ui/client/src/job-templates/edit/job-templates-edit.route.js @@ -19,14 +19,8 @@ export default { label: "{{name}}" }, onExit: function(){ - // close the job launch modal + // close the survey maker modal // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); if($("#survey-modal-dialog").hasClass('ui-dialog-content')) { $('#survey-modal-dialog').dialog('destroy'); diff --git a/awx/ui/client/src/job-templates/list/job-templates-list.route.js b/awx/ui/client/src/job-templates/list/job-templates-list.route.js index 6c646af729..deeb1982bd 100644 --- a/awx/ui/client/src/job-templates/list/job-templates-list.route.js +++ b/awx/ui/client/src/job-templates/list/job-templates-list.route.js @@ -17,15 +17,5 @@ export default { }, ncyBreadcrumb: { label: "JOB TEMPLATES" - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }; diff --git a/awx/ui/client/src/job-templates/survey-maker/render/survey-question.partial.html b/awx/ui/client/src/job-templates/survey-maker/render/survey-question.partial.html index 72e772bd62..0777b94c90 100644 --- a/awx/ui/client/src/job-templates/survey-maker/render/survey-question.partial.html +++ b/awx/ui/client/src/job-templates/survey-maker/render/survey-question.partial.html @@ -17,7 +17,7 @@ </div> <div ng-if="question.type === 'password'" class="input_area input-group"> <span class="input-group-btn"> - <button class="btn btn-default SurveyMaker-previewPasswordButton" id="{{ question.variable + '_show_input_button' }}" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput('#' + question.variable)" data-original-title="" title="">SHOW</button> + <button type="button" class="btn btn-default SurveyMaker-previewPasswordButton" id="{{ question.variable + '_show_input_button' }}" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput('#' + question.variable)" data-original-title="" title="">SHOW</button> </span> <input id="{{question.variable}}" type="password" name="" class="form-control ng-pristine ng-valid-api-error ng-invalid" autocomplete="false" ng-model="defaultValue" readonly> </div> diff --git a/awx/ui/client/src/job-templates/survey-maker/shared/question-definition.form.js b/awx/ui/client/src/job-templates/survey-maker/shared/question-definition.form.js index 32b1c1dd9a..56a3903f69 100644 --- a/awx/ui/client/src/job-templates/survey-maker/shared/question-definition.form.js +++ b/awx/ui/client/src/job-templates/survey-maker/shared/question-definition.form.js @@ -280,7 +280,7 @@ export default '<div>'+ '<div class="input-group">'+ '<span class="input-group-btn">'+ - '<button class="btn btn-default show_input_button" id="default_password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput("#default_password")" data-original-title="" title="">SHOW</button>'+ + '<button type="button" class="btn btn-default show_input_button" id="default_password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput("#default_password")" data-original-title="" title="">SHOW</button>'+ '</span>'+ '<input id="default_password" type="password" ng-model="default_password" name="default_password" class="form-control Form-textInput" autocomplete="false">'+ '</div>'+ diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index caa8d7692f..13d51cc68f 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -121,16 +121,6 @@ export default [ features: ['FeaturesService', function(FeaturesService) { return FeaturesService.get(); }] - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }, { diff --git a/awx/ui/client/src/portal-mode/portal-mode.route.js b/awx/ui/client/src/portal-mode/portal-mode.route.js index 6982f10620..c0f7e5f3fb 100644 --- a/awx/ui/client/src/portal-mode/portal-mode.route.js +++ b/awx/ui/client/src/portal-mode/portal-mode.route.js @@ -24,15 +24,5 @@ export default { templateUrl: templateUrl('portal-mode/portal-mode-jobs'), controller: PortalModeJobsController } - }, - onExit: function(){ - // close the job launch modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - // Destroy the dialog - if($("#job-launch-modal").hasClass('ui-dialog-content')) { - $('#job-launch-modal').dialog('destroy'); - } - // Remove the directive from the page (if it's there) - $('#content-container').find('submit-job').remove(); } }; diff --git a/awx/ui/client/src/search/tagSearch.service.js b/awx/ui/client/src/search/tagSearch.service.js index fdd808d3ad..4e5e6ae3ec 100644 --- a/awx/ui/client/src/search/tagSearch.service.js +++ b/awx/ui/client/src/search/tagSearch.service.js @@ -85,7 +85,7 @@ export default ['Rest', '$q', 'GetBasePath', 'Wait', 'ProcessErrors', '$log', fu if (needsRequest.length) { // make the options request to reutrn the typeOptions var url = needsRequest[0].basePath ? GetBasePath(needsRequest[0].basePath) : basePath; - if(url.indexOf('null') === 0 ){ + if(url.indexOf('null') === -1 ){ Rest.setUrl(url); Rest.options() .success(function (data) { diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 4b889eeb75..4c8382db38 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -922,7 +922,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "'>\n"; // TODO: make it so that the button won't show up if the mode is edit, hasShowInputButton !== true, and there are no contents in the field. html += "<span class='input-group-btn'>\n"; - html += "<button class='btn btn-default show_input_button Form-passwordButton' "; + html += "<button type='button' class='btn btn-default show_input_button Form-passwordButton' "; html += buildId(field, fld + "_show_input_button", this.form); html += "aw-tool-tip='Toggle the display of plaintext.' aw-tip-placement='top' "; html += "tabindex='-1' "; |