mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge branch 'devel' of github.com:ansible/ansible-tower into rbac-devel-integration
This commit is contained in:
commit
29f8d6b778
@ -1,5 +1,5 @@
|
||||
recursive-include awx *.py
|
||||
recursive-include awx/static *.ico
|
||||
recursive-include awx/static *
|
||||
recursive-include awx/templates *.html
|
||||
recursive-include awx/api/templates *.md *.html
|
||||
recursive-include awx/ui/templates *.html
|
||||
|
||||
@ -128,9 +128,10 @@ class Metadata(metadata.SimpleMetadata):
|
||||
metadata['added_in_version'] = added_in_version
|
||||
|
||||
# Add type(s) handled by this view/serializer.
|
||||
serializer = view.get_serializer()
|
||||
if hasattr(serializer, 'get_types'):
|
||||
metadata['types'] = serializer.get_types()
|
||||
if hasattr(view, 'get_serializer'):
|
||||
serializer = view.get_serializer()
|
||||
if hasattr(serializer, 'get_types'):
|
||||
metadata['types'] = serializer.get_types()
|
||||
|
||||
# Add search fields if available from the view.
|
||||
if getattr(view, 'search_fields', None):
|
||||
|
||||
@ -772,13 +772,16 @@ class OrganizationSerializer(BaseSerializer):
|
||||
|
||||
class ProjectOptionsSerializer(BaseSerializer):
|
||||
|
||||
scm_clean = serializers.NullBooleanField(default=False)
|
||||
scm_delete_on_update = serializers.NullBooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
fields = ('*', 'local_path', 'scm_type', 'scm_url', 'scm_branch',
|
||||
'scm_clean', 'scm_delete_on_update', 'credential')
|
||||
extra_kwargs = {
|
||||
'scm_type': {
|
||||
'allow_null': True
|
||||
}
|
||||
'allow_null': True,
|
||||
},
|
||||
}
|
||||
|
||||
def get_related(self, obj):
|
||||
@ -791,6 +794,12 @@ class ProjectOptionsSerializer(BaseSerializer):
|
||||
def validate_scm_type(self, value):
|
||||
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):
|
||||
errors = {}
|
||||
|
||||
@ -822,18 +831,20 @@ class ProjectOptionsSerializer(BaseSerializer):
|
||||
|
||||
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_update_on_launch = serializers.NullBooleanField(default=False)
|
||||
status = serializers.ChoiceField(choices=Project.PROJECT_STATUS_CHOICES, read_only=True, required=False)
|
||||
last_update_failed = serializers.BooleanField(read_only=True)
|
||||
last_updated = serializers.DateTimeField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
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') + \
|
||||
('last_update_failed', 'last_updated') # Backwards compatibility
|
||||
|
||||
|
||||
def clean_scm_update_on_launch(self, value):
|
||||
return bool(value)
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(ProjectSerializer, self).get_related(obj)
|
||||
@ -858,6 +869,8 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
||||
|
||||
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||
|
||||
playbooks = serializers.ReadOnlyField(help_text='Array of playbooks available within this project.')
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
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):
|
||||
field_class, field_kwargs = super(AdHocCommandSerializer, self).build_standard_field(field_name, model_field)
|
||||
# Load module name choices dynamically from DB settings.
|
||||
|
||||
@ -1379,14 +1379,15 @@ class UnifiedJobTemplateAccess(BaseAccess):
|
||||
qs = qs.select_related(
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'project',
|
||||
'inventory',
|
||||
'credential',
|
||||
'cloud_credential',
|
||||
#'project',
|
||||
#'inventory',
|
||||
#'credential',
|
||||
#'cloud_credential',
|
||||
'next_schedule',
|
||||
'last_job',
|
||||
'current_job',
|
||||
)
|
||||
# FIXME: Figure out how to do select/prefetch on related project/inventory/credential/cloud_credential.
|
||||
return qs
|
||||
|
||||
class UnifiedJobAccess(BaseAccess):
|
||||
@ -1412,18 +1413,19 @@ class UnifiedJobAccess(BaseAccess):
|
||||
qs = qs.select_related(
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'project',
|
||||
'inventory',
|
||||
'credential',
|
||||
'project___credential',
|
||||
'inventory_source___credential',
|
||||
'inventory_source___inventory',
|
||||
'job_template___inventory',
|
||||
'job_template___project',
|
||||
'job_template___credential',
|
||||
'job_template___cloud_credential',
|
||||
#'project',
|
||||
#'inventory',
|
||||
#'credential',
|
||||
#'project___credential',
|
||||
#'inventory_source___credential',
|
||||
#'inventory_source___inventory',
|
||||
#'job_template___inventory',
|
||||
#'job_template___project',
|
||||
#'job_template___credential',
|
||||
#'job_template___cloud_credential',
|
||||
)
|
||||
qs = qs.prefetch_related('unified_job_template')
|
||||
# FIXME: Figure out how to do select/prefetch on related project/inventory/credential/cloud_credential.
|
||||
return qs
|
||||
|
||||
class ScheduleAccess(BaseAccess):
|
||||
|
||||
@ -9,13 +9,21 @@
|
||||
* @name controllers.function: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
|
||||
Stream({
|
||||
scope: $scope
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default ['$scope', 'Stream', activityStreamController];
|
||||
export default ['$scope', '$state', 'subTitle', 'Stream', 'GetTargetTitle', activityStreamController];
|
||||
|
||||
@ -14,4 +14,39 @@ export default {
|
||||
ncyBreadcrumb: {
|
||||
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',
|
||||
'ui.router',
|
||||
'ncy-angular-breadcrumb',
|
||||
'scheduler'
|
||||
'scheduler',
|
||||
'ApiModelHelper',
|
||||
'ActivityStreamHelper'
|
||||
])
|
||||
|
||||
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
|
||||
|
||||
@ -41,6 +41,8 @@ import RelatedSearch from "./helpers/related-search";
|
||||
import Search from "./helpers/search";
|
||||
import Teams from "./helpers/teams";
|
||||
import AdhocHelper from "./helpers/Adhoc";
|
||||
import ApiModelHelper from "./helpers/ApiModel";
|
||||
import ActivityStreamHelper from "./helpers/ActivityStream";
|
||||
|
||||
export
|
||||
{ AboutAnsible,
|
||||
@ -76,5 +78,7 @@ export
|
||||
RelatedSearch,
|
||||
Search,
|
||||
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',
|
||||
iterator: 'activity',
|
||||
editTitle: 'Activity Stream',
|
||||
listTitle: 'Activity Stream',
|
||||
listTitle: 'Activity Stream<span ng-show="streamSubTitle"><div class="List-titleLockup"></div>{{streamSubTitle}}<span>',
|
||||
listTitleBadge: false,
|
||||
selectInstructions: '',
|
||||
index: false,
|
||||
|
||||
@ -119,7 +119,7 @@ angular.module('PromptDialog', ['Utilities', 'sanitizeFilter'])
|
||||
|
||||
$('#prompt-modal').off('hidden.bs.modal');
|
||||
$('#prompt-modal').modal({
|
||||
backdrop: 'local_backdrop',
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
show: true
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user