Finished the multiuser access functionality

This commit is contained in:
stefania.martziou 2021-07-14 12:16:40 +00:00
parent 103d07935c
commit d1bb87087d
14 changed files with 339 additions and 73 deletions

1
package-lock.json generated
View File

@ -4516,6 +4516,7 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"

View File

@ -72,7 +72,7 @@ export class AppComponent implements OnInit {
}
getReposOfUser(): void {
this.repositoryService.getRepositoriesOfUser()
this.repositoryService.getRepositoriesSnippetsOfUser()
.subscribe(
repos => { this.reposOfUser = repos; },
error => { console.log(error); },

View File

@ -716,3 +716,10 @@ export class CollectionMonitorSummary {
aggregationDetails: AggregationDetails[];
lastIndexedVersion: AggregationDetails;
}
export class User {
sub: string;
firstName: string;
lastName: string;
email: string;
}

View File

@ -41,7 +41,7 @@ export class JoinComponent implements OnInit {
}
getReposOfUser(): void {
this.repositoryService.getRepositoriesOfUser()
this.repositoryService.getRepositoriesSnippetsOfUser()
.subscribe(
repos => {
this.repositoriesOfUser = repos;

View File

@ -3,23 +3,24 @@ import { CommonModule } from '@angular/common';
import { TabsModule } from 'ngx-bootstrap';
import { ReusableComponentsModule } from '../../shared/reusablecomponents/reusable-components.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RepositoryComponent } from "./repository.component";
import { RepositoryRoutingModule } from "./repository-routing.module";
import { DashboardComponent } from "./dashboard/dashboard.component";
import {AuthenticationInterceptor} from "../../services/authentication-interceptor";
import {UsagestatsService} from "../../services/usagestats.service";
import {RepositoryService} from "../../services/repository.service";
import {AuthGuardService} from "../../services/auth-guard.service";
import {ValidatorService} from "../../services/validator.service";
import {AuthenticationService} from "../../services/authentication.service";
import {MonitorService} from "../../services/monitor.service";
import {PiwikService} from "../../services/piwik.service";
import {StatisticsService} from "../../services/statistics.service";
import {BrokerService} from "../../services/broker.service";
import {DashboardService} from "../../services/dashboard.service";
import { SharedService } from "../../services/shared.service";
import { SourcesUpdateRepoComponent } from "./update/sources-update-repo.component";
import {SourcesModule} from '../sources/sources.module';
import { RepositoryComponent } from './repository.component';
import { RepositoryRoutingModule } from './repository-routing.module';
import { DashboardComponent } from './dashboard/dashboard.component';
import {AuthenticationInterceptor} from '../../services/authentication-interceptor';
import {UsagestatsService} from '../../services/usagestats.service';
import {RepositoryService} from '../../services/repository.service';
import {AuthGuardService} from '../../services/auth-guard.service';
import {ValidatorService} from '../../services/validator.service';
import {AuthenticationService} from '../../services/authentication.service';
import {MonitorService} from '../../services/monitor.service';
import {PiwikService} from '../../services/piwik.service';
import {StatisticsService} from '../../services/statistics.service';
import {BrokerService} from '../../services/broker.service';
import {DashboardService} from '../../services/dashboard.service';
import { SharedService } from '../../services/shared.service';
import { SourcesUpdateRepoComponent } from './update/sources-update-repo.component';
import { SourcesModule } from '../sources/sources.module';
import { UpdateRepoAdminsComponent } from './update/update-repo-admins.component';
@NgModule ({
imports: [
@ -36,6 +37,7 @@ import {SourcesModule} from '../sources/sources.module';
RepositoryComponent,
DashboardComponent,
SourcesUpdateRepoComponent,
UpdateRepoAdminsComponent
// SourcesComponent,
// SourcesRegisterComponent,
// SourcesUpdateComponent,

View File

@ -84,51 +84,7 @@
</div>
</li>
<li>
<div class="uk-margin-small-top">
<div>This is a mock representation of the update admins functionality coming soon..</div>
<div class="uk-margin-top">
<div class="uk-flex uk-flex-right@m uk-flex-right@l">
<a class="uk-button uk-button-primary action uk-flex uk-flex-middle">
<!--<i class="md-icon material-icons">clear</i>-->
<span class="uk-margin-small-left">Invite admin</span>
</a>
</div>
</div>
<div class="uk-margin-medium-top">
<div class="uk-grid uk-grid-divider uk-flex uk-flex-middle" uk-grid="">
<div class="uk-width-3-4@l uk-width-1-2@m uk-first-column">
<div class="">
<span class="uk-text-muted">Email: </span>
<span class="uk-text-bold">admin1@gmail.com</span>
</div>
</div>
<div class="uk-width-expand">
<div class="uk-flex uk-flex-center">
<a class="uk-button uk-button-default action uk-flex uk-flex-middle">
<i class="md-icon material-icons">clear</i>
<span class="uk-margin-small-left">Remove</span>
</a>
</div>
</div>
</div>
<div class="uk-grid uk-grid-divider uk-flex uk-flex-middle" uk-grid="">
<div class="uk-width-3-4@l uk-width-1-2@m uk-first-column">
<div class="">
<span class="uk-text-muted">Email: </span>
<span class="uk-text-bold">admin2@gmail.com</span>
</div>
</div>
<div class="uk-width-expand">
<div class="uk-flex uk-flex-center">
<a class="uk-button uk-button-default action uk-flex uk-flex-middle">
<i class="md-icon material-icons">clear</i>
<span class="uk-margin-small-left">Remove</span>
</a>
</div>
</div>
</div>
</div>
</div>
<app-update-repo-admins [repo]="repo"></app-update-repo-admins>
</li>
<li class="el-item">
<div>

View File

@ -8,7 +8,7 @@ import { DatasourceUpdateFormComponent } from '../../../shared/reusablecomponent
import { ConfirmationDialogComponent } from '../../../shared/reusablecomponents/confirmation-dialog.component';
import { AuthenticationService } from '../../../services/authentication.service';
import { DatasourceNewInterfaceFormComponent } from '../../../shared/reusablecomponents/sources-forms/datasource-new-interface-form.component';
import {SharedService} from "../../../services/shared.service";
import { SharedService } from '../../../services/shared.service';
@Component ({
selector: 'sources-update-repo',

View File

@ -0,0 +1,129 @@
<div class="uk-margin-small-top">
<div>Update the users who can access the dashboard to manage the datasource.</div>
<div *ngIf="errorMessage" class="uk-alert uk-alert-danger">{{errorMessage}}</div>
<div *ngIf="loadingMessage" class="loading-medium">
<div class="loader-small" style="text-align: center; padding-top: 170px; color: rgb(47, 64, 80); font-weight: bold;">
{{ loadingMessage }}
</div>
<div class="whiteFilm"></div>
</div>
<div *ngIf="!loadingMessage" >
<div class="uk-margin-top">
<div class="uk-flex uk-flex-right@m uk-flex-right@l">
<a class="uk-button uk-button-primary action uk-flex uk-flex-middle" (click)="showAddRepoAdminModal()">
<!--<i class="md-icon material-icons">clear</i>-->
<span class="uk-margin-small-left">Invite admin</span>
</a>
</div>
</div>
<div class="uk-margin-medium-top">
<div *ngFor="let repoAdmin of repoAdmins" class="uk-grid uk-grid-divider uk-flex uk-flex-middle" uk-grid="">
<div class="uk-width-3-4@l uk-width-1-2@m uk-first-column">
<div class="">
<span class="uk-text-muted">Email: </span>
<span class="uk-text-bold">{{repoAdmin.email}}</span>
</div>
</div>
<div class="uk-width-expand">
<div class="uk-flex uk-flex-center">
<a class="uk-button uk-button-default action uk-flex uk-flex-middle" (click)="showDeletionModal(repoAdmin)">
<i class="md-icon material-icons">clear</i>
<span class="uk-margin-small-left">Remove</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<confirmation-dialog #deleteRepositoryAdminModal [title]="'Remove datasource admin'" [isModalShown]="isDeleteModalShown"
[confirmButNotCloseButton]="'Remove'" (emitObject)="deleteRepoAdmin($event)">
<div *ngIf="modalErrorMessage" class="uk-alert uk-alert-danger">{{modalErrorMessage}}</div>
<div *ngIf="modalLoadingMessage" class="loading-small">
<div class="loader-small" style="text-align: center; padding-top: 170px; color: rgb(47, 64, 80); font-weight: bold;">
{{ modalLoadingMessage }}
</div>
<div class="whiteFilm"></div>
</div>
<!--<div *ngIf="!modalLoadingMessage && !modalErrorMessage">-->
<!--<div *ngIf="selectedAdminForDelete">-->
<!--<h6>Are you sure you want to delete {{ selectedAdminForDelete.email }} from the list of repository admins?</h6>-->
<!--</div>-->
<!--</div>-->
<div *ngIf="selectedAdminForDelete">
<h6>Are you sure you want to remove this user admin?</h6>
<pre>
<ng-container *ngIf="selectedAdminForDelete.firstName || selectedAdminForDelete.lastName">
{{selectedAdminForDelete.firstName}} {{selectedAdminForDelete.lastName}}
</ng-container>
{{ selectedAdminForDelete.email }}
</pre>
</div>
</confirmation-dialog>
<confirmation-dialog #addRepositoryAdminModal [title]="'Add new datasource admin'" [isModalShown]="isAddModalShown"
[confirmButNotCloseButton]="'Submit'" (emitObject)="addRepositoryAdmin($event)">
<div *ngIf="modalErrorMessage" class="uk-alert uk-alert-danger">{{modalErrorMessage}}</div>
<div *ngIf="modalLoadingMessage" class="loading-small">
<div class="loader-small" style="text-align: center; padding-top: 170px; color: rgb(47, 64, 80); font-weight: bold;">
{{ modalLoadingMessage }}
</div>
<div class="whiteFilm"></div>
</div>
<h6>Add the email address of the new user admin. Make sure the user already has an OpenAIRE account with this email address.</h6>
<form>
<div class="uk-margin">
<input class="uk-input" placeholder="Enter email..." [formControl]="emailControl">
</div>
</form>
</confirmation-dialog>
<!--<div id="addAdminModal" uk-modal>-->
<!--<div class="uk-modal-dialog" style="top: 95.5px;">-->
<!--<div class="uk-modal-header">-->
<!--<h3 class="uk-modal-title">Adding a new repository admin for this repository</h3>-->
<!--</div>-->
<!--<div class="uk-modal-body">-->
<!--<p>Lorem ipsum.....</p>-->
<!--</div>-->
<!--<div class="uk-modal-footer uk-text-right">-->
<!--<button type="button" class="md-btn md-btn-flat uk-modal-close">Cancel</button>-->
<!--<button (click)="addRepositoryAdmin()" type="button" class="md-btn md-btn-flat md-btn-flat-primary">Add admin</button>-->
<!--</div>-->
<!--</div>-->
<!--&lt;!&ndash;<div *ngIf="!loadingMessage" class="uk-modal-dialog uk-modal-body">&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngIf="selectedAdminForDelete">&ndash;&gt;-->
<!--&lt;!&ndash;<h6>Are you sure you want to delete {{ selectedAdminForDelete.email }} from the list of repository admins?</h6>&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<p>Deleting a {{serviceORresource}} is an irreversible action.</p>&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<button class="uk-button uk-button-danger uk-modal-close" type="button" (click)="deleteRepoAdmin(selectedAdminForDelete.email)">Delete</button>&ndash;&gt;-->
<!--&lt;!&ndash;<button class="uk-button uk-button-primary uk-modal-close" type="button" (click)="closeDeletionModal()">Cancel</button>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;<div *ngIf="loadingMessage">&ndash;&gt;-->
<!--&lt;!&ndash;<div class="loader-small" style="text-align: center; padding-top: 170px; color: rgb(47, 64, 80); font-weight: bold;">&ndash;&gt;-->
<!--&lt;!&ndash;{{ loadingMessage }}&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash; <div class="whiteFilm"></div>&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--</div>-->

View File

@ -0,0 +1,147 @@
import {Component, Input, OnChanges, SimpleChanges, ViewChild} from '@angular/core';
import { Repository, User } from '../../../domain/typeScriptClasses';
import { RepositoryService } from '../../../services/repository.service';
import {loadingRepoMessage} from '../../../domain/shared-messages';
import {ConfirmationDialogComponent} from '../../../shared/reusablecomponents/confirmation-dialog.component';
import {FormControl} from '@angular/forms';
declare var UIkit: any;
@Component ({
selector: 'app-update-repo-admins',
templateUrl: 'update-repo-admins.component.html',
})
export class UpdateRepoAdminsComponent implements OnChanges {
@Input() repo: Repository;
loadingMessage: string;
errorMessage: string;
repoAdmins: User[];
selectedAdminForDelete: User;
isDeleteModalShown: boolean;
@ViewChild('deleteRepositoryAdminModal')
public deleteRepositoryAdminModal: ConfirmationDialogComponent;
isAddModalShown: boolean;
@ViewChild('addRepositoryAdminModal')
public addRepositoryAdminModal: ConfirmationDialogComponent;
modalErrorMessage: string;
modalLoadingMessage: string;
emailControl = new FormControl();
constructor (private repoService: RepositoryService) { }
ngOnChanges(changes: SimpleChanges): void {
this.getRepositoryAdmins();
}
getRepositoryAdmins() {
this.loadingMessage = 'Retrieving repository\'s admins...';
this.repoService.getRepositoryAdmins(this.repo.id).subscribe(
users => {
this.repoAdmins = users;
},
error => {
console.log(error);
this.loadingMessage = '';
this.errorMessage = 'Error retrieving the repository\'s admins';
},
() => {
this.loadingMessage = '';
}
);
}
showDeletionModal(repoAdmin: User) {
this.errorMessage = '';
this.loadingMessage = '';
this.selectedAdminForDelete = repoAdmin;
if (this.selectedAdminForDelete) {
this.deleteRepositoryAdminModal.showModal();
// UIkit.modal('#deletionModal').show();
}
}
closeDeletionModal() {
this.selectedAdminForDelete = null;
this.deleteRepositoryAdminModal.hideModal();
// UIkit.modal('#deletionModal').hide();
}
deleteRepoAdmin(event: any) {
console.log('deleting: ', this.selectedAdminForDelete.email);
// this.deleteRepositoryAdminModal.hideModal();
this.modalLoadingMessage = 'Deleting admin...';
this.repoService.deleteRepositoryAdmin(this.repo.id, this.selectedAdminForDelete.email).subscribe(
res => {
this.selectedAdminForDelete = null;
this.deleteRepositoryAdminModal.hideModal();
this.getRepositoryAdmins();
},
error => {
console.log('Error deleting repository\'s admins', error);
this.modalLoadingMessage = '';
this.modalErrorMessage = 'Error deleting admin';
},
() => {
this.modalLoadingMessage = '';
// this.deleteRepositoryAdminModal.hideModal();
}
);
}
showAddRepoAdminModal() {
this.emailControl.reset();
this.modalErrorMessage = '';
this.modalLoadingMessage = '';
this.addRepositoryAdminModal.showModal();
// UIkit.modal('#addAdminModal').show();
}
addRepositoryAdmin() {
this.modalLoadingMessage = 'Adding repository admin';
console.log('Adding repository admin..', this.emailControl.value);
this.repoService.addRepositoryAdmin(this.repo.id, this.emailControl.value).subscribe(
res => {
this.addRepositoryAdminModal.hideModal();
this.getRepositoryAdmins();
}, error => {
console.log('Error adding repository admin', error);
this.modalLoadingMessage = '';
if(error.status === 404) {
this.modalErrorMessage = 'This email address is not associated with an OpenAIRE user account.\n' +
'Please make sure the user has an OpenAIRE account and then try again.';
} else {
this.modalErrorMessage = 'Error deleting the user admin, please try again. If the error persists, ' +
'please contact helpdesk@openaire.eu';
}
},
() => {
this.modalLoadingMessage = '';
}
);
// UIkit.modal('#addAdminModal').hide();
}
}

View File

@ -12,7 +12,7 @@ import {
RepositoryInterface,
RepositorySnippet, RepositorySummaryInfo,
Timezone,
Typology
Typology, User
} from '../domain/typeScriptClasses';
import { Observable, of } from 'rxjs';
import { timezones } from '../domain/timezones';
@ -28,7 +28,7 @@ const headerOptions = {
@Injectable ()
export class RepositoryService {
private apiUrl = environment.API_ENDPOINT + '/repository/';
private apiUrl = environment.API_ENDPOINT + '/repositories/';
private dashboardAPIUrl = environment.API_ENDPOINT + '/dashboard/';
constructor(private httpClient: HttpClient) { }
@ -91,15 +91,15 @@ export class RepositoryService {
return this.httpClient.get<RepositorySnippet[]>(url, headerOptions);
}
getRepositoriesOfUser(): Observable<RepositorySnippet[]> {
const url = `${this.apiUrl}getRepositoriesOfUser/0/100`;
getRepositoriesSnippetsOfUser(): Observable<RepositorySnippet[]> {
const url = `${this.apiUrl}snippets/user`;
console.log(`knocking on: ${url}`);
return this.httpClient.get<RepositorySnippet[]>(url, headerOptions);
}
getRepositoryById(id: string): Observable<Repository> {
const url = `${this.apiUrl}getRepositoryById/${id}`;
const url = `${this.apiUrl}getRepositoryById/${id}`;
console.log(`knocking on: ${url}`);
return this.httpClient.get<Repository>(url, headerOptions);
}
@ -144,7 +144,7 @@ export class RepositoryService {
}
getCountries(): Observable<Country[]> {
const url = `${this.apiUrl}getCountries`;
const url = `${this.apiUrl}countries`;
console.log(`knocking on: ${url}`);
return this.httpClient.get<Country[]>(url, headerOptions);
}
@ -193,4 +193,22 @@ export class RepositoryService {
console.log(`knocking on: ${url}`);
return this.httpClient.get<RepositorySummaryInfo[]>(url, headerOptions);
}
getRepositoryAdmins(repoId: string): Observable<User[]> {
const url = `${this.apiUrl}${repoId}/admins`;
console.log(`knocking on: ${url}`);
return this.httpClient.get<User[]>(url, headerOptions);
}
deleteRepositoryAdmin(repoId: string, repoAdminEmail: string) {
const url = `${this.apiUrl}${repoId}/admins/${repoAdminEmail}`;
console.log(`knocking on: ${url}`);
return this.httpClient.delete(url, headerOptions);
}
addRepositoryAdmin(repoId: string, repoAdminEmail: string) {
const url = `${this.apiUrl}${repoId}/admins`;
return this.httpClient.post<string>(url, repoAdminEmail, headerOptions);
}
}

View File

@ -31,6 +31,7 @@
<div class="uk-modal-footer uk-text-right">
<button (click)="hideModal()" type="button" class="md-btn md-btn-flat uk-modal-close">{{ hideModalButton }}</button>
<button *ngIf="confirmActionButton" (click)="confirmedAction()" type="button" class="md-btn md-btn-flat md-btn-flat-primary">{{confirmActionButton}}</button>
<button *ngIf="confirmButNotCloseButton" (click)="confirmedButNotCloseAction()" type="button" class="md-btn md-btn-flat md-btn-flat-primary">{{confirmButNotCloseButton}}</button>
</div>
</div>
</div>

View File

@ -19,6 +19,7 @@ export class ConfirmationDialogComponent {
@Input() public title: string;
@Input() public confirmActionButton: string;
@Input() public confirmButNotCloseButton: string;
@Input() public hideModalButton: string = 'Cancel';
@ -50,4 +51,8 @@ export class ConfirmationDialogComponent {
// this.hideModal();
}
}
public confirmedButNotCloseAction() {
this.emitObject.emit(this._ids);
}
}

View File

@ -30,7 +30,7 @@ export class RepositoryTilesComponent implements OnInit {
getReposOfUser(): void {
this.loadingMessage = loadingReposMessage;
this.repoService.getRepositoriesOfUser()
this.repoService.getRepositoriesSnippetsOfUser()
.subscribe(
repos => this.reposOfUser = repos.sort( function(a, b) {
if (a.officialname < b.officialname) {

View File

@ -154,7 +154,7 @@ export class SideMenuComponent implements OnInit {
}
getReposOfUser(): void {
this.repositoryService.getRepositoriesOfUser()
this.repositoryService.getRepositoriesSnippetsOfUser()
.subscribe(
repos => {
this.reposOfUser = repos;