mirror of
https://github.com/ansible/awx.git
synced 2026-05-02 15:15:30 -02:30
Merge branch 'master' into db-backup-unstable
This commit is contained in:
15
Makefile
15
Makefile
@@ -199,10 +199,12 @@ server_noattach:
|
|||||||
tmux rename-window 'Tower'
|
tmux rename-window 'Tower'
|
||||||
tmux select-window -t tower:0
|
tmux select-window -t tower:0
|
||||||
tmux split-window -v 'exec make celeryd'
|
tmux split-window -v 'exec make celeryd'
|
||||||
tmux split-window -h 'exec make socketservice'
|
|
||||||
tmux select-pane -U
|
|
||||||
tmux split-window -v 'exec make receiver'
|
|
||||||
tmux split-window -h 'exec make taskmanager'
|
tmux split-window -h 'exec make taskmanager'
|
||||||
|
tmux new-window 'exec make receiver'
|
||||||
|
tmux select-window -t tower:1
|
||||||
|
tmux rename-window 'Extra Services'
|
||||||
|
tmux split-window -v 'exec make socketservice'
|
||||||
|
tmux split-window -h 'exec make factcacher'
|
||||||
|
|
||||||
server: server_noattach
|
server: server_noattach
|
||||||
tmux -2 attach-session -t tower
|
tmux -2 attach-session -t tower
|
||||||
@@ -229,6 +231,9 @@ taskmanager:
|
|||||||
socketservice:
|
socketservice:
|
||||||
$(PYTHON) manage.py run_socketio_service
|
$(PYTHON) manage.py run_socketio_service
|
||||||
|
|
||||||
|
factcacher:
|
||||||
|
$(PYTHON) manage.py run_fact_cache_receiver
|
||||||
|
|
||||||
pep8:
|
pep8:
|
||||||
pep8 -r awx/
|
pep8 -r awx/
|
||||||
|
|
||||||
@@ -255,7 +260,7 @@ ui_analysis_report: reports/ui_code node_modules Gruntfile.js
|
|||||||
|
|
||||||
reports/ui_code: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
reports/ui_code: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
||||||
rm -rf reports/ui_code
|
rm -rf reports/ui_code
|
||||||
$(BROCCOLI) build reports/ui_code -- --no-concat --no-tests --no-styles
|
$(BROCCOLI) build reports/ui_code -- --no-concat --no-tests --no-styles --no-sourcemaps
|
||||||
|
|
||||||
# Run UI unit tests
|
# Run UI unit tests
|
||||||
test_ui: node_modules minjs_ci Gruntfile.js
|
test_ui: node_modules minjs_ci Gruntfile.js
|
||||||
@@ -294,7 +299,7 @@ devjs: node_modules clean-ui Brocfile.js bower.json Gruntfile.js
|
|||||||
|
|
||||||
# Build minified JS/CSS.
|
# Build minified JS/CSS.
|
||||||
minjs: node_modules clean-ui Brocfile.js
|
minjs: node_modules clean-ui Brocfile.js
|
||||||
$(BROCCOLI) build awx/ui/dist -- --silent --no-debug --no-tests --compress --no-docs
|
$(BROCCOLI) build awx/ui/dist -- --silent --no-debug --no-tests --compress --no-docs --no-sourcemaps
|
||||||
|
|
||||||
minjs_ci: node_modules clean-ui Brocfile.js
|
minjs_ci: node_modules clean-ui Brocfile.js
|
||||||
$(BROCCOLI) build awx/ui/dist -- --no-debug --compress --no-docs
|
$(BROCCOLI) build awx/ui/dist -- --no-debug --compress --no-docs
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ class SubListCreateAPIView(SubListAPIView, ListCreateAPIView):
|
|||||||
data[parent_key] = self.kwargs['pk']
|
data[parent_key] = self.kwargs['pk']
|
||||||
|
|
||||||
# attempt to deserialize the object
|
# attempt to deserialize the object
|
||||||
serializer = self.serializer_class(data=data)
|
serializer = self.get_serializer(data=data)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return Response(serializer.errors,
|
return Response(serializer.errors,
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -377,7 +377,7 @@ class SubListCreateAPIView(SubListAPIView, ListCreateAPIView):
|
|||||||
# save the object through the serializer, reload and returned the saved
|
# save the object through the serializer, reload and returned the saved
|
||||||
# object deserialized
|
# object deserialized
|
||||||
obj = serializer.save()
|
obj = serializer.save()
|
||||||
serializer = self.serializer_class(obj)
|
serializer = self.get_serializer(instance=obj)
|
||||||
|
|
||||||
headers = {'Location': obj.get_absolute_url()}
|
headers = {'Location': obj.get_absolute_url()}
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
from dateutil import rrule
|
from dateutil import rrule
|
||||||
|
from ast import literal_eval
|
||||||
|
|
||||||
# PyYAML
|
# PyYAML
|
||||||
import yaml
|
import yaml
|
||||||
@@ -1294,6 +1295,16 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
for field in Credential.PASSWORD_FIELDS:
|
for field in Credential.PASSWORD_FIELDS:
|
||||||
if unicode(attrs.get(field, '')).startswith('$encrypted$'):
|
if unicode(attrs.get(field, '')).startswith('$encrypted$'):
|
||||||
attrs.pop(field, None)
|
attrs.pop(field, None)
|
||||||
|
|
||||||
|
# If creating a credential from a view that automatically sets the
|
||||||
|
# parent_key (user or team), set the other value to None.
|
||||||
|
view = self.context.get('view', None)
|
||||||
|
parent_key = getattr(view, 'parent_key', None)
|
||||||
|
if parent_key == 'user':
|
||||||
|
attrs['team'] = None
|
||||||
|
if parent_key == 'team':
|
||||||
|
attrs['user'] = None
|
||||||
|
|
||||||
instance = super(CredentialSerializer, self).restore_object(attrs, instance)
|
instance = super(CredentialSerializer, self).restore_object(attrs, instance)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@@ -1493,16 +1504,40 @@ class JobCancelSerializer(JobSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class JobRelaunchSerializer(JobSerializer):
|
class JobRelaunchSerializer(JobSerializer):
|
||||||
|
passwords_needed_to_start = serializers.SerializerMethodField('get_passwords_needed_to_start')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ()
|
fields = ('passwords_needed_to_start',)
|
||||||
|
|
||||||
def to_native(self, obj):
|
def to_native(self, obj):
|
||||||
if obj:
|
res = super(JobRelaunchSerializer, self).to_native(obj)
|
||||||
return dict([(p, u'') for p in obj.passwords_needed_to_start])
|
view = self.context.get('view', None)
|
||||||
else:
|
if hasattr(view, '_raw_data_form_marker'):
|
||||||
return {}
|
password_keys = dict([(p, u'') for p in self.get_passwords_needed_to_start(obj)])
|
||||||
|
res.update(password_keys)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_passwords_needed_to_start(self, obj):
|
||||||
|
if obj:
|
||||||
|
return obj.passwords_needed_to_start
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def validate_passwords_needed_to_start(self, attrs, source):
|
||||||
|
obj = self.context.get('obj')
|
||||||
|
data = self.context.get('data')
|
||||||
|
|
||||||
|
# Check for passwords needed
|
||||||
|
needed = self.get_passwords_needed_to_start(obj)
|
||||||
|
provided = dict([(field, data.get(field, '')) for field in needed])
|
||||||
|
if not all(provided.values()):
|
||||||
|
raise serializers.ValidationError(needed)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
obj = self.context.get('obj')
|
||||||
|
if not obj.credential or obj.credential.active is False:
|
||||||
|
raise serializers.ValidationError(dict(credential=["Credential not found or deleted."]))
|
||||||
|
return attrs
|
||||||
|
|
||||||
class AdHocCommandSerializer(UnifiedJobSerializer):
|
class AdHocCommandSerializer(UnifiedJobSerializer):
|
||||||
|
|
||||||
@@ -1547,6 +1582,9 @@ class AdHocCommandSerializer(UnifiedJobSerializer):
|
|||||||
ret['inventory'] = None
|
ret['inventory'] = None
|
||||||
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
||||||
ret['credential'] = None
|
ret['credential'] = None
|
||||||
|
# For the UI, only module_name is returned for name, instead of the
|
||||||
|
# longer module name + module_args format.
|
||||||
|
ret['name'] = obj.module_name
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@@ -1690,6 +1728,93 @@ class AdHocCommandEventSerializer(BaseSerializer):
|
|||||||
res['host'] = reverse('api:host_detail', args=(obj.host.pk,))
|
res['host'] = reverse('api:host_detail', args=(obj.host.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
class JobLaunchSerializer(BaseSerializer):
|
||||||
|
passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start')
|
||||||
|
can_start_without_user_input = serializers.Field(source='can_start_without_user_input')
|
||||||
|
variables_needed_to_start = serializers.Field(source='variables_needed_to_start')
|
||||||
|
credential_needed_to_start = serializers.SerializerMethodField('get_credential_needed_to_start')
|
||||||
|
survey_enabled = serializers.SerializerMethodField('get_survey_enabled')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = JobTemplate
|
||||||
|
fields = ('can_start_without_user_input', 'passwords_needed_to_start', 'extra_vars',
|
||||||
|
'ask_variables_on_launch', 'survey_enabled', 'variables_needed_to_start',
|
||||||
|
'credential', 'credential_needed_to_start',)
|
||||||
|
read_only_fields = ('ask_variables_on_launch',)
|
||||||
|
write_only_fields = ('credential','extra_vars',)
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
res = super(JobLaunchSerializer, self).to_native(obj)
|
||||||
|
view = self.context.get('view', None)
|
||||||
|
if obj and hasattr(view, '_raw_data_form_marker'):
|
||||||
|
if obj.passwords_needed_to_start:
|
||||||
|
password_keys = dict([(p, u'') for p in obj.passwords_needed_to_start])
|
||||||
|
res.update(password_keys)
|
||||||
|
if self.get_credential_needed_to_start(obj) is True:
|
||||||
|
res.update(dict(credential=''))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_credential_needed_to_start(self, obj):
|
||||||
|
return not (obj and obj.credential and obj.credential.active)
|
||||||
|
|
||||||
|
def get_survey_enabled(self, obj):
|
||||||
|
if obj:
|
||||||
|
return obj.survey_enabled and 'spec' in obj.survey_spec
|
||||||
|
return False
|
||||||
|
|
||||||
|
def validate_credential(self, attrs, source):
|
||||||
|
obj = self.context.get('obj')
|
||||||
|
credential = attrs.get(source, None) or (obj and obj.credential)
|
||||||
|
if not credential or not credential.active:
|
||||||
|
raise serializers.ValidationError('Credential not provided')
|
||||||
|
attrs[source] = credential
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def validate_passwords_needed_to_start(self, attrs, source):
|
||||||
|
obj = self.context.get('obj')
|
||||||
|
passwords = self.context.get('passwords')
|
||||||
|
data = self.context.get('data')
|
||||||
|
|
||||||
|
# fill passwords dict with request data passwords
|
||||||
|
if obj.passwords_needed_to_start:
|
||||||
|
try:
|
||||||
|
for p in obj.passwords_needed_to_start:
|
||||||
|
passwords[p] = data.get(p)
|
||||||
|
except KeyError:
|
||||||
|
raise serializers.ValidationError(obj.passwords_needed_to_start)
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
obj = self.context.get('obj')
|
||||||
|
extra_vars = attrs.get('extra_vars', {})
|
||||||
|
try:
|
||||||
|
extra_vars = literal_eval(extra_vars)
|
||||||
|
extra_vars = json.dumps(extra_vars)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
extra_vars = json.loads(extra_vars)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
try:
|
||||||
|
extra_vars = yaml.safe_load(extra_vars)
|
||||||
|
except (yaml.YAMLError, TypeError, AttributeError):
|
||||||
|
raise serializers.ValidationError(dict(extra_vars=['Must be valid JSON or YAML']))
|
||||||
|
|
||||||
|
if not isinstance(extra_vars, dict):
|
||||||
|
extra_vars = {}
|
||||||
|
|
||||||
|
if self.get_survey_enabled(obj):
|
||||||
|
validation_errors = obj.survey_variable_validation(extra_vars)
|
||||||
|
if validation_errors:
|
||||||
|
raise serializers.ValidationError(dict(variables_needed_to_start=validation_errors))
|
||||||
|
|
||||||
|
if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active):
|
||||||
|
raise serializers.ValidationError(dict(errors=["Job Template Project is missing or undefined"]))
|
||||||
|
if obj.inventory is None or not obj.inventory.active:
|
||||||
|
raise serializers.ValidationError(dict(errors=["Job Template Inventory is missing or undefined"]))
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
class ScheduleSerializer(BaseSerializer):
|
class ScheduleSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
|||||||
@@ -14,16 +14,16 @@
|
|||||||
.ansi4 { text-decoration: underline; }
|
.ansi4 { text-decoration: underline; }
|
||||||
.ansi9 { text-decoration: line-through; }
|
.ansi9 { text-decoration: line-through; }
|
||||||
.ansi30 { color: #000316; }
|
.ansi30 { color: #000316; }
|
||||||
.ansi31 { color: #AA0000; }
|
.ansi31 { color: #ff5850; }
|
||||||
.ansi32 { color: #00AA00; }
|
.ansi32 { color: #60D66F; }
|
||||||
.ansi33 { color: #AA5500; }
|
.ansi33 { color: #AA5500; }
|
||||||
.ansi34 { color: #0000AA; }
|
.ansi34 { color: #0000AA; }
|
||||||
.ansi35 { color: #E850A8; }
|
.ansi35 { color: #E850A8; }
|
||||||
.ansi36 { color: #00AAAA; }
|
.ansi36 { color: #00AAAA; }
|
||||||
.ansi37 { color: #F5F1DE; }
|
.ansi37 { color: #F5F1DE; }
|
||||||
.ansi40 { background-color: #000000; }
|
.ansi40 { background-color: #000000; }
|
||||||
.ansi41 { background-color: #AA0000; }
|
.ansi41 { background-color: #ff5850; }
|
||||||
.ansi42 { background-color: #00AA00; }
|
.ansi42 { background-color: #60D66F; }
|
||||||
.ansi43 { background-color: #AA5500; }
|
.ansi43 { background-color: #AA5500; }
|
||||||
.ansi44 { background-color: #0000AA; }
|
.ansi44 { background-color: #0000AA; }
|
||||||
.ansi45 { background-color: #E850A8; }
|
.ansi45 { background-color: #E850A8; }
|
||||||
|
|||||||
@@ -812,7 +812,7 @@ class UserDetail(RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
def update_filter(self, request, *args, **kwargs):
|
def update_filter(self, request, *args, **kwargs):
|
||||||
''' make sure non-read-only fields that can only be edited by admins, are only edited by admins '''
|
''' make sure non-read-only fields that can only be edited by admins, are only edited by admins '''
|
||||||
obj = User.objects.get(pk=kwargs['pk'])
|
obj = self.get_object()
|
||||||
can_change = request.user.can_access(User, 'change', obj, request.DATA)
|
can_change = request.user.can_access(User, 'change', obj, request.DATA)
|
||||||
can_admin = request.user.can_access(User, 'admin', obj, request.DATA)
|
can_admin = request.user.can_access(User, 'admin', obj, request.DATA)
|
||||||
if can_change and not can_admin:
|
if can_change and not can_admin:
|
||||||
@@ -828,7 +828,7 @@ class UserDetail(RetrieveUpdateDestroyAPIView):
|
|||||||
raise PermissionDenied('Cannot change %s' % ', '.join(changed.keys()))
|
raise PermissionDenied('Cannot change %s' % ', '.join(changed.keys()))
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
obj = User.objects.get(pk=kwargs['pk'])
|
obj = self.get_object()
|
||||||
can_delete = request.user.can_access(User, 'delete', obj)
|
can_delete = request.user.can_access(User, 'delete', obj)
|
||||||
if not can_delete:
|
if not can_delete:
|
||||||
raise PermissionDenied('Cannot delete user')
|
raise PermissionDenied('Cannot delete user')
|
||||||
@@ -1434,44 +1434,34 @@ class JobTemplateDetail(RetrieveUpdateDestroyAPIView):
|
|||||||
return super(JobTemplateDetail, self).destroy(request, *args, **kwargs)
|
return super(JobTemplateDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class JobTemplateLaunch(GenericAPIView):
|
class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
|
||||||
|
|
||||||
model = JobTemplate
|
model = JobTemplate
|
||||||
# FIXME: Add serializer class to define fields in OPTIONS request!
|
serializer_class = JobLaunchSerializer
|
||||||
is_job_start = True
|
is_job_start = True
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
obj = self.get_object()
|
|
||||||
data = {}
|
|
||||||
data['can_start_without_user_input'] = obj.can_start_without_user_input()
|
|
||||||
data['passwords_needed_to_start'] = obj.passwords_needed_to_start
|
|
||||||
data['ask_variables_on_launch'] = obj.ask_variables_on_launch
|
|
||||||
data['variables_needed_to_start'] = obj.variables_needed_to_start
|
|
||||||
data['credential_needed_to_start'] = obj.credential is None
|
|
||||||
data['survey_enabled'] = obj.survey_enabled and 'spec' in obj.survey_spec
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if not request.user.can_access(self.model, 'start', obj):
|
if not request.user.can_access(self.model, 'start', obj):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
if obj.survey_enabled and 'spec' in obj.survey_spec:
|
|
||||||
if request.DATA == "":
|
if 'credential' not in request.DATA and 'credential_id' in request.DATA:
|
||||||
request_data = {}
|
request.DATA['credential'] = request.DATA['credential_id']
|
||||||
else:
|
|
||||||
request_data = request.DATA
|
passwords = {}
|
||||||
validation_errors = obj.survey_variable_validation(request_data.get('extra_vars', {}))
|
serializer = self.serializer_class(data=request.DATA, context={'obj': obj, 'data': request.DATA, 'passwords': passwords})
|
||||||
if validation_errors:
|
if not serializer.is_valid():
|
||||||
return Response(dict(variables_needed_to_start=validation_errors),
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
if obj.credential is None and ('credential' not in request.DATA and 'credential_id' not in request.DATA):
|
kv = {
|
||||||
return Response(dict(errors="Credential not provided"), status=status.HTTP_400_BAD_REQUEST)
|
'credential': serializer.object.credential.pk,
|
||||||
if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active):
|
}
|
||||||
return Response(dict(errors="Job Template Project is missing or undefined"), status=status.HTTP_400_BAD_REQUEST)
|
if 'extra_vars' in request.DATA:
|
||||||
if obj.inventory is None or not obj.inventory.active:
|
kv['extra_vars'] = request.DATA['extra_vars']
|
||||||
return Response(dict(errors="Job Template Inventory is missing or undefined"), status=status.HTTP_400_BAD_REQUEST)
|
kv.update(passwords)
|
||||||
new_job = obj.create_unified_job(**request.DATA)
|
|
||||||
result = new_job.signal_start(**request.DATA)
|
new_job = obj.create_unified_job(**kv)
|
||||||
|
result = new_job.signal_start(**kv)
|
||||||
if not result:
|
if not result:
|
||||||
data = dict(passwords_needed_to_start=new_job.passwords_needed_to_start)
|
data = dict(passwords_needed_to_start=new_job.passwords_needed_to_start)
|
||||||
new_job.delete()
|
new_job.delete()
|
||||||
@@ -1843,7 +1833,7 @@ class JobCancel(RetrieveAPIView):
|
|||||||
else:
|
else:
|
||||||
return self.http_method_not_allowed(request, *args, **kwargs)
|
return self.http_method_not_allowed(request, *args, **kwargs)
|
||||||
|
|
||||||
class JobRelaunch(GenericAPIView):
|
class JobRelaunch(RetrieveAPIView, GenericAPIView):
|
||||||
|
|
||||||
model = Job
|
model = Job
|
||||||
serializer_class = JobRelaunchSerializer
|
serializer_class = JobRelaunchSerializer
|
||||||
@@ -1854,23 +1844,16 @@ class JobRelaunch(GenericAPIView):
|
|||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(JobRelaunch, self).dispatch(*args, **kwargs)
|
return super(JobRelaunch, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
obj = self.get_object()
|
|
||||||
data = {}
|
|
||||||
data['passwords_needed_to_start'] = obj.passwords_needed_to_start
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if not request.user.can_access(self.model, 'start', obj):
|
if not request.user.can_access(self.model, 'start', obj):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
# Check for passwords needed before copying job.
|
# Note: is_valid() may modify request.DATA
|
||||||
needed = obj.passwords_needed_to_start
|
# It will remove any key/value pair who's key is not in the 'passwords_needed_to_start' list
|
||||||
provided = dict([(field, request.DATA.get(field, '')) for field in needed])
|
serializer = self.serializer_class(data=request.DATA, context={'obj': obj, 'data': request.DATA})
|
||||||
if not all(provided.values()):
|
if not serializer.is_valid():
|
||||||
data = dict(passwords_needed_to_start=needed)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
new_job = obj.copy()
|
new_job = obj.copy()
|
||||||
result = new_job.signal_start(**request.DATA)
|
result = new_job.signal_start(**request.DATA)
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ class SocketIOHandler(WSGIHandler):
|
|||||||
del self.websocket.environ
|
del self.websocket.environ
|
||||||
del self.websocket
|
del self.websocket
|
||||||
if self.environ:
|
if self.environ:
|
||||||
|
self.environ.pop('wsgi.websocket', None)
|
||||||
del self.environ
|
del self.environ
|
||||||
|
|
||||||
def handle_bad_request(self):
|
def handle_bad_request(self):
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
# Python
|
# Python
|
||||||
import re
|
import re
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from datetime import datetime
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.fact.models.fact import * # noqa
|
from awx.fact.models.fact import * # noqa
|
||||||
@@ -30,20 +30,32 @@ class CleanupFacts(object):
|
|||||||
# pivot -= granularity
|
# pivot -= granularity
|
||||||
# group by host
|
# group by host
|
||||||
def cleanup(self, older_than_abs, granularity):
|
def cleanup(self, older_than_abs, granularity):
|
||||||
|
flag_delete_all = False
|
||||||
fact_oldest = FactVersion.objects.all().order_by('timestamp').first()
|
fact_oldest = FactVersion.objects.all().order_by('timestamp').first()
|
||||||
if not fact_oldest:
|
if not fact_oldest:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# Special case, granularity=0x where x is d, w, or y
|
||||||
|
# The intent is to delete all facts < older_than_abs
|
||||||
|
if granularity == relativedelta():
|
||||||
|
flag_delete_all = True
|
||||||
|
|
||||||
total = 0
|
total = 0
|
||||||
date_pivot = older_than_abs
|
date_pivot = older_than_abs
|
||||||
while date_pivot > fact_oldest.timestamp:
|
while date_pivot > fact_oldest.timestamp:
|
||||||
date_pivot_next = date_pivot - granularity
|
date_pivot_next = date_pivot - granularity
|
||||||
kv = {
|
kv = {
|
||||||
'timestamp__lte': date_pivot,
|
'timestamp__lte': date_pivot
|
||||||
'timestamp__gt': date_pivot_next,
|
|
||||||
}
|
}
|
||||||
|
if not flag_delete_all:
|
||||||
|
kv['timestamp__gt'] = date_pivot_next
|
||||||
|
|
||||||
version_objs = FactVersion.objects.filter(**kv).order_by('-timestamp')
|
version_objs = FactVersion.objects.filter(**kv).order_by('-timestamp')
|
||||||
|
|
||||||
|
if flag_delete_all:
|
||||||
|
total = version_objs.delete()
|
||||||
|
break
|
||||||
|
|
||||||
# Transform array -> {host_id} = [<fact_version>, <fact_version>, ...]
|
# Transform array -> {host_id} = [<fact_version>, <fact_version>, ...]
|
||||||
# TODO: If this set gets large then we can use mongo to transform the data set for us.
|
# TODO: If this set gets large then we can use mongo to transform the data set for us.
|
||||||
host_ids = {}
|
host_ids = {}
|
||||||
@@ -66,13 +78,14 @@ class CleanupFacts(object):
|
|||||||
total += count
|
total += count
|
||||||
|
|
||||||
date_pivot = date_pivot_next
|
date_pivot = date_pivot_next
|
||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
'''
|
'''
|
||||||
older_than and granularity are of type relativedelta
|
older_than and granularity are of type relativedelta
|
||||||
'''
|
'''
|
||||||
def run(self, older_than, granularity):
|
def run(self, older_than, granularity):
|
||||||
t = datetime.now()
|
t = now()
|
||||||
deleted_count = self.cleanup(t - older_than, granularity)
|
deleted_count = self.cleanup(t - older_than, granularity)
|
||||||
print("Deleted %d facts." % deleted_count)
|
print("Deleted %d facts." % deleted_count)
|
||||||
|
|
||||||
|
|||||||
@@ -357,8 +357,9 @@ class ExecutableJsonLoader(BaseLoader):
|
|||||||
stdout, stderr = proc.communicate()
|
stdout, stderr = proc.communicate()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise RuntimeError('%r failed (rc=%d) with output: %s' % (cmd, proc.returncode, stderr))
|
raise RuntimeError('%r failed (rc=%d) with output: %s' % (cmd, proc.returncode, stderr))
|
||||||
data = json.loads(stdout)
|
try:
|
||||||
if not isinstance(data, dict):
|
data = json.loads(stdout)
|
||||||
|
except ValueError:
|
||||||
raise TypeError('Returned JSON must be a dictionary, got %s instead' % str(type(data)))
|
raise TypeError('Returned JSON must be a dictionary, got %s instead' % str(type(data)))
|
||||||
except:
|
except:
|
||||||
logger.error('Failed to load JSON from: %s', stdout)
|
logger.error('Failed to load JSON from: %s', stdout)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ valid_sockets = []
|
|||||||
class TowerBaseNamespace(BaseNamespace):
|
class TowerBaseNamespace(BaseNamespace):
|
||||||
|
|
||||||
def get_allowed_methods(self):
|
def get_allowed_methods(self):
|
||||||
return []
|
return ['recv_disconnect']
|
||||||
|
|
||||||
def get_initial_acl(self):
|
def get_initial_acl(self):
|
||||||
global valid_sockets
|
global valid_sockets
|
||||||
@@ -99,7 +99,8 @@ class AdHocCommandEventNamespace(TowerBaseNamespace):
|
|||||||
class ScheduleNamespace(TowerBaseNamespace):
|
class ScheduleNamespace(TowerBaseNamespace):
|
||||||
|
|
||||||
def get_allowed_methods(self):
|
def get_allowed_methods(self):
|
||||||
return ["schedule_changed"]
|
parent_allowed = super(ScheduleNamespace, self).get_allowed_methods()
|
||||||
|
return parent_allowed + ["schedule_changed"]
|
||||||
|
|
||||||
def recv_connect(self):
|
def recv_connect(self):
|
||||||
logger.info("Received client connect for schedule namespace from %s" % str(self.environ['REMOTE_ADDR']))
|
logger.info("Received client connect for schedule namespace from %s" % str(self.environ['REMOTE_ADDR']))
|
||||||
@@ -131,7 +132,11 @@ def notification_handler(server):
|
|||||||
}
|
}
|
||||||
for session_id, socket in list(server.sockets.iteritems()):
|
for session_id, socket in list(server.sockets.iteritems()):
|
||||||
if session_id in valid_sockets:
|
if session_id in valid_sockets:
|
||||||
socket.send_packet(packet)
|
try:
|
||||||
|
socket.send_packet(packet)
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Error sending client packet to %s: %s" % (str(session_id), str(packet)))
|
||||||
|
logger.error("Error was: " + str(e))
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -379,6 +379,23 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
|||||||
# If update_fields has been specified, add our field names to it,
|
# If update_fields has been specified, add our field names to it,
|
||||||
# if hit hasn't been specified, then we're just doing a normal save.
|
# if hit hasn't been specified, then we're just doing a normal save.
|
||||||
update_fields = kwargs.get('update_fields', [])
|
update_fields = kwargs.get('update_fields', [])
|
||||||
|
# If updating a credential, make sure that we only allow user OR team
|
||||||
|
# to be set, and clear out the other field based on which one has
|
||||||
|
# changed.
|
||||||
|
if self.pk:
|
||||||
|
cred_before = Credential.objects.get(pk=self.pk)
|
||||||
|
if self.user and self.team:
|
||||||
|
# If the user changed, remove the previously assigned team.
|
||||||
|
if cred_before.user != self.user:
|
||||||
|
self.team = None
|
||||||
|
if 'team' not in update_fields:
|
||||||
|
update_fields.append('team')
|
||||||
|
# If the team changed, remove the previously assigned user.
|
||||||
|
elif cred_before.team != self.team:
|
||||||
|
self.user = None
|
||||||
|
if 'user' not in update_fields:
|
||||||
|
update_fields.append('user')
|
||||||
|
# Set cloud flag based on credential kind.
|
||||||
cloud = self.kind in CLOUD_PROVIDERS + ('aws',)
|
cloud = self.kind in CLOUD_PROVIDERS + ('aws',)
|
||||||
if self.cloud != cloud:
|
if self.cloud != cloud:
|
||||||
self.cloud = cloud
|
self.cloud = cloud
|
||||||
|
|||||||
@@ -945,6 +945,7 @@ class SystemJobOptions(BaseModel):
|
|||||||
('cleanup_jobs', _('Remove jobs older than a certain number of days')),
|
('cleanup_jobs', _('Remove jobs older than a certain number of days')),
|
||||||
('cleanup_activitystream', _('Remove activity stream entries older than a certain number of days')),
|
('cleanup_activitystream', _('Remove activity stream entries older than a certain number of days')),
|
||||||
('cleanup_deleted', _('Purge previously deleted items from the database')),
|
('cleanup_deleted', _('Purge previously deleted items from the database')),
|
||||||
|
('cleanup_facts', _('Purge and/or reduce the granularity of system tracking data')),
|
||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Socket(object):
|
|||||||
Intended to allow alteration of backend details in a single, consistent
|
Intended to allow alteration of backend details in a single, consistent
|
||||||
way throughout the Tower application.
|
way throughout the Tower application.
|
||||||
"""
|
"""
|
||||||
def __init__(self, bucket, rw, debug=0, logger=None):
|
def __init__(self, bucket, rw, debug=0, logger=None, nowait=False):
|
||||||
"""Instantiate a Socket object, which uses ZeroMQ to actually perform
|
"""Instantiate a Socket object, which uses ZeroMQ to actually perform
|
||||||
passing a message back and forth.
|
passing a message back and forth.
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ class Socket(object):
|
|||||||
|
|
||||||
self._debug = debug
|
self._debug = debug
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
|
self._nowait = nowait
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.connect()
|
self.connect()
|
||||||
@@ -92,7 +93,10 @@ class Socket(object):
|
|||||||
# Okay, create the connection.
|
# Okay, create the connection.
|
||||||
if self._context is None:
|
if self._context is None:
|
||||||
self._context = zmq.Context()
|
self._context = zmq.Context()
|
||||||
self._socket = self._context.socket(self._rw)
|
self._socket = self._context.socket(self._rw)
|
||||||
|
if self._nowait:
|
||||||
|
self._socket.setsockopt(zmq.RCVTIMEO, 2000)
|
||||||
|
self._socket.setsockopt(zmq.LINGER, 1000)
|
||||||
if self._rw == zmq.REQ:
|
if self._rw == zmq.REQ:
|
||||||
self._socket.connect(port)
|
self._socket.connect(port)
|
||||||
else:
|
else:
|
||||||
@@ -134,8 +138,8 @@ class Socket(object):
|
|||||||
break
|
break
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if self._logger:
|
if self._logger:
|
||||||
self._logger.info('Publish Exception: %r; retry=%d',
|
self._logger.error('Publish Exception: %r; retry=%d',
|
||||||
ex, retry, exc_info=True)
|
ex, retry, exc_info=True)
|
||||||
if retry >= 3:
|
if retry >= 3:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -230,10 +230,10 @@ class BaseTask(Task):
|
|||||||
'''
|
'''
|
||||||
return os.path.abspath(os.path.join(os.path.dirname(__file__), *args))
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), *args))
|
||||||
|
|
||||||
def build_private_data(self, instance, **kwargs):
|
def build_private_data(self, job, **kwargs):
|
||||||
'''
|
'''
|
||||||
Return any private data that needs to be written to a temporary file
|
Return SSH private key data (only if stored in DB as ssh_key_data).
|
||||||
for this task.
|
Return structure is a dict of the form:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def build_private_data_dir(self, instance, **kwargs):
|
def build_private_data_dir(self, instance, **kwargs):
|
||||||
@@ -244,20 +244,23 @@ class BaseTask(Task):
|
|||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def build_private_data_file(self, instance, **kwargs):
|
def build_private_data_files(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
Create a temporary file containing the private data.
|
Create a temporary files containing the private data.
|
||||||
|
Returns a dictionary with keys from build_private_data
|
||||||
|
(i.e. 'credential', 'cloud_credential') and values the file path.
|
||||||
'''
|
'''
|
||||||
private_data = self.build_private_data(instance, **kwargs)
|
private_data = self.build_private_data(instance, **kwargs)
|
||||||
|
private_data_files = {}
|
||||||
if private_data is not None:
|
if private_data is not None:
|
||||||
handle, path = tempfile.mkstemp(dir=kwargs.get('private_data_dir', None))
|
for name, data in private_data.iteritems():
|
||||||
f = os.fdopen(handle, 'w')
|
handle, path = tempfile.mkstemp(dir=kwargs.get('private_data_dir', None))
|
||||||
f.write(private_data)
|
f = os.fdopen(handle, 'w')
|
||||||
f.close()
|
f.write(data)
|
||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
f.close()
|
||||||
return path
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
||||||
else:
|
private_data_files[name] = path
|
||||||
return ''
|
return private_data_files
|
||||||
|
|
||||||
def build_passwords(self, instance, **kwargs):
|
def build_passwords(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -434,7 +437,8 @@ class BaseTask(Task):
|
|||||||
# Fetch ansible version once here to support version-dependent features.
|
# Fetch ansible version once here to support version-dependent features.
|
||||||
kwargs['ansible_version'] = get_ansible_version()
|
kwargs['ansible_version'] = get_ansible_version()
|
||||||
kwargs['private_data_dir'] = self.build_private_data_dir(instance, **kwargs)
|
kwargs['private_data_dir'] = self.build_private_data_dir(instance, **kwargs)
|
||||||
kwargs['private_data_file'] = self.build_private_data_file(instance, **kwargs)
|
# May have to serialize the value
|
||||||
|
kwargs['private_data_files'] = self.build_private_data_files(instance, **kwargs)
|
||||||
kwargs['passwords'] = self.build_passwords(instance, **kwargs)
|
kwargs['passwords'] = self.build_passwords(instance, **kwargs)
|
||||||
args = self.build_args(instance, **kwargs)
|
args = self.build_args(instance, **kwargs)
|
||||||
safe_args = self.build_safe_args(instance, **kwargs)
|
safe_args = self.build_safe_args(instance, **kwargs)
|
||||||
@@ -505,25 +509,22 @@ class RunJob(BaseTask):
|
|||||||
|
|
||||||
def build_private_data(self, job, **kwargs):
|
def build_private_data(self, job, **kwargs):
|
||||||
'''
|
'''
|
||||||
Return SSH private key data needed for this job (only if stored in DB
|
Returns a dict of the form
|
||||||
as ssh_key_data).
|
dict['credential'] = <credential_decrypted_ssh_key_data>
|
||||||
|
dict['cloud_credential'] = <cloud_credential_decrypted_ssh_key_data>
|
||||||
'''
|
'''
|
||||||
|
job_credentials = ['credential', 'cloud_credential']
|
||||||
|
private_data = {}
|
||||||
# If we were sent SSH credentials, decrypt them and send them
|
# If we were sent SSH credentials, decrypt them and send them
|
||||||
# back (they will be written to a temporary file).
|
# back (they will be written to a temporary file).
|
||||||
credential = getattr(job, 'credential', None)
|
|
||||||
if credential:
|
|
||||||
return decrypt_field(credential, 'ssh_key_data') or None
|
|
||||||
|
|
||||||
# We might also have been sent a cloud credential. If so, send it.
|
for cred_name in job_credentials:
|
||||||
#
|
credential = getattr(job, cred_name, None)
|
||||||
# This sets up an either/or situation with credential and cloud
|
if credential:
|
||||||
# credential when it comes to SSH data. This should be fine, as if
|
if credential.ssh_key_data not in (None, ''):
|
||||||
# you're running against cloud instances, you'll be using the cloud
|
private_data[cred_name] = decrypt_field(credential, 'ssh_key_data') or ''
|
||||||
# credentials to do so. I assert that no situation currently exists
|
|
||||||
# where we need both.
|
return private_data
|
||||||
cloud_credential = getattr(job, 'cloud_credential', None)
|
|
||||||
if cloud_credential:
|
|
||||||
return decrypt_field(cloud_credential, 'ssh_key_data') or None
|
|
||||||
|
|
||||||
def build_passwords(self, job, **kwargs):
|
def build_passwords(self, job, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -583,10 +584,10 @@ class RunJob(BaseTask):
|
|||||||
elif cloud_cred and cloud_cred.kind == 'gce':
|
elif cloud_cred and cloud_cred.kind == 'gce':
|
||||||
env['GCE_EMAIL'] = cloud_cred.username
|
env['GCE_EMAIL'] = cloud_cred.username
|
||||||
env['GCE_PROJECT'] = cloud_cred.project
|
env['GCE_PROJECT'] = cloud_cred.project
|
||||||
env['GCE_PEM_FILE_PATH'] = kwargs['private_data_file']
|
env['GCE_PEM_FILE_PATH'] = kwargs.get('private_data_files', {}).get('cloud_credential', '')
|
||||||
elif cloud_cred and cloud_cred.kind == 'azure':
|
elif cloud_cred and cloud_cred.kind == 'azure':
|
||||||
env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.username
|
env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.username
|
||||||
env['AZURE_CERT_PATH'] = kwargs['private_data_file']
|
env['AZURE_CERT_PATH'] = kwargs.get('private_data_files', {}).get('cloud_credential', '')
|
||||||
elif cloud_cred and cloud_cred.kind == 'vmware':
|
elif cloud_cred and cloud_cred.kind == 'vmware':
|
||||||
env['VMWARE_USER'] = cloud_cred.username
|
env['VMWARE_USER'] = cloud_cred.username
|
||||||
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
|
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
|
||||||
@@ -729,7 +730,7 @@ class RunJob(BaseTask):
|
|||||||
'''
|
'''
|
||||||
If using an SSH key, return the path for use by ssh-agent.
|
If using an SSH key, return the path for use by ssh-agent.
|
||||||
'''
|
'''
|
||||||
return kwargs.get('private_data_file', '')
|
return kwargs.get('private_data_files', {}).get('credential', '')
|
||||||
|
|
||||||
def should_use_proot(self, instance, **kwargs):
|
def should_use_proot(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -760,12 +761,17 @@ class RunProjectUpdate(BaseTask):
|
|||||||
name = 'awx.main.tasks.run_project_update'
|
name = 'awx.main.tasks.run_project_update'
|
||||||
model = ProjectUpdate
|
model = ProjectUpdate
|
||||||
|
|
||||||
|
|
||||||
def build_private_data(self, project_update, **kwargs):
|
def build_private_data(self, project_update, **kwargs):
|
||||||
'''
|
'''
|
||||||
Return SSH private key data needed for this project update.
|
Return SSH private key data needed for this project update.
|
||||||
'''
|
'''
|
||||||
|
private_data = {}
|
||||||
if project_update.credential:
|
if project_update.credential:
|
||||||
return decrypt_field(project_update.credential, 'ssh_key_data') or None
|
credential = project_update.credential
|
||||||
|
if credential.ssh_key_data not in (None, ''):
|
||||||
|
private_data['scm_credential'] = decrypt_field(project_update.credential, 'ssh_key_data')
|
||||||
|
return private_data
|
||||||
|
|
||||||
def build_passwords(self, project_update, **kwargs):
|
def build_passwords(self, project_update, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -916,7 +922,7 @@ class RunProjectUpdate(BaseTask):
|
|||||||
'''
|
'''
|
||||||
If using an SSH key, return the path for use by ssh-agent.
|
If using an SSH key, return the path for use by ssh-agent.
|
||||||
'''
|
'''
|
||||||
return kwargs.get('private_data_file', '')
|
return kwargs.get('private_data_files', {}).get('scm_credential', '')
|
||||||
|
|
||||||
class RunInventoryUpdate(BaseTask):
|
class RunInventoryUpdate(BaseTask):
|
||||||
|
|
||||||
@@ -930,7 +936,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
# If this is Microsoft Azure or GCE, return the RSA key
|
# If this is Microsoft Azure or GCE, return the RSA key
|
||||||
if inventory_update.source in ('azure', 'gce'):
|
if inventory_update.source in ('azure', 'gce'):
|
||||||
credential = inventory_update.credential
|
credential = inventory_update.credential
|
||||||
return decrypt_field(credential, 'ssh_key_data')
|
return dict(cloud_credential=decrypt_field(credential, 'ssh_key_data'))
|
||||||
|
|
||||||
if inventory_update.source == 'openstack':
|
if inventory_update.source == 'openstack':
|
||||||
credential = inventory_update.credential
|
credential = inventory_update.credential
|
||||||
@@ -938,8 +944,9 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
username=credential.username,
|
username=credential.username,
|
||||||
password=decrypt_field(credential, "password"),
|
password=decrypt_field(credential, "password"),
|
||||||
project_name=credential.project)
|
project_name=credential.project)
|
||||||
openstack_data = {"clouds": {"devstack": {"auth": openstack_auth}}}
|
private_state = str(inventory_update.source_vars_dict.get("private", "true"))
|
||||||
return yaml.safe_dump(openstack_data, default_flow_style=False, allow_unicode=True)
|
openstack_data = {"clouds": {"devstack": {"private": private_state, "auth": openstack_auth}}}
|
||||||
|
return dict(cloud_credential=yaml.safe_dump(openstack_data, default_flow_style=False, allow_unicode=True))
|
||||||
|
|
||||||
cp = ConfigParser.ConfigParser()
|
cp = ConfigParser.ConfigParser()
|
||||||
# Build custom ec2.ini for ec2 inventory script to use.
|
# Build custom ec2.ini for ec2 inventory script to use.
|
||||||
@@ -993,7 +1000,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
if cp.sections():
|
if cp.sections():
|
||||||
f = cStringIO.StringIO()
|
f = cStringIO.StringIO()
|
||||||
cp.write(f)
|
cp.write(f)
|
||||||
return f.getvalue()
|
return dict(cloud_credential=f.getvalue())
|
||||||
|
|
||||||
def build_passwords(self, inventory_update, **kwargs):
|
def build_passwords(self, inventory_update, **kwargs):
|
||||||
"""Build a dictionary of authentication/credential information for
|
"""Build a dictionary of authentication/credential information for
|
||||||
@@ -1038,31 +1045,32 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
# `awx/plugins/inventory` directory; those files should be kept in
|
# `awx/plugins/inventory` directory; those files should be kept in
|
||||||
# sync with those in Ansible core at all times.
|
# sync with those in Ansible core at all times.
|
||||||
passwords = kwargs.get('passwords', {})
|
passwords = kwargs.get('passwords', {})
|
||||||
|
cloud_credential = kwargs.get('private_data_files', {}).get('cloud_credential', '')
|
||||||
if inventory_update.source == 'ec2':
|
if inventory_update.source == 'ec2':
|
||||||
if passwords.get('source_username', '') and passwords.get('source_password', ''):
|
if passwords.get('source_username', '') and passwords.get('source_password', ''):
|
||||||
env['AWS_ACCESS_KEY_ID'] = passwords['source_username']
|
env['AWS_ACCESS_KEY_ID'] = passwords['source_username']
|
||||||
env['AWS_SECRET_ACCESS_KEY'] = passwords['source_password']
|
env['AWS_SECRET_ACCESS_KEY'] = passwords['source_password']
|
||||||
env['EC2_INI_PATH'] = kwargs.get('private_data_file', '')
|
env['EC2_INI_PATH'] = cloud_credential
|
||||||
elif inventory_update.source == 'rax':
|
elif inventory_update.source == 'rax':
|
||||||
env['RAX_CREDS_FILE'] = kwargs.get('private_data_file', '')
|
env['RAX_CREDS_FILE'] = cloud_credential
|
||||||
env['RAX_REGION'] = inventory_update.source_regions or 'all'
|
env['RAX_REGION'] = inventory_update.source_regions or 'all'
|
||||||
# Set this environment variable so the vendored package won't
|
# Set this environment variable so the vendored package won't
|
||||||
# complain about not being able to determine its version number.
|
# complain about not being able to determine its version number.
|
||||||
env['PBR_VERSION'] = '0.5.21'
|
env['PBR_VERSION'] = '0.5.21'
|
||||||
elif inventory_update.source == 'vmware':
|
elif inventory_update.source == 'vmware':
|
||||||
env['VMWARE_INI'] = kwargs.get('private_data_file', '')
|
env['VMWARE_INI'] = cloud_credential
|
||||||
env['VMWARE_HOST'] = passwords.get('source_host', '')
|
env['VMWARE_HOST'] = passwords.get('source_host', '')
|
||||||
env['VMWARE_USER'] = passwords.get('source_username', '')
|
env['VMWARE_USER'] = passwords.get('source_username', '')
|
||||||
env['VMWARE_PASSWORD'] = passwords.get('source_password', '')
|
env['VMWARE_PASSWORD'] = passwords.get('source_password', '')
|
||||||
elif inventory_update.source == 'azure':
|
elif inventory_update.source == 'azure':
|
||||||
env['AZURE_SUBSCRIPTION_ID'] = passwords.get('source_username', '')
|
env['AZURE_SUBSCRIPTION_ID'] = passwords.get('source_username', '')
|
||||||
env['AZURE_CERT_PATH'] = kwargs['private_data_file']
|
env['AZURE_CERT_PATH'] = cloud_credential
|
||||||
elif inventory_update.source == 'gce':
|
elif inventory_update.source == 'gce':
|
||||||
env['GCE_EMAIL'] = passwords.get('source_username', '')
|
env['GCE_EMAIL'] = passwords.get('source_username', '')
|
||||||
env['GCE_PROJECT'] = passwords.get('source_project', '')
|
env['GCE_PROJECT'] = passwords.get('source_project', '')
|
||||||
env['GCE_PEM_FILE_PATH'] = kwargs['private_data_file']
|
env['GCE_PEM_FILE_PATH'] = cloud_credential
|
||||||
elif inventory_update.source == 'openstack':
|
elif inventory_update.source == 'openstack':
|
||||||
env['OPENSTACK_CONFIG_FILE'] = kwargs.get('private_data_file', '')
|
env['OPENSTACK_CONFIG_FILE'] = cloud_credential
|
||||||
elif inventory_update.source == 'file':
|
elif inventory_update.source == 'file':
|
||||||
# FIXME: Parse source_env to dict, update env.
|
# FIXME: Parse source_env to dict, update env.
|
||||||
pass
|
pass
|
||||||
@@ -1163,7 +1171,6 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
def get_idle_timeout(self):
|
def get_idle_timeout(self):
|
||||||
return getattr(settings, 'INVENTORY_UPDATE_IDLE_TIMEOUT', None)
|
return getattr(settings, 'INVENTORY_UPDATE_IDLE_TIMEOUT', None)
|
||||||
|
|
||||||
|
|
||||||
class RunAdHocCommand(BaseTask):
|
class RunAdHocCommand(BaseTask):
|
||||||
'''
|
'''
|
||||||
Celery task to run an ad hoc command using ansible.
|
Celery task to run an ad hoc command using ansible.
|
||||||
@@ -1180,8 +1187,10 @@ class RunAdHocCommand(BaseTask):
|
|||||||
# If we were sent SSH credentials, decrypt them and send them
|
# If we were sent SSH credentials, decrypt them and send them
|
||||||
# back (they will be written to a temporary file).
|
# back (they will be written to a temporary file).
|
||||||
creds = ad_hoc_command.credential
|
creds = ad_hoc_command.credential
|
||||||
if creds:
|
private_data = {}
|
||||||
return decrypt_field(creds, 'ssh_key_data') or None
|
if creds and creds.ssh_key_data not in (None, ''):
|
||||||
|
private_data['ad_hoc_credential'] = decrypt_field(creds, 'ssh_key_data') or ''
|
||||||
|
return private_data
|
||||||
|
|
||||||
def build_passwords(self, ad_hoc_command, **kwargs):
|
def build_passwords(self, ad_hoc_command, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -1321,7 +1330,7 @@ class RunAdHocCommand(BaseTask):
|
|||||||
'''
|
'''
|
||||||
If using an SSH key, return the path for use by ssh-agent.
|
If using an SSH key, return the path for use by ssh-agent.
|
||||||
'''
|
'''
|
||||||
return kwargs.get('private_data_file', '')
|
return kwargs.get('private_data_files', {}).get('ad_hoc_credential', '')
|
||||||
|
|
||||||
def should_use_proot(self, instance, **kwargs):
|
def should_use_proot(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -1345,10 +1354,15 @@ class RunSystemJob(BaseTask):
|
|||||||
args = ['awx-manage', system_job.job_type]
|
args = ['awx-manage', system_job.job_type]
|
||||||
try:
|
try:
|
||||||
json_vars = json.loads(system_job.extra_vars)
|
json_vars = json.loads(system_job.extra_vars)
|
||||||
if 'days' in json_vars:
|
if 'days' in json_vars and system_job.job_type != 'cleanup_facts':
|
||||||
args.extend(['--days', str(json_vars['days'])])
|
args.extend(['--days', str(json_vars.get('days', 60))])
|
||||||
if system_job.job_type == 'cleanup_jobs':
|
if system_job.job_type == 'cleanup_jobs':
|
||||||
args.extend(['--jobs', '--project-updates', '--inventory-updates', '--management-jobs'])
|
args.extend(['--jobs', '--project-updates', '--inventory-updates', '--management-jobs'])
|
||||||
|
if system_job.job_type == 'cleanup_facts':
|
||||||
|
if 'older_than' in json_vars:
|
||||||
|
args.extend(['--older_than', str(json_vars['older_than'])])
|
||||||
|
if 'granularity' in json_vars:
|
||||||
|
args.extend(['--granularity', str(json_vars['granularity'])])
|
||||||
# Keeping this around in case we want to break this out
|
# Keeping this around in case we want to break this out
|
||||||
# if 'jobs' in json_vars and json_vars['jobs']:
|
# if 'jobs' in json_vars and json_vars['jobs']:
|
||||||
# args.extend(['--jobs'])
|
# args.extend(['--jobs'])
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ class CleanupFactsCommandFunctionalTest(BaseCommandMixin, BaseTest, MongoDBRequi
|
|||||||
result, stdout, stderr = self.run_command('cleanup_facts', granularity='1w',older_than='5d')
|
result, stdout, stderr = self.run_command('cleanup_facts', granularity='1w',older_than='5d')
|
||||||
self.assertEqual(stdout, 'Deleted 0 facts.\n')
|
self.assertEqual(stdout, 'Deleted 0 facts.\n')
|
||||||
|
|
||||||
|
def test_invoke_all_deleted(self):
|
||||||
|
self.create_hosts_and_facts(datetime(year=2015, day=2, month=1, microsecond=0), 10, 20)
|
||||||
|
|
||||||
|
result, stdout, stderr = self.run_command('cleanup_facts', granularity='0d', older_than='0d')
|
||||||
|
self.assertEqual(stdout, 'Deleted 200 facts.\n')
|
||||||
|
|
||||||
def test_invoke_params_required(self):
|
def test_invoke_params_required(self):
|
||||||
result, stdout, stderr = self.run_command('cleanup_facts')
|
result, stdout, stderr = self.run_command('cleanup_facts')
|
||||||
self.assertIsInstance(result, CommandError)
|
self.assertIsInstance(result, CommandError)
|
||||||
|
|||||||
@@ -1898,3 +1898,19 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
|||||||
other_inv_src_opts = {'source': 'custom', 'source_script': script_data['id']}
|
other_inv_src_opts = {'source': 'custom', 'source_script': script_data['id']}
|
||||||
with self.current_user(self.super_django_user):
|
with self.current_user(self.super_django_user):
|
||||||
self.put(other_inv_src, other_inv_src_opts, expect=400)
|
self.put(other_inv_src, other_inv_src_opts, expect=400)
|
||||||
|
|
||||||
|
def test_update_from_openstack(self):
|
||||||
|
api_url = getattr(settings, 'TEST_OPENSTACK_HOST', '')
|
||||||
|
api_user = getattr(settings, 'TEST_OPENSTACK_USER', '')
|
||||||
|
api_password = getattr(settings, 'TEST_OPENSTACK_PASSWORD', '')
|
||||||
|
api_project = getattr(settings, 'TEST_OPENSTACK_PROJECT', '')
|
||||||
|
if not all([api_url, api_user, api_password, api_project]):
|
||||||
|
self.skipTest("No test openstack credentials defined")
|
||||||
|
self.create_test_license_file()
|
||||||
|
credential = Credential.objects.create(kind='openstack',
|
||||||
|
host=api_url,
|
||||||
|
username=api_user,
|
||||||
|
password=api_password,
|
||||||
|
project=api_project)
|
||||||
|
inventory_source = self.update_inventory_source(self.group, source='openstack', credential=credential)
|
||||||
|
self.check_inventory_source(inventory_source)
|
||||||
|
|||||||
@@ -482,19 +482,37 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
|||||||
# Invalid auth can't trigger the launch endpoint
|
# Invalid auth can't trigger the launch endpoint
|
||||||
self.check_invalid_auth(launch_url, {}, methods=('post',))
|
self.check_invalid_auth(launch_url, {}, methods=('post',))
|
||||||
|
|
||||||
|
# Implicit, attached credentials
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
response = self.post(launch_url, {}, expect=202)
|
response = self.post(launch_url, {}, expect=202)
|
||||||
j = Job.objects.get(pk=response['job'])
|
j = Job.objects.get(pk=response['job'])
|
||||||
self.assertTrue(j.status == 'new')
|
self.assertTrue(j.status == 'new')
|
||||||
|
|
||||||
|
# Explicit, override credentials
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
response = self.post(launch_url, {'credential': self.cred_doug.pk}, expect=202)
|
response = self.post(launch_url, {'credential': self.cred_doug.pk}, expect=202)
|
||||||
j = Job.objects.get(pk=response['job'])
|
j = Job.objects.get(pk=response['job'])
|
||||||
self.assertTrue(j.status == 'new')
|
self.assertTrue(j.status == 'new')
|
||||||
|
self.assertEqual(j.credential.pk, self.cred_doug.pk)
|
||||||
|
|
||||||
# Can't launch a job template without a credential defined
|
# Explicit, override credentials
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
response = self.post(launch_url, {'credential_id': self.cred_doug.pk}, expect=202)
|
||||||
|
j = Job.objects.get(pk=response['job'])
|
||||||
|
self.assertTrue(j.status == 'new')
|
||||||
|
self.assertEqual(j.credential.pk, self.cred_doug.pk)
|
||||||
|
|
||||||
|
# Can't launch a job template without a credential defined (or if we
|
||||||
|
# pass an invalid/inactive credential value).
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
response = self.post(no_launch_url, {}, expect=400)
|
response = self.post(no_launch_url, {}, expect=400)
|
||||||
|
response = self.post(no_launch_url, {'credential': 0}, expect=400)
|
||||||
|
response = self.post(no_launch_url, {'credential_id': 0}, expect=400)
|
||||||
|
response = self.post(no_launch_url, {'credential': 'one'}, expect=400)
|
||||||
|
response = self.post(no_launch_url, {'credential_id': 'one'}, expect=400)
|
||||||
|
self.cred_doug.mark_inactive()
|
||||||
|
response = self.post(no_launch_url, {'credential': self.cred_doug.pk}, expect=400)
|
||||||
|
response = self.post(no_launch_url, {'credential_id': self.cred_doug.pk}, expect=400)
|
||||||
|
|
||||||
# Job Templates without projects can not be launched
|
# Job Templates without projects can not be launched
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
@@ -503,9 +521,9 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
|||||||
jt = JobTemplate.objects.get(pk=response['id'])
|
jt = JobTemplate.objects.get(pk=response['id'])
|
||||||
jt.project = None
|
jt.project = None
|
||||||
jt.save()
|
jt.save()
|
||||||
launch_url = reverse('api:job_template_launch',
|
launch_url2 = reverse('api:job_template_launch',
|
||||||
args=(response['id'],))
|
args=(response['id'],))
|
||||||
self.post(launch_url, {}, expect=400)
|
self.post(launch_url2, {}, expect=400)
|
||||||
|
|
||||||
# Job Templates without inventory can not be launched
|
# Job Templates without inventory can not be launched
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
@@ -514,9 +532,15 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
|||||||
jt = JobTemplate.objects.get(pk=response['id'])
|
jt = JobTemplate.objects.get(pk=response['id'])
|
||||||
jt.inventory = None
|
jt.inventory = None
|
||||||
jt.save()
|
jt.save()
|
||||||
launch_url = reverse('api:job_template_launch',
|
launch_url3 = reverse('api:job_template_launch',
|
||||||
args=(response['id'],))
|
args=(response['id'],))
|
||||||
self.post(launch_url, {}, expect=400)
|
self.post(launch_url3, {}, expect=400)
|
||||||
|
|
||||||
|
# Job Templates with deleted credentials cannot be launched.
|
||||||
|
self.cred_sue.mark_inactive()
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
response = self.post(launch_url, {}, expect=400)
|
||||||
|
|
||||||
|
|
||||||
class JobTest(BaseJobTestMixin, django.test.TestCase):
|
class JobTest(BaseJobTestMixin, django.test.TestCase):
|
||||||
|
|
||||||
@@ -1128,6 +1152,27 @@ class JobTemplateSurveyTest(BaseJobTestMixin, django.test.TestCase):
|
|||||||
job_extra = json.loads(job.extra_vars)
|
job_extra = json.loads(job.extra_vars)
|
||||||
self.assertTrue("favorite_color" in job_extra)
|
self.assertTrue("favorite_color" in job_extra)
|
||||||
|
|
||||||
|
# launch job template with required survey without providing survey data
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
self.post(url, json.loads(TEST_SIMPLE_REQUIRED_SURVEY), expect=200)
|
||||||
|
response = self.get(launch_url)
|
||||||
|
self.assertTrue('favorite_color' in response['variables_needed_to_start'])
|
||||||
|
response = self.post(launch_url, dict(extra_vars=dict()), expect=400)
|
||||||
|
# Note: The below assertion relies on how survey_variable_validation() crafts
|
||||||
|
# the error message
|
||||||
|
self.assertIn("'favorite_color' value missing", response['variables_needed_to_start'])
|
||||||
|
|
||||||
|
# launch job template with required survey without providing survey data and without
|
||||||
|
# even providing extra_vars
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
self.post(url, json.loads(TEST_SIMPLE_REQUIRED_SURVEY), expect=200)
|
||||||
|
response = self.get(launch_url)
|
||||||
|
self.assertTrue('favorite_color' in response['variables_needed_to_start'])
|
||||||
|
response = self.post(launch_url, {}, expect=400)
|
||||||
|
# Note: The below assertion relies on how survey_variable_validation() crafts
|
||||||
|
# the error message
|
||||||
|
self.assertIn("'favorite_color' value missing", response['variables_needed_to_start'])
|
||||||
|
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
response = self.post(url, json.loads(TEST_SIMPLE_NONREQUIRED_SURVEY), expect=200)
|
response = self.post(url, json.loads(TEST_SIMPLE_NONREQUIRED_SURVEY), expect=200)
|
||||||
response = self.get(launch_url)
|
response = self.get(launch_url)
|
||||||
|
|||||||
@@ -486,8 +486,11 @@ class ProjectsTest(BaseTransactionTest):
|
|||||||
# can add credentials to a user (if user or org admin or super user)
|
# can add credentials to a user (if user or org admin or super user)
|
||||||
self.post(other_creds, data=new_credentials, expect=401)
|
self.post(other_creds, data=new_credentials, expect=401)
|
||||||
self.post(other_creds, data=new_credentials, expect=401, auth=self.get_invalid_credentials())
|
self.post(other_creds, data=new_credentials, expect=401, auth=self.get_invalid_credentials())
|
||||||
|
new_credentials['team'] = team.pk
|
||||||
result = self.post(other_creds, data=new_credentials, expect=201, auth=self.get_super_credentials())
|
result = self.post(other_creds, data=new_credentials, expect=201, auth=self.get_super_credentials())
|
||||||
cred_user = result['id']
|
cred_user = result['id']
|
||||||
|
self.assertEqual(result['team'], None)
|
||||||
|
del new_credentials['team']
|
||||||
new_credentials['name'] = 'credential2'
|
new_credentials['name'] = 'credential2'
|
||||||
self.post(other_creds, data=new_credentials, expect=201, auth=self.get_normal_credentials())
|
self.post(other_creds, data=new_credentials, expect=201, auth=self.get_normal_credentials())
|
||||||
new_credentials['name'] = 'credential3'
|
new_credentials['name'] = 'credential3'
|
||||||
@@ -497,9 +500,12 @@ class ProjectsTest(BaseTransactionTest):
|
|||||||
|
|
||||||
# can add credentials to a team
|
# can add credentials to a team
|
||||||
new_credentials['name'] = 'credential'
|
new_credentials['name'] = 'credential'
|
||||||
|
new_credentials['user'] = other.pk
|
||||||
self.post(team_creds, data=new_credentials, expect=401)
|
self.post(team_creds, data=new_credentials, expect=401)
|
||||||
self.post(team_creds, data=new_credentials, expect=401, auth=self.get_invalid_credentials())
|
self.post(team_creds, data=new_credentials, expect=401, auth=self.get_invalid_credentials())
|
||||||
self.post(team_creds, data=new_credentials, expect=201, auth=self.get_super_credentials())
|
result = self.post(team_creds, data=new_credentials, expect=201, auth=self.get_super_credentials())
|
||||||
|
self.assertEqual(result['user'], None)
|
||||||
|
del new_credentials['user']
|
||||||
new_credentials['name'] = 'credential2'
|
new_credentials['name'] = 'credential2'
|
||||||
result = self.post(team_creds, data=new_credentials, expect=201, auth=self.get_normal_credentials())
|
result = self.post(team_creds, data=new_credentials, expect=201, auth=self.get_normal_credentials())
|
||||||
new_credentials['name'] = 'credential3'
|
new_credentials['name'] = 'credential3'
|
||||||
@@ -611,6 +617,25 @@ class ProjectsTest(BaseTransactionTest):
|
|||||||
cred_put_t = self.put(edit_creds2, data=d_cred_team, expect=200, auth=self.get_normal_credentials())
|
cred_put_t = self.put(edit_creds2, data=d_cred_team, expect=200, auth=self.get_normal_credentials())
|
||||||
self.put(edit_creds2, data=d_cred_team, expect=403, auth=self.get_other_credentials())
|
self.put(edit_creds2, data=d_cred_team, expect=403, auth=self.get_other_credentials())
|
||||||
|
|
||||||
|
# Reassign credential between team and user.
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
self.post(team_creds, data=dict(id=cred_user.pk), expect=204)
|
||||||
|
response = self.get(edit_creds1)
|
||||||
|
self.assertEqual(response['team'], team.pk)
|
||||||
|
self.assertEqual(response['user'], None)
|
||||||
|
self.post(other_creds, data=dict(id=cred_user.pk), expect=204)
|
||||||
|
response = self.get(edit_creds1)
|
||||||
|
self.assertEqual(response['team'], None)
|
||||||
|
self.assertEqual(response['user'], other.pk)
|
||||||
|
self.post(other_creds, data=dict(id=cred_team.pk), expect=204)
|
||||||
|
response = self.get(edit_creds2)
|
||||||
|
self.assertEqual(response['team'], None)
|
||||||
|
self.assertEqual(response['user'], other.pk)
|
||||||
|
self.post(team_creds, data=dict(id=cred_team.pk), expect=204)
|
||||||
|
response = self.get(edit_creds2)
|
||||||
|
self.assertEqual(response['team'], team.pk)
|
||||||
|
self.assertEqual(response['user'], None)
|
||||||
|
|
||||||
cred_put_t['disassociate'] = 1
|
cred_put_t['disassociate'] = 1
|
||||||
team_url = reverse('api:team_credentials_list', args=(cred_put_t['team'],))
|
team_url = reverse('api:team_credentials_list', args=(cred_put_t['team'],))
|
||||||
self.post(team_url, data=cred_put_t, expect=204, auth=self.get_normal_credentials())
|
self.post(team_url, data=cred_put_t, expect=204, auth=self.get_normal_credentials())
|
||||||
|
|||||||
@@ -322,6 +322,13 @@ class UsersTest(BaseTest):
|
|||||||
orig = User.objects.get(pk=self.super_django_user.pk)
|
orig = User.objects.get(pk=self.super_django_user.pk)
|
||||||
self.assertTrue(orig.username != 'change')
|
self.assertTrue(orig.username != 'change')
|
||||||
|
|
||||||
|
def test_user_delete_non_existant_user(self):
|
||||||
|
user_pk = self.normal_django_user.pk
|
||||||
|
fake_pk = user_pk + 1000
|
||||||
|
self.assertFalse(User.objects.filter(pk=fake_pk).exists(), "We made up a fake pk and it happened to exist")
|
||||||
|
url = reverse('api:user_detail', args=(fake_pk,))
|
||||||
|
self.delete(url, expect=404, auth=self.get_super_credentials())
|
||||||
|
|
||||||
def test_password_not_shown_in_get_operations_for_list_or_detail(self):
|
def test_password_not_shown_in_get_operations_for_list_or_detail(self):
|
||||||
url = reverse('api:user_detail', args=(self.super_django_user.pk,))
|
url = reverse('api:user_detail', args=(self.super_django_user.pk,))
|
||||||
data = self.get(url, expect=200, auth=self.get_super_credentials())
|
data = self.get(url, expect=200, auth=self.get_super_credentials())
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from django.utils.encoding import smart_str
|
|||||||
# PyCrypto
|
# PyCrypto
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.main.utils')
|
||||||
|
|
||||||
__all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
|
__all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
|
||||||
'get_ansible_version', 'get_awx_version', 'update_scm_url',
|
'get_ansible_version', 'get_awx_version', 'update_scm_url',
|
||||||
@@ -380,10 +381,13 @@ def get_system_task_capacity():
|
|||||||
def emit_websocket_notification(endpoint, event, payload):
|
def emit_websocket_notification(endpoint, event, payload):
|
||||||
from awx.main.socket import Socket
|
from awx.main.socket import Socket
|
||||||
|
|
||||||
with Socket('websocket', 'w') as websocket:
|
try:
|
||||||
payload['event'] = event
|
with Socket('websocket', 'w', nowait=True, logger=logger) as websocket:
|
||||||
payload['endpoint'] = endpoint
|
payload['event'] = event
|
||||||
websocket.publish(payload)
|
payload['endpoint'] = endpoint
|
||||||
|
websocket.publish(payload)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
_inventory_updates = threading.local()
|
_inventory_updates = threading.local()
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,16 @@ Author: Eric Johnson <erjohnso@google.com>
|
|||||||
Version: 0.0.1
|
Version: 0.0.1
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
__requires__ = ['pycrypto>=2.6']
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
# Use pkg_resources to find the correct versions of libraries and set
|
||||||
|
# sys.path appropriately when there are multiversion installs. We don't
|
||||||
|
# fail here as there is code that better expresses the errors where the
|
||||||
|
# library is used.
|
||||||
|
pass
|
||||||
|
|
||||||
USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
|
USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
|
||||||
USER_AGENT_VERSION="v1"
|
USER_AGENT_VERSION="v1"
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class OpenStackInventory(object):
|
|||||||
self.openstack_config = os_client_config.config.OpenStackConfig(
|
self.openstack_config = os_client_config.config.OpenStackConfig(
|
||||||
config_files)
|
config_files)
|
||||||
self.clouds = shade.openstack_clouds(self.openstack_config)
|
self.clouds = shade.openstack_clouds(self.openstack_config)
|
||||||
self.refresh = refresh
|
self.refresh = True
|
||||||
|
|
||||||
self.cache_max_age = self.openstack_config.get_cache_max_age()
|
self.cache_max_age = self.openstack_config.get_cache_max_age()
|
||||||
cache_path = self.openstack_config.get_cache_path()
|
cache_path = self.openstack_config.get_cache_path()
|
||||||
@@ -101,9 +101,9 @@ class OpenStackInventory(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
server_vars = meta['server_vars']
|
server_vars = meta['server_vars']
|
||||||
hostvars[server.name][
|
hostvars[server.name]['ansible_ssh_host'] = server_vars['interface_ip']
|
||||||
'ansible_ssh_host'] = server_vars['interface_ip']
|
|
||||||
hostvars[server.name]['openstack'] = server_vars
|
hostvars[server.name]['openstack'] = server_vars
|
||||||
|
hostvars[server.name]['id'] = server_vars['id']
|
||||||
|
|
||||||
for group in meta['groups']:
|
for group in meta['groups']:
|
||||||
groups[group].append(server.name)
|
groups[group].append(server.name)
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class AzureInventory(object):
|
|||||||
the Windows Azure API provides.
|
the Windows Azure API provides.
|
||||||
"""
|
"""
|
||||||
if hostname not in self.host_metadata:
|
if hostname not in self.host_metadata:
|
||||||
return "No host found: %s" % hostname
|
return "No host found: %s" % json.dumps(self.host_metadata)
|
||||||
if jsonify:
|
if jsonify:
|
||||||
return json.dumps(self.host_metadata[hostname])
|
return json.dumps(self.host_metadata[hostname])
|
||||||
return self.host_metadata[hostname]
|
return self.host_metadata[hostname]
|
||||||
@@ -220,12 +220,19 @@ class AzureInventory(object):
|
|||||||
def add_deployment(self, cloud_service, deployment):
|
def add_deployment(self, cloud_service, deployment):
|
||||||
"""Adds a deployment to the inventory and index"""
|
"""Adds a deployment to the inventory and index"""
|
||||||
for role in deployment.role_instance_list.role_instances:
|
for role in deployment.role_instance_list.role_instances:
|
||||||
for ie in role.instance_endpoints.instance_endpoints:
|
try:
|
||||||
if ie.name == 'SSH':
|
# Default port 22 unless port found with name 'SSH'
|
||||||
self.add_instance(role.instance_name, deployment, ie.public_port, cloud_service)
|
port = '22'
|
||||||
break
|
for ie in role.instance_endpoints.instance_endpoints:
|
||||||
|
if ie.name == 'SSH':
|
||||||
|
port = ie.public_port
|
||||||
|
break
|
||||||
|
except AttributeError as e:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.add_instance(role.instance_name, deployment, port, cloud_service, role.instance_status)
|
||||||
|
|
||||||
def add_instance(self, hostname, deployment, ssh_port, cloud_service):
|
def add_instance(self, hostname, deployment, ssh_port, cloud_service, status):
|
||||||
"""Adds an instance to the inventory and index"""
|
"""Adds an instance to the inventory and index"""
|
||||||
|
|
||||||
dest = urlparse(deployment.url).hostname
|
dest = urlparse(deployment.url).hostname
|
||||||
@@ -234,7 +241,9 @@ class AzureInventory(object):
|
|||||||
self.index[hostname] = deployment.name
|
self.index[hostname] = deployment.name
|
||||||
|
|
||||||
self.host_metadata[hostname] = dict(ansible_ssh_host=dest,
|
self.host_metadata[hostname] = dict(ansible_ssh_host=dest,
|
||||||
ansible_ssh_port=int(ssh_port))
|
ansible_ssh_port=int(ssh_port),
|
||||||
|
instance_status=status,
|
||||||
|
private_id=deployment.private_id)
|
||||||
|
|
||||||
# List of all azure deployments
|
# List of all azure deployments
|
||||||
self.push(self.inventory, "azure", hostname)
|
self.push(self.inventory, "azure", hostname)
|
||||||
|
|||||||
@@ -513,15 +513,15 @@ AZURE_REGIONS_BLACKLIST = []
|
|||||||
|
|
||||||
# Inventory variable name/value for determining whether a host is active
|
# Inventory variable name/value for determining whether a host is active
|
||||||
# in Microsoft Azure.
|
# in Microsoft Azure.
|
||||||
AZURE_ENABLED_VAR = 'status'
|
AZURE_ENABLED_VAR = 'instance_status'
|
||||||
AZURE_ENABLED_VALUE = 'created'
|
AZURE_ENABLED_VALUE = 'ReadyRole'
|
||||||
|
|
||||||
# Filter for allowed group and host names when importing inventory from
|
# Filter for allowed group and host names when importing inventory from
|
||||||
# Microsoft Azure.
|
# Microsoft Azure.
|
||||||
AZURE_GROUP_FILTER = r'^.+$'
|
AZURE_GROUP_FILTER = r'^.+$'
|
||||||
AZURE_HOST_FILTER = r'^.+$'
|
AZURE_HOST_FILTER = r'^.+$'
|
||||||
AZURE_EXCLUDE_EMPTY_GROUPS = True
|
AZURE_EXCLUDE_EMPTY_GROUPS = True
|
||||||
AZURE_INSTANCE_ID_VAR = None
|
AZURE_INSTANCE_ID_VAR = 'private_id'
|
||||||
|
|
||||||
# ---------------------
|
# ---------------------
|
||||||
# ----- OpenStack -----
|
# ----- OpenStack -----
|
||||||
@@ -531,7 +531,7 @@ OPENSTACK_ENABLED_VALUE = 'ACTIVE'
|
|||||||
OPENSTACK_GROUP_FILTER = r'^.+$'
|
OPENSTACK_GROUP_FILTER = r'^.+$'
|
||||||
OPENSTACK_HOST_FILTER = r'^.+$'
|
OPENSTACK_HOST_FILTER = r'^.+$'
|
||||||
OPENSTACK_EXCLUDE_EMPTY_GROUPS = True
|
OPENSTACK_EXCLUDE_EMPTY_GROUPS = True
|
||||||
OPENSTACK_INSTANCE_ID_VAR = None
|
OPENSTACK_INSTANCE_ID_VAR = "id"
|
||||||
|
|
||||||
# ---------------------
|
# ---------------------
|
||||||
# -- Activity Stream --
|
# -- Activity Stream --
|
||||||
|
|||||||
@@ -30,10 +30,7 @@
|
|||||||
// WebKit-style focus
|
// WebKit-style focus
|
||||||
.tab-focus() {
|
.tab-focus() {
|
||||||
// Default
|
// Default
|
||||||
outline: thin dotted;
|
outline: 0;
|
||||||
// WebKit
|
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center-align a block level element
|
// Center-align a block level element
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ var tower = angular.module('Tower', [
|
|||||||
.constant('$timezones.definitions.location', urlPrefix + 'lib/angular-tz-extensions/tz/data')
|
.constant('$timezones.definitions.location', urlPrefix + 'lib/angular-tz-extensions/tz/data')
|
||||||
.config(['$routeProvider',
|
.config(['$routeProvider',
|
||||||
function ($routeProvider) {
|
function ($routeProvider) {
|
||||||
|
|
||||||
$routeProvider.
|
$routeProvider.
|
||||||
|
|
||||||
when('/jobs', {
|
when('/jobs', {
|
||||||
@@ -644,6 +643,7 @@ var tower = angular.module('Tower', [
|
|||||||
if (!/^\/(login|logout)/.test($location.path())) {
|
if (!/^\/(login|logout)/.test($location.path())) {
|
||||||
// capture most recent URL, excluding login/logout
|
// capture most recent URL, excluding login/logout
|
||||||
$rootScope.lastPath = $location.path();
|
$rootScope.lastPath = $location.path();
|
||||||
|
$rootScope.enteredPath = $location.path();
|
||||||
$cookieStore.put('lastPath', $location.path());
|
$cookieStore.put('lastPath', $location.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
|||||||
$scope.id = id;
|
$scope.id = id;
|
||||||
$scope.argsPopOver = "<p>These arguments are used with the" +
|
$scope.argsPopOver = "<p>These arguments are used with the" +
|
||||||
" specified module.</p>";
|
" specified module.</p>";
|
||||||
|
|
||||||
// fix arguments help popover based on the module selected
|
// fix arguments help popover based on the module selected
|
||||||
$scope.moduleChange = function () {
|
$scope.moduleChange = function () {
|
||||||
// NOTE: for selenium testing link -
|
// NOTE: for selenium testing link -
|
||||||
@@ -64,20 +65,13 @@ export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
|||||||
$scope.providedHostPatterns = $scope.limit;
|
$scope.providedHostPatterns = $scope.limit;
|
||||||
delete $rootScope.hostPatterns;
|
delete $rootScope.hostPatterns;
|
||||||
|
|
||||||
if ($scope.removeLookUpInitialize) {
|
LookUpInit({
|
||||||
$scope.removeLookUpInitialize();
|
scope: $scope,
|
||||||
}
|
form: form,
|
||||||
$scope.removeLookUpInitialize = $scope.$on('lookUpInitialize', function () {
|
current_item: (!Empty($scope.credential_id)) ? $scope.credential_id : null,
|
||||||
LookUpInit({
|
list: CredentialList,
|
||||||
scope: $scope,
|
field: 'credential',
|
||||||
form: form,
|
input_type: 'radio'
|
||||||
current_item: (!Empty($scope.credential_id)) ? $scope.credential_id : null,
|
|
||||||
list: CredentialList,
|
|
||||||
field: 'credential',
|
|
||||||
input_type: 'radio'
|
|
||||||
});
|
|
||||||
|
|
||||||
Wait('stop'); // END: form population
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($scope.removeChoicesReady) {
|
if ($scope.removeChoicesReady) {
|
||||||
@@ -87,7 +81,10 @@ export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
|||||||
choicesReadyCount++;
|
choicesReadyCount++;
|
||||||
|
|
||||||
if (choicesReadyCount === 2) {
|
if (choicesReadyCount === 2) {
|
||||||
$scope.$emit('lookUpInitialize');
|
// this sets the default options for the selects as specified by the controller.
|
||||||
|
$scope.verbosity = $scope.adhoc_verbosity_options[$scope.verbosity_field.default];
|
||||||
|
$("#forks-number").spinner("value", $scope.forks_field.default);
|
||||||
|
Wait('stop'); // END: form population
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,23 +207,6 @@ export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
|||||||
credential: $scope.credential,
|
credential: $scope.credential,
|
||||||
callback: 'ContinueCred'
|
callback: 'ContinueCred'
|
||||||
});
|
});
|
||||||
|
|
||||||
// // Launch the adhoc job
|
|
||||||
// Rest.setUrl(url);
|
|
||||||
// Rest.post(data)
|
|
||||||
// .success(function (data) {
|
|
||||||
// Wait('stop');
|
|
||||||
// $location.path("/ad_hoc_commands/" + data.id);
|
|
||||||
// })
|
|
||||||
// .error(function (data, status) {
|
|
||||||
// ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
|
||||||
// msg: 'Failed to launch adhoc command. POST returned status: ' +
|
|
||||||
// status });
|
|
||||||
// // TODO: still need to implement popping up a password prompt
|
|
||||||
// // if the credential requires it. The way that the current end-
|
|
||||||
// // point works is that I find out if I need to ask for a
|
|
||||||
// // password from POST, thus I get an error response.
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove all data input into the form
|
// Remove all data input into the form
|
||||||
@@ -237,6 +217,8 @@ export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
|||||||
}
|
}
|
||||||
$scope.limit = $scope.providedHostPatterns;
|
$scope.limit = $scope.providedHostPatterns;
|
||||||
KindChange({ scope: $scope, form: form, reset: false });
|
KindChange({ scope: $scope, form: form, reset: false });
|
||||||
|
$scope.verbosity = $scope.adhoc_verbosity_options[$scope.verbosity_field.default];
|
||||||
|
$("#forks-number").spinner("value", $scope.forks_field.default);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,16 @@ export function Authenticate($log, $cookieStore, $compile, $window, $rootScope,
|
|||||||
if ($location.path() === '/logout') {
|
if ($location.path() === '/logout') {
|
||||||
//if logout request, clear AuthToken and user session data
|
//if logout request, clear AuthToken and user session data
|
||||||
Authorization.logout();
|
Authorization.logout();
|
||||||
|
} else if ($location.path() === '/login') {
|
||||||
|
if ($rootScope.enteredPath) {
|
||||||
|
$rootScope.lastPath = $rootScope.enteredPath;
|
||||||
|
} else if (!$rootScope.lastPath) {
|
||||||
|
// your last path was home
|
||||||
|
$cookieStore.remove('lastPath');
|
||||||
|
$rootScope.lastPath = '/home';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$rootScope.enteredPath = $location.path();
|
||||||
}
|
}
|
||||||
|
|
||||||
e = angular.element(document.getElementById('login-modal-content'));
|
e = angular.element(document.getElementById('login-modal-content'));
|
||||||
@@ -116,7 +126,7 @@ export function Authenticate($log, $cookieStore, $compile, $window, $rootScope,
|
|||||||
"<div class=\"login-alert\" ng-show=\"sessionExpired\">Your session timed out due to inactivity. Please sign in.</div>\n" +
|
"<div class=\"login-alert\" ng-show=\"sessionExpired\">Your session timed out due to inactivity. Please sign in.</div>\n" +
|
||||||
"<form id=\"login-form\" name=\"loginForm\" class=\"form-horizontal\" autocomplete=\"off\" novalidate >\n" +
|
"<form id=\"login-form\" name=\"loginForm\" class=\"form-horizontal\" autocomplete=\"off\" novalidate >\n" +
|
||||||
"<div class=\"form-group\">\n" +
|
"<div class=\"form-group\">\n" +
|
||||||
"<label class=\"control-label col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3 prepend-asterisk\">Username</label>\n" +
|
"<label class=\"control-label col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3 prepend-asterisk prepend-asterisk--login\">Username</label>\n" +
|
||||||
"<div class=\"col-md-8 col-sm-8 col-xs-9\">\n" +
|
"<div class=\"col-md-8 col-sm-8 col-xs-9\">\n" +
|
||||||
"<input type=\"text\" name=\"login_username\" class=\"form-control\" ng-model=\"login_username\"" +
|
"<input type=\"text\" name=\"login_username\" class=\"form-control\" ng-model=\"login_username\"" +
|
||||||
"id=\"login-username\" autocomplete=\"off\" required>\n" +
|
"id=\"login-username\" autocomplete=\"off\" required>\n" +
|
||||||
@@ -125,7 +135,7 @@ export function Authenticate($log, $cookieStore, $compile, $window, $rootScope,
|
|||||||
"</div>\n" +
|
"</div>\n" +
|
||||||
"</div>\n" +
|
"</div>\n" +
|
||||||
"<div class=\"form-group\">\n" +
|
"<div class=\"form-group\">\n" +
|
||||||
"<label class=\"control-label col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3 prepend-asterisk\">Password</label>\n" +
|
"<label class=\"control-label col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3 prepend-asterisk prepend-asterisk--login\">Password</label>\n" +
|
||||||
"<div class=\"col-md-8 col-sm-8 col-xs-9\">\n" +
|
"<div class=\"col-md-8 col-sm-8 col-xs-9\">\n" +
|
||||||
"<input type=\"password\" name=\"login_password\" id=\"login-password\" class=\"form-control\"" +
|
"<input type=\"password\" name=\"login_password\" id=\"login-password\" class=\"form-control\"" +
|
||||||
"ng-model=\"login_password\" required autocomplete=\"off\">\n" +
|
"ng-model=\"login_password\" required autocomplete=\"off\">\n" +
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ JobTemplatesList.$inject = ['$scope', '$rootScope', '$location', '$log', '$route
|
|||||||
export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm,
|
export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm,
|
||||||
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GetBasePath,
|
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GetBasePath,
|
||||||
InventoryList, CredentialList, ProjectList, LookUpInit, md5Setup, ParseTypeChange, Wait, Empty, ToJSON,
|
InventoryList, CredentialList, ProjectList, LookUpInit, md5Setup, ParseTypeChange, Wait, Empty, ToJSON,
|
||||||
CallbackHelpInit, SurveyControllerInit, Prompt) {
|
CallbackHelpInit, SurveyControllerInit, Prompt, GetChoices) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
|
|
||||||
@@ -274,18 +274,6 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
|||||||
$scope.parseType = 'yaml';
|
$scope.parseType = 'yaml';
|
||||||
ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback });
|
ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback });
|
||||||
|
|
||||||
$scope.job_type_options = [
|
|
||||||
{ value: 'run', label: 'Run' },
|
|
||||||
{ value: 'check', label: 'Check' },
|
|
||||||
{ value: 'scan' , label: 'Scan'}
|
|
||||||
];
|
|
||||||
|
|
||||||
$scope.verbosity_options = [
|
|
||||||
{ value: 0, label: 'Default' },
|
|
||||||
{ value: 1, label: 'Verbose' },
|
|
||||||
{ value: 3, label: 'Debug' }
|
|
||||||
];
|
|
||||||
|
|
||||||
$scope.playbook_options = [];
|
$scope.playbook_options = [];
|
||||||
$scope.allow_callbacks = 'false';
|
$scope.allow_callbacks = 'false';
|
||||||
|
|
||||||
@@ -315,34 +303,70 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
|||||||
CloudCredentialList.name = 'cloudcredentials';
|
CloudCredentialList.name = 'cloudcredentials';
|
||||||
CloudCredentialList.iterator = 'cloudcredential';
|
CloudCredentialList.iterator = 'cloudcredential';
|
||||||
|
|
||||||
LookUpInit({
|
|
||||||
url: GetBasePath('credentials') + '?cloud=true',
|
|
||||||
scope: $scope,
|
|
||||||
form: form,
|
|
||||||
current_item: null,
|
|
||||||
list: CloudCredentialList,
|
|
||||||
field: 'cloud_credential',
|
|
||||||
hdr: 'Select Cloud Credential',
|
|
||||||
input_type: 'radio'
|
|
||||||
});
|
|
||||||
|
|
||||||
LookUpInit({
|
|
||||||
url: GetBasePath('credentials') + '?kind=ssh',
|
|
||||||
scope: $scope,
|
|
||||||
form: form,
|
|
||||||
current_item: null,
|
|
||||||
list: CredentialList,
|
|
||||||
field: 'credential',
|
|
||||||
hdr: 'Select Machine Credential',
|
|
||||||
input_type: "radio"
|
|
||||||
});
|
|
||||||
|
|
||||||
SurveyControllerInit({
|
SurveyControllerInit({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
parent_scope: $scope
|
parent_scope: $scope
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($scope.removeLookUpInitialize) {
|
||||||
|
$scope.removeLookUpInitialize();
|
||||||
|
}
|
||||||
|
$scope.removeLookUpInitialize = $scope.$on('lookUpInitialize', function () {
|
||||||
|
LookUpInit({
|
||||||
|
url: GetBasePath('credentials') + '?cloud=true',
|
||||||
|
scope: $scope,
|
||||||
|
form: form,
|
||||||
|
current_item: null,
|
||||||
|
list: CloudCredentialList,
|
||||||
|
field: 'cloud_credential',
|
||||||
|
hdr: 'Select Cloud Credential',
|
||||||
|
input_type: 'radio'
|
||||||
|
});
|
||||||
|
|
||||||
|
LookUpInit({
|
||||||
|
url: GetBasePath('credentials') + '?kind=ssh',
|
||||||
|
scope: $scope,
|
||||||
|
form: form,
|
||||||
|
current_item: null,
|
||||||
|
list: CredentialList,
|
||||||
|
field: 'credential',
|
||||||
|
hdr: 'Select Machine Credential',
|
||||||
|
input_type: "radio"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var selectCount = 0;
|
||||||
|
|
||||||
|
if ($scope.removeChoicesReady) {
|
||||||
|
$scope.removeChoicesReady();
|
||||||
|
}
|
||||||
|
$scope.removeChoicesReady = $scope.$on('choicesReadyVerbosity', function () {
|
||||||
|
selectCount++;
|
||||||
|
if (selectCount === 2) {
|
||||||
|
// this sets the default options for the selects as specified by the controller.
|
||||||
|
$scope.verbosity = $scope.verbosity_options[$scope.verbosity_field.default];
|
||||||
|
$scope.job_type = $scope.job_type_options[$scope.job_type_field.default];
|
||||||
|
$scope.$emit('lookUpInitialize');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup verbosity options select
|
||||||
|
GetChoices({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
field: 'verbosity',
|
||||||
|
variable: 'verbosity_options',
|
||||||
|
callback: 'choicesReadyVerbosity'
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup job type options select
|
||||||
|
GetChoices({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
field: 'job_type',
|
||||||
|
variable: 'job_type_options',
|
||||||
|
callback: 'choicesReadyVerbosity'
|
||||||
|
});
|
||||||
|
|
||||||
// Update playbook select whenever project value changes
|
// Update playbook select whenever project value changes
|
||||||
selectPlaybook = function (oldValue, newValue) {
|
selectPlaybook = function (oldValue, newValue) {
|
||||||
@@ -440,7 +464,7 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (msg) {
|
if (msg) {
|
||||||
Alert('Waning', msg, 'alert-info');
|
Alert('Warning', msg, 'alert-info');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
@@ -621,7 +645,7 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
|||||||
JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
|
JobTemplatesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
|
||||||
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope',
|
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope',
|
||||||
'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit',
|
'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit',
|
||||||
'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON', 'CallbackHelpInit', 'SurveyControllerInit', 'Prompt'
|
'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON', 'CallbackHelpInit', 'SurveyControllerInit', 'Prompt', 'GetChoices'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@@ -653,19 +677,6 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
$scope.parseType = 'yaml';
|
$scope.parseType = 'yaml';
|
||||||
$scope.showJobType = false;
|
$scope.showJobType = false;
|
||||||
|
|
||||||
// Our job type options
|
|
||||||
$scope.job_type_options = [
|
|
||||||
{ value: 'run', label: 'Run' },
|
|
||||||
{ value: 'check', label: 'Check' },
|
|
||||||
{ value: 'scan', label: 'Scan'}
|
|
||||||
];
|
|
||||||
|
|
||||||
$scope.verbosity_options = [
|
|
||||||
{ value: 0, label: 'Default' },
|
|
||||||
{ value: 1, label: 'Verbose' },
|
|
||||||
{ value: 3, label: 'Debug' }
|
|
||||||
];
|
|
||||||
|
|
||||||
SurveyControllerInit({
|
SurveyControllerInit({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
parent_scope: $scope,
|
parent_scope: $scope,
|
||||||
@@ -770,7 +781,7 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
}
|
}
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
if (msg) {
|
if (msg) {
|
||||||
Alert('Waning', msg, 'alert-info');
|
Alert('Warning', msg, 'alert-info');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
@@ -963,7 +974,7 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
}
|
}
|
||||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||||
choicesCount++;
|
choicesCount++;
|
||||||
if (choicesCount === 2) {
|
if (choicesCount === 4) {
|
||||||
$scope.$emit('LoadJobs');
|
$scope.$emit('LoadJobs');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -984,6 +995,24 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
callback: 'choicesReady'
|
callback: 'choicesReady'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setup verbosity options lookup
|
||||||
|
GetChoices({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
field: 'verbosity',
|
||||||
|
variable: 'verbosity_options',
|
||||||
|
callback: 'choicesReady'
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup job type options lookup
|
||||||
|
GetChoices({
|
||||||
|
scope: $scope,
|
||||||
|
url: defaultUrl,
|
||||||
|
field: 'job_type',
|
||||||
|
variable: 'job_type_options',
|
||||||
|
callback: 'choicesReady'
|
||||||
|
});
|
||||||
|
|
||||||
function saveCompleted() {
|
function saveCompleted() {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$scope.$apply(function() {
|
$scope.$apply(function() {
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa
|
|||||||
scope: scheduled_scope,
|
scope: scheduled_scope,
|
||||||
list: ScheduledJobsList,
|
list: ScheduledJobsList,
|
||||||
id: 'scheduled-jobs-tab',
|
id: 'scheduled-jobs-tab',
|
||||||
|
searchSize: 'col-lg-4 col-md-4 col-sm-4 col-xs-12',
|
||||||
url: GetBasePath('schedules') + '?next_run__isnull=false',
|
url: GetBasePath('schedules') + '?next_run__isnull=false',
|
||||||
pageSize: max_rows
|
pageSize: max_rows
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export function PermissionsAdd($scope, $rootScope, $compile, $location, $log, $r
|
|||||||
master.category = 'Inventory';
|
master.category = 'Inventory';
|
||||||
master.inventoryrequired = true;
|
master.inventoryrequired = true;
|
||||||
master.projectrequired = false;
|
master.projectrequired = false;
|
||||||
|
$scope.run_ad_hoc_commands = false;
|
||||||
|
|
||||||
LookUpInit({
|
LookUpInit({
|
||||||
scope: $scope,
|
scope: $scope,
|
||||||
@@ -155,6 +156,22 @@ export function PermissionsAdd($scope, $rootScope, $compile, $location, $log, $r
|
|||||||
input_type: 'radio'
|
input_type: 'radio'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.changeAdhocCommandCheckbox = function () {
|
||||||
|
if ($scope.category === 'Deploy') {
|
||||||
|
$scope.run_ad_hoc_command = false;
|
||||||
|
} else {
|
||||||
|
if ($scope.permission_type === 'admin') {
|
||||||
|
$scope.run_ad_hoc_commands = true;
|
||||||
|
$("#permission_run_ad_hoc_commands_chbox").attr("disabled", true);
|
||||||
|
} else {
|
||||||
|
if (!$scope.run_ad_hoc_commands) {
|
||||||
|
$scope.run_ad_hoc_commands = false;
|
||||||
|
}
|
||||||
|
$("#permission_run_ad_hoc_commands_chbox").attr("disabled", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
$scope.formSave = function () {
|
$scope.formSave = function () {
|
||||||
var fld, url, data = {};
|
var fld, url, data = {};
|
||||||
@@ -226,12 +243,25 @@ export function PermissionsEdit($scope, $rootScope, $compile, $location, $log, $
|
|||||||
defaultUrl = GetBasePath('base') + 'permissions/' + id + '/',
|
defaultUrl = GetBasePath('base') + 'permissions/' + id + '/',
|
||||||
master = {};
|
master = {};
|
||||||
|
|
||||||
|
$scope.changeAdhocCommandCheckbox = function () {
|
||||||
|
if ($scope.category === 'Deploy') {
|
||||||
|
$scope.run_ad_hoc_command = false;
|
||||||
|
} else {
|
||||||
|
if ($scope.permission_type === 'admin') {
|
||||||
|
$scope.run_ad_hoc_commands = true;
|
||||||
|
$("#permission_run_ad_hoc_commands_chbox").attr("disabled", true);
|
||||||
|
} else {
|
||||||
|
if (!$scope.run_ad_hoc_commands) {
|
||||||
|
$scope.run_ad_hoc_commands = false;
|
||||||
|
}
|
||||||
|
$("#permission_run_ad_hoc_commands_chbox").attr("disabled", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||||
generator.reset();
|
generator.reset();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$scope.selectCategory = function (resetIn) {
|
$scope.selectCategory = function (resetIn) {
|
||||||
var reset = (resetIn === false) ? false : true;
|
var reset = (resetIn === false) ? false : true;
|
||||||
PermissionCategoryChange({ scope: $scope, reset: reset });
|
PermissionCategoryChange({ scope: $scope, reset: reset });
|
||||||
@@ -285,6 +315,8 @@ export function PermissionsEdit($scope, $rootScope, $compile, $location, $log, $
|
|||||||
input_type: 'radio'
|
input_type: 'radio'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.changeAdhocCommandCheckbox();
|
||||||
|
|
||||||
if (!$scope.PermissionAddAllowed) {
|
if (!$scope.PermissionAddAllowed) {
|
||||||
// If not a privileged user, disable access
|
// If not a privileged user, disable access
|
||||||
$('form[name="permission_form"]').find('select, input, button').each(function () {
|
$('form[name="permission_form"]').find('select, input, button').each(function () {
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ function HostStatusGraph($compile, $window) {
|
|||||||
if(data.hosts.total+data.hosts.failed>0){
|
if(data.hosts.total+data.hosts.failed>0){
|
||||||
data = [
|
data = [
|
||||||
{ "label": "Successful",
|
{ "label": "Successful",
|
||||||
"color": "#00aa00",
|
"color": "#60D66F",
|
||||||
"value" : data.hosts.total
|
"value" : data.hosts.total
|
||||||
} ,
|
} ,
|
||||||
{ "label": "Failed",
|
{ "label": "Failed",
|
||||||
"color" : "#aa0000",
|
"color" : "#ff5850",
|
||||||
"value" : data.hosts.failed
|
"value" : data.hosts.failed
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -68,7 +68,7 @@ function HostStatusGraph($compile, $window) {
|
|||||||
return '<b>'+x+'</b>'+ '<p>' + Math.floor(y.replace(',','')) + ' Hosts ' + '</p>';
|
return '<b>'+x+'</b>'+ '<p>' + Math.floor(y.replace(',','')) + ' Hosts ' + '</p>';
|
||||||
})
|
})
|
||||||
.labelType("percent")
|
.labelType("percent")
|
||||||
.color(['#00aa00', '#aa0000']);
|
.color(['#60D66F', '#ff5850']);
|
||||||
|
|
||||||
d3.select(element.find('svg')[0])
|
d3.select(element.find('svg')[0])
|
||||||
.datum(data)
|
.datum(data)
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
|
|||||||
scope.jobType = jobtype;
|
scope.jobType = jobtype;
|
||||||
|
|
||||||
var timeFormat, graphData = [
|
var timeFormat, graphData = [
|
||||||
{ "color": "#00aa00",
|
{ "color": "#60D66F",
|
||||||
"key": "Successful",
|
"key": "Successful",
|
||||||
"values": data.jobs.successful
|
"values": data.jobs.successful
|
||||||
},
|
},
|
||||||
{ "key" : "Failed" ,
|
{ "key" : "Failed" ,
|
||||||
"color" : "#aa0000",
|
"color" : "#ff5850",
|
||||||
"values": data.jobs.failed
|
"values": data.jobs.failed
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -82,14 +82,15 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
become_enabled: {
|
become_enabled: {
|
||||||
label: 'Enable Become for Credential',
|
label: 'Enable Privilege Escalation',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
editRequired: false
|
addRequired: false,
|
||||||
// awPopOver: '<p>If checked, user will be become the user ' +
|
editRequird: false,
|
||||||
// 'specified by the credential.</p>',
|
column: 2,
|
||||||
// dataPlacement: 'right',
|
awPopOver: "<p>If enabled, run this playbook as an administrator. This is the equivalent of passing the<code> --become</code> option to the <code> ansible</code> command. </p>",
|
||||||
// dataTitle: 'Enable Become for Credential',
|
dataPlacement: 'right',
|
||||||
// dataContainer: 'body'
|
dataTitle: 'Become Privilege Escalation',
|
||||||
|
dataContainer: "body"
|
||||||
},
|
},
|
||||||
verbosity: {
|
verbosity: {
|
||||||
label: 'Verbosity',
|
label: 'Verbosity',
|
||||||
@@ -103,8 +104,28 @@ export default
|
|||||||
'out of the command run that are supported.',
|
'out of the command run that are supported.',
|
||||||
dataTitle: 'Module',
|
dataTitle: 'Module',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: 'body'
|
dataContainer: 'body',
|
||||||
}
|
"default": 1
|
||||||
|
},
|
||||||
|
forks: {
|
||||||
|
label: 'Forks',
|
||||||
|
id: 'forks-number',
|
||||||
|
type: 'number',
|
||||||
|
integer: true,
|
||||||
|
min: 0,
|
||||||
|
spinner: true,
|
||||||
|
"default": 0,
|
||||||
|
addRequired: false,
|
||||||
|
editRequired: false,
|
||||||
|
'class': "input-small",
|
||||||
|
column: 1,
|
||||||
|
awPopOver: '<p>The number of parallel or simultaneous processes to use while executing the command. 0 signifies ' +
|
||||||
|
'the default value from the <a id="ansible_forks_docs" href=\"http://docs.ansible.com/intro_configuration.html#the-ansible-configuration-file\" ' +
|
||||||
|
' target=\"_blank\">ansible configuration file</a>.</p>',
|
||||||
|
dataTitle: 'Forks',
|
||||||
|
dataPlacement: 'right',
|
||||||
|
dataContainer: "body"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
buttons: {
|
buttons: {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default
|
|||||||
labelBind: 'hostLabel',
|
labelBind: 'hostLabel',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
ngShow: "kind.value == 'vmware' || kind.value == 'openstack'",
|
ngShow: "kind.value == 'vmware' || kind.value == 'openstack'",
|
||||||
awPopOverWatch: "projectPopOver",
|
awPopOverWatch: "hostPopOver",
|
||||||
awPopOver: "set in helpers/credentials",
|
awPopOver: "set in helpers/credentials",
|
||||||
dataTitle: 'Host',
|
dataTitle: 'Host',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export default
|
|||||||
'class': "input-small",
|
'class': "input-small",
|
||||||
column: 1,
|
column: 1,
|
||||||
awPopOver: '<p>The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
|
awPopOver: '<p>The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
|
||||||
'the default value from the <a href=\"http://docs.ansible.com/intro_configuration.html#the-ansible-configuration-file\" ' +
|
'the default value from the <a id="ansible_forks_docs" href=\"http://docs.ansible.com/intro_configuration.html#the-ansible-configuration-file\" ' +
|
||||||
' target=\"_blank\">ansible configuration file</a>.</p>',
|
' target=\"_blank\">ansible configuration file</a>.</p>',
|
||||||
dataTitle: 'Forks',
|
dataTitle: 'Forks',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
@@ -199,7 +199,7 @@ export default
|
|||||||
label: 'Verbosity',
|
label: 'Verbosity',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
ngOptions: 'v.label for v in verbosity_options track by v.value',
|
ngOptions: 'v.label for v in verbosity_options track by v.value',
|
||||||
"default": 0,
|
"default": 1,
|
||||||
addRequired: true,
|
addRequired: true,
|
||||||
editRequired: true,
|
editRequired: true,
|
||||||
column: 1,
|
column: 1,
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export default
|
|||||||
labelClass: 'prepend-asterisk',
|
labelClass: 'prepend-asterisk',
|
||||||
type: 'radio_group',
|
type: 'radio_group',
|
||||||
class: 'squeeze',
|
class: 'squeeze',
|
||||||
|
ngChange: 'changeAdhocCommandCheckbox()',
|
||||||
options: [{
|
options: [{
|
||||||
label: 'Read',
|
label: 'Read',
|
||||||
value: 'read',
|
value: 'read',
|
||||||
|
|||||||
@@ -53,11 +53,12 @@ export default
|
|||||||
},
|
},
|
||||||
source_regions: {
|
source_regions: {
|
||||||
label: 'Regions',
|
label: 'Regions',
|
||||||
type: 'text',
|
type: 'select',
|
||||||
|
ngOptions: 'source.label for source in source_region_choices track by source.value',
|
||||||
|
multiSelect: true,
|
||||||
ngShow: "source && (source.value == 'rax' || source.value == 'ec2' || source.value == 'gce' || source.value == 'azure')",
|
ngShow: "source && (source.value == 'rax' || source.value == 'ec2' || source.value == 'gce' || source.value == 'azure')",
|
||||||
addRequired: false,
|
addRequired: false,
|
||||||
editRequired: false,
|
editRequired: false,
|
||||||
awMultiselect: 'source_region_choices',
|
|
||||||
dataTitle: 'Source Regions',
|
dataTitle: 'Source Regions',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
awPopOver: "<p>Click on the regions field to see a list of regions for your cloud provider. You can select multiple regions, " +
|
awPopOver: "<p>Click on the regions field to see a list of regions for your cloud provider. You can select multiple regions, " +
|
||||||
@@ -87,11 +88,12 @@ export default
|
|||||||
},
|
},
|
||||||
group_by: {
|
group_by: {
|
||||||
label: 'Only Group By',
|
label: 'Only Group By',
|
||||||
type: 'text',
|
type: 'select',
|
||||||
ngShow: "source && source.value == 'ec2'",
|
ngShow: "source && source.value == 'ec2'",
|
||||||
|
ngOptions: 'source.label for source in group_by_choices track by source.value',
|
||||||
addRequired: false,
|
addRequired: false,
|
||||||
editRequired: false,
|
editRequired: false,
|
||||||
awMultiselect: 'group_by_choices',
|
multiSelect: true,
|
||||||
dataTitle: 'Only Group By',
|
dataTitle: 'Only Group By',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
awPopOver: "<p>Select which groups to create automatically. " +
|
awPopOver: "<p>Select which groups to create automatically. " +
|
||||||
@@ -115,9 +117,10 @@ export default
|
|||||||
ngShow: "source && source.value === 'custom'",
|
ngShow: "source && source.value === 'custom'",
|
||||||
sourceModel: 'source_script',
|
sourceModel: 'source_script',
|
||||||
sourceField: 'name',
|
sourceField: 'name',
|
||||||
ngClick: 'lookUpSource_script()' , //'lookUpCustom_inventory()',
|
ngClick: 'lookUpSource_script()' ,
|
||||||
addRequired: true,
|
addRequired: false,
|
||||||
editRequired: true
|
editRequired: false,
|
||||||
|
ngRequired: "source && source.value === 'custom'",
|
||||||
},
|
},
|
||||||
extra_vars: {
|
extra_vars: {
|
||||||
label: 'Environment Variables', //"{{vars_label}}" ,
|
label: 'Environment Variables', //"{{vars_label}}" ,
|
||||||
@@ -162,7 +165,9 @@ export default
|
|||||||
},
|
},
|
||||||
inventory_variables: {
|
inventory_variables: {
|
||||||
label: 'Source Variables', //"{{vars_label}}" ,
|
label: 'Source Variables', //"{{vars_label}}" ,
|
||||||
ngShow: "source && (source.value == 'vmware')",
|
|
||||||
|
ngShow: "source && (source.value == 'vmware' || " +
|
||||||
|
"source.value == 'openstack')",
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
addRequired: false,
|
addRequired: false,
|
||||||
editRequird: false,
|
editRequird: false,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*********************************************
|
/*********************************************
|
||||||
* Copyright (c) 2014 AnsibleWorks, Inc.
|
* Copyright (c) 2015 AnsibleWorks, Inc.
|
||||||
*
|
*
|
||||||
* GroupsHelper
|
* GroupsHelper
|
||||||
*
|
*
|
||||||
@@ -231,105 +231,113 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* TODO: Document
|
* TODO: Document
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', 'Wait', 'ParseTypeChange', 'CustomInventoryList' ,
|
.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', 'Empty', 'Wait', 'ParseTypeChange', 'CustomInventoryList', 'CreateSelect2',
|
||||||
function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, ParseTypeChange, CustomInventoryList) {
|
function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, ParseTypeChange, CustomInventoryList, CreateSelect2) {
|
||||||
return function (params) {
|
return function (params) {
|
||||||
|
|
||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
form = params.form,
|
form = params.form,
|
||||||
kind, url, callback, invUrl;
|
kind, url, callback, invUrl;
|
||||||
|
|
||||||
if (!Empty(scope.source)) {
|
if (!Empty(scope.source)) {
|
||||||
if (scope.source.value === 'file') {
|
if (scope.source.value === 'file') {
|
||||||
scope.sourcePathRequired = true;
|
scope.sourcePathRequired = true;
|
||||||
} else {
|
} else {
|
||||||
scope.sourcePathRequired = false;
|
scope.sourcePathRequired = false;
|
||||||
// reset fields
|
// reset fields
|
||||||
scope.source_path = '';
|
scope.source_path = '';
|
||||||
scope[form.name + '_form'].source_path.$setValidity('required', true);
|
scope[form.name + '_form'].source_path.$setValidity('required', true);
|
||||||
}
|
}
|
||||||
if (scope.source.value === 'rax') {
|
if (scope.source.value === 'rax') {
|
||||||
scope.source_region_choices = scope.rax_regions;
|
scope.source_region_choices = scope.rax_regions;
|
||||||
//$('#s2id_group_source_regions').select2('data', []);
|
$('#source_form').addClass('squeeze');
|
||||||
$('#s2id_source_source_regions').select2('data', [{
|
CreateSelect2({
|
||||||
id: 'all',
|
element: '#source_source_regions'
|
||||||
text: 'All'
|
});
|
||||||
}]);
|
} else if (scope.source.value === 'ec2') {
|
||||||
$('#source_form').addClass('squeeze');
|
scope.source_region_choices = scope.ec2_regions;
|
||||||
} else if (scope.source.value === 'ec2') {
|
|
||||||
scope.source_region_choices = scope.ec2_regions;
|
|
||||||
$('#s2id_source_source_regions').select2('data', [{
|
|
||||||
id: 'all',
|
|
||||||
text: 'All'
|
|
||||||
}]);
|
|
||||||
scope.group_by_choices = scope.ec2_group_by;
|
scope.group_by_choices = scope.ec2_group_by;
|
||||||
$('#s2id_group_by').select2('data', []);
|
|
||||||
$('#source_form').addClass('squeeze');
|
$('#source_form').addClass('squeeze');
|
||||||
} else if (scope.source.value === 'gce') {
|
CreateSelect2({
|
||||||
scope.source_region_choices = scope.gce_regions;
|
element: '#source_source_regions'
|
||||||
//$('#s2id_group_source_regions').select2('data', []);
|
|
||||||
$('#s2id_source_source_regions').select2('data', [{
|
|
||||||
id: 'all',
|
|
||||||
text: 'All'
|
|
||||||
}]);
|
|
||||||
$('#source_form').addClass('squeeze');
|
|
||||||
} else if (scope.source.value === 'azure') {
|
|
||||||
scope.source_region_choices = scope.azure_regions;
|
|
||||||
//$('#s2id_group_source_regions').select2('data', []);
|
|
||||||
$('#s2id_source_source_regions').select2('data', [{
|
|
||||||
id: 'all',
|
|
||||||
text: 'All'
|
|
||||||
}]);
|
|
||||||
$('#source_form').addClass('squeeze');
|
|
||||||
}
|
|
||||||
if(scope.source.value==="custom"){
|
|
||||||
// need to filter the possible custom scripts by the organization defined for the current inventory
|
|
||||||
invUrl = GetBasePath('inventory_scripts') + '?organization='+scope.$parent.inventory.organization;
|
|
||||||
LookUpInit({
|
|
||||||
url: invUrl,
|
|
||||||
scope: scope,
|
|
||||||
form: form,
|
|
||||||
hdr: "Select Custom Inventory",
|
|
||||||
list: CustomInventoryList,
|
|
||||||
field: 'source_script',
|
|
||||||
input_type: 'radio'
|
|
||||||
});
|
});
|
||||||
scope.extra_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
CreateSelect2({
|
||||||
ParseTypeChange({ scope: scope, variable: 'extra_vars', parse_variable: form.fields.extra_vars.parseTypeName,
|
element: '#source_group_by'
|
||||||
field_id: 'source_extra_vars', onReady: callback });
|
|
||||||
}
|
|
||||||
if(scope.source.value==="vmware"){
|
|
||||||
scope.inventory_variables = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
|
||||||
ParseTypeChange({ scope: scope, variable: 'inventory_variables', parse_variable: form.fields.inventory_variables.parseTypeName,
|
|
||||||
field_id: 'source_inventory_variables', onReady: callback });
|
|
||||||
}
|
|
||||||
if (scope.source.value === 'rax' || scope.source.value === 'ec2'|| scope.source.value==='gce' || scope.source.value === 'azure' || scope.source.value === 'vmware') {
|
|
||||||
kind = (scope.source.value === 'rax') ? 'rax' : (scope.source.value==='gce') ? 'gce' : (scope.source.value==='azure') ? 'azure' : (scope.source.value === 'vmware') ? 'vmware' : 'aws' ;
|
|
||||||
url = GetBasePath('credentials') + '?cloud=true&kind=' + kind;
|
|
||||||
LookUpInit({
|
|
||||||
url: url,
|
|
||||||
scope: scope,
|
|
||||||
form: form,
|
|
||||||
list: CredentialList,
|
|
||||||
field: 'credential',
|
|
||||||
input_type: "radio"
|
|
||||||
});
|
});
|
||||||
if ($('#group_tabs .active a').text() === 'Source' && (scope.source.value === 'ec2' )) {
|
} else if (scope.source.value === 'gce') {
|
||||||
callback = function(){ Wait('stop'); };
|
scope.source_region_choices = scope.gce_regions;
|
||||||
Wait('start');
|
$('#source_form').addClass('squeeze');
|
||||||
scope.source_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
CreateSelect2({
|
||||||
ParseTypeChange({ scope: scope, variable: 'source_vars', parse_variable: form.fields.source_vars.parseTypeName,
|
element: '#source_source_regions'
|
||||||
field_id: 'source_source_vars', onReady: callback });
|
});
|
||||||
}
|
} else if (scope.source.value === 'azure') {
|
||||||
}
|
scope.source_region_choices = scope.azure_regions;
|
||||||
}
|
$('#source_form').addClass('squeeze');
|
||||||
};
|
CreateSelect2({
|
||||||
}
|
element: '#source_source_regions'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(scope.source.value==="custom"){
|
||||||
|
// need to filter the possible custom scripts by the organization defined for the current inventory
|
||||||
|
invUrl = GetBasePath('inventory_scripts') + '?organization='+scope.$parent.inventory.organization;
|
||||||
|
LookUpInit({
|
||||||
|
url: invUrl,
|
||||||
|
scope: scope,
|
||||||
|
form: form,
|
||||||
|
hdr: "Select Custom Inventory",
|
||||||
|
list: CustomInventoryList,
|
||||||
|
field: 'source_script',
|
||||||
|
input_type: 'radio'
|
||||||
|
});
|
||||||
|
scope.extra_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
||||||
|
ParseTypeChange({ scope: scope, variable: 'extra_vars', parse_variable: form.fields.extra_vars.parseTypeName,
|
||||||
|
field_id: 'source_extra_vars', onReady: callback });
|
||||||
|
}
|
||||||
|
if(scope.source.value==="vmware" ||
|
||||||
|
scope.source.value==="openstack"){
|
||||||
|
scope.inventory_variables = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
||||||
|
ParseTypeChange({ scope: scope, variable: 'inventory_variables', parse_variable: form.fields.inventory_variables.parseTypeName,
|
||||||
|
field_id: 'source_inventory_variables', onReady: callback });
|
||||||
|
}
|
||||||
|
if (scope.source.value === 'rax' ||
|
||||||
|
scope.source.value === 'ec2' ||
|
||||||
|
scope.source.value==='gce' ||
|
||||||
|
scope.source.value === 'azure' ||
|
||||||
|
scope.source.value === 'vmware' ||
|
||||||
|
scope.source.value === 'openstack') {
|
||||||
|
if (scope.source.value === 'ec2') {
|
||||||
|
kind = 'aws';
|
||||||
|
} else {
|
||||||
|
kind = scope.source.value;
|
||||||
|
}
|
||||||
|
url = GetBasePath('credentials') + '?cloud=true&kind=' + kind;
|
||||||
|
LookUpInit({
|
||||||
|
url: url,
|
||||||
|
scope: scope,
|
||||||
|
form: form,
|
||||||
|
list: CredentialList,
|
||||||
|
field: 'credential',
|
||||||
|
input_type: "radio"
|
||||||
|
});
|
||||||
|
if ($('#group_tabs .active a').text() === 'Source' &&
|
||||||
|
(scope.source.value === 'ec2' )) {
|
||||||
|
callback = function(){ Wait('stop'); };
|
||||||
|
Wait('start');
|
||||||
|
scope.source_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
|
||||||
|
ParseTypeChange({ scope: scope, variable: 'source_vars',
|
||||||
|
parse_variable: form.fields.source_vars.parseTypeName,
|
||||||
|
field_id: 'source_source_vars', onReady: callback });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -700,11 +708,11 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
|
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
|
||||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
|
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
|
||||||
'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find', 'WatchInventoryWindowResize',
|
'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find', 'WatchInventoryWindowResize',
|
||||||
'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SourceForm', 'SetSchedulesInnerDialogSize',
|
'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SourceForm', 'SetSchedulesInnerDialogSize', 'CreateSelect2',
|
||||||
function ($rootScope, $location, $log, $routeParams, $compile, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
|
function ($rootScope, $location, $log, $routeParams, $compile, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
|
||||||
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
|
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
|
||||||
GetChoices, UpdateGroup, SourceChange, Find, WatchInventoryWindowResize, ParseVariableString, ToJSON, GroupsScheduleListInit,
|
GetChoices, UpdateGroup, SourceChange, Find, WatchInventoryWindowResize, ParseVariableString, ToJSON, GroupsScheduleListInit,
|
||||||
SourceForm, SetSchedulesInnerDialogSize) {
|
SourceForm, SetSchedulesInnerDialogSize, CreateSelect2) {
|
||||||
return function (params) {
|
return function (params) {
|
||||||
|
|
||||||
var parent_scope = params.scope,
|
var parent_scope = params.scope,
|
||||||
@@ -905,7 +913,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
Wait('start');
|
Wait('start');
|
||||||
ParseTypeChange({ scope: sources_scope, variable: 'source_vars', parse_variable: SourceForm.fields.source_vars.parseTypeName,
|
ParseTypeChange({ scope: sources_scope, variable: 'source_vars', parse_variable: SourceForm.fields.source_vars.parseTypeName,
|
||||||
field_id: 'source_source_vars', onReady: waitStop });
|
field_id: 'source_source_vars', onReady: waitStop });
|
||||||
} else if (sources_scope.source && (sources_scope.source.value === 'vmware')) {
|
} else if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
|
||||||
|
sources_scope.source.value === 'openstack')) {
|
||||||
Wait('start');
|
Wait('start');
|
||||||
ParseTypeChange({ scope: sources_scope, variable: 'inventory_variables', parse_variable: SourceForm.fields.inventory_variables.parseTypeName,
|
ParseTypeChange({ scope: sources_scope, variable: 'inventory_variables', parse_variable: SourceForm.fields.inventory_variables.parseTypeName,
|
||||||
field_id: 'source_inventory_variables', onReady: waitStop });
|
field_id: 'source_inventory_variables', onReady: waitStop });
|
||||||
@@ -1033,8 +1042,19 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
sources_scope.sourceChange(); //set defaults that rely on source value
|
sources_scope.sourceChange(); //set defaults that rely on source value
|
||||||
|
|
||||||
if (data.source_regions) {
|
if (data.source_regions) {
|
||||||
if (data.source === 'ec2' || data.source === 'rax') {
|
if (data.source === 'ec2' ||
|
||||||
set = (data.source === 'ec2') ? sources_scope.ec2_regions : sources_scope.rax_regions;
|
data.source === 'rax' ||
|
||||||
|
data.source === 'gce' ||
|
||||||
|
data.source === 'azure') {
|
||||||
|
if (data.source === 'ec2') {
|
||||||
|
set = sources_scope.ec2_regions;
|
||||||
|
} else if (data.source === 'rax') {
|
||||||
|
set = sources_scope.rax_regions;
|
||||||
|
} else if (data.source === 'gce') {
|
||||||
|
set = sources_scope.gce_regions;
|
||||||
|
} else if (data.source === 'azure') {
|
||||||
|
set = sources_scope.azure_regions;
|
||||||
|
}
|
||||||
opts = [];
|
opts = [];
|
||||||
list = data.source_regions.split(',');
|
list = data.source_regions.split(',');
|
||||||
for (i = 0; i < list.length; i++) {
|
for (i = 0; i < list.length; i++) {
|
||||||
@@ -1048,7 +1068,11 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
master.source_regions = opts;
|
master.source_regions = opts;
|
||||||
$('#s2id_source_source_regions').select2('data', opts);
|
CreateSelect2({
|
||||||
|
element: "#source_source_regions",
|
||||||
|
opts: opts
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If empty, default to all
|
// If empty, default to all
|
||||||
@@ -1056,7 +1080,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
id: 'all',
|
id: 'all',
|
||||||
text: 'All'
|
text: 'All'
|
||||||
}];
|
}];
|
||||||
$('#s2id_source_source_regions').select2('data', master.source_regions);
|
|
||||||
}
|
}
|
||||||
if (data.group_by && data.source === 'ec2') {
|
if (data.group_by && data.source === 'ec2') {
|
||||||
set = sources_scope.ec2_group_by;
|
set = sources_scope.ec2_group_by;
|
||||||
@@ -1073,7 +1096,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
master.group_by = opts;
|
master.group_by = opts;
|
||||||
$('#s2id_source_group_by').select2('data', opts);
|
CreateSelect2({
|
||||||
|
element: "#source_group_by",
|
||||||
|
opts: opts
|
||||||
|
});
|
||||||
}
|
}
|
||||||
sources_scope.group_update_url = data.related.update;
|
sources_scope.group_update_url = data.related.update;
|
||||||
modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded
|
modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded
|
||||||
@@ -1249,17 +1275,19 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create a string out of selected list of regions
|
// Create a string out of selected list of regions
|
||||||
regions = $('#s2id_source_source_regions').select2("data");
|
if(sources_scope.source_regions){
|
||||||
r = [];
|
regions = $('#source_source_regions').select2("data");
|
||||||
for (i = 0; i < regions.length; i++) {
|
r = [];
|
||||||
r.push(regions[i].id);
|
for (i = 0; i < regions.length; i++) {
|
||||||
|
r.push(regions[i].id);
|
||||||
|
}
|
||||||
|
data.source_regions = r.join();
|
||||||
}
|
}
|
||||||
data.source_regions = r.join();
|
|
||||||
|
|
||||||
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
||||||
data.instance_filters = sources_scope.instance_filters;
|
data.instance_filters = sources_scope.instance_filters;
|
||||||
// Create a string out of selected list of regions
|
// Create a string out of selected list of regions
|
||||||
group_by = $('#s2id_source_group_by').select2("data");
|
group_by = $('#source_group_by').select2("data");
|
||||||
r = [];
|
r = [];
|
||||||
for (i = 0; i < group_by.length; i++) {
|
for (i = 0; i < group_by.length; i++) {
|
||||||
r.push(group_by[i].id);
|
r.push(group_by[i].id);
|
||||||
@@ -1276,7 +1304,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.extra_vars, true);
|
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.extra_vars, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sources_scope.source && (sources_scope.source.value === 'vmware')) {
|
if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
|
||||||
|
sources_scope.source.value === 'openstack')) {
|
||||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.inventory_variables, true);
|
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.inventory_variables, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1376,7 +1405,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
|
|||||||
group_created = true;
|
group_created = true;
|
||||||
group_id = data.id;
|
group_id = data.id;
|
||||||
sources_scope.source_url = data.related.inventory_source;
|
sources_scope.source_url = data.related.inventory_source;
|
||||||
if (properties_scope.variables) {
|
if (properties_scope.variables && properties_scope.variables !== "---") {
|
||||||
modal_scope.$emit('updateVariables', json_data, data.related.variable_data);
|
modal_scope.$emit('updateVariables', json_data, data.related.variable_data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -1146,7 +1146,7 @@ export default
|
|||||||
graph_data.push({
|
graph_data.push({
|
||||||
label: 'OK',
|
label: 'OK',
|
||||||
value: (scope.host_summary.ok === scope.host_summary.total) ? 1 : scope.host_summary.ok,
|
value: (scope.host_summary.ok === scope.host_summary.total) ? 1 : scope.host_summary.ok,
|
||||||
color: '#00aa00'
|
color: '#60D66F'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (scope.host_summary.changed) {
|
if (scope.host_summary.changed) {
|
||||||
@@ -1167,7 +1167,7 @@ export default
|
|||||||
graph_data.push({
|
graph_data.push({
|
||||||
label: 'Failed',
|
label: 'Failed',
|
||||||
value: (scope.host_summary.failed === scope.host_summary.total) ? 1 : scope.host_summary.failed,
|
value: (scope.host_summary.failed === scope.host_summary.total) ? 1 : scope.host_summary.failed,
|
||||||
color: '#aa0000'
|
color: '#ff5850'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -710,9 +710,8 @@ function($compile, Rest, GetBasePath, TextareaResize,CreateDialog, GenerateForm,
|
|||||||
if(data.vault_password === "ASK"){
|
if(data.vault_password === "ASK"){
|
||||||
passwords.push("vault_password");
|
passwords.push("vault_password");
|
||||||
}
|
}
|
||||||
scope.$emit(callback, passwords);
|
|
||||||
}
|
}
|
||||||
|
scope.$emit(callback, passwords);
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
export default
|
export default
|
||||||
angular.module('LoadConfigHelper', ['Utilities'])
|
angular.module('LoadConfigHelper', ['Utilities'])
|
||||||
|
|
||||||
.factory('LoadConfig', ['$log', '$rootScope', '$http', 'ProcessErrors', 'Store', function($log, $rootScope, $http, ProcessErrors, Store) {
|
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location', 'ProcessErrors', 'Store', function($log, $rootScope, $http, $location, ProcessErrors, Store) {
|
||||||
return function() {
|
return function() {
|
||||||
|
|
||||||
if ($rootScope.removeLoadConfig) {
|
if ($rootScope.removeLoadConfig) {
|
||||||
@@ -42,6 +42,7 @@ angular.module('LoadConfigHelper', ['Utilities'])
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$rootScope.enteredPath = $location.path();
|
||||||
// Load js/local_config.js
|
// Load js/local_config.js
|
||||||
$http({ method:'GET', url: $basePath + 'js/local_config.js' })
|
$http({ method:'GET', url: $basePath + 'js/local_config.js' })
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import listGenerator from 'tower/shared/list-generator/main';
|
|||||||
export default
|
export default
|
||||||
angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ApiLoader', 'ModalDialog'])
|
angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ApiLoader', 'ModalDialog'])
|
||||||
|
|
||||||
.factory('LookUpInit', ['Alert', 'Rest', 'generateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', 'CreateDialog',
|
.factory('LookUpInit', ['Alert', 'Rest', 'ProcessErrors', 'generateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', 'CreateDialog',
|
||||||
function (Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty, CreateDialog) {
|
function (Alert, Rest, ProcessErrors, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty, CreateDialog) {
|
||||||
return function (params) {
|
return function (params) {
|
||||||
|
|
||||||
var parent_scope = params.scope,
|
var parent_scope = params.scope,
|
||||||
@@ -63,6 +63,31 @@ export default
|
|||||||
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
|
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
|
||||||
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);
|
$('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);
|
||||||
|
|
||||||
|
// Auto populate the field if there is only one result
|
||||||
|
Rest.setUrl(defaultUrl);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
if (data.count === 1) {
|
||||||
|
parent_scope[field] = data.results[0].id;
|
||||||
|
if (parent_scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) {
|
||||||
|
parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
|
||||||
|
data.results[0][form.fields[field].sourceField];
|
||||||
|
if (parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
|
||||||
|
parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]
|
||||||
|
.$setValidity('awlookup', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parent_scope[form.name + '_form']) {
|
||||||
|
parent_scope[form.name + '_form'].$setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(parent_scope, data, status, form, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to launch adhoc command. POST returned status: ' +
|
||||||
|
status });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
parent_scope['lookUp' + name] = function () {
|
parent_scope['lookUp' + name] = function () {
|
||||||
|
|
||||||
|
|||||||
@@ -570,6 +570,7 @@ export default
|
|||||||
list = params.list,
|
list = params.list,
|
||||||
id = params.id,
|
id = params.id,
|
||||||
url = params.url,
|
url = params.url,
|
||||||
|
searchSize = params.searchSize,
|
||||||
pageSize = params.pageSize || 5,
|
pageSize = params.pageSize || 5,
|
||||||
spinner = (params.spinner === undefined) ? true : params.spinner;
|
spinner = (params.spinner === undefined) ? true : params.spinner;
|
||||||
|
|
||||||
@@ -579,7 +580,7 @@ export default
|
|||||||
id: id,
|
id: id,
|
||||||
breadCrumbs: false,
|
breadCrumbs: false,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
searchSize: 'col-lg-6 col-md-6 col-sm-6 col-xs-12',
|
searchSize: (searchSize) ? searchSize : 'col-lg-6 col-md-6 col-sm-6 col-xs-12',
|
||||||
showSearch: true
|
showSearch: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ export default
|
|||||||
},{
|
},{
|
||||||
name: "Microsoft Azure",
|
name: "Microsoft Azure",
|
||||||
value: "azure"
|
value: "azure"
|
||||||
|
},{
|
||||||
|
name: "Openstack",
|
||||||
|
value: "openstack"
|
||||||
}],
|
}],
|
||||||
sourceModel: 'inventory_source',
|
sourceModel: 'inventory_source',
|
||||||
sourceField: 'source',
|
sourceField: 'source',
|
||||||
@@ -86,7 +89,7 @@ export default
|
|||||||
has_external_source: {
|
has_external_source: {
|
||||||
label: 'Has external source?',
|
label: 'Has external source?',
|
||||||
searchType: 'in',
|
searchType: 'in',
|
||||||
searchValue: 'ec2,rax,vmware,azure,gce',
|
searchValue: 'ec2,rax,vmware,azure,gce,openstack',
|
||||||
searchOnly: true,
|
searchOnly: true,
|
||||||
sourceModel: 'inventory_source',
|
sourceModel: 'inventory_source',
|
||||||
sourceField: 'source'
|
sourceField: 'source'
|
||||||
@@ -170,4 +173,4 @@ export default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ export default
|
|||||||
},{
|
},{
|
||||||
name: "Microsoft Azure",
|
name: "Microsoft Azure",
|
||||||
value: "azure"
|
value: "azure"
|
||||||
|
},{
|
||||||
|
name: "Openstack",
|
||||||
|
value: "openstack"
|
||||||
}],
|
}],
|
||||||
sourceModel: 'inventory_source',
|
sourceModel: 'inventory_source',
|
||||||
sourceField: 'source',
|
sourceField: 'source',
|
||||||
@@ -55,7 +58,7 @@ export default
|
|||||||
has_external_source: {
|
has_external_source: {
|
||||||
label: 'Has external source?',
|
label: 'Has external source?',
|
||||||
searchType: 'in',
|
searchType: 'in',
|
||||||
searchValue: 'ec2,rax,vmware,azure,gce',
|
searchValue: 'ec2,rax,vmware,azure,gce,openstack',
|
||||||
searchOnly: true,
|
searchOnly: true,
|
||||||
sourceModel: 'inventory_source',
|
sourceModel: 'inventory_source',
|
||||||
sourceField: 'source'
|
sourceField: 'source'
|
||||||
|
|||||||
@@ -70,7 +70,12 @@ angular.module('AuthService', ['ngCookies', Utilities.name])
|
|||||||
$cookieStore.put( 'lastPath', '/portal');
|
$cookieStore.put( 'lastPath', '/portal');
|
||||||
$rootScope.lastPath = '/portal';
|
$rootScope.lastPath = '/portal';
|
||||||
}
|
}
|
||||||
|
else if ($cookieStore.get('lastPath') !== '/home' || $cookieStore.get('lastPath') !== '/'){
|
||||||
|
// do nothing
|
||||||
|
$rootScope.lastPath = $cookieStore.get('lastPath');
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
|
// your last path was home
|
||||||
$cookieStore.remove('lastPath');
|
$cookieStore.remove('lastPath');
|
||||||
$rootScope.lastPath = '/home';
|
$rootScope.lastPath = '/home';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -659,6 +659,75 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name shared.function:Utilities#CreateSelect2
|
||||||
|
* @methodOf shared.function:Utilities
|
||||||
|
* @description Make a regular select drop down a select2 dropdown
|
||||||
|
* To make a ``<select>`` field a select2 select 2, create the field in the
|
||||||
|
* form definition with the multiSelect flag set to true. In the controller
|
||||||
|
* of the page in question, call the CreateSelect2 factory with the element
|
||||||
|
* id (be sure to include the appropriate jquery identifier in the parameter)
|
||||||
|
* or any options that should be pre-selected in the select2 field.
|
||||||
|
* The array of options should be formatted as
|
||||||
|
* ```
|
||||||
|
* [
|
||||||
|
* {
|
||||||
|
* id: 'id' ,
|
||||||
|
* text: 'text'
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* id: 'id' ,
|
||||||
|
* text: 'text'
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
.factory('CreateSelect2', [
|
||||||
|
function () {
|
||||||
|
return function (params) {
|
||||||
|
|
||||||
|
var element = params.element,
|
||||||
|
options = params.opts;
|
||||||
|
|
||||||
|
$.fn.select2.amd.require([
|
||||||
|
"select2/utils",
|
||||||
|
"select2/dropdown",
|
||||||
|
"select2/dropdown/attachContainer",
|
||||||
|
"select2/dropdown/search",
|
||||||
|
], function (Utils, DropdownAdapter, AttachContainer, DropdownSearch) {
|
||||||
|
|
||||||
|
var CustomAdapter = Utils.Decorate(
|
||||||
|
Utils.Decorate(
|
||||||
|
DropdownAdapter,
|
||||||
|
DropdownSearch
|
||||||
|
),
|
||||||
|
AttachContainer
|
||||||
|
);
|
||||||
|
|
||||||
|
$(element).select2({
|
||||||
|
dropdownAdapter: CustomAdapter,
|
||||||
|
multiple: 'true',
|
||||||
|
theme: "bootstrap",
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
if(options){
|
||||||
|
for (var d = 0; d < $(element + " option").length; d++) {
|
||||||
|
var item = $(element + " option")[d];
|
||||||
|
for ( var f = 0; f < options.length; f++){
|
||||||
|
if(item.value === options[f].id){
|
||||||
|
// Append it to the select
|
||||||
|
item.setAttribute('selected', 'selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(element).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}])
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name shared.function:Utilities#GetChoices
|
* @name shared.function:Utilities#GetChoices
|
||||||
|
|||||||
@@ -620,33 +620,6 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
|||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.directive('awMultiSelect', [ function() {
|
|
||||||
return {
|
|
||||||
require: 'ngModel',
|
|
||||||
link: function(scope, elm) {
|
|
||||||
$(elm).multiselect ({
|
|
||||||
buttonClass: 'btn-default, btn-mini',
|
|
||||||
buttonWidth: 'auto',
|
|
||||||
buttonContainer: '<div class="btn-group" />',
|
|
||||||
maxHeight: false,
|
|
||||||
buttonText: function(options) {
|
|
||||||
if (options.length === 0) {
|
|
||||||
return 'None selected <b class="caret"></b>';
|
|
||||||
}
|
|
||||||
if (options.length > 3) {
|
|
||||||
return options.length + ' selected <b class="caret"></b>';
|
|
||||||
}
|
|
||||||
var selected = '';
|
|
||||||
options.each(function() {
|
|
||||||
selected += $(this).text() + ', ';
|
|
||||||
});
|
|
||||||
return selected.substr(0, selected.length -2) + ' <b class="caret"></b>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Enable jqueryui spinner widget on a numeric input field
|
// Enable jqueryui spinner widget on a numeric input field
|
||||||
//
|
//
|
||||||
@@ -728,43 +701,6 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
|||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
awMultiSelect
|
|
||||||
Relies on select2.js to create a multi-select with tags.
|
|
||||||
*/
|
|
||||||
.directive('awMultiselect', [ function() {
|
|
||||||
return {
|
|
||||||
require: '^form', //inject the form into the ctrl parameter
|
|
||||||
link: function(scope, elm, attrs, ctrl) {
|
|
||||||
$(elm).select2({
|
|
||||||
multiple: true,
|
|
||||||
data: function() {
|
|
||||||
// dynamically load the possible values
|
|
||||||
if (scope[attrs.awMultiselect]) {
|
|
||||||
var set = scope[attrs.awMultiselect],
|
|
||||||
opts = [], i;
|
|
||||||
for (i=0; i < set.length; i++) {
|
|
||||||
opts.push({ id: set[i].value, text: set[i].label });
|
|
||||||
}
|
|
||||||
return {results: opts };
|
|
||||||
}
|
|
||||||
return {results: { id: '', text: ''} };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure the form buttons enable when the value changes
|
|
||||||
$(elm).on('change', function() {
|
|
||||||
ctrl.$setDirty();
|
|
||||||
if (!scope.$$phase) {
|
|
||||||
scope.$digest();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make an element draggable. Used on inventory groups tree.
|
* Make an element draggable. Used on inventory groups tree.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -671,10 +671,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
html += Attr(field, 'type');
|
html += Attr(field, 'type');
|
||||||
html += "ng-model=\"" + fld + '" ';
|
html += "ng-model=\"" + fld + '" ';
|
||||||
html += "name=\"" + fld + '" ';
|
html += "name=\"" + fld + '" ';
|
||||||
if (form.name === "permission") {
|
|
||||||
html += "ng-disabled='permission_type === \"admin\"'";
|
|
||||||
html += "ng-checked='permission_type === \"admin\"'";
|
|
||||||
}
|
|
||||||
html += (field.ngChange) ? Attr(field, 'ngChange') : "";
|
html += (field.ngChange) ? Attr(field, 'ngChange') : "";
|
||||||
html += "id=\"" + form.name + "_" + fld + "_chbox\" ";
|
html += "id=\"" + form.name + "_" + fld + "_chbox\" ";
|
||||||
html += (idx !== undefined) ? "_" + idx : "";
|
html += (idx !== undefined) ? "_" + idx : "";
|
||||||
@@ -791,7 +787,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
field.awRequiredWhen.variable + "\" " : "";
|
field.awRequiredWhen.variable + "\" " : "";
|
||||||
html += (field.awValidUrl) ? "aw-valid-url " : "";
|
html += (field.awValidUrl) ? "aw-valid-url " : "";
|
||||||
html += (field.associated && this.form.fields[field.associated].ask) ? "ng-disabled=\"" + field.associated + "_ask\" " : "";
|
html += (field.associated && this.form.fields[field.associated].ask) ? "ng-disabled=\"" + field.associated + "_ask\" " : "";
|
||||||
html += (field.awMultiselect) ? "aw-multiselect=\"" + field.awMultiselect + "\" " : "";
|
|
||||||
html += ">\n";
|
html += ">\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,7 +910,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
field.awRequiredWhen.variable + "' " : "";
|
field.awRequiredWhen.variable + "' " : "";
|
||||||
html += (field.awValidUrl) ? "aw-valid-url " : "";
|
html += (field.awValidUrl) ? "aw-valid-url " : "";
|
||||||
html += (field.associated && this.form.fields[field.associated].ask) ? "ng-disabled='" + field.associated + "_ask' " : "";
|
html += (field.associated && this.form.fields[field.associated].ask) ? "ng-disabled='" + field.associated + "_ask' " : "";
|
||||||
html += (field.awMultiselect) ? "aw-multiselect='" + field.awMultiselect + "' " : "";
|
|
||||||
html += ">\n";
|
html += ">\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,7 +1032,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
html += "class=\"form-control";
|
html += "class=\"form-control";
|
||||||
html += (field['class']) ? " " + field['class'] : "";
|
html += (field['class']) ? " " + field['class'] : "";
|
||||||
html += "\" ";
|
html += "\" ";
|
||||||
html += this.attr(field, 'ngOptions');
|
html += (field.ngOptions) ? this.attr(field, 'ngOptions') : "" ;
|
||||||
html += (field.ngChange) ? this.attr(field, 'ngChange') : "";
|
html += (field.ngChange) ? this.attr(field, 'ngChange') : "";
|
||||||
html += (field.ngDisabled) ? this.attr(field, 'ngDisabled'): "";
|
html += (field.ngDisabled) ? this.attr(field, 'ngDisabled'): "";
|
||||||
html += (field.ngRequired) ? this.attr(field, 'ngRequired') : "";
|
html += (field.ngRequired) ? this.attr(field, 'ngRequired') : "";
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ angular.module('PromptDialog', ['Utilities'])
|
|||||||
|
|
||||||
var dialog = angular.element(document.getElementById('prompt-modal')),
|
var dialog = angular.element(document.getElementById('prompt-modal')),
|
||||||
scope = dialog.scope(), cls, local_backdrop;
|
scope = dialog.scope(), cls, local_backdrop;
|
||||||
|
|
||||||
scope.promptHeader = params.hdr;
|
scope.promptHeader = params.hdr;
|
||||||
scope.promptBody = $sce.trustAsHtml(params.body);
|
scope.promptBody = $sce.trustAsHtml(params.body);
|
||||||
scope.promptAction = params.action;
|
scope.promptAction = params.action;
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ export default [ function() {
|
|||||||
barWidth: 7,
|
barWidth: 7,
|
||||||
barSpacing: 2,
|
barSpacing: 2,
|
||||||
zeroBarColor: 'grey',
|
zeroBarColor: 'grey',
|
||||||
posBarColor: '#00aa00',
|
posBarColor: '#60D66F',
|
||||||
negBarColor: '#aa0000',
|
negBarColor: '#ff5850',
|
||||||
tooltipFormatter: scope.formatter,
|
tooltipFormatter: scope.formatter,
|
||||||
tooltipFormat: '{{value:jobs}}',
|
tooltipFormat: '{{value:jobs}}',
|
||||||
tooltipValueLookups: {
|
tooltipValueLookups: {
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ angular.module('DashboardCountsWidget', ['RestServices', 'Utilities'])
|
|||||||
element.html(html);
|
element.html(html);
|
||||||
$compile(element)(scope);
|
$compile(element)(scope);
|
||||||
if(dashboard.hosts.failed>0 ){
|
if(dashboard.hosts.failed>0 ){
|
||||||
$('#failed-hosts').replaceWith("<a style=\"color:#aa0000\" href=\"/#/home/hosts/?has_active_failures=true\" id=\"failed-hosts\">"+dashboard.hosts.failed+"</a>");
|
$('#failed-hosts').replaceWith("<a style=\"color: #ff5850\" href=\"/#/home/hosts/?has_active_failures=true\" id=\"failed-hosts\">"+dashboard.hosts.failed+"</a>");
|
||||||
}
|
}
|
||||||
if(dashboard.inventories.inventory_failed>0 ){
|
if(dashboard.inventories.inventory_failed>0 ){
|
||||||
$('#failed-inventories').replaceWith("<a style=\"color:#aa0000\" href=/#/inventories/?inventory_sources_with_failures id=\"failed-inventories\">"+dashboard.inventories.inventory_failed+"</a>");
|
$('#failed-inventories').replaceWith("<a style=\"color: #ff5850\" href=/#/inventories/?inventory_sources_with_failures id=\"failed-inventories\">"+dashboard.inventories.inventory_failed+"</a>");
|
||||||
}
|
}
|
||||||
if(dashboard.projects.failed>0 ){
|
if(dashboard.projects.failed>0 ){
|
||||||
$('#failed-projects').replaceWith("<a style=\"color:#aa0000\" href=\"/#/projects/?status=failed\" id=\"failed-projects\">"+dashboard.projects.failed+"</a>");
|
$('#failed-projects').replaceWith("<a style=\"color: #ff5850\" href=\"/#/projects/?status=failed\" id=\"failed-projects\">"+dashboard.projects.failed+"</a>");
|
||||||
}
|
}
|
||||||
scope.$emit('WidgetLoaded');
|
scope.$emit('WidgetLoaded');
|
||||||
|
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ angular.module('HostPieChartWidget', ['RestServices', 'Utilities'])
|
|||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
"label": "Successful",
|
"label": "Successful",
|
||||||
"color": "#00aa00",
|
"color": "#60D66F",
|
||||||
"value" : dashboard.hosts.total
|
"value" : dashboard.hosts.total
|
||||||
} ,
|
} ,
|
||||||
{
|
{
|
||||||
"label": "Failed",
|
"label": "Failed",
|
||||||
"color" : "#aa0000",
|
"color" : "#ff5850",
|
||||||
"value" : dashboard.hosts.failed
|
"value" : dashboard.hosts.failed
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -85,7 +85,7 @@ angular.module('HostPieChartWidget', ['RestServices', 'Utilities'])
|
|||||||
.tooltipContent(function(x, y) {
|
.tooltipContent(function(x, y) {
|
||||||
return '<b>'+x+'</b>'+ '<p>' + Math.floor(y.replace(',','')) + ' Hosts ' + '</p>';
|
return '<b>'+x+'</b>'+ '<p>' + Math.floor(y.replace(',','')) + ' Hosts ' + '</p>';
|
||||||
})
|
})
|
||||||
.color(['#00aa00', '#aa0000']);
|
.color(['#60D66F', '#ff5850']);
|
||||||
|
|
||||||
host_pie_chart.pie.pieLabelsOutside(true).labelType("percent");
|
host_pie_chart.pie.pieLabelsOutside(true).labelType("percent");
|
||||||
|
|
||||||
|
|||||||
@@ -117,13 +117,13 @@ angular.module('JobStatusGraphWidget', ['RestServices', 'Utilities'])
|
|||||||
|
|
||||||
var timeFormat, graphData = [
|
var timeFormat, graphData = [
|
||||||
{
|
{
|
||||||
"color": "#00aa00",
|
"color": "#60D66F",
|
||||||
"key": "Successful",
|
"key": "Successful",
|
||||||
"values": data.jobs.successful
|
"values": data.jobs.successful
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key" : "Failed" ,
|
"key" : "Failed" ,
|
||||||
"color" : "#aa0000",
|
"color" : "#ff5850",
|
||||||
"values": data.jobs.failed
|
"values": data.jobs.failed
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -216,4 +216,4 @@ angular.module('JobStatusGraphWidget', ['RestServices', 'Utilities'])
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -12,14 +12,11 @@
|
|||||||
@blue: #1778c3; /* logo blue */
|
@blue: #1778c3; /* logo blue */
|
||||||
@blue-link: #1778c3;
|
@blue-link: #1778c3;
|
||||||
@blue-dark: #2a6496; /* link hover */
|
@blue-dark: #2a6496; /* link hover */
|
||||||
@green: #00aa00; // Ansible OK
|
|
||||||
@grey: #A9A9A9;
|
@grey: #A9A9A9;
|
||||||
@grey-txt: #707070;
|
@grey-txt: #707070;
|
||||||
@info: #d9edf7; /* alert info background color */
|
@info: #d9edf7; /* alert info background color */
|
||||||
@info-border: #bce8f1; /* alert info border color */
|
@info-border: #bce8f1; /* alert info border color */
|
||||||
@info-color: #3a87ad;
|
@info-color: #3a87ad;
|
||||||
@red: #aa0000; // Ansible Failed
|
|
||||||
@red-hover: #AE3F3A;
|
|
||||||
@unreachable: #FF0000;
|
@unreachable: #FF0000;
|
||||||
@changed: #FF9900; // Ansible Changed
|
@changed: #FF9900; // Ansible Changed
|
||||||
@skipped: #00aaaa; // Ansible Skipped
|
@skipped: #00aaaa; // Ansible Skipped
|
||||||
@@ -30,6 +27,10 @@
|
|||||||
|
|
||||||
@tip-background: #0088CC;
|
@tip-background: #0088CC;
|
||||||
@tip-color: #fff;
|
@tip-color: #fff;
|
||||||
|
@green: #60D66F;
|
||||||
|
@red: #ff5850;
|
||||||
|
@red-hover: #FA8C87;
|
||||||
|
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
@@ -186,6 +187,18 @@ a:focus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removing all the pesky outlines on buttons/links/etc.
|
||||||
|
a:focus,
|
||||||
|
a:active,
|
||||||
|
button:focus,
|
||||||
|
button:active,
|
||||||
|
i:focus,
|
||||||
|
i:active,
|
||||||
|
.btn:focus,
|
||||||
|
.btn:active:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.jqstooltip{
|
.jqstooltip{
|
||||||
background-color: black !important;
|
background-color: black !important;
|
||||||
border-radius:4px;
|
border-radius:4px;
|
||||||
@@ -365,6 +378,11 @@ textarea.allowresize {
|
|||||||
.prepend-asterisk:before {
|
.prepend-asterisk:before {
|
||||||
content: "\002A\00A0";
|
content: "\002A\00A0";
|
||||||
color: @red;
|
color: @red;
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prepend-asterisk--login:before {
|
||||||
|
margin-right: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
@@ -656,10 +674,10 @@ dd {
|
|||||||
|
|
||||||
/* Outline required fields in Red when there is an error */
|
/* Outline required fields in Red when there is an error */
|
||||||
.form-control.ng-dirty.ng-invalid, .form-control.ng-dirty.ng-invalid:focus {
|
.form-control.ng-dirty.ng-invalid, .form-control.ng-dirty.ng-invalid:focus {
|
||||||
border-color: rgba(204, 0, 0, 0.8);
|
border-color: rgba(255, 88, 80, 0.8);
|
||||||
outline: 0;
|
outline: 0;
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(204, 0, 0, 0.6);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6);
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(204, 0, 0, 0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For some reason TB 3 RC1 does not provide an input-mini */
|
/* For some reason TB 3 RC1 does not provide an input-mini */
|
||||||
@@ -1487,8 +1505,6 @@ input[type="checkbox"].checkbox-no-label {
|
|||||||
border-right: 1px solid #ddd;
|
border-right: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#groups_table .actions .cancel { padding-right: 2px; }
|
|
||||||
|
|
||||||
.node-toggle, .node-no-toggle {
|
.node-toggle, .node-no-toggle {
|
||||||
/* also used on job evetns */
|
/* also used on job evetns */
|
||||||
float: none;
|
float: none;
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "select2",
|
"name": "select2",
|
||||||
"version": "3.5.1",
|
"description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
|
||||||
"main": [
|
"main": [
|
||||||
"select2.js",
|
"dist/js/select2.js",
|
||||||
"select2.css",
|
"dist/css/select2.css"
|
||||||
"select2.png",
|
|
||||||
"select2x2.png",
|
|
||||||
"select2-spinner.gif"
|
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"repository": {
|
||||||
"jquery": ">= 1.7.1"
|
"type": "git",
|
||||||
|
"url": "git@github.com:select2/select2.git"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/ivaynberg/select2",
|
"homepage": "https://github.com/ivaynberg/select2",
|
||||||
"_release": "3.5.1",
|
"version": "4.0.0-rc.2",
|
||||||
|
"_release": "4.0.0-rc.2",
|
||||||
"_resolution": {
|
"_resolution": {
|
||||||
"type": "version",
|
"type": "version",
|
||||||
"tag": "3.5.1",
|
"tag": "4.0.0-rc.2",
|
||||||
"commit": "621a3f9532357148b05efc0602f7e06b44ff9bb4"
|
"commit": "69e2d73c42ae261b924a052e45d9b6f6dbc60fc6"
|
||||||
},
|
},
|
||||||
"_source": "git://github.com/ivaynberg/select2.git",
|
"_source": "git://github.com/ivaynberg/select2.git",
|
||||||
"_target": "~3.5.1",
|
"_target": "~4.0.0",
|
||||||
"_originalSource": "select2",
|
"_originalSource": "select2"
|
||||||
"_direct": true
|
|
||||||
}
|
}
|
||||||
6
awx/ui/static/lib/select2/.editorconfig
Normal file
6
awx/ui/static/lib/select2/.editorconfig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_size = 2
|
||||||
4
awx/ui/static/lib/select2/.gitignore
vendored
4
awx/ui/static/lib/select2/.gitignore
vendored
@@ -1,2 +1,2 @@
|
|||||||
.idea
|
node_modules
|
||||||
|
dist/js/i18n/build.txt
|
||||||
|
|||||||
4
awx/ui/static/lib/select2/.jshintignore
Normal file
4
awx/ui/static/lib/select2/.jshintignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
src/js/banner.*.js
|
||||||
|
src/js/wrapper.*.js
|
||||||
|
tests/vendor/*.js
|
||||||
|
tests/helpers.js
|
||||||
25
awx/ui/static/lib/select2/.jshintrc
Normal file
25
awx/ui/static/lib/select2/.jshintrc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"bitwise": true,
|
||||||
|
"camelcase": true,
|
||||||
|
"curly": true,
|
||||||
|
"es3": true,
|
||||||
|
"eqnull": true,
|
||||||
|
"freeze": true,
|
||||||
|
"globals": {
|
||||||
|
"console": false,
|
||||||
|
"define": false,
|
||||||
|
"document": false,
|
||||||
|
"expect": false,
|
||||||
|
"MockContainer": false,
|
||||||
|
"module": false,
|
||||||
|
"require": false,
|
||||||
|
"test": false,
|
||||||
|
"window": false
|
||||||
|
},
|
||||||
|
"indent": 2,
|
||||||
|
"maxlen": 80,
|
||||||
|
"noarg": true,
|
||||||
|
"nonew": true,
|
||||||
|
"quotmark": "single",
|
||||||
|
"undef": true
|
||||||
|
}
|
||||||
20
awx/ui/static/lib/select2/.travis.yml
Normal file
20
awx/ui/static/lib/select2/.travis.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- 0.10
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- secure: XMNK8GVxkwKa6oLl7nJwgg/wmY1YDk5rrMd+UXz26EDCsMDbiy1P7GhN2fEiBSLaQ7YfEuvaDcmzQxTrT0YTHp1PDzb2o9J4tIDdEkqPcv1y8xMaYDfmsN0rBPdBwZEg9H5zUgi7OdUbrGswSYxsKCE3x8EOqK89104HyOo1LN4=
|
||||||
|
- secure: BU5BPRx6H4O3WJ509YPixjUxg+hDF3z2BVJX6NiGmKWweqvCEYFfiiHLwDEgp/ynRcF9vGVi1V4Ly1jq7f8NIajbDZ5q443XchZFYFg78K/EwD5mK6LYt16zb7+Jn0KbzwHeGRGzc9AvcEYlW6i634cSCm4n3BnqtF5PpogSzdw=
|
||||||
|
|
||||||
|
script:
|
||||||
|
- grunt ci
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "chat.freenode.net#select2"
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
110
awx/ui/static/lib/select2/CONTRIBUTING.md
Normal file
110
awx/ui/static/lib/select2/CONTRIBUTING.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
Contributing to Select2
|
||||||
|
=======================
|
||||||
|
Looking to contribute something to Select2? **Here's how you can help.**
|
||||||
|
|
||||||
|
Please take a moment to review this document in order to make the contribution
|
||||||
|
process easy and effective for everyone involved.
|
||||||
|
|
||||||
|
Following these guidelines helps to communicate that you respect the time of
|
||||||
|
the developers managing and developing this open source project. In return,
|
||||||
|
they should reciprocate that respect in addressing your issue or assessing
|
||||||
|
patches and features.
|
||||||
|
|
||||||
|
Using the issue tracker
|
||||||
|
-----------------------
|
||||||
|
When [reporting bugs][reporting-bugs] or
|
||||||
|
[requesting features][requesting-features], the
|
||||||
|
[issue tracker on GitHub][issue-tracker] is the recommended channel to use.
|
||||||
|
|
||||||
|
The issue tracker **is not** a place for support requests. The
|
||||||
|
[mailing list][mailing-list] or [IRC channel][irc-channel] are better places to
|
||||||
|
get help.
|
||||||
|
|
||||||
|
Reporting bugs with Select2
|
||||||
|
---------------------------
|
||||||
|
We really appreciate clear bug reports that _consistently_ show an issue
|
||||||
|
_within Select2_.
|
||||||
|
|
||||||
|
The ideal bug report follows these guidelines:
|
||||||
|
|
||||||
|
1. **Use the [GitHub issue search][issue-search]** — Check if the issue
|
||||||
|
has already been reported.
|
||||||
|
2. **Check if the issue has been fixed** — Try to reproduce the problem
|
||||||
|
using the code in the `master` branch.
|
||||||
|
3. **Isolate the problem** — Try to create an
|
||||||
|
[isolated test case][isolated-case] that consistently reproduces the problem.
|
||||||
|
|
||||||
|
Please try to be as detailed as possible in your bug report, especially if an
|
||||||
|
isolated test case cannot be made. Some useful questions to include the answer
|
||||||
|
to are:
|
||||||
|
|
||||||
|
- What steps can be used to reproduce the issue?
|
||||||
|
- What is the bug and what is the expected outcome?
|
||||||
|
- What browser(s) and Operating System have you tested with?
|
||||||
|
- Does the bug happen consistently across all tested browsers?
|
||||||
|
- What version of jQuery are you using? And what version of Select2?
|
||||||
|
- Are you using Select2 with other plugins?
|
||||||
|
|
||||||
|
All of these questions will help others fix and identify any potential bugs.
|
||||||
|
|
||||||
|
Requesting features in Select2
|
||||||
|
------------------------------
|
||||||
|
Select2 is a large library that carries with it a lot of functionality. Because
|
||||||
|
of this, many feature requests will not be implemented in the core library.
|
||||||
|
|
||||||
|
Before starting work on a major feature for Select2, **contact the
|
||||||
|
[community][community] first** or you may risk spending a considerable amount of
|
||||||
|
time on something which the project developers are not interested in bringing
|
||||||
|
into the project.
|
||||||
|
|
||||||
|
Triaging issues and pull requests
|
||||||
|
---------------------------------
|
||||||
|
Anyone can help the project maintainers triage issues and review pull requests.
|
||||||
|
|
||||||
|
### Handling new issues
|
||||||
|
|
||||||
|
Select2 regularly receives new issues which need to be tested and organized.
|
||||||
|
|
||||||
|
When a new issue that comes in that is similar to another existing issue, it
|
||||||
|
should be checked to make sure it is not a duplicate. Duplicates issues should
|
||||||
|
be marked by replying to the issue with "Duplicate of #[issue number]" where
|
||||||
|
`[issue number]` is the url or issue number for the existing issue. This will
|
||||||
|
allow the project maintainers to quickly close off additional issues and keep
|
||||||
|
the discussion focused within a single issue.
|
||||||
|
|
||||||
|
If you can test issues that are reported to Select2 that contain test cases and
|
||||||
|
confirm under what conditions bugs happen, that will allow others to identify
|
||||||
|
what causes a bug quicker.
|
||||||
|
|
||||||
|
### Reviewing pull requests
|
||||||
|
|
||||||
|
It is very common for pull requests to be opened for issues that contain a clear
|
||||||
|
solution to the problem. These pull requests should be rigorously reviewed by
|
||||||
|
the community before being accepted. If you are not sure about a piece of
|
||||||
|
submitted code, or know of a better way to do something, do not hesitate to make
|
||||||
|
a comment on the pull request.
|
||||||
|
|
||||||
|
### Reviving old tickets
|
||||||
|
|
||||||
|
If you come across tickets which have not been updated for a while, you are
|
||||||
|
encouraged to revive them. While this can be as simple as saying `:+1:`, it is
|
||||||
|
best if you can include more information on the issue. Common bugs and feature
|
||||||
|
requests are more likely to be fixed, whether it is by the community or the
|
||||||
|
developers, so keeping tickets up to date is encouraged.
|
||||||
|
|
||||||
|
Licensing
|
||||||
|
---------
|
||||||
|
|
||||||
|
It should also be made clear that **all code contributed to Select** must be
|
||||||
|
licensable under the [MIT license][licensing]. Code that cannot be released
|
||||||
|
under this license **cannot be accepted** into the project.
|
||||||
|
|
||||||
|
[community]: https://select2.github.io/community.html
|
||||||
|
[reporting-bugs]: #reporting-bugs-with-select2
|
||||||
|
[requesting-features]: #requesting-features-in-select2
|
||||||
|
[issue-tracker]: https://github.com/select2/select2/issues
|
||||||
|
[mailing-list]: https://github.com/select2/select2#mailing-list
|
||||||
|
[irc-channel]: https://github.com/select2/select2#irc-channel
|
||||||
|
[issue-search]: https://github.com/select2/select2/search?q=&type=Issues
|
||||||
|
[isolated-case]: http://css-tricks.com/6263-reduced-test-cases/
|
||||||
|
[licensing]: https://github.com/select2/select2/blob/master/LICENSE.md
|
||||||
363
awx/ui/static/lib/select2/Gruntfile.js
vendored
Normal file
363
awx/ui/static/lib/select2/Gruntfile.js
vendored
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
module.exports = function (grunt) {
|
||||||
|
// Full list of files that must be included by RequireJS
|
||||||
|
includes = [
|
||||||
|
'jquery.select2',
|
||||||
|
'almond'
|
||||||
|
];
|
||||||
|
|
||||||
|
fullIncludes = [
|
||||||
|
'jquery',
|
||||||
|
'jquery.mousewheel',
|
||||||
|
|
||||||
|
'select2/compat/matcher',
|
||||||
|
'select2/compat/initSelection',
|
||||||
|
'select2/compat/inputData',
|
||||||
|
'select2/compat/query',
|
||||||
|
|
||||||
|
'select2/dropdown/attachContainer',
|
||||||
|
'select2/dropdown/stopPropagation',
|
||||||
|
|
||||||
|
'select2/selection/stopPropagation'
|
||||||
|
].concat(includes);
|
||||||
|
|
||||||
|
var i18nModules = [];
|
||||||
|
var i18nPaths = {};
|
||||||
|
|
||||||
|
var i18nFiles = grunt.file.expand({
|
||||||
|
cwd: 'src/js'
|
||||||
|
}, 'select2/i18n/*.js');
|
||||||
|
|
||||||
|
var testFiles = grunt.file.expand('tests/**/*.html');
|
||||||
|
var testUrls = testFiles.map(function (filePath) {
|
||||||
|
return 'http://localhost:9999/' + filePath;
|
||||||
|
});
|
||||||
|
|
||||||
|
var testBuildNumber = "unknown";
|
||||||
|
|
||||||
|
if (process.env.TRAVIS_JOB_ID) {
|
||||||
|
testBuildNumber = "travis-" + process.env.TRAVIS_JOB_ID;
|
||||||
|
} else {
|
||||||
|
var currentTime = new Date();
|
||||||
|
|
||||||
|
testBuildNumber = "manual-" + currentTime.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < i18nFiles.length; i++) {
|
||||||
|
var file = i18nFiles[i];
|
||||||
|
var name = file.split('.')[0];
|
||||||
|
|
||||||
|
i18nModules.push({
|
||||||
|
name: name
|
||||||
|
});
|
||||||
|
|
||||||
|
i18nPaths[name] = '../../' + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minifiedBanner = '/*! Select2 <%= package.version %> | https://github.com/select2/select2/blob/master/LICENSE.md */';
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
package: grunt.file.readJSON('package.json'),
|
||||||
|
|
||||||
|
clean: {
|
||||||
|
docs: ['docs/_site']
|
||||||
|
},
|
||||||
|
|
||||||
|
concat: {
|
||||||
|
'dist': {
|
||||||
|
options: {
|
||||||
|
banner: grunt.file.read('src/js/wrapper.start.js'),
|
||||||
|
},
|
||||||
|
src: [
|
||||||
|
'dist/js/select2.js',
|
||||||
|
'src/js/wrapper.end.js'
|
||||||
|
],
|
||||||
|
dest: 'dist/js/select2.js'
|
||||||
|
},
|
||||||
|
'dist.full': {
|
||||||
|
options: {
|
||||||
|
banner: grunt.file.read('src/js/wrapper.start.js'),
|
||||||
|
},
|
||||||
|
src: [
|
||||||
|
'dist/js/select2.full.js',
|
||||||
|
'src/js/wrapper.end.js'
|
||||||
|
],
|
||||||
|
dest: 'dist/js/select2.full.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
connect: {
|
||||||
|
tests: {
|
||||||
|
options: {
|
||||||
|
base: '.',
|
||||||
|
hostname: '127.0.0.1',
|
||||||
|
port: 9999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
uglify: {
|
||||||
|
'dist': {
|
||||||
|
src: 'dist/js/select2.js',
|
||||||
|
dest: 'dist/js/select2.min.js',
|
||||||
|
options: {
|
||||||
|
banner: minifiedBanner
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'dist.full': {
|
||||||
|
src: 'dist/js/select2.full.js',
|
||||||
|
dest: 'dist/js/select2.full.min.js',
|
||||||
|
options: {
|
||||||
|
banner: minifiedBanner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
qunit: {
|
||||||
|
all: {
|
||||||
|
options: {
|
||||||
|
urls: testUrls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'saucelabs-qunit': {
|
||||||
|
all: {
|
||||||
|
options: {
|
||||||
|
build: testBuildNumber,
|
||||||
|
tags: ['tests', 'qunit'],
|
||||||
|
urls: testUrls,
|
||||||
|
testname: 'QUnit test for Select2',
|
||||||
|
browsers: [
|
||||||
|
{
|
||||||
|
browserName: 'internet explorer',
|
||||||
|
version: '8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
browserName: 'internet explorer',
|
||||||
|
version: '9'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
browserName: 'internet explorer',
|
||||||
|
version: '10'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
browserName: 'internet explorer',
|
||||||
|
version: '11'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
browserName: 'firefox'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
browserName: 'chrome'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
browserName: 'opera',
|
||||||
|
version: '12'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'gh-pages': {
|
||||||
|
options: {
|
||||||
|
base: 'docs',
|
||||||
|
branch: 'master',
|
||||||
|
clone: 'node_modules/grunt-gh-pages/repo',
|
||||||
|
message: 'Updated docs with master',
|
||||||
|
push: true,
|
||||||
|
repo: 'git@github.com:select2/select2.github.io.git'
|
||||||
|
},
|
||||||
|
src: '**'
|
||||||
|
},
|
||||||
|
|
||||||
|
jekyll: {
|
||||||
|
options: {
|
||||||
|
src: 'docs',
|
||||||
|
dest: 'docs/_site'
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
d: null
|
||||||
|
},
|
||||||
|
serve: {
|
||||||
|
options: {
|
||||||
|
serve: true,
|
||||||
|
watch: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
jshint: {
|
||||||
|
options: {
|
||||||
|
jshintrc: true
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
src: ['src/js/**/*.js']
|
||||||
|
},
|
||||||
|
tests: {
|
||||||
|
src: ['tests/**/*.js']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sass: {
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
outputStyle: 'compressed'
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
'dist/css/select2.min.css': [
|
||||||
|
'src/scss/core.scss',
|
||||||
|
'src/scss/theme/default/layout.css'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
options: {
|
||||||
|
outputStyle: 'nested'
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
'dist/css/select2.css': [
|
||||||
|
'src/scss/core.scss',
|
||||||
|
'src/scss/theme/default/layout.css'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
symlink: {
|
||||||
|
docs: {
|
||||||
|
cwd: 'dist',
|
||||||
|
expand: true,
|
||||||
|
overwrite: false,
|
||||||
|
src: [
|
||||||
|
'*'
|
||||||
|
],
|
||||||
|
dest: 'docs/dist',
|
||||||
|
filter: 'isDirectory'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
requirejs: {
|
||||||
|
'dist': {
|
||||||
|
options: {
|
||||||
|
baseUrl: 'src/js',
|
||||||
|
optimize: 'none',
|
||||||
|
name: 'select2/core',
|
||||||
|
out: 'dist/js/select2.js',
|
||||||
|
include: includes,
|
||||||
|
namespace: 'S2',
|
||||||
|
paths: {
|
||||||
|
almond: '../../vendor/almond-0.2.9',
|
||||||
|
jquery: 'jquery.shim'
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
startFile: 'src/js/banner.start.js',
|
||||||
|
endFile: 'src/js/banner.end.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'dist.full': {
|
||||||
|
options: {
|
||||||
|
baseUrl: 'src/js',
|
||||||
|
optimize: 'none',
|
||||||
|
name: 'select2/core',
|
||||||
|
out: 'dist/js/select2.full.js',
|
||||||
|
include: fullIncludes,
|
||||||
|
namespace: 'S2',
|
||||||
|
paths: {
|
||||||
|
almond: '../../vendor/almond-0.2.9',
|
||||||
|
jquery: 'jquery.shim',
|
||||||
|
'jquery.mousewheel': '../../vendor/jquery.mousewheel'
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
startFile: 'src/js/banner.start.js',
|
||||||
|
endFile: 'src/js/banner.end.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'i18n': {
|
||||||
|
options: {
|
||||||
|
baseUrl: 'src/js/select2/i18n',
|
||||||
|
dir: 'dist/js/i18n',
|
||||||
|
paths: i18nPaths,
|
||||||
|
modules: i18nModules,
|
||||||
|
namespace: 'S2',
|
||||||
|
wrap: {
|
||||||
|
start: minifiedBanner + grunt.file.read('src/js/banner.start.js'),
|
||||||
|
end: grunt.file.read('src/js/banner.end.js')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
js: {
|
||||||
|
files: [
|
||||||
|
'src/js/select2/**/*.js',
|
||||||
|
'tests/**/*.js'
|
||||||
|
],
|
||||||
|
tasks: [
|
||||||
|
'compile',
|
||||||
|
'test',
|
||||||
|
'minify'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
files: [
|
||||||
|
'src/scss/**/*.scss'
|
||||||
|
],
|
||||||
|
tasks: [
|
||||||
|
'compile',
|
||||||
|
'minify'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-qunit');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-symlink');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-gh-pages');
|
||||||
|
grunt.loadNpmTasks('grunt-jekyll');
|
||||||
|
grunt.loadNpmTasks('grunt-saucelabs');
|
||||||
|
grunt.loadNpmTasks('grunt-sass');
|
||||||
|
|
||||||
|
grunt.registerTask('default', ['compile', 'test', 'minify']);
|
||||||
|
|
||||||
|
grunt.registerTask('compile', [
|
||||||
|
'requirejs:dist', 'requirejs:dist.full', 'requirejs:i18n',
|
||||||
|
'concat:dist', 'concat:dist.full',
|
||||||
|
'sass:dev'
|
||||||
|
]);
|
||||||
|
grunt.registerTask('minify', ['uglify', 'sass:dist']);
|
||||||
|
grunt.registerTask('test', ['connect:tests', 'qunit', 'jshint']);
|
||||||
|
|
||||||
|
var ciTasks = [];
|
||||||
|
|
||||||
|
ciTasks.push('compile')
|
||||||
|
ciTasks.push('connect:tests');
|
||||||
|
|
||||||
|
// Can't run Sauce Labs tests in pull requests
|
||||||
|
if (process.env.TRAVIS_PULL_REQUEST == 'false') {
|
||||||
|
ciTasks.push('saucelabs-qunit');
|
||||||
|
}
|
||||||
|
|
||||||
|
ciTasks.push('qunit');
|
||||||
|
ciTasks.push('jshint');
|
||||||
|
|
||||||
|
grunt.registerTask('ci', ciTasks);
|
||||||
|
|
||||||
|
grunt.registerTask('docs', ['symlink:docs', 'jekyll:serve']);
|
||||||
|
|
||||||
|
grunt.registerTask('docs-release', ['default', 'clean:docs', 'gh-pages']);
|
||||||
|
};
|
||||||
21
awx/ui/static/lib/select2/LICENSE.md
Normal file
21
awx/ui/static/lib/select2/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
Select2
|
Select2
|
||||||
=======
|
=======
|
||||||
|
[![Build Status][travis-ci-image]][travis-ci-status]
|
||||||
|
|
||||||
Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.
|
Select2 is a jQuery-based replacement for select boxes. It supports searching,
|
||||||
|
remote data sets, and pagination of results.
|
||||||
|
|
||||||
To get started, checkout examples and documentation at http://ivaynberg.github.com/select2
|
To get started, checkout examples and documentation at
|
||||||
|
https://select2.github.io/
|
||||||
|
|
||||||
Use cases
|
Use cases
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* Enhancing native selects with search.
|
* Enhancing native selects with search.
|
||||||
* Enhancing native selects with a better multi-select interface.
|
* Enhancing native selects with a better multi-select interface.
|
||||||
* Loading data from JavaScript: easily load items via ajax and have them searchable.
|
* Loading data from JavaScript: easily load items via AJAX and have them
|
||||||
* Nesting optgroups: native selects only support one level of nested. Select2 does not have this restriction.
|
searchable.
|
||||||
|
* Nesting optgroups: native selects only support one level of nesting. Select2
|
||||||
|
does not have this restriction.
|
||||||
* Tagging: ability to add new items on the fly.
|
* Tagging: ability to add new items on the fly.
|
||||||
* Working with large, remote datasets: ability to partially load a dataset based on the search term.
|
* Working with large, remote datasets: ability to partially load a dataset based
|
||||||
* Paging of large datasets: easy support for loading more pages when the results are scrolled to the end.
|
on the search term.
|
||||||
|
* Paging of large datasets: easy support for loading more pages when the results
|
||||||
|
are scrolled to the end.
|
||||||
* Templating: support for custom rendering of results and selections.
|
* Templating: support for custom rendering of results and selections.
|
||||||
|
|
||||||
Browser compatibility
|
Browser compatibility
|
||||||
@@ -24,76 +30,76 @@ Browser compatibility
|
|||||||
* Firefox 10+
|
* Firefox 10+
|
||||||
* Safari 3+
|
* Safari 3+
|
||||||
* Opera 10.6+
|
* Opera 10.6+
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
You can source Select2 directly from a [CDN like JSDliver](http://www.jsdelivr.com/#!select2), [download it from this GitHub repo](https://github.com/ivaynberg/select2/tags), or use one of the integrations below.
|
You can source Select2 directly from a CDN like [JSDliver][jsdelivr] or
|
||||||
|
[CDNJS][cdnjs], [download it from this GitHub repo][releases], or use one of
|
||||||
|
the integrations below.
|
||||||
|
|
||||||
Integrations
|
Integrations
|
||||||
------------
|
------------
|
||||||
|
* [Wicket-Select2][wicket-select2] (Java / [Apache Wicket][wicket])
|
||||||
* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / [Apache Wicket](http://wicket.apache.org))
|
* [select2-rails][select2-rails] (Ruby on Rails)
|
||||||
* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
|
* [AngularUI][angularui-select] ([AngularJS][angularjs])
|
||||||
* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
|
* [Django][django-select2]
|
||||||
* [Django](https://github.com/applegrew/django-select2)
|
* [Symfony][symfony-select2]
|
||||||
* [Symfony](https://github.com/19Gerhard85/sfSelect2WidgetsPlugin)
|
* [Symfony2][symfony2-select2]
|
||||||
* [Symfony2](https://github.com/avocode/FormExtensions)
|
* [Bootstrap 2][bootstrap2-select2] and [Bootstrap 3][bootstrap3-select2]
|
||||||
* [Bootstrap 2](https://github.com/t0m/select2-bootstrap-css) and [Bootstrap 3](https://github.com/t0m/select2-bootstrap-css/tree/bootstrap3) (CSS skins)
|
(CSS skins)
|
||||||
* [Meteor](https://github.com/nate-strauser/meteor-select2) (modern reactive JavaScript framework; + [Bootstrap 3 skin](https://github.com/esperadomedia/meteor-select2-bootstrap3-css/))
|
* [Meteor][meteor-select2] ([Bootstrap 3 skin][meteor-select2-bootstrap3])
|
||||||
* [Meteor](https://jquery-select2.meteor.com)
|
* [Meteor][meteor-select2-alt]
|
||||||
* [Yii 2.x](http://demos.krajee.com/widgets#select2)
|
* [Yii 2.x][yii2-select2]
|
||||||
* [Yii 1.x](https://github.com/tonybolzan/yii-select2)
|
* [Yii 1.x][yii-select2]
|
||||||
* [AtmosphereJS](https://atmospherejs.com/package/jquery-select2)
|
* [AtmosphereJS][atmospherejs-select2]
|
||||||
|
|
||||||
### Example Integrations
|
|
||||||
|
|
||||||
* [Knockout.js](https://github.com/ivaynberg/select2/wiki/Knockout.js-Integration)
|
|
||||||
* [Socket.IO](https://github.com/ivaynberg/select2/wiki/Socket.IO-Integration)
|
|
||||||
* [PHP](https://github.com/ivaynberg/select2/wiki/PHP-Example)
|
|
||||||
* [.Net MVC] (https://github.com/ivaynberg/select2/wiki/.Net-MVC-Example)
|
|
||||||
|
|
||||||
Internationalization (i18n)
|
Internationalization (i18n)
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Select2 supports multiple languages by simply including the right language JS
|
Select2 supports multiple languages by simply including the right language JS
|
||||||
file (`select2_locale_it.js`, `select2_locale_nl.js`, etc.) after `select2.js`.
|
file (`dist/js/i18n/it.js`, `dist/js/i18n/nl.js`, etc.) after
|
||||||
|
`dist/js/select2.js`.
|
||||||
|
|
||||||
Missing a language? Just copy `select2_locale_en.js.template`, translate
|
Missing a language? Just copy `src/js/select2/i18n/en.js`, translate it, and
|
||||||
it, and make a pull request back to Select2 here on GitHub.
|
make a pull request back to Select2 here on GitHub.
|
||||||
|
|
||||||
Bug tracker
|
Documentation
|
||||||
-----------
|
-------------
|
||||||
|
The documentation for Select2 is available
|
||||||
Have a bug? Please create an issue here on GitHub!
|
[through GitHub Pages][documentation] and is located within this repository
|
||||||
|
in the [`docs` folder][documentation-folder].
|
||||||
https://github.com/ivaynberg/select2/issues
|
|
||||||
|
|
||||||
Mailing list
|
|
||||||
------------
|
|
||||||
|
|
||||||
Have a question? Ask on our mailing list!
|
|
||||||
|
|
||||||
select2@googlegroups.com
|
|
||||||
|
|
||||||
https://groups.google.com/d/forum/select2
|
|
||||||
|
|
||||||
|
Community
|
||||||
|
---------
|
||||||
|
You can find out about the different ways to get in touch with the Select2
|
||||||
|
community at the [Select2 community page][community].
|
||||||
|
|
||||||
Copyright and license
|
Copyright and license
|
||||||
---------------------
|
---------------------
|
||||||
|
The license is available within the repository in the [LICENSE][license] file.
|
||||||
|
|
||||||
Copyright 2012 Igor Vaynberg
|
[angularjs]: https://angularjs.org/
|
||||||
|
[angularui-select]: http://angular-ui.github.io/#ui-select
|
||||||
This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
|
[atmospherejs-select2]: https://atmospherejs.com/package/jquery-select2
|
||||||
General Public License version 2 (the "GPL License"). You may choose either license to govern your
|
[bootstrap2-select2]: https://github.com/t0m/select2-bootstrap-css
|
||||||
use of this software only upon the condition that you accept all of the terms of either the Apache
|
[bootstrap3-select2]: https://github.com/t0m/select2-bootstrap-css/tree/bootstrap3
|
||||||
License or the GPL License.
|
[cdnjs]: http://www.cdnjs.com/libraries/select2
|
||||||
|
[community]: https://select2.github.io/community.html
|
||||||
You may obtain a copy of the Apache License and the GPL License in the LICENSE file, or at:
|
[django-select2]: https://github.com/applegrew/django-select2
|
||||||
|
[documentation]: https://select2.github.io/
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
[documentation-folder]: https://github.com/select2/select2/tree/master/docs
|
||||||
http://www.gnu.org/licenses/gpl-2.0.html
|
[freenode]: https://freenode.net/
|
||||||
|
[jsdelivr]: http://www.jsdelivr.com/#!select2
|
||||||
Unless required by applicable law or agreed to in writing, software distributed under the Apache License
|
[license]: LICENSE.md
|
||||||
or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
[meteor-select2]: https://github.com/nate-strauser/meteor-select2
|
||||||
either express or implied. See the Apache License and the GPL License for the specific language governing
|
[meteor-select2-alt]: https://jquery-select2.meteor.com
|
||||||
permissions and limitations under the Apache License and the GPL License.
|
[meteor-select2-bootstrap3]: https://github.com/zimme/meteor-select2-bootstrap3-css/
|
||||||
|
[releases]: https://github.com/select2/select2/releases
|
||||||
|
[select2-rails]: https://github.com/argerim/select2-rails
|
||||||
|
[symfony-select2]: https://github.com/19Gerhard85/sfSelect2WidgetsPlugin
|
||||||
|
[symfony2-select2]: https://github.com/avocode/FormExtensions
|
||||||
|
[travis-ci-image]: https://travis-ci.org/select2/select2.svg?branch=master
|
||||||
|
[travis-ci-status]: https://travis-ci.org/select2/select2
|
||||||
|
[wicket]: http://wicket.apache.org
|
||||||
|
[wicket-select2]: https://github.com/ivaynberg/wicket-select2
|
||||||
|
[yii-select2]: https://github.com/tonybolzan/yii-select2
|
||||||
|
[yii2-select2]: http://demos.krajee.com/widgets#select2
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "select2",
|
"name": "select2",
|
||||||
"version": "3.5.1",
|
"description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
|
||||||
"main": ["select2.js", "select2.css", "select2.png", "select2x2.png", "select2-spinner.gif"],
|
"main": [
|
||||||
"dependencies": {
|
"dist/js/select2.js",
|
||||||
"jquery": ">= 1.7.1"
|
"dist/css/select2.css"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@github.com:select2/select2.git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "select2",
|
"name": "select2",
|
||||||
"repo": "ivaynberg/select2",
|
"repo": "select/select2",
|
||||||
"description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
|
"description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
|
||||||
"version": "3.5.1",
|
"version": "4.0.0-rc.2",
|
||||||
"demo": "http://ivaynberg.github.io/select2/",
|
"demo": "https://select2.github.io/",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"jquery"
|
"jquery"
|
||||||
],
|
],
|
||||||
"main": "select2.js",
|
"main": "dist/js/select2.js",
|
||||||
"styles": [
|
"styles": [
|
||||||
"select2.css",
|
"dist/css/select2.css"
|
||||||
"select2-bootstrap.css"
|
|
||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"select2.js",
|
"dist/js/select2.js",
|
||||||
"select2_locale_ar.js",
|
"dist/js/i18n/*.js"
|
||||||
"select2_locale_bg.js",
|
|
||||||
"select2_locale_ca.js",
|
|
||||||
"select2_locale_cs.js",
|
|
||||||
"select2_locale_da.js",
|
|
||||||
"select2_locale_de.js",
|
|
||||||
"select2_locale_el.js",
|
|
||||||
"select2_locale_es.js",
|
|
||||||
"select2_locale_et.js",
|
|
||||||
"select2_locale_eu.js",
|
|
||||||
"select2_locale_fa.js",
|
|
||||||
"select2_locale_fi.js",
|
|
||||||
"select2_locale_fr.js",
|
|
||||||
"select2_locale_gl.js",
|
|
||||||
"select2_locale_he.js",
|
|
||||||
"select2_locale_hr.js",
|
|
||||||
"select2_locale_hu.js",
|
|
||||||
"select2_locale_id.js",
|
|
||||||
"select2_locale_is.js",
|
|
||||||
"select2_locale_it.js",
|
|
||||||
"select2_locale_ja.js",
|
|
||||||
"select2_locale_ka.js",
|
|
||||||
"select2_locale_ko.js",
|
|
||||||
"select2_locale_lt.js",
|
|
||||||
"select2_locale_lv.js",
|
|
||||||
"select2_locale_mk.js",
|
|
||||||
"select2_locale_ms.js",
|
|
||||||
"select2_locale_nl.js",
|
|
||||||
"select2_locale_no.js",
|
|
||||||
"select2_locale_pl.js",
|
|
||||||
"select2_locale_pt-BR.js",
|
|
||||||
"select2_locale_pt-PT.js",
|
|
||||||
"select2_locale_ro.js",
|
|
||||||
"select2_locale_ru.js",
|
|
||||||
"select2_locale_sk.js",
|
|
||||||
"select2_locale_sv.js",
|
|
||||||
"select2_locale_th.js",
|
|
||||||
"select2_locale_tr.js",
|
|
||||||
"select2_locale_uk.js",
|
|
||||||
"select2_locale_vi.js",
|
|
||||||
"select2_locale_zh-CN.js",
|
|
||||||
"select2_locale_zh-TW.js"
|
|
||||||
],
|
|
||||||
"images": [
|
|
||||||
"select2-spinner.gif",
|
|
||||||
"select2.png",
|
|
||||||
"select2x2.png"
|
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name":
|
"name": "select2/select2",
|
||||||
"ivaynberg/select2",
|
|
||||||
"description": "Select2 is a jQuery based replacement for select boxes.",
|
"description": "Select2 is a jQuery based replacement for select boxes.",
|
||||||
"version": "3.5.1",
|
|
||||||
"type": "component",
|
"type": "component",
|
||||||
"homepage": "http://ivaynberg.github.io/select2/",
|
"homepage": "https://select2.github.io/",
|
||||||
"license": "Apache-2.0",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"robloach/component-installer": "*",
|
"robloach/component-installer": "*"
|
||||||
"components/jquery": ">=1.7.1"
|
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"component": {
|
"component": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"select2.js"
|
"dist/js/select2.js"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"select2.js",
|
"dist/js/select2.js",
|
||||||
"select2_locale_*.js",
|
"dist/js/i18n/*.js",
|
||||||
"select2.css",
|
"dist/css/select2.css"
|
||||||
"select2-bootstrap.css",
|
|
||||||
"select2-spinner.gif",
|
|
||||||
"select2.png",
|
|
||||||
"select2x2.png"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
557
awx/ui/static/lib/select2/dist/css/select2-bootstrap.css
vendored
Normal file
557
awx/ui/static/lib/select2/dist/css/select2-bootstrap.css
vendored
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
/*! Select2 Bootstrap Theme v0.1.0-beta.1 | MIT License | github.com/fk/select2-bootstrap-theme */
|
||||||
|
.select2-container--bootstrap {
|
||||||
|
display: block;
|
||||||
|
/*------------------------------------*\
|
||||||
|
#COMMON STYLES
|
||||||
|
\*------------------------------------*/
|
||||||
|
/**
|
||||||
|
* Search field in the Select2 dropdown.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* No outline for all search fields - in the dropdown
|
||||||
|
* and inline in multi Select2s.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Adjust Select2's choices hover and selected styles to match
|
||||||
|
* Bootstrap 3's default dropdown styles.
|
||||||
|
*
|
||||||
|
* @see http://getbootstrap.com/components/#dropdowns
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Address disabled Select2 styles.
|
||||||
|
*
|
||||||
|
* @see https://select2.github.io/examples.html#disabled
|
||||||
|
* @see http://getbootstrap.com/css/#forms-control-disabled
|
||||||
|
*/
|
||||||
|
/*------------------------------------*\
|
||||||
|
#DROPDOWN
|
||||||
|
\*------------------------------------*/
|
||||||
|
/**
|
||||||
|
* Dropdown border color and box-shadow.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Limit the dropdown height.
|
||||||
|
*/
|
||||||
|
/*------------------------------------*\
|
||||||
|
#SINGLE SELECT2
|
||||||
|
\*------------------------------------*/
|
||||||
|
/*------------------------------------*\
|
||||||
|
#MULTIPLE SELECT2
|
||||||
|
\*------------------------------------*/
|
||||||
|
/**
|
||||||
|
* Address Bootstrap control sizing classes
|
||||||
|
*
|
||||||
|
* 1. Reset Bootstrap defaults.
|
||||||
|
* 2. Adjust the dropdown arrow button icon position.
|
||||||
|
*
|
||||||
|
* @see http://getbootstrap.com/css/#forms-control-sizes
|
||||||
|
*/
|
||||||
|
/* 1 */
|
||||||
|
/*------------------------------------*\
|
||||||
|
#RTL SUPPORT
|
||||||
|
\*------------------------------------*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #555555;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search--dropdown .select2-search__field {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #555555;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search__field {
|
||||||
|
outline: 0;
|
||||||
|
/* Firefox 18- */
|
||||||
|
/**
|
||||||
|
* Firefox 19+
|
||||||
|
*
|
||||||
|
* @see http://stackoverflow.com/questions/24236240/color-for-styled-placeholder-text-is-muted-in-firefox
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search__field::-webkit-input-placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search__field:-moz-placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search__field::-moz-placeholder {
|
||||||
|
color: #999;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-search__field:-ms-input-placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option {
|
||||||
|
/**
|
||||||
|
* Disabled results.
|
||||||
|
*
|
||||||
|
* @see https://select2.github.io/examples.html#disabled-results
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Hover state.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Selected state.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option[role=group] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option[aria-disabled=true] {
|
||||||
|
color: #777777;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #337ab7;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option {
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__group {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -12px;
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -24px;
|
||||||
|
padding-left: 36px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -36px;
|
||||||
|
padding-left: 48px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -48px;
|
||||||
|
padding-left: 60px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -60px;
|
||||||
|
padding-left: 72px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results__group {
|
||||||
|
color: #777777;
|
||||||
|
display: block;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--open {
|
||||||
|
/**
|
||||||
|
* Handle border radii of the container when the dropdown is showing.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--open .select2-selection {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
border-color: #66afe9;
|
||||||
|
/**
|
||||||
|
* Make the dropdown arrow point up while the dropdown is visible.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--open .select2-selection .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #999 transparent;
|
||||||
|
border-width: 0 4px 4px 4px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--open.select2-container--below .select2-selection {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--open.select2-container--above .select2-selection {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-color: transparent;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-selection,
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-search__field {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-selection,
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-selection__clear,
|
||||||
|
.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-dropdown {
|
||||||
|
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||||
|
border-color: #66afe9;
|
||||||
|
overflow-x: hidden;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-dropdown--above {
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
padding: 6px 24px 6px 12px;
|
||||||
|
/**
|
||||||
|
* Clear the selection.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Adjust the single Select2's dropdown arrow button appearance.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__clear {
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__clear:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__arrow {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 12px;
|
||||||
|
top: 0;
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #999 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 4px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #555555;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple {
|
||||||
|
min-height: 34px;
|
||||||
|
/**
|
||||||
|
* Make Multi Select2's choices match Bootstrap 3's default button styles.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Minus 2px borders.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-selection__placeholder {
|
||||||
|
color: #999;
|
||||||
|
float: left;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice {
|
||||||
|
color: #555555;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin: 5px 0 0 6px;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field {
|
||||||
|
background: transparent;
|
||||||
|
padding: 0 12px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
margin-top: 0;
|
||||||
|
min-width: 5em;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm, .select2-container--bootstrap.input-lg {
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
height: auto;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm .select2-selection--single, .input-group-sm .select2-container--bootstrap .select2-selection--single, .form-group-sm .select2-container--bootstrap .select2-selection--single {
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 5px 22px 5px 10px;
|
||||||
|
/* 2 */
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm .select2-selection--single .select2-selection__arrow b, .input-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, .form-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b {
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm .select2-selection--multiple, .input-group-sm .select2-container--bootstrap .select2-selection--multiple, .form-group-sm .select2-container--bootstrap .select2-selection--multiple {
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm .select2-selection--multiple .select2-selection__choice, .input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, .form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 4px 0 0 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-sm .select2-selection--multiple .select2-search--inline .select2-search__field, .input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, .form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field {
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--single, .input-group-lg .select2-container--bootstrap .select2-selection--single, .form-group-lg .select2-container--bootstrap .select2-selection--single {
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 18px;
|
||||||
|
height: 46px;
|
||||||
|
line-height: 1.3333333;
|
||||||
|
padding: 10px 31px 10px 16px;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--single .select2-selection__arrow, .input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow, .form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow {
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--single .select2-selection__arrow b, .input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, .form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-width: 5px 5px 0 5px;
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-top: -2.5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--multiple, .input-group-lg .select2-container--bootstrap .select2-selection--multiple, .form-group-lg .select2-container--bootstrap .select2-selection--multiple {
|
||||||
|
min-height: 46px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--multiple .select2-selection__choice, .input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, .form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.3333333;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 9px 0 0 8px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg .select2-selection--multiple .select2-search--inline .select2-search__field, .input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, .form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field {
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 1.3333333;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg.select2-container--open .select2-selection--single {
|
||||||
|
/**
|
||||||
|
* Make the dropdown arrow point up while the dropdown is visible.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap.input-lg.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #999 transparent;
|
||||||
|
border-width: 0 5px 5px 5px;
|
||||||
|
}
|
||||||
|
.input-group-lg .select2-container--bootstrap.select2-container--open .select2-selection--single {
|
||||||
|
/**
|
||||||
|
* Make the dropdown arrow point up while the dropdown is visible.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.input-group-lg .select2-container--bootstrap.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #999 transparent;
|
||||||
|
border-width: 0 5px 5px 5px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] {
|
||||||
|
/**
|
||||||
|
* Single Select2
|
||||||
|
*
|
||||||
|
* 1. Makes sure that .select2-selection__placeholder is positioned
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Multiple Select2
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--single {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
text-align: right;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
left: 12px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow b {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice,
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------*\
|
||||||
|
#ADDITIONAL GOODIES
|
||||||
|
\*------------------------------------*/
|
||||||
|
/**
|
||||||
|
* Address Bootstrap's validation states
|
||||||
|
*
|
||||||
|
* If a Select2 widget parent has one of Bootstrap's validation state modifier
|
||||||
|
* classes, adjust Select2's border colors and focus states accordingly.
|
||||||
|
* You may apply said classes to the Select2 dropdown (body > .select2-container)
|
||||||
|
* via JavaScript match Bootstraps' to make its styles match.
|
||||||
|
*
|
||||||
|
* @see http://getbootstrap.com/css/#forms-control-validation
|
||||||
|
*/
|
||||||
|
.has-warning .select2-dropdown,
|
||||||
|
.has-warning .select2-selection {
|
||||||
|
border-color: #8a6d3b;
|
||||||
|
}
|
||||||
|
.has-warning .select2-container--open .select2-selection {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
|
||||||
|
border-color: #66512c;
|
||||||
|
}
|
||||||
|
.has-warning.select2-drop-active {
|
||||||
|
border-color: #66512c;
|
||||||
|
}
|
||||||
|
.has-warning.select2-drop-active.select2-drop.select2-drop-above {
|
||||||
|
border-top-color: #66512c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-error .select2-dropdown,
|
||||||
|
.has-error .select2-selection {
|
||||||
|
border-color: #a94442;
|
||||||
|
}
|
||||||
|
.has-error .select2-container--open .select2-selection {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||||
|
border-color: #843534;
|
||||||
|
}
|
||||||
|
.has-error.select2-drop-active {
|
||||||
|
border-color: #843534;
|
||||||
|
}
|
||||||
|
.has-error.select2-drop-active.select2-drop.select2-drop-above {
|
||||||
|
border-top-color: #843534;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-success .select2-dropdown,
|
||||||
|
.has-success .select2-selection {
|
||||||
|
border-color: #3c763d;
|
||||||
|
}
|
||||||
|
.has-success .select2-container--open .select2-selection {
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
|
||||||
|
border-color: #2b542c;
|
||||||
|
}
|
||||||
|
.has-success.select2-drop-active {
|
||||||
|
border-color: #2b542c;
|
||||||
|
}
|
||||||
|
.has-success.select2-drop-active.select2-drop.select2-drop-above {
|
||||||
|
border-top-color: #2b542c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select2 widgets in Bootstrap Input Groups
|
||||||
|
*
|
||||||
|
* When Select2 widgets are combined with other elements using Bootstraps
|
||||||
|
* "Input Group" component, we don't want specific edges of the Select2
|
||||||
|
* container to have a border-radius.
|
||||||
|
*
|
||||||
|
* Use .select2-bootstrap-prepend and .select2-bootstrap-append on
|
||||||
|
* a Bootstrap 3 .input-group to let the contained Select2 widget know which
|
||||||
|
* edges should not be rounded as they are directly followed by another element.
|
||||||
|
*
|
||||||
|
* @see http://getbootstrap.com/components/#input-groups
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Mimick Bootstraps .input-group .form-control styles.
|
||||||
|
*
|
||||||
|
* @see https://github.com/twbs/bootstrap/blob/master/less/input-groups.less
|
||||||
|
*/
|
||||||
|
.input-group .select2-container--bootstrap {
|
||||||
|
display: table;
|
||||||
|
table-layout: fixed;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group.select2-bootstrap-prepend .select2-container--bootstrap .select2-selection {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group.select2-bootstrap-append .select2-container--bootstrap .select2-selection {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust alignment of Bootstrap buttons in Bootstrap Input Groups to address
|
||||||
|
* Multi Select2's height which - depending on how many elements have been selected -
|
||||||
|
* may grown higher than their initial size.
|
||||||
|
*
|
||||||
|
* @see http://getbootstrap.com/components/#input-groups
|
||||||
|
*/
|
||||||
|
.select2-bootstrap-append .select2-container--bootstrap,
|
||||||
|
.select2-bootstrap-append .input-group-btn,
|
||||||
|
.select2-bootstrap-append .input-group-btn .btn,
|
||||||
|
.select2-bootstrap-prepend .select2-container--bootstrap,
|
||||||
|
.select2-bootstrap-prepend .input-group-btn,
|
||||||
|
.select2-bootstrap-prepend .input-group-btn .btn {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
421
awx/ui/static/lib/select2/dist/css/select2.css
vendored
Normal file
421
awx/ui/static/lib/select2/dist/css/select2.css
vendored
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
.select2-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle; }
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
height: 28px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||||
|
display: block;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 20px; }
|
||||||
|
.select2-container .select2-selection--multiple {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
min-height: 32px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-container .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 8px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
.select2-container .select2-search--inline {
|
||||||
|
float: left; }
|
||||||
|
.select2-container .select2-search--inline .select2-search__field {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
font-size: 100%;
|
||||||
|
margin-top: 5px; }
|
||||||
|
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none; }
|
||||||
|
|
||||||
|
.select2-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -100000px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1051; }
|
||||||
|
|
||||||
|
.select2-results {
|
||||||
|
display: block; }
|
||||||
|
|
||||||
|
.select2-results__options {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
.select2-results__option {
|
||||||
|
padding: 6px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-results__option[aria-selected] {
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown {
|
||||||
|
left: 0; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--above {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--below {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-search--dropdown {
|
||||||
|
display: block;
|
||||||
|
padding: 4px; }
|
||||||
|
.select2-search--dropdown .select2-search__field {
|
||||||
|
padding: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box; }
|
||||||
|
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none; }
|
||||||
|
.select2-search--dropdown.select2-search--hide {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.select2-close-mask {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: #fff;
|
||||||
|
filter: alpha(opacity=0); }
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0; }
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left; }
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
left: 1px;
|
||||||
|
right: auto; }
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default; }
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
|
||||||
|
display: none; }
|
||||||
|
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px; }
|
||||||
|
.select2-container--default .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
width: 100%; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
|
||||||
|
color: #999;
|
||||||
|
margin-top: 5px;
|
||||||
|
float: left; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 10px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #333; }
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder {
|
||||||
|
float: right; }
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto; }
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto; }
|
||||||
|
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||||
|
border: solid black 1px;
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--multiple {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default; }
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
|
||||||
|
display: none; }
|
||||||
|
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
.select2-container--default .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa; }
|
||||||
|
.select2-container--default .select2-search--inline .select2-search__field {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--default .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto; }
|
||||||
|
.select2-container--default .select2-results__option[role=group] {
|
||||||
|
padding: 0; }
|
||||||
|
.select2-container--default .select2-results__option[aria-disabled=true] {
|
||||||
|
color: #999; }
|
||||||
|
.select2-container--default .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: #ddd; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option {
|
||||||
|
padding-left: 1em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
|
||||||
|
padding-left: 0; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -1em;
|
||||||
|
padding-left: 2em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -2em;
|
||||||
|
padding-left: 3em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -3em;
|
||||||
|
padding-left: 4em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -4em;
|
||||||
|
padding-left: 5em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -5em;
|
||||||
|
padding-left: 6em; }
|
||||||
|
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #5897fb;
|
||||||
|
color: white; }
|
||||||
|
.select2-container--default .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); }
|
||||||
|
.select2-container--classic .select2-selection--single:focus {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow {
|
||||||
|
background-color: #ddd;
|
||||||
|
border: none;
|
||||||
|
border-left: 1px solid #aaa;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0); }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0; }
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left; }
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid #aaa;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
left: 1px;
|
||||||
|
right: auto; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
|
||||||
|
background: transparent;
|
||||||
|
border: none; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px; }
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
|
||||||
|
background-image: -o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
|
||||||
|
background-image: linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); }
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); }
|
||||||
|
.select2-container--classic .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--classic .select2-selection--multiple:focus {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
|
||||||
|
display: none; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #555; }
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
float: right; }
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto; }
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--multiple {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
.select2-container--classic .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--classic .select2-search--inline .select2-search__field {
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--classic .select2-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid transparent; }
|
||||||
|
.select2-container--classic .select2-dropdown--above {
|
||||||
|
border-bottom: none; }
|
||||||
|
.select2-container--classic .select2-dropdown--below {
|
||||||
|
border-top: none; }
|
||||||
|
.select2-container--classic .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto; }
|
||||||
|
.select2-container--classic .select2-results__option[role=group] {
|
||||||
|
padding: 0; }
|
||||||
|
.select2-container--classic .select2-results__option[aria-disabled=true] {
|
||||||
|
color: grey; }
|
||||||
|
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #3875d7;
|
||||||
|
color: white; }
|
||||||
|
.select2-container--classic .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-dropdown {
|
||||||
|
border-color: #5897fb; }
|
||||||
1
awx/ui/static/lib/select2/dist/css/select2.min.css
vendored
Normal file
1
awx/ui/static/lib/select2/dist/css/select2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
3
awx/ui/static/lib/select2/dist/js/i18n/az.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/az.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/bg.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/bg.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/ca.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/ca.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/cs.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/cs.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/da.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/da.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Angiv venligst "+t+" tegn mindre";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Angiv venligst "+t+" tegn mere";return n},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/de.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/de.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/de",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Bitte "+t+" Zeichen weniger eingeben"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Bitte "+t+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:function(e){var t="Sie können nur "+e.maximum+" Eintr";return e.maximum===1?t+="ag":t+="äge",t+=" auswählen",t},noResults:function(){return"Keine Übereinstimmungen gefunden"},searching:function(){return"Suche…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/en.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/en.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Please enter "+t+" or more characters";return n},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/es.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/es.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"La carga falló"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor, elimine "+t+" car";return t==1?n+="ácter":n+="acteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Por favor, introduzca "+t+" car";return t==1?n+="ácter":n+="acteres",n},loadingMore:function(){return"Cargando más resultados…"},maximumSelected:function(e){var t="Sólo puede seleccionar "+e.maximum+" elemento";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No se encontraron resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/et.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/et.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" vähem",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" rohkem",n},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var t="Saad vaid "+e.maximum+" tulemus";return e.maximum==1?t+="e":t+="t",t+=" valida",t},noResults:function(){return"Tulemused puuduvad"},searching:function(){return"Otsin…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/eu.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/eu.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gutxiago",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gehiago",n},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:function(e){return e.maximum===1?"Elementu bakarra hauta dezakezu":e.maximum+" elementu hauta ditzakezu soilik"},noResults:function(){return"Ez da bat datorrenik aurkitu"},searching:function(){return"Bilatzen…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/fa.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/fa.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="لطفاً "+t+" کاراکتر را حذف نمایید";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="لطفاً تعداد "+t+" کاراکتر یا بیشتر وارد نمایید";return n},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelected:function(e){var t="شما تنها میتوانید "+e.maximum+" آیتم را انتخاب نمایید";return t},noResults:function(){return"هیچ نتیجهای یافت نشد"},searching:function(){return"در حال جستجو..."}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/fi.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/fi.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Ole hyvä ja anna "+t+" merkkiä vähemmän"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Ole hyvä ja anna "+t+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(e){return"Voit valita ainoastaan "+e.maximum+" kpl"},noResults:function(){return"Ei tuloksia"},searching:function(){}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/fr.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/fr.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/gl.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/gl.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/gl",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Engada ";return t===1?n+="un carácter":n+=t+" caracteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Elimine ";return t===1?n+="un carácter":n+=t+" caracteres",n},loadingMore:function(){return"Cargando máis resultados…"},maximumSelected:function(e){var t="Só pode ";return e.maximum===1?t+="un elemento":t+=e.maximum+" elementos",t},noResults:function(){return"Non se atoparon resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/hi.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/hi.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" अक्षर को हटा दें";return t>1&&(n=t+" अक्षरों को हटा दें "),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="कृपया "+t+" या अधिक अक्षर दर्ज करें";return n},loadingMore:function(){return"अधिक परिणाम लोड हो रहे है..."},maximumSelected:function(e){var t="आप केवल "+e.maximum+" आइटम का चयन कर सकते हैं";return t},noResults:function(){return"कोई परिणाम नहीं मिला"},searching:function(){return"खोज रहा है..."}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/hr.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/hr.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hr",[],function(){function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100>19)?e%10>1&&(t+="a"):t+="ova",t}return{inputTooLong:function(t){var n=t.input.length-t.maximum;return"Unesite "+e(n)},inputTooShort:function(t){var n=t.minimum-t.input.length;return"Unesite još "+e(n)},loadingMore:function(){return"Učitavanje rezultata…"},maximumSelected:function(e){return"Maksimalan broj odabranih stavki je "+e.maximum},noResults:function(){return"Nema rezultata"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/hu.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/hu.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Túl hosszú. "+t+" karakterrel több, mint kellene."},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Túl rövid. Még "+t+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:function(e){return"Csak "+e.maximum+" elemet lehet kiválasztani."},noResults:function(){return"Nincs találat."},searching:function(){return"Keresés…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/id.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/id.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/id",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Hapuskan "+t+" huruf"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Masukkan "+t+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(e){return"Anda hanya dapat memilih "+e.maximum+" pilihan"},noResults:function(){return"Tidak ada data yang sesuai"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/is.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/is.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/is",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vinsamlegast styttið texta um "+t+" staf";return t<=1?n:n+"i"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vinsamlegast skrifið "+t+" staf";return t>1&&(n+="i"),n+=" í viðbót",n},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(e){return"Þú getur aðeins valið "+e.maximum+" atriði"},noResults:function(){return"Ekkert fannst"},searching:function(){return"Leita…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/it.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/it.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Per favore cancella "+t+" caratter";return t!==1?n+="i":n+="e",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Per favore inserisci "+t+" o più caratteri";return n},loadingMore:function(){return"Caricando più risultati…"},maximumSelected:function(e){var t="Puoi selezionare solo "+e.maximum+" element";return e.maximum!==1?t+="i":t+="o",t},noResults:function(){return"Nessun risultato trovato"},searching:function(){return"Sto cercando…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/ko.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/ko.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="너무 깁니다. "+t+" 글자 지워주세요.";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="너무 짧습니다. "+t+" 글자 더 입력해주세요.";return n},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(e){var t="최대 "+e.maximum+"개까지만 선택 가능합니다.";return t},noResults:function(){return"결과가 없습니다."},searching:function(){return"검색 중…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/lt.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/lt.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lt",[],function(){function e(e,t,n,r){return e%100>9&&e%100<21||e%10===0?e%10>1?n:r:t}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Pašalinkite "+n+" simbol";return r+=e(n,"ių","ius","į"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Įrašykite dar "+n+" simbol";return r+=e(n,"ių","ius","į"),r},loadingMore:function(){return"Kraunama daugiau rezultatų…"},maximumSelected:function(t){var n="Jūs galite pasirinkti tik "+t.maximum+" element";return n+=e(t.maximum,"ų","us","ą"),n},noResults:function(){return"Atitikmenų nerasta"},searching:function(){return"Ieškoma…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/lv.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/lv.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lv",[],function(){function e(e,t,n,r){return e===11?t:e%10===1?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Lūdzu ievadiet par "+n;return r+=" simbol"+e(n,"iem","u","iem"),r+" mazāk"},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Lūdzu ievadiet vēl "+n;return r+=" simbol"+e(n,"us","u","us"),r},loadingMore:function(){return"Datu ielāde…"},maximumSelected:function(t){var n="Jūs varat izvēlēties ne vairāk kā "+t.maximum;return n+=" element"+e(t.maximum,"us","u","us"),n},noResults:function(){return"Sakritību nav"},searching:function(){return"Meklēšana…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/mk.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/mk.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/mk",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Ве молиме внесете "+e.maximum+" помалку карактер";return e.maximum!==1&&(n+="и"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Ве молиме внесете уште "+e.maximum+" карактер";return e.maximum!==1&&(n+="и"),n},loadingMore:function(){return"Вчитување резултати…"},maximumSelected:function(e){var t="Можете да изберете само "+e.maximum+" ставк";return e.maximum===1?t+="а":t+="и",t},noResults:function(){return"Нема пронајдено совпаѓања"},searching:function(){return"Пребарување…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/nb.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/nb.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nb",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Vennligst fjern "+t+" tegn"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vennligst skriv inn ";return t>1?n+=" flere tegn":n+=" tegn til",n},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan velge maks "+e.maximum+" elementer"},noResults:function(){return"Ingen treff"},searching:function(){return"Søker…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/nl.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/nl.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Gelieve "+t+" karakters te verwijderen";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Gelieve "+t+" of meer karakters in te voeren";return n},loadingMore:function(){return"Meer resultaten laden…"},maximumSelected:function(e){var t="Er kunnen maar "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t+=" worden geselecteerd",t},noResults:function(){return"Geen resultaten gevonden…"},searching:function(){return"Zoeken…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/pl.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/pl.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pl",[],function(){var e=["znak","znaki","znaków"],t=["element","elementy","elementów"],n=function(t,n){if(t===1)return n[0];if(t>1&&t<=4)return n[1];if(t>=5)return n[2]};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(t){var r=t.input.length-t.maximum;return"Usuń "+r+" "+n(r,e)},inputTooShort:function(t){var r=t.minimum-t.input.length;return"Podaj przynajmniej "+r+" "+n(r,e)},loadingMore:function(){return"Trwa ładowanie…"},maximumSelected:function(e){return"Możesz zaznaczyć tylko "+e.maximum+" "+n(e.maxiumum,t)},noResults:function(){return"Brak wyników"},searching:function(){return"Trwa wyszukiwanie…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/pt-BR.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/pt-BR.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Apague "+t+" caracter";return t!=1&&(n+="es"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Digite "+t+" ou mais caracteres";return n},loadingMore:function(){return"Carregando mais resultados…"},maximumSelected:function(e){var t="Você só pode selecionar "+e.maximum+" ite";return e.maximum==1?t+="m":t+="ns",t},noResults:function(){return"Nenhum resultado encontrado"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();
|
||||||
3
awx/ui/static/lib/select2/dist/js/i18n/pt.js
vendored
Normal file
3
awx/ui/static/lib/select2/dist/js/i18n/pt.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/*! Select2 4.0.0-rc.2 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||||
|
|
||||||
|
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor apague "+t+" ";return n+=t!=1?"caracteres":"carácter",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Introduza "+t+" ou mais caracteres";return n},loadingMore:function(){return"A carregar mais resultados…"},maximumSelected:function(e){var t="Apenas pode seleccionar "+e.maximum+" ";return t+=e.maximum!=1?"itens":"item",t},noResults:function(){return"Sem resultados"},searching:function(){return"A procurar…"}}}),{define:e.define,require:e.require}})();
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user