mirror of
https://github.com/ansible/awx.git
synced 2026-03-27 05:45:02 -02:30
Merge branch 'release_3.0.2' into devel
* release_3.0.2: (126 commits) Disable permissions tab in Credential > Edit form if Credential is private (#3288) Tweaked the popover text for job and skip tags on JT add/edit Workaround a cascade setnull polymorphic issue flake8 Fixed old test expectations Made it so the credential organization field can't be changed Skip some unit tests Fixed org auditor visibility of team credentials Fix sosreport fix credential kind options for list interpret any code below 300 as success bail when status code is over 300 Make CloudForms inventory_script work Use no_log when handling passwords Prevent ignored task from being displayed as failing. making ec2 cred optional on group->edit making ec2 credential optional for ec2 inventory Revert "Fix to ensure org auditors can see team credentials" Fixed team credential list to work with corrected permissions Making the username and password fields optional ...
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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] {
|
||||
|
||||
@@ -51,23 +51,6 @@ export default
|
||||
PaginateInit({ scope: scope,
|
||||
list: list, url: url, pageSize: 5 });
|
||||
|
||||
if (scope.removePostRefresh) {
|
||||
scope.removePostRefresh();
|
||||
}
|
||||
scope.removePostRefresh = scope.$on('PostRefresh', function () {
|
||||
if(scope.allSelected && scope.allSelected.length > 0) {
|
||||
// We need to check to see if any of the selected items are now in our list!
|
||||
for(var i=0; i<scope.allSelected.length; i++) {
|
||||
for(var j=0; j<scope[set].length; j++) {
|
||||
if(scope.allSelected[i].id === scope[set][j].id && scope.allSelected[i].type === scope[set][j].type) {
|
||||
// If so, let's go ahead and mark it as selected so that select-list-item knows to check the box
|
||||
scope[set][j].isSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scope.search(list.iterator);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
key: true,
|
||||
label: 'name'
|
||||
},
|
||||
organization: {
|
||||
label: 'organization',
|
||||
ngBind: 'team.summary_fields.organization.name',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name',
|
||||
searchable: true
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -46,13 +46,13 @@ export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
|
||||
list.fields.kind.searchOptions = $scope.credential_kind_options;
|
||||
list.fields.kind.searchOptions = $scope.credential_kind_options_list;
|
||||
|
||||
// Translate the kind value
|
||||
for (i = 0; i < $scope.credentials.length; i++) {
|
||||
for (j = 0; j < $scope.credential_kind_options.length; j++) {
|
||||
if ($scope.credential_kind_options[j].value === $scope.credentials[i].kind) {
|
||||
$scope.credentials[i].kind = $scope.credential_kind_options[j].label;
|
||||
for (j = 0; j < $scope.credential_kind_options_list.length; j++) {
|
||||
if ($scope.credential_kind_options_list[j].value === $scope.credentials[i].kind) {
|
||||
$scope.credentials[i].kind = $scope.credential_kind_options_list[j].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'kind',
|
||||
variable: 'credential_kind_options',
|
||||
variable: 'credential_kind_options_list',
|
||||
callback: 'choicesReadyCredential'
|
||||
});
|
||||
|
||||
@@ -148,7 +148,7 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
url;
|
||||
|
||||
$scope.keyEntered = false;
|
||||
|
||||
$scope.permissionsTooltip = 'Please save before assigning permissions';
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
generator.reset();
|
||||
|
||||
@@ -391,6 +391,12 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$scope.removeCredentialLoaded();
|
||||
}
|
||||
$scope.removeCredentialLoaded = $scope.$on('credentialLoaded', function () {
|
||||
// if the credential is assigned to an organization, allow permission delegation
|
||||
// do NOT use $scope.organization in a view directive to determine if a credential is associated with an org
|
||||
$scope.disablePermissionAssignment = typeof($scope.organization) === 'number' ? false : true;
|
||||
if ($scope.disablePermissionAssignment){
|
||||
$scope.permissionsTooltip = 'Credentials are only shared within an organization. Assign credentials to an organization to delegate credential permissions. The organization cannot be edited after credentials are assigned.';
|
||||
}
|
||||
var set;
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}];
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function(){
|
||||
awTipPlacement: 'right',
|
||||
dataPlacement: 'right',
|
||||
awPopOver: '{{ host.job_status_html }}',
|
||||
dataTitle: '{{host.job_status_title}}',
|
||||
ngClick:'viewHost(host.id)',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus'
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -404,7 +404,9 @@ export default
|
||||
|
||||
related: {
|
||||
permissions: {
|
||||
awToolTip: 'Please save before assigning permissions',
|
||||
disabled: 'disablePermissionAssignment',
|
||||
awToolTip: '{{permissionsTooltip}}',
|
||||
dataTipWatch: 'permissionsTooltip',
|
||||
dataPlacement: 'top',
|
||||
basePath: 'credentials/:id/access_list/',
|
||||
type: 'collection',
|
||||
@@ -452,7 +454,7 @@ export default
|
||||
return {
|
||||
permissions: {
|
||||
iterator: 'permission',
|
||||
url: urls.access_list
|
||||
url: urls.access_list,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -228,10 +228,7 @@ export default
|
||||
column: 2,
|
||||
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
|
||||
"<p>Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.</p>" +
|
||||
"<p>For example, you might have a task consisting of a long list of actions. Tag values can be assigned to each action. " +
|
||||
"Suppose the actions have been assigned tag values of "configuration", "packages" and "install".</p>" +
|
||||
"<p>If you just want to run the "configuration" and "packages" actions, you would enter the following here " +
|
||||
"in the Job Tags field:</p>\n<blockquote>configuration,packages</blockquote>\n",
|
||||
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>",
|
||||
dataTitle: "Job Tags",
|
||||
dataPlacement: "right",
|
||||
dataContainer: "body",
|
||||
@@ -240,6 +237,25 @@ export default
|
||||
text: 'Prompt on launch'
|
||||
}
|
||||
},
|
||||
skip_tags: {
|
||||
label: 'Skip Tags',
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
'elementClass': 'Form-textInput',
|
||||
column: 2,
|
||||
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
|
||||
"<p>Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.</p>" +
|
||||
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>",
|
||||
dataTitle: "Skip Tags",
|
||||
dataPlacement: "right",
|
||||
dataContainer: "body",
|
||||
subCheckbox: {
|
||||
variable: 'ask_skip_tags_on_launch',
|
||||
text: 'Prompt on launch'
|
||||
}
|
||||
},
|
||||
checkbox_group: {
|
||||
label: 'Options',
|
||||
type: 'checkbox_group',
|
||||
@@ -281,7 +297,7 @@ export default
|
||||
column: 2,
|
||||
awPopOver: "callback_help",
|
||||
awPopOverWatch: "callback_help",
|
||||
dataPlacement: 'right',
|
||||
dataPlacement: 'top',
|
||||
dataTitle: 'Provisioning Callback URL',
|
||||
dataContainer: "body"
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -161,7 +161,7 @@ angular.module('CredentialsHelper', ['Utilities'])
|
||||
break;
|
||||
case 'net':
|
||||
scope.username_required = true;
|
||||
scope.password_required = true;
|
||||
scope.password_required = false;
|
||||
scope.passwordLabel = 'Password';
|
||||
scope.sshKeyDataLabel = 'SSH Key';
|
||||
break;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,6 +132,9 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
||||
scope.ask_tags_on_launch = (data.ask_tags_on_launch) ? true : false;
|
||||
master.ask_tags_on_launch = scope.ask_tags_on_launch;
|
||||
|
||||
scope.ask_skip_tags_on_launch = (data.ask_skip_tags_on_launch) ? true : false;
|
||||
master.ask_skip_tags_on_launch = scope.ask_skip_tags_on_launch;
|
||||
|
||||
scope.ask_job_type_on_launch = (data.ask_job_type_on_launch) ? true : false;
|
||||
master.ask_job_type_on_launch = scope.ask_job_type_on_launch;
|
||||
|
||||
|
||||
@@ -34,7 +34,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
|
||||
|
||||
@@ -75,6 +75,9 @@ export default
|
||||
case 'missing':
|
||||
result = 'Missing. Click for details';
|
||||
break;
|
||||
case 'canceled':
|
||||
result = 'Canceled. Click for details';
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -50,16 +50,9 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
"aw-pop-over": html,
|
||||
"data-popover-title": title,
|
||||
"data-placement": "right" });
|
||||
elem.removeAttr('ng-click');
|
||||
$compile(elem)($scope);
|
||||
elem.on('shown.bs.popover', function() {
|
||||
$('.popover').each(function() {
|
||||
$compile($(this))($scope); //make nested directives work!
|
||||
});
|
||||
$('.popover-content, .popover-title').click(function() {
|
||||
elem.popover('hide');
|
||||
});
|
||||
});
|
||||
elem.popover('show');
|
||||
$scope.triggerPopover(event);
|
||||
}
|
||||
|
||||
view.inject(InventoryList, { mode: mode, scope: $scope });
|
||||
@@ -250,44 +243,62 @@ function InventoriesList($scope, $rootScope, $location, $log,
|
||||
});
|
||||
|
||||
$scope.showGroupSummary = function(event, id) {
|
||||
var inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.syncStatus !== 'na') {
|
||||
Wait('start');
|
||||
Rest.setUrl(inventory.related.inventory_sources + '?or__source=ec2&or__source=rax&order_by=-last_job_run&page_size=5');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.$emit('GroupSummaryReady', event, inventory, data);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + inventory.related.inventory_sources + ' failed. GET returned status: ' + status
|
||||
try{
|
||||
var elem = $(event.target).parent();
|
||||
// if the popover is visible already, then exit the function here
|
||||
if(elem.data()['bs.popover'].tip().hasClass('in')){
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(err){
|
||||
var inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.syncStatus !== 'na') {
|
||||
Wait('start');
|
||||
Rest.setUrl(inventory.related.inventory_sources + '?or__source=ec2&or__source=rax&order_by=-last_job_run&page_size=5');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.$emit('GroupSummaryReady', event, inventory, data);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + inventory.related.inventory_sources + ' failed. GET returned status: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showHostSummary = function(event, id) {
|
||||
var url, inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.total_hosts > 0) {
|
||||
Wait('start');
|
||||
url = GetBasePath('jobs') + "?type=job&inventory=" + id + "&failed=";
|
||||
url += (inventory.has_active_failures) ? 'true' : "false";
|
||||
url += "&order_by=-finished&page_size=5";
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
$scope.$emit('HostSummaryReady', event, data);
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status
|
||||
try{
|
||||
var elem = $(event.target).parent();
|
||||
// if the popover is visible already, then exit the function here
|
||||
if(elem.data()['bs.popover'].tip().hasClass('in')){
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(err){
|
||||
var url, inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.total_hosts > 0) {
|
||||
Wait('start');
|
||||
url = GetBasePath('jobs') + "?type=job&inventory=" + id + "&failed=";
|
||||
url += (inventory.has_active_failures) ? 'true' : "false";
|
||||
url += "&order_by=-finished&page_size=5";
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
$scope.$emit('HostSummaryReady', event, data);
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -19,18 +19,4 @@
|
||||
<li class="BreadCrumb-item" ng-if="currentState !== 'inventoryManage'"></li>
|
||||
<div class="InventoryManageBreadCrumb-ncy" ng-if="!licenseMissing" ncy-breadcrumb></div>
|
||||
</ol>
|
||||
<div class="BreadCrumb-menuLink"
|
||||
id="bread_crumb_activity_stream"
|
||||
aw-tool-tip="View Activity Stream"
|
||||
data-placement="left"
|
||||
data-trigger="hover"
|
||||
data-container="body"
|
||||
ng-class="{'BreadCrumb-menuLinkActive' : activityStreamActive}"
|
||||
ng-if="showActivityStreamButton"
|
||||
ng-hide= "licenseMissing"
|
||||
ng-click="openActivityStream()">
|
||||
<i class="BreadCrumb-menuLinkImage icon-activity-stream"
|
||||
alt="Activity Stream">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
// equal to case 'ec2' || 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'satellite6' || 'cloudforms' || 'openstack'
|
||||
else{
|
||||
var credentialBasePath = (source === 'ec2') ? GetBasePath('credentials') + '?kind=aws' : GetBasePath('credentials') + (source === '' ? '' : '?kind=' + (source));
|
||||
$scope.cloudCredentialRequired = source !== '' && source !== 'custom' && source !== 'ec2' ? true : false;
|
||||
CredentialList.basePath = credentialBasePath;
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
@@ -122,7 +123,7 @@
|
||||
$scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null;
|
||||
// azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint
|
||||
$scope.source_region_choices = source === 'azure_rm' ? $scope.azure_regions : $scope[source + '_regions'];
|
||||
$scope.cloudCredentialRequired = source !== '' && source !== 'custom' ? true : false;
|
||||
$scope.cloudCredentialRequired = source !== '' && source !== 'custom' && source !== 'ec2' ? true : false;
|
||||
$scope.group_by = null;
|
||||
$scope.source_regions = null;
|
||||
$scope.credential = null;
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
else{
|
||||
var credentialBasePath = (source.value === 'ec2') ? GetBasePath('credentials') + '?kind=aws' : GetBasePath('credentials') + (source.value === '' ? '' : '?kind=' + (source.value));
|
||||
CredentialList.basePath = credentialBasePath;
|
||||
$scope.cloudCredentialRequired = source.value !== '' && source.value !== 'custom' && source.value !== 'ec2' ? true : false;
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: credentialBasePath,
|
||||
@@ -122,7 +123,7 @@
|
||||
// reset fields
|
||||
// azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint
|
||||
$scope.source_region_choices = source.value === 'azure_rm' ? $scope.azure_regions : $scope[source.value + '_regions'];
|
||||
$scope.cloudCredentialRequired = source.value !== '' && source.value !== 'custom' ? true : false;
|
||||
$scope.cloudCredentialRequired = source.value !== '' && source.value !== 'custom' && source.value !== 'ec2' ? true : false;
|
||||
$scope.group_by = null;
|
||||
$scope.source_regions = null;
|
||||
$scope.credential = null;
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -13,11 +13,6 @@ import GroupsListController from './groups/groups-list.controller';
|
||||
export default {
|
||||
name: 'inventoryManage',
|
||||
url: '/inventories/:inventory_id/manage?{group:int}{failed}',
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory',
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
params:{
|
||||
group:{
|
||||
array: true
|
||||
|
||||
@@ -38,11 +38,13 @@
|
||||
break;
|
||||
case 'ok':
|
||||
params.event = 'runner_on_ok';
|
||||
params.changed = 'false';
|
||||
break;
|
||||
case 'failed':
|
||||
params.failed = true;
|
||||
params.event = 'runner_on_failed';
|
||||
break;
|
||||
case 'changed':
|
||||
params.event = 'runner_on_ok';
|
||||
params.changed = true;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -154,6 +154,11 @@
|
||||
<div class="JobDetail-resultRowText">{{ job.job_tags }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.skip_tags">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Skip Tags</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.skip_tags }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow JobDetail-resultRow--variables toggle-show" ng-show="variables">
|
||||
<label class="JobDetail-resultRowLabel JobDetail-extraVarsLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Extra Variables</label>
|
||||
<textarea rows="6" ng-model="variables" name="variables" class="JobDetail-extraVars" id="pre-formatted-variables"></textarea>
|
||||
|
||||
@@ -42,10 +42,14 @@ export default
|
||||
|
||||
}
|
||||
|
||||
if(scope.ask_tags_on_launch && scope.other_prompt_data && scope.other_prompt_data.job_tags){
|
||||
if(scope.ask_tags_on_launch && scope.other_prompt_data && typeof scope.other_prompt_data.job_tags === 'string'){
|
||||
job_launch_data.job_tags = scope.other_prompt_data.job_tags;
|
||||
}
|
||||
|
||||
if(scope.ask_skip_tags_on_launch && scope.other_prompt_data && typeof scope.other_prompt_data.skip_tags === 'string'){
|
||||
job_launch_data.skip_tags = scope.other_prompt_data.skip_tags;
|
||||
}
|
||||
|
||||
if(scope.ask_limit_on_launch && scope.other_prompt_data && scope.other_prompt_data.limit){
|
||||
job_launch_data.limit = scope.other_prompt_data.limit;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export default
|
||||
|
||||
// General catch-all for "other prompts" - used in this link function and to hide the Other Prompts tab when
|
||||
// it should be hidden
|
||||
$scope.has_other_prompts = (data.ask_job_type_on_launch || data.ask_limit_on_launch || data.ask_tags_on_launch || data.ask_variables_on_launch) ? true : false;
|
||||
$scope.has_other_prompts = (data.ask_job_type_on_launch || data.ask_limit_on_launch || data.ask_tags_on_launch || data.ask_skip_tags_on_launch || data.ask_variables_on_launch) ? true : false;
|
||||
$scope.password_needed = data.passwords_needed_to_start && data.passwords_needed_to_start.length > 0;
|
||||
$scope.has_default_inventory = data.defaults && data.defaults.inventory && data.defaults.inventory.id;
|
||||
$scope.has_default_credential = data.defaults && data.defaults.credential && data.defaults.credential.id;
|
||||
@@ -172,6 +172,10 @@ export default
|
||||
$scope.other_prompt_data.job_tags = (data.defaults && data.defaults.job_tags) ? data.defaults.job_tags : "";
|
||||
}
|
||||
|
||||
if($scope.ask_skip_tags_on_launch) {
|
||||
$scope.other_prompt_data.skip_tags = (data.defaults && data.defaults.skip_tags) ? data.defaults.skip_tags : "";
|
||||
}
|
||||
|
||||
if($scope.ask_variables_on_launch) {
|
||||
$scope.jobLaunchVariables = (data.defaults && data.defaults.extra_vars) ? data.defaults.extra_vars : "---";
|
||||
$scope.other_prompt_data.parseType = 'yaml';
|
||||
|
||||
@@ -148,7 +148,15 @@
|
||||
<span class="Form-inputLabel">Job Tags</span>
|
||||
</label>
|
||||
<div>
|
||||
<textarea rows="1" ng-model="other_prompt_data.job_tags" name="tags" class="form-control Form-textArea Form-textInput"></textarea>
|
||||
<textarea rows="5" ng-model="other_prompt_data.job_tags" name="tags" class="form-control Form-textArea Form-textInput"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group Form-formGroup Form-formGroup--singleColumn" ng-if="ask_skip_tags_on_launch">
|
||||
<label for="skip_tags">
|
||||
<span class="Form-inputLabel">Skip Tags</span>
|
||||
</label>
|
||||
<div>
|
||||
<textarea rows="5" ng-model="other_prompt_data.skip_tags" name="skip_tags" class="form-control Form-textArea Form-textInput"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -518,6 +518,7 @@
|
||||
}
|
||||
|
||||
data.ask_tags_on_launch = $scope.ask_tags_on_launch ? $scope.ask_tags_on_launch : false;
|
||||
data.ask_skip_tags_on_launch = $scope.ask_skip_tags_on_launch ? $scope.ask_skip_tags_on_launch : false;
|
||||
data.ask_limit_on_launch = $scope.ask_limit_on_launch ? $scope.ask_limit_on_launch : false;
|
||||
data.ask_job_type_on_launch = $scope.ask_job_type_on_launch ? $scope.ask_job_type_on_launch : false;
|
||||
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
||||
|
||||
@@ -640,6 +640,7 @@ export default
|
||||
}
|
||||
|
||||
data.ask_tags_on_launch = $scope.ask_tags_on_launch ? $scope.ask_tags_on_launch : false;
|
||||
data.ask_skip_tags_on_launch = $scope.ask_skip_tags_on_launch ? $scope.ask_skip_tags_on_launch : false;
|
||||
data.ask_limit_on_launch = $scope.ask_limit_on_launch ? $scope.ask_limit_on_launch : false;
|
||||
data.ask_job_type_on_launch = $scope.ask_job_type_on_launch ? $scope.ask_job_type_on_launch : false;
|
||||
data.ask_inventory_on_launch = $scope.ask_inventory_on_launch ? $scope.ask_inventory_on_launch : false;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"}}'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,7 @@ 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'
|
||||
searchDefault: true,
|
||||
},
|
||||
type: {
|
||||
label: 'Type',
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ export default
|
||||
hover: true,
|
||||
'class': 'table-no-border',
|
||||
multiSelect: true,
|
||||
trackBy: 'group.id',
|
||||
|
||||
fields: {
|
||||
sync_status: {
|
||||
|
||||
@@ -20,6 +20,7 @@ export default
|
||||
hasChildren: true,
|
||||
'class': 'table-no-border',
|
||||
multiSelect: true,
|
||||
trackBy: 'host.id',
|
||||
|
||||
fields: {
|
||||
active_failures: {
|
||||
|
||||
@@ -22,7 +22,8 @@ export default
|
||||
key: true,
|
||||
label: 'Name',
|
||||
columnClass: 'col-lg-5 col-md-5 col-sm-9 col-xs-8',
|
||||
linkTo: '/#/job_templates/{{job_template.id}}'
|
||||
linkTo: '/#/job_templates/{{job_template.id}}',
|
||||
searchDefault: true
|
||||
},
|
||||
description: {
|
||||
label: 'Description',
|
||||
|
||||
@@ -35,7 +35,8 @@ export default
|
||||
label: 'Name',
|
||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6 List-staticColumnAdjacent',
|
||||
defaultSearchField: true,
|
||||
linkTo: '/#/jobs/{{job.id}}'
|
||||
linkTo: '/#/jobs/{{job.id}}',
|
||||
searchDefault: true
|
||||
},
|
||||
finished: {
|
||||
label: 'Finished',
|
||||
|
||||
@@ -37,6 +37,7 @@ export default
|
||||
},
|
||||
name: {
|
||||
key: true,
|
||||
searchDefault: true,
|
||||
label: 'Name',
|
||||
columnClass: "col-lg-4 col-md-4 col-sm-5 col-xs-7 List-staticColumnAdjacent",
|
||||
modalColumnClass: 'col-md-8'
|
||||
|
||||
@@ -149,6 +149,12 @@ export default
|
||||
if(field.type === 'number'){
|
||||
$scope[i] = Number($scope[i]);
|
||||
}
|
||||
if(field.name === "username" && $scope.notification_type.value === "email" && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
if(field.type === 'sensitive' && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
return $scope[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,12 @@ export default
|
||||
if(field.type === 'number'){
|
||||
$scope[i] = Number($scope[i]);
|
||||
}
|
||||
if(field.name === "username" && $scope.notification_type.value === "email" && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
if(field.type === 'sensitive' && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
return $scope[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export default
|
||||
Wait('stop');
|
||||
if (scope.notification_templates) {
|
||||
scope.notification_templates.forEach(function(notification_template, i) {
|
||||
setStatus(notification_template);
|
||||
scope.notification_type_options.forEach(function(type) {
|
||||
if (type.value === notification_template.notification_type) {
|
||||
scope.notification_templates[i].notification_type = type.label;
|
||||
@@ -74,78 +75,33 @@ export default
|
||||
callback: 'choicesReadyNotifierList'
|
||||
});
|
||||
|
||||
function attachElem(event, html, title) {
|
||||
var elem = $(event.target).parent();
|
||||
try {
|
||||
elem.tooltip('hide');
|
||||
elem.popover('destroy');
|
||||
}
|
||||
catch(err) {
|
||||
//ignore
|
||||
}
|
||||
function setStatus(notification_template) {
|
||||
var html, recent_notifications = notification_template.summary_fields.recent_notifications;
|
||||
if (recent_notifications.length > 0) {
|
||||
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>";
|
||||
html += "<th>Status</th>";
|
||||
html += "<th>Time</th>";
|
||||
html += "</tr>\n";
|
||||
html += "</thead>\n";
|
||||
html += "<tbody>\n";
|
||||
|
||||
$('.popover').each(function() {
|
||||
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
||||
$(this).remove();
|
||||
});
|
||||
$('.tooltip').each( function() {
|
||||
// close any lingering tool tipss
|
||||
$(this).hide();
|
||||
});
|
||||
elem.attr({
|
||||
"aw-pop-over": html,
|
||||
"data-popover-title": title,
|
||||
"data-placement": "right" });
|
||||
$compile(elem)(scope);
|
||||
elem.on('shown.bs.popover', function() {
|
||||
$('.popover').each(function() {
|
||||
$compile($(this))(scope); //make nested directives work!
|
||||
recent_notifications.forEach(function(row) {
|
||||
html += "<tr>\n";
|
||||
html += `<td><i class=\"SmartStatus-tooltip--${row.status} fa icon-job-${row.status}"></i></td>`;
|
||||
html += "<td>" + ($filter('longDate')(row.created)).replace(/ /,'<br />') + "</td>\n";
|
||||
html += "</tr>\n";
|
||||
});
|
||||
$('.popover-content, .popover-title').click(function() {
|
||||
elem.popover('hide');
|
||||
});
|
||||
});
|
||||
elem.popover('show');
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
}
|
||||
else {
|
||||
html = "<p>No recent notifications.</p>\n";
|
||||
}
|
||||
notification_template.template_status_html = html;
|
||||
}
|
||||
|
||||
scope.showSummary = function(event, id) {
|
||||
setTimeout(function(){
|
||||
if (!Empty(id)) {
|
||||
var recent_notifications,
|
||||
html, title = "Recent Notifications";
|
||||
|
||||
scope.notification_templates.forEach(function(notification_template){
|
||||
if(notification_template.id === id){
|
||||
recent_notifications = notification_template.summary_fields.recent_notifications;
|
||||
}
|
||||
});
|
||||
if (recent_notifications.length > 0) {
|
||||
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>";
|
||||
html += "<th>Status</th>";
|
||||
html += "<th>Time</th>";
|
||||
html += "</tr>\n";
|
||||
html += "</thead>\n";
|
||||
html += "<tbody>\n";
|
||||
|
||||
recent_notifications.forEach(function(row) {
|
||||
html += "<tr>\n";
|
||||
html += `<td><i class=\"SmartStatus-tooltip--${row.status} fa icon-job-${row.status}"></i></td>`;
|
||||
html += "<td>" + ($filter('longDate')(row.created)).replace(/ /,'<br />') + "</td>\n";
|
||||
html += "</tr>\n";
|
||||
});
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
}
|
||||
else {
|
||||
html = "<p>No recent notifications.</p>\n";
|
||||
}
|
||||
attachElem(event, html, title);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
scope.testNotification = function(){
|
||||
var name = $filter('sanitize')(this.notification_template.name),
|
||||
pending_retries = 10;
|
||||
|
||||
@@ -59,10 +59,6 @@ export default function() {
|
||||
username: {
|
||||
label: 'Username',
|
||||
type: 'text',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "email_required",
|
||||
init: "false"
|
||||
},
|
||||
ngShow: "notification_type.value == 'email' ",
|
||||
subForm: 'typeSubForm'
|
||||
},
|
||||
|
||||
@@ -19,17 +19,14 @@ export default function(){
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'List-staticColumn--smallStatus',
|
||||
iconOnly: true,
|
||||
searchable: false,
|
||||
nosort: true,
|
||||
ngClick: "null",
|
||||
iconOnly: true,
|
||||
excludeModal: true,
|
||||
icons: [{
|
||||
icon: "{{ 'icon-job-' + notification_template.status }}",
|
||||
ngClick: "showSummary($event, notification_template.id)",
|
||||
ngClass: ""
|
||||
}]
|
||||
icon: 'icon-job-{{ notification_template.status }}',
|
||||
awPopOver: '{{ notification_template.template_status_html }}',
|
||||
dataTitle: "Recent Notifications",
|
||||
dataPlacement: 'right',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus'
|
||||
},
|
||||
name: {
|
||||
key: true,
|
||||
|
||||
@@ -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!',
|
||||
|
||||
@@ -28,7 +28,7 @@ function () {
|
||||
obj.passwordLabel = ' Password';
|
||||
obj.email_required = true;
|
||||
obj.port_required = true;
|
||||
obj.password_required = true;
|
||||
obj.password_required = false;
|
||||
break;
|
||||
case 'slack':
|
||||
obj.tokenLabel =' Token';
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ export default ['Rest', '$q', 'GetBasePath', 'Wait', 'ProcessErrors', '$log', fu
|
||||
type = 'text';
|
||||
}
|
||||
|
||||
if (field.searchDefault) {
|
||||
obj.default = true;
|
||||
}
|
||||
|
||||
obj.id = id;
|
||||
obj.value = value;
|
||||
obj.label = label;
|
||||
@@ -76,10 +80,13 @@ export default ['Rest', '$q', 'GetBasePath', 'Wait', 'ProcessErrors', '$log', fu
|
||||
passThrough = partitionedOptions[1];
|
||||
|
||||
var joinOptions = function() {
|
||||
return _.sortBy(_
|
||||
var options = _.sortBy(_
|
||||
.flatten([needsRequest, passThrough]), function(opt) {
|
||||
return opt.id;
|
||||
});
|
||||
|
||||
// put default first
|
||||
return _.flatten(_.partition(options, opt => opt.default));
|
||||
};
|
||||
|
||||
if (needsRequest.length) {
|
||||
|
||||
@@ -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
|
||||
@@ -548,6 +576,10 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
|
||||
template = '<div id="' + element[0].id + '_popover_container" class="popover" role="tooltip"><div class="arrow"></div><h3 id="' + element[0].id + '_popover_title" class="popover-title"></h3><div id="' + element[0].id + '_popover_content" class="popover-content"></div></div>';
|
||||
}
|
||||
|
||||
scope.triggerPopover = function(e){
|
||||
showPopover(e);
|
||||
};
|
||||
|
||||
if (attrs.awPopOverWatch) {
|
||||
$(element).popover({
|
||||
placement: placement,
|
||||
|
||||
@@ -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\">";
|
||||
@@ -1524,8 +1526,13 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
collection = this.form.related[itm];
|
||||
html += `<div id="${itm}_tab"`+
|
||||
`class="Form-tab"`+
|
||||
`ng-click="${this.form.related[itm].disabled} || toggleFormTabs($event)"` +
|
||||
`ng-class="{'is-selected': ${itm}Selected ` ;
|
||||
`ng-click="${this.form.related[itm].disabled} || toggleFormTabs($event)"`;
|
||||
if (collection.awToolTip){
|
||||
html += `aw-tool-tip="${collection.awToolTip}"` +
|
||||
`aw-tip-placement="${collection.dataPlacement}"` +
|
||||
`data-tip-watch="${collection.dataTipWatch}"`;
|
||||
}
|
||||
html += `ng-class="{'is-selected': ${itm}Selected ` ;
|
||||
if(this.form.related[itm].disabled){
|
||||
html += `, 'Form-tab--disabled' : ${this.form.related[itm].disabled }`;
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
} else if (field.link || (field.key && (field.link === undefined || field.link))) {
|
||||
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
|
||||
} else {
|
||||
html += "<a href=\"\">";
|
||||
html += "<a href=\"\"";
|
||||
}
|
||||
if (field.awDroppable) {
|
||||
html += Attr(field, 'awDroppable');
|
||||
@@ -394,7 +394,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
if (field.awPopOver) {
|
||||
html += "aw-pop-over=\"" + field.awPopOver + "\" ";
|
||||
html += (field.dataPlacement) ? "data-placement=\"" + field.dataPlacement + "\" " : "";
|
||||
html += (field.dataTitle) ? "data-title=\"" + field.dataTitle + "\" " : "";
|
||||
html += (field.dataTitle) ? "over-title=\"" + field.dataTitle + "\" " : "";
|
||||
}
|
||||
html += (field.ngClass) ? Attr(field, 'ngClass') : '';
|
||||
html += (field.ngEllipsis) ? "data-ng-bind=\"" + list.iterator + "." + fld + "\" data-ellipsis " : "";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -63,9 +63,16 @@ export default ['$scope',
|
||||
* {@link multiSelectList.controller:multiSelectList#decorateItem `decorateItem`}
|
||||
*/
|
||||
this.registerItem = function(item) {
|
||||
var decoratedItem = this.decorateItem(item);
|
||||
$scope.items = $scope.items.concat(decoratedItem);
|
||||
return decoratedItem;
|
||||
var foundItem = _.find($scope.items, function(existingItem) { return existingItem.id === item.id; });
|
||||
|
||||
if(foundItem) {
|
||||
return foundItem;
|
||||
}
|
||||
else {
|
||||
var decoratedItem = this.decorateItem(item);
|
||||
$scope.items = $scope.items.concat(decoratedItem);
|
||||
return decoratedItem;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,6 +106,7 @@ export default ['$scope',
|
||||
this.decorateItem = function(item) {
|
||||
return {
|
||||
isSelected: false,
|
||||
id: item.id,
|
||||
value: item
|
||||
};
|
||||
};
|
||||
@@ -129,11 +137,11 @@ export default ['$scope',
|
||||
* Triggers {@link multiSelectList.selectionChanged `multiSelectList.selectionChanged`}
|
||||
*/
|
||||
this.deselectAll = function() {
|
||||
$scope.items.forEach(function(item) {
|
||||
item.isSelected = false;
|
||||
});
|
||||
$scope.selection.isExtended = false;
|
||||
rebuildSelections();
|
||||
$scope.items.forEach(function(item) {
|
||||
item.isSelected = false;
|
||||
});
|
||||
$scope.selection.isExtended = false;
|
||||
rebuildSelections();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -33,11 +33,8 @@ export default
|
||||
template: '<input type="checkbox" data-multi-select-list-item ng-model="isSelected" ng-change="userInteractionSelect()">',
|
||||
link: function(scope, element, attrs, multiSelectList) {
|
||||
|
||||
var initializeItem = function() {
|
||||
scope.decoratedItem = multiSelectList.registerItem(scope.item);
|
||||
scope.isSelected = scope.item.isSelected ? true : false;
|
||||
scope.decoratedItem.isSelected = scope.item.isSelected ? true : false;
|
||||
};
|
||||
scope.decoratedItem = multiSelectList.registerItem(scope.item);
|
||||
scope.isSelected = scope.decoratedItem.isSelected ? true : false;
|
||||
|
||||
scope.$watch('isSelected', function(value) {
|
||||
if (value === true) {
|
||||
@@ -47,23 +44,10 @@ export default
|
||||
}
|
||||
});
|
||||
|
||||
scope.$watch('item', function() {
|
||||
// This is necessary for page changes where $scope.item gets updated via ng-repeat
|
||||
// but this link function never gets triggered (and scope.decoratedItem) never
|
||||
// gets updated.
|
||||
initializeItem();
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
multiSelectList.deregisterItem(scope.decoratedItem);
|
||||
});
|
||||
|
||||
scope.userInteractionSelect = function() {
|
||||
scope.$emit("selectedOrDeselected", scope.decoratedItem);
|
||||
};
|
||||
|
||||
initializeItem();
|
||||
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<body data-user-agent="{{userAgent}}">
|
||||
|
||||
<main-menu></main-menu>
|
||||
<bread-crumb ng-if="!includesCurrentState('inventoryManage')"></bread-crumb><div ui-view="groupBreadcrumbs" ng-if="includesCurrentState('inventoryManage')"></div>
|
||||
<bread-crumb ng-show="!includesCurrentState('inventoryManage')"></bread-crumb><div ui-view="groupBreadcrumbs" ng-show="includesCurrentState('inventoryManage')"></div>
|
||||
<toast></toast>
|
||||
|
||||
<div class="container-fluid" id="content-container">
|
||||
|
||||
Reference in New Issue
Block a user