mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[CST-5677] Item authorization page
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTooltipModule, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { EditItemPageRoutingModule } from './edit-item-page.routing.module';
|
import { EditItemPageRoutingModule } from './edit-item-page.routing.module';
|
||||||
@@ -48,7 +48,8 @@ import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-
|
|||||||
EditItemPageRoutingModule,
|
EditItemPageRoutingModule,
|
||||||
SearchPageModule,
|
SearchPageModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
ResourcePoliciesModule
|
ResourcePoliciesModule,
|
||||||
|
NgbModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
EditItemPageComponent,
|
EditItemPageComponent,
|
||||||
|
@@ -1,13 +1,33 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<ds-alert [type]="'alert-info'" [content]="'item.edit.authorizations.heading'"></ds-alert>
|
<ds-alert [type]="'alert-info'" [content]="'item.edit.authorizations.heading'"></ds-alert>
|
||||||
<ds-resource-policies [resourceType]="'item'" [resourceUUID]="(getItemUUID() | async)"></ds-resource-policies>
|
<ds-resource-policies [resourceType]="'item'" [resourceName]="(getItemName() | async)"
|
||||||
<ng-container *ngFor="let bundle of (getItemBundles() | async); trackById">
|
[resourceUUID]="(getItemUUID() | async)">
|
||||||
<ds-resource-policies [resourceType]="'bundle'"
|
</ds-resource-policies>
|
||||||
[resourceUUID]="bundle.id"></ds-resource-policies>
|
<ng-container *ngFor="let bundle of (bundlesToShow$ | async); trackById">
|
||||||
<ng-container *ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id) | async)?.page; trackById">
|
<ds-resource-policies [resourceType]="'bundle'" [resourceUUID]="bundle.id" [resourceName]="bundle.name">
|
||||||
<ds-resource-policies [resourceType]="'bitstream'"
|
</ds-resource-policies>
|
||||||
[resourceUUID]="bitstream.id"></ds-resource-policies>
|
<ng-container *ngIf="(bundleBitstreamsMap.get(bundle.id)?.bitstreams | async)?.length > 0">
|
||||||
</ng-container>
|
<div class="card auth-bitstream-container">
|
||||||
</ng-container>
|
<div class="card-header">
|
||||||
|
<button type="button" class="btn btn-outline-primary" (click)="collapseArea(bundle.id)"
|
||||||
|
[attr.aria-expanded]="false" [attr.aria-controls]="bundle.id">
|
||||||
|
Show all Bitstreams' Policies for Bundle {{bundle.name}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body" [id]="bundle.id" [ngbCollapse]="bundleBitstreamsMap.get(bundle.id).isCollapsed">
|
||||||
|
<ng-container
|
||||||
|
*ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id).bitstreams | async); trackById">
|
||||||
|
<ds-resource-policies [resourceType]="'bitstream'" [resourceUUID]="bitstream.id"
|
||||||
|
[resourceName]="bitstream.name"></ds-resource-policies>
|
||||||
|
</ng-container>
|
||||||
|
<div class="row justify-content-center" *ngIf="!bundleBitstreamsMap.get(bundle.id).allBitstreamsLoaded">
|
||||||
|
<button type="button" class="btn btn-link" (click)="onBitstreamsLoad(bundle)">Load more</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<div class="row justify-content-center" *ngIf="!allBundlesLoaded">
|
||||||
|
<button type="button" class="btn btn-link" (click)="onBunbleLoad()">Load more bundles</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
.auth-bitstream-container {
|
||||||
|
margin-top: -1em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
@@ -1,3 +1,5 @@
|
|||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import { DSONameService } from './../../../core/breadcrumbs/dso-name.service';
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
@@ -25,7 +27,8 @@ interface BundleBitstreamsMapEntry {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-authorizations',
|
selector: 'ds-item-authorizations',
|
||||||
templateUrl: './item-authorizations.component.html'
|
templateUrl: './item-authorizations.component.html',
|
||||||
|
styleUrls:['./item-authorizations.component.scss']
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Component that handles the item Authorizations
|
* Component that handles the item Authorizations
|
||||||
@@ -36,14 +39,20 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
* A map that contains all bitstream of the item's bundles
|
* A map that contains all bitstream of the item's bundles
|
||||||
* @type {Observable<Map<string, Observable<PaginatedList<Bitstream>>>>}
|
* @type {Observable<Map<string, Observable<PaginatedList<Bitstream>>>>}
|
||||||
*/
|
*/
|
||||||
public bundleBitstreamsMap: Map<string, Observable<PaginatedList<Bitstream>>> = new Map<string, Observable<PaginatedList<Bitstream>>>();
|
public bundleBitstreamsMap: Map<string, BitstreamMapValue> = new Map<string, BitstreamMapValue>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of bundle for the item
|
* The list of all bundles for the item
|
||||||
* @type {Observable<PaginatedList<Bundle>>}
|
* @type {Observable<PaginatedList<Bundle>>}
|
||||||
*/
|
*/
|
||||||
private bundles$: BehaviorSubject<Bundle[]> = new BehaviorSubject<Bundle[]>([]);
|
private bundles$: BehaviorSubject<Bundle[]> = new BehaviorSubject<Bundle[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of bundles to be displayed in the template
|
||||||
|
* @type {BehaviorSubject<Bundle[]>}
|
||||||
|
*/
|
||||||
|
bundlesToShow$: BehaviorSubject<Bundle[]> = new BehaviorSubject<Bundle[]>([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The target editing item
|
* The target editing item
|
||||||
* @type {Observable<Item>}
|
* @type {Observable<Item>}
|
||||||
@@ -56,6 +65,37 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the bundles to be loaded on demand
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
bunblesPerPage = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of current page
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
bunblesPageSize = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag to show or not the 'Load more' button
|
||||||
|
* based on the condition if all the bundles are loaded or not
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
allBundlesLoaded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial size of loaded bitstreams
|
||||||
|
* The size of incrementation used in bitstream pagination
|
||||||
|
*/
|
||||||
|
bitstreamSize = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the loaded bitstremas at a certain moment
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private bitstreamPageSize = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
*
|
*
|
||||||
@@ -64,7 +104,8 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private linkService: LinkService,
|
private linkService: LinkService,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
|
private nameService: DSONameService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +113,49 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
* Initialize the component, setting up the bundle and bitstream within the item
|
* Initialize the component, setting up the bundle and bitstream within the item
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.getBundlesPerItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the item's UUID
|
||||||
|
*/
|
||||||
|
getItemUUID(): Observable<string> {
|
||||||
|
return this.item$.pipe(
|
||||||
|
map((item: Item) => item.id),
|
||||||
|
first((UUID: string) => isNotEmpty(UUID))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the item's name
|
||||||
|
*/
|
||||||
|
getItemName(): Observable<string> {
|
||||||
|
return this.item$.pipe(
|
||||||
|
map((item: Item) => this.nameService.getName(item))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all item's bundles
|
||||||
|
*
|
||||||
|
* @return an observable that emits all item's bundles
|
||||||
|
*/
|
||||||
|
getItemBundles(): Observable<Bundle[]> {
|
||||||
|
return this.bundles$.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all bundles per item
|
||||||
|
* and all the bitstreams per bundle
|
||||||
|
* @param page number of current page
|
||||||
|
*/
|
||||||
|
getBundlesPerItem(page: number = 1) {
|
||||||
this.item$ = this.route.data.pipe(
|
this.item$ = this.route.data.pipe(
|
||||||
map((data) => data.dso),
|
map((data) => data.dso),
|
||||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
||||||
map((item: Item) => this.linkService.resolveLink(
|
map((item: Item) => this.linkService.resolveLink(
|
||||||
item,
|
item,
|
||||||
followLink('bundles', {}, followLink('bitstreams'))
|
followLink('bundles', {findListOptions: {currentPage : page, elementsPerPage: this.bunblesPerPage}}, followLink('bitstreams'))
|
||||||
))
|
))
|
||||||
) as Observable<Item>;
|
) as Observable<Item>;
|
||||||
|
|
||||||
@@ -96,37 +174,40 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
take(1),
|
take(1),
|
||||||
map((list: PaginatedList<Bundle>) => list.page)
|
map((list: PaginatedList<Bundle>) => list.page)
|
||||||
).subscribe((bundles: Bundle[]) => {
|
).subscribe((bundles: Bundle[]) => {
|
||||||
|
if (isEqual(bundles.length,0) || bundles.length < this.bunblesPerPage) {
|
||||||
|
this.allBundlesLoaded = true;
|
||||||
|
}
|
||||||
|
if (isEqual(page, 1)) {
|
||||||
this.bundles$.next(bundles);
|
this.bundles$.next(bundles);
|
||||||
|
this.bundlesToShow$.next(bundles);
|
||||||
|
} else {
|
||||||
|
this.bundlesToShow$.next(this.bundles$.getValue().concat(bundles));
|
||||||
|
this.bundles$.next(this.bundles$.getValue().concat(bundles));
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
bundles$.pipe(
|
bundles$.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
mergeMap((list: PaginatedList<Bundle>) => list.page),
|
mergeMap((list: PaginatedList<Bundle>) => list.page),
|
||||||
map((bundle: Bundle) => ({ id: bundle.id, bitstreams: this.getBundleBitstreams(bundle) }))
|
map((bundle: Bundle) => ({ id: bundle.id, bitstreams: this.getBundleBitstreams(bundle) }))
|
||||||
).subscribe((entry: BundleBitstreamsMapEntry) => {
|
).subscribe((entry: BundleBitstreamsMapEntry) => {
|
||||||
this.bundleBitstreamsMap.set(entry.id, entry.bitstreams);
|
let bitstreamMapValues: BitstreamMapValue = {
|
||||||
|
isCollapsed: true,
|
||||||
|
allBitstreamsLoaded: false,
|
||||||
|
bitstreams: null
|
||||||
|
};
|
||||||
|
let bits = entry.bitstreams.pipe(
|
||||||
|
map((b: PaginatedList<Bitstream>) => {
|
||||||
|
bitstreamMapValues.allBitstreamsLoaded = b?.page.length < this.bitstreamSize ;
|
||||||
|
let firstLoaded = [...b.page.slice(0, this.bitstreamSize)];
|
||||||
|
return firstLoaded;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
bitstreamMapValues.bitstreams = bits;
|
||||||
|
this.bundleBitstreamsMap.set(entry.id, bitstreamMapValues);
|
||||||
/**
|
})
|
||||||
* Return the item's UUID
|
|
||||||
*/
|
|
||||||
getItemUUID(): Observable<string> {
|
|
||||||
return this.item$.pipe(
|
|
||||||
map((item: Item) => item.id),
|
|
||||||
first((UUID: string) => isNotEmpty(UUID))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all item's bundles
|
|
||||||
*
|
|
||||||
* @return an observable that emits all item's bundles
|
|
||||||
*/
|
|
||||||
getItemBundles(): Observable<Bundle[]> {
|
|
||||||
return this.bundles$.asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all bundle's bitstreams
|
* Return all bundle's bitstreams
|
||||||
*
|
*
|
||||||
@@ -142,6 +223,48 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the collapsible state of the area that contains the bitstream list
|
||||||
|
* @param bundleId Id of bundle responsible for the requested bitstreams
|
||||||
|
*/
|
||||||
|
collapseArea(bundleId: string) {
|
||||||
|
this.bundleBitstreamsMap.get(bundleId).isCollapsed = !this.bundleBitstreamsMap.get(bundleId).isCollapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads as much bundles as initial value of bundleSize to be displayed
|
||||||
|
*/
|
||||||
|
onBunbleLoad(){
|
||||||
|
this.bunblesPageSize ++;
|
||||||
|
this.getBundlesPerItem(this.bunblesPageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the bitstreams that are going to be loaded on demand,
|
||||||
|
* based on the number configured on this.bitstreamSize.
|
||||||
|
* @param bundle parent of bitstreams that are requested to be shown
|
||||||
|
* @returns Subscription
|
||||||
|
*/
|
||||||
|
onBitstreamsLoad(bundle: Bundle) {
|
||||||
|
return this.getBundleBitstreams(bundle).pipe(
|
||||||
|
map((res: PaginatedList<Bitstream>) => {
|
||||||
|
let nextBitstreams = res?.page.slice(this.bitstreamPageSize, this.bitstreamPageSize + this.bitstreamSize);
|
||||||
|
let bitstreamsToShow = this.bundleBitstreamsMap.get(bundle.id).bitstreams.pipe(
|
||||||
|
map((existingBits: Bitstream[])=> {
|
||||||
|
return [... existingBits, ...nextBitstreams];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.bitstreamPageSize = this.bitstreamPageSize + this.bitstreamSize;
|
||||||
|
let bitstreamMapValues: BitstreamMapValue = {
|
||||||
|
bitstreams: bitstreamsToShow ,
|
||||||
|
isCollapsed: this.bundleBitstreamsMap.get(bundle.id).isCollapsed,
|
||||||
|
allBitstreamsLoaded: res?.page.length <= this.bitstreamPageSize
|
||||||
|
};
|
||||||
|
this.bundleBitstreamsMap.set(bundle.id, bitstreamMapValues);
|
||||||
|
})
|
||||||
|
).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from all subscriptions
|
* Unsubscribe from all subscriptions
|
||||||
*/
|
*/
|
||||||
@@ -151,3 +274,9 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
|
|||||||
.forEach((subscription) => subscription.unsubscribe());
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BitstreamMapValue {
|
||||||
|
bitstreams: Observable<Bitstream[]>;
|
||||||
|
isCollapsed: boolean;
|
||||||
|
allBitstreamsLoaded: boolean;
|
||||||
|
}
|
||||||
|
@@ -4,9 +4,16 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th colspan="10">
|
<th colspan="10">
|
||||||
<div class="d-flex justify-content-between align-items-center m-0">
|
<div class="d-flex justify-content-between align-items-center m-0">
|
||||||
{{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} {{resourceUUID}}
|
<span>
|
||||||
|
{{ 'resource-policies.table.headers.title.for.' + resourceType | translate }}
|
||||||
|
<!-- {{(getResourcePolicies() | async)?.length}} -->
|
||||||
|
<span class="text-info"> {{resourceName}} </span>
|
||||||
|
<ng-container *ngIf="resourceType != 'item'">
|
||||||
|
({{resourceUUID}})
|
||||||
|
</ng-container>
|
||||||
|
</span>
|
||||||
<div class="space-children-mr">
|
<div class="space-children-mr">
|
||||||
<button class="btn btn-danger"
|
<button class="btn btn-danger p-1"
|
||||||
[disabled]="(!(canDelete() | async)) || (isProcessingDelete() | async)"
|
[disabled]="(!(canDelete() | async)) || (isProcessingDelete() | async)"
|
||||||
[title]="'resource-policies.delete.btn.title' | translate"
|
[title]="'resource-policies.delete.btn.title' | translate"
|
||||||
(click)="deleteSelectedResourcePolicies()">
|
(click)="deleteSelectedResourcePolicies()">
|
||||||
@@ -18,7 +25,7 @@
|
|||||||
{{'resource-policies.delete.btn' | translate}}
|
{{'resource-policies.delete.btn' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-success"
|
<button class="btn btn-success p-1"
|
||||||
[disabled]="(isProcessingDelete() | async)"
|
[disabled]="(isProcessingDelete() | async)"
|
||||||
[title]="'resource-policies.add.for.' + resourceType | translate"
|
[title]="'resource-policies.add.for.' + resourceType | translate"
|
||||||
(click)="redirectToResourcePolicyCreatePage()">
|
(click)="redirectToResourcePolicyCreatePage()">
|
||||||
|
@@ -62,6 +62,12 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() public resourceType: string;
|
@Input() public resourceType: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource name
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
@Input() public resourceName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A boolean representing if component is active
|
* A boolean representing if component is active
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
|
Reference in New Issue
Block a user