diff --git a/awx/ui/client/lib/components/input/label.partial.html b/awx/ui/client/lib/components/input/label.partial.html
index d53a4a7a25..2a9a61690f 100644
--- a/awx/ui/client/lib/components/input/label.partial.html
+++ b/awx/ui/client/lib/components/input/label.partial.html
@@ -8,7 +8,7 @@
-
Prompt on launch
+
{{ vm.strings.components.label.PROMPT_ON_LAUNCH }}
diff --git a/awx/ui/client/lib/components/input/lookup.directive.js b/awx/ui/client/lib/components/input/lookup.directive.js
index 50e7ddef88..f8845ddd19 100644
--- a/awx/ui/client/lib/components/input/lookup.directive.js
+++ b/awx/ui/client/lib/components/input/lookup.directive.js
@@ -20,12 +20,13 @@ function AtInputLookupController (baseInputController, $state, $stateParams) {
scope = _scope_;
scope.$watch(scope.state._resource, vm.watchResource);
+ scope.state._validate = vm.checkOnInput;
vm.check();
};
vm.watchResource = () => {
- if (scope[scope.state._resource]) {
+ if (scope[scope.state._resource] !== scope.state._value) {
scope.state._value = scope[scope.state._resource];
scope.state._displayValue = scope[`${scope.state._resource}_name`];
@@ -33,15 +34,43 @@ function AtInputLookupController (baseInputController, $state, $stateParams) {
}
};
- vm.search = () => {
+ vm.lookup = () => {
let params = {};
- if (scope.state._value) {
+ if (scope.state._value && scope.state._isValid) {
params.selected = scope.state._value;
}
$state.go(scope.state._route, params);
};
+
+ vm.reset = () => {
+ scope.state._value = undefined;
+ scope[scope.state._resource] = undefined;
+ };
+
+ vm.checkOnInput = () => {
+ if (!scope.state._touched) {
+ return { isValid: true };
+ }
+
+ let result = scope.state._model.match('get', 'name', scope.state._displayValue);
+
+ if (result) {
+ scope[scope.state._resource] = result.id;
+ scope.state._value = result.id;
+ scope.state._displayValue = result.name;
+
+ return { isValid: true };
+ }
+
+ vm.reset();
+
+ return {
+ isValid: false,
+ message: vm.strings.components.lookup.NOT_FOUND
+ };
+ };
}
AtInputLookupController.$inject = [
diff --git a/awx/ui/client/lib/components/input/lookup.partial.html b/awx/ui/client/lib/components/input/lookup.partial.html
index 6f4d7a0a68..39900afcc1 100644
--- a/awx/ui/client/lib/components/input/lookup.partial.html
+++ b/awx/ui/client/lib/components/input/lookup.partial.html
@@ -6,13 +6,13 @@
{
if (scope.type === 'password') {
scope.type = 'text';
- scope.state._buttonText = 'HIDE';
+ scope.state._buttonText = vm.strings.components.HIDE;
} else {
scope.type = 'password';
- scope.state._buttonText = 'SHOW';
+ scope.state._buttonText = vm.strings.components.SHOW;
}
};
}
diff --git a/awx/ui/client/lib/components/input/select.directive.js b/awx/ui/client/lib/components/input/select.directive.js
index cbc6c2b0fa..d61c13f33d 100644
--- a/awx/ui/client/lib/components/input/select.directive.js
+++ b/awx/ui/client/lib/components/input/select.directive.js
@@ -1,5 +1,3 @@
-const DEFAULT_EMPTY_PLACEHOLDER = 'NO OPTIONS AVAILABLE';
-
function atInputSelectLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@@ -11,7 +9,7 @@ function atInputSelectLink (scope, element, attrs, controllers) {
inputController.init(scope, element, formController);
}
-function AtInputSelectController (baseInputController, eventService) {
+function AtInputSelectController (baseInputController, eventService) {
let vm = this || {};
let scope;
@@ -29,7 +27,7 @@ function AtInputSelectController (baseInputController, eventService) {
if (!scope.state._data || scope.state._data.length === 0) {
scope.state._disabled = true;
- scope.state._placeholder = DEFAULT_EMPTY_PLACEHOLDER;
+ scope.state._placeholder = vm.strings.components.EMPTY_PLACEHOLDER;
}
vm.setListeners();
@@ -67,7 +65,7 @@ function AtInputSelectController (baseInputController, eventService) {
} else if (scope.state._format === 'grouped-object') {
scope.displayModel = scope.state._value[scope.state._display];
} else {
- throw new Error('Unsupported display model type');
+ throw new Error(vm.strings.components.UNSUPPORTED_TYPE_ERROR);
}
};
}
diff --git a/awx/ui/client/lib/components/input/textarea-secret.directive.js b/awx/ui/client/lib/components/input/textarea-secret.directive.js
index a22491b3de..ffe15c2741 100644
--- a/awx/ui/client/lib/components/input/textarea-secret.directive.js
+++ b/awx/ui/client/lib/components/input/textarea-secret.directive.js
@@ -1,5 +1,3 @@
-const DEFAULT_HINT = 'HINT: Drag and drop an SSH private key file on the field below.';
-
function atInputTextareaSecretLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@@ -28,13 +26,13 @@ function AtInputTextareaSecretController (baseInputController, eventService) {
if (scope.state.format === 'ssh_private_key') {
scope.ssh = true;
- scope.state._hint = scope.state._hint || DEFAULT_HINT;
+ scope.state._hint = scope.state._hint || vm.strings.components.textarea.SSH_KEY_HINT;
input = element.find('input')[0];
}
if (scope.state._value) {
- scope.state._buttonText = 'REPLACE';
- scope.state._placeholder = 'ENCRYPTED';
+ scope.state._buttonText = vm.strings.components.REPLACE;
+ scope.state._placeholder = vm.strings.components.ENCRYPTED;
} else {
if (scope.state.format === 'ssh_private_key') {
vm.listeners = vm.setFileListeners(textarea, input);
@@ -54,7 +52,7 @@ function AtInputTextareaSecretController (baseInputController, eventService) {
vm.listeners = vm.setFileListeners(textarea, input);
} else {
scope.state._displayHint = false;
- scope.state._placeholder = 'ENCRYPTED';
+ scope.state._placeholder = vm.strings.components.ENCRYPTED;
eventService.remove(vm.listeners);
}
};
@@ -92,7 +90,11 @@ function AtInputTextareaSecretController (baseInputController, eventService) {
};
}
-AtInputTextareaSecretController.$inject = ['BaseInputController', 'EventService'];
+AtInputTextareaSecretController.$inject = [
+ 'BaseInputController',
+ 'EventService',
+ 'ComponentsStrings'
+];
function atInputTextareaSecret (pathService) {
return {
diff --git a/awx/ui/client/lib/components/modal/modal.partial.html b/awx/ui/client/lib/components/modal/modal.partial.html
index 66db4d9d49..4ff2f44a99 100644
--- a/awx/ui/client/lib/components/modal/modal.partial.html
+++ b/awx/ui/client/lib/components/modal/modal.partial.html
@@ -23,7 +23,7 @@
diff --git a/awx/ui/client/lib/components/tabs/group.directive.js b/awx/ui/client/lib/components/tabs/group.directive.js
index a78da714cf..907d8853fa 100644
--- a/awx/ui/client/lib/components/tabs/group.directive.js
+++ b/awx/ui/client/lib/components/tabs/group.directive.js
@@ -18,16 +18,8 @@ function AtTabGroupController ($state) {
};
vm.register = tab => {
-
tab.active = true;
-/*
- * if (vm.tabs.length === 0) {
- * tab.active = true;
- * } else {
- * tab.disabled = true;
- * }
- *
- */
+
vm.tabs.push(tab);
};
}
diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js
index 8f4e04cbc2..d1c8fd2936 100644
--- a/awx/ui/client/lib/models/Base.js
+++ b/awx/ui/client/lib/models/Base.js
@@ -12,8 +12,10 @@ function request (method, resource) {
}
function httpGet (resource) {
+ this.method = this.method || 'GET';
+
let req = {
- method: 'GET',
+ method: this.method,
url: this.path
};
@@ -85,6 +87,26 @@ function get (keys) {
return this.find('get', keys);
}
+function match (method, key, value) {
+ let model = this.model[method.toUpperCase()];
+
+ if (!model) {
+ return null;
+ }
+
+ if (!model.results) {
+ if (model[key] === value) {
+ return model;
+ }
+
+ return null;
+ }
+
+ let result = model.results.filter(result => result[key] === value);
+
+ return result.length === 0 ? null : result[0];
+}
+
function find (method, keys) {
let value = this.model[method.toUpperCase()];
@@ -138,6 +160,7 @@ function BaseModel (path) {
this.get = get;
this.options = options;
this.find = find;
+ this.match = match;
this.normalizePath = normalizePath;
this.getById = getById;
this.request = request;
diff --git a/awx/ui/client/lib/models/CredentialType.js b/awx/ui/client/lib/models/CredentialType.js
index 2679e191d7..f0ab0d6f0f 100644
--- a/awx/ui/client/lib/models/CredentialType.js
+++ b/awx/ui/client/lib/models/CredentialType.js
@@ -26,11 +26,18 @@ function mergeInputProperties (type) {
});
}
+function graft (id) {
+ let data = this.getById(id);
+
+ return new CredentialTypeModel('get', data);
+}
+
function CredentialTypeModel (method, id) {
BaseModel.call(this, 'credential_types');
this.categorizeByKind = categorizeByKind.bind(this);
this.mergeInputProperties = mergeInputProperties.bind(this);
+ this.graft = graft.bind(this);
return this.request(method, id)
.then(() => this);
diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js
new file mode 100644
index 0000000000..7226a921a9
--- /dev/null
+++ b/awx/ui/client/lib/services/base-string.service.js
@@ -0,0 +1,23 @@
+let i18n;
+
+function BaseStringService (namespace) {
+ let t = i18n._;
+
+ this.t = t;
+ this[namespace] = {};
+
+ this.CANCEL = t('CANCEL');
+ this.SAVE = t('SAVE');
+ this.OK = t('OK');
+}
+
+
+function BaseStringServiceLoader (_i18n_) {
+ i18n = _i18n_;
+
+ return BaseStringService;
+}
+
+BaseStringServiceLoader.$inject = ['i18n'];
+
+export default BaseStringServiceLoader;
diff --git a/awx/ui/client/lib/services/index.js b/awx/ui/client/lib/services/index.js
index d6a256b0fc..f82bc49616 100644
--- a/awx/ui/client/lib/services/index.js
+++ b/awx/ui/client/lib/services/index.js
@@ -1,7 +1,9 @@
import EventService from './event.service';
import PathService from './path.service';
+import BaseStringService from './base-string.service';
angular
.module('at.lib.services', [])
.service('EventService', EventService)
- .service('PathService', PathService);
+ .service('PathService', PathService)
+ .service('BaseStringService', BaseStringService);
diff --git a/awx/ui/client/src/i18n.js b/awx/ui/client/src/i18n.js
index 2b24a09822..062c2455ea 100644
--- a/awx/ui/client/src/i18n.js
+++ b/awx/ui/client/src/i18n.js
@@ -1,6 +1,7 @@
/* jshint ignore:start */
var sprintf = require('sprintf-js').sprintf;
+let defaultLanguage = 'en_US';
/**
* @ngdoc method
@@ -24,7 +25,12 @@ export default
$window.navigator.userLanguage ||
'';
var langUrl = langInfo.replace('-', '_');
- //gettextCatalog.debug = true;
+
+ if (langUrl === defaultLanguage) {
+ return;
+ }
+
+ // gettextCatalog.debug = true;
gettextCatalog.setCurrentLanguage(langInfo);
gettextCatalog.loadRemote('/static/languages/' + langUrl + '.json');
};
diff --git a/awx/ui/grunt-tasks/nggettext_extract.js b/awx/ui/grunt-tasks/nggettext_extract.js
index de297658e3..b264b6ada8 100644
--- a/awx/ui/grunt-tasks/nggettext_extract.js
+++ b/awx/ui/grunt-tasks/nggettext_extract.js
@@ -1,11 +1,19 @@
+let source = [
+ 'client/features/**/*.js',
+ 'client/features/**/*.html',
+ 'client/lib/**/*.js',
+ 'client/lib/**/*.html',
+ 'client/src/**/*.js',
+ 'client/src/**/*.html'
+];
+
module.exports = {
all: {
options: {
- markerNames: ['_', 'N_']
+ markerNames: ['_', 'N_', 't']
},
files: {
- 'po/ansible-tower-ui.pot': ['client/src/**/*.js',
- 'client/src/**/*.html']
+ 'po/ansible-tower-ui.pot': source
}
- },
+ }
};