Merge branch 'devel' into 4853-survey-workflow-buttons

This commit is contained in:
Michael Abashian
2017-06-29 10:13:13 -04:00
committed by GitHub
32 changed files with 105 additions and 69 deletions

View File

@@ -937,7 +937,7 @@ install:
$(PYTHON) setup.py install $(SETUP_INSTALL_ARGS) $(PYTHON) setup.py install $(SETUP_INSTALL_ARGS)
docker-auth: docker-auth:
docker login -e 1234@5678.com -u oauth2accesstoken -p "$(GCLOUD_AUTH)" https://gcr.io docker login -u oauth2accesstoken -p "$(GCLOUD_AUTH)" https://gcr.io
# Docker isolated rampart # Docker isolated rampart
docker-isolated: docker-isolated:

View File

@@ -59,7 +59,7 @@ import ansiconv
from social.backends.utils import load_backends from social.backends.utils import load_backends
# AWX # AWX
from awx.main.tasks import send_notifications, update_host_smart_inventory_memberships, delete_inventory from awx.main.tasks import send_notifications, update_host_smart_inventory_memberships
from awx.main.access import get_user_queryset from awx.main.access import get_user_queryset
from awx.main.ha import is_ha_environment from awx.main.ha import is_ha_environment
from awx.api.authentication import TaskAuthentication, TokenGetAuthentication from awx.api.authentication import TaskAuthentication, TokenGetAuthentication
@@ -1839,15 +1839,13 @@ class InventoryDetail(ControlledByScmMixin, RetrieveUpdateDestroyAPIView):
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
if obj.pending_deletion is True:
return Response(dict(error=_("Inventory is already being deleted.")), status=status.HTTP_400_BAD_REQUEST)
if not request.user.can_access(self.model, 'delete', obj): if not request.user.can_access(self.model, 'delete', obj):
raise PermissionDenied() raise PermissionDenied()
obj.websocket_emit_status('pending_deletion') try:
delete_inventory.delay(obj.id) obj.schedule_deletion()
obj.pending_deletion = True return Response(status=status.HTTP_202_ACCEPTED)
obj.save(update_fields=['pending_deletion']) except RuntimeError, e:
return Response(status=status.HTTP_202_ACCEPTED) return Response(dict(error=_("{0}".format(e))), status=status.HTTP_400_BAD_REQUEST)
class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView): class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):

View File

@@ -78,7 +78,8 @@ class IsolatedManager(object):
'CALLBACK_QUEUE': '', 'CALLBACK_QUEUE': '',
'CALLBACK_CONNECTION': '', 'CALLBACK_CONNECTION': '',
'ANSIBLE_RETRY_FILES_ENABLED': 'False', 'ANSIBLE_RETRY_FILES_ENABLED': 'False',
'ANSIBLE_HOST_KEY_CHECKING': 'False' 'ANSIBLE_HOST_KEY_CHECKING': 'False',
'ANSIBLE_LIBRARY': os.path.join(os.path.dirname(awx.__file__), 'plugins', 'isolated')
} }
@classmethod @classmethod
@@ -204,7 +205,7 @@ class IsolatedManager(object):
os.symlink(self.cwd, self.path_to('project')) os.symlink(self.cwd, self.path_to('project'))
# create directories for build artifacts to live in # create directories for build artifacts to live in
os.makedirs(self.path_to('artifacts', 'job_events'), mode=stat.S_IRUSR | stat.S_IWUSR) os.makedirs(self.path_to('artifacts', 'job_events'), mode=stat.S_IXUSR + stat.S_IWUSR + stat.S_IRUSR)
def _missing_artifacts(self, path_list, buff): def _missing_artifacts(self, path_list, buff):
missing_artifacts = filter(lambda path: not os.path.exists(path), path_list) missing_artifacts = filter(lambda path: not os.path.exists(path), path_list)
@@ -347,7 +348,6 @@ class IsolatedManager(object):
args = ['ansible-playbook', '-u', settings.AWX_ISOLATED_USERNAME, '-i', args = ['ansible-playbook', '-u', settings.AWX_ISOLATED_USERNAME, '-i',
hostname_string, 'heartbeat_isolated.yml'] hostname_string, 'heartbeat_isolated.yml']
env = cls._base_management_env() env = cls._base_management_env()
env['ANSIBLE_LIBRARY'] = os.path.join(os.path.dirname(awx.__file__), 'lib', 'management_modules')
env['ANSIBLE_STDOUT_CALLBACK'] = 'json' env['ANSIBLE_STDOUT_CALLBACK'] = 'json'
buff = cStringIO.StringIO() buff = cStringIO.StringIO()

View File

@@ -76,6 +76,11 @@ class Migration(migrations.Migration):
name='pending_deletion', name='pending_deletion',
field=models.BooleanField(default=False, help_text='Flag indicating the inventory is being deleted.', editable=False), field=models.BooleanField(default=False, help_text='Flag indicating the inventory is being deleted.', editable=False),
), ),
migrations.AlterField(
model_name='inventory',
name='organization',
field=models.ForeignKey(related_name='inventories', on_delete=models.deletion.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True),
),
# Facts # Facts
migrations.AlterField( migrations.AlterField(

View File

@@ -63,7 +63,8 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin):
'Organization', 'Organization',
related_name='inventories', related_name='inventories',
help_text=_('Organization containing this inventory.'), help_text=_('Organization containing this inventory.'),
on_delete=models.CASCADE, on_delete=models.SET_NULL,
null=True,
) )
variables = models.TextField( variables = models.TextField(
blank=True, blank=True,
@@ -373,6 +374,16 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin):
raise ValidationError(_("Credential kind must be 'insights'.")) raise ValidationError(_("Credential kind must be 'insights'."))
return self.insights_credential return self.insights_credential
@transaction.atomic
def schedule_deletion(self):
from awx.main.tasks import delete_inventory
if self.pending_deletion is True:
raise RuntimeError("Inventory is already pending deletion.")
self.websocket_emit_status('pending_deletion')
delete_inventory.delay(self.pk)
self.pending_deletion = True
self.save(update_fields=['pending_deletion'])
class SmartInventoryMembership(BaseModel): class SmartInventoryMembership(BaseModel):
''' '''

View File

@@ -510,3 +510,13 @@ def get_current_user_from_drf_request(sender, **kwargs):
request = get_current_request() request = get_current_request()
drf_request = getattr(request, 'drf_request', None) drf_request = getattr(request, 'drf_request', None)
return (getattr(drf_request, 'user', False), 0) return (getattr(drf_request, 'user', False), 0)
@receiver(pre_delete, sender=Organization)
def delete_inventory_for_org(sender, instance, **kwargs):
inventories = Inventory.objects.filter(organization__pk=instance.pk)
for inventory in inventories:
try:
inventory.schedule_deletion()
except RuntimeError, e:
logger.debug(e)

View File

@@ -360,18 +360,17 @@ def update_host_smart_inventory_memberships():
def delete_inventory(inventory_id): def delete_inventory(inventory_id):
with ignore_inventory_computed_fields(), \ with ignore_inventory_computed_fields(), \
ignore_inventory_group_removal(): ignore_inventory_group_removal():
with transaction.atomic(): try:
try: i = Inventory.objects.get(id=inventory_id)
i = Inventory.objects.get(id=inventory_id)
except Inventory.DoesNotExist:
logger.error("Delete Inventory failed due to missing inventory: " + str(inventory_id))
return
i.delete() i.delete()
emit_channel_notification( emit_channel_notification(
'inventories-status_changed', 'inventories-status_changed',
{'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'} {'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'}
) )
logger.debug('Deleted inventory: %s' % inventory_id) logger.debug('Deleted inventory: %s' % inventory_id)
except Inventory.DoesNotExist:
logger.error("Delete Inventory failed due to missing inventory: " + str(inventory_id))
return
class BaseTask(Task): class BaseTask(Task):

View File

@@ -59,7 +59,7 @@ def test_async_inventory_duplicate_deletion_prevention(delete, get, inventory, a
resp = delete(reverse('api:inventory_detail', kwargs={'pk': inventory.id}), alice) resp = delete(reverse('api:inventory_detail', kwargs={'pk': inventory.id}), alice)
assert resp.status_code == 400 assert resp.status_code == 400
assert resp.data['error'] == 'Inventory is already being deleted.' assert resp.data['error'] == 'Inventory is already pending deletion.'
@pytest.mark.parametrize('order_by', ('script', '-script', 'script,pk', '-script,pk')) @pytest.mark.parametrize('order_by', ('script', '-script', 'script,pk', '-script,pk'))
@@ -314,12 +314,12 @@ class TestControlledBySCM:
@pytest.mark.django_db @pytest.mark.django_db
class TestInsightsCredential: class TestInsightsCredential:
def test_insights_credential(self, patch, insights_inventory, admin_user, insights_credential): def test_insights_credential(self, patch, insights_inventory, admin_user, insights_credential):
patch(insights_inventory.get_absolute_url(), patch(insights_inventory.get_absolute_url(),
{'insights_credential': insights_credential.id}, admin_user, {'insights_credential': insights_credential.id}, admin_user,
expect=200) expect=200)
def test_non_insights_credential(self, patch, insights_inventory, admin_user, scm_credential): def test_non_insights_credential(self, patch, insights_inventory, admin_user, scm_credential):
patch(insights_inventory.get_absolute_url(), patch(insights_inventory.get_absolute_url(),
{'insights_credential': scm_credential.id}, admin_user, {'insights_credential': scm_credential.id}, admin_user,
expect=400) expect=400)

View File

@@ -149,7 +149,7 @@ def test_build_isolated_job_data(private_data_dir, rsa_key):
path = os.path.join(private_data_dir, 'artifacts') path = os.path.join(private_data_dir, 'artifacts')
assert os.path.isdir(path) assert os.path.isdir(path)
assert stat.S_IMODE(os.stat(path).st_mode) == stat.S_IRUSR + stat.S_IWUSR # user rw assert stat.S_IMODE(os.stat(path).st_mode) == stat.S_IXUSR + stat.S_IWUSR + stat.S_IRUSR # user rwx
path = os.path.join(private_data_dir, 'args') path = os.path.join(private_data_dir, 'args')
with open(path, 'r') as f: with open(path, 'r') as f:

View File

@@ -106,14 +106,14 @@ function(i18n) {
related: { related: {
ansible_facts: { ansible_facts: {
name: 'ansible_facts', name: 'ansible_facts',
awToolTip: i18n._('Please save before viewing facts'), awToolTip: i18n._('Please save before viewing facts.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Facts'), title: i18n._('Facts'),
skipGenerator: true skipGenerator: true
}, },
groups: { groups: {
name: 'groups', name: 'groups',
awToolTip: i18n._('Please save before defining groups'), awToolTip: i18n._('Please save before defining groups.'),
dataPlacement: 'top', dataPlacement: 'top',
ngClick: "$state.go('hosts.edit.groups')", ngClick: "$state.go('hosts.edit.groups')",
title: i18n._('Groups'), title: i18n._('Groups'),
@@ -122,7 +122,7 @@ function(i18n) {
}, },
insights: { insights: {
name: 'insights', name: 'insights',
awToolTip: i18n._('Please save before viewing Insights'), awToolTip: i18n._('Please save before viewing Insights.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Insights'), title: i18n._('Insights'),
skipGenerator: true, skipGenerator: true,

View File

@@ -8,7 +8,7 @@
export default ['i18n', function(i18n) { export default ['i18n', function(i18n) {
return { return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view // These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
awToolTip: i18n._('Please save and run a job to view'), awToolTip: i18n._('Please save and run a job to view.'),
dataPlacement: 'top', dataPlacement: 'top',
name: 'completed_jobs', name: 'completed_jobs',
basePath: 'unified_jobs', basePath: 'unified_jobs',

View File

@@ -83,7 +83,7 @@ function(i18n){
related: { related: {
nested_groups: { nested_groups: {
name: 'nested_groups', name: 'nested_groups',
awToolTip: i18n._('Please save before defining groups'), awToolTip: i18n._('Please save before defining groups.'),
dataPlacement: 'top', dataPlacement: 'top',
ngClick: "$state.go('inventories.edit.groups.edit.nested_groups')", ngClick: "$state.go('inventories.edit.groups.edit.nested_groups')",
title: i18n._('Groups'), title: i18n._('Groups'),
@@ -91,7 +91,7 @@ function(i18n){
}, },
nested_hosts: { nested_hosts: {
name: 'nested_hosts', name: 'nested_hosts',
awToolTip: i18n._('Please save before defining hosts'), awToolTip: i18n._('Please save before defining hosts.'),
dataPlacement: 'top', dataPlacement: 'top',
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts')", ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts')",
include: "NestedHostsListDefinition", include: "NestedHostsListDefinition",

View File

@@ -107,14 +107,14 @@ function(i18n) {
related: { related: {
ansible_facts: { ansible_facts: {
name: 'ansible_facts', name: 'ansible_facts',
awToolTip: i18n._('Please save before viewing facts'), awToolTip: i18n._('Please save before viewing facts.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Facts'), title: i18n._('Facts'),
skipGenerator: true skipGenerator: true
}, },
nested_groups: { nested_groups: {
name: 'nested_groups', name: 'nested_groups',
awToolTip: i18n._('Please save before defining groups'), awToolTip: i18n._('Please save before defining groups.'),
dataPlacement: 'top', dataPlacement: 'top',
ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts.edit.nested_groups')", ngClick: "$state.go('inventories.edit.groups.edit.nested_hosts.edit.nested_groups')",
title: i18n._('Groups'), title: i18n._('Groups'),

View File

@@ -107,14 +107,14 @@ function(i18n) {
related: { related: {
ansible_facts: { ansible_facts: {
name: 'ansible_facts', name: 'ansible_facts',
awToolTip: i18n._('Please save before viewing facts'), awToolTip: i18n._('Please save before viewing facts.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Facts'), title: i18n._('Facts'),
skipGenerator: true skipGenerator: true
}, },
nested_groups: { nested_groups: {
name: 'nested_groups', name: 'nested_groups',
awToolTip: i18n._('Please save before defining groups'), awToolTip: i18n._('Please save before defining groups.'),
dataPlacement: 'top', dataPlacement: 'top',
ngClick: "$state.go('inventories.edit.hosts.edit.nested_groups')", ngClick: "$state.go('inventories.edit.hosts.edit.nested_groups')",
title: i18n._('Groups'), title: i18n._('Groups'),
@@ -122,7 +122,7 @@ function(i18n) {
}, },
insights: { insights: {
name: 'insights', name: 'insights',
awToolTip: i18n._('Please save before viewing Insights'), awToolTip: i18n._('Please save before viewing Insights.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Insights'), title: i18n._('Insights'),
skipGenerator: true, skipGenerator: true,

View File

@@ -110,7 +110,7 @@ export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCo
related: { related: {
permissions: { permissions: {
name: 'permissions', name: 'permissions',
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
dataPlacement: 'top', dataPlacement: 'top',
basePath: 'api/v2/inventories/{{$stateParams.smartinventory_id}}/access_list/', basePath: 'api/v2/inventories/{{$stateParams.smartinventory_id}}/access_list/',
type: 'collection', type: 'collection',

View File

@@ -127,7 +127,7 @@ function(i18n, InventoryCompletedJobsList) {
related: { related: {
permissions: { permissions: {
name: 'permissions', name: 'permissions',
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
dataPlacement: 'top', dataPlacement: 'top',
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/access_list/', basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/access_list/',
type: 'collection', type: 'collection',
@@ -172,7 +172,7 @@ function(i18n, InventoryCompletedJobsList) {
}, },
groups: { groups: {
name: 'groups', name: 'groups',
awToolTip: i18n._('Please save before creating groups'), awToolTip: i18n._('Please save before creating groups.'),
dataPlacement: 'top', dataPlacement: 'top',
include: "GroupList", include: "GroupList",
title: i18n._('Groups'), title: i18n._('Groups'),
@@ -181,7 +181,7 @@ function(i18n, InventoryCompletedJobsList) {
}, },
hosts: { hosts: {
name: 'hosts', name: 'hosts',
awToolTip: i18n._('Please save before creating hosts'), awToolTip: i18n._('Please save before creating hosts.'),
dataPlacement: 'top', dataPlacement: 'top',
include: "RelatedHostsListDefinition", include: "RelatedHostsListDefinition",
title: i18n._('Hosts'), title: i18n._('Hosts'),
@@ -190,7 +190,7 @@ function(i18n, InventoryCompletedJobsList) {
}, },
inventory_sources: { inventory_sources: {
name: 'inventory_sources', name: 'inventory_sources',
awToolTip: i18n._('Please save before defining inventory sources'), awToolTip: i18n._('Please save before defining inventory sources.'),
dataPlacement: 'top', dataPlacement: 'top',
title: i18n._('Sources'), title: i18n._('Sources'),
iterator: 'inventory_source', iterator: 'inventory_source',

View File

@@ -50,7 +50,7 @@
data-placement="top" data-placement="top"
ng-click="deleteJob()" ng-click="deleteJob()"
ng-hide="job_status == 'running' || ng-hide="job_status == 'running' ||
job_status == 'pending' " job_status == 'pending' || !job.summary_fields.user_capabilities.delete"
aw-tool-tip="Delete" aw-tool-tip="Delete"
data-original-title="" data-original-title=""
title=""> title="">

View File

@@ -12,7 +12,7 @@ export default ['i18n', function(i18n){
return { return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view // These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
dataPlacement: 'top', dataPlacement: 'top',
awToolTip: i18n._('Please save before adding notifications'), awToolTip: i18n._('Please save before adding notifications.'),
name: 'notifications' , name: 'notifications' ,
title: i18n._('Notifications'), title: i18n._('Notifications'),
iterator: 'notification', iterator: 'notification',

View File

@@ -65,7 +65,7 @@ export default ['NotificationsList', 'i18n',
users: { users: {
name: 'users', name: 'users',
dataPlacement: 'top', dataPlacement: 'top',
awToolTip: i18n._('Please save before adding users'), awToolTip: i18n._('Please save before adding users.'),
basePath: 'api/v2/organizations/{{$stateParams.organization_id}}/access_list/', basePath: 'api/v2/organizations/{{$stateParams.organization_id}}/access_list/',
search: { search: {
order_by: 'username' order_by: 'username'

View File

@@ -221,7 +221,7 @@ export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) {
related: { related: {
permissions: { permissions: {
name: 'permissions', name: 'permissions',
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
djangoModel: 'access_list', djangoModel: 'access_list',
dataPlacement: 'top', dataPlacement: 'top',
basePath: 'api/v2/projects/{{$stateParams.project_id}}/access_list/', basePath: 'api/v2/projects/{{$stateParams.project_id}}/access_list/',

View File

@@ -66,7 +66,7 @@ export default ['i18n', function(i18n) {
users: { users: {
name: 'users', name: 'users',
dataPlacement: 'top', dataPlacement: 'top',
awToolTip: i18n._('Please save before adding users'), awToolTip: i18n._('Please save before adding users.'),
basePath: 'api/v2/teams/{{$stateParams.team_id}}/access_list/', basePath: 'api/v2/teams/{{$stateParams.team_id}}/access_list/',
search: { search: {
order_by: 'username' order_by: 'username'
@@ -110,7 +110,7 @@ export default ['i18n', function(i18n) {
// @todo ask about name field / serializer on this endpoint // @todo ask about name field / serializer on this endpoint
order_by: 'id' order_by: 'id'
}, },
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
dataPlacement: 'top', dataPlacement: 'top',
hideSearchAndActions: true, hideSearchAndActions: true,
type: 'collection', type: 'collection',

View File

@@ -8,7 +8,7 @@
export default ['i18n', function(i18n) { export default ['i18n', function(i18n) {
return { return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view // These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
awToolTip: i18n._('Please save and run a job to view'), awToolTip: i18n._('Please save and run a job to view.'),
dataPlacement: 'top', dataPlacement: 'top',
name: 'completed_jobs', name: 'completed_jobs',
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs', basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs',

View File

@@ -17,7 +17,7 @@
Empty, ToJSON, CallbackHelpInit, GetChoices, Empty, ToJSON, CallbackHelpInit, GetChoices,
$state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService $state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService
) { ) {
// Inject dynamic view // Inject dynamic view
let defaultUrl = GetBasePath('job_templates'), let defaultUrl = GetBasePath('job_templates'),
form = JobTemplateForm(), form = JobTemplateForm(),
@@ -45,6 +45,8 @@
default_val: false default_val: false
}); });
CallbackHelpInit({ scope: $scope }); CallbackHelpInit({ scope: $scope });
$scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.');
} }
callback = function() { callback = function() {

View File

@@ -14,7 +14,7 @@ export default
[ '$filter', '$scope', '$rootScope', [ '$filter', '$scope', '$rootScope',
'$location', '$stateParams', 'JobTemplateForm', 'GenerateForm', '$location', '$stateParams', 'JobTemplateForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup',
'ParseTypeChange', 'Wait', 'selectedLabels', 'ParseTypeChange', 'Wait', 'selectedLabels', 'i18n',
'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit', 'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit',
'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2', 'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels', 'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels',
@@ -22,7 +22,7 @@ export default
$filter, $scope, $rootScope, $filter, $scope, $rootScope,
$location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, $location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
ProcessErrors, GetBasePath, md5Setup, ProcessErrors, GetBasePath, md5Setup,
ParseTypeChange, Wait, selectedLabels, ParseTypeChange, Wait, selectedLabels, i18n,
Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state, Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state,
CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels
) { ) {
@@ -54,6 +54,7 @@ export default
$scope.showJobType = false; $scope.showJobType = false;
$scope.instance_groups = InstanceGroupsData; $scope.instance_groups = InstanceGroupsData;
$scope.credentialNotPresent = false; $scope.credentialNotPresent = false;
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
SurveyControllerInit({ SurveyControllerInit({
scope: $scope, scope: $scope,

View File

@@ -386,7 +386,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
}, },
permissions: { permissions: {
name: 'permissions', name: 'permissions',
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
dataPlacement: 'top', dataPlacement: 'top',
basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/access_list/', basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/access_list/',
search: { search: {
@@ -447,7 +447,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
ngClick: 'addSurvey()', ngClick: 'addSurvey()',
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)', ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && !survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
awFeature: 'surveys', awFeature: 'surveys',
awToolTip: 'Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.', awToolTip: '{{surveyTooltip}}',
dataPlacement: 'top', dataPlacement: 'top',
label: i18n._('Add Survey'), label: i18n._('Add Survey'),
class: 'Form-primaryButton' class: 'Form-primaryButton'
@@ -457,7 +457,9 @@ function(NotificationsList, CompletedJobsList, i18n) {
awFeature: 'surveys', awFeature: 'surveys',
ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)', ngShow: '($state.is(\'templates.addJobTemplate\') || $state.is(\'templates.editJobTemplate\')) && survey_exists && (job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
label: i18n._('Edit Survey'), label: i18n._('Edit Survey'),
class: 'Form-primaryButton' class: 'Form-primaryButton',
awToolTip: '{{surveyTooltip}}',
dataPlacement: 'top'
} }
} }
}; };

View File

@@ -106,7 +106,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
related: { related: {
permissions: { permissions: {
name: 'permissions', name: 'permissions',
awToolTip: i18n._('Please save before assigning permissions'), awToolTip: i18n._('Please save before assigning permissions.'),
dataPlacement: 'top', dataPlacement: 'top',
basePath: 'api/v2/workflow_job_templates/{{$stateParams.workflow_job_template_id}}/access_list/', basePath: 'api/v2/workflow_job_templates/{{$stateParams.workflow_job_template_id}}/access_list/',
search: { search: {
@@ -167,7 +167,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
ngClick: 'addSurvey()', ngClick: 'addSurvey()',
ngShow: '!survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))', ngShow: '!survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
awFeature: 'surveys', awFeature: 'surveys',
awToolTip: 'Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.', awToolTip: '{{surveyTooltip}}',
dataPlacement: 'top', dataPlacement: 'top',
label: i18n._('Add Survey'), label: i18n._('Add Survey'),
class: 'Form-primaryButton' class: 'Form-primaryButton'
@@ -177,12 +177,14 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
awFeature: 'surveys', awFeature: 'surveys',
ngShow: 'survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))', ngShow: 'survey_exists && ($state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\'))',
label: i18n._('Edit Survey'), label: i18n._('Edit Survey'),
class: 'Form-primaryButton' class: 'Form-primaryButton',
awToolTip: '{{surveyTooltip}}',
dataPlacement: 'top'
}, },
workflow_editor: { workflow_editor: {
ngClick: 'openWorkflowMaker()', ngClick: 'openWorkflowMaker()',
ngShow: '$state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\')', ngShow: '$state.includes(\'templates.addWorkflowJobTemplate\') || $state.includes(\'templates.editWorkflowJobTemplate\')',
awToolTip: i18n._('Please save before defining the workflow graph'), awToolTip: '{{workflowEditorTooltip}}',
dataPlacement: 'top', dataPlacement: 'top',
label: i18n._('Workflow Editor'), label: i18n._('Workflow Editor'),
class: 'Form-primaryButton' class: 'Form-primaryButton'

View File

@@ -7,10 +7,10 @@
export default [ export default [
'$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors', '$scope', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
'Wait', '$state', 'CreateSelect2', 'TemplatesService', 'Wait', '$state', 'CreateSelect2', 'TemplatesService',
'ToJSON', 'ParseTypeChange', '$q', 'Rest', 'GetBasePath', 'availableLabels', 'ToJSON', 'ParseTypeChange', '$q', 'Rest', 'GetBasePath', 'availableLabels', 'i18n',
function($scope, WorkflowForm, GenerateForm, Alert, ProcessErrors, function($scope, WorkflowForm, GenerateForm, Alert, ProcessErrors,
Wait, $state, CreateSelect2, TemplatesService, ToJSON, Wait, $state, CreateSelect2, TemplatesService, ToJSON,
ParseTypeChange, $q, Rest, GetBasePath, availableLabels) { ParseTypeChange, $q, Rest, GetBasePath, availableLabels, i18n) {
// Inject dynamic view // Inject dynamic view
let form = WorkflowForm(), let form = WorkflowForm(),
@@ -41,6 +41,9 @@ export default [
multiple: true, multiple: true,
addNew: true addNew: true
}); });
$scope.workflowEditorTooltip = i18n._("Please save before defining the workflow graph.");
$scope.surveyTooltip = i18n._('Please save before adding a survey to this workflow.');
} }
$scope.formSave = function () { $scope.formSave = function () {

View File

@@ -9,12 +9,12 @@ export default [
'ProcessErrors', 'GetBasePath', '$q', 'ParseTypeChange', 'ProcessErrors', 'GetBasePath', '$q', 'ParseTypeChange',
'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'Wait', 'Empty', 'ToJSON', 'initSurvey', '$state', 'CreateSelect2',
'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification', 'ParseVariableString', 'TemplatesService', 'Rest', 'ToggleNotification',
'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'OrgAdminLookup', 'availableLabels', 'selectedLabels', 'workflowJobTemplateData', 'i18n',
function($scope, $stateParams, WorkflowForm, GenerateForm, Alert, function($scope, $stateParams, WorkflowForm, GenerateForm, Alert,
ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty, ProcessErrors, GetBasePath, $q, ParseTypeChange, Wait, Empty,
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString, ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData) { TemplatesService, Rest, ToggleNotification, OrgAdminLookup, availableLabels, selectedLabels, workflowJobTemplateData, i18n) {
$scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) { $scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) {
if (val === false) { if (val === false) {
$scope.canAddWorkflowJobTemplate = false; $scope.canAddWorkflowJobTemplate = false;
@@ -60,6 +60,9 @@ export default [
opts: opts opts: opts
}); });
$scope.workflowEditorTooltip = i18n._("Click here to open the workflow graph editor.");
$scope.surveyTooltip = i18n._('Surveys allow users to be prompted at job launch with a series of questions related to the job. This allows for variables to be defined that affect the playbook run at time of launch.');
$scope.workflow_job_template_obj = workflowJobTemplateData; $scope.workflow_job_template_obj = workflowJobTemplateData;
$scope.name = workflowJobTemplateData.name; $scope.name = workflowJobTemplateData.name;
$scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit; $scope.can_edit = workflowJobTemplateData.summary_fields.user_capabilities.edit;

View File

@@ -118,7 +118,7 @@ export default ['i18n', function(i18n) {
related: { related: {
organizations: { organizations: {
name: 'organizations', name: 'organizations',
awToolTip: i18n._('Please save before assigning to organizations'), awToolTip: i18n._('Please save before assigning to organizations.'),
basePath: 'api/v2/users/{{$stateParams.user_id}}/organizations', basePath: 'api/v2/users/{{$stateParams.user_id}}/organizations',
emptyListText: i18n._('Please add user to an Organization.'), emptyListText: i18n._('Please add user to an Organization.'),
search: { search: {
@@ -146,7 +146,7 @@ export default ['i18n', function(i18n) {
}, },
teams: { teams: {
name: 'teams', name: 'teams',
awToolTip: i18n._('Please save before assigning to teams'), awToolTip: i18n._('Please save before assigning to teams.'),
basePath: 'api/v2/users/{{$stateParams.user_id}}/teams', basePath: 'api/v2/users/{{$stateParams.user_id}}/teams',
search: { search: {
page_size: '10' page_size: '10'
@@ -177,7 +177,7 @@ export default ['i18n', function(i18n) {
page_size: '10', page_size: '10',
order_by: 'id' order_by: 'id'
}, },
awToolTip: i18n._('Please save before assigning to organizations'), awToolTip: i18n._('Please save before assigning to organizations.'),
dataPlacement: 'top', dataPlacement: 'top',
hideSearchAndActions: true, hideSearchAndActions: true,
type: 'collection', type: 'collection',