Skip to content

Commit 04aab02

Browse files
committed
working ui-only user-management-app
* add share button to test modal * basic user list w/ permissions * add `avatar_ref` to the attributes returned in the users DTO * include `avatar_ref` in php test * add disabled option to permissions dropdown * refactor structure to be more angular * modal is now a component * main files moved inside feature folder * add x's to delete members & re-align * don't display X's on you or the owner * grey out based on permissions selected * adjust margins/padding * expand description for public listing
1 parent 167afdd commit 04aab02

21 files changed

+455
-63
lines changed

src/Api/Model/Shared/UserListProjectModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function __construct($projectId)
1919
parent::__construct(
2020
UserModelMongoMapper::instance(),
2121
array('isInvited' => array('$ne' => true), 'projects' => array('$in' => array(MongoMapper::mongoID($projectId)))),
22-
array('username', 'email', 'name') // TODO Stop exposing email this way
22+
array('username', 'email', 'name', 'avatar_ref') // TODO Stop exposing email this way
2323
);
2424
}
2525
}

src/Site/views/shared/image/globe.png

37.3 KB
Loading

src/angular-app/bellows/shared/model/project.model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export interface Project {
66
projectCode?: string;
77
projectName: string;
88
role: string;
9+
anonymousUserRole: string;
10+
reusableInviteLinkRole: string;
911
siteName: string;
1012
type?: string;
1113
isArchived: boolean;

src/angular-app/bellows/shared/model/user.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export class User {
22
active: boolean = false;
33
// noinspection TsLint
44
avatar_ref?: string;
5+
avatarUrl?: string;
56
dateCreated: string = '';
67
email: string = '';
78
id: string = '';

src/angular-app/languageforge/lexicon/lexicon-app.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55

66
<sil-notices></sil-notices>
77
<div data-ui-view></div>
8+
9+
<i class="fa fa-share-alt" data-ng-click="$ctrl.openShareWithOthersModal()"></i>
810
</div>

src/angular-app/languageforge/lexicon/lexicon-app.component.ts

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,26 +130,11 @@ export class LexiconAppController implements angular.IController {
130130

131131
openShareWithOthersModal(): void {
132132
const modalInstance = this.$modal.open({
133-
templateUrl: '/angular-app/languageforge/lexicon/shared/share-with-others.modal.html',
134-
controller: ['$scope', '$uibModalInstance',
135-
($scope: any, $modalInstance: angular.ui.bootstrap.IModalInstanceService) => {
136-
$scope.selected = {
137-
code: '',
138-
language: {}
139-
};
140-
$scope.add = () => {
141-
$modalInstance.close($scope.selected);
142-
};
143-
144-
$scope.close = $modalInstance.dismiss;
145-
}
146-
],
147-
windowTopClass: 'modal-select-language'
133+
component: 'shareWithOthersModal'
148134
});
149-
// modalInstance.result.then(selected => {
150-
// this.npsNewProject.languageCode = selected.code;
151-
// this.npsNewProject.language = selected.language;
152-
// }, () => {});
135+
modalInstance.result.then(data => {
136+
// TODO: save the data if not already
137+
}, () => {});
153138
}
154139

155140
private setupConfig(rights: Rights, editorConfig: LexiconConfig): void {

src/angular-app/languageforge/lexicon/lexicon-app.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import * as angular from 'angular';
22
import 'angular-sanitize';
33
import uiRouter from 'angular-ui-router';
4+
import './new-project/lexicon-new-project.module';
45

56
import {ApiService} from '../../bellows/core/api/api.service';
67
import {CoreModule} from '../../bellows/core/core.module';
78
import {LexiconCoreModule} from './core/lexicon-core.module';
89
import {LexiconEditorModule} from './editor/editor.module';
910
import {LexiconAppComponent} from './lexicon-app.component';
10-
import './new-project/lexicon-new-project.module';
1111
import {LexiconSettingsModule} from './settings/settings.module';
12+
import { ShareWithOthersModule } from './shared/share-with-others/share-with-others.module';
1213

1314
export const LexiconAppModule = angular
1415
.module('lexicon', [
@@ -18,7 +19,8 @@ export const LexiconAppModule = angular
1819
CoreModule,
1920
LexiconCoreModule,
2021
LexiconEditorModule,
21-
LexiconSettingsModule
22+
LexiconSettingsModule,
23+
ShareWithOthersModule
2224
])
2325
.component('lexiconApp', LexiconAppComponent)
2426
.config(['$stateProvider', '$urlRouterProvider',

src/angular-app/languageforge/lexicon/lexicon.scss

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,56 @@
1717
margin-bottom: 0;
1818
}
1919
}
20+
21+
#who-has-access {
22+
margin-top: 2rem;
23+
}
24+
25+
#invite-member-form {
26+
border-right: 1px solid white;
27+
28+
.permissions-spacer {
29+
width: 2.25rem;
30+
flex-shrink: 0;
31+
}
32+
}
33+
34+
#sharing-checkboxes {
35+
margin-top: 1rem;
36+
margin-bottom: 0;
37+
38+
input[type=checkbox] {
39+
margin-left: 0;
40+
}
41+
}
42+
43+
.user-summary-item {
44+
display: flex;
45+
flex-direction: row;
46+
align-items: center;
47+
48+
img.rounded-circle {
49+
max-width: 3em;
50+
}
51+
52+
input[type=text] {
53+
height: 1.5em;
54+
margin-left: 0.5em;
55+
padding: 0.25em;
56+
}
57+
58+
.permissions-indicator {
59+
width: 4em;
60+
text-align: center;
61+
}
62+
63+
.delete-member {
64+
width: 1rem;
65+
text-align:right;
66+
}
67+
68+
> *:not(:last-child) {
69+
margin-right: 0.5em;
70+
margin-bottom: 0;
71+
}
72+
}

src/angular-app/languageforge/lexicon/shared/share-with-others.modal.html

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
<b>Invite a new member</b>
2-
<div class="inline-form">
3-
<label>via: </label>
4-
<input type="email" class="form-control" placeholder="Email">
5-
<button type="button" class="btn btn-primary"><i class="fa fa-paper-plane"></i></button>
6-
<user-permissions-dropdown></user-permissions-dropdown>
7-
</div>
1+
<div id="invite-member-form">
2+
<b>Invite a new member</b>
3+
<div class="inline-form" style="margin-top: 8px;">
4+
<label>via: </label>
5+
<input type="email" class="form-control" placeholder="Email" ng-model="inviteUserEmail"
6+
ng-change="$ctrl.setEmailInviteUserAttr('email', this.value)">
7+
<button type="button" class="btn btn-primary"><i class="fa fa-paper-plane"></i></button>
8+
<permissions-dropdown permission-target="$ctrl.specialPermissionTargets.INVITE_USER_VIA_EMAIL" on-permission-changed="$ctrl.onPermissionChanged"></permissions-dropdown>
9+
<div class="permissions-spacer"></div>
10+
</div>
811

9-
<b>or</b>
10-
<div class="inline-form">
11-
<label>via: </label>
12-
<div class="input-group">
13-
<input type="text" class="form-control" value="{{$ctrl.inviteLink}}" ng-readonly="true" onfocus="this.select();">
12+
<b>or</b>
13+
<div class="inline-form">
14+
<label>via: </label>
15+
<div class="input-group">
16+
<input type="text" class="form-control" value="{{$ctrl.inviteLink}}" ng-readonly="true" onfocus="this.select();">
17+
</div>
18+
<permissions-dropdown permission-target="$ctrl.specialPermissionTargets.ANONYMOUS_USERS"></permissions-dropdown>
19+
<div class="permissions-spacer"></div>
1420
</div>
15-
<user-permissions-dropdown></user-permissions-dropdown>
1621
</div>

src/angular-app/languageforge/lexicon/shared/share-with-others/invite-member-form.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { NoticeService } from '../../../../bellows/core/notice/notice.service';
33

44
export class InviteMemberFormController implements angular.IController {
55
inviteLink: string;
6+
emailInviteUser: object;
7+
68
static $inject = ['$scope', 'silNoticeService'];
79
constructor(private $scope: angular.IScope) { }
810

@@ -18,6 +20,9 @@ export class InviteMemberFormController implements angular.IController {
1820

1921
export const InviteMemberFormComponent: angular.IComponentOptions = {
2022
bindings: {
23+
emailInviteUser: '<',
24+
setEmailInviteUserAttr: '&',
25+
onPermissionChanged: '<'
2126
},
2227
controller: InviteMemberFormController,
2328
templateUrl: '/angular-app/languageforge/lexicon/shared/share-with-others/invite-member-form.component.html'

src/angular-app/languageforge/lexicon/shared/share-with-others/permissions-dropdown.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<div class="btn-group" uib-dropdown is-open="status.isopen">
2-
<button id="single-button" type="button" class="btn btn-primary text-dark bg-light"
3-
uib-dropdown-toggle style="width: 4em;">
2+
<button id="single-button" type="button" class="btn btn-primary text-dark bg-light .permissions-indicator"
3+
uib-dropdown-toggle>
44
<i class="fa fa-{{ $ctrl.selectedPermission.icon }}"></i> <span class="caret"></span>
55
</button>
66
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
77
<li class="dropdown-item" role="menuitem"
88
ng-repeat="permission in $ctrl.permissions">
9-
<div ng-click="$ctrl.setSelectedPermission(permission)">
9+
<div ng-click="$ctrl.selectPermission(permission)">
1010
{{ permission.description }} <i class="fa fa-check" ng-show="$ctrl.selectedPermission == permission"></i>
1111
</div>
1212
</li>

src/angular-app/languageforge/lexicon/shared/share-with-others/permissions-dropdown.component.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
11
import * as angular from 'angular';
2-
import { CommentsOfflineCacheService } from '../../../../bellows/core/offline/comments-offline-cache.service';
2+
3+
/*
4+
* The options in the dropdown can be changed by using the `permissions=` attribute in HTML.
5+
* setting `allow-disable="true"` will add a disable option to the dropdown
6+
*/
7+
8+
export interface Permission {
9+
name: string;
10+
description: string;
11+
icon: string;
12+
}
313

414
export class PermissionsDropdownController implements angular.IController {
5-
permissions: any[];
6-
selectedPermission: object;
15+
permissionTarget: any;
16+
permissions: Permission[];
17+
selectedPermission: Permission;
18+
selected: string;
19+
allowDisable: boolean;
20+
onPermissionChanged: (params: {$event: {permission: Permission, target: any}}) => void;
721

822
static $inject = ['$scope'];
923
constructor(private $scope: angular.IScope) { }
1024

1125
$onInit(): void {
12-
this.permissions = [
13-
{name: 'edit', description: 'can edit', icon: 'pencil'},
14-
{name: 'comment', description: 'can comment', icon: 'comment'},
15-
{name: 'view', description: 'can view', icon: 'eye'}
26+
this.permissions = this.permissions || [
27+
{name: 'contributor', description: 'can edit', icon: 'pencil'},
28+
{name: 'observer_with_comment', description: 'can comment', icon: 'comment'},
29+
{name: 'observer', description: 'can view', icon: 'eye'}
1630
];
31+
if (this.allowDisable) {
32+
this.permissions.push({name: 'disabled', description: 'turn off', icon: 'ban'});
33+
}
1734

18-
this.setSelectedPermission(this.permissions[this.permissions.length-1]);
35+
this.selectedPermission = this.permissions.find(permission => permission.name === this.selected)
36+
|| this.permissions[this.permissions.length-1];
1937
}
2038

21-
setSelectedPermission(permission: object) {
22-
this.selectedPermission = permission;
39+
selectPermission(permission: Permission) {
40+
if (this.selectedPermission.name !== permission.name) {
41+
this.selectedPermission = permission;
42+
this.onPermissionChanged({$event: { permission, target: this.permissionTarget}});
43+
}
2344
}
2445

2546
}
2647

2748
export const PermissionsDropdownComponent: angular.IComponentOptions = {
2849
bindings: {
50+
allowDisable: '=',
51+
onPermissionChanged: '&',
52+
permissionTarget: '<',
53+
selected: '<'
2954
},
3055
controller: PermissionsDropdownController,
3156
templateUrl: '/angular-app/languageforge/lexicon/shared/share-with-others/permissions-dropdown.component.html'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as angular from 'angular';
2+
3+
export class ShareWithOthersModalInstanceController implements angular.IController {
4+
modalInstance: any;
5+
6+
constructor() {}
7+
8+
$onInit() {
9+
}
10+
}
11+
12+
export const ShareWithOthersComponent: angular.IComponentOptions = {
13+
bindings: {
14+
modalInstance: '<',
15+
close: '&',
16+
dismiss: '&'
17+
},
18+
controller: ShareWithOthersModalInstanceController,
19+
templateUrl: '/angular-app/languageforge/lexicon/shared/share-with-others/share-with-others.modal.html'
20+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div id="modal-share-with-others">
2+
<div class="modal-header">
3+
<h3 class="modal-title">Share with Others</h3>
4+
<i ng-click="$ctrl.dismiss()" class="close-modal fa fa-times"></i>
5+
</div>
6+
<div class="modal-body">
7+
<user-management-app></user-management-app>
8+
</div>
9+
<div class="modal-footer d-block clearfix">
10+
<button class="btn btn-primary float-right" id="apply-sharing-config-button" ng-click="$ctrl.close()">
11+
Done
12+
</button>
13+
</div>
14+
</div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as angular from 'angular';
2+
3+
import { InviteMemberFormComponent } from './invite-member-form.component';
4+
import { PermissionsDropdownComponent } from './permissions-dropdown.component';
5+
import { ShareWithOthersComponent } from './share-with-others.component';
6+
import { UserManagementAppComponent } from './user-management-app.component';
7+
import { UserPermissionListComponent } from './user-permission-list.component';
8+
9+
export const ShareWithOthersModule = angular
10+
.module('shareWithOthersModule', [
11+
'ui.bootstrap'
12+
])
13+
.component('shareWithOthersModal', ShareWithOthersComponent)
14+
.component('userManagementApp', UserManagementAppComponent)
15+
.component('permissionsDropdown', PermissionsDropdownComponent)
16+
.component('inviteMemberForm', InviteMemberFormComponent)
17+
.component('userPermissionList', UserPermissionListComponent)
18+
.name;

0 commit comments

Comments
 (0)