mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
55946: Intermediate commit
This commit is contained in:
@@ -124,6 +124,27 @@
|
||||
"curate": {
|
||||
"head": "Curate"
|
||||
}
|
||||
},
|
||||
"item-mapper": {
|
||||
"head": "Item Mapper - Map Item to Collections",
|
||||
"item": "Item: \"<b>{{name}}</b>\"",
|
||||
"description": "This is the item mapper tool that allows administrators to map this item to other collections. You can search for collections and map them, or browse the list of collections the item is currently mapped to.",
|
||||
"confirm": "Map item to selected collections",
|
||||
"tabs": {
|
||||
"browse": "Browse",
|
||||
"map": "Map"
|
||||
},
|
||||
"notifications": {
|
||||
"success": {
|
||||
"head": "Mapping completed",
|
||||
"content": "Successfully mapped item to {{amount}} collections."
|
||||
},
|
||||
"error": {
|
||||
"head": "Mapping errors",
|
||||
"content": "Errors occurred for mapping of item to {{amount}} collections."
|
||||
}
|
||||
},
|
||||
"return": "Return"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -5,11 +5,13 @@ import { EditItemPageRoutingModule } from './edit-item-page.routing.module';
|
||||
import { EditItemPageComponent } from './edit-item-page.component';
|
||||
import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component';
|
||||
import { ItemStatusComponent } from './item-status/item-status.component';
|
||||
import { SearchPageModule } from '../../+search-page/search-page.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SearchPageModule,
|
||||
EditItemPageRoutingModule
|
||||
],
|
||||
declarations: [
|
||||
|
@@ -15,7 +15,7 @@ import { ItemCollectionMapperComponent } from './item-collection-mapper/item-col
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'map',
|
||||
path: 'mapper',
|
||||
component: ItemCollectionMapperComponent,
|
||||
resolve: {
|
||||
item: ItemPageResolver
|
||||
|
@@ -1,7 +1,38 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p>It works!</p>
|
||||
<h2>{{'item.edit.item-mapper.head' | translate}}</h2>
|
||||
<p [innerHTML]="'item.edit.item-mapper.item' | translate:{ name: (itemRD$ | async)?.payload?.name }" id="item-name"></p>
|
||||
<p>{{'item.edit.item-mapper.description' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6">
|
||||
<ds-search-form id="search-form"
|
||||
[query]="(searchOptions$ | async)?.query"
|
||||
[scope]="(searchOptions$ | async)?.scope"
|
||||
[currentUrl]="getCurrentUrl()">
|
||||
</ds-search-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngb-tabset (tabChange)="tabChange($event)">
|
||||
<ngb-tab title="{{'item.edit.item-mapper.tabs.browse' | translate}}">
|
||||
<ng-template ngbTabContent>
|
||||
<div class="mt-2">
|
||||
<div *ngFor="let col of (itemCollectionsRD$ | async)?.payload">{{col.name}}</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
<ngb-tab title="{{'item.edit.item-mapper.tabs.map' | translate}}">
|
||||
<ng-template ngbTabContent>
|
||||
<div class="mt-2">
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
|
||||
<button [routerLink]="['/items/', (itemRD$ | async)?.payload?.id]" class="btn btn-outline-secondary">{{'item.edit.item-mapper.return' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,5 +1,20 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-collection-mapper',
|
||||
@@ -14,6 +29,81 @@ import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||
/**
|
||||
* Component for mapping collections to an item
|
||||
*/
|
||||
export class ItemCollectionMapperComponent {
|
||||
export class ItemCollectionMapperComponent implements OnInit {
|
||||
/**
|
||||
* The item to map to collections
|
||||
*/
|
||||
itemRD$: Observable<RemoteData<Item>>;
|
||||
|
||||
/**
|
||||
* Search options
|
||||
*/
|
||||
searchOptions$: Observable<PaginatedSearchOptions>;
|
||||
|
||||
/**
|
||||
* List of collections to show under the "Browse" tab
|
||||
* Collections that are mapped to the item
|
||||
*/
|
||||
itemCollectionsRD$: Observable<RemoteData<Collection[]>>;
|
||||
|
||||
/**
|
||||
* List of collections to show under the "Map" tab
|
||||
* Collections that are not mapped to the item
|
||||
*/
|
||||
mappingCollectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||
|
||||
/**
|
||||
* Sort on title ASC by default
|
||||
* @type {SortOptions}
|
||||
*/
|
||||
defaultSortOptions: SortOptions = new SortOptions('dc.title', SortDirection.ASC);
|
||||
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private searchConfigService: SearchConfigurationService,
|
||||
private searchService: SearchService,
|
||||
private collectionDataService: CollectionDataService,
|
||||
private itemDataService: ItemDataService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.itemRD$ = this.route.data.map((data) => data.item).pipe(getSucceededRemoteData()) as Observable<RemoteData<Item>>;
|
||||
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
||||
this.loadCollectionLists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load itemCollectionsRD$ with a fixed scope to only obtain the collections that own this item
|
||||
* Load mappingCollectionsRD$ to only obtain collections that don't own this item
|
||||
* TODO: When the API support it, fetch collections excluding the item's scope (currently fetches all collections)
|
||||
*/
|
||||
loadCollectionLists() {
|
||||
this.itemCollectionsRD$ = this.itemRD$.pipe(
|
||||
map((itemRD: RemoteData<Item>) => itemRD.payload),
|
||||
switchMap((item: Item) => this.itemDataService.getMappedCollections(item.id))
|
||||
);
|
||||
this.mappingCollectionsRD$ = this.collectionDataService.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear url parameters on tab change (temporary fix until pagination is improved)
|
||||
* @param event
|
||||
*/
|
||||
tabChange(event) {
|
||||
// TODO: Fix tabs to maintain their own pagination options (once the current pagination system is improved)
|
||||
// Temporary solution: Clear url params when changing tabs
|
||||
this.router.navigateByUrl(this.getCurrentUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current url without parameters
|
||||
* @returns {string}
|
||||
*/
|
||||
getCurrentUrl(): string {
|
||||
if (this.router.url.indexOf('?') > -1) {
|
||||
return this.router.url.substring(0, this.router.url.indexOf('?'));
|
||||
}
|
||||
return this.router.url;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ export class ItemStatusComponent implements OnInit {
|
||||
this.statusDataKeys = Object.keys(this.statusData);
|
||||
|
||||
this.actions = Object.assign({
|
||||
mappedCollections: this.getCurrentUrl() + '/map'
|
||||
mappedCollections: this.getCurrentUrl() + '/mapper'
|
||||
});
|
||||
this.actionsKeys = Object.keys(this.actions);
|
||||
}
|
||||
|
@@ -65,6 +65,7 @@ import { UploaderService } from '../shared/uploader/uploader.service';
|
||||
import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
|
||||
import { DSpaceObjectDataService } from './data/dspace-object-data.service';
|
||||
import { ItemSelectService } from '../shared/item-select/item-select.service';
|
||||
import { MappingCollectionsReponseParsingService } from './data/mapping-collections-reponse-parsing.service';
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
@@ -111,6 +112,7 @@ const PROVIDERS = [
|
||||
RegistryMetadataschemasResponseParsingService,
|
||||
RegistryMetadatafieldsResponseParsingService,
|
||||
RegistryBitstreamformatsResponseParsingService,
|
||||
MappingCollectionsReponseParsingService,
|
||||
MetadataschemaParsingService,
|
||||
DebugResponseParsingService,
|
||||
SearchResponseParsingService,
|
||||
|
@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { isEmpty, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { ensureArrayHasValue, isEmpty, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { NormalizedItem } from '../cache/models/normalized-item.model';
|
||||
@@ -15,11 +15,21 @@ import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
import { DataService } from './data.service';
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { FindAllOptions, PostRequest, RestRequest } from './request.models';
|
||||
import { FindAllOptions, GetRequest, MappingCollectionsRequest, PostRequest, RestRequest } from './request.models';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { configureRequest, getResponseFromSelflink } from '../shared/operators';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses,
|
||||
getRequestFromSelflink,
|
||||
getResponseFromSelflink
|
||||
} from '../shared/operators';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { RestResponse } from '../cache/response-cache.models';
|
||||
import { DSOSuccessResponse, GenericSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
import { Collection } from '../shared/collection.model';
|
||||
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
|
||||
@Injectable()
|
||||
export class ItemDataService extends DataService<NormalizedItem, Item> {
|
||||
@@ -71,4 +81,16 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
||||
);
|
||||
}
|
||||
|
||||
public getMappedCollections(itemId: string): Observable<RemoteData<Collection[]>> {
|
||||
const request$ = this.getMappingCollectionsEndpoint(itemId).pipe(
|
||||
isNotEmptyOperator(),
|
||||
distinctUntilChanged(),
|
||||
map((endpointURL: string) => new MappingCollectionsRequest(this.requestService.generateRequestId(), endpointURL)),
|
||||
configureRequest(this.requestService)
|
||||
);
|
||||
|
||||
// TODO: Create a remotedata object
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { ErrorResponse, GenericSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
|
||||
@Injectable()
|
||||
export class MappingCollectionsReponseParsingService implements ResponseParsingService {
|
||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||
const payload = data.payload;
|
||||
|
||||
if (payload._embedded && payload._embedded.mappingCollections) {
|
||||
const mappingCollections = payload._embedded.mappingCollections;
|
||||
return new GenericSuccessResponse(mappingCollections, data.statusCode);
|
||||
} else {
|
||||
return new ErrorResponse(
|
||||
Object.assign(
|
||||
new Error('Unexpected response from mappingCollections endpoint'),
|
||||
{ statusText: data.statusCode }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { IntegrationResponseParsingService } from '../integration/integration-response-parsing.service';
|
||||
import { BrowseItemsResponseParsingService } from './browse-items-response-parsing-service';
|
||||
import { MappingCollectionsReponseParsingService } from './mapping-collections-reponse-parsing.service';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
|
||||
@@ -191,6 +192,12 @@ export class BrowseItemsRequest extends GetRequest {
|
||||
}
|
||||
}
|
||||
|
||||
export class MappingCollectionsRequest extends GetRequest {
|
||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||
return MappingCollectionsReponseParsingService;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigRequest extends GetRequest {
|
||||
constructor(uuid: string, href: string) {
|
||||
super(uuid, href);
|
||||
|
Reference in New Issue
Block a user