diff options
-rw-r--r-- | MANIFEST.in | 2 | ||||
-rw-r--r-- | awx/main/tests/functional/test_licenses.py | 26 | ||||
-rw-r--r-- | awx/settings/defaults.py | 3 | ||||
-rw-r--r-- | awx/ui/__init__.py | 5 | ||||
-rw-r--r-- | awx/ui/apps.py | 10 | ||||
-rw-r--r-- | awx/ui/conf.py | 74 | ||||
-rw-r--r-- | awx/ui/fields.py | 45 | ||||
-rw-r--r-- | awx/ui_next/src/App.jsx | 6 | ||||
-rw-r--r-- | awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx | 4 | ||||
-rw-r--r-- | awx/ui_next/urls.py | 3 | ||||
-rw-r--r-- | awx/urls.py | 1 |
11 files changed, 141 insertions, 38 deletions
diff --git a/MANIFEST.in b/MANIFEST.in index 6260b87cea..1466361602 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,8 +4,6 @@ recursive-include awx *.mo recursive-include awx/static * recursive-include awx/templates *.html recursive-include awx/api/templates *.md *.html -recursive-include awx/ui/templates *.html -recursive-include awx/ui/static * recursive-include awx/ui_next/build *.html recursive-include awx/ui_next/build * recursive-include awx/playbooks *.yml diff --git a/awx/main/tests/functional/test_licenses.py b/awx/main/tests/functional/test_licenses.py index f3e623d281..757349ee13 100644 --- a/awx/main/tests/functional/test_licenses.py +++ b/awx/main/tests/functional/test_licenses.py @@ -1,6 +1,5 @@ import glob -import json import os from django.conf import settings @@ -65,28 +64,6 @@ def test_python_and_js_licenses(): ret[name] = { 'name': name, 'version': version} return ret - - def read_ui_requirements(path): - def json_deps(jsondata): - ret = {} - deps = jsondata.get('dependencies',{}) - for key in deps.keys(): - key = key.lower() - devonly = deps[key].get('dev',False) - if not devonly: - if key not in ret.keys(): - depname = key.replace('/','-') - ret[depname] = { - 'name': depname, - 'version': deps[key]['version'] - } - ret.update(json_deps(deps[key])) - return ret - - with open('%s/package-lock.json' % path) as f: - jsondata = json.load(f) - return json_deps(jsondata) - def remediate_licenses_and_requirements(licenses, requirements): errors = [] items = list(licenses.keys()) @@ -113,12 +90,9 @@ def test_python_and_js_licenses(): base_dir = settings.BASE_DIR api_licenses = index_licenses('%s/../docs/licenses' % base_dir) - ui_licenses = index_licenses('%s/../docs/licenses/ui' % base_dir) api_requirements = read_api_requirements('%s/../requirements' % base_dir) - ui_requirements = read_ui_requirements('%s/ui' % base_dir) errors = [] - errors += remediate_licenses_and_requirements(ui_licenses, ui_requirements) errors += remediate_licenses_and_requirements(api_licenses, api_requirements) if errors: raise Exception('Included licenses not consistent with requirements:\n%s' % diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 618f5282a4..6204486456 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -91,7 +91,6 @@ USE_L10N = True USE_TZ = True STATICFILES_DIRS = ( - os.path.join(BASE_DIR, 'ui', 'static'), os.path.join(BASE_DIR, 'ui_next', 'build', 'static'), os.path.join(BASE_DIR, 'static'), ) @@ -249,8 +248,6 @@ TEMPLATES = [ 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', - 'awx.ui.context_processors.settings', - 'awx.ui.context_processors.version', 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ], diff --git a/awx/ui/__init__.py b/awx/ui/__init__.py new file mode 100644 index 0000000000..bfb3e776cd --- /dev/null +++ b/awx/ui/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved. + +default_app_config = 'awx.ui.apps.UIConfig' + diff --git a/awx/ui/apps.py b/awx/ui/apps.py new file mode 100644 index 0000000000..5b8e5083c1 --- /dev/null +++ b/awx/ui/apps.py @@ -0,0 +1,10 @@ +# Django +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class UIConfig(AppConfig): + + name = 'awx.ui' + verbose_name = _('UI') + diff --git a/awx/ui/conf.py b/awx/ui/conf.py new file mode 100644 index 0000000000..3148aec6ee --- /dev/null +++ b/awx/ui/conf.py @@ -0,0 +1,74 @@ +# Copyright (c) 2016 Ansible, Inc. +# All Rights Reserved. + +# Django +from django.utils.translation import ugettext_lazy as _ + +# Tower +from awx.conf import register, fields +from awx.ui.fields import PendoTrackingStateField, CustomLogoField # noqa + + +register( + 'PENDO_TRACKING_STATE', + field_class=PendoTrackingStateField, + choices=[ + ('off', _('Off')), + ('anonymous', _('Anonymous')), + ('detailed', _('Detailed')), + ], + label=_('User Analytics Tracking State'), + help_text=_('Enable or Disable User Analytics Tracking.'), + category=_('UI'), + category_slug='ui', +) + +register( + 'CUSTOM_LOGIN_INFO', + field_class=fields.CharField, + allow_blank=True, + default='', + label=_('Custom Login Info'), + help_text=_('If needed, you can add specific information (such as a legal ' + 'notice or a disclaimer) to a text box in the login modal using ' + 'this setting. Any content added must be in plain text or an ' + 'HTML fragment, as other markup languages are not supported.'), + category=_('UI'), + category_slug='ui', +) + +register( + 'CUSTOM_LOGO', + field_class=CustomLogoField, + allow_blank=True, + default='', + label=_('Custom Logo'), + help_text=_('To set up a custom logo, provide a file that you create. For ' + 'the custom logo to look its best, use a .png file with a ' + 'transparent background. GIF, PNG and JPEG formats are supported.'), + placeholder='data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=', + category=_('UI'), + category_slug='ui', +) + +register( + 'MAX_UI_JOB_EVENTS', + field_class=fields.IntegerField, + min_value=100, + label=_('Max Job Events Retrieved by UI'), + help_text=_('Maximum number of job events for the UI to retrieve within a ' + 'single request.'), + category=_('UI'), + category_slug='ui', +) + +register( + 'UI_LIVE_UPDATES_ENABLED', + field_class=fields.BooleanField, + label=_('Enable Live Updates in the UI'), + help_text=_('If disabled, the page will not refresh when events are received. ' + 'Reloading the page will be required to get the latest details.'), + category=_('UI'), + category_slug='ui', +) + diff --git a/awx/ui/fields.py b/awx/ui/fields.py new file mode 100644 index 0000000000..4d96165d4d --- /dev/null +++ b/awx/ui/fields.py @@ -0,0 +1,45 @@ +# Copyright (c) 2016 Ansible, Inc. +# All Rights Reserved. + +# Python +import base64 +import binascii +import re + +# Django +from django.utils.translation import ugettext_lazy as _ + +# Tower +from awx.conf import fields, register + + +class PendoTrackingStateField(fields.ChoiceField): + + def to_internal_value(self, data): + # Any false/null values get converted to 'off'. + if data in fields.NullBooleanField.FALSE_VALUES or data in fields.NullBooleanField.NULL_VALUES: + return 'off' + return super(PendoTrackingStateField, self).to_internal_value(data) + + +class CustomLogoField(fields.CharField): + + CUSTOM_LOGO_RE = re.compile(r'^data:image/(?:png|jpeg|gif);base64,([A-Za-z0-9+/=]+?)$') + + default_error_messages = { + 'invalid_format': _('Invalid format for custom logo. Must be a data URL with a base64-encoded GIF, PNG or JPEG image.'), + 'invalid_data': _('Invalid base64-encoded data in data URL.'), + } + + def to_internal_value(self, data): + data = super(CustomLogoField, self).to_internal_value(data) + match = self.CUSTOM_LOGO_RE.match(data) + if not match: + self.fail('invalid_format') + b64data = match.group(1) + try: + base64.b64decode(b64data) + except (TypeError, binascii.Error): + self.fail('invalid_data') + return data + diff --git a/awx/ui_next/src/App.jsx b/awx/ui_next/src/App.jsx index 9b230ac553..ffafd07dba 100644 --- a/awx/ui_next/src/App.jsx +++ b/awx/ui_next/src/App.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { useRouteMatch, useLocation, - BrowserRouter, + HashRouter, Route, Switch, Redirect, @@ -76,7 +76,7 @@ function App() { } export default () => ( - <BrowserRouter basename="/next"> + <HashRouter> <App /> - </BrowserRouter> + </HashRouter> ); diff --git a/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx index 970e5651d6..f09bdbdf3f 100644 --- a/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx +++ b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx @@ -118,8 +118,8 @@ class PageHeaderToolbar extends Component { key="user" href={ loggedInUser - ? `/next/users/${loggedInUser.id}/details` - : '/next/home' + ? `/users/${loggedInUser.id}/details` + : '/home' } > {i18n._(t`User Details`)} diff --git a/awx/ui_next/urls.py b/awx/ui_next/urls.py index bb288cf625..03c3bbd3d2 100644 --- a/awx/ui_next/urls.py +++ b/awx/ui_next/urls.py @@ -10,5 +10,6 @@ class IndexView(TemplateView): app_name = 'ui_next' urlpatterns = [ - url(r'^next/*', IndexView.as_view(), name='ui_next') + url(r'^$', IndexView.as_view(), name='index'), + #url(r'^migrations_notran/$', migrations_notran, name='migrations_notran'), ] diff --git a/awx/urls.py b/awx/urls.py index bdcae84bcb..d26ea0d6f5 100644 --- a/awx/urls.py +++ b/awx/urls.py @@ -15,7 +15,6 @@ from awx.main.views import ( urlpatterns = [ url(r'', include('awx.ui_next.urls', namespace='ui_next')), - url(r'', include('awx.ui.urls', namespace='ui')), url(r'^api/', include('awx.api.urls', namespace='api')), url(r'^sso/', include('awx.sso.urls', namespace='sso')), url(r'^sso/', include('social_django.urls', namespace='social')), |