Merge branch 'ui_release_3.0.1' into release_3.0.2

* ui_release_3.0.1: (54 commits)
  Returning btn-primary to blue
  Fixed adhoc relaunch logic to match normal adhoc launch logic
  Breadcrumb appears correctly on first run
  Fixed display of non-breaking names
  Continuing audit work
  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
  ...
This commit is contained in:
Matthew Jones 2016-08-01 13:29:19 -04:00
commit 4881529688
45 changed files with 194 additions and 125 deletions

View File

@ -80,12 +80,6 @@ a.red-txt:active {
text-overflow: ellipsis;
}
.name-column {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
blockquote {
font-size: 14px;
}

View File

@ -44,9 +44,11 @@
color: @list-header-txt;
font-size: 14px;
font-weight: bold;
white-space: nowrap;
padding-bottom: 25px;
min-height: 45px;
word-break: break-all;
max-width: 90%;
word-wrap: break-word;
}
.Form-secondaryTitle{
@ -55,7 +57,10 @@
min-height: 40px;
}
.Form-title--is_superuser, .Form-title--is_system_auditor, .Form-title--is_ldap_user{
.Form-title--is_superuser,
.Form-title--is_system_auditor,
.Form-title--is_ldap_user,
.Form-title--is_external_account{
height:15px;
color: @default-interface-txt;
background-color: @default-list-header-bg;
@ -353,6 +358,11 @@
border-color: transparent transparent @field-dropdown-icon transparent!important;
}
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.select2-dropdown{
border:1px solid @field-border;

View File

@ -78,6 +78,7 @@ table, tbody {
padding-left: 15px;
padding-right: 15px;
border-top:0px!important;
word-wrap: break-word;
}
.List-tableCell.description-column {
@ -383,6 +384,7 @@ table, tbody {
.List-action--showTooltipOnDisabled {
display: inline-block;
cursor: not-allowed;
}
.List-action--showTooltipOnDisabled .btn[disabled] {

View File

@ -1,6 +1,6 @@
{
"name": "angular-codemirror",
"version": "1.0.2",
"version": "1.0.3",
"dependencies": {
"angular": "latest",
"angular-route": "latest",
@ -13,14 +13,13 @@
"codemirror": "latest"
},
"homepage": "https://github.com/chouseknecht/angular-codemirror",
"_release": "1.0.2",
"_release": "1.0.3",
"_resolution": {
"type": "version",
"tag": "v1.0.2",
"commit": "94b7aac548b036f4fbd94e56129ed9574e472616"
"tag": "1.0.3",
"commit": "b94dc86fde8f60a50b324054806d29d742177d21"
},
"_source": "git://github.com/chouseknecht/angular-codemirror.git",
"_target": "~1.0.2",
"_originalSource": "angular-codemirror",
"_direct": true
"_source": "https://github.com/chouseknecht/angular-codemirror.git",
"_target": "~1.0.3",
"_originalSource": "angular-codemirror"
}

View File

@ -1,6 +1,6 @@
{
"name": "angular-codemirror",
"version": "0.0.3",
"version": "1.0.2",
"dependencies": {
"angular": "latest",
"angular-route": "latest",

View File

@ -1,6 +1,6 @@
/**********************************************
* AngularCodeMirror.css
*
*
* CodeMirror.css overrides
*
* Copyright (c) 2014 Chris Houseknecht
@ -30,14 +30,14 @@
.CodeMirror {
height: auto;
}
.CodeMirror-activeline-background {
background-color: #f7f7f7;
}
/* Modal dialog overrides to make jqueryui dialog blend in with Twitter.
/* Modal dialog overrides to make jqueryui dialog blend in with Twitter.
Why? Twitter's modal is not draggable or resizable, which is not very
useful for a code editor */
@ -71,7 +71,7 @@
border-color: #ffffff;
color: #A9A9A9;
}
.ui-dialog .ui-resizable-se {
right: 5px;
bottom: 5px;
@ -108,3 +108,4 @@
.CodeMirror-lint-tooltip {
z-index: 2060;
}

View File

@ -30,7 +30,7 @@
angular.module('AngularCodeMirrorModule', [])
.factory('AngularCodeMirror', [ function() {
return function() {
return function(readOnly) {
var fn = function() {
this.myCodeMirror = null;
@ -43,7 +43,6 @@ angular.module('AngularCodeMirrorModule', [])
model = params.model,
mode = params.mode,
onReady = params.onReady,
onChange = params.onChange,
height = 0;
self.element = $(element);
@ -69,6 +68,15 @@ angular.module('AngularCodeMirrorModule', [])
// Initialize CodeMirror
self.modes[mode].value = scope[model];
// if readOnly is passed to AngularCodeMirror, set the
// options for all modes to be readOnly
if (readOnly) {
Object.keys(self.modes).forEach(function(val) {
self.modes[val].readOnly = true;
});
}
self.myCodeMirror = CodeMirror(document.getElementById('cm-' + model + '-container'), self.modes[mode]);
// Adjust the height
@ -85,14 +93,7 @@ angular.module('AngularCodeMirrorModule', [])
// Update the model on change
self.myCodeMirror.on('change', function() {
setTimeout(function() {
scope.$apply(function(){
scope[model] = self.myCodeMirror.getValue();
if (onChange) {
onChange();
}
});
}, 500);
setTimeout(function() { scope.$apply(function(){ scope[model] = self.myCodeMirror.getValue(); }); }, 500);
});
};

View File

@ -52,8 +52,7 @@
"tag": "2.0.0",
"commit": "8a1951c54a956c33964c99b338f3a4830e652689"
},
"_source": "git://github.com/tameraydin/ngToast.git",
"_source": "https://github.com/tameraydin/ngToast.git",
"_target": "~2.0.0",
"_originalSource": "ngtoast",
"_direct": true
"_originalSource": "ngtoast"
}

View File

@ -66,6 +66,12 @@
display: inline-block;
color: @default-interface-txt;
text-transform: uppercase;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: bottom;
}
.BreadCrumb-item + .BreadCrumb-item:before {

View File

@ -20,7 +20,8 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $statePa
var jobs_scope, scheduled_scope,
choicesCount = 0,
listCount = 0,
api_complete = false;
api_complete = false,
scheduledJobsList = _.cloneDeep(ScheduledJobsList);
$scope.jobsSelected = true;
@ -66,22 +67,23 @@ 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,new&order_by=-finished',
pageSize: 20,
searchParams: search_params,
spinner: false
});
scheduled_scope = $scope.$new(true);
scheduledJobsList.basePath = GetBasePath('schedules') + '?next_run__isnull=false';
LoadSchedulesScope({
parent_scope: $scope,
scope: scheduled_scope,
list: ScheduledJobsList,
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'
url: scheduledJobsList.basePath
});
$scope.refreshJobs = function() {

View File

@ -358,11 +358,8 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$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');
if (!(project.scm_type === "Manual" || Empty(project.scm_type)) && !(project.status === 'updating' || project.status === 'running' || project.status === 'pending')) {
$state.go('projectSchedules', {id: id});
}
};
}

View File

@ -162,6 +162,7 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
$scope.not_ldap_user = !$scope.ldap_user;
$scope.ldap_dn = null;
$scope.socialAuthUser = false;
$scope.external_account = null;
generator.reset();
@ -334,6 +335,7 @@ export function UsersEdit($scope, $rootScope, $location,
$scope.not_ldap_user = !$scope.ldap_user;
master.ldap_user = $scope.ldap_user;
$scope.socialAuthUser = (data.auth.length > 0) ? true : false;
$scope.external_account = data.external_account;
$scope.user_type = $scope.user_type_options[0];
$scope.is_system_auditor = false;

View File

@ -6,8 +6,8 @@
export default
['$scope', '$state', '$stateParams', 'PageRangeSetup', 'GetBasePath', 'DashboardHostsList',
'generateList', 'PaginateInit', 'SetStatus', 'DashboardHostService', 'hosts', '$rootScope',
function($scope, $state, $stateParams, PageRangeSetup, GetBasePath, DashboardHostsList, GenerateList, PaginateInit, SetStatus, DashboardHostService, hosts, $rootScope){
'generateList', 'PaginateInit', 'SetStatus', 'DashboardHostService', 'hosts', '$rootScope', 'SearchInit',
function($scope, $state, $stateParams, PageRangeSetup, GetBasePath, DashboardHostsList, GenerateList, PaginateInit, SetStatus, DashboardHostService, hosts, $rootScope, SearchInit){
var setJobStatus = function(){
_.forEach($scope.hosts, function(value){
SetStatus({
@ -59,6 +59,12 @@ export default
$scope.hosts = hosts.results;
setJobStatus();
generator.inject(list, {mode: 'edit', scope: $scope});
SearchInit({
scope: $scope,
set: 'hosts',
list: list,
url: defaultUrl
});
PaginateInit({
scope: $scope,
list: list,
@ -77,6 +83,7 @@ export default
$scope.rowBeingEdited = $state.params.id;
$scope.listBeingEdited = "hosts";
}
$scope.search(list.iterator);
};
init();
}];

View File

@ -101,10 +101,8 @@
.DashboardList-nameCell {
padding-left: 15px;
text-overflow: ellipsis;
overflow:hidden;
white-space: nowrap;
width: 100%;
word-wrap: break-word;
}
.DashboardList-nameContainer {

View File

@ -81,7 +81,7 @@ export default
permissions: {
awToolTip: 'Please save before assigning permissions',
dataPlacement: 'top',
basePath: 'projects/:id/access_list/',
basePath: 'inventories/:id/access_list/',
type: 'collection',
title: 'Permissions',
iterator: 'permission',

View File

@ -281,7 +281,7 @@ export default
column: 2,
awPopOver: "callback_help",
awPopOverWatch: "callback_help",
dataPlacement: 'right',
dataPlacement: 'top',
dataTitle: 'Provisioning Callback URL',
dataContainer: "body"
},

View File

@ -46,7 +46,7 @@ export default
label: 'Username',
type: 'text',
awRequiredWhen: {
reqExpression: "not_ldap_user",
reqExpression: "not_ldap_user && external_account === null",
init: true
},
autocomplete: false
@ -69,7 +69,7 @@ export default
label: 'Password',
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false',
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
addRequired: true,
editRequired: false,
ngChange: "clearPWConfirm('password_confirm')",
@ -80,7 +80,7 @@ export default
label: 'Confirm Password',
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false',
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
addRequired: true,
editRequired: false,
awPassMatch: true,

View File

@ -55,9 +55,9 @@ export default
// Submit request to run an adhoc comamand
.factory('AdhocRun', ['$location','$stateParams', 'LaunchJob',
'PromptForPasswords', 'Rest', 'GetBasePath', 'Alert', 'ProcessErrors',
'Wait', 'Empty', 'CreateLaunchDialog',
'Wait', 'Empty', 'CreateLaunchDialog', '$state',
function ($location, $stateParams, LaunchJob, PromptForPasswords,
Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, CreateLaunchDialog) {
Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, CreateLaunchDialog, $state) {
return function (params) {
var id = params.project_id,
scope = params.scope.$new(),
@ -87,25 +87,31 @@ export default
});
});
if (scope.removeAdhocLaunchFinished) {
scope.removeAdhocLaunchFinished();
}
scope.removeAdhocLaunchFinished = scope.$on('AdhocLaunchFinished',
function(e, data) {
$location.path('/ad_hoc_commands/' + data.id);
});
if (scope.removeStartAdhocRun) {
scope.removeStartAdhocRun();
}
scope.removeStartAdhocRun = scope.$on('StartAdhocRun', function() {
LaunchJob({
scope: scope,
url: url,
callback: 'AdhocLaunchFinished' // send to the adhoc
// standard out page
});
var password,
postData={};
for (password in scope.passwords) {
postData[scope.passwords[password]] = scope[
scope.passwords[password]
];
}
// Re-launch the adhoc job
Rest.setUrl(url);
Rest.post(postData)
.success(function (data) {
Wait('stop');
$state.go('adHocJobStdout', {id: data.id});
})
.error(function (data, status) {
ProcessErrors(scope, data, status, {
hdr: 'Error!',
msg: 'Failed to launch adhoc command. POST ' +
'returned status: ' + status });
});
});
// start routine only if passwords need to be prompted

View File

@ -165,6 +165,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
has_inventory_sources = params.has_inventory_sources,
launch_class = '',
launch_tip = 'Start sync process',
schedule_tip = 'Schedule future inventory syncs',
stat, stat_class, status_tip;
stat = status;
@ -225,7 +226,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
"tooltip": status_tip,
"status": stat,
"launch_class": launch_class,
"launch_tip": launch_tip
"launch_tip": launch_tip,
"schedule_tip": schedule_tip
};
};
}

View File

@ -25,7 +25,8 @@ export default
fld = (params.variable) ? params.variable : 'variables',
pfld = (params.parse_variable) ? params.parse_variable : 'parseType',
onReady = params.onReady,
onChange = params.onChange;
onChange = params.onChange,
readOnly = params.readOnly;
function removeField(fld) {
//set our model to the last change in CodeMirror and then destroy CodeMirror
@ -35,8 +36,7 @@ export default
function createField(onChange, onReady, fld) {
//hide the textarea and show a fresh CodeMirror with the current mode (json or yaml)
scope[fld + 'codeMirror'] = AngularCodeMirror();
scope[fld + 'codeMirror'] = AngularCodeMirror(readOnly);
scope[fld + 'codeMirror'].addModes($AnsibleConfig.variable_edit_modes);
scope[fld + 'codeMirror'].showTextArea({
scope: scope,

View File

@ -75,6 +75,9 @@ export default
case 'missing':
result = 'Missing. Click for details';
break;
case 'canceled':
result = 'Canceled. Click for details';
break;
}
return result;
};

View File

@ -242,7 +242,7 @@ function adhocController($q, $scope, $location, $stateParams,
Rest.post(data)
.success(function (data) {
Wait('stop');
$location.path("/ad_hoc_commands/" + data.id);
$state.go('adHocJobStdout', {id: data.id});
})
.error(function (data, status) {
ProcessErrors($scope, data, status, adhocForm, {

View File

@ -151,6 +151,7 @@
{status_tooltip: group_status.tooltip},
{launch_tooltip: group_status.launch_tip},
{launch_class: group_status.launch_class},
{group_schedule_tooltip: group_status.schedule_tip},
{hosts_status_tip: hosts_status.tooltip},
{hosts_status_class: hosts_status.class},
{source: group.summary_fields.inventory_source ? group.summary_fields.inventory_source.source : null},

View File

@ -641,7 +641,7 @@ export default
return true;
});
//scope.setSearchAll('host');
ParseTypeChange({ scope: scope, field_id: 'pre-formatted-variables' });
ParseTypeChange({ scope: scope, field_id: 'pre-formatted-variables', readOnly: true });
scope.$emit('LoadPlays', data.related.job_events);
})
.error(function(data, status) {

View File

@ -8,7 +8,7 @@
}
.LabelList-tagContainer,
.LabelList-seeMore {
.LabelList-seeMoreLess {
display: flex;
max-width: 100%;
}
@ -27,7 +27,7 @@
overflow: hidden;
}
.LabelList-seeMore {
.LabelList-seeMoreLess {
color: @default-link;
margin: 4px 0px;
text-transform: uppercase;
@ -37,7 +37,7 @@
font-size: 11px;
}
.LabelList-seeMore:hover {
.LabelList-seeMoreLess:hover {
color: @default-link-hov;
}

View File

@ -47,6 +47,13 @@ export default
});
};
scope.seeLess = function() {
// Trim the labels array back down to 10 items
scope.labels = scope.labels.slice(0, 10);
// Re-set the seeMoreInteractive flag so that the "See More" will be displayed
scope.seeMoreInactive = true;
};
scope.deleteLabel = function(templateId, templateName, labelId, labelName) {
var action = function () {
$('#prompt-modal').modal('hide');
@ -56,13 +63,13 @@ export default
Rest.setUrl(url);
Rest.post({"disassociate": true, "id": labelId})
.success(function () {
scope.search("job_template");
scope.search("job_template", scope.$parent.job_template_page);
Wait('stop');
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Could not disacssociate label from JT. Call to ' + url + ' failed. DELETE returned status: ' + status });
msg: 'Could not disassociate label from JT. Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
@ -86,6 +93,7 @@ export default
scope.count = null;
}
});
}
};
}

View File

@ -8,5 +8,7 @@
<span class="LabelList-name">{{ label.name }}</span>
</div>
</div>
<div class="LabelList-seeMore" ng-show="count > 10 && seeMoreInactive"
<div class="LabelList-seeMoreLess" ng-show="count > 10 && seeMoreInactive"
ng-click="seeMore()">View More</div>
<div class="LabelList-seeMoreLess" ng-show="count > 10 && !seeMoreInactive"
ng-click="seeLess()">View Less</div>

View File

@ -314,7 +314,7 @@ export default
ngClick: 'submitQuestion($event)',
ngDisabled: true,
'class': 'btn btn-sm Form-saveButton',
label: '{{editQuestionIndex === null ? "ADD" : "UPDATE"}}'
label: '{{editQuestionIndex === null ? "+ ADD" : "UPDATE"}}'
}
}

View File

@ -48,9 +48,6 @@ export default
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
ngClick: "viewJobDetails(all_job)",
defaultSearchField: true,
awToolTip: "{{ all_job.name | sanitize }}",
dataTipWatch: 'all_job.name',
dataPlacement: 'top'
},
type: {
label: 'Type',

View File

@ -35,6 +35,7 @@ export default
ngClick:"viewJobDetails(completed_job)",
searchable: true,
searchType: 'select',
defaultSearchField: true,
nosort: true,
searchOptions: [
{ label: "Success", value: "successful" },
@ -54,8 +55,8 @@ export default
name: {
label: 'Name',
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
searchable: false,
ngClick: "viewJobDetails(completed_job)",
defaultSearchField: true,
awToolTip: "{{ completed_job.name | sanitize }}",
dataPlacement: 'top'
},
@ -64,7 +65,7 @@ export default
ngBind: 'completed_job.type_label',
link: false,
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
searchable: true,
searchable: false,
searchType: 'select',
searchOptions: [] // populated via GetChoices() in controller
},

View File

@ -36,6 +36,8 @@ export default ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest',
disassociate: 1
};
}
// Show the working spinner
Wait('start');
Rest.setUrl(url);
Rest.post(params)
.success( function(data) {
@ -43,9 +45,8 @@ export default ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest',
scope.$emit(callback, data.id);
notifier[column] = !notifier[column];
}
else {
Wait('stop');
}
// Hide the working spinner
Wait('stop');
})
.error( function(data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',

View File

@ -334,11 +334,8 @@ export default ['$scope', '$rootScope', '$location', '$log',
$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');
if (!(project.scm_type === "Manual" || Empty(project.scm_type)) && !(project.status === 'updating' || project.status === 'running' || project.status === 'pending')) {
$state.go('projectSchedules', {id: id});
}
};

View File

@ -134,6 +134,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
});
$(document).bind('keydown', function (e) {
if (e.keyCode === 27 || e.keyCode === 13) {
e.preventDefault();
$('#alert-modal2').modal('hide');
}
});
@ -161,6 +162,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
});
$(document).bind('keydown', function (e) {
if (e.keyCode === 27 || e.keyCode === 13) {
e.preventDefault();
$('#alert-modal').modal('hide');
}
});

View File

@ -390,46 +390,57 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
// lookup Validate lookup value against API
//
.directive('awlookup', ['Rest', function(Rest) {
.directive('awlookup', ['Rest', '$timeout', function(Rest, $timeout) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var restTimeout;
ctrl.$parsers.unshift( function(viewValue) {
if (viewValue !== '' && viewValue !== null) {
var url = elm.attr('data-url');
url = url.replace(/\:value/, encodeURI(viewValue));
scope[elm.attr('data-source')] = null;
Rest.setUrl(url);
Rest.get().then( function(data) {
var results = data.data.results;
if (results.length > 0) {
scope[elm.attr('data-source')] = results[0].id;
if(restTimeout) {
$timeout.cancel(restTimeout);
}
restTimeout = $timeout( function(){
Rest.setUrl(url);
Rest.get().then( function(data) {
var results = data.data.results;
if (results.length > 0) {
scope[elm.attr('data-source')] = results[0].id;
// For user lookups the API endpoint doesn't
// have a `name` property, so this is `undefined`
// which causes the input to clear after typing
// a valid value O_o
//
// Only assign if there is a value, so that we avoid
// this situation.
//
// TODO: Evaluate if assigning name on the scope is
// even necessary at all.
//
if (!_.isEmpty(results[0].name)) {
scope[elm.attr('name')] = results[0].name;
// For user lookups the API endpoint doesn't
// have a `name` property, so this is `undefined`
// which causes the input to clear after typing
// a valid value O_o
//
// Only assign if there is a value, so that we avoid
// this situation.
//
// TODO: Evaluate if assigning name on the scope is
// even necessary at all.
//
if (!_.isEmpty(results[0].name)) {
scope[elm.attr('name')] = results[0].name;
}
ctrl.$setValidity('required', true);
ctrl.$setValidity('awlookup', true);
return viewValue;
}
ctrl.$setValidity('required', true);
ctrl.$setValidity('awlookup', true);
return viewValue;
}
ctrl.$setValidity('required', true);
ctrl.$setValidity('awlookup', false);
return undefined;
});
ctrl.$setValidity('awlookup', false);
return undefined;
});
}, 750);
}
else {
if(restTimeout) {
$timeout.cancel(restTimeout);
}
ctrl.$setValidity('awlookup', true);
scope[elm.attr('data-source')] = null;
}
@ -477,7 +488,8 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
return {
link: function(scope, element, attrs) {
var delay = (attrs.delay !== undefined && attrs.delay !== null) ? attrs.delay : ($AnsibleConfig) ? $AnsibleConfig.tooltip_delay : {show: 500, hide: 100},
placement;
placement,
stateChangeWatcher;
if (attrs.awTipPlacement) {
placement = attrs.awTipPlacement;
}
@ -493,6 +505,22 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
template = '<div class="tooltip Tooltip" role="tooltip"><div class="tooltip-arrow Tooltip-arrow"></div><div class="tooltip-inner Tooltip-inner"></div></div>';
}
// This block helps clean up tooltips that may get orphaned by a click event
$(element).on('mouseenter', function() {
if(stateChangeWatcher) {
// Un-bind - we don't want a bunch of listeners firing
stateChangeWatcher();
}
stateChangeWatcher = scope.$on('$stateChangeStart', function() {
// Go ahead and force the tooltip setTimeout to expire (if it hasn't already fired)
$(element).tooltip('hide');
// Clean up any existing tooltips including this one
$('.tooltip').each(function() {
$(this).remove();
});
});
});
$(element).on('hidden.bs.tooltip', function( ) {
// TB3RC1 is leaving behind tooltip <div> elements. This will remove them
// after a tooltip fades away. If not, they lay overtop of other elements and

View File

@ -1487,6 +1487,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
"ng-show='is_system_auditor'>Auditor</span>";
html+= "<span class=\"Form-title--is_ldap_user\" "+
"ng-show='ldap_user'>LDAP</span>";
html+= "<span class=\"Form-title--is_external_account\" "+
"ng-show='external_account'>{{external_account}}</span>";
}
html += "</div>\n";
html += "<div class=\"Form-header--fields\">";

View File

@ -466,7 +466,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
innerTable += "ng-class-odd=\"'List-tableRow--oddRow'\" ";
innerTable += "ng-class-even=\"'List-tableRow--evenRow'\" ";
innerTable += "ng-repeat=\"" + list.iterator + " in " + list.name;
innerTable += (list.trackBy) ? " track by " + list.trackBy : " track by $index";
innerTable += (list.trackBy) ? " track by " + list.trackBy : "";
innerTable += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
innerTable += (list.filterBy) ? " | filter: " + list.filterBy : "";
innerTable += "\">\n";

View File

@ -74,6 +74,7 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
$scope.limit = data.limit;
$scope.verbosity = data.verbosity;
$scope.job_tags = data.job_tags;
$scope.job.module_name = data.module_name;
if (data.extra_vars) {
$scope.variables = ParseVariableString(data.extra_vars);
}