diff --git a/awx/ui/.eslintrc.js b/awx/ui/.eslintrc.js
index 217601f5f9..9a42d34ab2 100644
--- a/awx/ui/.eslintrc.js
+++ b/awx/ui/.eslintrc.js
@@ -54,6 +54,18 @@ module.exports = {
'no-multiple-empty-lines': ['error', { max: 1 }],
'object-curly-newline': 'off',
'space-before-function-paren': ['error', 'always'],
- 'no-trailing-spaces': ['error']
- }
+ 'no-trailing-spaces': ['error'],
+ 'prefer-destructuring': ['error', {
+ 'VariableDeclarator': {
+ 'array': false,
+ 'object': true
+ },
+ 'AssignmentExpression': {
+ 'array': false,
+ 'object': true
+ }
+ }, {
+ 'enforceForRenamedProperties': false
+ }]
+ }
};
diff --git a/awx/ui/client/features/applications/add-applications.controller.js b/awx/ui/client/features/applications/add-applications.controller.js
index 173460157e..486c20dffb 100644
--- a/awx/ui/client/features/applications/add-applications.controller.js
+++ b/awx/ui/client/features/applications/add-applications.controller.js
@@ -3,10 +3,8 @@ function AddApplicationsController (models, $state, strings) {
const { application, me, organization } = models;
const omit = [
- 'authorization_grant_type',
'client_id',
'client_secret',
- 'client_type',
'created',
'modified',
'related',
@@ -44,7 +42,7 @@ function AddApplicationsController (models, $state, strings) {
vm.form.organization._resource = 'organization';
vm.form.organization._route = 'applications.add.organization';
vm.form.organization._model = organization;
- vm.form.organization._placeholder = strings.get('SELECT AN ORGANIZATION');
+ vm.form.organization._placeholder = strings.get('inputs.ORGANIZATION_PLACEHOLDER');
vm.form.name.required = true;
vm.form.organization.required = true;
@@ -54,9 +52,7 @@ function AddApplicationsController (models, $state, strings) {
vm.form.save = data => {
const hiddenData = {
- authorization_grant_type: 'implicit',
- user: me.get('id'),
- client_type: 'public'
+ user: me.get('id')
};
const payload = _.merge(data, hiddenData);
diff --git a/awx/ui/client/features/applications/add-edit-applications.view.html b/awx/ui/client/features/applications/add-edit-applications.view.html
index a8d8d68e6c..9f2bbb96d3 100644
--- a/awx/ui/client/features/applications/add-edit-applications.view.html
+++ b/awx/ui/client/features/applications/add-edit-applications.view.html
@@ -15,6 +15,8 @@
+
+
diff --git a/awx/ui/client/features/applications/applications.strings.js b/awx/ui/client/features/applications/applications.strings.js
index 18158c4d4e..0194efbf42 100644
--- a/awx/ui/client/features/applications/applications.strings.js
+++ b/awx/ui/client/features/applications/applications.strings.js
@@ -25,6 +25,10 @@ function ApplicationsStrings (BaseString) {
ROW_ITEM_LABEL_ORGANIZATION: t.s('ORG'),
ROW_ITEM_LABEL_MODIFIED: t.s('LAST MODIFIED')
};
+
+ ns.inputs = {
+ ORGANIZATION_PLACEHOLDER: t.s('SELECT AN ORGANIZATION')
+ };
}
ApplicationsStrings.$inject = ['BaseStringService'];
diff --git a/awx/ui/client/features/applications/edit-applications.controller.js b/awx/ui/client/features/applications/edit-applications.controller.js
index 6279b642ee..60b08be6ba 100644
--- a/awx/ui/client/features/applications/edit-applications.controller.js
+++ b/awx/ui/client/features/applications/edit-applications.controller.js
@@ -4,10 +4,8 @@ function EditApplicationsController (models, $state, strings, $scope) {
const { me, application, organization } = models;
const omit = [
- 'authorization_grant_type',
'client_id',
'client_secret',
- 'client_type',
'created',
'modified',
'related',
@@ -54,45 +52,31 @@ function EditApplicationsController (models, $state, strings, $scope) {
vm.form.disabled = !isEditable;
+ vm.form.name.required = true;
+ vm.form.redirect_uris.required = true;
+
const isOrgAdmin = _.some(me.get('related.admin_of_organizations.results'), (org) => org.id === organization.get('id'));
const isSuperuser = me.get('is_superuser');
const isCurrentAuthor = Boolean(application.get('summary_fields.created_by.id') === me.get('id'));
-
- vm.form.organization = {
- type: 'field',
- label: 'Organization',
- id: 'organization'
- };
- vm.form.description = {
- type: 'String',
- label: 'Description',
- id: 'description'
- };
-
- vm.form.organization._resource = 'organization';
- vm.form.organization._route = 'applications.edit.organization';
- vm.form.organization._model = organization;
- vm.form.organization._placeholder = strings.get('SELECT AN ORGANIZATION');
-
- // TODO: org not returned via api endpoint, check on this
- vm.form.organization._value = application.get('organization');
-
vm.form.organization._disabled = true;
+
if (isSuperuser || isOrgAdmin || (application.get('organization') === null && isCurrentAuthor)) {
vm.form.organization._disabled = false;
}
- vm.form.name.required = true;
+ vm.form.organization._resource = 'organization';
+ vm.form.organization._model = organization;
+ vm.form.organization._route = 'applications.edit.organization';
+ vm.form.organization._value = application.get('summary_fields.organization.id');
+ vm.form.organization._displayValue = application.get('summary_fields.organization.name');
+ vm.form.organization._placeholder = strings.get('inputs.ORGANIZATION_PLACEHOLDER');
vm.form.organization.required = true;
- vm.form.redirect_uris.required = true;
delete vm.form.name.help_text;
vm.form.save = data => {
const hiddenData = {
- authorization_grant_type: 'implicit',
- user: me.get('id'),
- client_type: 'public'
+ user: me.get('id')
};
const payload = _.merge(data, hiddenData);
diff --git a/awx/ui/client/features/applications/index.js b/awx/ui/client/features/applications/index.js
index ca70be6165..af4783884f 100644
--- a/awx/ui/client/features/applications/index.js
+++ b/awx/ui/client/features/applications/index.js
@@ -62,8 +62,7 @@ function ApplicationsRun ($stateExtender, strings) {
},
data: {
activityStream: true,
- // TODO: double-check activity stream works
- activityStreamTarget: 'application'
+ activityStreamTarget: 'o_auth2_application'
},
views: {
'@': {
@@ -111,8 +110,7 @@ function ApplicationsRun ($stateExtender, strings) {
},
data: {
activityStream: true,
- // TODO: double-check activity stream works
- activityStreamTarget: 'application'
+ activityStreamTarget: 'o_auth2_application'
},
views: {
'add@applications': {
@@ -134,7 +132,7 @@ function ApplicationsRun ($stateExtender, strings) {
},
data: {
activityStream: true,
- activityStreamTarget: 'application',
+ activityStreamTarget: 'o_auth2_application',
activityStreamId: 'application_id'
},
views: {
@@ -264,8 +262,7 @@ function ApplicationsRun ($stateExtender, strings) {
},
data: {
activityStream: true,
- // TODO: double-check activity stream works
- activityStreamTarget: 'application'
+ activityStreamTarget: 'o_auth2_application'
},
views: {
'userList@applications.edit': {
diff --git a/awx/ui/client/lib/components/form/form.directive.js b/awx/ui/client/lib/components/form/form.directive.js
index dc19c61e2a..e0ac85c595 100644
--- a/awx/ui/client/lib/components/form/form.directive.js
+++ b/awx/ui/client/lib/components/form/form.directive.js
@@ -76,7 +76,9 @@ function AtFormController (eventService, strings) {
return values;
}
- if (component.state._key && typeof component.state._value === 'object') {
+ if (component.state._format === 'selectFromOptions') {
+ values[component.state.id] = component.state._value[0];
+ } else if (component.state._key && typeof component.state._value === 'object') {
values[component.state.id] = component.state._value[component.state._key];
} else if (component.state._group) {
values[component.state._key] = values[component.state._key] || {};
diff --git a/awx/ui/client/lib/components/input/base.controller.js b/awx/ui/client/lib/components/input/base.controller.js
index 5c27d903f0..32fa30b458 100644
--- a/awx/ui/client/lib/components/input/base.controller.js
+++ b/awx/ui/client/lib/components/input/base.controller.js
@@ -14,7 +14,7 @@ function BaseInputController (strings) {
scope.state._required = scope.state.required || false;
scope.state._isValid = scope.state._isValid || false;
scope.state._disabled = scope.state._disabled || false;
- scope.state._activeModel = '_value';
+ scope.state._activeModel = scope.state._activeModel || '_value';
if (scope.state.ask_at_runtime) {
scope.state._displayPromptOnLaunch = true;
@@ -49,7 +49,8 @@ function BaseInputController (strings) {
scope.state._touched = true;
}
- if (scope.state._required && !scope.state._value && !scope.state._displayValue) {
+ if (scope.state._required && (!scope.state._value || !scope.state._value[0]) &&
+ !scope.state._displayValue) {
isValid = false;
message = vm.strings.get('message.REQUIRED_INPUT_MISSING');
} else if (scope.state._validate) {
diff --git a/awx/ui/client/lib/components/input/select.directive.js b/awx/ui/client/lib/components/input/select.directive.js
index 8507573a61..ddc0e23766 100644
--- a/awx/ui/client/lib/components/input/select.directive.js
+++ b/awx/ui/client/lib/components/input/select.directive.js
@@ -59,7 +59,9 @@ function AtInputSelectController (baseInputController, eventService) {
};
vm.updateDisplayModel = () => {
- if (scope.state._format === 'array') {
+ if (scope.state._format === 'selectFromOptions') {
+ scope.displayModel = scope.state._value[1];
+ } else if (scope.state._format === 'array') {
scope.displayModel = scope.state._value;
} else if (scope.state._format === 'objects') {
scope.displayModel = scope.state._value[scope.state._display];
diff --git a/awx/ui/client/lib/models/Application.js b/awx/ui/client/lib/models/Application.js
index 5c8cdbd066..2a364eb9e7 100644
--- a/awx/ui/client/lib/models/Application.js
+++ b/awx/ui/client/lib/models/Application.js
@@ -1,6 +1,21 @@
let Base;
function createFormSchema (method, config) {
+ function mungeSelectFromOptions (configObj, value) {
+ configObj.choices = [[null, '']].concat(configObj.choices);
+ configObj._data = configObj.choices;
+ configObj._exp = 'choice[1] for choice in state._data';
+ configObj._format = 'selectFromOptions';
+
+ configObj._data.forEach((val, i) => {
+ if (val[0] === value) {
+ configObj._value = configObj._data[i];
+ }
+ });
+
+ return configObj;
+ }
+
if (!config) {
config = method;
method = 'GET';
@@ -15,11 +30,25 @@ function createFormSchema (method, config) {
Object.keys(schema).forEach(key => {
schema[key].id = key;
- if (this.has(key)) {
+ if (this.has(key) && schema[key].type !== 'choice') {
schema[key]._value = this.get(key);
}
+
+ if (schema[key].type === 'choice') {
+ schema[key] = mungeSelectFromOptions(schema[key], this.get(key));
+ }
});
+ // necessary because authorization_grant_type is not changeable on update
+ if (method === 'put') {
+ schema.authorization_grant_type = mungeSelectFromOptions(Object.assign({}, this
+ .options('actions.GET.authorization_grant_type')), this
+ .get('authorization_grant_type'));
+
+ schema.authorization_grant_type._required = false;
+ schema.authorization_grant_type._disabled = true;
+ }
+
return schema;
}
diff --git a/awx/ui/client/src/activity-stream/get-target-title.factory.js b/awx/ui/client/src/activity-stream/get-target-title.factory.js
index 3921f2d530..88d274f58f 100644
--- a/awx/ui/client/src/activity-stream/get-target-title.factory.js
+++ b/awx/ui/client/src/activity-stream/get-target-title.factory.js
@@ -43,6 +43,9 @@ export default function GetTargetTitle(i18n) {
case 'template':
rtnTitle = i18n._('TEMPLATES');
break;
+ case 'o_auth2_application':
+ rtnTitle = i18n._('APPLICATIONS');
+ break;
}
return rtnTitle;
diff --git a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js
index 10285871d0..2ce145c4dc 100644
--- a/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js
+++ b/awx/ui/client/src/activity-stream/model-to-base-path-key.factory.js
@@ -18,6 +18,9 @@ export default function ModelToBasePathKey() {
var basePathKey;
switch(model) {
+ case 'o_auth2_application':
+ basePathKey = 'applications';
+ break;
case 'project':
basePathKey = 'projects';
break;
diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js
index 7231cdd9c2..e60170c310 100644
--- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js
+++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js
@@ -21,6 +21,7 @@ export default ['templateUrl', 'i18n', function(templateUrl, i18n) {
$scope.options = [
{label: i18n._('All Activity'), value: 'dashboard'},
+ {label: i18n._('Applications'), value: 'o_auth2_application'},
{label: i18n._('Credentials'), value: 'credential'},
{label: i18n._('Hosts'), value: 'host'},
{label: i18n._('Inventories'), value: 'inventory'},