Merge pull request #2462 from mabashian/2309-breadcrumb

Move ui-view from lookup component out to form
This commit is contained in:
Jared Tabor
2018-07-11 22:30:12 -07:00
committed by GitHub
19 changed files with 265 additions and 116 deletions

View File

@@ -1,4 +1,4 @@
function AddApplicationsController (models, $state, strings) { function AddApplicationsController (models, $state, strings, $scope) {
const vm = this || {}; const vm = this || {};
const { application, me, organization } = models; const { application, me, organization } = models;
@@ -62,12 +62,19 @@ function AddApplicationsController (models, $state, strings) {
vm.form.onSaveSuccess = res => { vm.form.onSaveSuccess = res => {
$state.go('applications.edit', { application_id: res.data.id }, { reload: true }); $state.go('applications.edit', { application_id: res.data.id }, { reload: true });
}; };
$scope.$watch('organization', () => {
if ($scope.organization) {
vm.form.organization._idFromModal = $scope.organization;
}
});
} }
AddApplicationsController.$inject = [ AddApplicationsController.$inject = [
'resolvedModels', 'resolvedModels',
'$state', '$state',
'ApplicationsStrings' 'ApplicationsStrings',
'$scope'
]; ];
export default AddApplicationsController; export default AddApplicationsController;

View File

@@ -1,3 +1,4 @@
<div ui-view="organization"></div>
<at-panel> <at-panel>
<at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading> <at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading>

View File

@@ -43,6 +43,12 @@ function EditApplicationsController (models, $state, strings, $scope) {
} }
}); });
$scope.$watch('organization', () => {
if ($scope.organization) {
vm.form.organization._idFromModal = $scope.organization;
}
});
if (isEditable) { if (isEditable) {
vm.form = application.createFormSchema('put', { omit }); vm.form = application.createFormSchema('put', { omit });
} else { } else {

View File

@@ -115,6 +115,18 @@ function AddCredentialsController (models, $state, $scope, strings, componentsSt
return { obj, error }; return { obj, error };
}; };
$scope.$watch('organization', () => {
if ($scope.organization) {
vm.form.organization._idFromModal = $scope.organization;
}
});
$scope.$watch('credential_type', () => {
if ($scope.credential_type) {
vm.form.credential_type._idFromModal = $scope.credential_type;
}
});
} }
AddCredentialsController.$inject = [ AddCredentialsController.$inject = [

View File

@@ -1,3 +1,5 @@
<div ui-view="organization"></div>
<div ui-view="credential_type"></div>
<at-panel ng-if="!$state.current.name.includes('permissions')"> <at-panel ng-if="!$state.current.name.includes('permissions')">
<at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading> <at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading>

View File

@@ -32,6 +32,18 @@ function EditCredentialsController (models, $state, $scope, strings, componentsS
} }
}); });
$scope.$watch('organization', () => {
if ($scope.organization) {
vm.form.organization._idFromModal = $scope.organization;
}
});
$scope.$watch('credential_type', () => {
if ($scope.credential_type) {
vm.form.credential_type._idFromModal = $scope.credential_type;
}
});
// Only exists for permissions compatibility // Only exists for permissions compatibility
$scope.credential_obj = credential.get(); $scope.credential_obj = credential.get();

View File

@@ -13,8 +13,7 @@ export default {
} }
}, },
data: { data: {
basePath: 'applications', basePath: 'applications'
formChildState: true
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
skip: true skip: true
@@ -42,14 +41,19 @@ export default {
name: { name: {
key: true, key: true,
label: 'Name', label: 'Name',
columnClass: 'col-lg-4 col-md-6 col-sm-8 col-xs-8', columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-4',
awToolTip: '{{application.description | sanitize}}', awToolTip: '{{application.description | sanitize}}',
dataPlacement: 'top' dataPlacement: 'top'
}, },
}, organization: {
actions: { label: 'Organization',
}, columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-4',
fieldActions: { modalColumnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-4',
key: false,
ngBind: 'application.summary_fields.organization.name',
sourceModel: 'organization',
includeModal: true
}
} }
})], })],
Dataset: ['QuerySet', 'GetBasePath', '$stateParams', 'ListDefinition', Dataset: ['QuerySet', 'GetBasePath', '$stateParams', 'ListDefinition',

View File

@@ -1,6 +1,6 @@
function AddTokensController ( function AddTokensController (
models, $state, strings, Rest, Alert, Wait, GetBasePath, models, $state, strings, Rest, Alert, Wait, GetBasePath,
$filter, ProcessErrors $filter, ProcessErrors, $scope
) { ) {
const vm = this || {}; const vm = this || {};
const { application } = models; const { application } = models;
@@ -94,6 +94,12 @@ function AddTokensController (
vm.form.onSaveSuccess = () => { vm.form.onSaveSuccess = () => {
$state.go('^', { user_id: $state.params.user_id }, { reload: true }); $state.go('^', { user_id: $state.params.user_id }, { reload: true });
}; };
$scope.$watch('application', () => {
if ($scope.application) {
vm.form.application._idFromModal = $scope.application;
}
});
} }
AddTokensController.$inject = [ AddTokensController.$inject = [
@@ -105,7 +111,8 @@ AddTokensController.$inject = [
'Wait', 'Wait',
'GetBasePath', 'GetBasePath',
'$filter', '$filter',
'ProcessErrors' 'ProcessErrors',
'$scope'
]; ];
export default AddTokensController; export default AddTokensController;

View File

@@ -1,3 +1,4 @@
<div ui-view="application"></div>
<at-panel> <at-panel>
<at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading> <at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading>
@@ -8,7 +9,7 @@
<at-input-select col="4" tab="3" state="vm.form.scope"></at-input-select> <at-input-select col="4" tab="3" state="vm.form.scope"></at-input-select>
<at-action-group col="12" pos="right"> <at-action-group col="12" pos="right">
<at-form-action type="cancel" to="tokens"></at-form-action> <at-form-action type="cancel" to="users.edit.tokens"></at-form-action>
<at-form-action type="save"></at-form-action> <at-form-action type="save"></at-form-action>
</at-action-group> </at-action-group>
</at-form> </at-form>

View File

@@ -16,6 +16,21 @@ TokensDetailResolve.$inject = [
'ApplicationModel' 'ApplicationModel'
]; ];
function isMeResolve ($rootScope, $stateParams, $state) {
// The user should not be able to add tokens for users other than
// themselves. Adding this redirect so that a user is not able to
// visit the add-token URL directly for a different user.
if (_.has($stateParams, 'user_id') && Number($stateParams.user_id) !== $rootScope.current_user.id) {
$state.go('users');
}
}
isMeResolve.$inject = [
'$rootScope',
'$stateParams',
'$state'
];
export default { export default {
url: '/add-token', url: '/add-token',
name: 'users.edit.tokens.add', name: 'users.edit.tokens.add',
@@ -30,13 +45,14 @@ export default {
label: N_('CREATE TOKEN') label: N_('CREATE TOKEN')
}, },
views: { views: {
'preFormView@users.edit': { 'preFormView@users': {
templateUrl: addTemplate, templateUrl: addTemplate,
controller: AddController, controller: AddController,
controllerAs: 'vm' controllerAs: 'vm'
} }
}, },
resolve: { resolve: {
resolvedModels: TokensDetailResolve resolvedModels: TokensDetailResolve,
isMe: isMeResolve
} }
}; };

View File

@@ -34,23 +34,20 @@ function AtInputLookupController (baseInputController, $q, $state) {
} }
}; };
scope.$watch(scope.state._resource, vm.watchResource); // This should get triggered when the user selects something in the lookup modal and
// hits save to close the modal. This won't get triggered when the user types in
// a value in the input.
scope.$watch('state._idFromModal', () => {
if (scope.state._idFromModal &&
(scope.state._idFromModal !== scope.state._value)
) {
vm.search({ id: scope.state._idFromModal });
}
});
vm.check(); vm.check();
}; };
vm.watchResource = () => {
if (!scope[scope.state._resource]) {
return;
}
if (scope[scope.state._resource] !== scope.state._value) {
scope.state._displayValue = scope[`${scope.state._resource}_name`];
vm.search();
}
};
vm.lookup = () => { vm.lookup = () => {
const params = {}; const params = {};
@@ -62,6 +59,7 @@ function AtInputLookupController (baseInputController, $q, $state) {
}; };
vm.reset = () => { vm.reset = () => {
scope.state._idFromModal = undefined;
scope.state._value = undefined; scope.state._value = undefined;
scope[scope.state._resource] = undefined; scope[scope.state._resource] = undefined;
}; };
@@ -80,15 +78,20 @@ function AtInputLookupController (baseInputController, $q, $state) {
vm.searchAfterDebounce(); vm.searchAfterDebounce();
}; };
vm.search = () => { vm.search = (searchParams) => {
scope.state._touched = true; scope.state._touched = true;
if (scope.state._displayValue === '' && !scope.state._required) { if (!scope.state._required &&
scope.state._displayValue === '' &&
!scope.state._idFromModal
) {
scope.state._value = null; scope.state._value = null;
return vm.check({ isValid: true }); return vm.check({ isValid: true });
} }
return model.search({ [search.key]: scope.state._displayValue }, search.config) searchParams = searchParams || { [search.key]: scope.state._displayValue };
return model.search(searchParams, search.config)
.then(found => { .then(found => {
if (!found) { if (!found) {
vm.reset(); vm.reset();
@@ -99,6 +102,7 @@ function AtInputLookupController (baseInputController, $q, $state) {
scope[scope.state._resource] = model.get('id'); scope[scope.state._resource] = model.get('id');
scope.state._value = model.get('id'); scope.state._value = model.get('id');
scope.state._displayValue = model.get('name'); scope.state._displayValue = model.get('name');
scope.state._idFromModal = undefined;
}) })
.catch(() => vm.reset()) .catch(() => vm.reset())
.finally(() => { .finally(() => {

View File

@@ -30,6 +30,4 @@
<at-input-message></at-input-message> <at-input-message></at-input-message>
</div> </div>
</div>
<div ui-view="{{ state._resource }}"></div>
</div>

View File

@@ -167,7 +167,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
wrapPanel(html, ignorePanel){ wrapPanel(html, ignorePanel){
if(ignorePanel) { if(ignorePanel) {
return ` return `
<div ui-view="preFormView"></div>
<div> <div>
${html} ${html}
<div ui-view="related"></div> <div ui-view="related"></div>
@@ -176,7 +175,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
} }
else { else {
return ` return `
<div ui-view="preFormView"></div>
${MessageBar(this.form)} ${MessageBar(this.form)}
<div class="Panel"> <div class="Panel">
${html} ${html}

View File

@@ -545,22 +545,27 @@ export default ['$compile', 'Attr', 'Icon',
} }
} }
if (options.mode === 'lookup') { if (options.mode === 'lookup') {
let customClass = list.fields.name.modalColumnClass || ''; for (fld in list.fields) {
html += `<th if(fld === 'name' || _.has(list.fields[fld], 'includeModal')){
base-path="${list.basePath || list.name}" let customClass = list.fields.name.modalColumnClass || '';
collection="${list.name}" html += `<th
dataset="${list.iterator}_dataset" base-path="${list.basePath || list.name}"
column-sort collection="${list.name}"
column-field="name" dataset="${list.iterator}_dataset"
column-iterator="${list.iterator}" column-sort
column-no-sort="${list.fields.name.nosort}" column-field="name"
column-label="${list.fields.name.label}" column-iterator="${list.iterator}"
column-custom-class="${customClass}" column-no-sort="${list.fields.name.nosort}"
query-set="${list.iterator}_queryset"> column-label="${list.fields[fld].label}"
</th>`; column-custom-class="${customClass}"
query-set="${list.iterator}_queryset">
</th>`;
}
}
if(list.fields.info) { if(list.fields.info) {
customClass = list.fields.name.modalColumnClass || ''; let customClass = list.fields.name.modalColumnClass || '';
const infoHeaderClass = _.get(list.fields.info, 'infoHeaderClass', 'List-tableHeader--info'); const infoHeaderClass = _.get(list.fields.info, 'infoHeaderClass', 'List-tableHeader--info');
html += `<th html += `<th
class="${infoHeaderClass}" class="${infoHeaderClass}"

View File

@@ -14,10 +14,10 @@ const user_type_options = [
export default ['$scope', '$rootScope', 'UserForm', 'GenerateForm', 'Rest', export default ['$scope', '$rootScope', 'UserForm', 'GenerateForm', 'Rest',
'Alert', 'ProcessErrors', 'ReturnToCaller', 'GetBasePath', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'GetBasePath',
'Wait', 'CreateSelect2', '$state', '$location', 'i18n', 'Wait', 'CreateSelect2', '$state', '$location', 'i18n', 'canAdd',
function($scope, $rootScope, UserForm, GenerateForm, Rest, Alert, function($scope, $rootScope, UserForm, GenerateForm, Rest, Alert,
ProcessErrors, ReturnToCaller, GetBasePath, Wait, CreateSelect2, ProcessErrors, ReturnToCaller, GetBasePath, Wait, CreateSelect2,
$state, $location, i18n) { $state, $location, i18n, canAdd) {
var defaultUrl = GetBasePath('organizations'), var defaultUrl = GetBasePath('organizations'),
form = UserForm; form = UserForm;
@@ -28,6 +28,7 @@ export default ['$scope', '$rootScope', 'UserForm', 'GenerateForm', 'Rest',
// apply form definition's default field values // apply form definition's default field values
GenerateForm.applyDefaults(form, $scope); GenerateForm.applyDefaults(form, $scope);
$scope.canAdd = canAdd;
$scope.isAddForm = true; $scope.isAddForm = true;
$scope.ldap_user = false; $scope.ldap_user = false;
$scope.not_ldap_user = !$scope.ldap_user; $scope.not_ldap_user = !$scope.ldap_user;

View File

@@ -14,10 +14,10 @@ const user_type_options = [
export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest', export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest',
'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2', 'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2',
'$state', 'i18n', 'resolvedModels', '$state', 'i18n', 'resolvedModels', 'resourceData',
function($scope, $rootScope, $stateParams, UserForm, Rest, ProcessErrors, function($scope, $rootScope, $stateParams, UserForm, Rest, ProcessErrors,
GetBasePath, Wait, CreateSelect2, $state, i18n, models) { GetBasePath, Wait, CreateSelect2, $state, i18n, models, resourceData) {
for (var i = 0; i < user_type_options.length; i++) { for (var i = 0; i < user_type_options.length; i++) {
user_type_options[i].label = i18n._(user_type_options[i].label); user_type_options[i].label = i18n._(user_type_options[i].label);
} }
@@ -26,7 +26,10 @@ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest',
var form = UserForm, var form = UserForm,
master = {}, master = {},
id = $stateParams.user_id, id = $stateParams.user_id,
defaultUrl = GetBasePath('users') + id; defaultUrl = GetBasePath('users') + id,
user_obj = resourceData.data;
$scope.breadcrumb.user_name = user_obj.username;
init(); init();
@@ -40,49 +43,39 @@ export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'Rest',
$scope.user_type = user_type_options[0]; $scope.user_type = user_type_options[0];
$scope.$watch('user_type', user_type_sync($scope)); $scope.$watch('user_type', user_type_sync($scope));
$scope.$watch('is_superuser', hidePermissionsTabSmartSearchAndPaginationIfSuperUser($scope)); $scope.$watch('is_superuser', hidePermissionsTabSmartSearchAndPaginationIfSuperUser($scope));
Rest.setUrl(defaultUrl); $scope.user_id = id;
Wait('start'); $scope.ldap_user = (user_obj.ldap_dn !== null && user_obj.ldap_dn !== undefined && user_obj.ldap_dn !== '') ? true : false;
Rest.get(defaultUrl).then(({data}) => { $scope.not_ldap_user = !$scope.ldap_user;
$scope.user_id = id; master.ldap_user = $scope.ldap_user;
$scope.ldap_user = (data.ldap_dn !== null && data.ldap_dn !== undefined && data.ldap_dn !== '') ? true : false; $scope.socialAuthUser = (user_obj.auth.length > 0) ? true : false;
$scope.not_ldap_user = !$scope.ldap_user; $scope.external_account = user_obj.external_account;
master.ldap_user = $scope.ldap_user;
$scope.socialAuthUser = (data.auth.length > 0) ? true : false;
$scope.external_account = data.external_account;
$scope.user_type = $scope.user_type_options[0]; $scope.user_type = $scope.user_type_options[0];
$scope.is_system_auditor = false; $scope.is_system_auditor = false;
$scope.is_superuser = false; $scope.is_superuser = false;
if (data.is_system_auditor) { if (user_obj.is_system_auditor) {
$scope.user_type = $scope.user_type_options[1]; $scope.user_type = $scope.user_type_options[1];
$scope.is_system_auditor = true; $scope.is_system_auditor = true;
} }
if (data.is_superuser) { if (user_obj.is_superuser) {
$scope.user_type = $scope.user_type_options[2]; $scope.user_type = $scope.user_type_options[2];
$scope.is_superuser = true; $scope.is_superuser = true;
} }
$scope.user_obj = data; $scope.user_obj = user_obj;
$scope.name = data.username; $scope.name = user_obj.username;
CreateSelect2({ CreateSelect2({
element: '#user_user_type', element: '#user_user_type',
multiple: false multiple: false
}); });
$scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) { $scope.$watch('user_obj.summary_fields.user_capabilities.edit', function(val) {
$scope.canAdd = (val === false) ? false : true; $scope.canAdd = (val === false) ? false : true;
}); });
setScopeFields(data); setScopeFields(user_obj);
Wait('stop');
})
.catch(({data, status}) => {
ProcessErrors($scope, data, status, null, {
hdr: i18n._('Error!'),
msg: i18n.sprintf(i18n._('Failed to retrieve user: %s. GET status: '), $stateParams.id) + status
});
});
} }
function user_type_sync($scope) { function user_type_sync($scope) {

View File

@@ -10,12 +10,11 @@ import UsersEdit from './edit/users-edit.controller';
import UserForm from './users.form'; import UserForm from './users.form';
import UserList from './users.list'; import UserList from './users.list';
import userListRoute from './users.route';
import UserTokensListRoute from '../../features/users/tokens/users-tokens-list.route'; import UserTokensListRoute from '../../features/users/tokens/users-tokens-list.route';
import UserTokensAddRoute from '../../features/users/tokens/users-tokens-add.route'; import UserTokensAddRoute from '../../features/users/tokens/users-tokens-add.route';
import UserTokensAddApplicationRoute from '../../features/users/tokens/users-tokens-add-application.route'; import UserTokensAddApplicationRoute from '../../features/users/tokens/users-tokens-add-application.route';
import { N_ } from '../i18n';
export default export default
angular.module('Users', []) angular.module('Users', [])
.controller('UsersList', UsersList) .controller('UsersList', UsersList)
@@ -29,20 +28,52 @@ angular.module('Users', [])
let stateExtender = $stateExtenderProvider.$get(); let stateExtender = $stateExtenderProvider.$get();
function generateStateTree() { function generateStateTree() {
let userTree = stateDefinitions.generateTree({ let userAdd = stateDefinitions.generateTree({
parent: 'users', name: 'users.add',
modes: ['add', 'edit'], url: '/add',
list: 'UserList', modes: ['add'],
form: 'UserForm', form: 'UserForm',
controllers: { controllers: {
list: UsersList, add: 'UsersAdd'
add: UsersAdd, },
edit: UsersEdit resolve: {
add: {
canAdd: ['rbacUiControlService', '$state', function(rbacUiControlService, $state) {
return rbacUiControlService.canAdd('users')
.then(function(res) {
return res.canAdd;
})
.catch(function() {
$state.go('users');
});
}],
resolvedModels: ['MeModel', '$q', function(Me, $q) {
const promises= {
me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations'))
};
return $q.all(promises);
}]
}
}
});
let userEdit = stateDefinitions.generateTree({
name: 'users.edit',
url: '/:user_id',
modes: ['edit'],
form: 'UserForm',
parent: 'users',
controllers: {
edit: 'UsersEdit'
}, },
data: { data: {
activityStream: true, activityStream: true,
activityStreamTarget: 'user' activityStreamTarget: 'user'
}, },
breadcrumbs: {
edit: "{{breadcrumb.user_name}}"
},
resolve: { resolve: {
edit: { edit: {
resolvedModels: ['MeModel', '$q', function(Me, $q) { resolvedModels: ['MeModel', '$q', function(Me, $q) {
@@ -50,31 +81,21 @@ angular.module('Users', [])
me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations')) me: new Me('get').then((me) => me.extend('get', 'admin_of_organizations'))
}; };
return $q.all(promises);
}]
},
list: {
resolvedModels: ['MeModel', '$q', function(Me, $q) {
const promises= {
me: new Me('get')
};
return $q.all(promises); return $q.all(promises);
}] }]
} }
}, },
ncyBreadcrumb: {
label: N_('USERS')
}
}); });
return Promise.all([ return Promise.all([
userTree userAdd,
userEdit
]).then((generated) => { ]).then((generated) => {
return { return {
states: _.reduce(generated, (result, definition) => { states: _.reduce(generated, (result, definition) => {
return result.concat(definition.states); return result.concat(definition.states);
}, [ }, [
stateExtender.buildDefinition(userListRoute),
stateExtender.buildDefinition(UserTokensListRoute), stateExtender.buildDefinition(UserTokensListRoute),
stateExtender.buildDefinition(UserTokensAddRoute), stateExtender.buildDefinition(UserTokensAddRoute),
stateExtender.buildDefinition(UserTokensAddApplicationRoute) stateExtender.buildDefinition(UserTokensAddApplicationRoute)

View File

@@ -0,0 +1,6 @@
<div class="tab-pane" id="users-panel">
<aw-limit-panels max-panels="2" panel-container="users-panel"></aw-limit-panels>
<div ui-view="preFormView"></div>
<div ui-view="form"></div>
<div ui-view="list"></div>
</div>

View File

@@ -0,0 +1,55 @@
import {templateUrl} from '../shared/template-url/template-url.factory';
import { N_ } from '../i18n';
export default {
name: 'users',
route: '/users',
ncyBreadcrumb: {
label: N_('USERS')
},
data: {
activityStream: true,
activityStreamTarget: 'user'
},
params: {
user_search: {
value: {
page_size: 20,
order_by: 'username'
}
}
},
views: {
'@': {
templateUrl: templateUrl('users/users')
},
'list@users': {
templateProvider: function(UserList, generateList) {
let html = generateList.build({
list: UserList,
mode: 'edit'
});
html = generateList.wrapPanel(html);
return html;
},
controller: 'UsersList'
}
},
searchPrefix: 'user',
resolve: {
Dataset: ['UserList', 'QuerySet', '$stateParams', 'GetBasePath',
function(list, qs, $stateParams, GetBasePath) {
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}
],
resolvedModels: ['MeModel', '$q', function(Me, $q) {
const promises= {
me: new Me('get')
};
return $q.all(promises);
}]
}
};