Merge branch 'master' into licenses-unstable
@ -33,6 +33,7 @@ from polymorphic import PolymorphicModel
|
||||
from awx.main.constants import SCHEDULEABLE_PROVIDERS
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.utils import get_type_for_model, get_model_for_type
|
||||
from awx.main.redact import REPLACE_STR
|
||||
|
||||
logger = logging.getLogger('awx.api.serializers')
|
||||
|
||||
@ -1419,6 +1420,17 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
|
||||
return ret
|
||||
if 'job_template' in ret and (not obj.job_template or not obj.job_template.active):
|
||||
ret['job_template'] = None
|
||||
|
||||
if obj.job_template and obj.job_template.survey_enabled:
|
||||
if ret['extra_vars']:
|
||||
try:
|
||||
extra_vars = json.loads(ret['extra_vars'])
|
||||
for key in obj.job_template.survey_password_variables():
|
||||
if key in extra_vars:
|
||||
extra_vars[key] = REPLACE_STR
|
||||
ret['extra_vars'] = json.dumps(extra_vars)
|
||||
except ValueError:
|
||||
pass
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
50
awx/api/templates/api/stdout.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% if content_only %}<div class="nocode ansi_fore ansi_back{% if dark %} ansi_dark{% endif %}">{% else %}
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>{{ title }}</title>
|
||||
{% endif %}<style type="text/css">
|
||||
.ansi_fore { color: #000000; }
|
||||
.ansi_back { background-color: #F5F5F5; }
|
||||
.ansi_fore.ansi_dark { color: #AAAAAA; }
|
||||
.ansi_back.ansi_dark { background-color: #000000; }
|
||||
.ansi1 { font-weight: bold; }
|
||||
.ansi3 { font-weight: italic; }
|
||||
.ansi4 { text-decoration: underline; }
|
||||
.ansi9 { text-decoration: line-through; }
|
||||
.ansi30 { color: #000316; }
|
||||
.ansi31 { color: #AA0000; }
|
||||
.ansi32 { color: #00AA00; }
|
||||
.ansi33 { color: #AA5500; }
|
||||
.ansi34 { color: #0000AA; }
|
||||
.ansi35 { color: #E850A8; }
|
||||
.ansi36 { color: #00AAAA; }
|
||||
.ansi37 { color: #F5F1DE; }
|
||||
.ansi40 { background-color: #000000; }
|
||||
.ansi41 { background-color: #AA0000; }
|
||||
.ansi42 { background-color: #00AA00; }
|
||||
.ansi43 { background-color: #AA5500; }
|
||||
.ansi44 { background-color: #0000AA; }
|
||||
.ansi45 { background-color: #E850A8; }
|
||||
.ansi46 { background-color: #00AAAA; }
|
||||
.ansi47 { background-color: #F5F1DE; }
|
||||
body.ansi_back pre {
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
div.ansi_back.ansi_dark {
|
||||
padding: 0 8px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>{% if content_only %}{{ body }}
|
||||
</div>
|
||||
{% else %}
|
||||
</head>
|
||||
<body class="ansi_fore ansi_back{% if dark %} ansi_dark{% endif %}">
|
||||
<pre>{{ body }}</pre>
|
||||
</body>
|
||||
</html>
|
||||
{% endif %}
|
||||
@ -17,16 +17,6 @@ Use the `format` query string parameter to specify the output format.
|
||||
formats, the `start_line` and `end_line` query string parameters can be used
|
||||
to specify a range of line numbers to retrieve.
|
||||
|
||||
When using the HTML or API formats, use the `scheme` query string parameter to
|
||||
change the output colors. The value must be one of the following (default is
|
||||
`ansi2html`):
|
||||
|
||||
* `ansi2html`
|
||||
* `osx`
|
||||
* `xterm`
|
||||
* `xterm-bright`
|
||||
* `solarized`
|
||||
|
||||
Use `dark=1` or `dark=0` as a query string parameter to force or disable a
|
||||
dark background.
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import cgi
|
||||
import datetime
|
||||
import dateutil
|
||||
import time
|
||||
@ -33,13 +34,12 @@ from rest_framework.settings import api_settings
|
||||
from rest_framework.views import exception_handler
|
||||
from rest_framework import status
|
||||
|
||||
# Ansi2HTML
|
||||
from ansi2html import Ansi2HTMLConverter
|
||||
from ansi2html.style import SCHEME
|
||||
|
||||
# QSStats
|
||||
import qsstats
|
||||
|
||||
# ANSIConv
|
||||
import ansiconv
|
||||
|
||||
# AWX
|
||||
from awx.main.task_engine import TaskSerializer, TASK_FILE
|
||||
from awx.main.access import get_user_queryset
|
||||
@ -2201,28 +2201,23 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
unified_job = self.get_object()
|
||||
if request.accepted_renderer.format in ('html', 'api', 'json'):
|
||||
scheme = request.QUERY_PARAMS.get('scheme', None)
|
||||
start_line = request.QUERY_PARAMS.get('start_line', 0)
|
||||
end_line = request.QUERY_PARAMS.get('end_line', None)
|
||||
if scheme not in SCHEME:
|
||||
scheme = 'ansi2html'
|
||||
dark_val = request.QUERY_PARAMS.get('dark', '')
|
||||
dark = bool(dark_val and dark_val[0].lower() in ('1', 't', 'y'))
|
||||
content_only = bool(request.accepted_renderer.format in ('api', 'json'))
|
||||
dark_bg = (content_only and dark) or (not content_only and (dark or not dark_val))
|
||||
conv = Ansi2HTMLConverter(scheme=scheme, dark_bg=dark_bg,
|
||||
title=get_view_name(self.__class__))
|
||||
content, start, end, absolute_end = unified_job.result_stdout_raw_limited(start_line, end_line)
|
||||
if content_only:
|
||||
headers = conv.produce_headers()
|
||||
body = conv.convert(content, full=False) # Escapes any HTML that may be in content.
|
||||
data = '\n'.join([headers, body])
|
||||
data = '<div class="nocode body_foreground body_background">%s</div>' % data
|
||||
else:
|
||||
data = conv.convert(content)
|
||||
# Fix ugly grey background used by default.
|
||||
data = data.replace('.body_background { background-color: #AAAAAA; }',
|
||||
'.body_background { background-color: #f5f5f5; }')
|
||||
|
||||
body = ansiconv.to_html(cgi.escape(content))
|
||||
context = {
|
||||
'title': get_view_name(self.__class__),
|
||||
'body': mark_safe(body),
|
||||
'dark': dark_bg,
|
||||
'content_only': content_only,
|
||||
}
|
||||
data = render_to_string('api/stdout.html', context).strip()
|
||||
|
||||
if request.accepted_renderer.format == 'api':
|
||||
return Response(mark_safe(data))
|
||||
if request.accepted_renderer.format == 'json':
|
||||
|
||||
@ -2,7 +2,7 @@ Local versions of third-party packages required by Tower. Package names and
|
||||
versions are listed below, along with notes on which files are included.
|
||||
|
||||
amqp==1.4.5 (amqp/*)
|
||||
ansi2html==1.0.6 (ansi2html/*)
|
||||
ansiconv==1.0.0 (ansiconv.py)
|
||||
anyjson==0.3.3 (anyjson/*)
|
||||
argparse==1.2.1 (argparse.py, needed for Python 2.6 support)
|
||||
azure==0.9.0 (azure/*)
|
||||
@ -21,7 +21,7 @@ django-celery==3.1.10 (djcelery/*)
|
||||
django-crum==0.6.1 (crum/*)
|
||||
django-extensions==1.3.3 (django_extensions/*)
|
||||
django-jsonfield==0.9.12 (jsonfield/*, minor fix in jsonfield/fields.py)
|
||||
django-polymorphic==0.5.3 (polymorphic/*)
|
||||
django_polymorphic==0.5.3 (polymorphic/*)
|
||||
django-split-settings==0.1.1 (split_settings/*)
|
||||
django-taggit==0.11.2 (taggit/*)
|
||||
djangorestframework==2.3.13 (rest_framework/*)
|
||||
@ -36,9 +36,9 @@ kombu==3.0.21 (kombu/*)
|
||||
Markdown==2.4.1 (markdown/*, excluded bin/markdown_py)
|
||||
mock==1.0.1 (mock.py)
|
||||
ordereddict==1.1 (ordereddict.py, needed for Python 2.6 support)
|
||||
os-diskconfig-python-novaclient-ext==0.1.2 (os_diskconfig_python_novaclient_ext/*)
|
||||
os-networksv2-python-novaclient-ext==0.21 (os_networksv2_python_novaclient_ext.py)
|
||||
os-virtual-interfacesv2-python-novaclient-ext==0.15 (os_virtual_interfacesv2_python_novaclient_ext.py)
|
||||
os_diskconfig_python_novaclient_ext==0.1.2 (os_diskconfig_python_novaclient_ext/*)
|
||||
os_networksv2_python_novaclient_ext==0.21 (os_networksv2_python_novaclient_ext.py)
|
||||
os_virtual_interfacesv2_python_novaclient_ext==0.15 (os_virtual_interfacesv2_python_novaclient_ext.py)
|
||||
pbr==0.10.0 (pbr/*)
|
||||
pexpect==3.1 (pexpect/*, excluded pxssh.py, fdpexpect.py, FSM.py, screen.py,
|
||||
ANSI.py)
|
||||
@ -51,8 +51,8 @@ python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
|
||||
pytz==2014.10 (pytz/*)
|
||||
rackspace-auth-openstack==1.3 (rackspace_auth_openstack/*)
|
||||
rackspace-novaclient==1.4 (no files)
|
||||
rax-default-network-flags-python-novaclient-ext==0.2.3 (rax_default_network_flags_python_novaclient_ext/*)
|
||||
rax-scheduled-images-python-novaclient-ext==0.2.1 (rax_scheduled_images_python_novaclient_ext/*)
|
||||
rax_default_network_flags_python_novaclient_ext==0.2.3 (rax_default_network_flags_python_novaclient_ext/*)
|
||||
rax_scheduled_images_python_novaclient_ext==0.2.1 (rax_scheduled_images_python_novaclient_ext/*)
|
||||
requests==2.5.1 (requests/*)
|
||||
setuptools==12.0.5 (setuptools/*, _markerlib/*, pkg_resources/*, easy_install.py)
|
||||
simplejson==3.6.0 (simplejson/*, excluded simplejson/_speedups.so)
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
from ansi2html.converter import Ansi2HTMLConverter
|
||||
__all__ = ['Ansi2HTMLConverter']
|
||||
@ -1,492 +0,0 @@
|
||||
# This file is part of ansi2html
|
||||
# Convert ANSI (terminal) colours and attributes to HTML
|
||||
# Copyright (C) 2012 Ralph Bean <rbean@redhat.com>
|
||||
# Copyright (C) 2013 Sebastian Pipping <sebastian@pipping.org>
|
||||
#
|
||||
# Inspired by and developed off of the work by pixelbeat and blackjack.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import sys
|
||||
import optparse
|
||||
import pkg_resources
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
from ansi2html.style import get_styles, SCHEME
|
||||
import six
|
||||
from six.moves import map
|
||||
from six.moves import zip
|
||||
|
||||
|
||||
ANSI_FULL_RESET = 0
|
||||
ANSI_INTENSITY_INCREASED = 1
|
||||
ANSI_INTENSITY_REDUCED = 2
|
||||
ANSI_INTENSITY_NORMAL = 22
|
||||
ANSI_STYLE_ITALIC = 3
|
||||
ANSI_STYLE_NORMAL = 23
|
||||
ANSI_BLINK_SLOW = 5
|
||||
ANSI_BLINK_FAST = 6
|
||||
ANSI_BLINK_OFF = 25
|
||||
ANSI_UNDERLINE_ON = 4
|
||||
ANSI_UNDERLINE_OFF = 24
|
||||
ANSI_CROSSED_OUT_ON = 9
|
||||
ANSI_CROSSED_OUT_OFF = 29
|
||||
ANSI_VISIBILITY_ON = 28
|
||||
ANSI_VISIBILITY_OFF = 8
|
||||
ANSI_FOREGROUND_CUSTOM_MIN = 30
|
||||
ANSI_FOREGROUND_CUSTOM_MAX = 37
|
||||
ANSI_FOREGROUND_256 = 38
|
||||
ANSI_FOREGROUND_DEFAULT = 39
|
||||
ANSI_BACKGROUND_CUSTOM_MIN = 40
|
||||
ANSI_BACKGROUND_CUSTOM_MAX = 47
|
||||
ANSI_BACKGROUND_256 = 48
|
||||
ANSI_BACKGROUND_DEFAULT = 49
|
||||
ANSI_NEGATIVE_ON = 7
|
||||
ANSI_NEGATIVE_OFF = 27
|
||||
|
||||
|
||||
_template = six.u("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=%(output_encoding)s">
|
||||
<title>%(title)s</title>
|
||||
<style type="text/css">\n%(style)s\n</style>
|
||||
</head>
|
||||
<body class="body_foreground body_background" style="font-size: %(font_size)s;" >
|
||||
<pre>
|
||||
%(content)s
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
""")
|
||||
|
||||
|
||||
class _State(object):
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.intensity = ANSI_INTENSITY_NORMAL
|
||||
self.style = ANSI_STYLE_NORMAL
|
||||
self.blink = ANSI_BLINK_OFF
|
||||
self.underline = ANSI_UNDERLINE_OFF
|
||||
self.crossedout = ANSI_CROSSED_OUT_OFF
|
||||
self.visibility = ANSI_VISIBILITY_ON
|
||||
self.foreground = (ANSI_FOREGROUND_DEFAULT, None)
|
||||
self.background = (ANSI_BACKGROUND_DEFAULT, None)
|
||||
self.negative = ANSI_NEGATIVE_OFF
|
||||
|
||||
def adjust(self, ansi_code, parameter=None):
|
||||
if ansi_code in (ANSI_INTENSITY_INCREASED, ANSI_INTENSITY_REDUCED, ANSI_INTENSITY_NORMAL):
|
||||
self.intensity = ansi_code
|
||||
elif ansi_code in (ANSI_STYLE_ITALIC, ANSI_STYLE_NORMAL):
|
||||
self.style = ansi_code
|
||||
elif ansi_code in (ANSI_BLINK_SLOW, ANSI_BLINK_FAST, ANSI_BLINK_OFF):
|
||||
self.blink = ansi_code
|
||||
elif ansi_code in (ANSI_UNDERLINE_ON, ANSI_UNDERLINE_OFF):
|
||||
self.underline = ansi_code
|
||||
elif ansi_code in (ANSI_CROSSED_OUT_ON, ANSI_CROSSED_OUT_OFF):
|
||||
self.crossedout = ansi_code
|
||||
elif ansi_code in (ANSI_VISIBILITY_ON, ANSI_VISIBILITY_OFF):
|
||||
self.visibility = ansi_code
|
||||
elif ANSI_FOREGROUND_CUSTOM_MIN <= ansi_code <= ANSI_FOREGROUND_CUSTOM_MAX:
|
||||
self.foreground = (ansi_code, None)
|
||||
elif ansi_code == ANSI_FOREGROUND_256:
|
||||
self.foreground = (ansi_code, parameter)
|
||||
elif ansi_code == ANSI_FOREGROUND_DEFAULT:
|
||||
self.foreground = (ansi_code, None)
|
||||
elif ANSI_BACKGROUND_CUSTOM_MIN <= ansi_code <= ANSI_BACKGROUND_CUSTOM_MAX:
|
||||
self.background = (ansi_code, None)
|
||||
elif ansi_code == ANSI_BACKGROUND_256:
|
||||
self.background = (ansi_code, parameter)
|
||||
elif ansi_code == ANSI_BACKGROUND_DEFAULT:
|
||||
self.background = (ansi_code, None)
|
||||
elif ansi_code in (ANSI_NEGATIVE_ON, ANSI_NEGATIVE_OFF):
|
||||
self.negative = ansi_code
|
||||
|
||||
def to_css_classes(self):
|
||||
css_classes = []
|
||||
|
||||
def append_unless_default(output, value, default):
|
||||
if value != default:
|
||||
css_class = 'ansi%d' % value
|
||||
output.append(css_class)
|
||||
|
||||
def append_color_unless_default(output, color, default, negative, neg_css_class):
|
||||
value, parameter = color
|
||||
if value != default:
|
||||
prefix = 'inv' if negative else 'ansi'
|
||||
css_class_index = str(value) \
|
||||
if (parameter is None) \
|
||||
else '%d-%d' % (value, parameter)
|
||||
output.append(prefix + css_class_index)
|
||||
elif negative:
|
||||
output.append(neg_css_class)
|
||||
|
||||
append_unless_default(css_classes, self.intensity, ANSI_INTENSITY_NORMAL)
|
||||
append_unless_default(css_classes, self.style, ANSI_STYLE_NORMAL)
|
||||
append_unless_default(css_classes, self.blink, ANSI_BLINK_OFF)
|
||||
append_unless_default(css_classes, self.underline, ANSI_UNDERLINE_OFF)
|
||||
append_unless_default(css_classes, self.crossedout, ANSI_CROSSED_OUT_OFF)
|
||||
append_unless_default(css_classes, self.visibility, ANSI_VISIBILITY_ON)
|
||||
|
||||
flip_fore_and_background = (self.negative == ANSI_NEGATIVE_ON)
|
||||
append_color_unless_default(css_classes, self.foreground, ANSI_FOREGROUND_DEFAULT, flip_fore_and_background, 'inv_background')
|
||||
append_color_unless_default(css_classes, self.background, ANSI_BACKGROUND_DEFAULT, flip_fore_and_background, 'inv_foreground')
|
||||
|
||||
return css_classes
|
||||
|
||||
|
||||
def linkify(line):
|
||||
for match in re.findall(r'https?:\/\/\S+', line):
|
||||
line = line.replace(match, '<a href="%s">%s</a>' % (match, match))
|
||||
|
||||
return line
|
||||
|
||||
|
||||
def _needs_extra_newline(text):
|
||||
if not text or text.endswith('\n'):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class CursorMoveUp(object):
|
||||
pass
|
||||
|
||||
|
||||
class Ansi2HTMLConverter(object):
|
||||
""" Convert Ansi color codes to CSS+HTML
|
||||
|
||||
Example:
|
||||
>>> conv = Ansi2HTMLConverter()
|
||||
>>> ansi = " ".join(sys.stdin.readlines())
|
||||
>>> html = conv.convert(ansi)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
inline=False,
|
||||
dark_bg=True,
|
||||
font_size='normal',
|
||||
linkify=False,
|
||||
escaped=True,
|
||||
markup_lines=False,
|
||||
output_encoding='utf-8',
|
||||
scheme='ansi2html',
|
||||
title=''
|
||||
):
|
||||
|
||||
self.inline = inline
|
||||
self.dark_bg = dark_bg
|
||||
self.font_size = font_size
|
||||
self.linkify = linkify
|
||||
self.escaped = escaped
|
||||
self.markup_lines = markup_lines
|
||||
self.output_encoding = output_encoding
|
||||
self.scheme = scheme
|
||||
self.title = title
|
||||
self._attrs = None
|
||||
|
||||
if inline:
|
||||
self.styles = dict([(item.klass.strip('.'), item) for item in get_styles(self.dark_bg, self.scheme)])
|
||||
|
||||
self.ansi_codes_prog = re.compile('\033\\[' '([\\d;]*)' '([a-zA-z])')
|
||||
|
||||
def apply_regex(self, ansi):
|
||||
parts = self._apply_regex(ansi)
|
||||
parts = self._collapse_cursor(parts)
|
||||
parts = list(parts)
|
||||
|
||||
if self.linkify:
|
||||
parts = [linkify(part) for part in parts]
|
||||
|
||||
combined = "".join(parts)
|
||||
|
||||
if self.markup_lines:
|
||||
combined = "\n".join([
|
||||
"""<span id="line-%i">%s</span>""" % (i, line)
|
||||
for i, line in enumerate(combined.split('\n'))
|
||||
])
|
||||
|
||||
return combined
|
||||
|
||||
def _apply_regex(self, ansi):
|
||||
if self.escaped:
|
||||
specials = OrderedDict([
|
||||
('&', '&'),
|
||||
('<', '<'),
|
||||
('>', '>'),
|
||||
])
|
||||
for pattern, special in specials.items():
|
||||
ansi = ansi.replace(pattern, special)
|
||||
|
||||
state = _State()
|
||||
inside_span = False
|
||||
last_end = 0 # the index of the last end of a code we've seen
|
||||
for match in self.ansi_codes_prog.finditer(ansi):
|
||||
yield ansi[last_end:match.start()]
|
||||
last_end = match.end()
|
||||
|
||||
params, command = match.groups()
|
||||
|
||||
if command not in 'mMA':
|
||||
continue
|
||||
|
||||
# Special cursor-moving code. The only supported one.
|
||||
if command == 'A':
|
||||
yield CursorMoveUp
|
||||
continue
|
||||
|
||||
try:
|
||||
params = list(map(int, params.split(';')))
|
||||
except ValueError:
|
||||
params = [ANSI_FULL_RESET]
|
||||
|
||||
# Find latest reset marker
|
||||
last_null_index = None
|
||||
skip_after_index = -1
|
||||
for i, v in enumerate(params):
|
||||
if i <= skip_after_index:
|
||||
continue
|
||||
|
||||
if v == ANSI_FULL_RESET:
|
||||
last_null_index = i
|
||||
elif v in (ANSI_FOREGROUND_256, ANSI_BACKGROUND_256):
|
||||
skip_after_index = i + 2
|
||||
|
||||
# Process reset marker, drop everything before
|
||||
if last_null_index is not None:
|
||||
params = params[last_null_index + 1:]
|
||||
if inside_span:
|
||||
inside_span = False
|
||||
yield '</span>'
|
||||
state.reset()
|
||||
|
||||
if not params:
|
||||
continue
|
||||
|
||||
# Turn codes into CSS classes
|
||||
skip_after_index = -1
|
||||
for i, v in enumerate(params):
|
||||
if i <= skip_after_index:
|
||||
continue
|
||||
|
||||
if v in (ANSI_FOREGROUND_256, ANSI_BACKGROUND_256):
|
||||
try:
|
||||
parameter = params[i + 2]
|
||||
except IndexError:
|
||||
continue
|
||||
skip_after_index = i + 2
|
||||
else:
|
||||
parameter = None
|
||||
state.adjust(v, parameter=parameter)
|
||||
|
||||
if inside_span:
|
||||
yield '</span>'
|
||||
inside_span = False
|
||||
|
||||
css_classes = state.to_css_classes()
|
||||
if not css_classes:
|
||||
continue
|
||||
|
||||
if self.inline:
|
||||
style = [self.styles[klass].kw for klass in css_classes if
|
||||
klass in self.styles]
|
||||
yield '<span style="%s">' % "; ".join(style)
|
||||
else:
|
||||
yield '<span class="%s">' % " ".join(css_classes)
|
||||
inside_span = True
|
||||
|
||||
yield ansi[last_end:]
|
||||
if inside_span:
|
||||
yield '</span>'
|
||||
inside_span = False
|
||||
|
||||
def _collapse_cursor(self, parts):
|
||||
""" Act on any CursorMoveUp commands by deleting preceding tokens """
|
||||
|
||||
final_parts = []
|
||||
for part in parts:
|
||||
|
||||
# Throw out empty string tokens ("")
|
||||
if not part:
|
||||
continue
|
||||
|
||||
# Go back, deleting every token in the last 'line'
|
||||
if part == CursorMoveUp:
|
||||
final_parts.pop()
|
||||
while '\n' not in final_parts[-1]:
|
||||
final_parts.pop()
|
||||
|
||||
continue
|
||||
|
||||
# Otherwise, just pass this token forward
|
||||
final_parts.append(part)
|
||||
|
||||
return final_parts
|
||||
|
||||
def prepare(self, ansi='', ensure_trailing_newline=False):
|
||||
""" Load the contents of 'ansi' into this object """
|
||||
|
||||
body = self.apply_regex(ansi)
|
||||
|
||||
if ensure_trailing_newline and _needs_extra_newline(body):
|
||||
body += '\n'
|
||||
|
||||
self._attrs = {
|
||||
'dark_bg': self.dark_bg,
|
||||
'font_size': self.font_size,
|
||||
'body': body,
|
||||
}
|
||||
|
||||
return self._attrs
|
||||
|
||||
def attrs(self):
|
||||
""" Prepare attributes for the template """
|
||||
if not self._attrs:
|
||||
raise Exception("Method .prepare not yet called.")
|
||||
return self._attrs
|
||||
|
||||
def convert(self, ansi, full=True, ensure_trailing_newline=False):
|
||||
attrs = self.prepare(ansi, ensure_trailing_newline=ensure_trailing_newline)
|
||||
if not full:
|
||||
return attrs["body"]
|
||||
else:
|
||||
return _template % {
|
||||
'style' : "\n".join(map(str, get_styles(self.dark_bg, self.scheme))),
|
||||
'title' : self.title,
|
||||
'font_size' : self.font_size,
|
||||
'content' : attrs["body"],
|
||||
'output_encoding' : self.output_encoding,
|
||||
}
|
||||
|
||||
def produce_headers(self):
|
||||
return '<style type="text/css">\n%(style)s\n</style>\n' % {
|
||||
'style' : "\n".join(map(str, get_styles(self.dark_bg, self.scheme)))
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
$ ls --color=always | ansi2html > directories.html
|
||||
$ sudo tail /var/log/messages | ccze -A | ansi2html > logs.html
|
||||
$ task burndown | ansi2html > burndown.html
|
||||
"""
|
||||
|
||||
scheme_names = sorted(six.iterkeys(SCHEME))
|
||||
version_str = pkg_resources.get_distribution('ansi2html').version
|
||||
parser = optparse.OptionParser(
|
||||
usage=main.__doc__,
|
||||
version="%%prog %s" % version_str)
|
||||
parser.add_option(
|
||||
"-p", "--partial", dest="partial",
|
||||
default=False, action="store_true",
|
||||
help="Process lines as them come in. No headers are produced.")
|
||||
parser.add_option(
|
||||
"-i", "--inline", dest="inline",
|
||||
default=False, action="store_true",
|
||||
help="Inline style without headers or template.")
|
||||
parser.add_option(
|
||||
"-H", "--headers", dest="headers",
|
||||
default=False, action="store_true",
|
||||
help="Just produce the <style> tag.")
|
||||
parser.add_option(
|
||||
"-f", '--font-size', dest='font_size', metavar='SIZE',
|
||||
default="normal",
|
||||
help="Set the global font size in the output.")
|
||||
parser.add_option(
|
||||
"-l", '--light-background', dest='light_background',
|
||||
default=False, action="store_true",
|
||||
help="Set output to 'light background' mode.")
|
||||
parser.add_option(
|
||||
"-a", '--linkify', dest='linkify',
|
||||
default=False, action="store_true",
|
||||
help="Transform URLs into <a> links.")
|
||||
parser.add_option(
|
||||
"-u", '--unescape', dest='escaped',
|
||||
default=True, action="store_false",
|
||||
help="Do not escape XML tags found in the input.")
|
||||
parser.add_option(
|
||||
"-m", '--markup-lines', dest="markup_lines",
|
||||
default=False, action="store_true",
|
||||
help="Surround lines with <span id='line-n'>..</span>.")
|
||||
parser.add_option(
|
||||
'--input-encoding', dest='input_encoding', metavar='ENCODING',
|
||||
default='utf-8',
|
||||
help="Specify input encoding")
|
||||
parser.add_option(
|
||||
'--output-encoding', dest='output_encoding', metavar='ENCODING',
|
||||
default='utf-8',
|
||||
help="Specify output encoding")
|
||||
parser.add_option(
|
||||
'-s', '--scheme', dest='scheme', metavar='SCHEME',
|
||||
default='ansi2html', choices=scheme_names,
|
||||
help=("Specify color palette scheme. Default: %%default. Choices: %s"
|
||||
% scheme_names))
|
||||
parser.add_option(
|
||||
'-t', '--title', dest='output_title',
|
||||
default='',
|
||||
help="Specify output title")
|
||||
|
||||
opts, args = parser.parse_args()
|
||||
|
||||
conv = Ansi2HTMLConverter(
|
||||
inline=opts.inline,
|
||||
dark_bg=not opts.light_background,
|
||||
font_size=opts.font_size,
|
||||
linkify=opts.linkify,
|
||||
escaped=opts.escaped,
|
||||
markup_lines=opts.markup_lines,
|
||||
output_encoding=opts.output_encoding,
|
||||
scheme=opts.scheme,
|
||||
title=opts.output_title,
|
||||
)
|
||||
|
||||
def _read(input_bytes):
|
||||
if six.PY3:
|
||||
# This is actually already unicode. How to we explicitly decode in
|
||||
# python3? I don't know the answer yet.
|
||||
return input_bytes
|
||||
else:
|
||||
return input_bytes.decode(opts.input_encoding)
|
||||
|
||||
def _print(output_unicode, end='\n'):
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
output_bytes = (output_unicode + end).encode(opts.output_encoding)
|
||||
sys.stdout.buffer.write(output_bytes)
|
||||
elif not six.PY3:
|
||||
sys.stdout.write((output_unicode + end).encode(opts.output_encoding))
|
||||
else:
|
||||
sys.stdout.write(output_unicode + end)
|
||||
|
||||
# Produce only the headers and quit
|
||||
if opts.headers:
|
||||
_print(conv.produce_headers(), end='')
|
||||
return
|
||||
|
||||
full = not bool(opts.partial or opts.inline)
|
||||
if six.PY3:
|
||||
output = conv.convert("".join(sys.stdin.readlines()), full=full, ensure_trailing_newline=True)
|
||||
_print(output, end='')
|
||||
else:
|
||||
output = conv.convert(six.u("").join(
|
||||
map(_read, sys.stdin.readlines())
|
||||
), full=full, ensure_trailing_newline=True)
|
||||
_print(output, end='')
|
||||
@ -1,113 +0,0 @@
|
||||
# This file is part of ansi2html.
|
||||
# Copyright (C) 2012 Kuno Woudt <kuno@frob.nl>
|
||||
# Copyright (C) 2013 Sebastian Pipping <sebastian@pipping.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
class Rule(object):
|
||||
|
||||
def __init__(self, klass, **kw):
|
||||
|
||||
self.klass = klass
|
||||
self.kw = '; '.join([(k.replace('_', '-')+': '+kw[k])
|
||||
for k in sorted(kw.keys())]).strip()
|
||||
|
||||
def __str__(self):
|
||||
return '%s { %s; }' % (self.klass, self.kw)
|
||||
|
||||
|
||||
def index(r, g, b):
|
||||
return str(16 + (r * 36) + (g * 6) + b)
|
||||
|
||||
|
||||
def color(r, g, b):
|
||||
return "#%.2x%.2x%.2x" % (r * 42, g * 42, b * 42)
|
||||
|
||||
|
||||
def level(grey):
|
||||
return "#%.2x%.2x%.2x" % (((grey * 10) + 8,) * 3)
|
||||
|
||||
|
||||
def index2(grey):
|
||||
return str(232 + grey)
|
||||
|
||||
# http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
SCHEME = { # black red green brown/yellow blue magenta cyan grey/white
|
||||
'ansi2html': ("#000316", "#aa0000", "#00aa00", "#aa5500", "#0000aa",
|
||||
"#E850A8", "#00aaaa", "#F5F1DE"),
|
||||
'xterm': ("#000000", "#cd0000", "#00cd00", "#cdcd00", "#0000ee",
|
||||
"#cd00cd", "#00cdcd", "#e5e5e5"),
|
||||
'xterm-bright': ("#7f7f7f", "#ff0000", "#00ff00", "#ffff00", "#5c5cff",
|
||||
"#ff00ff", "#00ffff", "#ffffff"),
|
||||
'osx': ("#000000", "#c23621", "#25bc24", "#adad27", "#492ee1",
|
||||
"#d338d3", "#33bbc8", "#cbcccd"),
|
||||
|
||||
# http://ethanschoonover.com/solarized
|
||||
'solarized': ("#262626", "#d70000", "#5f8700", "#af8700", "#0087ff",
|
||||
"#af005f", "#00afaf", "#e4e4e4"),
|
||||
}
|
||||
|
||||
def get_styles(dark_bg=True, scheme='ansi2html'):
|
||||
|
||||
css = [
|
||||
Rule('.body_foreground', color=('#000000', '#AAAAAA')[dark_bg]),
|
||||
Rule('.body_background', background_color=('#AAAAAA', '#000000')[dark_bg]),
|
||||
Rule('.body_foreground > .bold,.bold > .body_foreground, body.body_foreground > pre > .bold',
|
||||
color=('#000000', '#FFFFFF')[dark_bg], font_weight=('bold', 'normal')[dark_bg]),
|
||||
Rule('.inv_foreground', color=('#000000', '#FFFFFF')[not dark_bg]),
|
||||
Rule('.inv_background', background_color=('#AAAAAA', '#000000')[not dark_bg]),
|
||||
Rule('.ansi1', font_weight='bold'),
|
||||
Rule('.ansi2', font_weight='lighter'),
|
||||
Rule('.ansi3', font_style='italic'),
|
||||
Rule('.ansi4', text_decoration='underline'),
|
||||
Rule('.ansi5', text_decoration='blink'),
|
||||
Rule('.ansi6', text_decoration='blink'),
|
||||
Rule('.ansi8', visibility='hidden'),
|
||||
Rule('.ansi9', text_decoration='line-through'),
|
||||
]
|
||||
|
||||
# set palette
|
||||
pal = SCHEME[scheme]
|
||||
for _index in range(8):
|
||||
css.append(Rule('.ansi3%s' % _index, color=pal[_index]))
|
||||
css.append(Rule('.inv3%s' % _index, background_color=pal[_index]))
|
||||
for _index in range(8):
|
||||
css.append(Rule('.ansi4%s' % _index, background_color=pal[_index]))
|
||||
css.append(Rule('.inv4%s' % _index, color=pal[_index]))
|
||||
|
||||
# css.append("/* Define the explicit color codes (obnoxious) */\n\n")
|
||||
|
||||
for green in range(0, 6):
|
||||
for red in range(0, 6):
|
||||
for blue in range(0, 6):
|
||||
css.append(Rule(".ansi38-%s" % index(red, green, blue),
|
||||
color=color(red, green, blue)))
|
||||
css.append(Rule(".inv38-%s" % index(red, green, blue),
|
||||
background=color(red, green, blue)))
|
||||
css.append(Rule(".ansi48-%s" % index(red, green, blue),
|
||||
background=color(red, green, blue)))
|
||||
css.append(Rule(".inv48-%s" % index(red, green, blue),
|
||||
color=color(red, green, blue)))
|
||||
|
||||
for grey in range(0, 24):
|
||||
css.append(Rule('.ansi38-%s' % index2(grey), color=level(grey)))
|
||||
css.append(Rule('.inv38-%s' % index2(grey), background=level(grey)))
|
||||
css.append(Rule('.ansi48-%s' % index2(grey), background=level(grey)))
|
||||
css.append(Rule('.inv48-%s' % index2(grey), color=level(grey)))
|
||||
|
||||
return css
|
||||
@ -1,2 +0,0 @@
|
||||
def read_to_unicode(obj):
|
||||
return [line.decode('utf-8') for line in obj.readlines()]
|
||||
127
awx/lib/site-packages/ansiconv.py
Normal file
@ -0,0 +1,127 @@
|
||||
"""
|
||||
Converts ANSI coded text and converts it to either plain text
|
||||
or to HTML.
|
||||
"""
|
||||
import re
|
||||
|
||||
supported_sgr_codes = [1, 3, 4, 9, 30, 31, 32, 33, 34, 35, 36, 37, 40, 41, 42,
|
||||
43, 44, 45, 46, 47]
|
||||
|
||||
|
||||
def to_plain(ansi):
|
||||
"""Takes the given string and strips all ANSI codes out.
|
||||
|
||||
:param ansi: The string to strip
|
||||
:return: The stripped string
|
||||
"""
|
||||
return re.sub(r'\x1B\[[0-9;]*[ABCDEFGHJKSTfmnsulh]', '', ansi)
|
||||
|
||||
|
||||
def to_html(ansi, replace_newline=False):
|
||||
"""Converts the given ANSI string to HTML
|
||||
|
||||
If `replace_newline` is set to True, then all newlines will be
|
||||
replaced with <br />.
|
||||
|
||||
:param ansi: The ANSI text.
|
||||
:param replace_newline: Whether to replace newlines with HTML.
|
||||
:return: The resulting HTML string.
|
||||
"""
|
||||
blocks = ansi.split('\x1B')
|
||||
parsed_blocks = []
|
||||
for block in blocks:
|
||||
command, text = _block_to_html(block)
|
||||
|
||||
# The command "A" means move the cursor up, so we emulate that here.
|
||||
if command == 'A' and len(parsed_blocks) > 0:
|
||||
parsed_blocks.pop()
|
||||
while len(parsed_blocks) > 0 and '\n' not in parsed_blocks[-1]:
|
||||
parsed_blocks.pop()
|
||||
|
||||
parsed_blocks.append(text)
|
||||
|
||||
text = ''.join(parsed_blocks)
|
||||
|
||||
if replace_newline:
|
||||
text = text.replace('\n', '<br />\n')
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def base_css(dark=True):
|
||||
"""Some base CSS with all of the default ANSI styles/colors.
|
||||
|
||||
:param dark: Whether background should be dark or light.
|
||||
:return: A string of CSS
|
||||
"""
|
||||
return "\n".join([
|
||||
css_rule('.ansi_fore', color=('#000000', '#FFFFFF')[dark]),
|
||||
css_rule('.ansi_back', background_color=('#FFFFFF', '#000000')[dark]),
|
||||
css_rule('.ansi1', font_weight='bold'),
|
||||
css_rule('.ansi3', font_weight='italic'),
|
||||
css_rule('.ansi4', text_decoration='underline'),
|
||||
css_rule('.ansi9', text_decoration='line-through'),
|
||||
css_rule('.ansi30', color="#000000"),
|
||||
css_rule('.ansi31', color="#FF0000"),
|
||||
css_rule('.ansi32', color="#00FF00"),
|
||||
css_rule('.ansi33', color="#FFFF00"),
|
||||
css_rule('.ansi34', color="#0000FF"),
|
||||
css_rule('.ansi35', color="#FF00FF"),
|
||||
css_rule('.ansi36', color="#00FFFF"),
|
||||
css_rule('.ansi37', color="#FFFFFF"),
|
||||
css_rule('.ansi40', background_color="#000000"),
|
||||
css_rule('.ansi41', background_color="#FF0000"),
|
||||
css_rule('.ansi42', background_color="#00FF00"),
|
||||
css_rule('.ansi43', background_color="#FFFF00"),
|
||||
css_rule('.ansi44', background_color="#0000FF"),
|
||||
css_rule('.ansi45', background_color="#FF00FF"),
|
||||
css_rule('.ansi46', background_color="#00FFFF"),
|
||||
css_rule('.ansi47', background_color="#FFFFFF")
|
||||
])
|
||||
|
||||
|
||||
def css_rule(class_name, **properties):
|
||||
"""Creates a CSS rule string.
|
||||
|
||||
The named parameters are used as the css properties. Underscores
|
||||
are converted to hyphens.
|
||||
|
||||
:param class_name: The CSS class name
|
||||
:param properties: The properties sent as named params.
|
||||
:return: The CSS string
|
||||
"""
|
||||
prop_str = lambda name, val: name.replace('_', '-') + ': ' + val
|
||||
return '{0} {{ {1}; }}'.format(
|
||||
class_name,
|
||||
'; '.join([prop_str(prop, properties[prop]) for prop in properties])
|
||||
)
|
||||
|
||||
|
||||
def _block_to_html(text):
|
||||
"""Converts the given block of ANSI coded text to HTML.
|
||||
|
||||
The text is only given back as HTML if the ANSI code is at the
|
||||
beginning of the string (e.g. "[0;33mFoobar")
|
||||
|
||||
:param text: The text block to convert.
|
||||
:return: The text as HTML
|
||||
"""
|
||||
match = re.match(r'^\[(?P<code>\d+(?:;\d+)*)?(?P<command>[Am])', text)
|
||||
if match is None:
|
||||
return None, text
|
||||
|
||||
command = match.group('command')
|
||||
text = text[match.end():]
|
||||
|
||||
if match.group('code') is None:
|
||||
return command, text
|
||||
|
||||
classes = []
|
||||
for code in match.group('code').split(';'):
|
||||
if int(code) in supported_sgr_codes:
|
||||
classes.append('ansi{0}'.format(code))
|
||||
|
||||
if classes:
|
||||
text = '<span class="{0}">{1}</span>'.format(' '.join(classes), text)
|
||||
|
||||
return command, text
|
||||
@ -24,6 +24,8 @@ class Command(BaseCommandInstance):
|
||||
self.include_option_hostname_uuid_find()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
# Is there an existing record for this machine? If so, retrieve that record and look for issues.
|
||||
try:
|
||||
# Get the instance.
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
@ -210,6 +209,15 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
vars.append(survey_element['variable'])
|
||||
return vars
|
||||
|
||||
def survey_password_variables(self):
|
||||
vars = []
|
||||
if self.survey_enabled and 'spec' in self.survey_spec:
|
||||
# Get variables that are type password
|
||||
for survey_element in self.survey_spec['spec']:
|
||||
if survey_element['type'] == 'password':
|
||||
vars.append(survey_element['variable'])
|
||||
return vars
|
||||
|
||||
def survey_variable_validation(self, data):
|
||||
errors = []
|
||||
if not self.survey_enabled:
|
||||
@ -459,14 +467,8 @@ class Job(UnifiedJob, JobOptions):
|
||||
# Then lookup password fields in extra_vars and save the values
|
||||
jt = self.job_template
|
||||
if jt and jt.survey_enabled and 'spec' in jt.survey_spec:
|
||||
vars = []
|
||||
# Get variables that are type password
|
||||
for survey_element in jt.survey_spec['spec']:
|
||||
if survey_element['type'] == 'password':
|
||||
vars.append(survey_element['variable'])
|
||||
|
||||
# Use password vars to find in extra_vars
|
||||
for key in vars:
|
||||
for key in jt.survey_password_variables():
|
||||
if key in self.extra_vars_dict:
|
||||
content = PlainTextCleaner.remove_sensitive(content, self.extra_vars_dict[key])
|
||||
return content
|
||||
|
||||
@ -231,7 +231,15 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique):
|
||||
if field not in update_fields:
|
||||
update_fields.append(field)
|
||||
# Do the actual save.
|
||||
super(UnifiedJobTemplate, self).save(*args, **kwargs)
|
||||
try:
|
||||
super(UnifiedJobTemplate, self).save(*args, **kwargs)
|
||||
except ValueError:
|
||||
# A fix for https://trello.com/c/S4rU1F21
|
||||
# Does not resolve the root cause. Tis merely a bandaid.
|
||||
if 'scm_delete_on_next_update' in update_fields:
|
||||
update_fields.remove('scm_delete_on_next_update')
|
||||
super(UnifiedJobTemplate, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
def _get_current_status(self):
|
||||
# Override in subclasses as needed.
|
||||
|
||||
@ -14,4 +14,3 @@ from awx.main.tests.activity_stream import * # noqa
|
||||
from awx.main.tests.schedules import * # noqa
|
||||
from awx.main.tests.redact import * # noqa
|
||||
from awx.main.tests.views import * # noqa
|
||||
from awx.main.tests.jobs import *
|
||||
|
||||
@ -323,8 +323,8 @@ class BaseTestMixin(QueueTestMixin):
|
||||
job_template = self.make_job_template(created_by=created_by)
|
||||
|
||||
opts = {
|
||||
'created_by': created_by,
|
||||
'status': inital_state,
|
||||
'created_by': created_by,
|
||||
'status': inital_state,
|
||||
}
|
||||
opts.update(kwargs)
|
||||
return job_template.create_job(**opts)
|
||||
@ -448,9 +448,13 @@ class BaseTestMixin(QueueTestMixin):
|
||||
elif response['Content-Type'].startswith('application/yaml'):
|
||||
obj = yaml.safe_load(response.content)
|
||||
elif response['Content-Type'].startswith('text/plain'):
|
||||
obj = { 'content': response.content }
|
||||
obj = {
|
||||
'content': response.content
|
||||
}
|
||||
elif response['Content-Type'].startswith('text/html'):
|
||||
obj = { 'content': response.content }
|
||||
obj = {
|
||||
'content': response.content
|
||||
}
|
||||
else:
|
||||
self.fail('Unsupport response content type %s' % response['Content-Type'])
|
||||
else:
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
|
||||
from awx.main.tests.jobs.jobs import *
|
||||
from awx.main.tests.jobs.survey_password import *
|
||||
from awx.main.tests.jobs.jobs_monolithic import * # noqa
|
||||
from survey_password import * # noqa
|
||||
from base import * # noqa
|
||||
|
||||
515
awx/main/tests/jobs/base.py
Normal file
@ -0,0 +1,515 @@
|
||||
# Python
|
||||
import uuid
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTestMixin
|
||||
|
||||
TEST_PLAYBOOK = '''- hosts: all
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: woohoo
|
||||
command: test 1 = 1
|
||||
'''
|
||||
|
||||
class BaseJobTestMixin(BaseTestMixin):
|
||||
|
||||
|
||||
def _create_inventory(self, name, organization, created_by,
|
||||
groups_hosts_dict):
|
||||
'''Helper method for creating inventory with groups and hosts.'''
|
||||
inventory = organization.inventories.create(
|
||||
name=name,
|
||||
created_by=created_by,
|
||||
)
|
||||
for group_name, host_names in groups_hosts_dict.items():
|
||||
group = inventory.groups.create(
|
||||
name=group_name,
|
||||
created_by=created_by,
|
||||
)
|
||||
for host_name in host_names:
|
||||
host = inventory.hosts.create(
|
||||
name=host_name,
|
||||
created_by=created_by,
|
||||
)
|
||||
group.hosts.add(host)
|
||||
return inventory
|
||||
|
||||
def populate(self):
|
||||
# Here's a little story about the Ansible Bread Company, or ABC. They
|
||||
# make machines that make bread - bakers, slicers, and packagers - and
|
||||
# these machines are each controlled by a Linux boxes, which is in turn
|
||||
# managed by Ansible Commander.
|
||||
|
||||
# Sue is the super user. You don't mess with Sue or you're toast. Ha.
|
||||
self.user_sue = self.make_user('sue', super_user=True)
|
||||
|
||||
# There are three organizations in ABC using Ansible, since it's the
|
||||
# best thing for dev ops automation since, well, sliced bread.
|
||||
|
||||
# Engineering - They design and build the machines.
|
||||
self.org_eng = Organization.objects.create(
|
||||
name='engineering',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# Support - They fix it when it's not working.
|
||||
self.org_sup = Organization.objects.create(
|
||||
name='support',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# Operations - They implement the production lines using the machines.
|
||||
self.org_ops = Organization.objects.create(
|
||||
name='operations',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
|
||||
# Alex is Sue's IT assistant who can also administer all of the
|
||||
# organizations.
|
||||
self.user_alex = self.make_user('alex')
|
||||
self.org_eng.admins.add(self.user_alex)
|
||||
self.org_sup.admins.add(self.user_alex)
|
||||
self.org_ops.admins.add(self.user_alex)
|
||||
|
||||
# Bob is the head of engineering. He's an admin for engineering, but
|
||||
# also a user within the operations organization (so he can see the
|
||||
# results if things go wrong in production).
|
||||
self.user_bob = self.make_user('bob')
|
||||
self.org_eng.admins.add(self.user_bob)
|
||||
self.org_ops.users.add(self.user_bob)
|
||||
|
||||
# Chuck is the lead engineer. He has full reign over engineering, but
|
||||
# no other organizations.
|
||||
self.user_chuck = self.make_user('chuck')
|
||||
self.org_eng.admins.add(self.user_chuck)
|
||||
|
||||
# Doug is the other engineer working under Chuck. He can write
|
||||
# playbooks and check them, but Chuck doesn't quite think he's ready to
|
||||
# run them yet. Poor Doug.
|
||||
self.user_doug = self.make_user('doug')
|
||||
self.org_eng.users.add(self.user_doug)
|
||||
|
||||
# Juan is another engineer working under Chuck. He has a little more freedom
|
||||
# to run playbooks but can't create job templates
|
||||
self.user_juan = self.make_user('juan')
|
||||
self.org_eng.users.add(self.user_juan)
|
||||
|
||||
# Hannibal is Chuck's right-hand man. Chuck usually has him create the job
|
||||
# templates that the rest of the team will use
|
||||
self.user_hannibal = self.make_user('hannibal')
|
||||
self.org_eng.users.add(self.user_hannibal)
|
||||
|
||||
# Eve is the head of support. She can also see what goes on in
|
||||
# operations to help them troubleshoot problems.
|
||||
self.user_eve = self.make_user('eve')
|
||||
self.org_sup.admins.add(self.user_eve)
|
||||
self.org_ops.users.add(self.user_eve)
|
||||
|
||||
# Frank is the other support guy.
|
||||
self.user_frank = self.make_user('frank')
|
||||
self.org_sup.users.add(self.user_frank)
|
||||
|
||||
# Greg is the head of operations.
|
||||
self.user_greg = self.make_user('greg')
|
||||
self.org_ops.admins.add(self.user_greg)
|
||||
|
||||
# Holly is an operations engineer.
|
||||
self.user_holly = self.make_user('holly')
|
||||
self.org_ops.users.add(self.user_holly)
|
||||
|
||||
# Iris is another operations engineer.
|
||||
self.user_iris = self.make_user('iris')
|
||||
self.org_ops.users.add(self.user_iris)
|
||||
|
||||
# Randall and Billybob are new ops interns that ops uses to test
|
||||
# their playbooks and inventory
|
||||
self.user_randall = self.make_user('randall')
|
||||
self.org_ops.users.add(self.user_randall)
|
||||
|
||||
# He works with Randall
|
||||
self.user_billybob = self.make_user('billybob')
|
||||
self.org_ops.users.add(self.user_billybob)
|
||||
|
||||
# Jim is the newest intern. He can login, but can't do anything quite yet
|
||||
# except make everyone else fresh coffee.
|
||||
self.user_jim = self.make_user('jim')
|
||||
|
||||
# There are three main projects, one each for the development, test and
|
||||
# production branches of the playbook repository. All three orgs can
|
||||
# use the production branch, support can use the production and testing
|
||||
# branches, and operations can only use the production branch.
|
||||
self.proj_dev = self.make_project('dev', 'development branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_dev)
|
||||
self.proj_test = self.make_project('test', 'testing branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_test)
|
||||
self.org_sup.projects.add(self.proj_test)
|
||||
self.proj_prod = self.make_project('prod', 'production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_prod)
|
||||
self.org_sup.projects.add(self.proj_prod)
|
||||
self.org_ops.projects.add(self.proj_prod)
|
||||
|
||||
# Operations also has 2 additional projects specific to the east/west
|
||||
# production environments.
|
||||
self.proj_prod_east = self.make_project('prod-east',
|
||||
'east production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_ops.projects.add(self.proj_prod_east)
|
||||
self.proj_prod_west = self.make_project('prod-west',
|
||||
'west production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_ops.projects.add(self.proj_prod_west)
|
||||
|
||||
# The engineering organization has a set of servers to use for
|
||||
# development and testing (2 bakers, 1 slicer, 1 packager).
|
||||
self.inv_eng = self._create_inventory(
|
||||
name='engineering environment',
|
||||
organization=self.org_eng,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['eng-baker1', 'eng-baker2'],
|
||||
'slicers': ['eng-slicer1'],
|
||||
'packagers': ['eng-packager1'],
|
||||
},
|
||||
)
|
||||
|
||||
# The support organization has a set of servers to use for
|
||||
# testing and reproducing problems from operations (1 baker, 1 slicer,
|
||||
# 1 packager).
|
||||
self.inv_sup = self._create_inventory(
|
||||
name='support environment',
|
||||
organization=self.org_sup,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['sup-baker1'],
|
||||
'slicers': ['sup-slicer1'],
|
||||
'packagers': ['sup-packager1'],
|
||||
},
|
||||
)
|
||||
|
||||
# The operations organization manages multiple sets of servers for the
|
||||
# east and west production facilities.
|
||||
self.inv_ops_east = self._create_inventory(
|
||||
name='east production environment',
|
||||
organization=self.org_ops,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['east-baker%d' % n for n in range(1, 4)],
|
||||
'slicers': ['east-slicer%d' % n for n in range(1, 3)],
|
||||
'packagers': ['east-packager%d' % n for n in range(1, 3)],
|
||||
},
|
||||
)
|
||||
self.inv_ops_west = self._create_inventory(
|
||||
name='west production environment',
|
||||
organization=self.org_ops,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['west-baker%d' % n for n in range(1, 6)],
|
||||
'slicers': ['west-slicer%d' % n for n in range(1, 4)],
|
||||
'packagers': ['west-packager%d' % n for n in range(1, 3)],
|
||||
},
|
||||
)
|
||||
|
||||
# Operations is divided into teams to work on the east/west servers.
|
||||
# Greg and Holly work on east, Greg and iris work on west.
|
||||
self.team_ops_east = self.org_ops.teams.create(
|
||||
name='easterners',
|
||||
created_by=self.user_sue)
|
||||
self.team_ops_east.projects.add(self.proj_prod)
|
||||
self.team_ops_east.projects.add(self.proj_prod_east)
|
||||
self.team_ops_east.users.add(self.user_greg)
|
||||
self.team_ops_east.users.add(self.user_holly)
|
||||
self.team_ops_west = self.org_ops.teams.create(
|
||||
name='westerners',
|
||||
created_by=self.user_sue)
|
||||
self.team_ops_west.projects.add(self.proj_prod)
|
||||
self.team_ops_west.projects.add(self.proj_prod_west)
|
||||
self.team_ops_west.users.add(self.user_greg)
|
||||
self.team_ops_west.users.add(self.user_iris)
|
||||
|
||||
# The south team is no longer active having been folded into the east team
|
||||
self.team_ops_south = self.org_ops.teams.create(
|
||||
name='southerners',
|
||||
created_by=self.user_sue,
|
||||
active=False,
|
||||
)
|
||||
self.team_ops_south.projects.add(self.proj_prod)
|
||||
self.team_ops_south.users.add(self.user_greg)
|
||||
|
||||
# The north team is going to be deleted
|
||||
self.team_ops_north = self.org_ops.teams.create(
|
||||
name='northerners',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.team_ops_north.projects.add(self.proj_prod)
|
||||
self.team_ops_north.users.add(self.user_greg)
|
||||
|
||||
# The testers team are interns that can only check playbooks but can't
|
||||
# run them
|
||||
self.team_ops_testers = self.org_ops.teams.create(
|
||||
name='testers',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.team_ops_testers.projects.add(self.proj_prod)
|
||||
self.team_ops_testers.users.add(self.user_randall)
|
||||
self.team_ops_testers.users.add(self.user_billybob)
|
||||
|
||||
# Each user has his/her own set of credentials.
|
||||
from awx.main.tests.tasks import (TEST_SSH_KEY_DATA,
|
||||
TEST_SSH_KEY_DATA_LOCKED,
|
||||
TEST_SSH_KEY_DATA_UNLOCK)
|
||||
self.cred_sue = self.user_sue.credentials.create(
|
||||
username='sue',
|
||||
password=TEST_SSH_KEY_DATA,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_bob = self.user_bob.credentials.create(
|
||||
username='bob',
|
||||
password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_chuck = self.user_chuck.credentials.create(
|
||||
username='chuck',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_doug = self.user_doug.credentials.create(
|
||||
username='doug',
|
||||
password='doug doesn\'t mind his password being saved. this '
|
||||
'is why we dont\'t let doug actually run jobs.',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_eve = self.user_eve.credentials.create(
|
||||
username='eve',
|
||||
password='ASK',
|
||||
sudo_username='root',
|
||||
sudo_password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_frank = self.user_frank.credentials.create(
|
||||
username='frank',
|
||||
password='fr@nk the t@nk',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_greg = self.user_greg.credentials.create(
|
||||
username='greg',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
|
||||
ssh_key_unlock='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_holly = self.user_holly.credentials.create(
|
||||
username='holly',
|
||||
password='holly rocks',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_iris = self.user_iris.credentials.create(
|
||||
username='iris',
|
||||
password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
|
||||
# Each operations team also has shared credentials they can use.
|
||||
self.cred_ops_east = self.team_ops_east.credentials.create(
|
||||
username='east',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
|
||||
ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK,
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
self.cred_ops_west = self.team_ops_west.credentials.create(
|
||||
username='west',
|
||||
password='Heading270',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
self.cred_ops_south = self.team_ops_south.credentials.create(
|
||||
username='south',
|
||||
password='Heading180',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.cred_ops_north = self.team_ops_north.credentials.create(
|
||||
username='north',
|
||||
password='Heading0',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.cred_ops_test = self.team_ops_testers.credentials.create(
|
||||
username='testers',
|
||||
password='HeadingNone',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.ops_testers_permission = Permission.objects.create(
|
||||
inventory = self.inv_ops_west,
|
||||
project = self.proj_prod,
|
||||
team = self.team_ops_testers,
|
||||
permission_type = PERM_INVENTORY_CHECK,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.doug_check_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_doug,
|
||||
permission_type = PERM_INVENTORY_CHECK,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.juan_deploy_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_juan,
|
||||
permission_type = PERM_INVENTORY_DEPLOY,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.hannibal_create_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_hannibal,
|
||||
permission_type = PERM_JOBTEMPLATE_CREATE,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
# FIXME: Define explicit permissions for tests.
|
||||
# other django user is on the project team and can deploy
|
||||
#self.permission1 = Permission.objects.create(
|
||||
# inventory = self.inventory,
|
||||
# project = self.project,
|
||||
# team = self.team,
|
||||
# permission_type = PERM_INVENTORY_DEPLOY,
|
||||
# created_by = self.normal_django_user
|
||||
#)
|
||||
# individual permission granted to other2 user, can run check mode
|
||||
#self.permission2 = Permission.objects.create(
|
||||
# inventory = self.inventory,
|
||||
# project = self.project,
|
||||
# user = self.other2_django_user,
|
||||
# permission_type = PERM_INVENTORY_CHECK,
|
||||
# created_by = self.normal_django_user
|
||||
#)
|
||||
|
||||
# Engineering has job templates to check/run the dev project onto
|
||||
# their own inventory.
|
||||
self.jt_eng_check = JobTemplate.objects.create(
|
||||
name='eng-dev-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_eng,
|
||||
project=self.proj_dev,
|
||||
playbook=self.proj_dev.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_eng_check = self.jt_eng_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_doug,
|
||||
# )
|
||||
self.jt_eng_run = JobTemplate.objects.create(
|
||||
name='eng-dev-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_eng,
|
||||
project=self.proj_dev,
|
||||
playbook=self.proj_dev.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_eng_run = self.jt_eng_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_chuck,
|
||||
# )
|
||||
|
||||
# Support has job templates to check/run the test project onto
|
||||
# their own inventory.
|
||||
self.jt_sup_check = JobTemplate.objects.create(
|
||||
name='sup-test-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_sup,
|
||||
project=self.proj_test,
|
||||
playbook=self.proj_test.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_sup_check = self.jt_sup_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_frank,
|
||||
# )
|
||||
self.jt_sup_run = JobTemplate.objects.create(
|
||||
name='sup-test-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_sup,
|
||||
project=self.proj_test,
|
||||
playbook=self.proj_test.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
credential=self.cred_eve,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_sup_run = self.jt_sup_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
|
||||
# Operations has job templates to check/run the prod project onto
|
||||
# both east and west inventories, by default using the team credential.
|
||||
self.jt_ops_east_check = JobTemplate.objects.create(
|
||||
name='ops-east-prod-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_ops_east,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_east,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_east_check = self.jt_ops_east_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_east_run = JobTemplate.objects.create(
|
||||
name='ops-east-prod-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_ops_east,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_east,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_east_run = self.jt_ops_east_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_west_check = JobTemplate.objects.create(
|
||||
name='ops-west-prod-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_ops_west,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_west,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_west_check = self.jt_ops_west_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_west_run = JobTemplate.objects.create(
|
||||
name='ops-west-prod-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_ops_west,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_west,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_west_run = self.jt_ops_west_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
|
||||
def setUp(self):
|
||||
super(BaseJobTestMixin, self).setUp()
|
||||
self.start_redis()
|
||||
self.setup_instances()
|
||||
self.populate()
|
||||
self.start_queue()
|
||||
|
||||
def tearDown(self):
|
||||
super(BaseJobTestMixin, self).tearDown()
|
||||
self.stop_redis()
|
||||
self.terminate_queue()
|
||||
@ -9,7 +9,6 @@ import struct
|
||||
import threading
|
||||
import time
|
||||
import urlparse
|
||||
import uuid
|
||||
|
||||
# Django
|
||||
import django.test
|
||||
@ -24,18 +23,11 @@ import requests
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTestMixin
|
||||
from base import BaseJobTestMixin
|
||||
|
||||
__all__ = ['JobTemplateTest', 'JobTest', 'JobStartCancelTest',
|
||||
'JobTemplateCallbackTest', 'JobTransactionTest', 'JobTemplateSurveyTest']
|
||||
|
||||
TEST_PLAYBOOK = '''- hosts: all
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: woohoo
|
||||
command: test 1 = 1
|
||||
'''
|
||||
|
||||
TEST_ASYNC_PLAYBOOK = '''
|
||||
- hosts: all
|
||||
gather_facts: false
|
||||
@ -193,508 +185,6 @@ TEST_SURVEY_REQUIREMENTS = '''
|
||||
}
|
||||
'''
|
||||
|
||||
class BaseJobTestMixin(BaseTestMixin):
|
||||
|
||||
|
||||
def _create_inventory(self, name, organization, created_by,
|
||||
groups_hosts_dict):
|
||||
'''Helper method for creating inventory with groups and hosts.'''
|
||||
inventory = organization.inventories.create(
|
||||
name=name,
|
||||
created_by=created_by,
|
||||
)
|
||||
for group_name, host_names in groups_hosts_dict.items():
|
||||
group = inventory.groups.create(
|
||||
name=group_name,
|
||||
created_by=created_by,
|
||||
)
|
||||
for host_name in host_names:
|
||||
host = inventory.hosts.create(
|
||||
name=host_name,
|
||||
created_by=created_by,
|
||||
)
|
||||
group.hosts.add(host)
|
||||
return inventory
|
||||
|
||||
def populate(self):
|
||||
# Here's a little story about the Ansible Bread Company, or ABC. They
|
||||
# make machines that make bread - bakers, slicers, and packagers - and
|
||||
# these machines are each controlled by a Linux boxes, which is in turn
|
||||
# managed by Ansible Commander.
|
||||
|
||||
# Sue is the super user. You don't mess with Sue or you're toast. Ha.
|
||||
self.user_sue = self.make_user('sue', super_user=True)
|
||||
|
||||
# There are three organizations in ABC using Ansible, since it's the
|
||||
# best thing for dev ops automation since, well, sliced bread.
|
||||
|
||||
# Engineering - They design and build the machines.
|
||||
self.org_eng = Organization.objects.create(
|
||||
name='engineering',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# Support - They fix it when it's not working.
|
||||
self.org_sup = Organization.objects.create(
|
||||
name='support',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# Operations - They implement the production lines using the machines.
|
||||
self.org_ops = Organization.objects.create(
|
||||
name='operations',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
|
||||
# Alex is Sue's IT assistant who can also administer all of the
|
||||
# organizations.
|
||||
self.user_alex = self.make_user('alex')
|
||||
self.org_eng.admins.add(self.user_alex)
|
||||
self.org_sup.admins.add(self.user_alex)
|
||||
self.org_ops.admins.add(self.user_alex)
|
||||
|
||||
# Bob is the head of engineering. He's an admin for engineering, but
|
||||
# also a user within the operations organization (so he can see the
|
||||
# results if things go wrong in production).
|
||||
self.user_bob = self.make_user('bob')
|
||||
self.org_eng.admins.add(self.user_bob)
|
||||
self.org_ops.users.add(self.user_bob)
|
||||
|
||||
# Chuck is the lead engineer. He has full reign over engineering, but
|
||||
# no other organizations.
|
||||
self.user_chuck = self.make_user('chuck')
|
||||
self.org_eng.admins.add(self.user_chuck)
|
||||
|
||||
# Doug is the other engineer working under Chuck. He can write
|
||||
# playbooks and check them, but Chuck doesn't quite think he's ready to
|
||||
# run them yet. Poor Doug.
|
||||
self.user_doug = self.make_user('doug')
|
||||
self.org_eng.users.add(self.user_doug)
|
||||
|
||||
# Juan is another engineer working under Chuck. He has a little more freedom
|
||||
# to run playbooks but can't create job templates
|
||||
self.user_juan = self.make_user('juan')
|
||||
self.org_eng.users.add(self.user_juan)
|
||||
|
||||
# Hannibal is Chuck's right-hand man. Chuck usually has him create the job
|
||||
# templates that the rest of the team will use
|
||||
self.user_hannibal = self.make_user('hannibal')
|
||||
self.org_eng.users.add(self.user_hannibal)
|
||||
|
||||
# Eve is the head of support. She can also see what goes on in
|
||||
# operations to help them troubleshoot problems.
|
||||
self.user_eve = self.make_user('eve')
|
||||
self.org_sup.admins.add(self.user_eve)
|
||||
self.org_ops.users.add(self.user_eve)
|
||||
|
||||
# Frank is the other support guy.
|
||||
self.user_frank = self.make_user('frank')
|
||||
self.org_sup.users.add(self.user_frank)
|
||||
|
||||
# Greg is the head of operations.
|
||||
self.user_greg = self.make_user('greg')
|
||||
self.org_ops.admins.add(self.user_greg)
|
||||
|
||||
# Holly is an operations engineer.
|
||||
self.user_holly = self.make_user('holly')
|
||||
self.org_ops.users.add(self.user_holly)
|
||||
|
||||
# Iris is another operations engineer.
|
||||
self.user_iris = self.make_user('iris')
|
||||
self.org_ops.users.add(self.user_iris)
|
||||
|
||||
# Randall and Billybob are new ops interns that ops uses to test
|
||||
# their playbooks and inventory
|
||||
self.user_randall = self.make_user('randall')
|
||||
self.org_ops.users.add(self.user_randall)
|
||||
|
||||
# He works with Randall
|
||||
self.user_billybob = self.make_user('billybob')
|
||||
self.org_ops.users.add(self.user_billybob)
|
||||
|
||||
# Jim is the newest intern. He can login, but can't do anything quite yet
|
||||
# except make everyone else fresh coffee.
|
||||
self.user_jim = self.make_user('jim')
|
||||
|
||||
# There are three main projects, one each for the development, test and
|
||||
# production branches of the playbook repository. All three orgs can
|
||||
# use the production branch, support can use the production and testing
|
||||
# branches, and operations can only use the production branch.
|
||||
self.proj_dev = self.make_project('dev', 'development branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_dev)
|
||||
self.proj_test = self.make_project('test', 'testing branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_test)
|
||||
self.org_sup.projects.add(self.proj_test)
|
||||
self.proj_prod = self.make_project('prod', 'production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_eng.projects.add(self.proj_prod)
|
||||
self.org_sup.projects.add(self.proj_prod)
|
||||
self.org_ops.projects.add(self.proj_prod)
|
||||
|
||||
# Operations also has 2 additional projects specific to the east/west
|
||||
# production environments.
|
||||
self.proj_prod_east = self.make_project('prod-east',
|
||||
'east production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_ops.projects.add(self.proj_prod_east)
|
||||
self.proj_prod_west = self.make_project('prod-west',
|
||||
'west production branch',
|
||||
self.user_sue, TEST_PLAYBOOK)
|
||||
self.org_ops.projects.add(self.proj_prod_west)
|
||||
|
||||
# The engineering organization has a set of servers to use for
|
||||
# development and testing (2 bakers, 1 slicer, 1 packager).
|
||||
self.inv_eng = self._create_inventory(
|
||||
name='engineering environment',
|
||||
organization=self.org_eng,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['eng-baker1', 'eng-baker2'],
|
||||
'slicers': ['eng-slicer1'],
|
||||
'packagers': ['eng-packager1'],
|
||||
},
|
||||
)
|
||||
|
||||
# The support organization has a set of servers to use for
|
||||
# testing and reproducing problems from operations (1 baker, 1 slicer,
|
||||
# 1 packager).
|
||||
self.inv_sup = self._create_inventory(
|
||||
name='support environment',
|
||||
organization=self.org_sup,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['sup-baker1'],
|
||||
'slicers': ['sup-slicer1'],
|
||||
'packagers': ['sup-packager1'],
|
||||
},
|
||||
)
|
||||
|
||||
# The operations organization manages multiple sets of servers for the
|
||||
# east and west production facilities.
|
||||
self.inv_ops_east = self._create_inventory(
|
||||
name='east production environment',
|
||||
organization=self.org_ops,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['east-baker%d' % n for n in range(1, 4)],
|
||||
'slicers': ['east-slicer%d' % n for n in range(1, 3)],
|
||||
'packagers': ['east-packager%d' % n for n in range(1, 3)],
|
||||
},
|
||||
)
|
||||
self.inv_ops_west = self._create_inventory(
|
||||
name='west production environment',
|
||||
organization=self.org_ops,
|
||||
created_by=self.user_sue,
|
||||
groups_hosts_dict={
|
||||
'bakers': ['west-baker%d' % n for n in range(1, 6)],
|
||||
'slicers': ['west-slicer%d' % n for n in range(1, 4)],
|
||||
'packagers': ['west-packager%d' % n for n in range(1, 3)],
|
||||
},
|
||||
)
|
||||
|
||||
# Operations is divided into teams to work on the east/west servers.
|
||||
# Greg and Holly work on east, Greg and iris work on west.
|
||||
self.team_ops_east = self.org_ops.teams.create(
|
||||
name='easterners',
|
||||
created_by=self.user_sue)
|
||||
self.team_ops_east.projects.add(self.proj_prod)
|
||||
self.team_ops_east.projects.add(self.proj_prod_east)
|
||||
self.team_ops_east.users.add(self.user_greg)
|
||||
self.team_ops_east.users.add(self.user_holly)
|
||||
self.team_ops_west = self.org_ops.teams.create(
|
||||
name='westerners',
|
||||
created_by=self.user_sue)
|
||||
self.team_ops_west.projects.add(self.proj_prod)
|
||||
self.team_ops_west.projects.add(self.proj_prod_west)
|
||||
self.team_ops_west.users.add(self.user_greg)
|
||||
self.team_ops_west.users.add(self.user_iris)
|
||||
|
||||
# The south team is no longer active having been folded into the east team
|
||||
self.team_ops_south = self.org_ops.teams.create(
|
||||
name='southerners',
|
||||
created_by=self.user_sue,
|
||||
active=False,
|
||||
)
|
||||
self.team_ops_south.projects.add(self.proj_prod)
|
||||
self.team_ops_south.users.add(self.user_greg)
|
||||
|
||||
# The north team is going to be deleted
|
||||
self.team_ops_north = self.org_ops.teams.create(
|
||||
name='northerners',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.team_ops_north.projects.add(self.proj_prod)
|
||||
self.team_ops_north.users.add(self.user_greg)
|
||||
|
||||
# The testers team are interns that can only check playbooks but can't
|
||||
# run them
|
||||
self.team_ops_testers = self.org_ops.teams.create(
|
||||
name='testers',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.team_ops_testers.projects.add(self.proj_prod)
|
||||
self.team_ops_testers.users.add(self.user_randall)
|
||||
self.team_ops_testers.users.add(self.user_billybob)
|
||||
|
||||
# Each user has his/her own set of credentials.
|
||||
from awx.main.tests.tasks import (TEST_SSH_KEY_DATA,
|
||||
TEST_SSH_KEY_DATA_LOCKED,
|
||||
TEST_SSH_KEY_DATA_UNLOCK)
|
||||
self.cred_sue = self.user_sue.credentials.create(
|
||||
username='sue',
|
||||
password=TEST_SSH_KEY_DATA,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_bob = self.user_bob.credentials.create(
|
||||
username='bob',
|
||||
password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_chuck = self.user_chuck.credentials.create(
|
||||
username='chuck',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_doug = self.user_doug.credentials.create(
|
||||
username='doug',
|
||||
password='doug doesn\'t mind his password being saved. this '
|
||||
'is why we dont\'t let doug actually run jobs.',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_eve = self.user_eve.credentials.create(
|
||||
username='eve',
|
||||
password='ASK',
|
||||
sudo_username='root',
|
||||
sudo_password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_frank = self.user_frank.credentials.create(
|
||||
username='frank',
|
||||
password='fr@nk the t@nk',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_greg = self.user_greg.credentials.create(
|
||||
username='greg',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
|
||||
ssh_key_unlock='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_holly = self.user_holly.credentials.create(
|
||||
username='holly',
|
||||
password='holly rocks',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
self.cred_iris = self.user_iris.credentials.create(
|
||||
username='iris',
|
||||
password='ASK',
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
|
||||
# Each operations team also has shared credentials they can use.
|
||||
self.cred_ops_east = self.team_ops_east.credentials.create(
|
||||
username='east',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
|
||||
ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK,
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
self.cred_ops_west = self.team_ops_west.credentials.create(
|
||||
username='west',
|
||||
password='Heading270',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
self.cred_ops_south = self.team_ops_south.credentials.create(
|
||||
username='south',
|
||||
password='Heading180',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.cred_ops_north = self.team_ops_north.credentials.create(
|
||||
username='north',
|
||||
password='Heading0',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.cred_ops_test = self.team_ops_testers.credentials.create(
|
||||
username='testers',
|
||||
password='HeadingNone',
|
||||
created_by = self.user_sue,
|
||||
)
|
||||
|
||||
self.ops_testers_permission = Permission.objects.create(
|
||||
inventory = self.inv_ops_west,
|
||||
project = self.proj_prod,
|
||||
team = self.team_ops_testers,
|
||||
permission_type = PERM_INVENTORY_CHECK,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.doug_check_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_doug,
|
||||
permission_type = PERM_INVENTORY_CHECK,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.juan_deploy_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_juan,
|
||||
permission_type = PERM_INVENTORY_DEPLOY,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
self.hannibal_create_permission = Permission.objects.create(
|
||||
inventory = self.inv_eng,
|
||||
project = self.proj_dev,
|
||||
user = self.user_hannibal,
|
||||
permission_type = PERM_JOBTEMPLATE_CREATE,
|
||||
created_by = self.user_sue
|
||||
)
|
||||
|
||||
# FIXME: Define explicit permissions for tests.
|
||||
# other django user is on the project team and can deploy
|
||||
#self.permission1 = Permission.objects.create(
|
||||
# inventory = self.inventory,
|
||||
# project = self.project,
|
||||
# team = self.team,
|
||||
# permission_type = PERM_INVENTORY_DEPLOY,
|
||||
# created_by = self.normal_django_user
|
||||
#)
|
||||
# individual permission granted to other2 user, can run check mode
|
||||
#self.permission2 = Permission.objects.create(
|
||||
# inventory = self.inventory,
|
||||
# project = self.project,
|
||||
# user = self.other2_django_user,
|
||||
# permission_type = PERM_INVENTORY_CHECK,
|
||||
# created_by = self.normal_django_user
|
||||
#)
|
||||
|
||||
# Engineering has job templates to check/run the dev project onto
|
||||
# their own inventory.
|
||||
self.jt_eng_check = JobTemplate.objects.create(
|
||||
name='eng-dev-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_eng,
|
||||
project=self.proj_dev,
|
||||
playbook=self.proj_dev.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_eng_check = self.jt_eng_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_doug,
|
||||
# )
|
||||
self.jt_eng_run = JobTemplate.objects.create(
|
||||
name='eng-dev-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_eng,
|
||||
project=self.proj_dev,
|
||||
playbook=self.proj_dev.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_eng_run = self.jt_eng_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_chuck,
|
||||
# )
|
||||
|
||||
# Support has job templates to check/run the test project onto
|
||||
# their own inventory.
|
||||
self.jt_sup_check = JobTemplate.objects.create(
|
||||
name='sup-test-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_sup,
|
||||
project=self.proj_test,
|
||||
playbook=self.proj_test.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_sup_check = self.jt_sup_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# credential=self.cred_frank,
|
||||
# )
|
||||
self.jt_sup_run = JobTemplate.objects.create(
|
||||
name='sup-test-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_sup,
|
||||
project=self.proj_test,
|
||||
playbook=self.proj_test.playbooks[0],
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
credential=self.cred_eve,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_sup_run = self.jt_sup_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
|
||||
# Operations has job templates to check/run the prod project onto
|
||||
# both east and west inventories, by default using the team credential.
|
||||
self.jt_ops_east_check = JobTemplate.objects.create(
|
||||
name='ops-east-prod-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_ops_east,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_east,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_east_check = self.jt_ops_east_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_east_run = JobTemplate.objects.create(
|
||||
name='ops-east-prod-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_ops_east,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_east,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_east_run = self.jt_ops_east_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_west_check = JobTemplate.objects.create(
|
||||
name='ops-west-prod-check',
|
||||
job_type='check',
|
||||
inventory= self.inv_ops_west,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_west,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_west_check = self.jt_ops_west_check.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
self.jt_ops_west_run = JobTemplate.objects.create(
|
||||
name='ops-west-prod-run',
|
||||
job_type='run',
|
||||
inventory= self.inv_ops_west,
|
||||
project=self.proj_prod,
|
||||
playbook=self.proj_prod.playbooks[0],
|
||||
credential=self.cred_ops_west,
|
||||
host_config_key=uuid.uuid4().hex,
|
||||
created_by=self.user_sue,
|
||||
)
|
||||
# self.job_ops_west_run = self.jt_ops_west_run.create_job(
|
||||
# created_by=self.user_sue,
|
||||
# )
|
||||
|
||||
def setUp(self):
|
||||
super(BaseJobTestMixin, self).setUp()
|
||||
self.start_redis()
|
||||
self.setup_instances()
|
||||
self.populate()
|
||||
self.start_queue()
|
||||
|
||||
def tearDown(self):
|
||||
super(BaseJobTestMixin, self).tearDown()
|
||||
self.stop_redis()
|
||||
self.terminate_queue()
|
||||
|
||||
class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
||||
|
||||
JOB_TEMPLATE_FIELDS = ('id', 'type', 'url', 'related', 'summary_fields',
|
||||
@ -2,14 +2,13 @@
|
||||
import json
|
||||
|
||||
# Django
|
||||
import django.test
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTest
|
||||
from awx.main.tests.base import BaseTest, QueueStartStopTestMixin
|
||||
|
||||
__all__ = ['SurveyPasswordTest']
|
||||
__all__ = ['SurveyPasswordRedactedTest']
|
||||
|
||||
PASSWORD="5m/h"
|
||||
ENCRYPTED_STR='$encrypted$'
|
||||
@ -91,32 +90,32 @@ TEST_SINGLE_PASSWORDS = [
|
||||
{
|
||||
'description': 'Single instance with a . after',
|
||||
'text' : 'See spot. See spot run. See spot run %s. That is a fast run.' % PASSWORD,
|
||||
'passwords': [ PASSWORD ],
|
||||
'passwords': [PASSWORD],
|
||||
'occurances': 1,
|
||||
},
|
||||
{
|
||||
'description': 'Single instance with , after',
|
||||
'text': 'Spot goes %s, at a fast pace' % PASSWORD,
|
||||
'passwords': [ PASSWORD ],
|
||||
'passwords': [PASSWORD],
|
||||
'occurances': 1,
|
||||
},
|
||||
{
|
||||
'description': 'Single instance with a space after',
|
||||
'text': 'Is %s very fast?' % PASSWORD,
|
||||
'passwords': [ PASSWORD ],
|
||||
'passwords': [PASSWORD],
|
||||
'occurances': 1,
|
||||
},
|
||||
{
|
||||
'description': 'Many instances, also with newline',
|
||||
'text': 'I think %s is very very fast. If I ran %s for 4 hours how many hours would I run?.\nTrick question. %s for 4 hours would result in running for 4 hours' % (PASSWORD, PASSWORD, PASSWORD),
|
||||
'passwords': [ PASSWORD ],
|
||||
'passwords': [PASSWORD],
|
||||
'occurances': 3,
|
||||
},
|
||||
]
|
||||
passwd = 'my!@#$%^pass&*()_+'
|
||||
TEST_SINGLE_PASSWORDS.append({
|
||||
'description': 'password includes characters not in a-z 0-9 range',
|
||||
'passwords': [ passwd ],
|
||||
'passwords': [passwd],
|
||||
'text': 'Text is fun yeah with passwords %s.' % passwd,
|
||||
'occurances': 1
|
||||
})
|
||||
@ -142,7 +141,7 @@ TESTS = {
|
||||
}
|
||||
}
|
||||
|
||||
class SurveyPasswordBaseTest(BaseTest):
|
||||
class SurveyPasswordBaseTest(BaseTest, QueueStartStopTestMixin):
|
||||
def setUp(self):
|
||||
super(SurveyPasswordBaseTest, self).setUp()
|
||||
self.setup_instances()
|
||||
@ -155,11 +154,41 @@ class SurveyPasswordBaseTest(BaseTest):
|
||||
|
||||
self.check_found(response['content'], ENCRYPTED_STR, test['occurances'], test['description'])
|
||||
|
||||
def _get_url_job_stdout(self, job):
|
||||
job_stdout_url = reverse('api:job_stdout', args=(job.pk,))
|
||||
return self.get(job_stdout_url, expect=200, auth=self.get_super_credentials(), accept='application/json')
|
||||
# TODO: A more complete test would ensure that the variable value isn't found
|
||||
def check_extra_vars_redacted(self, test, response):
|
||||
self.assertIsNotNone(response)
|
||||
# Ensure that all extra_vars of type password have the value '$encrypted$'
|
||||
vars = []
|
||||
for question in test['survey']['spec']:
|
||||
if question['type'] == 'password':
|
||||
vars.append(question['variable'])
|
||||
|
||||
class SurveyPasswordTest(SurveyPasswordBaseTest):
|
||||
extra_vars = json.loads(response['extra_vars'])
|
||||
for var in vars:
|
||||
self.assertIn(var, extra_vars, 'Variable "%s" should exist in "%s"' % (var, extra_vars))
|
||||
self.assertEqual(extra_vars[var], ENCRYPTED_STR)
|
||||
|
||||
def _get_url_job_stdout(self, job):
|
||||
url = reverse('api:job_stdout', args=(job.pk,))
|
||||
return self.get(url, expect=200, auth=self.get_super_credentials(), accept='application/json')
|
||||
|
||||
def _get_url_job_details(self, job):
|
||||
url = reverse('api:job_detail', args=(job.pk,))
|
||||
return self.get(url, expect=200, auth=self.get_super_credentials(), accept='application/json')
|
||||
|
||||
class SurveyPasswordRedactedTest(SurveyPasswordBaseTest):
|
||||
'''
|
||||
Transpose TEST[]['tests'] to the below format. A more flat format."
|
||||
[
|
||||
{
|
||||
'text': '...',
|
||||
'description': '...',
|
||||
...,
|
||||
'job': '...',
|
||||
'survey': '...'
|
||||
},
|
||||
]
|
||||
'''
|
||||
def setup_test(self, test_name):
|
||||
blueprint = TESTS[test_name]
|
||||
self.tests[test_name] = []
|
||||
@ -179,25 +208,30 @@ class SurveyPasswordTest(SurveyPasswordBaseTest):
|
||||
job.result_stdout_text = test['text']
|
||||
job.save()
|
||||
test['job'] = job
|
||||
test['survey'] = blueprint['survey']
|
||||
self.tests[test_name].append(test)
|
||||
|
||||
def setUp(self):
|
||||
super(SurveyPasswordTest, self).setUp()
|
||||
super(SurveyPasswordRedactedTest, self).setUp()
|
||||
|
||||
self.tests = {}
|
||||
self.setup_test('simple')
|
||||
self.setup_test('complex')
|
||||
|
||||
# should redact single variable survey
|
||||
def test_survey_password_redact_simple_survey(self):
|
||||
def test_redact_stdout_simple_survey(self):
|
||||
for test in self.tests['simple']:
|
||||
response = self._get_url_job_stdout(test['job'])
|
||||
self.check_passwords_redacted(test, response)
|
||||
|
||||
# should redact multiple variables survey
|
||||
def test_survey_password_redact_complex_survey(self):
|
||||
def test_redact_stdout_complex_survey(self):
|
||||
for test in self.tests['complex']:
|
||||
response = self._get_url_job_stdout(test['job'])
|
||||
self.check_passwords_redacted(test, response)
|
||||
|
||||
|
||||
# should redact values in extra_vars
|
||||
def test_redact_job_extra_vars(self):
|
||||
for test in self.tests['simple']:
|
||||
response = self._get_url_job_details(test['job'])
|
||||
self.check_extra_vars_redacted(test, response)
|
||||
|
||||
@ -13,7 +13,6 @@ import unittest
|
||||
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.timezone import now
|
||||
|
||||
# Django-CRUM
|
||||
|
||||
@ -75,4 +75,4 @@ class UnifiedJobStdoutRedactedTests(BaseLiveServerTest, QueueStartStopTestMixin)
|
||||
self._test_redaction_enabled('html')
|
||||
|
||||
def test_redaction_enabled_txt(self):
|
||||
self._test_redaction_enabled('txt')
|
||||
self._test_redaction_enabled('txt')
|
||||
|
||||
@ -45,6 +45,17 @@ import {UsersList, UsersAdd, UsersEdit} from 'tower/controllers/Users';
|
||||
import {TeamsList, TeamsAdd, TeamsEdit} from 'tower/controllers/Teams';
|
||||
import {PermissionsAdd, PermissionsList, PermissionsEdit} from 'tower/controllers/Permissions';
|
||||
|
||||
import 'tower/shared/RestServices';
|
||||
import 'tower/shared/api-loader';
|
||||
import 'tower/shared/form-generator';
|
||||
import 'tower/shared/Modal';
|
||||
import 'tower/shared/prompt-dialog';
|
||||
import 'tower/shared/directives';
|
||||
import 'tower/shared/filters';
|
||||
import 'tower/shared/InventoryTree';
|
||||
import 'tower/shared/Timer';
|
||||
import 'tower/shared/Socket';
|
||||
|
||||
|
||||
|
||||
var tower = angular.module('Tower', [
|
||||
|
||||
@ -422,6 +422,7 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
||||
$scope.removeSurveySaved = $scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
$('#job_templates_survey_enabled_chbox').attr('checked', true);
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
@ -451,6 +452,7 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
$scope.invalid_survey = false;
|
||||
if ($scope.removeGatherFormFields) {
|
||||
$scope.removeGatherFormFields();
|
||||
}
|
||||
@ -525,7 +527,14 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $
|
||||
});
|
||||
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.$emit("PromptForSurvey");
|
||||
// $scope.$emit("PromptForSurvey");
|
||||
|
||||
// The original design for this was a pop up that would prompt the user if they wanted to create a
|
||||
// survey, because they had enabled one but not created it yet. We switched this for now so that
|
||||
// an error message would be displayed by the survey buttons that tells the user to add a survey or disabled
|
||||
// surveys.
|
||||
$scope.invalid_survey = true;
|
||||
return;
|
||||
} else {
|
||||
$scope.$emit("GatherFormFields");
|
||||
}
|
||||
@ -837,6 +846,7 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
||||
$scope.removeSurveySaved = $scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
$('#job_templates_survey_enabled_chbox').attr('checked', true);
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
@ -905,7 +915,7 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
||||
|
||||
// Save changes to the parent
|
||||
$scope.formSave = function () {
|
||||
|
||||
$scope.invalid_survey = false;
|
||||
if ($scope.removeGatherFormFields) {
|
||||
$scope.removeGatherFormFields();
|
||||
}
|
||||
@ -965,7 +975,14 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
||||
});
|
||||
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.$emit("PromptForSurvey");
|
||||
// $scope.$emit("PromptForSurvey");
|
||||
|
||||
// The original design for this was a pop up that would prompt the user if they wanted to create a
|
||||
// survey, because they had enabled one but not created it yet. We switched this for now so that
|
||||
// an error message would be displayed by the survey buttons that tells the user to add a survey or disabled
|
||||
// surveys.
|
||||
$scope.invalid_survey = true;
|
||||
return;
|
||||
} else {
|
||||
$scope.$emit("GatherFormFields");
|
||||
}
|
||||
|
||||
@ -202,19 +202,25 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
}
|
||||
$scope.teamLoadedRemove = $scope.$on('teamLoaded', function () {
|
||||
CheckAccess({ scope: $scope });
|
||||
Rest.setUrl($scope.organization_url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.organization_name = data.name;
|
||||
master.organization_name = data.name;
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve organization: ' +
|
||||
$scope.orgnization_url + '. GET status: ' + status });
|
||||
});
|
||||
for (var set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
if ($scope.organization_url) {
|
||||
Rest.setUrl($scope.organization_url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.organization_name = data.name;
|
||||
master.organization_name = data.name;
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve organization: ' +
|
||||
$scope.orgnization_url + '. GET status: ' + status });
|
||||
});
|
||||
for (var set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
} else {
|
||||
$scope.organization_name = "";
|
||||
master.organization_name = "";
|
||||
Wait('stop');
|
||||
}
|
||||
});
|
||||
|
||||
@ -387,4 +393,4 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
TeamsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm',
|
||||
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit',
|
||||
'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait', 'Stream'
|
||||
];
|
||||
];
|
||||
|
||||
@ -273,19 +273,9 @@ export default
|
||||
column: 2,
|
||||
control: '<button type="button" class="btn btn-sm btn-primary" id="job_templates_create_survey_btn" ng-show="survey_enabled" ng-click="addSurvey()"><i class="fa fa-pencil"></i> Create Survey</button>'+
|
||||
'<button style="display:none;" type="button" class="btn btn-sm btn-primary" id="job_templates_edit_survey_btn" ng-show="survey_enabled" ng-click="editSurvey()"><i class="fa fa-pencil"></i> Edit Survey</button>'+
|
||||
'<button style="display:none;margin-left:5px" type="button" class="btn btn-sm btn-primary" id="job_templates_delete_survey_btn" ng-show="survey_enabled" ng-click="deleteSurvey()"><i class="fa fa-trash-o"></i> Delete Survey</button>'
|
||||
// label: 'Create Survey',
|
||||
// type: 'text',
|
||||
// addRequired: false,
|
||||
// editRequired: false,
|
||||
// // readonly: true,
|
||||
// // ngShow: "survey_enabled",
|
||||
// column: 2,
|
||||
// awPopOver: "survey_help",
|
||||
// awPopOverWatch: "survey_help",
|
||||
// dataPlacement: 'right',
|
||||
// dataTitle: 'Provisioning Callback URL',
|
||||
// dataContainer: "body"
|
||||
'<button style="display:none;margin-left:5px" type="button" class="btn btn-sm btn-primary" id="job_templates_delete_survey_btn" ng-show="survey_enabled" ng-click="deleteSurvey()"><i class="fa fa-trash-o"></i> Delete Survey</button>'+
|
||||
// '<div class="error ng-hide" id="job-template-survey-error" ng-show="survey_enabled === true && survey_exists!==true">A survey is enabled but it does not exist. Create a survey or disable the survey. </div>'
|
||||
'<div class="error ng-hide" id="job-template-survey-error" ng-show="invalid_survey">A survey is enabled but it does not exist. Create a survey or uncheck the Enable Survey box to disable the survey. </div>'
|
||||
},
|
||||
allow_callbacks: {
|
||||
label: 'Allow Provisioning Callbacks',
|
||||
|
||||
@ -106,13 +106,13 @@ export default
|
||||
control:'<div class="row">'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="text_min"><span class="label-text">Minimum Length</span></label><input id="text_min" type="number" name="text_min" ng-model="text_min" min=0 aw-min="0" aw-max="text_max" class="form-control" integer />'+
|
||||
'<div class="error" ng-show="survey_question_form.text_min.$error.integer || survey_question_form.text_min.$error.number">The minimum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.text_min.$error.integer || survey_question_form.text_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.text_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.text_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
|
||||
'</div>'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="text_max"><span class="label-text">Maximum Length</span></label><input id="text_max" type="number" name="text_max" ng-model="text_max" aw-min="text_min || 0" min=0 class="form-control" integer >'+
|
||||
'<div class="error" ng-show="survey_question_form.text_max.$error.integer || survey_question_form.text_max.$error.number">The maximum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.text_max.$error.integer || survey_question_form.text_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole nnumber.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.text_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
|
||||
'</div>'+
|
||||
'</div>',
|
||||
@ -127,13 +127,13 @@ export default
|
||||
control:'<div class="row">'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="textarea_min"><span class="label-text">Minimum Length</span></label><input id="textarea_min" type="number" name="textarea_min" ng-model="textarea_min" min=0 aw-min="0" aw-max="textarea_max" class="form-control" integer />'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_min.$error.integer || survey_question_form.textarea_min.$error.number">The minimum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_min.$error.integer || survey_question_form.textarea_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
|
||||
'</div>'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="textarea_max"><span class="label-text">Maximum Length</span></label><input id="textarea_max" type="number" name="textarea_max" ng-model="textarea_max" aw-min="textarea_min || 0" min=0 class="form-control" integer >'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_max.$error.integer || survey_question_form.textarea_max.$error.number">The maximum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_max.$error.integer || survey_question_form.textarea_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.textarea_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
|
||||
'</div>'+
|
||||
'</div>',
|
||||
@ -148,13 +148,13 @@ export default
|
||||
control:'<div class="row">'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="password_min"><span class="label-text">Minimum Length</span></label><input id="password_min" type="number" name="password_min" ng-model="password_min" min=0 aw-min="0" aw-max="password_max" class="form-control" integer />'+
|
||||
'<div class="error" ng-show="survey_question_form.password_min.$error.integer || survey_question_form.password_min.$error.number">The minimum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.password_min.$error.integer || survey_question_form.password_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.password_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.password_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
|
||||
'</div>'+
|
||||
'<div class="col-xs-6">'+
|
||||
'<label for="password_max"><span class="label-text">Maximum Length</span></label><input id="password_max" type="number" name="password_max" ng-model="password_max" aw-min="password_min || 0" min=0 class="form-control" integer >'+
|
||||
'<div class="error" ng-show="survey_question_form.password_max.$error.integer || survey_question_form.password_max.$error.number">The maximum length you entered is not a number. Please enter a number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.password_max.$error.integer || survey_question_form.password_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole number.</div>'+
|
||||
'<div class="error" ng-show="survey_question_form.password_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
|
||||
'</div>'+
|
||||
'</div>',
|
||||
|
||||
@ -151,9 +151,22 @@ export default
|
||||
e = angular.element(document.getElementById('prompt_for_days_form'));
|
||||
scope.prompt_for_days_form.days_to_keep.$setViewValue(30);
|
||||
$compile(e)(scope);
|
||||
$('#prompt-for-days-launch').attr("ng-disabled", 'prompt_for_days_form.$invalid');
|
||||
e = angular.element(document.getElementById('prompt-for-days-launch'));
|
||||
$compile(e)(scope);
|
||||
|
||||
// this is a work-around for getting awMax to work (without
|
||||
// clearing out the form)
|
||||
scope.$watch('days_to_keep', function(newVal) { // oldVal, scope) { // unused params get caught by jshint
|
||||
if (!newVal) {
|
||||
$('#prompt-for-days-launch').prop("disabled", true);
|
||||
} else if (isNaN(newVal)) {
|
||||
$('#prompt-for-days-launch').prop("disabled", true);
|
||||
} else if (newVal <= 0) {
|
||||
$('#prompt-for-days-launch').prop("disabled", true);
|
||||
} else if (newVal > 9999) {
|
||||
$('#prompt-for-days-launch').prop("disabled", true);
|
||||
} else {
|
||||
$('#prompt-for-days-launch').prop("disabled", false);
|
||||
}
|
||||
});
|
||||
},
|
||||
buttons: [{
|
||||
"label": "Cancel",
|
||||
|
||||
@ -39,7 +39,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if(!Empty(data.extra_vars)){
|
||||
data.extra_vars = ToJSON('json', data.extra_vars, false);
|
||||
data.extra_vars = ToJSON('yaml', data.extra_vars, false);
|
||||
$.each(data.extra_vars, function(key,value){
|
||||
job_launch_data.extra_vars[key] = value;
|
||||
});
|
||||
@ -77,7 +77,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
|
||||
for (var i=0; i < scope.survey_questions.length; i++){
|
||||
var fld = scope.survey_questions[i].variable;
|
||||
// grab all survey questions that have answers
|
||||
if(scope[fld]) {
|
||||
if(scope.survey_questions[i].required || (scope.survey_questions[i].required === false && scope[fld].toString()!=="")) {
|
||||
job_launch_data.extra_vars[fld] = scope[fld];
|
||||
}
|
||||
// for optional text and text-areas, submit a blank string if min length is 0
|
||||
@ -544,7 +544,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
|
||||
}
|
||||
|
||||
if(question.type === "textarea"){
|
||||
scope[question.variable] = question.default || question.default_textarea;
|
||||
scope[question.variable] = (question.default_textarea) ? question.default_textarea : (question.default) ? question.default : "";
|
||||
minlength = (!Empty(question.min)) ? Number(question.min) : "";
|
||||
maxlength =(!Empty(question.max)) ? Number(question.max) : "" ;
|
||||
html+='<textarea id="'+question.variable+'" name="'+question.variable+'" ng-model="'+question.variable+'" '+
|
||||
|
||||
@ -756,6 +756,7 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper',
|
||||
scope.default_float = "";
|
||||
scope.default_int = "";
|
||||
scope.default_textarea = "";
|
||||
scope.default_password = "" ;
|
||||
scope.choices = "";
|
||||
scope.text_min = "";
|
||||
scope.text_max = "" ;
|
||||
|
||||
@ -10,9 +10,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import Utilities from './Utilities';
|
||||
|
||||
|
||||
angular.module('AuthService', ['ngCookies', 'Utilities'])
|
||||
export default
|
||||
angular.module('AuthService', ['ngCookies', Utilities.name])
|
||||
|
||||
.factory('Authorization', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath', 'Store',
|
||||
function ($http, $rootScope, $location, $cookieStore, GetBasePath, Store) {
|
||||
@ -155,4 +156,4 @@ angular.module('AuthService', ['ngCookies', 'Utilities'])
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
|
||||
|
||||
.factory('SortNodes', [
|
||||
@ -603,4 +603,4 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
|
||||
/**
|
||||
@ -51,9 +51,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import AuthService from './AuthService';
|
||||
|
||||
|
||||
angular.module('RestServices', ['ngCookies', 'AuthService'])
|
||||
export default
|
||||
angular.module('RestServices', ['ngCookies', AuthService.name])
|
||||
.factory('Rest', ['$http', '$rootScope', '$cookieStore', '$q', 'Authorization',
|
||||
function ($http, $rootScope, $cookieStore, $q, Authorization) {
|
||||
return {
|
||||
@ -267,4 +268,4 @@ angular.module('RestServices', ['ngCookies', 'AuthService'])
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
@ -19,6 +19,7 @@
|
||||
* @methodOf lib.ansible.function:Socket
|
||||
* @description
|
||||
*/
|
||||
export default
|
||||
angular.module('SocketIO', ['AuthService', 'Utilities'])
|
||||
|
||||
.factory('Socket', ['$rootScope', '$location', '$log', 'Authorization', 'Store', function ($rootScope, $location, $log, Authorization, Store) {
|
||||
@ -71,7 +72,7 @@ angular.module('SocketIO', ['AuthService', 'Utilities'])
|
||||
$log.debug('Socket connecting to: ' + url);
|
||||
self.scope.socket_url = url;
|
||||
self.socket = io.connect(url, {
|
||||
query: "Token="+token,
|
||||
query: "Token="+token,
|
||||
headers:
|
||||
{
|
||||
'Authorization': 'Token ' + token, // i don't think these are actually inserted into the header--jt
|
||||
@ -115,6 +116,7 @@ angular.module('SocketIO', ['AuthService', 'Utilities'])
|
||||
});
|
||||
self.socket.on('error', function(reason) {
|
||||
var r = reason || 'connection refused by host';
|
||||
console.error(reason)
|
||||
$log.debug('Socket error: ' + r);
|
||||
$log.error('Socket error: ' + r);
|
||||
self.scope.$apply(function() {
|
||||
@ -156,7 +158,7 @@ angular.module('SocketIO', ['AuthService', 'Utilities'])
|
||||
if (self.socket.socket.connected) {
|
||||
self.scope.socketStatus = 'ok';
|
||||
}
|
||||
else if (self.socket.socket.connecting || self.socket.socket.reconnecting) {
|
||||
else if (self.socket.socket.connecting || self.socket.reconnecting) {
|
||||
self.scope.socketStatus = 'connecting';
|
||||
}
|
||||
else {
|
||||
@ -18,6 +18,7 @@
|
||||
* @methodOf lib.ansible.function:Timer
|
||||
* @description
|
||||
*/
|
||||
export default
|
||||
angular.module('TimerService', ['ngCookies', 'Utilities'])
|
||||
.factory('Timer', ['$rootScope', '$cookieStore', '$location', 'GetBasePath', 'Empty',
|
||||
function ($rootScope, $cookieStore) {
|
||||
@ -65,4 +66,4 @@ angular.module('TimerService', ['ngCookies', 'Utilities'])
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('Utilities', ['RestServices', 'Utilities'])
|
||||
|
||||
/**
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('ApiLoader', ['Utilities'])
|
||||
|
||||
.factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors',
|
||||
@ -72,4 +73,4 @@ angular.module('ApiLoader', ['Utilities'])
|
||||
return $rootScope.defaultUrls[set];
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
@ -13,6 +13,9 @@
|
||||
|
||||
/* global chkPass:false */
|
||||
|
||||
import {chkPass} from './pwdmeter';
|
||||
|
||||
export default
|
||||
angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper'])
|
||||
|
||||
// awpassmatch: Add to password_confirm field. Will test if value
|
||||
@ -534,15 +537,23 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Job
|
||||
|
||||
$(element).one('click', showPopover);
|
||||
|
||||
$(element).on('shown.bs.popover', function() {
|
||||
function bindPopoverDismiss() {
|
||||
$('body').one('click.popover' + id_to_close, function(e) {
|
||||
if ($(e.target).parents(id_to_close).length === 0) {
|
||||
// case: you clicked to open the popover and then you
|
||||
// clicked outside of it...hide it.
|
||||
$(element).popover('hide');
|
||||
} else {
|
||||
// case: you clicked to open the popover and then you
|
||||
// clicked inside the popover
|
||||
bindPopoverDismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(element).on('shown.bs.popover', function() {
|
||||
bindPopoverDismiss();
|
||||
$(document).on('keydown.popover', dismissOnEsc);
|
||||
|
||||
});
|
||||
|
||||
$(element).on('hidden.bs.popover', function() {
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('AWFilters', [])
|
||||
|
||||
//
|
||||
@ -91,4 +92,4 @@ angular.module('AWFilters', [])
|
||||
}
|
||||
return input;
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
@ -132,9 +132,11 @@
|
||||
* Applying CodeMirror to the text area is handled by ParseTypeChange() found in helpers/Parse.js. Within the controller will be a call to ParseTypeChange that creates the CodeMirror object and sets up the required $scope methods for handles getting, settting and type conversion.
|
||||
*/
|
||||
|
||||
import GeneratorHelpers from './generator-helpers';
|
||||
import ListGenerator from './list-generator';
|
||||
|
||||
|
||||
angular.module('FormGenerator', ['GeneratorHelpers', 'Utilities', 'ListGenerator'])
|
||||
export default
|
||||
angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', ListGenerator.name])
|
||||
|
||||
.factory('GenerateForm', ['$rootScope', '$location', '$compile', 'GenerateList', 'SearchWidget', 'PaginateWidget', 'Attr',
|
||||
'Icon', 'Column', 'NavigationLink', 'HelpCollapse', 'Button', 'DropDown', 'Empty', 'SelectIcon', 'Store',
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('GeneratorHelpers', [])
|
||||
|
||||
.factory('Attr', function () {
|
||||
@ -97,7 +97,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export default
|
||||
angular.module('ListGenerator', ['GeneratorHelpers'])
|
||||
.factory('GenerateList', ['$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon',
|
||||
'Column', 'DropDown', 'NavigationLink', 'Button', 'SelectIcon', 'Breadcrumbs',
|
||||
@ -25,6 +25,8 @@
|
||||
* @methodOf lib.ansible.function:prompt-dialog
|
||||
* @description discuss difference b/t this and other modals
|
||||
*/
|
||||
|
||||
export default
|
||||
angular.module('PromptDialog', ['Utilities'])
|
||||
.factory('Prompt', ['$sce',
|
||||
function ($sce) {
|
||||
@ -42,7 +42,7 @@ String.prototype.strReverse = function () {
|
||||
|
||||
var nScore = 0;
|
||||
|
||||
function chkPass(pwd) {
|
||||
export function chkPass(pwd) {
|
||||
// Simultaneous variable declaration and value assignment aren't supported in IE apparently
|
||||
// so I'm forced to assign the same value individually per var to support a crappy browser *sigh*
|
||||
var nLength = 0,
|
||||
@ -324,4 +324,4 @@ function chkPass(pwd) {
|
||||
}
|
||||
|
||||
return nScore;
|
||||
}
|
||||
}
|
||||
9
awx/ui/static/js/system-status/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import conversionService from './conversions.service'
|
||||
import smartStatusGraph from './smart-status.directive'
|
||||
import controller from './smart-status.controller'
|
||||
|
||||
export default
|
||||
angular.module('systemStatus', [])
|
||||
.service('conversions', conversionService)
|
||||
.directive('smartStatusGraph', smartStatusGraph)
|
||||
.controller('smartStatusLoad', controller);
|
||||
21
awx/ui/static/lib/jquery-ui/.bower.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "jquery-ui",
|
||||
"version": "1.11.3",
|
||||
"main": [
|
||||
"jquery-ui.js"
|
||||
],
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6"
|
||||
},
|
||||
"homepage": "https://github.com/components/jqueryui",
|
||||
"_release": "1.11.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.11.3",
|
||||
"commit": "7e2d6bec1c729925d9ad7c0c9ab4b561174efa99"
|
||||
},
|
||||
"_source": "git://github.com/components/jqueryui.git",
|
||||
"_target": "~1.11.1",
|
||||
"_originalSource": "jquery-ui"
|
||||
}
|
||||
4
awx/ui/static/lib/jquery-ui/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
components
|
||||
composer.lock
|
||||
vendor
|
||||
.DS_Store
|
||||
284
awx/ui/static/lib/jquery-ui/AUTHORS.txt
Normal file
@ -0,0 +1,284 @@
|
||||
Authors ordered by first contribution
|
||||
A list of current team members is available at http://jqueryui.com/about
|
||||
|
||||
Paul Bakaus <paul.bakaus@gmail.com>
|
||||
Richard Worth <rdworth@gmail.com>
|
||||
Yehuda Katz <wycats@gmail.com>
|
||||
Sean Catchpole <sean@sunsean.com>
|
||||
John Resig <jeresig@gmail.com>
|
||||
Tane Piper <piper.tane@gmail.com>
|
||||
Dmitri Gaskin <dmitrig01@gmail.com>
|
||||
Klaus Hartl <klaus.hartl@gmail.com>
|
||||
Stefan Petre <stefan.petre@gmail.com>
|
||||
Gilles van den Hoven <gilles@webunity.nl>
|
||||
Micheil Bryan Smith <micheil@brandedcode.com>
|
||||
Jörn Zaefferer <joern.zaefferer@gmail.com>
|
||||
Marc Grabanski <m@marcgrabanski.com>
|
||||
Keith Wood <kbwood@iinet.com.au>
|
||||
Brandon Aaron <brandon.aaron@gmail.com>
|
||||
Scott González <scott.gonzalez@gmail.com>
|
||||
Eduardo Lundgren <eduardolundgren@gmail.com>
|
||||
Aaron Eisenberger <aaronchi@gmail.com>
|
||||
Joan Piedra <theneojp@gmail.com>
|
||||
Bruno Basto <b.basto@gmail.com>
|
||||
Remy Sharp <remy@leftlogic.com>
|
||||
Bohdan Ganicky <bohdan.ganicky@gmail.com>
|
||||
David Bolter <david.bolter@gmail.com>
|
||||
Chi Cheng <cloudream@gmail.com>
|
||||
Ca-Phun Ung <pazu2k@gmail.com>
|
||||
Ariel Flesler <aflesler@gmail.com>
|
||||
Maggie Wachs <maggie@filamentgroup.com>
|
||||
Scott Jehl <scott@scottjehl.com>
|
||||
Todd Parker <todd@filamentgroup.com>
|
||||
Andrew Powell <andrew@shellscape.org>
|
||||
Brant Burnett <btburnett3@gmail.com>
|
||||
Douglas Neiner <doug@dougneiner.com>
|
||||
Paul Irish <paul.irish@gmail.com>
|
||||
Ralph Whitbeck <ralph.whitbeck@gmail.com>
|
||||
Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
Dominique Vincent <dominique.vincent@toitl.com>
|
||||
Jack Hsu <jack.hsu@gmail.com>
|
||||
Adam Sontag <ajpiano@ajpiano.com>
|
||||
Carl Fürstenberg <carl@excito.com>
|
||||
Kevin Dalman <development@allpro.net>
|
||||
Alberto Fernández Capel <afcapel@gmail.com>
|
||||
Jacek Jędrzejewski (http://jacek.jedrzejewski.name)
|
||||
Ting Kuei <ting@kuei.com>
|
||||
Samuel Cormier-Iijima <sam@chide.it>
|
||||
Jon Palmer <jonspalmer@gmail.com>
|
||||
Ben Hollis <bhollis@amazon.com>
|
||||
Justin MacCarthy <Justin@Rubystars.biz>
|
||||
Eyal Kobrigo <kobrigo@hotmail.com>
|
||||
Tiago Freire <tiago.freire@gmail.com>
|
||||
Diego Tres <diegotres@gmail.com>
|
||||
Holger Rüprich <holger@rueprich.de>
|
||||
Ziling Zhao <zizhao@cisco.com>
|
||||
Mike Alsup <malsup@gmail.com>
|
||||
Robson Braga Araujo <robsonbraga@gmail.com>
|
||||
Pierre-Henri Ausseil <ph.ausseil@gmail.com>
|
||||
Christopher McCulloh <cmcculloh@gmail.com>
|
||||
Andrew Newcomb <ext.github@preceptsoftware.co.uk>
|
||||
Lim Chee Aun <cheeaun@gmail.com>
|
||||
Jorge Barreiro <yortx.barry@gmail.com>
|
||||
Daniel Steigerwald <daniel@steigerwald.cz>
|
||||
John Firebaugh <john_firebaugh@bigfix.com>
|
||||
John Enters <github@darkdark.net>
|
||||
Andrey Kapitcyn <ru.m157y@gmail.com>
|
||||
Dmitry Petrov <dpetroff@gmail.com>
|
||||
Eric Hynds <eric@hynds.net>
|
||||
Chairat Sunthornwiphat <pipo@sixhead.com>
|
||||
Josh Varner <josh.varner@gmail.com>
|
||||
Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
Jay Merrifield <fracmak@gmail.com>
|
||||
J. Ryan Stinnett <jryans@gmail.com>
|
||||
Peter Heiberg <peter@heiberg.se>
|
||||
Alex Dovenmuehle <adovenmuehle@gmail.com>
|
||||
Jamie Gegerson <git@jamiegegerson.com>
|
||||
Raymond Schwartz <skeetergraphics@gmail.com>
|
||||
Phillip Barnes <philbar@gmail.com>
|
||||
Kyle Wilkinson <kai@wikyd.org>
|
||||
Khaled AlHourani <me@khaledalhourani.com>
|
||||
Marian Rudzynski <mr@impaled.org>
|
||||
Jean-Francois Remy <jeff@melix.org>
|
||||
Doug Blood
|
||||
Filippo Cavallarin <filippo.cavallarin@codseq.it>
|
||||
Heiko Henning <heiko@thehennings.ch>
|
||||
Aliaksandr Rahalevich <saksmlz@gmail.com>
|
||||
Mario Visic <mario@mariovisic.com>
|
||||
Xavi Ramirez <xavi.rmz@gmail.com>
|
||||
Max Schnur <max.schnur@gmail.com>
|
||||
Saji Nediyanchath <saji89@gmail.com>
|
||||
Corey Frang <gnarf37@gmail.com>
|
||||
Aaron Peterson <aaronp123@yahoo.com>
|
||||
Ivan Peters <ivan@ivanpeters.com>
|
||||
Mohamed Cherif Bouchelaghem <cherifbouchelaghem@yahoo.fr>
|
||||
Marcos Sousa <falecomigo@marcossousa.com>
|
||||
Michael DellaNoce <mdellanoce@mailtrust.com>
|
||||
George Marshall <echosx@gmail.com>
|
||||
Tobias Brunner <tobias@strongswan.org>
|
||||
Martin Solli <msolli@gmail.com>
|
||||
David Petersen <public@petersendidit.com>
|
||||
Dan Heberden <danheberden@gmail.com>
|
||||
William Kevin Manire <williamkmanire@gmail.com>
|
||||
Gilmore Davidson <gilmoreorless@gmail.com>
|
||||
Michael Wu <michaelmwu@gmail.com>
|
||||
Adam Parod <mystic414@gmail.com>
|
||||
Guillaume Gautreau <guillaume+github@ghusse.com>
|
||||
Marcel Toele <EleotleCram@gmail.com>
|
||||
Dan Streetman <ddstreet@ieee.org>
|
||||
Matt Hoskins <matt@nipltd.com>
|
||||
Giovanni Giacobbi <giovanni@giacobbi.net>
|
||||
Kyle Florence <kyle.florence@gmail.com>
|
||||
Pavol Hluchý <lopo@losys.sk>
|
||||
Hans Hillen <hans.hillen@gmail.com>
|
||||
Mark Johnson <virgofx@live.com>
|
||||
Trey Hunner <treyhunner@gmail.com>
|
||||
Shane Whittet <whittet@gmail.com>
|
||||
Edward A Faulkner <ef@alum.mit.edu>
|
||||
Adam Baratz <adam@adambaratz.com>
|
||||
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||
Eike Send <eike.send@gmail.com>
|
||||
Kris Borchers <kris.borchers@gmail.com>
|
||||
Eddie Monge <eddie@eddiemonge.com>
|
||||
Israel Tsadok <itsadok@gmail.com>
|
||||
Carson McDonald <carson@ioncannon.net>
|
||||
Jason Davies <jason@jasondavies.com>
|
||||
Garrison Locke <gplocke@gmail.com>
|
||||
David Murdoch <david@davidmurdoch.com>
|
||||
Benjamin Scott Boyle <benjamins.boyle@gmail.com>
|
||||
Jesse Baird <jebaird@gmail.com>
|
||||
Jonathan Vingiano <jvingiano@gmail.com>
|
||||
Dylan Just <dev@ephox.com>
|
||||
Hiroshi Tomita <tomykaira@gmail.com>
|
||||
Glenn Goodrich <glenn.goodrich@gmail.com>
|
||||
Tarafder Ashek-E-Elahi <mail.ashek@gmail.com>
|
||||
Ryan Neufeld <ryan@neufeldmail.com>
|
||||
Marc Neuwirth <marc.neuwirth@gmail.com>
|
||||
Philip Graham <philip.robert.graham@gmail.com>
|
||||
Benjamin Sterling <benjamin.sterling@kenzomedia.com>
|
||||
Wesley Walser <waw325@gmail.com>
|
||||
Kouhei Sutou <kou@clear-code.com>
|
||||
Karl Kirch <karlkrch@gmail.com>
|
||||
Chris Kelly <ckdake@ckdake.com>
|
||||
Jay Oster <jay@loyalize.com>
|
||||
Alexander Polomoshnov <alex.polomoshnov@gmail.com>
|
||||
David Leal <dgleal@gmail.com>
|
||||
Igor Milla <igor.fsp.milla@gmail.com>
|
||||
Dave Methvin <dave.methvin@gmail.com>
|
||||
Florian Gutmann <f.gutmann@chronimo.com>
|
||||
Marwan Al Jubeh <marwan.aljubeh@gmail.com>
|
||||
Milan Broum <midlis@googlemail.com>
|
||||
Sebastian Sauer <info@dynpages.de>
|
||||
Gaëtan Muller <m.gaetan89@gmail.com>
|
||||
William Griffiths <william@ycymro.com>
|
||||
Stojce Slavkovski <stojce@gmail.com>
|
||||
David Soms <david.soms@gmail.com>
|
||||
David De Sloovere <david.desloovere@outlook.com>
|
||||
Michael P. Jung <michael.jung@terreon.de>
|
||||
Shannon Pekary <spekary@gmail.com>
|
||||
Matthew Edward Hutton <meh@corefiling.co.uk>
|
||||
James Khoury <james@jameskhoury.com>
|
||||
Rob Loach <robloach@gmail.com>
|
||||
Alberto Monteiro <betimbrasil@gmail.com>
|
||||
Alex Rhea <alex.rhea@gmail.com>
|
||||
Krzysztof Rosiński <rozwell69@gmail.com>
|
||||
Ryan Olton <oltonr@gmail.com>
|
||||
Genie <386@mail.com>
|
||||
Rick Waldron <waldron.rick@gmail.com>
|
||||
Ian Simpson <spoonlikesham@gmail.com>
|
||||
Lev Kitsis <spam4lev@gmail.com>
|
||||
TJ VanToll <tj.vantoll@gmail.com>
|
||||
Justin Domnitz <jdomnitz@gmail.com>
|
||||
Douglas Cerna <douglascerna@yahoo.com>
|
||||
Bert ter Heide <bertjh@hotmail.com>
|
||||
Jasvir Nagra <jasvir@gmail.com>
|
||||
Yuriy Khabarov <13real008@gmail.com>
|
||||
Harri Kilpiö <harri.kilpio@gmail.com>
|
||||
Lado Lomidze <lado.lomidze@gmail.com>
|
||||
Amir E. Aharoni <amir.aharoni@mail.huji.ac.il>
|
||||
Simon Sattes <simon.sattes@gmail.com>
|
||||
Jo Liss <joliss42@gmail.com>
|
||||
Guntupalli Karunakar <karunakarg@yahoo.com>
|
||||
Shahyar Ghobadpour <shahyar@gmail.com>
|
||||
Lukasz Lipinski <uzza17@gmail.com>
|
||||
Timo Tijhof <krinklemail@gmail.com>
|
||||
Jason Moon <jmoon@socialcast.com>
|
||||
Martin Frost <martinf55@hotmail.com>
|
||||
Eneko Illarramendi <eneko@illarra.com>
|
||||
EungJun Yi <semtlenori@gmail.com>
|
||||
Courtland Allen <courtlandallen@gmail.com>
|
||||
Viktar Varvanovich <non4eg@gmail.com>
|
||||
Danny Trunk <dtrunk90@gmail.com>
|
||||
Pavel Stetina <pavel.stetina@nangu.tv>
|
||||
Michael Stay <metaweta@gmail.com>
|
||||
Steven Roussey <sroussey@gmail.com>
|
||||
Michael Hollis <hollis21@gmail.com>
|
||||
Lee Rowlands <lee.rowlands@previousnext.com.au>
|
||||
Timmy Willison <timmywillisn@gmail.com>
|
||||
Karl Swedberg <kswedberg@gmail.com>
|
||||
Baoju Yuan <the_guy_1987@hotmail.com>
|
||||
Maciej Mroziński <maciej.k.mrozinski@gmail.com>
|
||||
Luis Dalmolin <luis.nh@gmail.com>
|
||||
Mark Aaron Shirley <maspwr@gmail.com>
|
||||
Martin Hoch <martin@fidion.de>
|
||||
Jiayi Yang <tr870829@gmail.com>
|
||||
Philipp Benjamin Köppchen <xgxtpbk@gws.ms>
|
||||
Sindre Sorhus <sindresorhus@gmail.com>
|
||||
Bernhard Sirlinger <bernhard.sirlinger@tele2.de>
|
||||
Jared A. Scheel <jared@jaredscheel.com>
|
||||
Rafael Xavier de Souza <rxaviers@gmail.com>
|
||||
John Chen <zhang.z.chen@intel.com>
|
||||
Dale Kocian <dale.kocian@gmail.com>
|
||||
Mike Sherov <mike.sherov@gmail.com>
|
||||
Andrew Couch <andy@couchand.com>
|
||||
Marc-Andre Lafortune <github@marc-andre.ca>
|
||||
Nate Eagle <nate.eagle@teamaol.com>
|
||||
David Souther <davidsouther@gmail.com>
|
||||
Mathias Stenbom <mathias@stenbom.com>
|
||||
Sergey Kartashov <ebishkek@yandex.ru>
|
||||
Avinash R <nashpapa@gmail.com>
|
||||
Ethan Romba <ethanromba@gmail.com>
|
||||
Cory Gackenheimer <cory.gack@gmail.com>
|
||||
Juan Pablo Kaniefsky <jpkaniefsky@gmail.com>
|
||||
Roman Salnikov <bardt.dz@gmail.com>
|
||||
Anika Henke <anika@selfthinker.org>
|
||||
Samuel Bovée <samycookie2000@yahoo.fr>
|
||||
Fabrício Matté <ult_combo@hotmail.com>
|
||||
Viktor Kojouharov <vkojouharov@gmail.com>
|
||||
Pawel Maruszczyk (http://hrabstwo.net)
|
||||
Pavel Selitskas <p.selitskas@gmail.com>
|
||||
Bjørn Johansen <post@bjornjohansen.no>
|
||||
Matthieu Penant <thieum22@hotmail.com>
|
||||
Dominic Barnes <dominic@dbarnes.info>
|
||||
David Sullivan <david.sullivan@gmail.com>
|
||||
Thomas Jaggi <thomas@responsive.ch>
|
||||
Vahid Sohrabloo <vahid4134@gmail.com>
|
||||
Travis Carden <travis.carden@gmail.com>
|
||||
Bruno M. Custódio <bruno@brunomcustodio.com>
|
||||
Nathanael Silverman <nathanael.silverman@gmail.com>
|
||||
Christian Wenz <christian@wenz.org>
|
||||
Steve Urmston <steve@urm.st>
|
||||
Zaven Muradyan <megalivoithos@gmail.com>
|
||||
Woody Gilk <shadowhand@deviantart.com>
|
||||
Zbigniew Motyka <zbigniew.motyka@gmail.com>
|
||||
Suhail Alkowaileet <xsoh.k7@gmail.com>
|
||||
Toshi MARUYAMA <marutosijp2@yahoo.co.jp>
|
||||
David Hansen <hansede@gmail.com>
|
||||
Brian Grinstead <briangrinstead@gmail.com>
|
||||
Christian Klammer <christian314159@gmail.com>
|
||||
Steven Luscher <jquerycla@steveluscher.com>
|
||||
Gan Eng Chin <engchin.gan@gmail.com>
|
||||
Gabriel Schulhof <gabriel.schulhof@intel.com>
|
||||
Alexander Schmitz <arschmitz@gmail.com>
|
||||
Vilhjálmur Skúlason <vis@dmm.is>
|
||||
Siebrand Mazeland <s.mazeland@xs4all.nl>
|
||||
Mohsen Ekhtiari <mohsenekhtiari@yahoo.com>
|
||||
Pere Orga <gotrunks@gmail.com>
|
||||
Jasper de Groot <mail@ugomobi.com>
|
||||
Stephane Deschamps <stephane.deschamps@gmail.com>
|
||||
Jyoti Deka <dekajp@gmail.com>
|
||||
Andrei Picus <office.nightcrawler@gmail.com>
|
||||
Ondrej Novy <novy@ondrej.org>
|
||||
Jacob McCutcheon <jacob.mccutcheon@gmail.com>
|
||||
Monika Piotrowicz <monika.piotrowicz@gmail.com>
|
||||
Imants Horsts <imants.horsts@inbox.lv>
|
||||
Eric Dahl <eric.c.dahl@gmail.com>
|
||||
Dave Stein <dave@behance.com>
|
||||
Dylan Barrell <dylan@barrell.com>
|
||||
Daniel DeGroff <djdegroff@gmail.com>
|
||||
Michael Wiencek <mwtuea@gmail.com>
|
||||
Thomas Meyer <meyertee@gmail.com>
|
||||
Ruslan Yakhyaev <ruslan@ruslan.io>
|
||||
Brian J. Dowling <bjd-dev@simplicity.net>
|
||||
Ben Higgins <ben@extrahop.com>
|
||||
Yermo Lamers <yml@yml.com>
|
||||
Patrick Stapleton <github@gdi2290.com>
|
||||
Trisha Crowley <trisha.crowley@gmail.com>
|
||||
Usman Akeju <akeju00+github@gmail.com>
|
||||
Rodrigo Menezes <rod333@gmail.com>
|
||||
Jacques Perrault <jacques_perrault@us.ibm.com>
|
||||
Frederik Elvhage <frederik.elvhage@googlemail.com>
|
||||
Will Holley <willholley@gmail.com>
|
||||
Uri Gilad <antishok@gmail.com>
|
||||
Richard Gibson <richard.gibson@gmail.com>
|
||||
Simen Bekkhus <sbekkhus91@gmail.com>
|
||||
44
awx/ui/static/lib/jquery-ui/LICENSE.txt
Normal file
@ -0,0 +1,44 @@
|
||||
Copyright 2007, 2014 jQuery Foundation and other contributors,
|
||||
https://jquery.org/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery-ui
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
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.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code contained within the demos directory.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
||||
11
awx/ui/static/lib/jquery-ui/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
[jQuery UI](http://jqueryui.com/) - Interactions and Widgets for the web
|
||||
================================
|
||||
|
||||
jQuery UI provides interactions like Drag and Drop and widgets like Autocomplete, Tabs and Slider and makes these as easy to use as jQuery itself.
|
||||
|
||||
If you want to use jQuery UI, go to [jqueryui.com](http://jqueryui.com) to get started. Or visit the [Using jQuery UI Forum](http://forum.jquery.com/using-jquery-ui) for discussions and questions.
|
||||
|
||||
If you are interested in helping develop jQuery UI, you are in the right place.
|
||||
To discuss development with team members and the community, visit the [Developing jQuery UI Forum](http://forum.jquery.com/developing-jquery-ui) or in #jquery on irc.freednode.net.
|
||||
|
||||
## This repo only holds precompiled files.
|
||||
12
awx/ui/static/lib/jquery-ui/bower.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "jquery-ui",
|
||||
"version": "1.11.3",
|
||||
"main": [
|
||||
"jquery-ui.js"
|
||||
],
|
||||
"ignore": [
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6"
|
||||
}
|
||||
}
|
||||
13
awx/ui/static/lib/jquery-ui/component.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "jquery-ui",
|
||||
"repo": "components/jqueryui",
|
||||
"version": "1.11.3",
|
||||
"license": "MIT",
|
||||
"scripts": [
|
||||
"jquery-ui.js"
|
||||
],
|
||||
"main": "jquery-ui.js",
|
||||
"dependencies": {
|
||||
"components/jquery": "*"
|
||||
}
|
||||
}
|
||||
69
awx/ui/static/lib/jquery-ui/composer.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "components/jqueryui",
|
||||
"type": "component",
|
||||
"description": "jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. Whether you're building highly interactive web applications or you just need to add a date picker to a form control, jQuery UI is the perfect choice.",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"components/jquery": ">=1.6"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "jQuery UI Team",
|
||||
"homepage": "http://jqueryui.com/about"
|
||||
},
|
||||
{
|
||||
"name": "Scott Gonzalez",
|
||||
"email": "scott.gonzalez@gmail.com",
|
||||
"homepage": "http://scottgonzalez.com"
|
||||
},
|
||||
{
|
||||
"name": "Joern Zaefferer",
|
||||
"email": "joern.zaefferer@gmail.com",
|
||||
"homepage": "http://bassistance.de"
|
||||
},
|
||||
{
|
||||
"name": "Kris Borchers",
|
||||
"email": "kris.borchers@gmail.com",
|
||||
"homepage": "http://krisborchers.com"
|
||||
},
|
||||
{
|
||||
"name": "Corey Frang",
|
||||
"email": "gnarf37@gmail.com",
|
||||
"homepage": "http://gnarf.net"
|
||||
},
|
||||
{
|
||||
"name": "Mike Sherov",
|
||||
"email": "mike.sherov@gmail.com",
|
||||
"homepage": "http://mike.sherov.com"
|
||||
},
|
||||
{
|
||||
"name": "TJ VanToll",
|
||||
"email": "tj.vantoll@gmail.com",
|
||||
"homepage": "http://tjvantoll.com"
|
||||
},
|
||||
{
|
||||
"name": "Felix Nagel",
|
||||
"email": "info@felixnagel.com",
|
||||
"homepage": "http://www.felixnagel.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"component": {
|
||||
"name": "jquery-ui",
|
||||
"scripts": [
|
||||
"jquery-ui.js"
|
||||
],
|
||||
"files": [
|
||||
"ui/**",
|
||||
"themes/**",
|
||||
"jquery-ui.min.js"
|
||||
],
|
||||
"shim": {
|
||||
"deps": [
|
||||
"jquery"
|
||||
],
|
||||
"exports": "jQuery"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16608
awx/ui/static/lib/jquery-ui/jquery-ui.js
vendored
Normal file
13
awx/ui/static/lib/jquery-ui/jquery-ui.min.js
vendored
Normal file
71
awx/ui/static/lib/jquery-ui/package.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "jquery-ui",
|
||||
"title": "jQuery UI",
|
||||
"description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.",
|
||||
"version": "1.11.3",
|
||||
"homepage": "http://jqueryui.com",
|
||||
"author": {
|
||||
"name": "jQuery Foundation and other contributors",
|
||||
"url": "https://github.com/jquery/jquery-ui/blob/1-11-stable/AUTHORS.txt"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Scott González",
|
||||
"email": "scott.gonzalez@gmail.com",
|
||||
"url": "http://scottgonzalez.com"
|
||||
},
|
||||
{
|
||||
"name": "Jörn Zaefferer",
|
||||
"email": "joern.zaefferer@gmail.com",
|
||||
"url": "http://bassistance.de"
|
||||
},
|
||||
{
|
||||
"name": "Kris Borchers",
|
||||
"email": "kris.borchers@gmail.com",
|
||||
"url": "http://krisborchers.com"
|
||||
},
|
||||
{
|
||||
"name": "Corey Frang",
|
||||
"email": "gnarf37@gmail.com",
|
||||
"url": "http://gnarf.net"
|
||||
},
|
||||
{
|
||||
"name": "Mike Sherov",
|
||||
"email": "mike.sherov@gmail.com",
|
||||
"url": "http://mike.sherov.com"
|
||||
},
|
||||
{
|
||||
"name": "TJ VanToll",
|
||||
"email": "tj.vantoll@gmail.com",
|
||||
"url": "http://tjvantoll.com"
|
||||
},
|
||||
{
|
||||
"name": "Felix Nagel",
|
||||
"email": "info@felixnagel.com",
|
||||
"url": "http://www.felixnagel.com"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jquery/jquery-ui.git"
|
||||
},
|
||||
"bugs": "http://bugs.jqueryui.com/",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/jquery/jquery-ui/blob/1-11-stable/LICENSE.txt"
|
||||
}
|
||||
],
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.3.17",
|
||||
"grunt-css": "0.2.0",
|
||||
"grunt-compare-size": "0.1.4",
|
||||
"grunt-html": "0.1.1",
|
||||
"grunt-junit": "0.1.5",
|
||||
"grunt-git-authors": "1.0.0",
|
||||
"rimraf": "2.0.1",
|
||||
"testswarm": "0.3.0"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
36
awx/ui/static/lib/jquery-ui/themes/base/accordion.css
Normal file
@ -0,0 +1,36 @@
|
||||
/*!
|
||||
* jQuery UI Accordion 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/accordion/#theming
|
||||
*/
|
||||
.ui-accordion .ui-accordion-header {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
margin: 2px 0 0 0;
|
||||
padding: .5em .5em .5em .7em;
|
||||
min-height: 0; /* support: IE7 */
|
||||
font-size: 100%;
|
||||
}
|
||||
.ui-accordion .ui-accordion-icons {
|
||||
padding-left: 2.2em;
|
||||
}
|
||||
.ui-accordion .ui-accordion-icons .ui-accordion-icons {
|
||||
padding-left: 2.2em;
|
||||
}
|
||||
.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
|
||||
position: absolute;
|
||||
left: .5em;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ui-accordion .ui-accordion-content {
|
||||
padding: 1em 2.2em;
|
||||
border-top: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
12
awx/ui/static/lib/jquery-ui/themes/base/all.css
Normal file
@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
*/
|
||||
@import "base.css";
|
||||
@import "theme.css";
|
||||
16
awx/ui/static/lib/jquery-ui/themes/base/autocomplete.css
Normal file
@ -0,0 +1,16 @@
|
||||
/*!
|
||||
* jQuery UI Autocomplete 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/autocomplete/#theming
|
||||
*/
|
||||
.ui-autocomplete {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: default;
|
||||
}
|
||||
28
awx/ui/static/lib/jquery-ui/themes/base/base.css
Normal file
@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
*/
|
||||
@import url("core.css");
|
||||
|
||||
@import url("accordion.css");
|
||||
@import url("autocomplete.css");
|
||||
@import url("button.css");
|
||||
@import url("datepicker.css");
|
||||
@import url("dialog.css");
|
||||
@import url("draggable.css");
|
||||
@import url("menu.css");
|
||||
@import url("progressbar.css");
|
||||
@import url("resizable.css");
|
||||
@import url("selectable.css");
|
||||
@import url("selectmenu.css");
|
||||
@import url("sortable.css");
|
||||
@import url("slider.css");
|
||||
@import url("spinner.css");
|
||||
@import url("tabs.css");
|
||||
@import url("tooltip.css");
|
||||
114
awx/ui/static/lib/jquery-ui/themes/base/button.css
Normal file
@ -0,0 +1,114 @@
|
||||
/*!
|
||||
* jQuery UI Button 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/button/#theming
|
||||
*/
|
||||
.ui-button {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
margin-right: .1em;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
overflow: visible; /* removes extra width in IE */
|
||||
}
|
||||
.ui-button,
|
||||
.ui-button:link,
|
||||
.ui-button:visited,
|
||||
.ui-button:hover,
|
||||
.ui-button:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
/* to make room for the icon, a width needs to be set here */
|
||||
.ui-button-icon-only {
|
||||
width: 2.2em;
|
||||
}
|
||||
/* button elements seem to need a little more width */
|
||||
button.ui-button-icon-only {
|
||||
width: 2.4em;
|
||||
}
|
||||
.ui-button-icons-only {
|
||||
width: 3.4em;
|
||||
}
|
||||
button.ui-button-icons-only {
|
||||
width: 3.7em;
|
||||
}
|
||||
|
||||
/* button text element */
|
||||
.ui-button .ui-button-text {
|
||||
display: block;
|
||||
line-height: normal;
|
||||
}
|
||||
.ui-button-text-only .ui-button-text {
|
||||
padding: .4em 1em;
|
||||
}
|
||||
.ui-button-icon-only .ui-button-text,
|
||||
.ui-button-icons-only .ui-button-text {
|
||||
padding: .4em;
|
||||
text-indent: -9999999px;
|
||||
}
|
||||
.ui-button-text-icon-primary .ui-button-text,
|
||||
.ui-button-text-icons .ui-button-text {
|
||||
padding: .4em 1em .4em 2.1em;
|
||||
}
|
||||
.ui-button-text-icon-secondary .ui-button-text,
|
||||
.ui-button-text-icons .ui-button-text {
|
||||
padding: .4em 2.1em .4em 1em;
|
||||
}
|
||||
.ui-button-text-icons .ui-button-text {
|
||||
padding-left: 2.1em;
|
||||
padding-right: 2.1em;
|
||||
}
|
||||
/* no icon support for input elements, provide padding by default */
|
||||
input.ui-button {
|
||||
padding: .4em 1em;
|
||||
}
|
||||
|
||||
/* button icon element(s) */
|
||||
.ui-button-icon-only .ui-icon,
|
||||
.ui-button-text-icon-primary .ui-icon,
|
||||
.ui-button-text-icon-secondary .ui-icon,
|
||||
.ui-button-text-icons .ui-icon,
|
||||
.ui-button-icons-only .ui-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ui-button-icon-only .ui-icon {
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.ui-button-text-icon-primary .ui-button-icon-primary,
|
||||
.ui-button-text-icons .ui-button-icon-primary,
|
||||
.ui-button-icons-only .ui-button-icon-primary {
|
||||
left: .5em;
|
||||
}
|
||||
.ui-button-text-icon-secondary .ui-button-icon-secondary,
|
||||
.ui-button-text-icons .ui-button-icon-secondary,
|
||||
.ui-button-icons-only .ui-button-icon-secondary {
|
||||
right: .5em;
|
||||
}
|
||||
|
||||
/* button sets */
|
||||
.ui-buttonset {
|
||||
margin-right: 7px;
|
||||
}
|
||||
.ui-buttonset .ui-button {
|
||||
margin-left: 0;
|
||||
margin-right: -.3em;
|
||||
}
|
||||
|
||||
/* workarounds */
|
||||
/* reset extra padding in Firefox, see h5bp.com/l */
|
||||
input.ui-button::-moz-focus-inner,
|
||||
button.ui-button::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
93
awx/ui/static/lib/jquery-ui/themes/base/core.css
Normal file
@ -0,0 +1,93 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ui-helper-hidden-accessible {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.ui-helper-reset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
font-size: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
.ui-helper-clearfix:before,
|
||||
.ui-helper-clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.ui-helper-clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.ui-helper-clearfix {
|
||||
min-height: 0; /* support: IE7 */
|
||||
}
|
||||
.ui-helper-zfix {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0); /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
display: block;
|
||||
text-indent: -99999px;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
175
awx/ui/static/lib/jquery-ui/themes/base/datepicker.css
Normal file
@ -0,0 +1,175 @@
|
||||
/*!
|
||||
* jQuery UI Datepicker 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/datepicker/#theming
|
||||
*/
|
||||
.ui-datepicker {
|
||||
width: 17em;
|
||||
padding: .2em .2em 0;
|
||||
display: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-header {
|
||||
position: relative;
|
||||
padding: .2em 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev,
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover,
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
top: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev {
|
||||
left: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
right: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover {
|
||||
left: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
right: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev span,
|
||||
.ui-datepicker .ui-datepicker-next span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title {
|
||||
margin: 0 2.3em;
|
||||
line-height: 1.8em;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title select {
|
||||
font-size: 1em;
|
||||
margin: 1px 0;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year {
|
||||
width: 45%;
|
||||
}
|
||||
.ui-datepicker table {
|
||||
width: 100%;
|
||||
font-size: .9em;
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 .4em;
|
||||
}
|
||||
.ui-datepicker th {
|
||||
padding: .7em .3em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
}
|
||||
.ui-datepicker td {
|
||||
border: 0;
|
||||
padding: 1px;
|
||||
}
|
||||
.ui-datepicker td span,
|
||||
.ui-datepicker td a {
|
||||
display: block;
|
||||
padding: .2em;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane {
|
||||
background-image: none;
|
||||
margin: .7em 0 0 0;
|
||||
padding: 0 .2em;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button {
|
||||
float: right;
|
||||
margin: .5em .2em .4em;
|
||||
cursor: pointer;
|
||||
padding: .2em .6em .3em .6em;
|
||||
width: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi {
|
||||
width: auto;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group table {
|
||||
width: 95%;
|
||||
margin: 0 auto .4em;
|
||||
}
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group {
|
||||
width: 50%;
|
||||
}
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group {
|
||||
width: 33.3%;
|
||||
}
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group {
|
||||
width: 25%;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-left-width: 0;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane {
|
||||
clear: left;
|
||||
}
|
||||
.ui-datepicker-row-break {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev {
|
||||
right: 2px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next {
|
||||
left: 2px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover {
|
||||
right: 1px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover {
|
||||
left: 1px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane {
|
||||
clear: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
|
||||
.ui-datepicker-rtl .ui-datepicker-group {
|
||||
float: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
70
awx/ui/static/lib/jquery-ui/themes/base/dialog.css
Normal file
@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* jQuery UI Dialog 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/dialog/#theming
|
||||
*/
|
||||
.ui-dialog {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: .2em;
|
||||
outline: 0;
|
||||
}
|
||||
.ui-dialog .ui-dialog-titlebar {
|
||||
padding: .4em 1em;
|
||||
position: relative;
|
||||
}
|
||||
.ui-dialog .ui-dialog-title {
|
||||
float: left;
|
||||
margin: .1em 0;
|
||||
white-space: nowrap;
|
||||
width: 90%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.ui-dialog .ui-dialog-titlebar-close {
|
||||
position: absolute;
|
||||
right: .3em;
|
||||
top: 50%;
|
||||
width: 20px;
|
||||
margin: -10px 0 0 0;
|
||||
padding: 1px;
|
||||
height: 20px;
|
||||
}
|
||||
.ui-dialog .ui-dialog-content {
|
||||
position: relative;
|
||||
border: 0;
|
||||
padding: .5em 1em;
|
||||
background: none;
|
||||
overflow: auto;
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane {
|
||||
text-align: left;
|
||||
border-width: 1px 0 0 0;
|
||||
background-image: none;
|
||||
margin-top: .5em;
|
||||
padding: .3em 1em .5em .4em;
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
|
||||
float: right;
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane button {
|
||||
margin: .5em .4em .5em 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-dialog .ui-resizable-se {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
right: -5px;
|
||||
bottom: -5px;
|
||||
background-position: 16px 16px;
|
||||
}
|
||||
.ui-draggable .ui-dialog-titlebar {
|
||||
cursor: move;
|
||||
}
|
||||
12
awx/ui/static/lib/jquery-ui/themes/base/draggable.css
Normal file
@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* jQuery UI Draggable 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
.ui-draggable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 105 B |
|
After Width: | Height: | Size: 111 B |
|
After Width: | Height: | Size: 110 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 101 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
1225
awx/ui/static/lib/jquery-ui/themes/base/jquery-ui.css
vendored
Normal file
7
awx/ui/static/lib/jquery-ui/themes/base/jquery-ui.min.css
vendored
Normal file
63
awx/ui/static/lib/jquery-ui/themes/base/menu.css
Normal file
@ -0,0 +1,63 @@
|
||||
/*!
|
||||
* jQuery UI Menu 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/menu/#theming
|
||||
*/
|
||||
.ui-menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
outline: none;
|
||||
}
|
||||
.ui-menu .ui-menu {
|
||||
position: absolute;
|
||||
}
|
||||
.ui-menu .ui-menu-item {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 3px 1em 3px .4em;
|
||||
cursor: pointer;
|
||||
min-height: 0; /* support: IE7 */
|
||||
/* support: IE10, see #8844 */
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
|
||||
}
|
||||
.ui-menu .ui-menu-divider {
|
||||
margin: 5px 0;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
.ui-menu .ui-state-focus,
|
||||
.ui-menu .ui-state-active {
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
/* icon support */
|
||||
.ui-menu-icons {
|
||||
position: relative;
|
||||
}
|
||||
.ui-menu-icons .ui-menu-item {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
/* left-aligned */
|
||||
.ui-menu .ui-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: .2em;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
/* right-aligned */
|
||||
.ui-menu .ui-menu-icon {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
28
awx/ui/static/lib/jquery-ui/themes/base/progressbar.css
Normal file
@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* jQuery UI Progressbar 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/progressbar/#theming
|
||||
*/
|
||||
.ui-progressbar {
|
||||
height: 2em;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ui-progressbar .ui-progressbar-value {
|
||||
margin: -1px;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
filter: alpha(opacity=25); /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
background-image: none;
|
||||
}
|
||||
78
awx/ui/static/lib/jquery-ui/themes/base/resizable.css
Normal file
@ -0,0 +1,78 @@
|
||||
/*!
|
||||
* jQuery UI Resizable 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
.ui-resizable {
|
||||
position: relative;
|
||||
}
|
||||
.ui-resizable-handle {
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable-disabled .ui-resizable-handle,
|
||||
.ui-resizable-autohide .ui-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
.ui-resizable-n {
|
||||
cursor: n-resize;
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
top: -5px;
|
||||
left: 0;
|
||||
}
|
||||
.ui-resizable-s {
|
||||
cursor: s-resize;
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
}
|
||||
.ui-resizable-e {
|
||||
cursor: e-resize;
|
||||
width: 7px;
|
||||
right: -5px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-resizable-w {
|
||||
cursor: w-resize;
|
||||
width: 7px;
|
||||
left: -5px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-resizable-se {
|
||||
cursor: se-resize;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
}
|
||||
.ui-resizable-sw {
|
||||
cursor: sw-resize;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
left: -5px;
|
||||
bottom: -5px;
|
||||
}
|
||||
.ui-resizable-nw {
|
||||
cursor: nw-resize;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
left: -5px;
|
||||
top: -5px;
|
||||
}
|
||||
.ui-resizable-ne {
|
||||
cursor: ne-resize;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
}
|
||||
17
awx/ui/static/lib/jquery-ui/themes/base/selectable.css
Normal file
@ -0,0 +1,17 @@
|
||||
/*!
|
||||
* jQuery UI Selectable 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
.ui-selectable {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-selectable-helper {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
border: 1px dotted black;
|
||||
}
|
||||
59
awx/ui/static/lib/jquery-ui/themes/base/selectmenu.css
Normal file
@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* jQuery UI Selectmenu 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/selectmenu/#theming
|
||||
*/
|
||||
.ui-selectmenu-menu {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
.ui-selectmenu-menu .ui-menu {
|
||||
overflow: auto;
|
||||
/* Support: IE7 */
|
||||
overflow-x: hidden;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
padding: 2px 0.4em;
|
||||
margin: 0.5em 0 0 0;
|
||||
height: auto;
|
||||
border: 0;
|
||||
}
|
||||
.ui-selectmenu-open {
|
||||
display: block;
|
||||
}
|
||||
.ui-selectmenu-button {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-selectmenu-button span.ui-icon {
|
||||
right: 0.5em;
|
||||
left: auto;
|
||||
margin-top: -8px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ui-selectmenu-button span.ui-selectmenu-text {
|
||||
text-align: left;
|
||||
padding: 0.4em 2.1em 0.4em 1em;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
75
awx/ui/static/lib/jquery-ui/themes/base/slider.css
Normal file
@ -0,0 +1,75 @@
|
||||
/*!
|
||||
* jQuery UI Slider 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/slider/#theming
|
||||
*/
|
||||
.ui-slider {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
.ui-slider .ui-slider-handle {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: default;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
font-size: .7em;
|
||||
display: block;
|
||||
border: 0;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* support: IE8 - See #6727 */
|
||||
.ui-slider.ui-state-disabled .ui-slider-handle,
|
||||
.ui-slider.ui-state-disabled .ui-slider-range {
|
||||
filter: inherit;
|
||||
}
|
||||
|
||||
.ui-slider-horizontal {
|
||||
height: .8em;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-handle {
|
||||
top: -.3em;
|
||||
margin-left: -.6em;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-range {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-range-min {
|
||||
left: 0;
|
||||
}
|
||||
.ui-slider-horizontal .ui-slider-range-max {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.ui-slider-vertical {
|
||||
width: .8em;
|
||||
height: 100px;
|
||||
}
|
||||
.ui-slider-vertical .ui-slider-handle {
|
||||
left: -.3em;
|
||||
margin-left: 0;
|
||||
margin-bottom: -.6em;
|
||||
}
|
||||
.ui-slider-vertical .ui-slider-range {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.ui-slider-vertical .ui-slider-range-min {
|
||||
bottom: 0;
|
||||
}
|
||||
.ui-slider-vertical .ui-slider-range-max {
|
||||
top: 0;
|
||||
}
|
||||
12
awx/ui/static/lib/jquery-ui/themes/base/sortable.css
Normal file
@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* jQuery UI Sortable 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
.ui-sortable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
65
awx/ui/static/lib/jquery-ui/themes/base/spinner.css
Normal file
@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* jQuery UI Spinner 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/spinner/#theming
|
||||
*/
|
||||
.ui-spinner {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-spinner-input {
|
||||
border: none;
|
||||
background: none;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
margin: .2em 0;
|
||||
vertical-align: middle;
|
||||
margin-left: .4em;
|
||||
margin-right: 22px;
|
||||
}
|
||||
.ui-spinner-button {
|
||||
width: 16px;
|
||||
height: 50%;
|
||||
font-size: .5em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
right: 0;
|
||||
}
|
||||
/* more specificity required here to override default borders */
|
||||
.ui-spinner a.ui-spinner-button {
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
}
|
||||
/* vertically center icon */
|
||||
.ui-spinner .ui-icon {
|
||||
position: absolute;
|
||||
margin-top: -8px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
.ui-spinner-up {
|
||||
top: 0;
|
||||
}
|
||||
.ui-spinner-down {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
/* TR overrides */
|
||||
.ui-spinner .ui-icon-triangle-1-s {
|
||||
/* need to fix icons sprite */
|
||||
background-position: -65px -16px;
|
||||
}
|
||||
51
awx/ui/static/lib/jquery-ui/themes/base/tabs.css
Normal file
@ -0,0 +1,51 @@
|
||||
/*!
|
||||
* jQuery UI Tabs 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/tabs/#theming
|
||||
*/
|
||||
.ui-tabs {
|
||||
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
|
||||
padding: .2em;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav {
|
||||
margin: 0;
|
||||
padding: .2em .2em 0;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li {
|
||||
list-style: none;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: 0;
|
||||
margin: 1px .2em 0 0;
|
||||
border-bottom-width: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
|
||||
float: left;
|
||||
padding: .5em 1em;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
|
||||
margin-bottom: -1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
|
||||
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
|
||||
cursor: text;
|
||||
}
|
||||
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
display: block;
|
||||
border-width: 0;
|
||||
padding: 1em 1.4em;
|
||||
background: none;
|
||||
}
|
||||
410
awx/ui/static/lib/jquery-ui/themes/base/theme.css
Normal file
@ -0,0 +1,410 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
*
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget {
|
||||
font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
|
||||
font-size: 1.1em/*{fsDefault}*/;
|
||||
}
|
||||
.ui-widget .ui-widget {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget input,
|
||||
.ui-widget select,
|
||||
.ui-widget textarea,
|
||||
.ui-widget button {
|
||||
font-family: Verdana,Arial,sans-serif/*{ffDefault}*/;
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget-content {
|
||||
border: 1px solid #aaaaaa/*{borderColorContent}*/;
|
||||
background: #ffffff/*{bgColorContent}*/ url("images/ui-bg_flat_75_ffffff_40x100.png")/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/;
|
||||
color: #222222/*{fcContent}*/;
|
||||
}
|
||||
.ui-widget-content a {
|
||||
color: #222222/*{fcContent}*/;
|
||||
}
|
||||
.ui-widget-header {
|
||||
border: 1px solid #aaaaaa/*{borderColorHeader}*/;
|
||||
background: #cccccc/*{bgColorHeader}*/ url("images/ui-bg_highlight-soft_75_cccccc_1x100.png")/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/;
|
||||
color: #222222/*{fcHeader}*/;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-widget-header a {
|
||||
color: #222222/*{fcHeader}*/;
|
||||
}
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
border: 1px solid #d3d3d3/*{borderColorDefault}*/;
|
||||
background: #e6e6e6/*{bgColorDefault}*/ url("images/ui-bg_glass_75_e6e6e6_1x400.png")/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/;
|
||||
font-weight: normal/*{fwDefault}*/;
|
||||
color: #555555/*{fcDefault}*/;
|
||||
}
|
||||
.ui-state-default a,
|
||||
.ui-state-default a:link,
|
||||
.ui-state-default a:visited {
|
||||
color: #555555/*{fcDefault}*/;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
border: 1px solid #999999/*{borderColorHover}*/;
|
||||
background: #dadada/*{bgColorHover}*/ url("images/ui-bg_glass_75_dadada_1x400.png")/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/;
|
||||
font-weight: normal/*{fwDefault}*/;
|
||||
color: #212121/*{fcHover}*/;
|
||||
}
|
||||
.ui-state-hover a,
|
||||
.ui-state-hover a:hover,
|
||||
.ui-state-hover a:link,
|
||||
.ui-state-hover a:visited,
|
||||
.ui-state-focus a,
|
||||
.ui-state-focus a:hover,
|
||||
.ui-state-focus a:link,
|
||||
.ui-state-focus a:visited {
|
||||
color: #212121/*{fcHover}*/;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
border: 1px solid #aaaaaa/*{borderColorActive}*/;
|
||||
background: #ffffff/*{bgColorActive}*/ url("images/ui-bg_glass_65_ffffff_1x400.png")/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/;
|
||||
font-weight: normal/*{fwDefault}*/;
|
||||
color: #212121/*{fcActive}*/;
|
||||
}
|
||||
.ui-state-active a,
|
||||
.ui-state-active a:link,
|
||||
.ui-state-active a:visited {
|
||||
color: #212121/*{fcActive}*/;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #fcefa1/*{borderColorHighlight}*/;
|
||||
background: #fbf9ee/*{bgColorHighlight}*/ url("images/ui-bg_glass_55_fbf9ee_1x400.png")/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/;
|
||||
color: #363636/*{fcHighlight}*/;
|
||||
}
|
||||
.ui-state-highlight a,
|
||||
.ui-widget-content .ui-state-highlight a,
|
||||
.ui-widget-header .ui-state-highlight a {
|
||||
color: #363636/*{fcHighlight}*/;
|
||||
}
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
border: 1px solid #cd0a0a/*{borderColorError}*/;
|
||||
background: #fef1ec/*{bgColorError}*/ url("images/ui-bg_glass_95_fef1ec_1x400.png")/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/;
|
||||
color: #cd0a0a/*{fcError}*/;
|
||||
}
|
||||
.ui-state-error a,
|
||||
.ui-widget-content .ui-state-error a,
|
||||
.ui-widget-header .ui-state-error a {
|
||||
color: #cd0a0a/*{fcError}*/;
|
||||
}
|
||||
.ui-state-error-text,
|
||||
.ui-widget-content .ui-state-error-text,
|
||||
.ui-widget-header .ui-state-error-text {
|
||||
color: #cd0a0a/*{fcError}*/;
|
||||
}
|
||||
.ui-priority-primary,
|
||||
.ui-widget-content .ui-priority-primary,
|
||||
.ui-widget-header .ui-priority-primary {
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-priority-secondary,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70); /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35); /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url("images/ui-icons_222222_256x240.png")/*{iconsContent}*/;
|
||||
}
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url("images/ui-icons_222222_256x240.png")/*{iconsHeader}*/;
|
||||
}
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url("images/ui-icons_888888_256x240.png")/*{iconsDefault}*/;
|
||||
}
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url("images/ui-icons_454545_256x240.png")/*{iconsHover}*/;
|
||||
}
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url("images/ui-icons_454545_256x240.png")/*{iconsActive}*/;
|
||||
}
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url("images/ui-icons_2e83ff_256x240.png")/*{iconsHighlight}*/;
|
||||
}
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url("images/ui-icons_cd0a0a_256x240.png")/*{iconsError}*/;
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-blank { background-position: 16px 16px; }
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-on { background-position: -96px -144px; }
|
||||
.ui-icon-radio-off { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-left,
|
||||
.ui-corner-tl {
|
||||
border-top-left-radius: 4px/*{cornerRadius}*/;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-right,
|
||||
.ui-corner-tr {
|
||||
border-top-right-radius: 4px/*{cornerRadius}*/;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-left,
|
||||
.ui-corner-bl {
|
||||
border-bottom-left-radius: 4px/*{cornerRadius}*/;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-right,
|
||||
.ui-corner-br {
|
||||
border-bottom-right-radius: 4px/*{cornerRadius}*/;
|
||||
}
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa/*{bgColorOverlay}*/ url("images/ui-bg_flat_0_aaaaaa_40x100.png")/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/;
|
||||
opacity: .3/*{opacityOverlay}*/;
|
||||
filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/; /* support: IE8 */
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/;
|
||||
padding: 8px/*{thicknessShadow}*/;
|
||||
background: #aaaaaa/*{bgColorShadow}*/ url("images/ui-bg_flat_0_aaaaaa_40x100.png")/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/;
|
||||
opacity: .3/*{opacityShadow}*/;
|
||||
filter: Alpha(Opacity=30)/*{opacityFilterShadow}*/; /* support: IE8 */
|
||||
border-radius: 8px/*{cornerRadiusShadow}*/;
|
||||
}
|
||||
21
awx/ui/static/lib/jquery-ui/themes/base/tooltip.css
Normal file
@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* jQuery UI Tooltip 1.11.3
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/tooltip/#theming
|
||||
*/
|
||||
.ui-tooltip {
|
||||
padding: 8px;
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
max-width: 300px;
|
||||
-webkit-box-shadow: 0 0 5px #aaa;
|
||||
box-shadow: 0 0 5px #aaa;
|
||||
}
|
||||
body .ui-tooltip {
|
||||
border-width: 2px;
|
||||
}
|
||||
BIN
awx/ui/static/lib/jquery-ui/themes/black-tie/images/animated-overlay.gif
Executable file
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 312 B |