mirror of
https://github.com/ansible/awx.git
synced 2026-01-23 15:38:06 -03:30
Merge branch 'release_3.1.0' into disableScheduleToggle
This commit is contained in:
commit
9c88c9a7dc
6
Makefile
6
Makefile
@ -45,7 +45,7 @@ ifeq ($(OFFICIAL),yes)
|
||||
AW_REPO_URL ?= http://releases.ansible.com/ansible-tower
|
||||
else
|
||||
RELEASE ?= $(BUILD)
|
||||
AW_REPO_URL ?= http://jenkins.testing.ansible.com/ansible-tower_nightlies_RTYUIOPOIUYTYU/$(GIT_BRANCH)
|
||||
AW_REPO_URL ?= http://jenkins.testing.ansible.com/ansible-tower_nightlies_f8b8c5588b2505970227a7b0900ef69040ad5a00/$(GIT_BRANCH)
|
||||
endif
|
||||
|
||||
# Allow AMI license customization
|
||||
@ -473,7 +473,7 @@ pylint: reports
|
||||
|
||||
check: flake8 pep8 # pyflakes pylint
|
||||
|
||||
TEST_DIRS ?= awx/main/tests
|
||||
TEST_DIRS ?= awx/main/tests awx/conf/tests
|
||||
# Run all API unit tests.
|
||||
test:
|
||||
@if [ "$(VENV_BASE)" ]; then \
|
||||
@ -485,7 +485,7 @@ test_unit:
|
||||
@if [ "$(VENV_BASE)" ]; then \
|
||||
. $(VENV_BASE)/tower/bin/activate; \
|
||||
fi; \
|
||||
py.test awx/main/tests/unit
|
||||
py.test awx/main/tests/unit awx/conf/tests/unit
|
||||
|
||||
# Run all API unit tests with coverage enabled.
|
||||
test_coverage:
|
||||
|
||||
@ -285,6 +285,10 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
|
||||
if name.endswith('_set'):
|
||||
continue
|
||||
fields.append('{}__search'.format(name))
|
||||
for relationship in self.model._meta.local_many_to_many:
|
||||
if relationship.related_model._meta.app_label != 'main':
|
||||
continue
|
||||
fields.append('{}__search'.format(relationship.name))
|
||||
return fields
|
||||
|
||||
|
||||
|
||||
@ -1956,16 +1956,25 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO
|
||||
return res
|
||||
|
||||
def validate(self, attrs):
|
||||
survey_enabled = attrs.get('survey_enabled', self.instance and self.instance.survey_enabled or False)
|
||||
job_type = attrs.get('job_type', self.instance and self.instance.job_type or None)
|
||||
inventory = attrs.get('inventory', self.instance and self.instance.inventory or None)
|
||||
project = attrs.get('project', self.instance and self.instance.project or None)
|
||||
def get_field_from_model_or_attrs(fd):
|
||||
return attrs.get(fd, self.instance and getattr(self.instance, fd) or None)
|
||||
|
||||
survey_enabled = get_field_from_model_or_attrs('survey_enabled')
|
||||
job_type = get_field_from_model_or_attrs('job_type')
|
||||
inventory = get_field_from_model_or_attrs('inventory')
|
||||
credential = get_field_from_model_or_attrs('credential')
|
||||
project = get_field_from_model_or_attrs('project')
|
||||
|
||||
prompting_error_message = _("Must either set a default value or ask to prompt on launch.")
|
||||
if job_type == "scan":
|
||||
if inventory is None or attrs.get('ask_inventory_on_launch', False):
|
||||
raise serializers.ValidationError({'inventory': _('Scan jobs must be assigned a fixed inventory.')})
|
||||
elif project is None:
|
||||
raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")})
|
||||
elif credential is None and not get_field_from_model_or_attrs('ask_credential_on_launch'):
|
||||
raise serializers.ValidationError({'credential': prompting_error_message})
|
||||
elif inventory is None and not get_field_from_model_or_attrs('ask_inventory_on_launch'):
|
||||
raise serializers.ValidationError({'inventory': prompting_error_message})
|
||||
|
||||
if survey_enabled and job_type == PERM_INVENTORY_SCAN:
|
||||
raise serializers.ValidationError({'survey_enabled': _('Survey Enabled cannot be used with scan jobs.')})
|
||||
@ -2964,6 +2973,10 @@ class ActivityStreamSerializer(BaseSerializer):
|
||||
|
||||
changes = serializers.SerializerMethodField()
|
||||
object_association = serializers.SerializerMethodField()
|
||||
# Needed related fields that are not in the default summary fields
|
||||
extra_listing = [
|
||||
('workflow_job_template_node', ('id', 'unified_job_template_id'))
|
||||
]
|
||||
|
||||
class Meta:
|
||||
model = ActivityStream
|
||||
@ -3005,7 +3018,7 @@ class ActivityStreamSerializer(BaseSerializer):
|
||||
rel = {}
|
||||
if obj.actor is not None:
|
||||
rel['actor'] = reverse('api:user_detail', args=(obj.actor.pk,))
|
||||
for fk, __ in SUMMARIZABLE_FK_FIELDS.items():
|
||||
for fk, __ in SUMMARIZABLE_FK_FIELDS.items() + self.extra_listing:
|
||||
if not hasattr(obj, fk):
|
||||
continue
|
||||
allm2m = getattr(obj, fk).all()
|
||||
@ -3027,7 +3040,7 @@ class ActivityStreamSerializer(BaseSerializer):
|
||||
|
||||
def get_summary_fields(self, obj):
|
||||
summary_fields = OrderedDict()
|
||||
for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items():
|
||||
for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items() + self.extra_listing:
|
||||
try:
|
||||
if not hasattr(obj, fk):
|
||||
continue
|
||||
|
||||
@ -3108,7 +3108,7 @@ class WorkflowJobTemplateActivityStreamList(WorkflowsEnforcementMixin, ActivityS
|
||||
self.check_parent_access(parent)
|
||||
qs = self.request.user.get_queryset(self.model)
|
||||
return qs.filter(Q(workflow_job_template=parent) |
|
||||
Q(workflow_job_template_node__workflow_job_template=parent))
|
||||
Q(workflow_job_template_node__workflow_job_template=parent)).distinct()
|
||||
|
||||
|
||||
class WorkflowJobList(WorkflowsEnforcementMixin, ListCreateAPIView):
|
||||
@ -3475,6 +3475,13 @@ class HostJobEventsList(BaseJobEventsList):
|
||||
|
||||
parent_model = Host
|
||||
|
||||
def get_queryset(self):
|
||||
parent_obj = self.get_parent_object()
|
||||
self.check_parent_access(parent_obj)
|
||||
qs = self.request.user.get_queryset(self.model).filter(
|
||||
Q(host=parent_obj) | Q(hosts=parent_obj)).distinct()
|
||||
return qs
|
||||
|
||||
|
||||
class GroupJobEventsList(BaseJobEventsList):
|
||||
|
||||
|
||||
@ -22,4 +22,3 @@ class ConfConfig(AppConfig):
|
||||
if 'http_receiver' not in LOGGING_DICT['loggers']['awx']['handlers']:
|
||||
LOGGING_DICT['loggers']['awx']['handlers'] += ['http_receiver']
|
||||
configure_logging(settings.LOGGING_CONFIG, LOGGING_DICT)
|
||||
# checks.register(SettingsWrapper._check_settings)
|
||||
|
||||
@ -18,9 +18,18 @@ __all__ = ['settings_registry']
|
||||
class SettingsRegistry(object):
|
||||
"""Registry of all API-configurable settings and categories."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, settings=None):
|
||||
"""
|
||||
:param settings: a ``django.conf.LazySettings`` object used to lookup
|
||||
file-based field values (e.g., ``local_settings.py``
|
||||
and ``/etc/tower/conf.d/example.py``). If unspecified,
|
||||
defaults to ``django.conf.settings``.
|
||||
"""
|
||||
if settings is None:
|
||||
from django.conf import settings
|
||||
self._registry = OrderedDict()
|
||||
self._dependent_settings = {}
|
||||
self.settings = settings
|
||||
|
||||
def register(self, setting, **kwargs):
|
||||
if setting in self._registry:
|
||||
@ -94,7 +103,6 @@ class SettingsRegistry(object):
|
||||
return bool(self._registry.get(setting, {}).get('encrypted', False))
|
||||
|
||||
def get_setting_field(self, setting, mixin_class=None, for_user=False, **kwargs):
|
||||
from django.conf import settings
|
||||
from rest_framework.fields import empty
|
||||
field_kwargs = {}
|
||||
field_kwargs.update(self._registry[setting])
|
||||
@ -124,12 +132,12 @@ class SettingsRegistry(object):
|
||||
original_field_instance = original_field_class(**field_kwargs)
|
||||
if category_slug == 'user' and for_user:
|
||||
try:
|
||||
field_instance.default = original_field_instance.to_representation(getattr(settings, setting))
|
||||
field_instance.default = original_field_instance.to_representation(getattr(self.settings, setting))
|
||||
except:
|
||||
logger.warning('Unable to retrieve default value for user setting "%s".', setting, exc_info=True)
|
||||
elif not field_instance.read_only or field_instance.default is empty:
|
||||
elif not field_instance.read_only or field_instance.default is empty or not field_instance.default:
|
||||
try:
|
||||
field_instance.default = original_field_instance.to_representation(settings._awx_conf_settings._get_default(setting))
|
||||
field_instance.default = original_field_instance.to_representation(self.settings._awx_conf_settings._get_default(setting))
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
|
||||
@ -7,8 +7,7 @@ import time
|
||||
|
||||
# Django
|
||||
from django.conf import settings, UserSettingsHolder
|
||||
from django.core.cache import cache
|
||||
from django.core import checks
|
||||
from django.core.cache import cache as django_cache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import ProgrammingError, OperationalError
|
||||
|
||||
@ -65,35 +64,46 @@ def _log_database_error():
|
||||
class SettingsWrapper(UserSettingsHolder):
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
def initialize(cls, cache=None, registry=None):
|
||||
"""
|
||||
Used to initialize and wrap the Django settings context.
|
||||
|
||||
:param cache: the Django cache backend to use for caching setting
|
||||
values. ``django.core.cache`` is used by default.
|
||||
:param registry: the settings registry instance used. The global
|
||||
``awx.conf.settings_registry`` is used by default.
|
||||
"""
|
||||
if not getattr(settings, '_awx_conf_settings', False):
|
||||
settings_wrapper = cls(settings._wrapped)
|
||||
settings_wrapper = cls(
|
||||
settings._wrapped,
|
||||
cache=cache or django_cache,
|
||||
registry=registry or settings_registry
|
||||
)
|
||||
settings._wrapped = settings_wrapper
|
||||
|
||||
@classmethod
|
||||
def _check_settings(cls, app_configs, **kwargs):
|
||||
errors = []
|
||||
# FIXME: Warn if database not available!
|
||||
for setting in Setting.objects.filter(key__in=settings_registry.get_registered_settings(), user__isnull=True):
|
||||
field = settings_registry.get_setting_field(setting.key)
|
||||
try:
|
||||
field.to_internal_value(setting.value)
|
||||
except Exception as e:
|
||||
errors.append(checks.Error(str(e)))
|
||||
return errors
|
||||
def __init__(self, default_settings, cache, registry):
|
||||
"""
|
||||
This constructor is generally not called directly, but by
|
||||
``SettingsWrapper.initialize`` at app startup time when settings are
|
||||
parsed.
|
||||
"""
|
||||
|
||||
def __init__(self, default_settings):
|
||||
# These values have to be stored via self.__dict__ in this way to get
|
||||
# around the magic __setattr__ method on this class (which is used to
|
||||
# store API-assigned settings in the database).
|
||||
self.__dict__['default_settings'] = default_settings
|
||||
self.__dict__['_awx_conf_settings'] = self
|
||||
self.__dict__['_awx_conf_preload_expires'] = None
|
||||
self.__dict__['_awx_conf_preload_lock'] = threading.RLock()
|
||||
self.__dict__['_awx_conf_init_readonly'] = False
|
||||
self.__dict__['cache'] = cache
|
||||
self.__dict__['registry'] = registry
|
||||
|
||||
def _get_supported_settings(self):
|
||||
return settings_registry.get_registered_settings()
|
||||
return self.registry.get_registered_settings()
|
||||
|
||||
def _get_writeable_settings(self):
|
||||
return settings_registry.get_registered_settings(read_only=False)
|
||||
return self.registry.get_registered_settings(read_only=False)
|
||||
|
||||
def _get_cache_value(self, value):
|
||||
if value is None:
|
||||
@ -124,11 +134,11 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
file_default = None
|
||||
if file_default != init_default and file_default is not None:
|
||||
logger.warning('Setting %s has been marked read-only!', key)
|
||||
settings_registry._registry[key]['read_only'] = True
|
||||
self.registry._registry[key]['read_only'] = True
|
||||
self.__dict__['_awx_conf_init_readonly'] = True
|
||||
# If local preload timer has expired, check to see if another process
|
||||
# has already preloaded the cache and skip preloading if so.
|
||||
if cache.get('_awx_conf_preload_expires', empty) is not empty:
|
||||
if self.cache.get('_awx_conf_preload_expires', empty) is not empty:
|
||||
return
|
||||
# Initialize all database-configurable settings with a marker value so
|
||||
# to indicate from the cache that the setting is not configured without
|
||||
@ -138,7 +148,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
for setting in Setting.objects.filter(key__in=settings_to_cache.keys(), user__isnull=True).order_by('pk'):
|
||||
if settings_to_cache[setting.key] != SETTING_CACHE_NOTSET:
|
||||
continue
|
||||
if settings_registry.is_setting_encrypted(setting.key):
|
||||
if self.registry.is_setting_encrypted(setting.key):
|
||||
value = decrypt_field(setting, 'value')
|
||||
else:
|
||||
value = setting.value
|
||||
@ -148,7 +158,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
for key, value in settings_to_cache.items():
|
||||
if value != SETTING_CACHE_NOTSET:
|
||||
continue
|
||||
field = settings_registry.get_setting_field(key)
|
||||
field = self.registry.get_setting_field(key)
|
||||
try:
|
||||
settings_to_cache[key] = self._get_cache_value(field.get_default())
|
||||
except SkipField:
|
||||
@ -157,13 +167,13 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
settings_to_cache = dict([(Setting.get_cache_key(k), v) for k, v in settings_to_cache.items()])
|
||||
settings_to_cache['_awx_conf_preload_expires'] = self._awx_conf_preload_expires
|
||||
logger.debug('cache set_many(%r, %r)', settings_to_cache, SETTING_CACHE_TIMEOUT)
|
||||
cache.set_many(settings_to_cache, SETTING_CACHE_TIMEOUT)
|
||||
self.cache.set_many(settings_to_cache, SETTING_CACHE_TIMEOUT)
|
||||
|
||||
def _get_local(self, name):
|
||||
self._preload_cache()
|
||||
cache_key = Setting.get_cache_key(name)
|
||||
try:
|
||||
cache_value = cache.get(cache_key, empty)
|
||||
cache_value = self.cache.get(cache_key, empty)
|
||||
except ValueError:
|
||||
cache_value = empty
|
||||
logger.debug('cache get(%r, %r) -> %r', cache_key, empty, cache_value)
|
||||
@ -177,7 +187,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
value = {}
|
||||
else:
|
||||
value = cache_value
|
||||
field = settings_registry.get_setting_field(name)
|
||||
field = self.registry.get_setting_field(name)
|
||||
if value is empty:
|
||||
setting = None
|
||||
if not field.read_only:
|
||||
@ -198,8 +208,10 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
if value is None and SETTING_CACHE_NOTSET == SETTING_CACHE_NONE:
|
||||
value = SETTING_CACHE_NOTSET
|
||||
if cache_value != value:
|
||||
logger.debug('cache set(%r, %r, %r)', cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT)
|
||||
cache.set(cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT)
|
||||
logger.debug('cache set(%r, %r, %r)', cache_key,
|
||||
self._get_cache_value(value),
|
||||
SETTING_CACHE_TIMEOUT)
|
||||
self.cache.set(cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT)
|
||||
if value == SETTING_CACHE_NOTSET and not SETTING_CACHE_DEFAULTS:
|
||||
try:
|
||||
value = field.get_default()
|
||||
@ -214,7 +226,9 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
else:
|
||||
return field.run_validation(value)
|
||||
except:
|
||||
logger.warning('The current value "%r" for setting "%s" is invalid.', value, name, exc_info=True)
|
||||
logger.warning(
|
||||
'The current value "%r" for setting "%s" is invalid.',
|
||||
value, name, exc_info=True)
|
||||
return empty
|
||||
|
||||
def _get_default(self, name):
|
||||
@ -234,7 +248,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
return self._get_default(name)
|
||||
|
||||
def _set_local(self, name, value):
|
||||
field = settings_registry.get_setting_field(name)
|
||||
field = self.registry.get_setting_field(name)
|
||||
if field.read_only:
|
||||
logger.warning('Attempt to set read only setting "%s".', name)
|
||||
raise ImproperlyConfigured('Setting "%s" is read only.'.format(name))
|
||||
@ -244,7 +258,8 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
setting_value = field.run_validation(data)
|
||||
db_value = field.to_representation(setting_value)
|
||||
except Exception as e:
|
||||
logger.exception('Unable to assign value "%r" to setting "%s".', value, name, exc_info=True)
|
||||
logger.exception('Unable to assign value "%r" to setting "%s".',
|
||||
value, name, exc_info=True)
|
||||
raise e
|
||||
|
||||
setting = Setting.objects.filter(key=name, user__isnull=True).order_by('pk').first()
|
||||
@ -264,7 +279,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
setattr(self.default_settings, name, value)
|
||||
|
||||
def _del_local(self, name):
|
||||
field = settings_registry.get_setting_field(name)
|
||||
field = self.registry.get_setting_field(name)
|
||||
if field.read_only:
|
||||
logger.warning('Attempt to delete read only setting "%s".', name)
|
||||
raise ImproperlyConfigured('Setting "%s" is read only.'.format(name))
|
||||
@ -282,7 +297,8 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
def __dir__(self):
|
||||
keys = []
|
||||
with _log_database_error():
|
||||
for setting in Setting.objects.filter(key__in=self._get_supported_settings(), user__isnull=True):
|
||||
for setting in Setting.objects.filter(
|
||||
key__in=self._get_supported_settings(), user__isnull=True):
|
||||
# Skip returning settings that have been overridden but are
|
||||
# considered to be "not set".
|
||||
if setting.value is None and SETTING_CACHE_NOTSET == SETTING_CACHE_NONE:
|
||||
|
||||
2
awx/conf/tests/__init__.py
Normal file
2
awx/conf/tests/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
311
awx/conf/tests/unit/test_registry.py
Normal file
311
awx/conf/tests/unit/test_registry.py
Normal file
@ -0,0 +1,311 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import LazySettings
|
||||
from django.core.cache.backends.locmem import LocMemCache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.fields import empty
|
||||
import pytest
|
||||
|
||||
from awx.conf import fields
|
||||
from awx.conf.settings import SettingsWrapper
|
||||
from awx.conf.registry import SettingsRegistry
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reg(request):
|
||||
"""
|
||||
This fixture initializes an awx settings registry object and passes it as
|
||||
an argument into the test function.
|
||||
"""
|
||||
cache = LocMemCache(str(uuid4()), {}) # make a new random cache each time
|
||||
settings = LazySettings()
|
||||
registry = SettingsRegistry(settings)
|
||||
|
||||
# @pytest.mark.readonly can be used to mark specific setting values as
|
||||
# "read-only". This is analogous to manually specifying a setting on the
|
||||
# filesystem (e.g., in a local_settings.py in development, or in
|
||||
# /etc/tower/conf.d/<something>.py)
|
||||
defaults = request.node.get_marker('readonly')
|
||||
if defaults:
|
||||
settings.configure(**defaults.kwargs)
|
||||
settings._wrapped = SettingsWrapper(settings._wrapped,
|
||||
cache,
|
||||
registry)
|
||||
return registry
|
||||
|
||||
|
||||
def test_simple_setting_registration(reg):
|
||||
assert reg.get_registered_settings() == []
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings() == ['AWX_SOME_SETTING_ENABLED']
|
||||
|
||||
|
||||
def test_simple_setting_unregistration(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings() == ['AWX_SOME_SETTING_ENABLED']
|
||||
|
||||
reg.unregister('AWX_SOME_SETTING_ENABLED')
|
||||
assert reg.get_registered_settings() == []
|
||||
|
||||
|
||||
def test_duplicate_setting_registration(reg):
|
||||
"ensure that settings cannot be registered twice."
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
for i in range(2):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
|
||||
def test_field_class_required_for_registration(reg):
|
||||
"settings must specify a field class to register"
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
reg.register('AWX_SOME_SETTING_ENABLED')
|
||||
|
||||
|
||||
def test_get_registered_settings_by_slug(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings(category_slug='system') == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
assert reg.get_registered_settings(category_slug='other') == []
|
||||
|
||||
|
||||
def test_get_registered_read_only_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_READ_ONLY',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
read_only=True
|
||||
)
|
||||
assert reg.get_registered_settings(read_only=True) ==[
|
||||
'AWX_SOME_READ_ONLY'
|
||||
]
|
||||
assert reg.get_registered_settings(read_only=False) == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
assert reg.get_registered_settings() == [
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
'AWX_SOME_READ_ONLY'
|
||||
]
|
||||
|
||||
|
||||
def test_get_registered_settings_with_required_features(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
feature_required='superpowers',
|
||||
)
|
||||
assert reg.get_registered_settings(features_enabled=[]) == []
|
||||
assert reg.get_registered_settings(features_enabled=['superpowers']) == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
|
||||
|
||||
def test_get_dependent_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_DEPENDENT_SETTING',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
depends_on=['AWX_SOME_SETTING_ENABLED']
|
||||
)
|
||||
assert reg.get_dependent_settings('AWX_SOME_SETTING_ENABLED') == set([
|
||||
'AWX_SOME_DEPENDENT_SETTING'
|
||||
])
|
||||
|
||||
|
||||
def test_get_registered_categories(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_OTHER_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('OtherSystem'),
|
||||
category_slug='other-system'
|
||||
)
|
||||
assert reg.get_registered_categories() == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
|
||||
|
||||
def test_get_registered_categories_with_required_features(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
feature_required='superpowers'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_OTHER_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('OtherSystem'),
|
||||
category_slug='other-system',
|
||||
feature_required='sortapowers'
|
||||
)
|
||||
assert reg.get_registered_categories(features_enabled=[]) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
}
|
||||
assert reg.get_registered_categories(features_enabled=['superpowers']) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
}
|
||||
assert reg.get_registered_categories(features_enabled=['sortapowers']) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
assert reg.get_registered_categories(
|
||||
features_enabled=['superpowers', 'sortapowers']
|
||||
) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
|
||||
|
||||
def test_is_setting_encrypted(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_ENCRYPTED_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
encrypted=True
|
||||
)
|
||||
assert reg.is_setting_encrypted('AWX_SOME_SETTING_ENABLED') is False
|
||||
assert reg.is_setting_encrypted('AWX_SOME_ENCRYPTED_SETTING') is True
|
||||
|
||||
|
||||
def test_simple_field(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
placeholder='Example Value',
|
||||
feature_required='superpowers'
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert isinstance(field, fields.CharField)
|
||||
assert field.category == _('System')
|
||||
assert field.category_slug == 'system'
|
||||
assert field.default is empty
|
||||
assert field.placeholder == 'Example Value'
|
||||
assert field.feature_required == 'superpowers'
|
||||
|
||||
|
||||
def test_field_with_custom_attribute(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING_ENABLED',
|
||||
category_slug='other-system')
|
||||
assert field.category_slug == 'other-system'
|
||||
|
||||
|
||||
def test_field_with_custom_mixin(reg):
|
||||
class GreatMixin(object):
|
||||
|
||||
def is_great(self):
|
||||
return True
|
||||
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING_ENABLED',
|
||||
mixin_class=GreatMixin)
|
||||
assert isinstance(field, fields.BooleanField)
|
||||
assert isinstance(field, GreatMixin)
|
||||
assert field.is_great() is True
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_default_value_from_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert field.default == 'DEFAULT'
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_default_value_from_settings_with_custom_representation(reg):
|
||||
class LowercaseCharField(fields.CharField):
|
||||
|
||||
def to_representation(self, value):
|
||||
return value.lower()
|
||||
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=LowercaseCharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert field.default == 'default'
|
||||
264
awx/conf/tests/unit/test_settings.py
Normal file
264
awx/conf/tests/unit/test_settings.py
Normal file
@ -0,0 +1,264 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from contextlib import contextmanager
|
||||
from uuid import uuid4
|
||||
import time
|
||||
|
||||
from django.conf import LazySettings
|
||||
from django.core.cache.backends.locmem import LocMemCache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import fields
|
||||
import pytest
|
||||
|
||||
from awx.conf import models
|
||||
from awx.conf.settings import SettingsWrapper, SETTING_CACHE_NOTSET
|
||||
from awx.conf.registry import SettingsRegistry
|
||||
|
||||
|
||||
@contextmanager
|
||||
def apply_patches(_patches):
|
||||
[p.start() for p in _patches]
|
||||
yield
|
||||
[p.stop() for p in _patches]
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def settings(request):
|
||||
"""
|
||||
This fixture initializes a Django settings object that wraps our
|
||||
`awx.conf.settings.SettingsWrapper` and passes it as an argument into the
|
||||
test function.
|
||||
|
||||
This mimics the work done by `awx.conf.settings.SettingsWrapper.initialize`
|
||||
on `django.conf.settings`.
|
||||
"""
|
||||
cache = LocMemCache(str(uuid4()), {}) # make a new random cache each time
|
||||
settings = LazySettings()
|
||||
registry = SettingsRegistry(settings)
|
||||
|
||||
# @pytest.mark.readonly can be used to mark specific setting values as
|
||||
# "read-only". This is analogous to manually specifying a setting on the
|
||||
# filesystem (e.g., in a local_settings.py in development, or in
|
||||
# /etc/tower/conf.d/<something>.py)
|
||||
readonly_marker = request.node.get_marker('readonly')
|
||||
defaults = readonly_marker.kwargs if readonly_marker else {}
|
||||
defaults['DEFAULTS_SNAPSHOT'] = {}
|
||||
settings.configure(**defaults)
|
||||
settings._wrapped = SettingsWrapper(settings._wrapped,
|
||||
cache,
|
||||
registry)
|
||||
return settings
|
||||
|
||||
|
||||
@pytest.mark.readonly(DEBUG=True)
|
||||
def test_unregistered_setting(settings):
|
||||
"native Django settings are not stored in DB, and aren't cached"
|
||||
assert settings.DEBUG is True
|
||||
assert settings.cache.get('DEBUG') is None
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting(settings):
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert len(settings.registry.get_registered_settings(read_only=False)) == 0
|
||||
settings = settings.registry.get_registered_settings(read_only=True)
|
||||
assert settings == ['AWX_SOME_SETTING']
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_with_empty_default(settings):
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='',
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert len(settings.registry.get_registered_settings(read_only=False)) == 0
|
||||
settings = settings.registry.get_registered_settings(read_only=True)
|
||||
assert settings == ['AWX_SOME_SETTING']
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_defaults_are_cached(settings):
|
||||
"read-only settings are stored in the cache"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_cache_respects_timeout(settings):
|
||||
"only preload the cache every SETTING_CACHE_TIMEOUT settings"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
cache_expiration = settings.cache.get('_awx_conf_preload_expires')
|
||||
assert cache_expiration > time.time()
|
||||
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('_awx_conf_preload_expires') == cache_expiration
|
||||
|
||||
|
||||
def test_default_setting(settings, mocker):
|
||||
"settings that specify a default are inserted into the cache"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='DEFAULT'
|
||||
)
|
||||
|
||||
settings_to_cache = mocker.Mock(**{'order_by.return_value': []})
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=settings_to_cache):
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
|
||||
|
||||
|
||||
def test_empty_setting(settings, mocker):
|
||||
"settings with no default and no defined value are not valid"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
settings_to_cache = [
|
||||
mocker.Mock(**{'order_by.return_value': []}),
|
||||
mocker.Mock(**{'order_by.return_value.first.return_value': None})
|
||||
]
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', side_effect=settings_to_cache):
|
||||
with pytest.raises(AttributeError):
|
||||
settings.AWX_SOME_SETTING
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == SETTING_CACHE_NOTSET
|
||||
|
||||
|
||||
def test_setting_from_db(settings, mocker):
|
||||
"settings can be loaded from the database"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='DEFAULT'
|
||||
)
|
||||
|
||||
settings_to_cache = [
|
||||
mocker.Mock(**{'order_by.return_value': [
|
||||
mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
]}),
|
||||
]
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', side_effect=settings_to_cache):
|
||||
assert settings.AWX_SOME_SETTING == 'FROM_DB'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'FROM_DB'
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_assignment(settings):
|
||||
"read-only settings cannot be overwritten"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
settings.AWX_SOME_SETTING = 'CHANGED'
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
|
||||
|
||||
def test_db_setting_create(settings, mocker):
|
||||
"settings are stored in the database when set for the first time"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
setting_list = mocker.Mock(**{'order_by.return_value.first.return_value': None})
|
||||
with apply_patches([
|
||||
mocker.patch('awx.conf.models.Setting.objects.filter',
|
||||
return_value=setting_list),
|
||||
mocker.patch('awx.conf.models.Setting.objects.create', mocker.Mock())
|
||||
]):
|
||||
settings.AWX_SOME_SETTING = 'NEW-VALUE'
|
||||
|
||||
models.Setting.objects.create.assert_called_with(
|
||||
key='AWX_SOME_SETTING',
|
||||
user=None,
|
||||
value='NEW-VALUE'
|
||||
)
|
||||
|
||||
|
||||
def test_db_setting_update(settings, mocker):
|
||||
"settings are updated in the database when their value changes"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
existing_setting = mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
setting_list = mocker.Mock(**{
|
||||
'order_by.return_value.first.return_value': existing_setting
|
||||
})
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=setting_list):
|
||||
settings.AWX_SOME_SETTING = 'NEW-VALUE'
|
||||
|
||||
assert existing_setting.value == 'NEW-VALUE'
|
||||
existing_setting.save.assert_called_with(update_fields=['value'])
|
||||
|
||||
|
||||
def test_db_setting_deletion(settings, mocker):
|
||||
"settings are auto-deleted from the database"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
existing_setting = mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=[existing_setting]):
|
||||
del settings.AWX_SOME_SETTING
|
||||
|
||||
assert existing_setting.delete.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_deletion(settings):
|
||||
"read-only settings cannot be deleted"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
del settings.AWX_SOME_SETTING
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
@ -71,7 +71,10 @@ def terminate_ssh_control_masters():
|
||||
# Terminate then kill control master processes. Workaround older
|
||||
# version of psutil that may not have wait_procs implemented.
|
||||
for proc in ssh_cm_procs:
|
||||
proc.terminate()
|
||||
try:
|
||||
proc.terminate()
|
||||
except psutil.NoSuchProcess:
|
||||
continue
|
||||
procs_gone, procs_alive = psutil.wait_procs(ssh_cm_procs, timeout=5)
|
||||
for proc in procs_alive:
|
||||
proc.kill()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2109,7 +2109,7 @@ class ActivityStreamAccess(BaseAccess):
|
||||
'job_template', 'job', 'ad_hoc_command',
|
||||
'notification_template', 'notification', 'label', 'role', 'actor',
|
||||
'schedule', 'custom_inventory_script', 'unified_job_template',
|
||||
'workflow_job_template', 'workflow_job')
|
||||
'workflow_job_template', 'workflow_job', 'workflow_job_template_node')
|
||||
if self.user.is_superuser or self.user.is_system_auditor:
|
||||
return qs.all()
|
||||
|
||||
|
||||
@ -126,4 +126,5 @@ activity_stream_registrar.connect(Notification)
|
||||
activity_stream_registrar.connect(Label)
|
||||
activity_stream_registrar.connect(User)
|
||||
activity_stream_registrar.connect(WorkflowJobTemplate)
|
||||
activity_stream_registrar.connect(WorkflowJobTemplateNode)
|
||||
activity_stream_registrar.connect(WorkflowJob)
|
||||
|
||||
@ -127,10 +127,10 @@ class SurveyJobTemplateMixin(models.Model):
|
||||
# Overwrite with job template extra vars with survey default vars
|
||||
if self.survey_enabled and 'spec' in self.survey_spec:
|
||||
for survey_element in self.survey_spec.get("spec", []):
|
||||
default = survey_element['default']
|
||||
variable_key = survey_element['variable']
|
||||
default = survey_element.get('default')
|
||||
variable_key = survey_element.get('variable')
|
||||
if survey_element.get('type') == 'password':
|
||||
if variable_key in kwargs_extra_vars:
|
||||
if variable_key in kwargs_extra_vars and default:
|
||||
kw_value = kwargs_extra_vars[variable_key]
|
||||
if kw_value.startswith('$encrypted$') and kw_value != default:
|
||||
kwargs_extra_vars[variable_key] = default
|
||||
|
||||
@ -10,9 +10,9 @@ import re
|
||||
# Django
|
||||
from django.db import models, transaction, connection
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
# AWX
|
||||
@ -33,29 +33,29 @@ ROLE_SINGLETON_SYSTEM_ADMINISTRATOR='system_administrator'
|
||||
ROLE_SINGLETON_SYSTEM_AUDITOR='system_auditor'
|
||||
|
||||
role_names = {
|
||||
'system_administrator' : 'System Administrator',
|
||||
'system_auditor' : 'System Auditor',
|
||||
'adhoc_role' : 'Ad Hoc',
|
||||
'admin_role' : 'Admin',
|
||||
'auditor_role' : 'Auditor',
|
||||
'execute_role' : 'Execute',
|
||||
'member_role' : 'Member',
|
||||
'read_role' : 'Read',
|
||||
'update_role' : 'Update',
|
||||
'use_role' : 'Use',
|
||||
'system_administrator' : _('System Administrator'),
|
||||
'system_auditor' : _('System Auditor'),
|
||||
'adhoc_role' : _('Ad Hoc'),
|
||||
'admin_role' : _('Admin'),
|
||||
'auditor_role' : _('Auditor'),
|
||||
'execute_role' : _('Execute'),
|
||||
'member_role' : _('Member'),
|
||||
'read_role' : _('Read'),
|
||||
'update_role' : _('Update'),
|
||||
'use_role' : _('Use'),
|
||||
}
|
||||
|
||||
role_descriptions = {
|
||||
'system_administrator' : 'Can manage all aspects of the system',
|
||||
'system_auditor' : 'Can view all settings on the system',
|
||||
'adhoc_role' : 'May run ad hoc commands on an inventory',
|
||||
'admin_role' : 'Can manage all aspects of the %s',
|
||||
'auditor_role' : 'Can view all settings for the %s',
|
||||
'execute_role' : 'May run the %s',
|
||||
'member_role' : 'User is a member of the %s',
|
||||
'read_role' : 'May view settings for the %s',
|
||||
'update_role' : 'May update project or inventory or group using the configured source update system',
|
||||
'use_role' : 'Can use the %s in a job template',
|
||||
'system_administrator' : _('Can manage all aspects of the system'),
|
||||
'system_auditor' : _('Can view all settings on the system'),
|
||||
'adhoc_role' : _('May run ad hoc commands on an inventory'),
|
||||
'admin_role' : _('Can manage all aspects of the %s'),
|
||||
'auditor_role' : _('Can view all settings for the %s'),
|
||||
'execute_role' : _('May run the %s'),
|
||||
'member_role' : _('User is a member of the %s'),
|
||||
'read_role' : _('May view settings for the %s'),
|
||||
'update_role' : _('May update project or inventory or group using the configured source update system'),
|
||||
'use_role' : _('Can use the %s in a job template'),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -721,7 +721,7 @@ class BaseTask(Task):
|
||||
stdout_handle = self.get_stdout_handle(instance)
|
||||
if self.should_use_proot(instance, **kwargs):
|
||||
if not check_proot_installed():
|
||||
raise RuntimeError('proot is not installed')
|
||||
raise RuntimeError('bubblewrap is not installed')
|
||||
kwargs['proot_temp_dir'] = build_proot_temp_dir()
|
||||
args = wrap_args_with_proot(args, cwd, **kwargs)
|
||||
safe_args = wrap_args_with_proot(safe_args, cwd, **kwargs)
|
||||
@ -874,7 +874,7 @@ class RunJob(BaseTask):
|
||||
cp_dir = os.path.join(kwargs['private_data_dir'], 'cp')
|
||||
if not os.path.exists(cp_dir):
|
||||
os.mkdir(cp_dir, 0700)
|
||||
env['ANSIBLE_SSH_CONTROL_PATH'] = os.path.join(cp_dir, 'ansible-ssh-%%h-%%p-%%r')
|
||||
env['ANSIBLE_SSH_CONTROL_PATH'] = os.path.join(cp_dir, '%%h%%p%%r')
|
||||
|
||||
# Allow the inventory script to include host variables inline via ['_meta']['hostvars'].
|
||||
env['INVENTORY_HOSTVARS'] = str(True)
|
||||
@ -1609,7 +1609,6 @@ class RunInventoryUpdate(BaseTask):
|
||||
if inventory_update.overwrite_vars:
|
||||
args.append('--overwrite-vars')
|
||||
args.append('--source')
|
||||
|
||||
# If this is a cloud-based inventory (e.g. from AWS, Rackspace, etc.)
|
||||
# then we need to set some extra flags based on settings in
|
||||
# Tower.
|
||||
@ -1665,10 +1664,7 @@ class RunInventoryUpdate(BaseTask):
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
args.append(runpath)
|
||||
args.append("--custom")
|
||||
# try:
|
||||
# shutil.rmtree(runpath, True)
|
||||
# except OSError:
|
||||
# pass
|
||||
self.custom_dir_path.append(runpath)
|
||||
verbosity = getattr(settings, 'INVENTORY_UPDATE_VERBOSITY', 1)
|
||||
args.append('-v%d' % verbosity)
|
||||
if settings.DEBUG:
|
||||
@ -1690,6 +1686,19 @@ class RunInventoryUpdate(BaseTask):
|
||||
def get_idle_timeout(self):
|
||||
return getattr(settings, 'INVENTORY_UPDATE_IDLE_TIMEOUT', None)
|
||||
|
||||
def pre_run_hook(self, instance, **kwargs):
|
||||
self.custom_dir_path = []
|
||||
|
||||
def post_run_hook(self, instance, status, **kwargs):
|
||||
print("In post run hook")
|
||||
if self.custom_dir_path:
|
||||
for p in self.custom_dir_path:
|
||||
try:
|
||||
shutil.rmtree(p, True)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class RunAdHocCommand(BaseTask):
|
||||
'''
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from awx.api.views import (
|
||||
ApiV1RootView,
|
||||
JobTemplateLabelList,
|
||||
JobTemplateSurveySpec,
|
||||
)
|
||||
|
||||
|
||||
@ -65,3 +68,16 @@ class TestJobTemplateLabelList:
|
||||
|
||||
super(JobTemplateLabelList, view).unattach(mock_request, None, None)
|
||||
assert mixin_unattach.called_with(mock_request, None, None)
|
||||
|
||||
|
||||
class TestJobTemplateSurveySpec(object):
|
||||
@mock.patch('awx.api.views.feature_enabled', lambda feature: True)
|
||||
def test_get_password_type(self, mocker, mock_response_new):
|
||||
JobTemplate = namedtuple('JobTemplate', 'survey_spec')
|
||||
obj = JobTemplate(survey_spec={'spec':[{'type': 'password', 'default': 'my_default'}]})
|
||||
with mocker.patch.object(JobTemplateSurveySpec, 'get_object', return_value=obj):
|
||||
view = JobTemplateSurveySpec()
|
||||
response = view.get(mocker.MagicMock())
|
||||
assert response == mock_response_new
|
||||
# which there was a better way to do this!
|
||||
assert response.call_args[0][1]['spec'][0]['default'] == '$encrypted$'
|
||||
|
||||
@ -84,3 +84,18 @@ def test_job_template_survey_variable_validation(job_template_factory):
|
||||
}
|
||||
obj.survey_enabled = True
|
||||
assert obj.survey_variable_validation({"a": 5}) == ["Value 5 for 'a' expected to be a string."]
|
||||
|
||||
|
||||
def test_job_template_survey_mixin(job_template_factory):
|
||||
objects = job_template_factory(
|
||||
'survey_mixin_test',
|
||||
organization='org1',
|
||||
inventory='inventory1',
|
||||
credential='cred1',
|
||||
persisted=False,
|
||||
)
|
||||
obj = objects.job_template
|
||||
obj.survey_enabled = True
|
||||
obj.survey_spec = {'spec': [{'default':'my_default', 'type':'password', 'variable':'my_variable'}]}
|
||||
kwargs = obj._update_unified_job_kwargs(extra_vars={'my_variable':'$encrypted$'})
|
||||
assert kwargs['extra_vars'] == '{"my_variable": "my_default"}'
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import controller from './about.controller';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'setup.about',
|
||||
route: '/about',
|
||||
controller: controller,
|
||||
ncyBreadcrumb: {
|
||||
label: "ABOUT"
|
||||
label: N_("ABOUT")
|
||||
},
|
||||
onExit: function(){
|
||||
// hacky way to handle user browsing away via URL bar
|
||||
|
||||
@ -23,10 +23,10 @@
|
||||
<span class="AddPermissions-directionNumber">
|
||||
1
|
||||
</span>
|
||||
<div ng-hide='withoutTeamPermissions'>
|
||||
<div ng-hide='withoutTeamPermissions' translate>
|
||||
Please select Users / Teams from the lists below.
|
||||
</div>
|
||||
<div ng-show='withoutTeamPermissions'>
|
||||
<div ng-show='withoutTeamPermissions' translate>
|
||||
Please select Users from the list below.
|
||||
</div>
|
||||
</div>
|
||||
@ -34,12 +34,12 @@
|
||||
<div class="Form-tabHolder" ng-hide='withoutTeamPermissions'>
|
||||
<div id="users_tab" class="Form-tab"
|
||||
ng-click="toggleFormTabs('users')"
|
||||
ng-class="{'is-selected': usersSelected }">
|
||||
ng-class="{'is-selected': usersSelected }" translate>
|
||||
Users
|
||||
</div>
|
||||
<div id="teams_tab" class="Form-tab"
|
||||
ng-click="toggleFormTabs('teams')"
|
||||
ng-class="{'is-selected': teamsSelected }">
|
||||
ng-class="{'is-selected': teamsSelected }" translate>
|
||||
Teams
|
||||
</div>
|
||||
</div>
|
||||
@ -59,7 +59,7 @@
|
||||
<span class="AddPermissions-directionNumber">
|
||||
2
|
||||
</span>
|
||||
Please assign roles to the selected users/teams
|
||||
<translate>Please assign roles to the selected users/teams</translate>
|
||||
<div class="AddPermissions-keyToggle btn"
|
||||
ng-class="{'is-active': showKeyPane}"
|
||||
ng-click="toggleKeyPane()">
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
<span class="AddPermissions-directionNumber">
|
||||
1
|
||||
</span>
|
||||
<div>
|
||||
<div translate>
|
||||
Please select resources from the lists below.
|
||||
</div>
|
||||
</div>
|
||||
@ -34,30 +34,30 @@
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('job_templates')"
|
||||
ng-class="{'is-selected': tab.job_templates }">
|
||||
ng-class="{'is-selected': tab.job_templates }" translate>
|
||||
Job Templates
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('workflow_templates')"
|
||||
ng-class="{'is-selected': tab.workflow_templates}"
|
||||
>
|
||||
translate>
|
||||
Workflow Templates
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('projects')"
|
||||
ng-class="{'is-selected': tab.projects }">
|
||||
ng-class="{'is-selected': tab.projects }" translate>
|
||||
Projects
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('inventories')"
|
||||
ng-class="{'is-selected': tab.inventories}"
|
||||
>
|
||||
translate>
|
||||
Inventories
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('credentials')"
|
||||
ng-class="{'is-selected': tab.credentials}"
|
||||
>
|
||||
translate>
|
||||
Credentials
|
||||
</div>
|
||||
</div>
|
||||
@ -86,10 +86,10 @@
|
||||
<span class="AddPermissions-directionNumber">
|
||||
2
|
||||
</span>
|
||||
Please assign roles to the selected resources
|
||||
<translate>Please assign roles to the selected resources</translate>
|
||||
<div class="AddPermissions-keyToggle btn"
|
||||
ng-class="{'is-active': showKeyPane}"
|
||||
ng-click="toggleKeyPane()">
|
||||
ng-click="toggleKeyPane()" translate>
|
||||
Key
|
||||
</div>
|
||||
</div>
|
||||
@ -97,34 +97,34 @@
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('job_templates')"
|
||||
ng-class="{'is-selected': tab.job_templates }"
|
||||
ng-show="showSection2Tab('job_templates')">
|
||||
ng-show="showSection2Tab('job_templates')" translate>
|
||||
Job Templates
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('workflow_templates')"
|
||||
ng-class="{'is-selected': tab.workflow_templates }"
|
||||
ng-show="showSection2Tab('workflow_templates')">
|
||||
ng-show="showSection2Tab('workflow_templates')" translate>
|
||||
Workflow Templates
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('projects')"
|
||||
ng-class="{'is-selected': tab.projects}"
|
||||
ng-show="showSection2Tab('projects')"
|
||||
>
|
||||
translate>
|
||||
Projects
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('inventories')"
|
||||
ng-class="{'is-selected': tab.inventories}"
|
||||
ng-show="showSection2Tab('inventories')"
|
||||
>
|
||||
translate>
|
||||
Inventories
|
||||
</div>
|
||||
<div class="Form-tab"
|
||||
ng-click="selectTab('credentials')"
|
||||
ng-class="{'is-selected': tab.credentials}"
|
||||
ng-show="showSection2Tab('credentials')"
|
||||
>
|
||||
translate>
|
||||
Credentials
|
||||
</div>
|
||||
</div>
|
||||
@ -170,13 +170,13 @@
|
||||
<div class="buttons Form-buttons AddPermissions-buttons">
|
||||
<button type="button"
|
||||
class="btn btn-sm Form-cancelButton"
|
||||
ng-click="closeModal()">
|
||||
ng-click="closeModal()" translate>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm Form-saveButton"
|
||||
ng-click="saveForm()"
|
||||
ng-disabled="!saveEnabled()">
|
||||
ng-disabled="!saveEnabled()" translate>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default function() {
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
searchSize: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
|
||||
name: 'teams',
|
||||
@ -15,19 +15,20 @@
|
||||
multiSelectExtended: true,
|
||||
index: false,
|
||||
hover: true,
|
||||
emptyListText : 'No Teams exist',
|
||||
emptyListText : i18n._('No Teams exist'),
|
||||
fields: {
|
||||
name: {
|
||||
key: true,
|
||||
label: 'name'
|
||||
label: i18n._('name')
|
||||
},
|
||||
organization: {
|
||||
label: 'organization',
|
||||
label: i18n._('organization'),
|
||||
ngBind: 'team.summary_fields.organization.name',
|
||||
sourceModel: 'organization',
|
||||
sourceField: 'name'
|
||||
sourceField: 'name',
|
||||
searchable: true
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}];
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default function() {
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
name: 'users',
|
||||
iterator: 'user',
|
||||
@ -21,22 +21,22 @@
|
||||
multiSelectExtended: true,
|
||||
index: false,
|
||||
hover: true,
|
||||
emptyListText : 'No Users exist',
|
||||
emptyListText : i18n._('No Users exist'),
|
||||
fields: {
|
||||
first_name: {
|
||||
label: 'First Name',
|
||||
label: i18n._('First Name'),
|
||||
columnClass: 'col-md-3 col-sm-3 hidden-xs'
|
||||
},
|
||||
last_name: {
|
||||
label: 'Last Name',
|
||||
label: i18n._('Last Name'),
|
||||
columnClass: 'col-md-3 col-sm-3 hidden-xs'
|
||||
},
|
||||
username: {
|
||||
key: true,
|
||||
label: 'Username',
|
||||
label: i18n._('Username'),
|
||||
columnClass: 'col-md-5 col-sm-5 col-xs-11'
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
}];
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
export default
|
||||
[
|
||||
'CreateSelect2',
|
||||
function(CreateSelect2) {
|
||||
'i18n',
|
||||
function(CreateSelect2, i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
@ -21,7 +22,7 @@ export default
|
||||
CreateSelect2({
|
||||
element: '.roleSelect2',
|
||||
multiple: true,
|
||||
placeholder: 'Select roles'
|
||||
placeholder: i18n._('Select roles')
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'activityStream',
|
||||
route: '/activity_stream?target&id',
|
||||
@ -22,7 +24,7 @@ export default {
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "ACTIVITY STREAM"
|
||||
label: N_("ACTIVITY STREAM")
|
||||
},
|
||||
onExit: function() {
|
||||
$('#stream-detail-modal').modal('hide');
|
||||
|
||||
@ -9,15 +9,15 @@
|
||||
</div>
|
||||
<div class="Modal-body" id="detail-body">
|
||||
<div ng-show="user" class="StreamDetail-inlineRow">
|
||||
<div class="StreamDetail-rowTitle StreamDetail-inlineRowTitle">INITIATED BY</div>
|
||||
<div class="StreamDetail-rowTitle StreamDetail-inlineRowTitle" translate>INITIATED BY</div>
|
||||
<div class="StreamDetail-inlineRowData" ng-bind="user"></div>
|
||||
</div>
|
||||
<div ng-show="operation" class="StreamDetail-inlineRow">
|
||||
<div class="StreamDetail-rowTitle StreamDetail-inlineRowTitle">ACTION</div>
|
||||
<div class="StreamDetail-rowTitle StreamDetail-inlineRowTitle" translate>ACTION</div>
|
||||
<div class="StreamDetail-inlineRowData StreamDetail-actions" ng-bind-html="operation"></div>
|
||||
</div>
|
||||
<div ng-show="changes">
|
||||
<div class="StreamDetail-rowTitle StreamDetail-changesRowTitle">CHANGES</div>
|
||||
<div class="StreamDetail-rowTitle StreamDetail-changesRowTitle" translate>CHANGES</div>
|
||||
<pre class="StreamDetail-changes">{{ changes | json : spacing}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['templateUrl', function(templateUrl) {
|
||||
export default ['templateUrl', 'i18n', function(templateUrl, i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: true,
|
||||
@ -15,18 +15,18 @@ export default ['templateUrl', function(templateUrl) {
|
||||
$scope.streamTarget = ($state.params && $state.params.target) ? $state.params.target : 'dashboard';
|
||||
|
||||
$scope.options = [
|
||||
{label: 'All Activity', value: 'dashboard'},
|
||||
{label: 'Credentials', value: 'credential'},
|
||||
{label: 'Hosts', value: 'host'},
|
||||
{label: 'Inventories', value: 'inventory'},
|
||||
{label: 'Inventory Scripts', value: 'inventory_script'},
|
||||
{label: 'Jobs', value: 'job'},
|
||||
{label: 'Organizations', value: 'organization'},
|
||||
{label: 'Projects', value: 'project'},
|
||||
{label: 'Schedules', value: 'schedule'},
|
||||
{label: 'Teams', value: 'team'},
|
||||
{label: 'Templates', value: 'template'},
|
||||
{label: 'Users', value: 'user'}
|
||||
{label: i18n._('All Activity'), value: 'dashboard'},
|
||||
{label: i18n._('Credentials'), value: 'credential'},
|
||||
{label: i18n._('Hosts'), value: 'host'},
|
||||
{label: i18n._('Inventories'), value: 'inventory'},
|
||||
{label: i18n._('Inventory Scripts'), value: 'inventory_script'},
|
||||
{label: i18n._('Jobs'), value: 'job'},
|
||||
{label: i18n._('Organizations'), value: 'organization'},
|
||||
{label: i18n._('Projects'), value: 'project'},
|
||||
{label: i18n._('Schedules'), value: 'schedule'},
|
||||
{label: i18n._('Teams'), value: 'team'},
|
||||
{label: i18n._('Templates'), value: 'template'},
|
||||
{label: i18n._('Users'), value: 'user'}
|
||||
];
|
||||
|
||||
CreateSelect2({
|
||||
|
||||
@ -99,6 +99,8 @@ var tower = angular.module('Tower', [
|
||||
require('angular-tz-extensions'),
|
||||
require('lr-infinite-scroll'),
|
||||
require('ng-toast'),
|
||||
'gettext',
|
||||
'I18N',
|
||||
uiRouter,
|
||||
'ui.router.state.events',
|
||||
|
||||
@ -201,8 +203,6 @@ var tower = angular.module('Tower', [
|
||||
scheduler.name,
|
||||
'ApiModelHelper',
|
||||
'ActivityStreamHelper',
|
||||
'gettext',
|
||||
'I18N',
|
||||
'WorkflowFormDefinition',
|
||||
'InventorySourcesListDefinition',
|
||||
'WorkflowMakerFormDefinition'
|
||||
@ -290,6 +290,9 @@ var tower = angular.module('Tower', [
|
||||
"jobs": ["status_changed"]
|
||||
}
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: N_('PROJECTS')
|
||||
}
|
||||
})
|
||||
});
|
||||
@ -371,12 +374,12 @@ var tower = angular.module('Tower', [
|
||||
'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
|
||||
'ClearScope', 'LoadConfig', 'Store', 'pendoService', 'Prompt', 'Rest',
|
||||
'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'ConfigService',
|
||||
'FeaturesService', '$filter', 'SocketService', 'I18NInit',
|
||||
'FeaturesService', '$filter', 'SocketService',
|
||||
function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log, $stateParams,
|
||||
CheckLicense, $location, Authorization, LoadBasePaths, Timer,
|
||||
ClearScope, LoadConfig, Store, pendoService, Prompt, Rest, Wait,
|
||||
ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService,
|
||||
$filter, SocketService, I18NInit) {
|
||||
$filter, SocketService) {
|
||||
|
||||
$rootScope.$state = $state;
|
||||
$rootScope.$state.matches = function(stateName) {
|
||||
@ -388,7 +391,6 @@ var tower = angular.module('Tower', [
|
||||
$log.debug(`$state.defaultErrorHandler: ${error}`);
|
||||
});
|
||||
|
||||
I18NInit();
|
||||
$stateExtender.addState({
|
||||
name: 'dashboard',
|
||||
url: '/home',
|
||||
|
||||
@ -73,6 +73,10 @@
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.BreadCrumb-invItem {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.BreadCrumb-item + .BreadCrumb-item:before {
|
||||
content: "/";
|
||||
padding: 0 5px;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export default
|
||||
['templateUrl', '$state', 'FeaturesService', 'ProcessErrors','$rootScope', 'Store', 'Empty', '$window', 'BreadCrumbService',
|
||||
function(templateUrl, $state, FeaturesService, ProcessErrors, $rootScope, Store, Empty, $window, BreadCrumbService) {
|
||||
['templateUrl', '$state', 'FeaturesService', 'ProcessErrors','$rootScope', 'Store', 'Empty', '$window', 'BreadCrumbService', 'i18n',
|
||||
function(templateUrl, $state, FeaturesService, ProcessErrors, $rootScope, Store, Empty, $window, BreadCrumbService, i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('bread-crumb/bread-crumb'),
|
||||
@ -103,7 +103,7 @@ export default
|
||||
if(features){
|
||||
scope.loadingLicense = false;
|
||||
scope.activityStreamActive = (toState.name === 'activityStream') ? true : false;
|
||||
scope.activityStreamTooltip = (toState.name === 'activityStream') ? 'Hide Activity Stream' : 'View Activity Stream';
|
||||
scope.activityStreamTooltip = (toState.name === 'activityStream') ? i18n._('Hide Activity Stream') : i18n._('View Activity Stream');
|
||||
scope.showActivityStreamButton = (FeaturesService.featureEnabled('activity_streams') || toState.name ==='activityStream') ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div class="tab-pane Configuration-container" id="configuration_edit">
|
||||
<div class="Form-nav--dropdownContainer">
|
||||
<div class="Form-nav--dropdownLabel">Sub Category</div>
|
||||
<div class="Form-nav--dropdownLabel" translate>Sub Category</div>
|
||||
<div class="Form-nav--dropdown">
|
||||
<select
|
||||
id="configure-dropdown-nav"
|
||||
|
||||
@ -7,15 +7,15 @@
|
||||
<div class="tab-pane" id="configuration-panel">
|
||||
<div ng-cloak id="htmlTemplate" class="Panel">
|
||||
<div class="Form-header">
|
||||
<div class="Form-title">Configure Tower</div>
|
||||
<div class="Form-title" translate>Configure Tower</div>
|
||||
</div>
|
||||
<div class="row Form-tabRow">
|
||||
<div class="col-lg-12">
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('auth')" ng-class="{'is-selected': vm.activeTab === 'auth' }">Authentication</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('jobs')" ng-class="{'is-selected': vm.activeTab === 'jobs' }">Jobs</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('system')" ng-class="{'is-selected': vm.activeTab === 'system' }">System</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('ui')" ng-class="{'is-selected': vm.activeTab === 'ui' }">User Interface</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('auth')" ng-class="{'is-selected': vm.activeTab === 'auth' }" translate>Authentication</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('jobs')" ng-class="{'is-selected': vm.activeTab === 'jobs' }" translate>Jobs</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('system')" ng-class="{'is-selected': vm.activeTab === 'system' }" translate>System</div>
|
||||
<div class="Form-tab" ng-click="vm.activeTabCheck('ui')" ng-class="{'is-selected': vm.activeTab === 'ui' }" translate>User Interface</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
import ConfigurationJobsController from './jobs-form/configuration-jobs.controller';
|
||||
import ConfigurationSystemController from './system-form/configuration-system.controller';
|
||||
import ConfigurationUiController from './ui-form/configuration-ui.controller';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'configuration',
|
||||
@ -26,7 +27,7 @@
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: "Edit Configuration"
|
||||
label: N_("Edit Configuration")
|
||||
},
|
||||
controller: ConfigurationController,
|
||||
resolve: {
|
||||
|
||||
@ -17,10 +17,6 @@
|
||||
reset: 'AD_HOC_COMMANDS',
|
||||
multiSelect: true
|
||||
},
|
||||
STDOUT_MAX_BYTES_DISPLAY: {
|
||||
type: 'number',
|
||||
reset: 'STDOUT_MAX_BYTES_DISPLAY'
|
||||
},
|
||||
AWX_PROOT_BASE_PATH: {
|
||||
type: 'text',
|
||||
reset: 'AWX_PROOT_BASE_PATH',
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
|
||||
export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
$stateParams, Rest, Alert, CredentialList, Prompt, ClearScope,
|
||||
ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset) {
|
||||
ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset,
|
||||
i18n) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
@ -38,31 +39,34 @@ export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
|
||||
$scope.$on(`${list.iterator}_options`, function(event, data){
|
||||
$scope.options = data.data.actions.GET;
|
||||
console.log($scope.options);
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
if ($scope[list.name] !== undefined) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
|
||||
// Set the item type label
|
||||
if (list.fields.kind && $scope.options &&
|
||||
$scope.options.hasOwnProperty('kind')) {
|
||||
$scope.options.kind.choices.every(function(choice) {
|
||||
if (choice[0] === item.kind) {
|
||||
itm.kind_label = choice[1];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
// Set the item type label
|
||||
if (list.fields.kind && $scope.options &&
|
||||
$scope.options.hasOwnProperty('kind')) {
|
||||
$scope.options.kind.choices.every(function(choice) {
|
||||
if (choice[0] === item.kind) {
|
||||
itm.kind_label = choice[1];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.addCredential = function() {
|
||||
@ -97,24 +101,24 @@ export function CredentialsList($scope, $rootScope, $location, $log,
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the credential below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
|
||||
hdr: i18n._('Delete'),
|
||||
body: '<div class="Prompt-bodyQuery">' + i18n._('Are you sure you want to delete the credential below?') + '</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
CredentialsList.$inject = ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'CredentialList', 'Prompt', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset'
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'i18n'
|
||||
];
|
||||
|
||||
|
||||
export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, GetChoices, Empty, KindChange, BecomeMethodChange,
|
||||
OwnerChange, FormSave, $state, CreateSelect2) {
|
||||
OwnerChange, FormSave, $state, CreateSelect2, i18n) {
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
@ -154,7 +158,7 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
GenerateForm.applyDefaults(form, $scope);
|
||||
|
||||
$scope.keyEntered = false;
|
||||
$scope.permissionsTooltip = 'Please save before assigning permissions';
|
||||
$scope.permissionsTooltip = i18n._('Please save before assigning permissions');
|
||||
|
||||
// determine if the currently logged-in user may share this credential
|
||||
// previous commentary said: "$rootScope.current_user isn't available because a call to the config endpoint hasn't finished resolving yet"
|
||||
@ -281,7 +285,7 @@ export function CredentialsAdd($scope, $rootScope, $compile, $location, $log,
|
||||
CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'BecomeMethodChange',
|
||||
'OwnerChange', 'FormSave', '$state', 'CreateSelect2'
|
||||
'OwnerChange', 'FormSave', '$state', 'CreateSelect2', 'i18n'
|
||||
];
|
||||
|
||||
export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
@ -571,10 +575,10 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to remove the ' + title + ' below from ' + $scope.name + '?</div><div class="Prompt-bodyTarget">' + name + '</div>',
|
||||
hdr: i18n._('Delete'),
|
||||
body: '<div class="Prompt-bodyQuery">' + i18n.sprintf(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '</div><div class="Prompt-bodyTarget">' + name + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
@ -47,7 +47,7 @@ export function Home($scope, $compile, $stateParams, $rootScope, $location, $log
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status });
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath("job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false");
|
||||
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.dashboardJobTemplatesListData = data.results;
|
||||
@ -123,7 +123,7 @@ export function Home($scope, $compile, $stateParams, $rootScope, $location, $log
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status });
|
||||
});
|
||||
Rest.setUrl(GetBasePath("job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false");
|
||||
Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template");
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
data = data.results;
|
||||
|
||||
@ -9,6 +9,7 @@ import form from './dashboard-hosts.form';
|
||||
import listController from './dashboard-hosts-list.controller';
|
||||
import editController from './dashboard-hosts-edit.controller';
|
||||
import service from './dashboard-hosts.service';
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default
|
||||
angular.module('dashboardHosts', [])
|
||||
@ -51,7 +52,7 @@ angular.module('dashboardHosts', [])
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'dashboard',
|
||||
label: "HOSTS"
|
||||
label: N_("HOSTS")
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
@ -3,7 +3,8 @@ export default
|
||||
[ 'InitiatePlaybookRun',
|
||||
'templateUrl',
|
||||
'$state',
|
||||
function JobTemplatesList(InitiatePlaybookRun, templateUrl, $state) {
|
||||
'Alert',
|
||||
function JobTemplatesList(InitiatePlaybookRun, templateUrl, $state, Alert) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: link,
|
||||
@ -32,7 +33,8 @@ export default
|
||||
launch_url: job_template.url,
|
||||
edit_url: job_template.url.replace('api/v1', '#'),
|
||||
name: job_template.name,
|
||||
id: job_template.id
|
||||
id: job_template.id,
|
||||
type: job_template.type
|
||||
}; });
|
||||
|
||||
scope.snapRows = (list.length < 4);
|
||||
@ -42,8 +44,23 @@ export default
|
||||
return (status === "successful");
|
||||
};
|
||||
|
||||
scope.launchJobTemplate = function(jobTemplateId){
|
||||
InitiatePlaybookRun({ scope: scope, id: jobTemplateId, job_type: 'job_template' });
|
||||
scope.launchJobTemplate = function(template){
|
||||
if(template) {
|
||||
if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
|
||||
InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'job_template' });
|
||||
}
|
||||
else if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
|
||||
InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'workflow_job_template' });
|
||||
}
|
||||
else {
|
||||
// Something went wrong - Let the user know that we're unable to launch because we don't know
|
||||
// what type of job template this is
|
||||
Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while launching.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
Alert('Error: Unable to launch template', 'Template parameter is missing');
|
||||
}
|
||||
};
|
||||
|
||||
scope.editJobTemplate = function (jobTemplateId) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="DashboardList" ng-hide="noJobTemplates">
|
||||
<div class="DashboardList-header">
|
||||
<h3 class="DashboardList-headerText">
|
||||
<translate>RECENTLY USED JOB TEMPLATES</translate>
|
||||
<translate>RECENTLY USED TEMPLATES</translate>
|
||||
</h3>
|
||||
<a href="/#/templates" class="DashboardList-viewAll">
|
||||
<translate>VIEW ALL</translate>
|
||||
@ -34,7 +34,7 @@
|
||||
</td>
|
||||
<td class="List-actionsContainer">
|
||||
<div class="List-actionButtonCell">
|
||||
<button class="List-actionButton" ng-click="launchJobTemplate(job_template.id)">
|
||||
<button class="List-actionButton" ng-click="launchJobTemplate(job_template)">
|
||||
<i class="icon-launch"></i>
|
||||
</button>
|
||||
<button class="List-actionButton" ng-click="editJobTemplate(job_template.id)">
|
||||
@ -53,7 +53,8 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="DashboardList-container">
|
||||
<p class="DashboardList-noJobs">No job templates were recently used.<br />
|
||||
You can create a job template <a href="#/templates/add_job_template">here</a>.</p>
|
||||
<p class="DashboardList-noJobs"><translate>No job templates were recently used.</translate><br />
|
||||
<!-- TODO: Seems $sce.trustAsHtml() does not work here. -->
|
||||
<translate>You can create a job template <a href="#/templates/add_job_template">here</a>.</translate></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -18,10 +18,11 @@
|
||||
|
||||
export default
|
||||
angular.module('ActivityDetailDefinition', [])
|
||||
.value('ActivityDetailForm', {
|
||||
.factory('ActivityDetailForm', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'activity',
|
||||
editTitle: 'Activity Detail',
|
||||
editTitle: i18n._('Activity Detail'),
|
||||
well: false,
|
||||
'class': 'horizontal-narrow',
|
||||
formFieldSize: 'col-lg-10',
|
||||
@ -29,17 +30,17 @@ export default
|
||||
|
||||
fields: {
|
||||
user: {
|
||||
label: "Initiated by",
|
||||
label: i18n._("Initiated by"),
|
||||
type: 'text',
|
||||
readonly: true
|
||||
},
|
||||
operation: {
|
||||
label: 'Action',
|
||||
label: i18n._('Action'),
|
||||
type: 'text',
|
||||
readonly: true
|
||||
},
|
||||
changes: {
|
||||
label: 'Changes',
|
||||
label: i18n._('Changes'),
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel',
|
||||
ngHide: "!changes || changes =='' || changes == 'null'",
|
||||
@ -47,4 +48,4 @@ export default
|
||||
}
|
||||
}
|
||||
|
||||
}); //Form
|
||||
};}]); //Form
|
||||
|
||||
@ -12,73 +12,74 @@
|
||||
|
||||
export default
|
||||
angular.module('EventsViewerFormDefinition', [])
|
||||
.value('EventsViewerForm', {
|
||||
.factory('EventsViewerForm', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
fields: {
|
||||
host_name: {
|
||||
label: 'Host',
|
||||
section: 'Event'
|
||||
label: i18n._('Host'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
status: {
|
||||
label: 'Status',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Status'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
id: {
|
||||
label: 'ID',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('ID'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
created: {
|
||||
label: 'Created On',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Created On'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
role: {
|
||||
label: 'Role',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Role'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
play: {
|
||||
label: 'Play',
|
||||
labellabel: i18n._('Play'),
|
||||
type: 'text',
|
||||
section: 'Event'
|
||||
section: i18n._('Event')
|
||||
},
|
||||
task: {
|
||||
label: 'Task',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Task'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
item: {
|
||||
label: 'Item',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Item'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
module_name: {
|
||||
label: 'Module',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Module'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
module_args: {
|
||||
label: 'Arguments',
|
||||
section: 'Event'
|
||||
labellabel: i18n._('Arguments'),
|
||||
section: i18n._('Event')
|
||||
},
|
||||
rc: {
|
||||
label: 'Return Code',
|
||||
section: 'Results'
|
||||
labellabel: i18n._('Return Code'),
|
||||
section: i18n._('Results')
|
||||
},
|
||||
msg: {
|
||||
label: 'Message',
|
||||
section: 'Results'
|
||||
labellabel: i18n._('Message'),
|
||||
section: i18n._('Results')
|
||||
},
|
||||
results: {
|
||||
label: 'Results',
|
||||
section: 'Results'
|
||||
labellabel: i18n._('Results'),
|
||||
section: i18n._('Results')
|
||||
},
|
||||
start: {
|
||||
label: 'Start',
|
||||
section: 'Timing'
|
||||
labellabel: i18n._('Start'),
|
||||
section: i18n._('Timing')
|
||||
},
|
||||
end: {
|
||||
label: 'End',
|
||||
section: 'Timing'
|
||||
labellabel: i18n._('End'),
|
||||
section: i18n._('Timing')
|
||||
},
|
||||
delta: {
|
||||
label: 'Elapsed',
|
||||
section: 'Timing'
|
||||
labellabel: i18n._('Elapsed'),
|
||||
section: i18n._('Timing')
|
||||
}
|
||||
}
|
||||
});
|
||||
};}]);
|
||||
|
||||
@ -92,7 +92,7 @@ angular.module('InventoryFormDefinition', [])
|
||||
dataPlacement: 'top',
|
||||
basePath: 'api/v1/inventories/{{$stateParams.inventory_id}}/access_list/',
|
||||
type: 'collection',
|
||||
title: 'Permissions',
|
||||
title: i18n._('Permissions'),
|
||||
iterator: 'permission',
|
||||
index: false,
|
||||
open: false,
|
||||
|
||||
@ -12,49 +12,50 @@
|
||||
|
||||
export default
|
||||
angular.module('LogViewerStatusDefinition', [])
|
||||
.value('LogViewerStatusForm', {
|
||||
.factory('LogViewerStatusForm', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'status',
|
||||
well: false,
|
||||
|
||||
fields: {
|
||||
"name": {
|
||||
label: "Name",
|
||||
label: i18n._("Name"),
|
||||
type: "text",
|
||||
readonly: true,
|
||||
},
|
||||
"status": {
|
||||
label: "Status",
|
||||
label: i18n._("Status"),
|
||||
type: "text",
|
||||
readonly: true
|
||||
},
|
||||
"license_error": {
|
||||
label: "License Error",
|
||||
label: i18n._("License Error"),
|
||||
type: "text",
|
||||
readonly: true
|
||||
},
|
||||
"started": {
|
||||
label: "Started",
|
||||
label: i18n._("Started"),
|
||||
type: "date",
|
||||
"filter": "longDate",
|
||||
readonly: true
|
||||
},
|
||||
"finished": {
|
||||
label: "Finished",
|
||||
label: i18n._("Finished"),
|
||||
type: "date",
|
||||
"filter": "longDate",
|
||||
readonly: true
|
||||
},
|
||||
"elapsed": {
|
||||
label: "Elapsed",
|
||||
label: i18n._("Elapsed"),
|
||||
type: "text",
|
||||
readonly: true
|
||||
},
|
||||
"launch_type": {
|
||||
label: "Launch Type",
|
||||
label: i18n._("Launch Type"),
|
||||
type: "text",
|
||||
readonly: true
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};}]);
|
||||
|
||||
@ -132,18 +132,18 @@ export default
|
||||
fields: {
|
||||
username: {
|
||||
key: true,
|
||||
label: 'User',
|
||||
label: i18n._('User'),
|
||||
linkBase: 'users',
|
||||
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
|
||||
},
|
||||
role: {
|
||||
label: 'Role',
|
||||
label: i18n._('Role'),
|
||||
type: 'role',
|
||||
noSort: true,
|
||||
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
||||
},
|
||||
team_roles: {
|
||||
label: 'Team Roles',
|
||||
label: i18n._('Team Roles'),
|
||||
type: 'team_roles',
|
||||
noSort: true,
|
||||
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
||||
|
||||
@ -12,45 +12,45 @@
|
||||
|
||||
export default
|
||||
angular.module('ActivityStreamHelper', ['Utilities'])
|
||||
.factory('GetTargetTitle', [
|
||||
function () {
|
||||
.factory('GetTargetTitle', ['i18n',
|
||||
function (i18n) {
|
||||
return function (target) {
|
||||
|
||||
var rtnTitle = 'ALL ACTIVITY';
|
||||
var rtnTitle = i18n._('ALL ACTIVITY');
|
||||
|
||||
switch(target) {
|
||||
case 'project':
|
||||
rtnTitle = 'PROJECTS';
|
||||
rtnTitle = i18n._('PROJECTS');
|
||||
break;
|
||||
case 'inventory':
|
||||
rtnTitle = 'INVENTORIES';
|
||||
rtnTitle = i18n._('INVENTORIES');
|
||||
break;
|
||||
case 'credential':
|
||||
rtnTitle = 'CREDENTIALS';
|
||||
rtnTitle = i18n._('CREDENTIALS');
|
||||
break;
|
||||
case 'user':
|
||||
rtnTitle = 'USERS';
|
||||
rtnTitle = i18n._('USERS');
|
||||
break;
|
||||
case 'team':
|
||||
rtnTitle = 'TEAMS';
|
||||
rtnTitle = i18n._('TEAMS');
|
||||
break;
|
||||
case 'organization':
|
||||
rtnTitle = 'ORGANIZATIONS';
|
||||
rtnTitle = i18n._('ORGANIZATIONS');
|
||||
break;
|
||||
case 'job':
|
||||
rtnTitle = 'JOBS';
|
||||
rtnTitle = i18n._('JOBS');
|
||||
break;
|
||||
case 'inventory_script':
|
||||
rtnTitle = 'INVENTORY SCRIPTS';
|
||||
rtnTitle = i18n._('INVENTORY SCRIPTS');
|
||||
break;
|
||||
case 'schedule':
|
||||
rtnTitle = 'SCHEDULES';
|
||||
rtnTitle = i18n._('SCHEDULES');
|
||||
break;
|
||||
case 'host':
|
||||
rtnTitle = 'HOSTS';
|
||||
rtnTitle = i18n._('HOSTS');
|
||||
break;
|
||||
case 'template':
|
||||
rtnTitle = 'TEMPLATES';
|
||||
rtnTitle = i18n._('TEMPLATES');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -126,9 +126,9 @@ export default
|
||||
}])
|
||||
|
||||
.factory('DeleteJob', ['$state', 'Find', 'GetBasePath', 'Rest', 'Wait',
|
||||
'ProcessErrors', 'Prompt', 'Alert', '$filter',
|
||||
'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n',
|
||||
function($state, Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert,
|
||||
$filter){
|
||||
$filter, i18n){
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
id = params.id,
|
||||
@ -158,11 +158,11 @@ export default
|
||||
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
||||
url = job.related.cancel;
|
||||
action_label = 'cancel';
|
||||
hdr = 'Cancel';
|
||||
hdr = i18n._('Cancel');
|
||||
} else {
|
||||
url = job.url;
|
||||
action_label = 'delete';
|
||||
hdr = 'Delete';
|
||||
hdr = i18n._('Delete');
|
||||
}
|
||||
|
||||
action = function () {
|
||||
@ -227,8 +227,8 @@ export default
|
||||
scope.removeCancelJob();
|
||||
}
|
||||
scope.removeCancelJob = scope.$on('CancelJob', function() {
|
||||
var cancelBody = "<div class=\"Prompt-bodyQuery\">Submit the request to cancel?</div>";
|
||||
var deleteBody = "<div class=\"Prompt-bodyQuery\">Are you sure you want to delete the job below?</div><div class=\"Prompt-bodyTarget\">#" + id + " " + $filter('sanitize')(job.name) + "</div>";
|
||||
var cancelBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Submit the request to cancel?") + "</div>";
|
||||
var deleteBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Are you sure you want to delete the job below?") + "</div><div class=\"Prompt-bodyTarget\">#" + id + " " + $filter('sanitize')(job.name) + "</div>";
|
||||
Prompt({
|
||||
hdr: hdr,
|
||||
body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody,
|
||||
|
||||
@ -48,35 +48,35 @@ export default
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('GetProjectToolTip', [ function() {
|
||||
.factory('GetProjectToolTip', ['i18n', function(i18n) {
|
||||
return function(status) {
|
||||
var result = '';
|
||||
switch (status) {
|
||||
case 'n/a':
|
||||
case 'ok':
|
||||
case 'never updated':
|
||||
result = 'No SCM updates have run for this project';
|
||||
result = i18n._('No SCM updates have run for this project');
|
||||
break;
|
||||
case 'pending':
|
||||
case 'waiting':
|
||||
case 'new':
|
||||
result = 'Queued. Click for details';
|
||||
result = i18n._('Queued. Click for details');
|
||||
break;
|
||||
case 'updating':
|
||||
case 'running':
|
||||
result = 'Running! Click for details';
|
||||
result = i18n._('Running! Click for details');
|
||||
break;
|
||||
case 'successful':
|
||||
result = 'Success! Click for details';
|
||||
result = i18n._('Success! Click for details');
|
||||
break;
|
||||
case 'failed':
|
||||
result = 'Failed. Click for details';
|
||||
result = i18n._('Failed. Click for details');
|
||||
break;
|
||||
case 'missing':
|
||||
result = 'Missing. Click for details';
|
||||
result = i18n._('Missing. Click for details');
|
||||
break;
|
||||
case 'canceled':
|
||||
result = 'Canceled. Click for details';
|
||||
result = i18n._('Canceled. Click for details');
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
||||
@ -12,6 +12,7 @@ import inventoryManageListRoute from './manage/inventory-manage.route';
|
||||
import { copyMoveGroupRoute, copyMoveHostRoute } from './manage/copy-move/copy-move.route';
|
||||
import adHocRoute from './manage/adhoc/adhoc.route';
|
||||
import { templateUrl } from '../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../i18n';
|
||||
export default
|
||||
angular.module('inventory', [
|
||||
inventoryAdd.name,
|
||||
@ -55,7 +56,7 @@ angular.module('inventory', [
|
||||
searchPrefix: 'schedule',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'inventoryManage.editGroup({group_id: parentObject.id})',
|
||||
label: 'SCHEDULES'
|
||||
label: N_('SCHEDULES')
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', 'groupData',
|
||||
@ -89,7 +90,7 @@ angular.module('inventory', [
|
||||
'@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject) {
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Schedules');
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
@ -106,7 +107,7 @@ angular.module('inventory', [
|
||||
name: 'inventoryManage.editGroup.schedules.add',
|
||||
url: '/add',
|
||||
ncyBreadcrumb: {
|
||||
label: "CREATE SCHEDULE"
|
||||
label: N_("CREATE SCHEDULE")
|
||||
},
|
||||
views: {
|
||||
'form': {
|
||||
@ -286,6 +287,9 @@ angular.module('inventory', [
|
||||
stateTree = {
|
||||
name: 'inventories',
|
||||
url: '/inventories',
|
||||
ncyBreadcrumb: {
|
||||
label: N_("INVENTORIES")
|
||||
},
|
||||
lazyLoad: () => generateStateTree()
|
||||
};
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../../../i18n';
|
||||
|
||||
export default {
|
||||
url: '/adhoc',
|
||||
@ -22,6 +23,6 @@ export default {
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "RUN COMMAND"
|
||||
label: N_("RUN COMMAND")
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
<div class="BreadCrumb InventoryManageBreadCrumbs">
|
||||
<ol class="BreadCrumb-list">
|
||||
<li class="BreadCrumb-item"><a ui-sref="inventories">Inventories</a></li>
|
||||
<li class="BreadCrumb-item">
|
||||
<li class="BreadCrumb-item BreadCrumb-invItem">
|
||||
<a href ng-if="currentState !== 'inventoryManage' || groups.length > 0" ng-click="goToInventory()">{{inventory.name}}</a>
|
||||
<span ng-if="currentState === 'inventoryManage' && groups.length === 0">{{inventory.name}}</span>
|
||||
</li>
|
||||
<!-- inside inventoryManage list view (last item is not clickable) -->
|
||||
<li ng-repeat="group in groups | limitTo:(groups.length-1) track by $index" class="BreadCrumb-item" ng-if="currentState === 'inventoryManage'">
|
||||
<li ng-repeat="group in groups | limitTo:(groups.length-1) track by $index" class="BreadCrumb-item BreadCrumb-invItem" ng-if="currentState === 'inventoryManage'">
|
||||
<a href ng-click="goToGroup($index+1)">{{group.name}}</a>
|
||||
</li>
|
||||
<li ng-hide="groups.length == 0" class="BreadCrumb-item" ng-if="currentState === 'inventoryManage'">
|
||||
<li ng-hide="groups.length == 0" class="BreadCrumb-item BreadCrumb-invItem" ng-if="currentState === 'inventoryManage'">
|
||||
<span>{{groups[groups.length-1].name}}</span>
|
||||
</li>
|
||||
<!-- inside inventoryManage.child like add/edit (last item is clickable)-->
|
||||
<li ng-repeat="group in groups track by $index" class="BreadCrumb-item" ng-if="currentState !== 'inventoryManage'">
|
||||
<li ng-repeat="group in groups track by $index" class="BreadCrumb-item BreadCrumb-invItem" ng-if="currentState !== 'inventoryManage'">
|
||||
<a href ng-click="goToGroup($index+1)">{{group.name}}</a>
|
||||
</li>
|
||||
<li class="BreadCrumb-item" ng-if="currentState !== 'inventoryManage'"></li>
|
||||
<li class="BreadCrumb-item BreadCrumb-invItem" ng-if="currentState !== 'inventoryManage'"></li>
|
||||
<div class="InventoryManageBreadCrumb-ncy" ng-if="!licenseMissing" ncy-breadcrumb></div>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../../../i18n';
|
||||
|
||||
import CopyMoveGroupsController from './copy-move-groups.controller';
|
||||
import CopyMoveHostsController from './copy-move-hosts.controller';
|
||||
@ -25,7 +26,7 @@ var copyMoveGroupRoute = {
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "COPY OR MOVE {{item.name}}"
|
||||
label: N_("COPY OR MOVE") + " {{item.name}}"
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', 'group',
|
||||
@ -61,7 +62,7 @@ var copyMoveHostRoute = {
|
||||
url: '/copy-move-host/{host_id}',
|
||||
searchPrefix: 'copy',
|
||||
ncyBreadcrumb: {
|
||||
label: "COPY OR MOVE {{item.name}}"
|
||||
label: N_("COPY OR MOVE") + " {{item.name}}"
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
|
||||
@ -9,6 +9,7 @@ import inventoryScriptsAdd from './add/main';
|
||||
import inventoryScriptsEdit from './edit/main';
|
||||
import list from './inventory-scripts.list';
|
||||
import form from './inventory-scripts.form';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default
|
||||
angular.module('inventoryScripts', [
|
||||
@ -62,7 +63,7 @@ angular.module('inventoryScripts', [
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'INVENTORY SCRIPTS'
|
||||
label: N_('INVENTORY SCRIPTS')
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessStatus" href="" ng-click="toggleLessStatus()">
|
||||
RESULTS<i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
<translate>RESULTS</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessStatus" href="" ng-click="toggleLessStatus()">
|
||||
RESULTS<i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
<translate>RESULTS</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="JobDetail-actions">
|
||||
@ -23,12 +23,12 @@
|
||||
|
||||
<div class="form-horizontal JobDetail-resultsDetails" role="form" id="job-status-form">
|
||||
<div class="form-group JobDetail-resultRow toggle-show">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Status</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Status</label>
|
||||
<div class="JobDetail-resultRowText"><i class="JobDetail-statusIcon--results fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.explanation">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12">Explanation</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12" translate>Explanation</label>
|
||||
<div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
|
||||
ng-show="!previousTaskFailed" ng-bind-html="job_status.explanation"></div>
|
||||
<div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
|
||||
@ -49,118 +49,118 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.traceback">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-12 col-sm-12 col-xs-12">Results Traceback</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-12 col-sm-12 col-xs-12" translate>Results Traceback</label>
|
||||
<div class="JobDetail-resultRowText col-lg-10 col-md-12 col-sm-12 col-xs-12 job_status_traceback" ng-bind-html="job_status.traceback"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_template_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Template</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Template</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ job_template_url }}" aw-tool-tip="Edit the job template" data-placement="top">{{ job_template_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Started</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Started</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.started | longDate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_type">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Type</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Job Type</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_type }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Finished</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Finished</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.finished | longDate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="created_by">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Launched By</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ users_url }}" aw-tool-tip="Edit the User" data-placement="top">{{ created_by }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Elapsed</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Elapsed</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.elapsed }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="scheduled_by">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Launched By</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href aw-tool-tip="Edit the Schedule" data-placement="top" ng-click="editSchedule()">{{scheduled_by}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="inventory_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Inventory</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Inventory</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ inventory_url }}" aw-tool-tip="Edit the inventory" data-placement="top">{{ inventory_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="project_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Project</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Project</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ project_url }}" aw-tool-tip="Edit the project" data-placement="top">{{ project_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.playbook">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Playbook</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Playbook</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.playbook }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Machine Credential</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Machine Credential</label>
|
||||
<div class="JobDetail-resultRowText JobDetail-resultRowText">
|
||||
<a href="{{ credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="cloud_credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Cloud Credential</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Cloud Credential</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ cloud_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ cloud_credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="network_credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Network Credential</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Network Credential</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ network_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ network_credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.forks">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Forks</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Forks</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.forks }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.limit">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Limit</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Limit</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.limit }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="verbosity">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Verbosity</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Verbosity</label>
|
||||
<div class="JobDetail-resultRowText">{{ verbosity }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.job_tags">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Tags</label>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Job Tags</label>
|
||||
<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>
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>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>
|
||||
<label class="JobDetail-resultRowLabel JobDetail-extraVarsLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Extra Variables</label>
|
||||
<textarea rows="6" ng-model="variables" name="variables" class="JobDetail-extraVars" id="pre-formatted-variables"></textarea>
|
||||
</div>
|
||||
|
||||
@ -173,10 +173,10 @@
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessDetail" href="" ng-click="toggleLessDetail()">
|
||||
DETAILS<i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
<translate>DETAILS</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessDetail" href="" ng-click="toggleLessDetail()">
|
||||
DETAILS<i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
<translate>DETAILS</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -194,8 +194,8 @@
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterPlayStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active">All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default">Failed</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -205,9 +205,9 @@
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-7 col-md-6 col-sm-6 col-xs-4">Plays</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Started</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Elapsed</th>
|
||||
<th class="List-tableHeader col-lg-7 col-md-6 col-sm-6 col-xs-4" translate>Plays</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Started</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Elapsed</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@ -242,7 +242,7 @@
|
||||
<!-- end of plays section of details-->
|
||||
|
||||
<div id="task-section" class="section JobDetail-tasks" >
|
||||
<div class="JobDetail-instructions"><span class="badge">2</span> Please select a task below to view its associated hosts</div>
|
||||
<div class="JobDetail-instructions"><span class="badge">2</span> <translate>Please select a task below to view its associated hosts</translate></div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
@ -253,8 +253,8 @@
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterTaskStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active">All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default">Failed</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -263,10 +263,10 @@
|
||||
<table id="tasks-table-header" class="table table-condensed" ng-show="taskList.length !== 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-3 col-md-3 col-sm-6 col-xs-4">Tasks</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Started</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Elapsed</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-4 col-md-3 hidden-xs hidden-sm">Host Status</th>
|
||||
<th class="List-tableHeader col-lg-3 col-md-3 col-sm-6 col-xs-4" translate>Tasks</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Started</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Elapsed</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-4 col-md-3 hidden-xs hidden-sm" translate>Host Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@ -304,19 +304,19 @@
|
||||
<span class="badge missing-hosts">{{ task.missingCount }}</span>
|
||||
</a>
|
||||
<div class="no-matching-hosts inner-bar" id="{{ task.id }}-{{ task.play_id }}-no-matching-hosts-bar" aw-tool-tip="No matching hosts were found." data-placement="top" style="width: 100%;" ng-show="task.status === 'no-matching-hosts'">
|
||||
No matching hosts.
|
||||
<translate>No matching hosts.</translate>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">Waiting...</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Waiting...</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && tasksLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">Loading...</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Loading...</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && !tasksLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">No matching tasks</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>No matching tasks</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -337,8 +337,8 @@
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterHostStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active">All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default">Failed</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -346,9 +346,9 @@
|
||||
<table class="table table-condensed" ng-show="results.length !== 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-4 col-md-3 col-sm-3 col-xs-3">Hosts</th>
|
||||
<th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Item</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Message</th>
|
||||
<th class="List-tableHeader col-lg-4 col-md-3 col-sm-3 col-xs-3" translate>Hosts</th>
|
||||
<th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3" translate>Item</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3" translate>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@ -365,13 +365,13 @@
|
||||
<td class="List-tableCell col-lg-3 col-md-4 col-sm-3 col-xs-3">{{ result.msg }}</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">Waiting...</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Waiting...</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && hostResultsLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">Loading...</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Loading...</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && !hostResultsLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info">No matching host events</td>
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>No matching host events</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -388,10 +388,10 @@
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessEvents" ui-sref="jobDetail.host-summary" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
<translate>EVENT SUMMARY</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessEvents" ui-sref="jobDetail" ng-click="toggleLessEvents()">
|
||||
EVENT SUMMARY<i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
<translate>EVENT SUMMARY</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -411,13 +411,13 @@
|
||||
<div class="JobDetail-rightSide">
|
||||
<div class="JobDetail-stdoutPanel Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderText" translate>STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderActions">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Toggle Output" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Toggle Output'|translate}}" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a ng-show="job_status.status === 'failed' || job_status.status === 'successful' || job_status.status === 'canceled'" href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Download Output'|translate}}" data-placement="top">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
|
||||
export default
|
||||
function LaunchJob(Rest, Wait, ProcessErrors, ToJSON, Empty, GetBasePath, $state, $location, $rootScope) {
|
||||
function LaunchJob(Rest, Wait, ProcessErrors, ToJSON, Empty, GetBasePath, $state, $location, $rootScope, i18n) {
|
||||
|
||||
// This factory gathers up all the job launch data and POST's it.
|
||||
|
||||
@ -163,8 +163,10 @@ export default
|
||||
}
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
|
||||
let template_id = scope.job_template_id;
|
||||
template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id);
|
||||
ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'),
|
||||
msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) });
|
||||
});
|
||||
};
|
||||
|
||||
@ -182,8 +184,8 @@ export default
|
||||
buildData();
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve job template extra variables.' });
|
||||
ProcessErrors(scope, data, status, { hdr: i18n._('Error!'),
|
||||
msg: i18n._('Failed to retrieve job template extra variables.') });
|
||||
});
|
||||
};
|
||||
|
||||
@ -209,5 +211,6 @@ LaunchJob.$inject =
|
||||
'GetBasePath',
|
||||
'$state',
|
||||
'$location',
|
||||
'$rootScope'
|
||||
'$rootScope',
|
||||
'i18n'
|
||||
];
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'license',
|
||||
@ -14,7 +15,7 @@ export default {
|
||||
data: {},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'LICENSE'
|
||||
label: N_('LICENSE')
|
||||
},
|
||||
resolve: {
|
||||
features: ['CheckLicense', '$rootScope',
|
||||
|
||||
@ -13,7 +13,7 @@ export default
|
||||
name: 'jobs',
|
||||
basePath: 'unified_jobs',
|
||||
iterator: 'job',
|
||||
editTitle: 'All Jobs',
|
||||
editTitle: i18n._('All Jobs'),
|
||||
index: false,
|
||||
hover: true,
|
||||
well: false,
|
||||
@ -42,13 +42,13 @@ export default
|
||||
noLink: true
|
||||
},
|
||||
name: {
|
||||
label: 'Name',
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
ngClick: "viewJobDetails(job)",
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
aw-tool-tip="View workflow results"
|
||||
aw-tool-tip=i18n._("View workflow results")
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
@ -58,14 +58,14 @@ export default
|
||||
</a>`
|
||||
},
|
||||
type: {
|
||||
label: 'Type',
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'job.type_label',
|
||||
link: false,
|
||||
columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
|
||||
columnShow: "showJobType",
|
||||
},
|
||||
finished: {
|
||||
label: 'Finished',
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
|
||||
@ -73,7 +73,7 @@ export default
|
||||
desc: true
|
||||
},
|
||||
labels: {
|
||||
label: 'Labels',
|
||||
label: i18n._('Labels'),
|
||||
type: 'labels',
|
||||
nosort: true,
|
||||
showDelete: false,
|
||||
@ -91,28 +91,28 @@ export default
|
||||
"view": {
|
||||
mode: "all",
|
||||
ngClick: "viewJobDetails(job)",
|
||||
awToolTip: "View the job",
|
||||
awToolTip: i18n._("View the job"),
|
||||
dataPlacement: "top"
|
||||
},
|
||||
submit: {
|
||||
icon: 'icon-rocket',
|
||||
mode: 'all',
|
||||
ngClick: 'relaunchJob($event, job.id)',
|
||||
awToolTip: 'Relaunch using the same parameters',
|
||||
awToolTip: i18n._('Relaunch using the same parameters'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start"
|
||||
},
|
||||
cancel: {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(job.id)',
|
||||
awToolTip: 'Cancel the job',
|
||||
awToolTip: i18n._('Cancel the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: "(job.status === 'running'|| job.status === 'waiting' || job.status === 'pending') && job.summary_fields.user_capabilities.start"
|
||||
},
|
||||
"delete": {
|
||||
mode: 'all',
|
||||
ngClick: 'deleteJob(job.id)',
|
||||
awToolTip: 'Delete the job',
|
||||
awToolTip: i18n._('Delete the job'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: "(job.status !== 'running' && job.status !== 'waiting' && job.status !== 'pending') && job.summary_fields.user_capabilities.delete"
|
||||
}
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
|
||||
export default
|
||||
angular.module('JobEventsListDefinition', [])
|
||||
.value('JobEventList', {
|
||||
.factory('JobEventList', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'jobevents',
|
||||
iterator: 'jobevent',
|
||||
editTitle: 'Job Events',
|
||||
editTitle: i18n._('Job Events'),
|
||||
index: false,
|
||||
hover: true,
|
||||
"class": "condensed",
|
||||
@ -27,27 +28,27 @@ export default
|
||||
//},
|
||||
events: {
|
||||
href: '/#/job_events/{{ job_id }}',
|
||||
label: 'Events',
|
||||
label: i18n._('Events'),
|
||||
active: true,
|
||||
icon: 'icon-list-ul'
|
||||
},
|
||||
hosts: {
|
||||
href: '/#/job_host_summaries/{{ job_id }}',
|
||||
label: 'Host Summary',
|
||||
label: i18n._('Host Summary'),
|
||||
icon: 'icon-laptop'
|
||||
}
|
||||
},
|
||||
|
||||
fields: {
|
||||
created: {
|
||||
label: 'Created On',
|
||||
label: i18n._('Created On'),
|
||||
columnClass: 'col-lg-1 col-md-1 hidden-sm hidden-xs',
|
||||
key: true,
|
||||
nosort: true,
|
||||
noLink: true
|
||||
},
|
||||
status: {
|
||||
label: 'Status',
|
||||
label: i18n._('Status'),
|
||||
showValue: false,
|
||||
columnClass: 'col-sm-1 col-xs-2 text-center',
|
||||
nosort: true,
|
||||
@ -61,7 +62,7 @@ export default
|
||||
badgeNgClick: 'viewJobEvent(jobevent.id)'
|
||||
},
|
||||
event_display: {
|
||||
label: 'Event',
|
||||
label: i18n._('Event'),
|
||||
hasChildren: true,
|
||||
ngClick: 'toggleChildren(jobevent.id, jobevent.related.children)',
|
||||
nosort: true,
|
||||
@ -69,7 +70,7 @@ export default
|
||||
appendHTML: 'jobevent.event_detail'
|
||||
},
|
||||
host: {
|
||||
label: 'Host',
|
||||
label: i18n._('Host'),
|
||||
ngBind: 'jobevent.summary_fields.host.name',
|
||||
ngHref: '{{ jobevent.hostLink }}',
|
||||
nosort: true,
|
||||
@ -85,7 +86,7 @@ export default
|
||||
awToolTip: 'Refresh the page',
|
||||
ngClick: 'refresh()',
|
||||
actionClass: 'btn List-buttonDefault',
|
||||
buttonContent: 'REFRESH'
|
||||
buttonContent: i18n._('REFRESH')
|
||||
}
|
||||
},
|
||||
|
||||
@ -94,10 +95,10 @@ export default
|
||||
columnClass: 'col-sm-1 col-xs-2',
|
||||
|
||||
view: {
|
||||
label: 'View',
|
||||
label: i18n._('View'),
|
||||
ngClick: 'viewJobEvent(jobevent.id)',
|
||||
awToolTip: 'View event details',
|
||||
awToolTip: i18n._('View event details'),
|
||||
dataPlacement: 'top'
|
||||
}
|
||||
}
|
||||
});
|
||||
};}]);
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
|
||||
export default
|
||||
angular.module('SchedulesListDefinition', [])
|
||||
.value('SchedulesList', {
|
||||
.factory('StreamList', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'schedules',
|
||||
iterator: 'schedule',
|
||||
@ -31,22 +32,22 @@ export default
|
||||
},
|
||||
name: {
|
||||
key: true,
|
||||
label: 'Name',
|
||||
label: i18n._('Name'),
|
||||
ngClick: "editSchedule(schedule)",
|
||||
columnClass: "col-md-3 col-sm-3 col-xs-6"
|
||||
},
|
||||
dtstart: {
|
||||
label: 'First Run',
|
||||
label: i18n._('First Run'),
|
||||
filter: "longDate",
|
||||
columnClass: "List-staticColumn--schedulerTime hidden-sm hidden-xs"
|
||||
},
|
||||
next_run: {
|
||||
label: 'Next Run',
|
||||
label: i18n._('Next Run'),
|
||||
filter: "longDate",
|
||||
columnClass: "List-staticColumn--schedulerTime hidden-xs"
|
||||
},
|
||||
dtend: {
|
||||
label: 'Final Run',
|
||||
label: i18n._('Final Run'),
|
||||
filter: "longDate",
|
||||
columnClass: "List-staticColumn--schedulerTime hidden-xs"
|
||||
},
|
||||
@ -55,45 +56,45 @@ export default
|
||||
actions: {
|
||||
refresh: {
|
||||
mode: 'all',
|
||||
awToolTip: "Refresh the page",
|
||||
awToolTip: i18n._("Refresh the page"),
|
||||
ngClick: "refreshSchedules()",
|
||||
actionClass: 'btn List-buttonDefault',
|
||||
ngShow: "socketStatus == 'error'",
|
||||
buttonContent: 'REFRESH'
|
||||
buttonContent: i18n._('REFRESH')
|
||||
},
|
||||
add: {
|
||||
mode: 'all',
|
||||
ngClick: 'addSchedule()',
|
||||
awToolTip: 'Add a new schedule',
|
||||
awToolTip: i18n._('Add a new schedule'),
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: '+ ADD',
|
||||
buttonContent: '+ ' + i18n._('ADD'),
|
||||
ngShow: 'canAdd'
|
||||
}
|
||||
},
|
||||
|
||||
fieldActions: {
|
||||
edit: {
|
||||
label: 'Edit',
|
||||
label: i18n._('Edit'),
|
||||
ngClick: "editSchedule(schedule)",
|
||||
icon: 'icon-edit',
|
||||
awToolTip: 'Edit schedule',
|
||||
awToolTip: i18n._('Edit schedule'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: 'schedule.summary_fields.user_capabilities.edit'
|
||||
},
|
||||
view: {
|
||||
label: 'View',
|
||||
label: i18n._('View'),
|
||||
ngClick: "editSchedule(schedule)",
|
||||
awToolTip: 'View schedule',
|
||||
awToolTip: i18n._('View schedule'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: '!schedule.summary_fields.user_capabilities.edit'
|
||||
},
|
||||
"delete": {
|
||||
label: 'Delete',
|
||||
label: i18n._('Delete'),
|
||||
ngClick: "deleteSchedule(schedule.id)",
|
||||
icon: 'icon-trash',
|
||||
awToolTip: 'Delete schedule',
|
||||
awToolTip: i18n._('Delete schedule'),
|
||||
dataPlacement: 'top',
|
||||
ngShow: 'schedule.summary_fields.user_capabilities.delete'
|
||||
}
|
||||
}
|
||||
});
|
||||
};}]);
|
||||
|
||||
@ -7,15 +7,16 @@
|
||||
|
||||
export default
|
||||
angular.module('StreamListDefinition', [])
|
||||
.value('StreamList', {
|
||||
.factory('StreamList', ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'activities',
|
||||
iterator: 'activity',
|
||||
basePath: 'activity_stream',
|
||||
editTitle: 'Activity Stream',
|
||||
listTitle: 'Activity Stream<span ng-show="streamSubTitle"><div class="List-titleLockup"></div>{{streamSubTitle}}<span>',
|
||||
editTitle: i18n._('Activity Stream'),
|
||||
listTitle: i18n._('Activity Stream') + '<span ng-show="streamSubTitle"><div class="List-titleLockup"></div>{{streamSubTitle}}<span>',
|
||||
listTitleBadge: false,
|
||||
emptyListText: 'There are no events to display at this time',
|
||||
emptyListText: i18n._('There are no events to display at this time'),
|
||||
selectInstructions: '',
|
||||
index: false,
|
||||
hover: true,
|
||||
@ -24,7 +25,7 @@ export default
|
||||
|
||||
fields: {
|
||||
timestamp: {
|
||||
label: 'Time',
|
||||
label: i18n._('Time'),
|
||||
key: true,
|
||||
desc: true,
|
||||
noLink: true,
|
||||
@ -32,14 +33,14 @@ export default
|
||||
columnClass: 'col-lg-3 col-md-2 col-sm-3 col-xs-3'
|
||||
},
|
||||
user: {
|
||||
label: 'Initiated by',
|
||||
label: i18n._('Initiated by'),
|
||||
ngBindHtml: 'activity.user', // @todo punch monkey
|
||||
sourceModel: 'actor',
|
||||
sourceField: 'username',
|
||||
columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3'
|
||||
},
|
||||
description: {
|
||||
label: 'Event',
|
||||
label: i18n._('Event'),
|
||||
ngBindHtml: 'activity.description', // @todo punch monkey
|
||||
nosort: true,
|
||||
columnClass: 'ActivityStream-eventColumnHeader col-lg-5 col-md-6 col-sm-4 col-xs-4'
|
||||
@ -50,10 +51,10 @@ export default
|
||||
refresh: {
|
||||
mode: 'all',
|
||||
id: 'activity-stream-refresh-btn',
|
||||
awToolTip: "Refresh the page",
|
||||
awToolTip: i18n._("Refresh the page"),
|
||||
ngClick: "refreshStream()",
|
||||
actionClass: 'btn List-buttonDefault ActivityStream-refreshButton',
|
||||
buttonContent: 'REFRESH'
|
||||
buttonContent: i18n._('REFRESH')
|
||||
}
|
||||
},
|
||||
|
||||
@ -62,13 +63,13 @@ export default
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2',
|
||||
|
||||
view: {
|
||||
label: 'View',
|
||||
label: i18n._('View'),
|
||||
ngClick: "showDetail(activity.id)",
|
||||
icon: 'fa-zoom-in',
|
||||
"class": 'btn-default btn-xs',
|
||||
awToolTip: 'View event details',
|
||||
awToolTip: i18n._('View event details'),
|
||||
dataPlacement: 'top'
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};}]);
|
||||
|
||||
@ -23,9 +23,9 @@
|
||||
*/
|
||||
export default
|
||||
['$rootScope', '$cookieStore', 'CreateDialog', 'Authorization',
|
||||
'Store', '$interval', '$state', '$q',
|
||||
'Store', '$interval', '$state', '$q', 'i18n',
|
||||
function ($rootScope, $cookieStore, CreateDialog, Authorization,
|
||||
Store, $interval, $state, $q) {
|
||||
Store, $interval, $state, $q, i18n) {
|
||||
return {
|
||||
|
||||
sessionTime: null,
|
||||
@ -154,7 +154,7 @@ export default
|
||||
});
|
||||
CreateDialog({
|
||||
id: 'idle-modal' ,
|
||||
title: "Idle Session",
|
||||
title: i18n._("Idle Session"),
|
||||
scope: $rootScope,
|
||||
buttons: buttons,
|
||||
width: 470,
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="login_modal_notice" class="LoginModalNotice" ng-if="customLoginInfoPresent"><div class="LoginModalNotice-title">NOTICE</div>{{ customLoginInfo | sanitize }}</div>
|
||||
<div id="login_modal_notice" class="LoginModalNotice" ng-if="customLoginInfoPresent"><div class="LoginModalNotice-title" translate>NOTICE</div>{{ customLoginInfo | sanitize }}</div>
|
||||
</div>
|
||||
<div class="LoginModal-footer">
|
||||
<div class="LoginModal-footerBlock">
|
||||
|
||||
@ -10,11 +10,11 @@ export default
|
||||
[ 'Wait', '$compile', 'CreateDialog', 'GetBasePath' ,
|
||||
'SchedulesList', 'Rest' ,
|
||||
'ProcessErrors', 'managementJobsListObject', '$rootScope', '$state',
|
||||
'$scope', 'CreateSelect2',
|
||||
'$scope', 'CreateSelect2', 'i18n',
|
||||
function( Wait, $compile, CreateDialog, GetBasePath,
|
||||
SchedulesList, Rest, ProcessErrors,
|
||||
managementJobsListObject, $rootScope, $state, $scope,
|
||||
CreateSelect2) {
|
||||
CreateSelect2, i18n) {
|
||||
|
||||
var defaultUrl = GetBasePath('system_job_templates') + "?order_by=name";
|
||||
|
||||
@ -26,8 +26,8 @@ export default
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function(data, status){
|
||||
ProcessErrors($scope, data, status, null, {hdr: 'Error!',
|
||||
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
|
||||
ProcessErrors($scope, data, status, null, {hdr: i18n._('Error!'),
|
||||
msg: i18n.sprintf(i18n._('Call to %s failed. Return status: %d'), (defaultUrl === undefined) ? "undefined" : defaultUrl, status )});
|
||||
});
|
||||
};
|
||||
getManagementJobs();
|
||||
@ -145,8 +145,10 @@ export default
|
||||
$state.go('managementJobStdout', {id: data.system_job}, {reload:true});
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
|
||||
let template_id = scope.job_template_id;
|
||||
template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id);
|
||||
ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'),
|
||||
msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) });
|
||||
});
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
@ -233,8 +235,10 @@ export default
|
||||
$state.go('managementJobStdout', {id: data.system_job}, {reload:true});
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status });
|
||||
let template_id = scope.job_template_id;
|
||||
template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id);
|
||||
ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'),
|
||||
msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) });
|
||||
});
|
||||
},
|
||||
"class": "btn btn-primary",
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'managementJobsList.notifications',
|
||||
route: '/:management_id/notifications',
|
||||
@ -16,7 +18,7 @@ export default {
|
||||
controller: 'managementJobsNotificationsController',
|
||||
templateProvider: function(NotificationsList, generateList, ParentObject) {
|
||||
// include name of parent resource in listTitle
|
||||
NotificationsList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Notifications`;
|
||||
NotificationsList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Notifications');
|
||||
let html = generateList.build({
|
||||
list: NotificationsList,
|
||||
mode: 'edit'
|
||||
@ -41,6 +43,6 @@ export default {
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'managementJobsList',
|
||||
label: 'NOTIFICATIONS'
|
||||
label: N_('NOTIFICATIONS')
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { templateUrl } from '../../shared/template-url/template-url.factory';
|
||||
import controller from '../../scheduler/schedulerList.controller';
|
||||
import addController from '../../scheduler/schedulerAdd.controller';
|
||||
import editController from '../../scheduler/schedulerEdit.controller';
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default
|
||||
angular.module('managementJobScheduler', [])
|
||||
@ -22,13 +23,13 @@ angular.module('managementJobScheduler', [])
|
||||
route: '/management_jobs/:id/schedules',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'managementJobsList',
|
||||
label: 'SCHEDULES'
|
||||
label: N_('SCHEDULES')
|
||||
},
|
||||
views: {
|
||||
'@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject) {
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Schedules');
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
@ -70,7 +71,7 @@ angular.module('managementJobScheduler', [])
|
||||
route: '/add',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'managementJobSchedules',
|
||||
label: 'CREATE SCHEDULED JOB'
|
||||
label: N_('CREATE SCHEDULED JOB')
|
||||
},
|
||||
views: {
|
||||
'form': {
|
||||
@ -84,7 +85,7 @@ angular.module('managementJobScheduler', [])
|
||||
route: '/edit/:schedule_id',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'managementJobSchedules',
|
||||
label: 'EDIT SCHEDULED JOB'
|
||||
label: N_('EDIT SCHEDULED JOB')
|
||||
},
|
||||
views: {
|
||||
'form': {
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="form-group SchedulerForm-formGroup">
|
||||
<label class="Form-inputLabel">
|
||||
<span class="red-text">*</span>
|
||||
Name
|
||||
<span translate>Name</span>
|
||||
</label>
|
||||
<input
|
||||
ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)"
|
||||
@ -31,14 +31,14 @@
|
||||
ng-model="schedulerName" required
|
||||
placeholder="Schedule name">
|
||||
<div class="error"
|
||||
ng-show="scheduler_form.$dirty && scheduler_form.schedulerName.$error.required">
|
||||
ng-show="scheduler_form.$dirty && scheduler_form.schedulerName.$error.required" translate>
|
||||
A schedule name is required.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group SchedulerForm-formGroup">
|
||||
<label class="Form-inputLabel">
|
||||
<span class="red-text">*</span>
|
||||
Start Date
|
||||
<span translate>Start Date</span>
|
||||
</label>
|
||||
<div class="input-group Form-inputGroup SchedulerForm-inputGroup--date">
|
||||
<scheduler-date-picker date="schedulerStartDt"
|
||||
@ -53,7 +53,7 @@
|
||||
<div class="form-group SchedulerForm-formGroup">
|
||||
<label class="Form-inputLabel">
|
||||
<span class="red-text">*</span>
|
||||
Start Time
|
||||
<span translate>Start Time</span>
|
||||
<span class="fmt-help"
|
||||
ng-show="schedulerShowTimeZone">
|
||||
(HH24:MM:SS)
|
||||
@ -122,7 +122,7 @@
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="error"
|
||||
ng-show="scheduler_startTime_error">
|
||||
ng-show="scheduler_startTime_error" translate>
|
||||
The time must be in HH24:MM:SS format.
|
||||
</div>
|
||||
</div>
|
||||
@ -130,7 +130,7 @@
|
||||
ng-show="schedulerShowTimeZone">
|
||||
<label class="Form-inputLabel">
|
||||
<span class="red-text">*</span>
|
||||
Local Time Zone
|
||||
<span translate>Local Time Zone</span>
|
||||
</label>
|
||||
<select
|
||||
ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)"
|
||||
@ -146,7 +146,7 @@
|
||||
<div class="form-group SchedulerForm-formGroup">
|
||||
<label class="Form-inputLabel">
|
||||
<span class="red-text">*</span>
|
||||
Repeat frequency
|
||||
<span translate>Repeat frequency</span>
|
||||
</label>
|
||||
<select name="schedulerFrequency"
|
||||
ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)"
|
||||
@ -163,14 +163,13 @@
|
||||
</div>
|
||||
<div class="form-group SchedulerForm-formGroup" ng-if="cleanupJob && !isFactCleanup">
|
||||
<label class="Form-inputLabel"><span class="red-text">*</span> Days of data to keep</label>
|
||||
<input type="number" sch-spinner="scheduler_form" class="scheduler-time-spinner SchedulerTime-input SpinnerInput RepeatFrequencyOptions-number" name="schedulerPurgeDays" id="schedulerPurgeDays" min="1" aw-spinner="schedulerPurgeDays" ng-model="schedulerPurgeDays" required placeholder="Days of data to keep"
|
||||
ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)">
|
||||
<div class="error" ng-show="scheduler_form.schedulerPurgeDays.$dirty && scheduler_form.schedulerPurgeDays.$error.required">A value is required.</div>
|
||||
<div class="error" ng-show="scheduler_form.schedulerPurgeDays.$error.number">This is not a valid number.</div>
|
||||
<input type="number" sch-spinner="scheduler_form" class="scheduler-time-spinner SchedulerTime-input SpinnerInput RepeatFrequencyOptions-number" name="schedulerPurgeDays" id="schedulerPurgeDays" min="1" aw-spinner="schedulerPurgeDays" ng-model="schedulerPurgeDays" required placeholder="Days of data to keep" ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)">
|
||||
<div class="error" ng-show="scheduler_form.schedulerPurgeDays.$dirty && scheduler_form.schedulerPurgeDays.$error.required" translate>A value is required.</div>
|
||||
<div class="error" ng-show="scheduler_form.schedulerPurgeDays.$error.number" translate>This is not a valid number.</div>
|
||||
</div>
|
||||
<div class="RepeatFrequencyOptions-label"
|
||||
ng-show="schedulerFrequency.value && schedulerFrequency.value !== 'none'">
|
||||
Frequency Details</div>
|
||||
<span translate>Frequency Details</span></div>
|
||||
<div class="RepeatFrequencyOptions Form"
|
||||
ng-show="schedulerFrequency.value && schedulerFrequency.value !== 'none'">
|
||||
<div class="form-group
|
||||
|
||||
@ -15,6 +15,7 @@ import notificationsList from './notifications.list';
|
||||
import toggleNotification from './shared/toggle-notification.factory';
|
||||
import notificationsListInit from './shared/notification-list-init.factory';
|
||||
import typeChange from './shared/type-change.service';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default
|
||||
angular.module('notifications', [
|
||||
@ -39,7 +40,7 @@ angular.module('notifications', [
|
||||
url: '/notification_templates',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: "NOTIFICATIONS"
|
||||
label: N_("NOTIFICATIONS")
|
||||
},
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'notifications', // top-most node in the generated tree
|
||||
@ -84,7 +85,7 @@ angular.module('notifications', [
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
name: 'NOTIFICATIONS'
|
||||
label: N_('NOTIFICATIONS')
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@ -7,10 +7,12 @@
|
||||
export default ['$rootScope', '$scope', 'Wait', 'generateList', 'NotificationTemplatesList',
|
||||
'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', 'GetChoices',
|
||||
'Empty', 'Find', 'ngToast', '$compile', '$filter', 'Dataset', 'rbacUiControlService',
|
||||
'i18n',
|
||||
function(
|
||||
$rootScope, $scope, Wait, GenerateList, NotificationTemplatesList,
|
||||
GetBasePath, Rest, ProcessErrors, Prompt, $state, GetChoices,
|
||||
Empty, Find, ngToast, $compile, $filter, Dataset, rbacUiControlService) {
|
||||
Empty, Find, ngToast, $compile, $filter, Dataset, rbacUiControlService,
|
||||
i18n) {
|
||||
|
||||
var defaultUrl = GetBasePath('notification_templates'),
|
||||
list = NotificationTemplatesList;
|
||||
@ -68,8 +70,8 @@
|
||||
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 += "<th>" + i18n._("Status") + "</th>";
|
||||
html += "<th>" + i18n._("Time") + "</th>";
|
||||
html += "</tr>\n";
|
||||
html += "</thead>\n";
|
||||
html += "<tbody>\n";
|
||||
@ -83,7 +85,7 @@
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
} else {
|
||||
html = "<p>No recent notifications.</p>\n";
|
||||
html = "<p>" + i18n._("No recent notifications.") + "</p>\n";
|
||||
}
|
||||
notification_template.template_status_html = html;
|
||||
}
|
||||
@ -110,7 +112,7 @@
|
||||
})
|
||||
.catch(function() {
|
||||
ngToast.danger({
|
||||
content: `<i class="fa fa-check-circle Toast-successIcon"></i> <b>${name}:</b> Notification Failed.`,
|
||||
content: `<i class="fa fa-check-circle Toast-successIcon"></i> <b>${name}:</b> ` + i18n._('Notification Failed.'),
|
||||
});
|
||||
});
|
||||
|
||||
@ -179,12 +181,12 @@
|
||||
});
|
||||
});
|
||||
};
|
||||
var bodyHtml = '<div class="Prompt-bodyQuery">Are you sure you want to delete the notification template below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>';
|
||||
var bodyHtml = '<div class="Prompt-bodyQuery">' + i18n._('Are you sure you want to delete the notification template below?') + '</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>';
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
hdr: i18n._('Delete'),
|
||||
body: bodyHtml,
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -16,6 +16,9 @@ export default ['i18n', function(i18n) {
|
||||
addTitle: i18n._('New Notification Template'),
|
||||
editTitle: '{{ name }}',
|
||||
name: 'notification_template',
|
||||
// I18N for "CREATE NOTIFICATION_TEMPLATE"
|
||||
// on /#/notification_templates/add
|
||||
breadcrumbName: i18n._('NOTIFICATION TEMPLATE'),
|
||||
stateTree: 'notifications',
|
||||
basePath: 'notification_templates',
|
||||
showActions: true,
|
||||
@ -389,19 +392,19 @@ export default ['i18n', function(i18n) {
|
||||
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
|
||||
},
|
||||
email_options: {
|
||||
label: 'Options',
|
||||
label: i18n._('Options'),
|
||||
type: 'radio_group',
|
||||
subForm: 'typeSubForm',
|
||||
ngShow: "notification_type.value == 'email'",
|
||||
ngChange: "emailOptionsChange()",
|
||||
options: [{
|
||||
value: 'use_tls',
|
||||
label: 'Use TLS',
|
||||
label: i18n._('Use TLS'),
|
||||
ngShow: "notification_type.value == 'email' ",
|
||||
labelClass: 'NotificationsForm-radioButtons'
|
||||
}, {
|
||||
value: 'use_ssl',
|
||||
label: 'Use SSL',
|
||||
label: i18n._('Use SSL'),
|
||||
ngShow: "notification_type.value == 'email'",
|
||||
labelClass: 'NotificationsForm-radioButtons'
|
||||
}]
|
||||
|
||||
@ -23,7 +23,7 @@ export default ['i18n', function(i18n){
|
||||
nosort: true,
|
||||
icon: 'icon-job-{{ notification_template.status }}',
|
||||
awPopOver: '{{ notification_template.template_status_html }}',
|
||||
dataTitle: "Recent Notifications",
|
||||
dataTitle: i18n._("Recent Notifications"),
|
||||
dataPlacement: 'right',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus'
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="AddUsers-header">
|
||||
<div class="List-header">
|
||||
<div class="List-title">
|
||||
<div class="List-titleText ng-binding">{{ $parent.organization_name }}<div class="List-titleLockup"></div>Add {{ addType | capitalize}}
|
||||
<div class="List-titleText ng-binding">{{ $parent.organization_name }}<div class="List-titleLockup"></div><span translate>Add</span> {{ addType | capitalize}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="Form-exitHolder">
|
||||
|
||||
@ -10,6 +10,7 @@ import OrganizationsJobTemplates from './controllers/organizations-job-templates
|
||||
import OrganizationsProjects from './controllers/organizations-projects.controller';
|
||||
import OrganizationsTeams from './controllers/organizations-teams.controller';
|
||||
import OrganizationsUsers from './controllers/organizations-users.controller';
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default [{
|
||||
name: 'organizations.users',
|
||||
@ -55,7 +56,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "USERS"
|
||||
label: N_("USERS")
|
||||
},
|
||||
|
||||
data: {
|
||||
@ -128,7 +129,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "TEAMS"
|
||||
label: N_("TEAMS")
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
@ -174,7 +175,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "INVENTORIES"
|
||||
label: N_("INVENTORIES")
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
@ -225,7 +226,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "PROJECTS"
|
||||
label: N_("PROJECTS")
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
@ -283,7 +284,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "JOB TEMPLATES"
|
||||
label: N_("JOB TEMPLATES")
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
@ -359,7 +360,7 @@ export default [{
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: "organizations.edit",
|
||||
label: "ADMINS"
|
||||
label: N_("ADMINS")
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
|
||||
export default ['$stateParams', '$scope', '$rootScope', '$location',
|
||||
'$log', '$compile', 'Rest', 'OrganizationList', 'Alert', 'Prompt', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'rbacUiControlService', '$filter', 'Dataset',
|
||||
'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'rbacUiControlService', '$filter', 'Dataset', 'i18n',
|
||||
function($stateParams, $scope, $rootScope, $location,
|
||||
$log, $compile, Rest, OrganizationList, Alert, Prompt, ClearScope,
|
||||
ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset) {
|
||||
ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset, i18n) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
@ -162,10 +162,10 @@ export default ['$stateParams', '$scope', '$rootScope', '$location',
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the organization below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
|
||||
hdr: i18n._('Delete'),
|
||||
body: '<div class="Prompt-bodyQuery">' + i18n._('Are you sure you want to delete the organization below?') + '</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
actionText: i18n._('DELETE')
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import OrganizationsAdd from './add/organizations-add.controller';
|
||||
import OrganizationsEdit from './edit/organizations-edit.controller';
|
||||
import organizationsLinkout from './linkout/main';
|
||||
import OrganizationsLinkoutStates from './linkout/organizations-linkout.route';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
|
||||
export default
|
||||
@ -48,7 +49,7 @@ angular.module('Organizations', [
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'ORGANIZATIONS'
|
||||
label: N_('ORGANIZATIONS')
|
||||
},
|
||||
// concat manually-defined state definitions with generated defintions
|
||||
}).then((generated) => {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
|
||||
<div id="logviewer-modal-dialog" style="display: none;">
|
||||
<ul id="logview-tabs" class="nav nav-tabs">
|
||||
<li class="active"><a href="#status" id="status-link" data-toggle="tab" ng-click="toggleTab($event, 'status-link', 'logview-tabs')">Status</a></li>
|
||||
<li><a href="#stdout" id="stdout-link" data-toggle="tab" ng-click="toggleTab($event, 'stdout-link', 'logview-tabs')">Standard Out</a></li>
|
||||
<li><a href="#traceback" id="traceback-link" data-toggle="tab" ng-click="toggleTab($event, 'traceback-link', 'logview-tabs')">Traceback</a></li>
|
||||
<li><a href="#options" id="options-link" data-toggle="tab" ng-click="toggleTab($event, 'options-link', 'logview-tabs')">Options</a></li>
|
||||
<li><a href="#variables" id="variables-link" data-toggle="tab" ng-click="toggleTab($event, 'variable-link', 'logview-tabs')">Extra Variables</a></li>
|
||||
<li><a href="#source-variables" id="source-variables-link" data-toggle="tab" ng-click="toggleTab($event, 'source-variable-link', 'logview-tabs')">Source Vars</a></li>
|
||||
<li class="active"><a href="#status" id="status-link" data-toggle="tab" ng-click="toggleTab($event, 'status-link', 'logview-tabs')" translate>Status</a></li>
|
||||
<li><a href="#stdout" id="stdout-link" data-toggle="tab" ng-click="toggleTab($event, 'stdout-link', 'logview-tabs')" translate>Standard Out</a></li>
|
||||
<li><a href="#traceback" id="traceback-link" data-toggle="tab" ng-click="toggleTab($event, 'traceback-link', 'logview-tabs')" translate>Traceback</a></li>
|
||||
<li><a href="#options" id="options-link" data-toggle="tab" ng-click="toggleTab($event, 'options-link', 'logview-tabs')" translate>Options</a></li>
|
||||
<li><a href="#variables" id="variables-link" data-toggle="tab" ng-click="toggleTab($event, 'variable-link', 'logview-tabs')" translate>Extra Variables</a></li>
|
||||
<li><a href="#source-variables" id="source-variables-link" data-toggle="tab" ng-click="toggleTab($event, 'source-variable-link', 'logview-tabs')" translate>Source Vars</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="status">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { templateUrl } from '../shared/template-url/template-url.factory';
|
||||
import { PortalModeJobTemplatesController } from './portal-mode-job-templates.controller';
|
||||
import { PortalModeJobsController } from './portal-mode-jobs.controller';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
// Using multiple named views requires a parent layout
|
||||
// https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
|
||||
@ -8,7 +9,7 @@ export default {
|
||||
name: 'portalMode',
|
||||
url: '/portal?{group_search:queryset}{host_search:queryset}',
|
||||
ncyBreadcrumb: {
|
||||
label: 'MY VIEW'
|
||||
label: N_('MY VIEW')
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
|
||||
@ -9,6 +9,7 @@ import addController from './schedulerAdd.controller';
|
||||
import editController from './schedulerEdit.controller';
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import schedulerDatePicker from './schedulerDatePicker.directive';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default
|
||||
angular.module('scheduler', [])
|
||||
@ -32,7 +33,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'templates.editJobTemplate({job_template_id: parentObject.id})',
|
||||
label: 'SCHEDULES'
|
||||
label: N_('SCHEDULES')
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
@ -63,7 +64,7 @@ export default
|
||||
'@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject){
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Schedules');
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
@ -86,7 +87,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'jobTemplateSchedules',
|
||||
label: 'CREATE SCHEDULE'
|
||||
label: N_('CREATE SCHEDULE')
|
||||
}
|
||||
});
|
||||
$stateExtender.addState({
|
||||
@ -118,7 +119,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'templates.editWorkflowJobTemplate({workflow_job_template_id: parentObject.id})',
|
||||
label: 'SCHEDULES'
|
||||
label: N_('SCHEDULES')
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
@ -149,7 +150,7 @@ export default
|
||||
'@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject){
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Schedules');
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
@ -172,7 +173,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'workflowJobTemplateSchedules',
|
||||
label: 'CREATE SCHEDULE'
|
||||
label: N_('CREATE SCHEDULE')
|
||||
}
|
||||
});
|
||||
$stateExtender.addState({
|
||||
@ -201,7 +202,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'projects.edit({project_id: parentObject.id})',
|
||||
label: 'SCHEDULES'
|
||||
label: N_('SCHEDULES')
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
@ -232,7 +233,7 @@ export default
|
||||
'@': {
|
||||
templateProvider: function(SchedulesList, generateList, ParentObject){
|
||||
// include name of parent resource in listTitle
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>Schedules`;
|
||||
SchedulesList.listTitle = `${ParentObject.name}<div class='List-titleLockup'></div>` + N_('Schedules');
|
||||
let html = generateList.build({
|
||||
list: SchedulesList,
|
||||
mode: 'edit'
|
||||
@ -249,7 +250,7 @@ export default
|
||||
name: 'projectSchedules.add',
|
||||
route: '/add',
|
||||
ncyBreadcrumb: {
|
||||
label: 'CREATE SCHEDULE'
|
||||
label: N_('CREATE SCHEDULE')
|
||||
},
|
||||
views: {
|
||||
'form': {
|
||||
@ -289,7 +290,7 @@ export default
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'jobs',
|
||||
label: 'SCHEDULED'
|
||||
label: N_('SCHEDULED')
|
||||
},
|
||||
resolve: {
|
||||
SchedulesList: ['ScheduledJobsList', function(list){
|
||||
|
||||
@ -4,6 +4,9 @@ import icon from '../shared/icon/main';
|
||||
export default
|
||||
angular.module('setupMenu',
|
||||
[ icon.name])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
.run(['$stateExtender', 'I18NInit',
|
||||
function($stateExtender, I18NInit) {
|
||||
I18NInit();
|
||||
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
|
||||
@ -50,8 +50,8 @@
|
||||
</p>
|
||||
</a>
|
||||
<a ui-sref="configuration" class="SetupItem" ng-if="user_is_superuser || user_is_system_auditor">
|
||||
<h4 class="SetupItem-title">Configure Tower</h4>
|
||||
<p class="SetupItem-description">
|
||||
<h4 class="SetupItem-title" translate>Configure Tower</h4>
|
||||
<p class="SetupItem-description" translate>
|
||||
Edit Tower's configuration.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'setup',
|
||||
route: '/setup',
|
||||
ncyBreadcrumb: {
|
||||
label: "SETTINGS"
|
||||
label: N_("SETTINGS")
|
||||
},
|
||||
templateUrl: templateUrl('setup-menu/setup-menu'),
|
||||
controller: function(orgAdmin, $scope){
|
||||
|
||||
@ -678,7 +678,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
|
||||
if(field.reset && !field.disabled) {
|
||||
var resetValue = "'" + field.reset+ "'";
|
||||
html+= `<a class="Form-resetValue" ng-click="resetValue(${resetValue})">Reset</a>`;
|
||||
html+= `<a class="Form-resetValue" ng-click="resetValue(${resetValue})">` + i18n._("Reset") + `</a>`;
|
||||
}
|
||||
|
||||
html += "\n\t</label>\n";
|
||||
@ -1870,9 +1870,9 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
<div
|
||||
class="row"
|
||||
ng-show="${itm}.length === 0 && !(searchTags | isEmpty)">
|
||||
<div class="col-lg-12 List-searchNoResults">
|
||||
No records matched your search.
|
||||
</div>
|
||||
<div class="col-lg-12 List-searchNoResults">`;
|
||||
html += i18n._('No records matched your search.');
|
||||
html += `</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
@ -30,15 +30,15 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="Paginate-pager--pageof">Page
|
||||
<span class="Paginate-pager--pageof" translate>Page
|
||||
<span id="current-page">{{current()}}</span> of
|
||||
<span id="total-pages">{{last()}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="Paginate-total page-label" ng-hide="dataCount === 0">
|
||||
<span>ITEMS
|
||||
<span translate>ITEMS
|
||||
<span>{{dataRange}}</span>
|
||||
<span>of {{dataCount()}}</span>
|
||||
<span translate>of {{dataCount()}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', 'QuerySet', 'SmartSearchService',
|
||||
function($stateParams, $scope, $state, QuerySet, GetBasePath, qs, SmartSearchService) {
|
||||
export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', 'QuerySet', 'SmartSearchService', 'i18n',
|
||||
function($stateParams, $scope, $state, QuerySet, GetBasePath, qs, SmartSearchService, i18n) {
|
||||
|
||||
let path, relations,
|
||||
defaults,
|
||||
@ -35,6 +35,7 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
||||
$scope.options = data.options.data;
|
||||
$scope.$emit(`${$scope.list.iterator}_options`, data.options);
|
||||
});
|
||||
$scope.searchPlaceholder = $scope.disableSearch ? i18n._('Cannot search running job') : i18n._('Search');
|
||||
|
||||
function compareParams(a, b) {
|
||||
for (let key in a) {
|
||||
@ -73,6 +74,15 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', stateChangeSuccessListener);
|
||||
|
||||
$scope.$watch('disableSearch', function(disableSearch){
|
||||
if(disableSearch) {
|
||||
$scope.searchPlaceholder = i18n._('Cannot search running job');
|
||||
}
|
||||
else {
|
||||
$scope.searchPlaceholder = i18n._('Search');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Removes state definition defaults and pagination terms
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
<div class="SmartSearch-searchTermContainer">
|
||||
<!-- string search input -->
|
||||
<form name="smartSearch" class="SmartSearch-form" aw-enter-key="add(searchTerm)" novalidate>
|
||||
<input class="SmartSearch-input" ng-model="searchTerm" placeholder="{{disableSearch ? 'Cannot search running job' : 'Search'}}"
|
||||
<input class="SmartSearch-input" ng-model="searchTerm" placeholder="{{searchPlaceholder}}"
|
||||
ng-disabled="disableSearch">
|
||||
</form>
|
||||
<div type="submit" class="SmartSearch-searchButton" ng-disabled="!searchTerm" ng-click="add(searchTerm)">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="SmartSearch-keyToggle btn" ng-class="{'is-active': showKeyPane}" ng-click="toggleKeyPane()">
|
||||
<div class="SmartSearch-keyToggle btn" ng-class="{'is-active': showKeyPane}" ng-click="toggleKeyPane()" translate>
|
||||
Key
|
||||
</div>
|
||||
</div>
|
||||
@ -28,7 +28,7 @@
|
||||
<span class="SmartSearch-name">{{tag}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href class="SmartSearch-clearAll" ng-click="clearAll()" ng-show="!(searchTags | isEmpty)">CLEAR ALL</a>
|
||||
<a href class="SmartSearch-clearAll" ng-click="clearAll()" ng-show="!(searchTags | isEmpty)" translate>CLEAR ALL</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -37,7 +37,7 @@
|
||||
<div class="SmartSearch-keyRow">
|
||||
<div class="SmartSearch-examples">
|
||||
<div class="SmartSearch-examples--title">
|
||||
<b>EXAMPLES:</b>
|
||||
<b translate>EXAMPLES:</b>
|
||||
</div>
|
||||
<div class="SmartSearch-examples--search">name:foo</div>
|
||||
<div class="SmartSearch-examples--search">organization.name:Default</div>
|
||||
@ -45,13 +45,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="SmartSearch-keyRow">
|
||||
<b>FIELDS:</b> <span ng-repeat="(key,value) in model.base">{{ key }}<span ng-if="!$last">, </span></span>
|
||||
<b translate>FIELDS:</b> <span ng-repeat="(key,value) in model.base">{{ key }}<span ng-if="!$last">, </span></span>
|
||||
</div>
|
||||
<div class="SmartSearch-keyRow">
|
||||
<b>RELATED FIELDS:</b> <span ng-repeat="relation in model.related">{{ relation }}<span ng-if="!$last">, </span></span>
|
||||
<b translate>RELATED FIELDS:</b> <span ng-repeat="relation in model.related">{{ relation }}<span ng-if="!$last">, </span></span>
|
||||
</div>
|
||||
<div class="SmartSearch-keyRow">
|
||||
<b>ADDITIONAL INFORMATION:</b> <span>For additional information on advanced search search syntax please see the Ansible Tower documentation.</span>
|
||||
<b translate>ADDITIONAL INFORMATION:</b> <span translate>For additional information on advanced search search syntax please see the Ansible Tower documentation.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
* generateLookupNodes - Attaches to a form node. Builds an abstract '*.lookup' node with field-specific 'lookup.*' children e.g. {name: 'projects.add.lookup.organizations', ...}
|
||||
*/
|
||||
|
||||
export default ['$injector', '$stateExtender', '$log', function($injector, $stateExtender, $log) {
|
||||
export default ['$injector', '$stateExtender', '$log', 'i18n', function($injector, $stateExtender, $log, i18n) {
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
@ -150,7 +150,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat
|
||||
url: url,
|
||||
ncyBreadcrumb: {
|
||||
[params.parent ? 'parent' : null]: `${params.parent}`,
|
||||
label: `CREATE ${form.breadcrumbName || form.name}`
|
||||
label: i18n.sprintf(i18n._("CREATE %s"), i18n._(`${form.breadcrumbName || form.name}`))
|
||||
},
|
||||
views: {
|
||||
'form': {
|
||||
@ -274,7 +274,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat
|
||||
},
|
||||
views: {
|
||||
[`modal@${formStateDefinition.name}`]: {
|
||||
template: `<add-rbac-user-team resolve="$resolve" title="Add Permissions"></add-rbac-user-team>`
|
||||
template: `<add-rbac-user-team resolve="$resolve" title="` + i18n._('Add Permissions') + `"></add-rbac-user-team>`
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
@ -339,7 +339,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat
|
||||
},
|
||||
views: {
|
||||
[`modal@${formStateDefinition.name}`]: {
|
||||
template: `<add-rbac-resource users-dataset="$resolve.usersDataset" teams-dataset="$resolve.teamsDataset" selected="allSelected" resource-data="$resolve.resourceData" title="Add Users / Teams"></add-rbac-resource>`
|
||||
template: `<add-rbac-resource users-dataset="$resolve.usersDataset" teams-dataset="$resolve.teamsDataset" selected="allSelected" resource-data="$resolve.resourceData" title="` + i18n._('Add Users') + ' / ' + i18n._('Teams') + `"></add-rbac-resource>`
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
@ -492,7 +492,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat
|
||||
// }
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
function buildRbacUserDirective() {
|
||||
let states = [];
|
||||
|
||||
@ -508,7 +508,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat
|
||||
},
|
||||
views: {
|
||||
[`modal@${formStateDefinition.name}`]: {
|
||||
template: `<add-rbac-resource users-dataset="$resolve.usersDataset" selected="allSelected" resource-data="$resolve.resourceData" without-team-permissions="true" title="Add Users"></add-rbac-resource>`
|
||||
template: `<add-rbac-resource users-dataset="$resolve.usersDataset" selected="allSelected" resource-data="$resolve.resourceData" without-team-permissions="true" title="` + i18n._('Add Users') + `"></add-rbac-resource>`
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.SmartStatus--failed:before {
|
||||
content: "\f06a";
|
||||
}
|
||||
|
||||
.SmartStatus--running{
|
||||
color: @default-icon;
|
||||
margin-top: 10px;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="StandardOut-leftPanel" ng-show="!stdoutFullScreen">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">
|
||||
<div class="StandardOut-panelHeaderText" translate>
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
@ -16,12 +16,12 @@
|
||||
<div class="StandardOut-details">
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.module_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Name</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Name</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">{{ job.module_name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STATUS</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STATUS</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<i class="fa icon-job-{{ job.status }}"></i>
|
||||
<span class="StandardOut-statusText StandardOut--capitalize">{{ job.status }}</span>
|
||||
@ -29,54 +29,54 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.started">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STARTED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STARTED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.started | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">FINISHED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>FINISHED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.finished | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">ELAPSED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>ELAPSED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.elapsed }} seconds
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.module_args">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Module Args</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Module Args</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">{{ job.module_args }}</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Inventory</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Inventory</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a href="{{ inventory_url }}"
|
||||
aw-tool-tip="The inventory this command ran on."
|
||||
aw-tool-tip="{{'The inventory this command ran on.'|translate}}"
|
||||
data-placement="top">{{ inventory_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="credential_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Credential</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Credential</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a href="{{ credential_url }}"
|
||||
aw-tool-tip="The credential used to run this command."
|
||||
aw-tool-tip="{{'The credential used to run this command.'|translate}}"
|
||||
data-placement="top">{{ credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="created_by">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Launched By</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Launched By</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a href="/#/users/{{ created_by.id }}"
|
||||
aw-tool-tip="The user who ran this command."
|
||||
aw-tool-tip="{{'The user who ran this command.'|translate}}"
|
||||
data-placement="top">{{ created_by.username }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,19 +84,19 @@
|
||||
<!-- since zero is a falsy value, you need ng-show such that
|
||||
the number is >= 0 -->
|
||||
<div class="StandardOut-detailsRow" ng-show="forks >= 0">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Forks</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Forks</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">{{ forks }}</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="limit">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Limit</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Limit</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">{{ limit }}</div>
|
||||
</div>
|
||||
|
||||
<!-- since zero is a falsy value, you need ng-show such that
|
||||
the number is >= 0 -->
|
||||
<div class="StandardOut-detailsRow" ng-show="verbosity >= 0">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">Verbosity</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>Verbosity</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">{{ verbosity }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -105,13 +105,13 @@
|
||||
<div class="StandardOut-rightPanel">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderText" translate>STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderActions">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{ toggleStdoutFullscreenTooltip }}" data-tip-watch="toggleStdoutFullscreenTooltip" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a href="/api/v1/ad_hoc_commands/{{ job.id }}/stdout?format=txt_download">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Download Output'|translate}}" data-placement="top">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="StandardOut-leftPanel" ng-show="!stdoutFullScreen">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">
|
||||
<div class="StandardOut-panelHeaderText" translate>
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
@ -16,13 +16,13 @@
|
||||
<div class="StandardOut-details">
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="inventory_source_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">NAME</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>NAME</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a href="{{inv_manage_group_link}}">
|
||||
{{ inventory_source_name }}
|
||||
</a>
|
||||
<a href="{{ workflow_result_link }}"
|
||||
aw-tool-tip="View workflow results"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STATUS</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STATUS</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<i class="fa icon-job-{{ job.status }}"></i>
|
||||
<span class="StandardOut-statusText StandardOut--capitalize">{{ job.status }}</span>
|
||||
@ -42,42 +42,42 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="{{job.license_error !== null}}">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">LICENSE ERROR</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>LICENSE ERROR</div>
|
||||
<div class="StandardOut-detailsContent StandardOut--capitalize">
|
||||
{{ job.license_error }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.started">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STARTED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STARTED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.started | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">FINISHED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>FINISHED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.finished | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">ELAPSED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>ELAPSED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.elapsed }} seconds
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.launch_type">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">LAUNCH TYPE</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>LAUNCH TYPE</div>
|
||||
<div class="StandardOut-detailsContent StandardOut--capitalize">
|
||||
{{ job.launch_type }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="credential_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">CREDENTIAL</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>CREDENTIAL</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a ui-sref="credentials.edit({credential_id: credential})">
|
||||
{{ credential_name }}
|
||||
@ -86,7 +86,7 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="inventory_source_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">GROUP</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>GROUP</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a href="{{inv_manage_group_link}}">
|
||||
{{ inventory_source_name }}
|
||||
@ -95,28 +95,28 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="source">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">SOURCE</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>SOURCE</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ source }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="source_regions">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">REGIONS</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>REGIONS</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ source_regions }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="{{ job.overwrite !== null }}">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">OVERWRITE</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>OVERWRITE</div>
|
||||
<div class="StandardOut-detailsContent StandardOut--capitalize">
|
||||
{{ job.overwrite }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="{{ job.overwrite_vars !== null }}">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">OVERWRITE VARS</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>OVERWRITE VARS</div>
|
||||
<div class="StandardOut-detailsContent StandardOut--capitalize">
|
||||
{{ job.overwrite_vars }}
|
||||
</div>
|
||||
@ -128,13 +128,13 @@
|
||||
<div class="StandardOut-rightPanel">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderText" translate>STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderActions">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{ toggleStdoutFullscreenTooltip }}" data-tip-watch="toggleStdoutFullscreenTooltip" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}"ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a href="/api/v1/inventory_updates/{{ job.id }}/stdout?format=txt_download">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Download Output'|translate}}" data-placement="top">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@ -4,25 +4,25 @@
|
||||
<div class="StandardOut-leftPanel" ng-show="!stdoutFullScreen">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">
|
||||
<div class="StandardOut-panelHeaderText" translate>
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
<button id="relaunch-job-button" class="List-actionButton JobDetail-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Relaunch using the same parameters" data-original-title="" title=""><i class="icon-launch"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status == 'running' || job.status=='pending' " aw-tool-tip="Cancel" data-original-title="" title=""><i class="fa fa-minus-circle"></i> </button>
|
||||
<button id="delete-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status == 'running' || job.status == 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
<button id="relaunch-job-button" class="List-actionButton JobDetail-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="{{'Relaunch using the same parameters'|translate}}" data-original-title="" title=""><i class="icon-launch"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status == 'running' || job.status=='pending' " aw-tool-tip="{{'Cancel'|translate}}" data-original-title="" title=""><i class="fa fa-minus-circle"></i> </button>
|
||||
<button id="delete-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status == 'running' || job.status == 'pending' " aw-tool-tip="{{'Delete'|translate}}" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="StandardOut-details">
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="project_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">NAME</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>NAME</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a ui-sref="projects.edit({id: job.project})">
|
||||
{{ project_name }}
|
||||
</a>
|
||||
<a href="{{ workflow_result_link }}"
|
||||
aw-tool-tip="View workflow results"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STATUS</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STATUS</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<i class="fa icon-job-{{ job.status }}"></i>
|
||||
<span class="StandardOut-statusText StandardOut--capitalize">{{ job.status }}</span>
|
||||
@ -42,35 +42,35 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.started">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">STARTED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>STARTED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.started | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">FINISHED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>FINISHED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.finished | longDate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.finished">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">ELAPSED</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>ELAPSED</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
{{ job.elapsed }} seconds
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="job.launch_type">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">LAUNCH TYPE</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>LAUNCH TYPE</div>
|
||||
<div class="StandardOut-detailsContent StandardOut--capitalize">
|
||||
{{ job.launch_type }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="project_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">PROJECT</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>PROJECT</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a ui-sref="projects.edit({id: job.project})">
|
||||
{{ project_name }}
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
|
||||
<div class="StandardOut-detailsRow" ng-show="credential_name">
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4">CREDENTIAL</div>
|
||||
<div class="StandardOut-detailsLabel col-lg-3 col-md-3 col-sm-3 col-xs-4" translate>CREDENTIAL</div>
|
||||
<div class="StandardOut-detailsContent col-lg-9 col-md-9 col-sm-9 col-xs-8">
|
||||
<a ui-sref="credentials.edit({credential_id: credential})">
|
||||
{{ credential_name }}
|
||||
@ -93,13 +93,13 @@
|
||||
<div class="StandardOut-rightPanel">
|
||||
<div class="Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderText" translate>STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderActions">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{ toggleStdoutFullscreenTooltip }}" data-tip-watch="toggleStdoutFullscreenTooltip" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}"ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a href="/api/v1/project_updates/{{ job.id }}/stdout?format=txt_download">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Download Output'|translate}}" data-placement="top">
|
||||
<i class="fa fa-download"></i>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'systemTracking',
|
||||
@ -14,7 +15,7 @@ export default {
|
||||
params: {hosts: null, inventory: null},
|
||||
reloadOnSearch: false,
|
||||
ncyBreadcrumb: {
|
||||
label: "SYSTEM TRACKING"
|
||||
label: N_("SYSTEM TRACKING")
|
||||
},
|
||||
resolve: {
|
||||
moduleOptions:
|
||||
|
||||
@ -9,13 +9,13 @@
|
||||
'$stateParams', 'JobTemplateForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'Wait',
|
||||
'Empty', 'ToJSON', 'CallbackHelpInit', 'Prompt', 'GetChoices', '$state',
|
||||
'CreateSelect2', '$q',
|
||||
'CreateSelect2', '$q', 'i18n',
|
||||
function(
|
||||
$filter, $scope, $rootScope, $compile,
|
||||
$location, $log, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
||||
ProcessErrors, ClearScope, GetBasePath, md5Setup, ParseTypeChange, Wait,
|
||||
Empty, ToJSON, CallbackHelpInit, Prompt, GetChoices,
|
||||
$state, CreateSelect2, $q
|
||||
$state, CreateSelect2, $q, i18n
|
||||
) {
|
||||
|
||||
Rest.setUrl(GetBasePath('job_templates'));
|
||||
@ -23,7 +23,7 @@
|
||||
.success(function(data) {
|
||||
if (!data.actions.POST) {
|
||||
$state.go("^");
|
||||
Alert('Permission Error', 'You do not have permission to add a job template.', 'alert-info');
|
||||
Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a job template.'), 'alert-info');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest',
|
||||
// attempts to transition the state and they were squashing each other.
|
||||
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
qs.search(path, $stateParams[`${list.iterator}_search`])
|
||||
qs.search(path, $state.params[`${list.iterator}_search`])
|
||||
.then(function(searchResponse) {
|
||||
$scope[`${list.iterator}_dataset`] = searchResponse.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
|
||||
@ -4,11 +4,13 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'templates',
|
||||
route: '/templates',
|
||||
ncyBreadcrumb: {
|
||||
label: "TEMPLATES"
|
||||
label: N_("TEMPLATES")
|
||||
},
|
||||
data: {
|
||||
activityStream: true,
|
||||
|
||||
@ -92,8 +92,8 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
}
|
||||
])
|
||||
|
||||
.factory('BuildDescription', ['BuildAnchor', '$log',
|
||||
function (BuildAnchor, $log) {
|
||||
.factory('BuildDescription', ['BuildAnchor', '$log', 'i18n',
|
||||
function (BuildAnchor, $log, i18n) {
|
||||
return function (activity) {
|
||||
|
||||
var pastTense = function(operation){
|
||||
@ -212,7 +212,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
}
|
||||
catch(err){
|
||||
$log.debug(err);
|
||||
activity.description = 'Event summary not available';
|
||||
activity.description = i18n._('Event summary not available');
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -258,10 +258,10 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
|
||||
.factory('Stream', ['$rootScope', '$location', '$state', 'Rest', 'GetBasePath',
|
||||
'ProcessErrors', 'Wait', 'StreamList', 'generateList', 'FormatDate', 'BuildDescription',
|
||||
'ShowDetail',
|
||||
'ShowDetail', 'i18n',
|
||||
function ($rootScope, $location, $state, Rest, GetBasePath, ProcessErrors,
|
||||
Wait, StreamList, GenerateList, FormatDate,
|
||||
BuildDescription, ShowDetail) {
|
||||
BuildDescription, ShowDetail, i18n) {
|
||||
return function (params) {
|
||||
|
||||
var list = _.cloneDeep(StreamList),
|
||||
@ -297,7 +297,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
|
||||
if ($state.params.target === 'credential') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Credential',
|
||||
label: i18n._('Credential'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'credential',
|
||||
@ -305,7 +305,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'host') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Host',
|
||||
label: i18n._('Host'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'host',
|
||||
@ -313,7 +313,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'inventory') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Inventory',
|
||||
label: i18n._('Inventory'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'inventory',
|
||||
@ -321,7 +321,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'inventory_script') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Inventory Script',
|
||||
label: i18n._('Inventory Script'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'custom_inventory_script',
|
||||
@ -329,7 +329,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'job_template') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Job Template',
|
||||
label: i18n._('Job Template'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'job_template',
|
||||
@ -337,7 +337,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'job') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Job',
|
||||
label: i18n._('Job'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'job',
|
||||
@ -345,7 +345,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'organization') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Organization',
|
||||
label: i18n._('Organization'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'organization',
|
||||
@ -353,7 +353,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'project') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Project',
|
||||
label: i18n._('Project'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'project',
|
||||
@ -361,7 +361,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'schedule') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Schedule',
|
||||
label: i18n._('Schedule'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'schedule',
|
||||
@ -369,7 +369,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'team') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'Team',
|
||||
label: i18n._('Team'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'team',
|
||||
@ -377,7 +377,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
|
||||
};
|
||||
} else if ($state.params.target === 'user') {
|
||||
list.fields.customSearchField = {
|
||||
label: 'User',
|
||||
label: i18n._('User'),
|
||||
searchType: 'text',
|
||||
searchOnly: 'true',
|
||||
sourceModel: 'user',
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
data-placement="top"
|
||||
mode="all"
|
||||
ng-click="relaunchJob()"
|
||||
aw-tool-tip="Relaunch using the same parameters"
|
||||
aw-tool-tip="{{'Relaunch using the same parameters'|translate}}"
|
||||
data-original-title=""
|
||||
title="">
|
||||
<i class="icon-launch"></i>
|
||||
@ -39,7 +39,7 @@
|
||||
ng-click="deleteJob()"
|
||||
ng-show="workflow_status.status == 'running' ||
|
||||
job_status.status=='pending' "
|
||||
aw-tool-tip="Cancel"
|
||||
aw-tool-tip="{{'Cancel'|translate}}"
|
||||
data-original-title="" title="">
|
||||
<i class="fa fa-minus-circle"></i>
|
||||
</button>
|
||||
@ -51,7 +51,7 @@
|
||||
ng-click="deleteJob()"
|
||||
ng-hide="job_status.status == 'running' ||
|
||||
job_status.status == 'pending' "
|
||||
aw-tool-tip="Delete"
|
||||
aw-tool-tip="{{'Delete'|translate}}"
|
||||
data-original-title=""
|
||||
title="">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
@ -106,7 +106,7 @@
|
||||
</label>
|
||||
<div class="WorkflowResults-resultRowText">
|
||||
<a href="{{ created_by_link }}"
|
||||
aw-tool-tip="Edit the User"
|
||||
aw-tool-tip="{{'Edit the User'|translate}}"
|
||||
data-placement="top">
|
||||
{{ workflow.summary_fields.created_by.username }}
|
||||
</a>
|
||||
@ -122,7 +122,7 @@
|
||||
WorkflowResults-resultRowLabel--fullWidth">
|
||||
Extra Variables
|
||||
<i class="WorkflowResults-extraVarsHelp fa fa-question-circle"
|
||||
aw-tool-tip="Read only view of extra variables added to the workflow."
|
||||
aw-tool-tip="{{'Read only view of extra variables added to the workflow.'|translate}}"
|
||||
data-placement="top">
|
||||
</i>
|
||||
</label>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ msgid ""
|
||||
" put the username and key in the URL. If using Bitbucket and SSH, do not "
|
||||
"supply your Bitbucket username."
|
||||
msgstr ""
|
||||
"%Remarque :%s Mercurial ne prend pas en charge l'authentification par mot de"
|
||||
"%sRemarque%s : Mercurial ne prend pas en charge l'authentification par mot de"
|
||||
" passe pour SSH. N'entrez ni le nom d'utilisateur, ni la clé dans l'URL. Si "
|
||||
"vous utilisez Bitbucket et SSH, ne saisissez pas votre nom d'utilisateur "
|
||||
"Bitbucket."
|
||||
@ -37,7 +37,7 @@ msgid ""
|
||||
"Bitbucket do not support password authentication when using SSH. GIT read "
|
||||
"only protocol (git://) does not use username or password information."
|
||||
msgstr ""
|
||||
"%Remarque :%s Si vous utilisez le protocole SSH pour GitHub ou Bitbucket, "
|
||||
"%sRemarque%s : Si vous utilisez le protocole SSH pour GitHub ou Bitbucket, "
|
||||
"entrez uniquement une clé SSH sans nom d'utilisateur (autre que git). De "
|
||||
"plus, GitHub et Bitbucket ne prennent pas en charge l'authentification par "
|
||||
"mot de passe lorsque SSH est utilisé. Le protocole GIT en lecture seule "
|
||||
|
||||
@ -239,7 +239,7 @@ Repositories
|
||||
The nightly repositories are hosted on the AnsibleWorks Jenkins server, and can
|
||||
be found at the following location:
|
||||
|
||||
http://jenkins.testing.ansible.com/ansible-tower_nightlies_RTYUIOPOIUYTYU/devel
|
||||
http://jenkins.testing.ansible.com/ansible-tower_nightlies_f8b8c5588b2505970227a7b0900ef69040ad5a00/devel
|
||||
|
||||
There are several sub-folders, including `deb/`, `rpm/`, `docs/` and `setup/`
|
||||
|
||||
@ -260,7 +260,7 @@ The `setup/` folder contains the Ansible Tower setup playbook tar.gz file.
|
||||
These nightly repositories can be used by the Ansible Tower setup playbook by
|
||||
running the `setup.sh` shell script with the following option:
|
||||
|
||||
./setup.sh -e "aw_repo_url=http://jenkins.testing.ansible.com/ansible-tower_nightlies_RTYUIOPOIUYTYU/devel gpgcheck=0"
|
||||
./setup.sh -e "aw_repo_url=http://jenkins.testing.ansible.com/ansible-tower_nightlies_f8b8c5588b2505970227a7b0900ef69040ad5a00/devel gpgcheck=0"
|
||||
|
||||
### Official Releases ###
|
||||
|
||||
|
||||
@ -9,11 +9,13 @@ services:
|
||||
RABBITMQ_USER: guest
|
||||
RABBITMQ_PASS: guest
|
||||
RABBITMQ_VHOST: /
|
||||
CELERY_RDB_HOST: 0.0.0.0
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "5555:5555"
|
||||
- "8013:8013"
|
||||
- "8043:8043"
|
||||
- "6899-6999:6899-6999" # default port range for celery.contrib.rdb
|
||||
links:
|
||||
- postgres
|
||||
- memcached
|
||||
|
||||
@ -8,7 +8,7 @@ services:
|
||||
image: gcr.io/ansible-tower-engineering/unit-test-runner:latest
|
||||
environment:
|
||||
SWIG_FEATURES: "-cpperraswarn -includeall -I/usr/include/openssl"
|
||||
TEST_DIRS: awx/main/tests/functional awx/main/tests/unit
|
||||
TEST_DIRS: awx/main/tests/functional awx/main/tests/unit awx/conf/tests
|
||||
command: ["make test"]
|
||||
volumes:
|
||||
- ../../../:/tower_devel
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user