mirror of
https://github.com/ansible/awx.git
synced 2026-02-21 05:00:07 -03:30
Merge branch 'devel' of github.com:ansible/ansible-tower into rbac-devel-integration
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
recursive-include awx *.py
|
recursive-include awx *.py
|
||||||
recursive-include awx/static *.ico
|
recursive-include awx/static *
|
||||||
recursive-include awx/templates *.html
|
recursive-include awx/templates *.html
|
||||||
recursive-include awx/api/templates *.md *.html
|
recursive-include awx/api/templates *.md *.html
|
||||||
recursive-include awx/ui/templates *.html
|
recursive-include awx/ui/templates *.html
|
||||||
|
|||||||
@@ -128,9 +128,10 @@ class Metadata(metadata.SimpleMetadata):
|
|||||||
metadata['added_in_version'] = added_in_version
|
metadata['added_in_version'] = added_in_version
|
||||||
|
|
||||||
# Add type(s) handled by this view/serializer.
|
# Add type(s) handled by this view/serializer.
|
||||||
serializer = view.get_serializer()
|
if hasattr(view, 'get_serializer'):
|
||||||
if hasattr(serializer, 'get_types'):
|
serializer = view.get_serializer()
|
||||||
metadata['types'] = serializer.get_types()
|
if hasattr(serializer, 'get_types'):
|
||||||
|
metadata['types'] = serializer.get_types()
|
||||||
|
|
||||||
# Add search fields if available from the view.
|
# Add search fields if available from the view.
|
||||||
if getattr(view, 'search_fields', None):
|
if getattr(view, 'search_fields', None):
|
||||||
|
|||||||
@@ -772,13 +772,16 @@ class OrganizationSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class ProjectOptionsSerializer(BaseSerializer):
|
class ProjectOptionsSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
scm_clean = serializers.NullBooleanField(default=False)
|
||||||
|
scm_delete_on_update = serializers.NullBooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('*', 'local_path', 'scm_type', 'scm_url', 'scm_branch',
|
fields = ('*', 'local_path', 'scm_type', 'scm_url', 'scm_branch',
|
||||||
'scm_clean', 'scm_delete_on_update', 'credential')
|
'scm_clean', 'scm_delete_on_update', 'credential')
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'scm_type': {
|
'scm_type': {
|
||||||
'allow_null': True
|
'allow_null': True,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
@@ -791,6 +794,12 @@ class ProjectOptionsSerializer(BaseSerializer):
|
|||||||
def validate_scm_type(self, value):
|
def validate_scm_type(self, value):
|
||||||
return value or u''
|
return value or u''
|
||||||
|
|
||||||
|
def validate_scm_clean(self, value):
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
|
def validate_scm_delete_on_update(self, value):
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
@@ -822,18 +831,20 @@ class ProjectOptionsSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
||||||
|
|
||||||
playbooks = serializers.ReadOnlyField(help_text='Array of playbooks available within this project.')
|
|
||||||
scm_delete_on_next_update = serializers.BooleanField(read_only=True)
|
scm_delete_on_next_update = serializers.BooleanField(read_only=True)
|
||||||
|
scm_update_on_launch = serializers.NullBooleanField(default=False)
|
||||||
status = serializers.ChoiceField(choices=Project.PROJECT_STATUS_CHOICES, read_only=True, required=False)
|
status = serializers.ChoiceField(choices=Project.PROJECT_STATUS_CHOICES, read_only=True, required=False)
|
||||||
last_update_failed = serializers.BooleanField(read_only=True)
|
last_update_failed = serializers.BooleanField(read_only=True)
|
||||||
last_updated = serializers.DateTimeField(read_only=True)
|
last_updated = serializers.DateTimeField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Project
|
model = Project
|
||||||
fields = ('*', 'playbooks', 'scm_delete_on_next_update', 'scm_update_on_launch',
|
fields = ('*', 'scm_delete_on_next_update', 'scm_update_on_launch',
|
||||||
'scm_update_cache_timeout') + \
|
'scm_update_cache_timeout') + \
|
||||||
('last_update_failed', 'last_updated') # Backwards compatibility
|
('last_update_failed', 'last_updated') # Backwards compatibility
|
||||||
|
|
||||||
|
def clean_scm_update_on_launch(self, value):
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(ProjectSerializer, self).get_related(obj)
|
res = super(ProjectSerializer, self).get_related(obj)
|
||||||
@@ -858,6 +869,8 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
|||||||
|
|
||||||
class ProjectPlaybooksSerializer(ProjectSerializer):
|
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||||
|
|
||||||
|
playbooks = serializers.ReadOnlyField(help_text='Array of playbooks available within this project.')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Project
|
model = Project
|
||||||
fields = ('playbooks',)
|
fields = ('playbooks',)
|
||||||
@@ -1736,6 +1749,12 @@ class AdHocCommandSerializer(UnifiedJobSerializer):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_field_names(self, declared_fields, info):
|
||||||
|
field_names = super(AdHocCommandSerializer, self).get_field_names(declared_fields, info)
|
||||||
|
# Meta inheritance and -field_name options don't seem to be taking
|
||||||
|
# effect above, so remove the undesired fields here.
|
||||||
|
return tuple(x for x in field_names if x not in ('unified_job_template', 'description'))
|
||||||
|
|
||||||
def build_standard_field(self, field_name, model_field):
|
def build_standard_field(self, field_name, model_field):
|
||||||
field_class, field_kwargs = super(AdHocCommandSerializer, self).build_standard_field(field_name, model_field)
|
field_class, field_kwargs = super(AdHocCommandSerializer, self).build_standard_field(field_name, model_field)
|
||||||
# Load module name choices dynamically from DB settings.
|
# Load module name choices dynamically from DB settings.
|
||||||
|
|||||||
@@ -1379,14 +1379,15 @@ class UnifiedJobTemplateAccess(BaseAccess):
|
|||||||
qs = qs.select_related(
|
qs = qs.select_related(
|
||||||
'created_by',
|
'created_by',
|
||||||
'modified_by',
|
'modified_by',
|
||||||
'project',
|
#'project',
|
||||||
'inventory',
|
#'inventory',
|
||||||
'credential',
|
#'credential',
|
||||||
'cloud_credential',
|
#'cloud_credential',
|
||||||
'next_schedule',
|
'next_schedule',
|
||||||
'last_job',
|
'last_job',
|
||||||
'current_job',
|
'current_job',
|
||||||
)
|
)
|
||||||
|
# FIXME: Figure out how to do select/prefetch on related project/inventory/credential/cloud_credential.
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
class UnifiedJobAccess(BaseAccess):
|
class UnifiedJobAccess(BaseAccess):
|
||||||
@@ -1412,18 +1413,19 @@ class UnifiedJobAccess(BaseAccess):
|
|||||||
qs = qs.select_related(
|
qs = qs.select_related(
|
||||||
'created_by',
|
'created_by',
|
||||||
'modified_by',
|
'modified_by',
|
||||||
'project',
|
#'project',
|
||||||
'inventory',
|
#'inventory',
|
||||||
'credential',
|
#'credential',
|
||||||
'project___credential',
|
#'project___credential',
|
||||||
'inventory_source___credential',
|
#'inventory_source___credential',
|
||||||
'inventory_source___inventory',
|
#'inventory_source___inventory',
|
||||||
'job_template___inventory',
|
#'job_template___inventory',
|
||||||
'job_template___project',
|
#'job_template___project',
|
||||||
'job_template___credential',
|
#'job_template___credential',
|
||||||
'job_template___cloud_credential',
|
#'job_template___cloud_credential',
|
||||||
)
|
)
|
||||||
qs = qs.prefetch_related('unified_job_template')
|
qs = qs.prefetch_related('unified_job_template')
|
||||||
|
# FIXME: Figure out how to do select/prefetch on related project/inventory/credential/cloud_credential.
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
class ScheduleAccess(BaseAccess):
|
class ScheduleAccess(BaseAccess):
|
||||||
|
|||||||
@@ -9,7 +9,15 @@
|
|||||||
* @name controllers.function:Activity Stream
|
* @name controllers.function:Activity Stream
|
||||||
* @description This controller controls the activity stream.
|
* @description This controller controls the activity stream.
|
||||||
*/
|
*/
|
||||||
function activityStreamController($scope, Stream) {
|
function activityStreamController($scope, $state, subTitle, Stream, GetTargetTitle) {
|
||||||
|
|
||||||
|
// subTitle is passed in via a resolve on the route. If there is no subtitle
|
||||||
|
// generated in the resolve then we go get the targets generic title.
|
||||||
|
|
||||||
|
// Get the streams sub-title based on the target. This scope variable is leveraged
|
||||||
|
// when we define the activity stream list. Specifically it is included in the list
|
||||||
|
// title.
|
||||||
|
$scope.streamSubTitle = subTitle ? subTitle : GetTargetTitle($state.params.target);
|
||||||
|
|
||||||
// Open the stream
|
// Open the stream
|
||||||
Stream({
|
Stream({
|
||||||
@@ -18,4 +26,4 @@ function activityStreamController($scope, Stream) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ['$scope', 'Stream', activityStreamController];
|
export default ['$scope', '$state', 'subTitle', 'Stream', 'GetTargetTitle', activityStreamController];
|
||||||
|
|||||||
@@ -14,4 +14,39 @@ export default {
|
|||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
label: "ACTIVITY STREAM"
|
label: "ACTIVITY STREAM"
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
subTitle:
|
||||||
|
[ '$stateParams',
|
||||||
|
'Rest',
|
||||||
|
'ModelToPlural',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
function($stateParams, rest, ModelToPlural, getBasePath, ProcessErrors) {
|
||||||
|
// If we have a target and an ID then we want to go grab the name of the object
|
||||||
|
// that we're examining with the activity stream. This name will be used in the
|
||||||
|
// subtitle.
|
||||||
|
if ($stateParams.target && $stateParams.id) {
|
||||||
|
var target = $stateParams.target;
|
||||||
|
var id = $stateParams.id;
|
||||||
|
|
||||||
|
var url = getBasePath(ModelToPlural(target)) + id + '/';
|
||||||
|
rest.setUrl(url);
|
||||||
|
return rest.get()
|
||||||
|
.then(function(data) {
|
||||||
|
// Return the name or the username depending on which is available.
|
||||||
|
return (data.data.name || data.data.username);
|
||||||
|
}).catch(function (response) {
|
||||||
|
ProcessErrors(null, response.data, response.status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get title info. GET returned status: ' +
|
||||||
|
response.status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -185,7 +185,9 @@ var tower = angular.module('Tower', [
|
|||||||
'pendolytics',
|
'pendolytics',
|
||||||
'ui.router',
|
'ui.router',
|
||||||
'ncy-angular-breadcrumb',
|
'ncy-angular-breadcrumb',
|
||||||
'scheduler'
|
'scheduler',
|
||||||
|
'ApiModelHelper',
|
||||||
|
'ActivityStreamHelper'
|
||||||
])
|
])
|
||||||
|
|
||||||
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
|
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import RelatedSearch from "./helpers/related-search";
|
|||||||
import Search from "./helpers/search";
|
import Search from "./helpers/search";
|
||||||
import Teams from "./helpers/teams";
|
import Teams from "./helpers/teams";
|
||||||
import AdhocHelper from "./helpers/Adhoc";
|
import AdhocHelper from "./helpers/Adhoc";
|
||||||
|
import ApiModelHelper from "./helpers/ApiModel";
|
||||||
|
import ActivityStreamHelper from "./helpers/ActivityStream";
|
||||||
|
|
||||||
export
|
export
|
||||||
{ AboutAnsible,
|
{ AboutAnsible,
|
||||||
@@ -76,5 +78,7 @@ export
|
|||||||
RelatedSearch,
|
RelatedSearch,
|
||||||
Search,
|
Search,
|
||||||
Teams,
|
Teams,
|
||||||
AdhocHelper
|
AdhocHelper,
|
||||||
|
ApiModelHelper,
|
||||||
|
ActivityStreamHelper
|
||||||
};
|
};
|
||||||
|
|||||||
58
awx/ui/client/src/helpers/ActivityStream.js
Normal file
58
awx/ui/client/src/helpers/ActivityStream.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name helpers.function:ActivityStream
|
||||||
|
* @description Helper functions for the activity stream
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('ActivityStreamHelper', ['Utilities'])
|
||||||
|
.factory('GetTargetTitle', [
|
||||||
|
function () {
|
||||||
|
return function (target) {
|
||||||
|
|
||||||
|
var rtnTitle = 'DASHBOARD';
|
||||||
|
|
||||||
|
switch(target) {
|
||||||
|
case 'project':
|
||||||
|
rtnTitle = 'PROJECTS';
|
||||||
|
break;
|
||||||
|
case 'inventory':
|
||||||
|
rtnTitle = 'INVENTORIES';
|
||||||
|
break;
|
||||||
|
case 'job_template':
|
||||||
|
rtnTitle = 'JOB TEMPLATES';
|
||||||
|
break;
|
||||||
|
case 'credential':
|
||||||
|
rtnTitle = 'CREDENTIALS';
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
rtnTitle = 'USERS';
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
rtnTitle = 'TEAMS';
|
||||||
|
break;
|
||||||
|
case 'organization':
|
||||||
|
rtnTitle = 'ORGANIZATIONS';
|
||||||
|
break;
|
||||||
|
case 'management_job':
|
||||||
|
rtnTitle = 'MANAGEMENT JOBS';
|
||||||
|
break;
|
||||||
|
case 'inventory_script':
|
||||||
|
rtnTitle = 'INVENTORY SCRIPTS';
|
||||||
|
break;
|
||||||
|
case 'schedule':
|
||||||
|
rtnTitle = 'SCHEDULES';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtnTitle;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
||||||
100
awx/ui/client/src/helpers/ApiModel.js
Normal file
100
awx/ui/client/src/helpers/ApiModel.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name helpers.function:ApiModel
|
||||||
|
* @description Helper functions to convert singular/plural versions of our models to the opposite
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('ApiModelHelper', ['Utilities'])
|
||||||
|
.factory('ModelToSingular', [
|
||||||
|
function () {
|
||||||
|
return function (model) {
|
||||||
|
// This function takes in the plural model string and spits out the singular
|
||||||
|
// version.
|
||||||
|
|
||||||
|
var singularModel;
|
||||||
|
|
||||||
|
switch(model) {
|
||||||
|
case 'projects':
|
||||||
|
singularModel = 'project';
|
||||||
|
break;
|
||||||
|
case 'inventories':
|
||||||
|
singularModel = 'inventory';
|
||||||
|
break;
|
||||||
|
case 'job_templates':
|
||||||
|
singularModel = 'job_template';
|
||||||
|
break;
|
||||||
|
case 'credentials':
|
||||||
|
singularModel = 'credential';
|
||||||
|
break;
|
||||||
|
case 'users':
|
||||||
|
singularModel = 'user';
|
||||||
|
break;
|
||||||
|
case 'teams':
|
||||||
|
singularModel = 'team';
|
||||||
|
break;
|
||||||
|
case 'organizations':
|
||||||
|
singularModel = 'organization';
|
||||||
|
break;
|
||||||
|
case 'management_jobs':
|
||||||
|
singularModel = 'management_job';
|
||||||
|
break;
|
||||||
|
case 'inventory_scripts':
|
||||||
|
singularModel = 'inventory_script';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return singularModel;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.factory('ModelToPlural', [
|
||||||
|
function () {
|
||||||
|
return function (model) {
|
||||||
|
// This function takes in the singular model string and spits out the plural
|
||||||
|
// version.
|
||||||
|
|
||||||
|
var pluralModel;
|
||||||
|
|
||||||
|
switch(model) {
|
||||||
|
case 'project':
|
||||||
|
pluralModel = 'projects';
|
||||||
|
break;
|
||||||
|
case 'inventory':
|
||||||
|
pluralModel = 'inventories';
|
||||||
|
break;
|
||||||
|
case 'job_template':
|
||||||
|
pluralModel = 'job_templates';
|
||||||
|
break;
|
||||||
|
case 'credential':
|
||||||
|
pluralModel = 'credentials';
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
pluralModel = 'users';
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
pluralModel = 'teams';
|
||||||
|
break;
|
||||||
|
case 'organization':
|
||||||
|
pluralModel = 'organizations';
|
||||||
|
break;
|
||||||
|
case 'management_job':
|
||||||
|
pluralModel = 'management_jobs';
|
||||||
|
break;
|
||||||
|
case 'inventory_script':
|
||||||
|
pluralModel = 'inventory_scripts';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluralModel;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
||||||
@@ -12,7 +12,7 @@ export default
|
|||||||
name: 'activities',
|
name: 'activities',
|
||||||
iterator: 'activity',
|
iterator: 'activity',
|
||||||
editTitle: 'Activity Stream',
|
editTitle: 'Activity Stream',
|
||||||
listTitle: 'Activity Stream',
|
listTitle: 'Activity Stream<span ng-show="streamSubTitle"><div class="List-titleLockup"></div>{{streamSubTitle}}<span>',
|
||||||
listTitleBadge: false,
|
listTitleBadge: false,
|
||||||
selectInstructions: '',
|
selectInstructions: '',
|
||||||
index: false,
|
index: false,
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ angular.module('PromptDialog', ['Utilities', 'sanitizeFilter'])
|
|||||||
|
|
||||||
$('#prompt-modal').off('hidden.bs.modal');
|
$('#prompt-modal').off('hidden.bs.modal');
|
||||||
$('#prompt-modal').modal({
|
$('#prompt-modal').modal({
|
||||||
backdrop: 'local_backdrop',
|
backdrop: 'static',
|
||||||
keyboard: true,
|
keyboard: true,
|
||||||
show: true
|
show: true
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user