Merge pull request #1588 from westlywright/tech-preview

Tech preview
This commit is contained in:
Vincent Fiduccia 2018-01-22 22:21:31 -07:00 committed by GitHub
commit b3b0375c48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 314 additions and 75 deletions

View File

@ -1,10 +1,10 @@
import { computed } from '@ember/object'; import { get, computed, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
export default Controller.extend({ export default Controller.extend({
access: service(), access: service(),
sortBy: 'name', sortBy: null,
headers: computed('isLocal', function() { headers: computed('isLocal', function() {
let out = [ let out = [
{ {
@ -26,6 +26,11 @@ export default Controller.extend({
return out; return out;
}), }),
init() {
this._super(...arguments);
set(this, 'sortBy', get(this, 'isLocal') ? 'username' : 'name');
},
isLocal: computed('access.provider', function() { isLocal: computed('access.provider', function() {
return true; // TODO 2.0 return true; // TODO 2.0
// return this.get('access.provider') === 'localauthconfig'; // return this.get('access.provider') === 'localauthconfig';

View File

@ -24,14 +24,4 @@ export default Route.extend({
} }
}); });
}, },
setupController(controller, model) {
this._super(...arguments);
controller.set('errors', null);
let bindings = (get(model,'cluster.clusterRoleTemplateBindings')||[]).slice();
bindings = bindings.filter(x =>get(x, 'name') !== 'creator');
set(controller, 'memberArray', bindings);
}
}); });

View File

@ -21,10 +21,9 @@
}} }}
{{form-members {{form-members
creator=model.me creator=model.me
editing=false editing=true
memberArray=memberArray
memberConfig=memberConfig memberConfig=memberConfig
project=primaryResource primaryResource=primaryResource
roles=model.roles roles=model.roles
users=model.users users=model.users
type="cluster" type="cluster"

View File

@ -112,9 +112,8 @@
{{form-members {{form-members
creator=model.me creator=model.me
editing=false editing=false
memberArray=memberArray
memberConfig=memberConfig memberConfig=memberConfig
project=primaryResource primaryResource=primaryResource
roles=model.roles roles=model.roles
users=model.users users=model.users
type="cluster" type="cluster"

View File

@ -1,7 +1,6 @@
import Component from '@ember/component'; import Component from '@ember/component';
import layout from './template'; import layout from './template';
import { computed, observer, get, set } from '@ember/object'; import { computed, get, set, setProperties } from '@ember/object';
import { on } from '@ember/object/evented';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { all as PromiseAll } from 'rsvp'; import { all as PromiseAll } from 'rsvp';
@ -14,41 +13,103 @@ export default Component.extend({
memberArray: null, memberArray: null,
memberConfig: null, memberConfig: null,
model: null, model: null,
project: null, primaryResource: null,
roles: null, roles: null,
type: null, type: null,
users: null, users: null,
creator: null, creator: null,
showCreator: true, showCreator: true,
toAddCustom: null,
toAdd: null, _bindings: null,
toUpdate: null,
toRemove: null,
init() { init() {
this._super(...arguments); this._super(...arguments);
set(this, 'toAdd', []); this.buildUpdateList(get(this,'primaryResource'))
set(this, 'toUpdate', []);
set(this, 'toRemove', []);
this.sendAction('initAlert', this.primaryResourceSaved.bind(this)); this.sendAction('initAlert', this.primaryResourceSaved.bind(this));
}, },
didReceiveAttrs() { buildUpdateList(resource) {
let ma = get(this, 'memberArray').filter(( m ) => { let bindingType = `${get(resource, 'type')}RoleTemplateBindings`;
return get(m, 'roleTemplateId').indexOf('-owner') < 0 && !get(this, 'toAdd').includes(m); let bindings = get(resource, bindingType).filter(b => b.name !== 'creator');
}); let existing = [];
if (ma && get(ma, 'length') > 0) { let grouped = {};
set(this, 'toUpdate', ma); set(this, "_bindings", bindings.slice());
if (bindings && get(this, 'editing')) {
bindings.forEach((b) => {
if (grouped[get(b, 'subjectName')]) {
grouped[get(b, 'subjectName')].push(b);
} else {
grouped[get(b, 'subjectName')] = [b];
} }
});
if (grouped) {
Object.keys(grouped).forEach((g) => {
if (grouped[g].length >1) {
let idsOut = [];
grouped[g].forEach((m) => {
idsOut.push(get(m, 'roleTemplateId'))
});
let config = get(this, 'memberConfig');
set(config, 'subjectKind', 'User'); //should be upper but make sure
set(config, 'subjectName', g);
let record = get(this,'globalStore').createRecord(config);
let out = {
role: record,
toUpdate: false,
toAdd: false,
toDelete: false,
customRolesExisting: idsOut
}
existing.push(out);
} else {
existing.push({
role: grouped[g][0],
toUpdate: true,
toAdd: false,
toDelete: false,
id: Math.random()
})
}
});
}
}
set(this, 'memberArray', existing);
}, },
primaryResourceSaved: function() { primaryResourceSaved: function() {
// returns a promise of all the adds/removes/updates to the parent // returns a promise of all the adds/removes/updates to the parent
const pr = get(this, 'project'); const pr = get(this, 'primaryResource');
const resourceId = get(pr, 'id'); const resourceId = get(pr, 'id');
const add = (get(this, 'toAdd')||[]); const memberArray = get(this, 'memberArray').slice();
const update = get(this, 'toUpdate')||[];
const remove = get(this, 'toRemove')||[]; const customToAdd = memberArray.filterBy('customRolesToAdd');
const customToRemove = memberArray.filterBy('customRolesToRemove');
const add = get(this, 'memberArray').filter(r => get(r, 'toAdd') && !get(r, 'customRolesToAdd')).map(a => a.role);
const update = get(this, 'memberArray').filterBy('toUpdate', true).map(u => u.role);
const remove = get(this, 'memberArray').filterBy('toDelete', true).map(r => r.role);
customToRemove.forEach((rm) => {
// custom obj from custom
get(rm, 'customRolesToRemove').forEach((roleTempId) => {
let match = get(this, '_bindings').find((binding) => {
if (get(binding, 'roleTemplateId') === roleTempId) {
return binding;
}
});
remove.push(match);
});
});
customToAdd.forEach((m) => {
get(m, 'customRolesToAdd').forEach((a) => {
let role = get(m, 'role').clone();
set(role, 'roleTemplateId', a);
add.addObject(role);
})
});
add.forEach((x) => { add.forEach((x) => {
x.set(`${get(this, 'type')}Id`, resourceId); x.set(`${get(this, 'type')}Id`, resourceId);
@ -58,6 +119,7 @@ export default Component.extend({
return PromiseAll(add.map(x => x.save())).then(() => { return PromiseAll(add.map(x => x.save())).then(() => {
return PromiseAll(update.map(x => x.save())).then(() => { return PromiseAll(update.map(x => x.save())).then(() => {
return PromiseAll(remove.map(x => x.delete())).then(() => { return PromiseAll(remove.map(x => x.delete())).then(() => {
set(this, 'memberArray', []);
return pr; return pr;
}); });
}); });
@ -65,6 +127,11 @@ export default Component.extend({
}, },
actions: { actions: {
setCustomToAdd(member, customIds, customIdsRemove) {
set(member, 'customRolesToAdd', customIds);
set(member, 'customRolesToRemove', customIdsRemove);
},
cancel() { cancel() {
this.goBack(); this.goBack();
}, },
@ -74,18 +141,37 @@ export default Component.extend({
set(config, 'subjectKind', kind.capitalize()); //should be upper but make sure set(config, 'subjectKind', kind.capitalize()); //should be upper but make sure
// not setting the name correctly // not setting the name correctly
let record = get(this,'globalStore').createRecord(config); let record = get(this,'globalStore').createRecord(config);
get(this,'memberArray').pushObject(record); let out = {
get(this, 'toAdd').pushObject(record); role: record,
toAdd: true,
toUpate: false,
toDelete: false,
id: Math.random()
};
get(this,'memberArray').pushObject(out);
}, },
removeMember(obj) { removeMember(obj) {
let exists = get(this, 'memberArray').findBy('id', get(obj, 'id'));
if (get(obj, 'toAdd') && exists) {
// we just added and should remove it because the record has not been persisted yet
get(this,'memberArray').removeObject(obj); get(this,'memberArray').removeObject(obj);
get(this, 'toRemove').pushObject(obj); } else if (exists) {
setProperties(exists, {
toAdd: false,
toUpdate: false,
toDelete: true,
});
}
}, },
}, },
filteredUsers: computed('users.@each.{id,state}', function() { filteredUsers: computed('users.@each.{id,state}', function() {
return get(this, 'users').sortBy('displayName'); let users = get(this, 'users');
if (get(this, 'editing')) {
users = users.filter(u => !u.hasOwnProperty('me'));
}
return users.sortBy('displayName');
}), }),
}); });

View File

@ -10,9 +10,25 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{project-member-row member=member roles=roles users=users owner=creator type=type}} {{project-member-row
roles=roles
users=users
owner=creator
type=type
}}
{{#each memberArray as |member|}} {{#each memberArray as |member|}}
{{project-member-row member=member roles=roles users=filteredUsers remove="removeMember" }} {{#unless member.toDelete}}
{{project-member-row
member=member
editing=editing
resource=primaryResource
roles=roles
users=filteredUsers
pageType=primaryResource.type
remove="removeMember"
alertNewCustoms=(action "setCustomToAdd")
}}
{{/unless}}
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>

View File

@ -9,7 +9,7 @@
{{#accordion-list-item {{#accordion-list-item
title=(t 'formScopedRoles.title' type=cTyped) title=(t 'formScopedRoles.title' type=cTyped)
detail=(t 'formScopedRoles.description') detail=(t 'formScopedRoles.description' type=cTyped)
expandOnInit=true expandOnInit=true
showExpand=false showExpand=false
}} }}

View File

@ -0,0 +1,44 @@
import Component from '@ember/component';
import ModalBase from 'shared/mixins/modal-base';
import layout from './template';
import { get, set } from '@ember/object';
import { alias } from 'ember-computed';
export default Component.extend(ModalBase, {
layout,
classNames: ['small-modal'],
type: alias('modalOpts.type'),
init() {
this._super(...arguments);
let custom = get(this, 'modalOpts.roles').filterBy('hidden', false).filter((role) => {
return role.get('id') !== `${get(this, 'type')}-owner` && role.get('id') !== `${get(this, 'type')}-member`;
}).map((role) => {
let binding = null;
if ( get(this, 'modalOpts.current') ) {
binding = get(this, 'modalOpts.current').findBy('roleTemplateId', get(role, 'id'));
}
return {
role,
active: !!binding,
existing: binding,
}
});
set(this, 'custom', custom)
},
actions: {
save() {
get(this, 'modalOpts.done')(get(this, 'custom'));
this.get('modalService').toggleModal();
},
completed() {
this.get('modalService').toggleModal();
},
goBack() {
this.get('modalService').toggleModal();
},
},
});

View File

@ -0,0 +1,24 @@
<section class="header">
<h1>Custom Roles</h1>
</section>
<section>
{{#accordion-list-item
title=(t 'formScopedRoles.title' type=type)
detail=(t 'formScopedRoles.description' type=type)
expandOnInit=true
showExpand=false
}}
<div class="pl-20">
{{#each custom as |row|}}
<div class="box p-10 mt-5 mb-5">
<label>
{{input type="checkbox" checked=row.active}} {{row.role.displayName}}
<p class="help-block">{{row.role.detail}}</p>
</label>
</div>
{{/each}}
</div>
{{/accordion-list-item}}
</section>
{{save-cancel save="save" cancel="goBack" createLabel="generic.save"}}

View File

@ -33,9 +33,8 @@
{{form-members {{form-members
creator=creator creator=creator
editing=editing editing=editing
memberArray=memberArray
memberConfig=memberConfig memberConfig=memberConfig
project=primaryResource primaryResource=primaryResource
roles=model.roles roles=model.roles
users=model.users users=model.users
type="project" type="project"

View File

@ -1,7 +1,23 @@
import Component from '@ember/component'; import Component from '@ember/component';
import layout from './template'; import layout from './template';
import { computed, observer, get, set } from '@ember/object'; import { computed, get, observer, set, setProperties } from '@ember/object';
import { on } from '@ember/object/evented'; import { on } from '@ember/object/evented';
import { inject as service } from '@ember/service';
const BASIC_ROLES = [
{
label: 'Standard User',
value: 'member',
},
{
label: 'Admin',
value: 'owner',
},
{
label: 'Custom',
value: 'custom',
},
];
export default Component.extend({ export default Component.extend({
layout, layout,
@ -12,23 +28,77 @@ export default Component.extend({
roles: null, roles: null,
owner: null, owner: null,
type: null, type: null,
pageType: null,
modalService: service('modal'),
hasCustom: false,
customRoles: null,
actions: { actions: {
showEdit(member) {
if (get(member, 'role.roleTemplateId') === 'custom'){
this.openModal();
} else {
set(member, 'role.roleTemplateId', 'custom');
}
},
remove: function () { remove: function () {
this.sendAction('remove', get(this,'member')); this.sendAction('remove', get(this,'member'));
} }
}, },
init: function () { doneAdding(customs) {
this._super(...arguments); setProperties(this, {
set(this, 'choices', get(this,'roles').filterBy('hidden', false).map(role => { hasCustom: true,
return { customRoles: customs
label: role.name, });
value: role.id, let customIds = [];
}; let customIdsRemove = [];
})); customs.forEach((c) => {
if (get(c, 'active') && !get(c, 'existing')) {
customIds.push(get(c, 'role.id'));
}
if (get(c, 'existing') && !get(c, 'active')) {
customIdsRemove.push(get(c, 'role.id'));
}
});
this.alertNewCustoms(get(this, 'member'), customIds, customIdsRemove);
}, },
openModal() {
let current = null;
if (get(this, 'member.customRolesExisting')) {
current = get(this, `resource.${get(this, 'pageType')}RoleTemplateBindings`);
}
// append roletemplate ids from the modal to the custom field. split that field in the add promise of form-members?
get(this,'modalService').toggleModal('modal-add-custom-roles', {
model: get(this, 'member'),
roles: get(this, 'roles'),
done: this.doneAdding.bind(this),
current: current,
type: get(this, 'pageType')
});
},
openCustomModal: on('init', observer('member.role.roleTemplateId', function() {
if (get(this, 'member.role.roleTemplateId') === 'custom') {
this.openModal();
}
})),
choices: computed('roles.[]', 'pageType', function() {
let pt = get(this, 'pageType');
if (pt) {
return BASIC_ROLES.map((r) => {
return {
label: r.label,
value: r.value.indexOf('custom') >= 0 ? 'custom' : `${pt}-${r.value}`
};
});
}
return [];
}),
userList: computed('users.[]', function() { userList: computed('users.[]', function() {
return (get(this, 'users')||[]).map(( user ) =>{ return (get(this, 'users')||[]).map(( user ) =>{
return { return {
@ -38,11 +108,11 @@ export default Component.extend({
}); });
}), }),
kind: computed('member.subjectKind', function () { kind: computed('member.role.subjectKind', function () {
if (get(this, 'owner')) { if (get(this, 'owner')) {
return `projectsPage.new.form.members.${get(this,'owner.type').toLowerCase()}`; // TODO translations return `projectsPage.new.form.members.${get(this,'owner.type').toLowerCase()}`; // TODO translations
} else { } else {
return `projectsPage.new.form.members.${get(this,'member.subjectKind').toLowerCase()}` return `projectsPage.new.form.members.${get(this,'member.role.subjectKind').toLowerCase()}`
} }
}), }),

View File

@ -4,20 +4,26 @@
<td class="pr-20"> <td class="pr-20">
{{#if owner}} {{#if owner}}
{{owner.displayName}} {{owner.displayName}}
{{else if editing}}
{{member.role.user.displayName}}
{{else}} {{else}}
{{searchable-select content=userList value=member.subjectName disabled=true}} {{searchable-select content=userList value=member.role.subjectName}}
{{/if}} {{/if}}
</td> </td>
<td class="pr-20"> <td class="pr-20">
{{#if owner}} {{#if owner}}
{{if (eq type 'project') 'Project Owner' 'Cluster Owner'}} {{if (eq type 'project') 'Project Owner' 'Cluster Owner'}}
{{else if (or hasCustom member.customRolesExisting)}}
Multple Roles <span class="edit btn-sm hand" {{action "showEdit" member}}><i class="icon icon-edit"></i></span>
{{else}} {{else}}
{{searchable-select content=choices value=member.roleTemplateId}} {{searchable-select content=choices value=member.role.roleTemplateId}}
{{/if}} {{/if}}
</td> </td>
<td>&nbsp;</td> <td>&nbsp;</td>
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn bg-primary btn-sm" {{action "remove"}} disabled={{if owner 'true' null}}> {{#unless owner}}
<button class="btn bg-primary btn-sm" {{action "remove"}}>
<i class="icon icon-minus" /> <i class="icon icon-minus" />
</button> </button>
{{/unless}}
</div> </div>

View File

@ -0,0 +1 @@
export { default } from 'shared/components/modal-add-custom-roles/component';

View File

@ -2240,7 +2240,7 @@ formGlobalRoles:
formScopedRoles: formScopedRoles:
title: '{type} Permissions' title: '{type} Permissions'
description: TBD. description: 'Controls what access users have to the {type}.'
mode: mode:
admin: admin:
label: Owner label: Owner