mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Intermediate commit
This commit is contained in:
@@ -236,6 +236,7 @@
|
|||||||
},
|
},
|
||||||
"login": "Log In",
|
"login": "Log In",
|
||||||
"logout": "Log Out",
|
"logout": "Log Out",
|
||||||
|
"mydspace": "MyDSpace",
|
||||||
"language": "Language switch",
|
"language": "Language switch",
|
||||||
"search": "Search"
|
"search": "Search"
|
||||||
},
|
},
|
||||||
@@ -272,12 +273,62 @@
|
|||||||
"help": "Select a community to browse its collections."
|
"help": "Select a community to browse its collections."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mydspace": {
|
||||||
|
"title": "MyDSpace",
|
||||||
|
"description": "",
|
||||||
|
"new-submission-btn": "New submission",
|
||||||
|
"results": {
|
||||||
|
"head": "Your submissions",
|
||||||
|
"no-results": "There were no items to show",
|
||||||
|
"no-title": "No title",
|
||||||
|
"no-authors": "No Authors",
|
||||||
|
"no-date": "No Date",
|
||||||
|
"no-abstract": "No Abstract"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"title": "Messages",
|
||||||
|
"to": "To",
|
||||||
|
"hide-msg": "Hide message",
|
||||||
|
"show-msg": "Show message",
|
||||||
|
"no-messages": "No messages yet.",
|
||||||
|
"no-content": "No content.",
|
||||||
|
"send-btn": "Send",
|
||||||
|
"subject-placeholder": "Subject...",
|
||||||
|
"description-placeholder": "Insert your message here...",
|
||||||
|
"mark-as-read": "Mark as read",
|
||||||
|
"mark-as-unread": "Mark as unread",
|
||||||
|
"submitter-help": "Select this option to send a message to controller.",
|
||||||
|
"controller-help": "Select this option to send a message to item's submitter."
|
||||||
|
},
|
||||||
|
"show": {
|
||||||
|
"workspace": "Your Submissions",
|
||||||
|
"workflow": "All tasks"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"workflow": "Under review",
|
||||||
|
"rejected": "Rejected",
|
||||||
|
"validation": "Validate",
|
||||||
|
"waiting-for-controller": "Claim",
|
||||||
|
"in-progress": "To do",
|
||||||
|
"accepted": "Finalized"
|
||||||
|
},
|
||||||
|
"view-btn": "View",
|
||||||
|
"general": {
|
||||||
|
"text-here": "HERE"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"upload-successful": "New workspace item created. Click {{here}} for edit it.",
|
||||||
|
"upload-multiple-successful": "{{qty}} new workspace items created.",
|
||||||
|
"upload-failed": "Error creating new workspace. Please verify the content uploaded before retry."
|
||||||
|
}
|
||||||
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"title": "DSpace Angular :: Search",
|
"title": "DSpace Angular :: Search",
|
||||||
"description": "",
|
"description": "",
|
||||||
"form": {
|
"form": {
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"search_dspace": "Search DSpace"
|
"search_dspace": "Search DSpace",
|
||||||
|
"search_mydspace": "Search MyDSpace"
|
||||||
},
|
},
|
||||||
"results": {
|
"results": {
|
||||||
"head": "Search Results",
|
"head": "Search Results",
|
||||||
@@ -295,11 +346,18 @@
|
|||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
"sort-by": "Sort By",
|
"sort-by": "Sort By",
|
||||||
"rpp": "Results per page"
|
"rpp": "Results per page"
|
||||||
|
},
|
||||||
|
"tab":{
|
||||||
|
"title":"Show"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"switch-configuration": {
|
||||||
|
"title":"Show"
|
||||||
|
},
|
||||||
"view-switch": {
|
"view-switch": {
|
||||||
"show-list": "Show as list",
|
"show-list": "Show as list",
|
||||||
"show-grid": "Show as grid"
|
"show-grid": "Show as grid",
|
||||||
|
"show-detail": "Show detail"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"head": "Filters",
|
"head": "Filters",
|
||||||
@@ -309,7 +367,10 @@
|
|||||||
"f.dateIssued.min": "Start date",
|
"f.dateIssued.min": "Start date",
|
||||||
"f.dateIssued.max": "End date",
|
"f.dateIssued.max": "End date",
|
||||||
"f.subject": "Subject",
|
"f.subject": "Subject",
|
||||||
"f.has_content_in_original_bundle": "Has files"
|
"f.namedresourcetype": "Status",
|
||||||
|
"f.dateSubmitted": "Date submitted",
|
||||||
|
"f.itemtype": "Type",
|
||||||
|
"f.submitter": "Submitter"
|
||||||
},
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"show-more": "Show more",
|
"show-more": "Show more",
|
||||||
@@ -337,6 +398,26 @@
|
|||||||
},
|
},
|
||||||
"has_content_in_original_bundle": {
|
"has_content_in_original_bundle": {
|
||||||
"head": "Has files"
|
"head": "Has files"
|
||||||
|
},
|
||||||
|
"namedresourcetype": {
|
||||||
|
"placeholder": "Status",
|
||||||
|
"head": "Status"
|
||||||
|
},
|
||||||
|
"dateSubmitted": {
|
||||||
|
"placeholder": "Date submitted",
|
||||||
|
"head": "Date submitted"
|
||||||
|
},
|
||||||
|
"itemtype": {
|
||||||
|
"placeholder": "Type",
|
||||||
|
"head": "Type"
|
||||||
|
},
|
||||||
|
"submitter": {
|
||||||
|
"placeholder": "Submitter",
|
||||||
|
"head": "Submitter"
|
||||||
|
},
|
||||||
|
"objectpeople": {
|
||||||
|
"placeholder": "People",
|
||||||
|
"head": "People"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,6 +613,7 @@
|
|||||||
"item": "Loading item...",
|
"item": "Loading item...",
|
||||||
"objects": "Loading...",
|
"objects": "Loading...",
|
||||||
"search-results": "Loading search results...",
|
"search-results": "Loading search results...",
|
||||||
|
"mydspace-results": "Loading items...",
|
||||||
"browse-by": "Loading items..."
|
"browse-by": "Loading items..."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -692,6 +774,49 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"workflow": {
|
||||||
|
"generic": {
|
||||||
|
"delete": "Delete",
|
||||||
|
"delete-help": "If you would to discard this item, select \"Delete\". You will then be asked to confirm it.",
|
||||||
|
"edit": "Edit",
|
||||||
|
"edit-help": "Select this option to change the item's metadata.",
|
||||||
|
"view": "View",
|
||||||
|
"view-help": "Select this option to view the item's metadata."
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"generic": {
|
||||||
|
"processing": "Processing...",
|
||||||
|
"success": "Operation successful",
|
||||||
|
"error": "Error occurred during operation...",
|
||||||
|
"submitter": "Submitter"
|
||||||
|
},
|
||||||
|
"claimed": {
|
||||||
|
"approve": "Approve",
|
||||||
|
"approve_help": "If you have reviewed the item and it is suitable for inclusion in the collection, select \"Approve\".",
|
||||||
|
"edit": "Edit",
|
||||||
|
"edit_help": "Select this option to change the item's metadata.",
|
||||||
|
"reject": {
|
||||||
|
"submit": "Reject",
|
||||||
|
"reason": {
|
||||||
|
"submit": "Reject item",
|
||||||
|
"title": "Reason",
|
||||||
|
"info": "Please enter your reason for rejecting the submission into the box below, indicating whether the submitter may fix a problem and resubmit.",
|
||||||
|
"placeholder": "Describe the reason of reject"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reject_help": "If you have reviewed the item and found it is <strong>not</strong> suitable for inclusion in the collection, select \"Reject\". You will then be asked to enter a message indicating why the item is unsuitable, and whether the submitter should change something and resubmit.",
|
||||||
|
"return": "Return to pool",
|
||||||
|
"return_help": "Return the task to the pool so that another user may perform the task."
|
||||||
|
|
||||||
|
},
|
||||||
|
"pool": {
|
||||||
|
"claim": "Claim",
|
||||||
|
"claim_help": "Assign this task to yourself.",
|
||||||
|
"show-detail": "Show detail",
|
||||||
|
"hide-detail": "Hide detail"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uploader": {
|
"uploader": {
|
||||||
|
@@ -5,17 +5,6 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
|
|
||||||
import { ItemPageComponent } from './simple/item-page.component';
|
import { ItemPageComponent } from './simple/item-page.component';
|
||||||
import { ItemPageRoutingModule } from './item-page-routing.module';
|
import { ItemPageRoutingModule } from './item-page-routing.module';
|
||||||
import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
|
|
||||||
import { MetadataUriValuesComponent } from './field-components/metadata-uri-values/metadata-uri-values.component';
|
|
||||||
import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
|
||||||
import { ItemPageAuthorFieldComponent } from './simple/field-components/specific-field/author/item-page-author-field.component';
|
|
||||||
import { ItemPageDateFieldComponent } from './simple/field-components/specific-field/date/item-page-date-field.component';
|
|
||||||
import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component';
|
|
||||||
import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
|
|
||||||
import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
|
|
||||||
import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component';
|
|
||||||
import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
|
|
||||||
import { CollectionsComponent } from './field-components/collections/collections.component';
|
|
||||||
import { FullItemPageComponent } from './full/full-item-page.component';
|
import { FullItemPageComponent } from './full/full-item-page.component';
|
||||||
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
|
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
|
||||||
import { EditItemPageModule } from './edit-item-page/edit-item-page.module';
|
import { EditItemPageModule } from './edit-item-page/edit-item-page.module';
|
||||||
@@ -30,17 +19,6 @@ import { EditItemPageModule } from './edit-item-page/edit-item-page.module';
|
|||||||
declarations: [
|
declarations: [
|
||||||
ItemPageComponent,
|
ItemPageComponent,
|
||||||
FullItemPageComponent,
|
FullItemPageComponent,
|
||||||
MetadataValuesComponent,
|
|
||||||
MetadataUriValuesComponent,
|
|
||||||
MetadataFieldWrapperComponent,
|
|
||||||
ItemPageAuthorFieldComponent,
|
|
||||||
ItemPageDateFieldComponent,
|
|
||||||
ItemPageAbstractFieldComponent,
|
|
||||||
ItemPageUriFieldComponent,
|
|
||||||
ItemPageTitleFieldComponent,
|
|
||||||
ItemPageSpecificFieldComponent,
|
|
||||||
FileSectionComponent,
|
|
||||||
CollectionsComponent,
|
|
||||||
FullFileSectionComponent
|
FullFileSectionComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@@ -51,6 +51,6 @@ export class ItemPageComponent implements OnInit {
|
|||||||
this.thumbnail$ = this.itemRD$.pipe(
|
this.thumbnail$ = this.itemRD$.pipe(
|
||||||
map((rd: RemoteData<Item>) => rd.payload),
|
map((rd: RemoteData<Item>) => rd.payload),
|
||||||
filter((item: Item) => hasValue(item)),
|
filter((item: Item) => hasValue(item)),
|
||||||
mergeMap((item: Item) => item.getThumbnail()),);
|
mergeMap((item: Item) => item.getThumbnail()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,67 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
|
||||||
import { combineLatest, Observable } from 'rxjs';
|
import { combineLatest, Observable } from 'rxjs';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
|
import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
|
||||||
import { RoleService } from '../core/roles/role.service';
|
import { RoleService } from '../core/roles/role.service';
|
||||||
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
|
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
|
||||||
|
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||||
|
import { RouteService } from '../shared/services/route.service';
|
||||||
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all actions that have to do with the current search configuration
|
* Service that performs all actions that have to do with the current search configuration
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MyDSpaceConfigurationService {
|
export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||||
|
/**
|
||||||
|
* Default pagination settings
|
||||||
|
*/
|
||||||
|
protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
|
||||||
|
id: 'mydspace-page-configuration',
|
||||||
|
pageSize: 10,
|
||||||
|
currentPage: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default sort settings
|
||||||
|
*/
|
||||||
|
protected defaultSort = new SortOptions('dc.date.issued', SortDirection.DESC);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default configuration parameter setting
|
||||||
|
*/
|
||||||
|
protected defaultConfiguration = 'default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default scope setting
|
||||||
|
*/
|
||||||
|
protected defaultScope = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default query setting
|
||||||
|
*/
|
||||||
|
protected defaultQuery = '';
|
||||||
|
|
||||||
private isAdmin$: Observable<boolean>;
|
private isAdmin$: Observable<boolean>;
|
||||||
private isController$: Observable<boolean>;
|
private isController$: Observable<boolean>;
|
||||||
private isSubmitter$: Observable<boolean>;
|
private isSubmitter$: Observable<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* Initialize class
|
||||||
|
*
|
||||||
|
* @param {roleService} roleService
|
||||||
|
* @param {RouteService} routeService
|
||||||
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(protected roleService: RoleService) {
|
constructor(protected roleService: RoleService,
|
||||||
|
protected routeService: RouteService,
|
||||||
|
protected route: ActivatedRoute) {
|
||||||
|
|
||||||
|
super(routeService, route);
|
||||||
this.isSubmitter$ = this.roleService.isSubmitter();
|
this.isSubmitter$ = this.roleService.isSubmitter();
|
||||||
this.isController$ = this.roleService.isController();
|
this.isController$ = this.roleService.isController();
|
||||||
this.isAdmin$ = this.roleService.isAdmin();
|
this.isAdmin$ = this.roleService.isAdmin();
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core';
|
||||||
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, flatMap, map, switchMap, tap, } from 'rxjs/operators';
|
import { switchMap, } from 'rxjs/operators';
|
||||||
import { PaginatedList } from '../core/data/paginated-list';
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||||
@@ -18,9 +18,9 @@ import { SearchConfigurationOption } from '../+search-page/search-switch-configu
|
|||||||
import { RoleType } from '../core/roles/role-types';
|
import { RoleType } from '../core/roles/role-types';
|
||||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||||
import { SearchFilterConfig } from '../+search-page/search-service/search-filter-config.model';
|
|
||||||
|
|
||||||
export const MYDSPACE_ROUTE = '/mydspace';
|
export const MYDSPACE_ROUTE = '/mydspace';
|
||||||
|
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -33,7 +33,13 @@ export const MYDSPACE_ROUTE = '/mydspace';
|
|||||||
styleUrls: ['./my-dspace-page.component.scss'],
|
styleUrls: ['./my-dspace-page.component.scss'],
|
||||||
templateUrl: './my-dspace-page.component.html',
|
templateUrl: './my-dspace-page.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
animations: [pushInOut]
|
animations: [pushInOut],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: SEARCH_CONFIG_SERVICE,
|
||||||
|
useClass: MyDSpaceConfigurationService
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,8 +83,7 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
private sidebarService: SearchSidebarService,
|
private sidebarService: SearchSidebarService,
|
||||||
private windowService: HostWindowService,
|
private windowService: HostWindowService,
|
||||||
private filterService: SearchFilterService,
|
private filterService: SearchFilterService,
|
||||||
private configurationService: MyDSpaceConfigurationService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) {
|
||||||
private searchConfigService: SearchConfigurationService) {
|
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
this.service.setServiceOptions(MyDSpaceResponseParsingService, true);
|
this.service.setServiceOptions(MyDSpaceResponseParsingService, true);
|
||||||
}
|
}
|
||||||
@@ -91,7 +96,7 @@ export class MyDSpacePageComponent implements OnInit {
|
|||||||
* If something changes, update the list of scopes for the dropdown
|
* If something changes, update the list of scopes for the dropdown
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.configurationList$ = this.configurationService.getAvailableConfigurationOptions();
|
this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions();
|
||||||
|
|
||||||
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
||||||
this.sub = this.searchOptions$.pipe(
|
this.sub = this.searchOptions$.pipe(
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { autoserialize } from 'cerialize';
|
import { autoserialize, autoserializeAs } from 'cerialize';
|
||||||
import { MetadataMap } from '../core/shared/metadata.interfaces';
|
import { MetadataMap } from '../core/shared/metadata.interfaces';
|
||||||
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ export class NormalizedSearchResult implements ListableObject {
|
|||||||
/**
|
/**
|
||||||
* The UUID of the DSpaceObject that was found
|
* The UUID of the DSpaceObject that was found
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserializeAs(String, 'rObject')
|
||||||
dspaceObject: string;
|
dspaceObject: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,7 +12,7 @@ export class PaginatedSearchOptions extends SearchOptions {
|
|||||||
pagination?: PaginationComponentOptions;
|
pagination?: PaginationComponentOptions;
|
||||||
sort?: SortOptions;
|
sort?: SortOptions;
|
||||||
|
|
||||||
constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], pagination?: PaginationComponentOptions, sort?: SortOptions}) {
|
constructor(options: {configuration?: string, scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], pagination?: PaginationComponentOptions, sort?: SortOptions}) {
|
||||||
super(options);
|
super(options);
|
||||||
this.pagination = options.pagination;
|
this.pagination = options.pagination;
|
||||||
this.sort = options.sort;
|
this.sort = options.sort;
|
||||||
|
@@ -21,6 +21,7 @@ import { SearchService } from '../../../search-service/search.service';
|
|||||||
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
|
import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
|
||||||
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
||||||
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-facet-filter',
|
selector: 'ds-search-facet-filter',
|
||||||
@@ -74,9 +75,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(protected searchService: SearchService,
|
constructor(protected searchService: SearchService,
|
||||||
protected filterService: SearchFilterService,
|
protected filterService: SearchFilterService,
|
||||||
protected searchConfigService: SearchConfigurationService,
|
|
||||||
protected rdbs: RemoteDataBuildService,
|
protected rdbs: RemoteDataBuildService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) {
|
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
import { take } from 'rxjs/operators';
|
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
|
import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
|
||||||
import { SearchFilterService } from './search-filter.service';
|
import { SearchFilterService } from './search-filter.service';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { slide } from '../../../shared/animations/slide';
|
import { slide } from '../../../shared/animations/slide';
|
||||||
import { isNotEmpty } from '../../../shared/empty.util';
|
import { isNotEmpty } from '../../../shared/empty.util';
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@ import * as moment from 'moment';
|
|||||||
import { RouteService } from '../../../../shared/services/route.service';
|
import { RouteService } from '../../../../shared/services/route.service';
|
||||||
import { hasValue } from '../../../../shared/empty.util';
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -67,13 +68,13 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
|
|||||||
|
|
||||||
constructor(protected searchService: SearchService,
|
constructor(protected searchService: SearchService,
|
||||||
protected filterService: SearchFilterService,
|
protected filterService: SearchFilterService,
|
||||||
protected searchConfigService: SearchConfigurationService,
|
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected rdbs: RemoteDataBuildService,
|
protected rdbs: RemoteDataBuildService,
|
||||||
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||||
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
|
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
|
||||||
@Inject(PLATFORM_ID) private platformId: any,
|
@Inject(PLATFORM_ID) private platformId: any,
|
||||||
private route: RouteService) {
|
private route: RouteService) {
|
||||||
super(searchService, filterService, searchConfigService, rdbs, router, filterConfig);
|
super(searchService, filterService, rdbs, router, searchConfigService, filterConfig);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<h3>{{"search.filters.head" | translate}}</h3>
|
<h3>{{"search.filters.head" | translate}}</h3>
|
||||||
<div *ngIf="(filters | async)?.hasSucceeded">
|
<div *ngIf="!isLoadingFilters$.value">
|
||||||
<div *ngFor="let filter of (filters | async)?.payload">
|
<div *ngFor="let filter of filters">
|
||||||
<ds-search-filter *ngIf="isActive(filter) | async" class="d-block mb-3 p-3" [filter]="filter"></ds-search-filter>
|
<ds-search-filter class="d-block mb-3 p-3" [filter]="filter"></ds-search-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-primary" [routerLink]="[getSearchLink()]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button">{{"search.filters.reset" | translate}}</a>
|
<a class="btn btn-primary" [routerLink]="[getSearchLink()]" [queryParams]="clearParams | async" [queryParamsHandling]="merge" role="button">{{"search.filters.reset" | translate}}</a>
|
||||||
|
@@ -1,14 +1,17 @@
|
|||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||||
|
import { filter, first, map, mergeMap, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { filter, map, mergeMap, startWith, switchMap } from 'rxjs/operators';
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { SearchService } from '../search-service/search.service';
|
import { SearchService } from '../search-service/search.service';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { SearchFilterConfig } from '../search-service/search-filter-config.model';
|
import { SearchFilterConfig } from '../search-service/search-filter-config.model';
|
||||||
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { SearchFilterService } from './search-filter/search-filter.service';
|
import { SearchFilterService } from './search-filter/search-filter.service';
|
||||||
import { getSucceededRemoteData } from '../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../core/shared/operators';
|
||||||
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-filters',
|
selector: 'ds-search-filters',
|
||||||
@@ -19,11 +22,12 @@ import { getSucceededRemoteData } from '../../core/shared/operators';
|
|||||||
/**
|
/**
|
||||||
* This component represents the part of the search sidebar that contains filters.
|
* This component represents the part of the search sidebar that contains filters.
|
||||||
*/
|
*/
|
||||||
export class SearchFiltersComponent {
|
export class SearchFiltersComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An observable containing configuration about which filters are shown and how they are shown
|
* An Array containing configuration about which filters are shown and how they are shown
|
||||||
*/
|
*/
|
||||||
filters: Observable<RemoteData<SearchFilterConfig[]>>;
|
filters: SearchFilterConfig[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all filters that are currently active with their value set to null.
|
* List of all filters that are currently active with their value set to null.
|
||||||
@@ -31,15 +35,44 @@ export class SearchFiltersComponent {
|
|||||||
*/
|
*/
|
||||||
clearParams;
|
clearParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing load state of filters configuration
|
||||||
|
*/
|
||||||
|
isLoadingFilters$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current paginated search options
|
||||||
|
*/
|
||||||
|
searchOptions$: Observable<PaginatedSearchOptions>;
|
||||||
|
|
||||||
|
private sub: Subscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
|
* @param {ChangeDetectorRef} cdr
|
||||||
* @param {SearchService} searchService
|
* @param {SearchService} searchService
|
||||||
* @param {SearchConfigurationService} searchConfigService
|
* @param {SearchConfigurationService} searchConfigService
|
||||||
* @param {SearchFilterService} filterService
|
* @param {SearchFilterService} filterService
|
||||||
*/
|
*/
|
||||||
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) {
|
constructor(
|
||||||
this.filters = searchService.getConfig().pipe(getSucceededRemoteData());
|
private cdr: ChangeDetectorRef,
|
||||||
this.clearParams = searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
private searchService: SearchService,
|
||||||
|
@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService,
|
||||||
|
private filterService: SearchFilterService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.searchOptions$ = this.searchConfigService.searchOptions;
|
||||||
|
|
||||||
|
this.sub = this.searchOptions$.pipe(
|
||||||
|
tap(() => this.setLoading()),
|
||||||
|
switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData())))
|
||||||
|
.subscribe((filtersRD: RemoteData<SearchFilterConfig[]>) => {
|
||||||
|
this.filters = filtersRD.payload;
|
||||||
|
this.isLoadingFilters$.next(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
||||||
Object.keys(filters).forEach((f) => filters[f] = null);
|
Object.keys(filters).forEach((f) => filters[f] = null);
|
||||||
return filters;
|
return filters;
|
||||||
}));
|
}));
|
||||||
@@ -58,12 +91,13 @@ export class SearchFiltersComponent {
|
|||||||
* @returns {Observable<boolean>} Emits true whenever a given filter config should be shown
|
* @returns {Observable<boolean>} Emits true whenever a given filter config should be shown
|
||||||
*/
|
*/
|
||||||
isActive(filterConfig: SearchFilterConfig): Observable<boolean> {
|
isActive(filterConfig: SearchFilterConfig): Observable<boolean> {
|
||||||
|
console.log('isActive', filterConfig);
|
||||||
return this.filterService.getSelectedValuesForFilter(filterConfig).pipe(
|
return this.filterService.getSelectedValuesForFilter(filterConfig).pipe(
|
||||||
mergeMap((isActive) => {
|
mergeMap((isActive) => {
|
||||||
if (isNotEmpty(isActive)) {
|
if (isNotEmpty(isActive)) {
|
||||||
return observableOf(true);
|
return observableOf(true);
|
||||||
} else {
|
} else {
|
||||||
return this.searchConfigService.searchOptions.pipe(
|
return this.searchOptions$.pipe(
|
||||||
switchMap((options) => {
|
switchMap((options) => {
|
||||||
return this.searchService.getFacetValuesFor(filterConfig, 1, options).pipe(
|
return this.searchService.getFacetValuesFor(filterConfig, 1, options).pipe(
|
||||||
filter((RD) => !RD.isLoading),
|
filter((RD) => !RD.isLoading),
|
||||||
@@ -73,6 +107,20 @@ export class SearchFiltersComponent {
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}),startWith(true),);
|
}),
|
||||||
|
first(),
|
||||||
|
startWith(true),);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setLoading() {
|
||||||
|
this.isLoadingFilters$.next(true);
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (hasValue(this.sub)) {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { SearchService } from '../search-service/search.service';
|
import { SearchService } from '../search-service/search.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Params } from '@angular/router';
|
import { Params } from '@angular/router';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-labels',
|
selector: 'ds-search-labels',
|
||||||
@@ -24,7 +25,9 @@ export class SearchLabelsComponent {
|
|||||||
/**
|
/**
|
||||||
* Initialize the instance variable
|
* Initialize the instance variable
|
||||||
*/
|
*/
|
||||||
constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService) {
|
constructor(
|
||||||
|
private searchService: SearchService,
|
||||||
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
|
||||||
this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters();
|
this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,12 +8,14 @@ import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
|
|||||||
* This model class represents all parameters needed to request information about a certain search request
|
* This model class represents all parameters needed to request information about a certain search request
|
||||||
*/
|
*/
|
||||||
export class SearchOptions {
|
export class SearchOptions {
|
||||||
|
configuration?: string;
|
||||||
scope?: string;
|
scope?: string;
|
||||||
query?: string;
|
query?: string;
|
||||||
dsoType?: DSpaceObjectType;
|
dsoType?: DSpaceObjectType;
|
||||||
filters?: SearchFilter[];
|
filters?: SearchFilter[];
|
||||||
|
|
||||||
constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) {
|
constructor(options: {configuration?: string, scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) {
|
||||||
|
this.configuration = options.configuration;
|
||||||
this.scope = options.scope;
|
this.scope = options.scope;
|
||||||
this.query = options.query;
|
this.query = options.query;
|
||||||
this.dsoType = options.dsoType;
|
this.dsoType = options.dsoType;
|
||||||
@@ -28,6 +30,9 @@ export class SearchOptions {
|
|||||||
*/
|
*/
|
||||||
toRestUrl(url: string, args: string[] = []): string {
|
toRestUrl(url: string, args: string[] = []): string {
|
||||||
|
|
||||||
|
if (isNotEmpty(this.configuration)) {
|
||||||
|
args.push(`configuration=${this.configuration}`);
|
||||||
|
}
|
||||||
if (isNotEmpty(this.query)) {
|
if (isNotEmpty(this.query)) {
|
||||||
args.push(`query=${this.query}`);
|
args.push(`query=${this.query}`);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
|
||||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
import { switchMap, } from 'rxjs/operators';
|
import { switchMap, } from 'rxjs/operators';
|
||||||
import { PaginatedList } from '../core/data/paginated-list';
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
@@ -7,13 +7,13 @@ import { DSpaceObject } from '../core/shared/dspace-object.model';
|
|||||||
import { pushInOut } from '../shared/animations/push';
|
import { pushInOut } from '../shared/animations/push';
|
||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||||
import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
|
|
||||||
import { SearchResult } from './search-result.model';
|
import { SearchResult } from './search-result.model';
|
||||||
import { SearchService } from './search-service/search.service';
|
import { SearchService } from './search-service/search.service';
|
||||||
import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
|
import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -26,7 +26,13 @@ import { getSucceededRemoteData } from '../core/shared/operators';
|
|||||||
styleUrls: ['./search-page.component.scss'],
|
styleUrls: ['./search-page.component.scss'],
|
||||||
templateUrl: './search-page.component.html',
|
templateUrl: './search-page.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
animations: [pushInOut]
|
animations: [pushInOut],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: SEARCH_CONFIG_SERVICE,
|
||||||
|
useClass: SearchConfigurationService
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,8 +68,7 @@ export class SearchPageComponent implements OnInit {
|
|||||||
constructor(private service: SearchService,
|
constructor(private service: SearchService,
|
||||||
private sidebarService: SearchSidebarService,
|
private sidebarService: SearchSidebarService,
|
||||||
private windowService: HostWindowService,
|
private windowService: HostWindowService,
|
||||||
private filterService: SearchFilterService,
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
|
||||||
private searchConfigService: SearchConfigurationService) {
|
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,11 +28,37 @@ import { SearchFacetFilterWrapperComponent } from './search-filters/search-filte
|
|||||||
import { SearchBooleanFilterComponent } from './search-filters/search-filter/search-boolean-filter/search-boolean-filter.component';
|
import { SearchBooleanFilterComponent } from './search-filters/search-filter/search-boolean-filter/search-boolean-filter.component';
|
||||||
import { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component';
|
import { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component';
|
||||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||||
|
import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component';
|
||||||
|
|
||||||
const effects = [
|
const effects = [
|
||||||
SearchSidebarEffects
|
SearchSidebarEffects
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
SearchPageComponent,
|
||||||
|
SearchResultsComponent,
|
||||||
|
SearchSidebarComponent,
|
||||||
|
SearchSettingsComponent,
|
||||||
|
ItemSearchResultListElementComponent,
|
||||||
|
CollectionSearchResultListElementComponent,
|
||||||
|
CommunitySearchResultListElementComponent,
|
||||||
|
ItemSearchResultGridElementComponent,
|
||||||
|
CollectionSearchResultGridElementComponent,
|
||||||
|
CommunitySearchResultGridElementComponent,
|
||||||
|
CommunitySearchResultListElementComponent,
|
||||||
|
SearchFiltersComponent,
|
||||||
|
SearchFilterComponent,
|
||||||
|
SearchFacetFilterComponent,
|
||||||
|
SearchLabelsComponent,
|
||||||
|
SearchFacetFilterComponent,
|
||||||
|
SearchFacetFilterWrapperComponent,
|
||||||
|
SearchRangeFilterComponent,
|
||||||
|
SearchTextFilterComponent,
|
||||||
|
SearchHierarchyFilterComponent,
|
||||||
|
SearchBooleanFilterComponent,
|
||||||
|
SearchSwitchConfigurationComponent
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
SearchPageRoutingModule,
|
SearchPageRoutingModule,
|
||||||
@@ -41,29 +67,7 @@ const effects = [
|
|||||||
EffectsModule.forFeature(effects),
|
EffectsModule.forFeature(effects),
|
||||||
CoreModule.forRoot()
|
CoreModule.forRoot()
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: components,
|
||||||
SearchPageComponent,
|
|
||||||
SearchResultsComponent,
|
|
||||||
SearchSidebarComponent,
|
|
||||||
SearchSettingsComponent,
|
|
||||||
ItemSearchResultListElementComponent,
|
|
||||||
CollectionSearchResultListElementComponent,
|
|
||||||
CommunitySearchResultListElementComponent,
|
|
||||||
ItemSearchResultGridElementComponent,
|
|
||||||
CollectionSearchResultGridElementComponent,
|
|
||||||
CommunitySearchResultGridElementComponent,
|
|
||||||
CommunitySearchResultListElementComponent,
|
|
||||||
SearchFiltersComponent,
|
|
||||||
SearchFilterComponent,
|
|
||||||
SearchFacetFilterComponent,
|
|
||||||
SearchLabelsComponent,
|
|
||||||
SearchFacetFilterComponent,
|
|
||||||
SearchFacetFilterWrapperComponent,
|
|
||||||
SearchRangeFilterComponent,
|
|
||||||
SearchTextFilterComponent,
|
|
||||||
SearchHierarchyFilterComponent,
|
|
||||||
SearchBooleanFilterComponent,
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
SearchService,
|
SearchService,
|
||||||
SearchSidebarService,
|
SearchSidebarService,
|
||||||
@@ -82,7 +86,8 @@ const effects = [
|
|||||||
SearchTextFilterComponent,
|
SearchTextFilterComponent,
|
||||||
SearchHierarchyFilterComponent,
|
SearchHierarchyFilterComponent,
|
||||||
SearchBooleanFilterComponent,
|
SearchBooleanFilterComponent,
|
||||||
]
|
],
|
||||||
|
exports: components
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -6,7 +6,13 @@ import { autoserialize, autoserializeAs } from 'cerialize';
|
|||||||
*/
|
*/
|
||||||
export class FacetValue {
|
export class FacetValue {
|
||||||
/**
|
/**
|
||||||
* The display value of the facet value
|
* The display label of the facet value
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the facet value
|
||||||
*/
|
*/
|
||||||
@autoserializeAs(String, 'label')
|
@autoserializeAs(String, 'label')
|
||||||
value: string;
|
value: string;
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
import { Injectable, OnDestroy } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
@@ -7,12 +10,11 @@ import {
|
|||||||
Subscription
|
Subscription
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SearchOptions } from '../search-options.model';
|
import { SearchOptions } from '../search-options.model';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
|
||||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
import { Injectable, OnDestroy } from '@angular/core';
|
|
||||||
import { RouteService } from '../../shared/services/route.service';
|
import { RouteService } from '../../shared/services/route.service';
|
||||||
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
@@ -28,7 +30,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Default pagination settings
|
* Default pagination settings
|
||||||
*/
|
*/
|
||||||
private defaultPagination = Object.assign(new PaginationComponentOptions(), {
|
protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
|
||||||
id: 'search-page-configuration',
|
id: 'search-page-configuration',
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
currentPage: 1
|
currentPage: 1
|
||||||
@@ -37,17 +39,22 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Default sort settings
|
* Default sort settings
|
||||||
*/
|
*/
|
||||||
private defaultSort = new SortOptions('score', SortDirection.DESC);
|
protected defaultSort = new SortOptions('score', SortDirection.DESC);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default configuration parameter setting
|
||||||
|
*/
|
||||||
|
protected defaultConfiguration = 'default';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default scope setting
|
* Default scope setting
|
||||||
*/
|
*/
|
||||||
private defaultScope = '';
|
protected defaultScope = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default query setting
|
* Default query setting
|
||||||
*/
|
*/
|
||||||
private defaultQuery = '';
|
protected defaultQuery = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits the current default values
|
* Emits the current default values
|
||||||
@@ -74,8 +81,8 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
* @param {RouteService} routeService
|
* @param {RouteService} routeService
|
||||||
* @param {ActivatedRoute} route
|
* @param {ActivatedRoute} route
|
||||||
*/
|
*/
|
||||||
constructor(private routeService: RouteService,
|
constructor(protected routeService: RouteService,
|
||||||
private route: ActivatedRoute) {
|
protected route: ActivatedRoute) {
|
||||||
this.defaults
|
this.defaults
|
||||||
.pipe(getSucceededRemoteData())
|
.pipe(getSucceededRemoteData())
|
||||||
.subscribe((defRD) => {
|
.subscribe((defRD) => {
|
||||||
@@ -85,10 +92,20 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
|
|
||||||
this.subs.push(this.subscribeToSearchOptions(defs));
|
this.subs.push(this.subscribeToSearchOptions(defs));
|
||||||
this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
|
this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Observable<string>} Emits the current configuration string
|
||||||
|
*/
|
||||||
|
getCurrentConfiguration(defaultConfiguration: string) {
|
||||||
|
return this.routeService.getQueryParameterValue('configuration').pipe(map((configuration) => {
|
||||||
|
return configuration || defaultConfiguration;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Observable<string>} Emits the current scope's identifier
|
* @returns {Observable<string>} Emits the current scope's identifier
|
||||||
*/
|
*/
|
||||||
@@ -188,6 +205,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
subscribeToSearchOptions(defaults: SearchOptions): Subscription {
|
subscribeToSearchOptions(defaults: SearchOptions): Subscription {
|
||||||
return observableMerge(
|
return observableMerge(
|
||||||
|
this.getConfigurationPart(defaults.configuration),
|
||||||
this.getScopePart(defaults.scope),
|
this.getScopePart(defaults.scope),
|
||||||
this.getQueryPart(defaults.query),
|
this.getQueryPart(defaults.query),
|
||||||
this.getDSOTypePart(),
|
this.getDSOTypePart(),
|
||||||
@@ -208,6 +226,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
return observableMerge(
|
return observableMerge(
|
||||||
this.getPaginationPart(defaults.pagination),
|
this.getPaginationPart(defaults.pagination),
|
||||||
this.getSortPart(defaults.sort),
|
this.getSortPart(defaults.sort),
|
||||||
|
this.getConfigurationPart(defaults.configuration),
|
||||||
this.getScopePart(defaults.scope),
|
this.getScopePart(defaults.scope),
|
||||||
this.getQueryPart(defaults.query),
|
this.getQueryPart(defaults.query),
|
||||||
this.getDSOTypePart(),
|
this.getDSOTypePart(),
|
||||||
@@ -226,6 +245,7 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
if (hasNoValue(this._defaults)) {
|
if (hasNoValue(this._defaults)) {
|
||||||
const options = new PaginatedSearchOptions({
|
const options = new PaginatedSearchOptions({
|
||||||
pagination: this.defaultPagination,
|
pagination: this.defaultPagination,
|
||||||
|
configuration: this.defaultConfiguration,
|
||||||
sort: this.defaultSort,
|
sort: this.defaultSort,
|
||||||
scope: this.defaultScope,
|
scope: this.defaultScope,
|
||||||
query: this.defaultQuery
|
query: this.defaultQuery
|
||||||
@@ -242,6 +262,16 @@ export class SearchConfigurationService implements OnDestroy {
|
|||||||
this.subs.forEach((sub) => {
|
this.subs.forEach((sub) => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
});
|
});
|
||||||
|
this.subs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Observable<string>} Emits the current configuration settings as a partial SearchOptions object
|
||||||
|
*/
|
||||||
|
private getConfigurationPart(defaultConfiguration: string): Observable<any> {
|
||||||
|
return this.getCurrentConfiguration(defaultConfiguration).pipe(map((configuration) => {
|
||||||
|
return { configuration }
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -34,7 +34,7 @@ export class SearchQueryResponse {
|
|||||||
* The sort parameters used in the search request
|
* The sort parameters used in the search request
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
configurationName: string;
|
configuration: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sort parameters used in the search request
|
* The sort parameters used in the search request
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||||
|
import { isNull } from '../../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the mapping between a search result component and a DSpaceObject
|
* Contains the mapping between a search result component and a DSpaceObject
|
||||||
@@ -11,12 +12,19 @@ const searchResultMap = new Map();
|
|||||||
* @param {GenericConstructor<ListableObject>} domainConstructor The constructor of the DSpaceObject
|
* @param {GenericConstructor<ListableObject>} domainConstructor The constructor of the DSpaceObject
|
||||||
* @returns Decorator function that performs the actual mapping on initialization of the component
|
* @returns Decorator function that performs the actual mapping on initialization of the component
|
||||||
*/
|
*/
|
||||||
export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
|
export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
|
||||||
return function decorator(searchResult: any) {
|
return function decorator(searchResult: any) {
|
||||||
if (!searchResult) {
|
if (!searchResult) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
searchResultMap.set(domainConstructor, searchResult);
|
if (isNull(configuration)) {
|
||||||
|
searchResultMap.set(domainConstructor, searchResult);
|
||||||
|
} else {
|
||||||
|
if (!searchResultMap.get(configuration)) {
|
||||||
|
searchResultMap.set(configuration, new Map());
|
||||||
|
}
|
||||||
|
searchResultMap.get(configuration).set(domainConstructor, searchResult);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +33,10 @@ export function searchResultFor(domainConstructor: GenericConstructor<ListableOb
|
|||||||
* @param {GenericConstructor<ListableObject>} domainConstructor The DSpaceObject's constructor for which the search result component is requested
|
* @param {GenericConstructor<ListableObject>} domainConstructor The DSpaceObject's constructor for which the search result component is requested
|
||||||
* @returns The component's constructor that matches the given DSpaceObject
|
* @returns The component's constructor that matches the given DSpaceObject
|
||||||
*/
|
*/
|
||||||
export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
|
export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
|
||||||
return searchResultMap.get(domainConstructor);
|
if (isNull(configuration) || configuration === 'default') {
|
||||||
|
return searchResultMap.get(domainConstructor);
|
||||||
|
} else {
|
||||||
|
return searchResultMap.get(configuration).get(domainConstructor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
Router,
|
Router,
|
||||||
UrlSegmentGroup
|
UrlSegmentGroup
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { map, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, first, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||||
import {
|
import {
|
||||||
FacetConfigSuccessResponse,
|
FacetConfigSuccessResponse,
|
||||||
@@ -23,12 +23,12 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
|||||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||||
import {
|
import {
|
||||||
configureRequest,
|
configureRequest, filterSuccessfulResponses,
|
||||||
getResponseFromEntry,
|
getResponseFromEntry,
|
||||||
getSucceededRemoteData
|
getSucceededRemoteData
|
||||||
} from '../../core/shared/operators';
|
} from '../../core/shared/operators';
|
||||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
|
||||||
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
||||||
import { SearchOptions } from '../search-options.model';
|
import { SearchOptions } from '../search-options.model';
|
||||||
import { SearchResult } from '../search-result.model';
|
import { SearchResult } from '../search-result.model';
|
||||||
@@ -63,6 +63,16 @@ export class SearchService implements OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private facetLinkPathPrefix = 'discover/facets/';
|
private facetLinkPathPrefix = 'discover/facets/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, a new search request is always dispatched
|
||||||
|
*/
|
||||||
|
private forceBypassCache = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ResponseParsingService constructor name
|
||||||
|
*/
|
||||||
|
private parser: GenericConstructor<ResponseParsingService> = SearchResponseParsingService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription to unsubscribe from
|
* Subscription to unsubscribe from
|
||||||
*/
|
*/
|
||||||
@@ -78,6 +88,19 @@ export class SearchService implements OnDestroy {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to set service options
|
||||||
|
* @param {GenericConstructor<ResponseParsingService>} parser The configuration necessary to perform this search
|
||||||
|
* @param {boolean} forceBypassCache When true, a new search request is always dispatched
|
||||||
|
* @returns {Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>} Emits a paginated list with all search results found
|
||||||
|
*/
|
||||||
|
setServiceOptions(parser: GenericConstructor<ResponseParsingService>, forceBypassCache: boolean) {
|
||||||
|
if (parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
}
|
||||||
|
this.forceBypassCache = forceBypassCache;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to retrieve a paginated list of search results from the server
|
* Method to retrieve a paginated list of search results from the server
|
||||||
* @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search
|
* @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search
|
||||||
@@ -90,13 +113,15 @@ export class SearchService implements OnDestroy {
|
|||||||
url = (searchOptions as PaginatedSearchOptions).toRestUrl(url);
|
url = (searchOptions as PaginatedSearchOptions).toRestUrl(url);
|
||||||
}
|
}
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), url);
|
const request = new GetRequest(this.requestService.generateRequestId(), url);
|
||||||
|
const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
|
||||||
|
return this.parser;
|
||||||
|
};
|
||||||
|
|
||||||
return Object.assign(request, {
|
return Object.assign(request, {
|
||||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
getResponseParser: getResponseParserFn
|
||||||
return SearchResponseParsingService;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
configureRequest(this.requestService)
|
configureRequest(this.requestService, this.forceBypassCache),
|
||||||
);
|
);
|
||||||
const requestEntryObs = requestObs.pipe(
|
const requestEntryObs = requestObs.pipe(
|
||||||
switchMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
switchMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
@@ -111,8 +136,11 @@ export class SearchService implements OnDestroy {
|
|||||||
// turn dspace href from search results to effective list of DSpaceObjects
|
// turn dspace href from search results to effective list of DSpaceObjects
|
||||||
// Turn list of observable remote data DSO's into observable remote data object with list of DSO
|
// Turn list of observable remote data DSO's into observable remote data object with list of DSO
|
||||||
const dsoObs: Observable<RemoteData<DSpaceObject[]>> = sqrObs.pipe(
|
const dsoObs: Observable<RemoteData<DSpaceObject[]>> = sqrObs.pipe(
|
||||||
|
// filter((sqr: SearchQueryResponse) => isNotUndefined(sqr)),
|
||||||
map((sqr: SearchQueryResponse) => {
|
map((sqr: SearchQueryResponse) => {
|
||||||
return sqr.objects.map((nsr: NormalizedSearchResult) => {
|
return sqr.objects
|
||||||
|
.filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.dspaceObject))
|
||||||
|
.map((nsr: NormalizedSearchResult) => {
|
||||||
return this.rdb.buildSingle(nsr.dspaceObject);
|
return this.rdb.buildSingle(nsr.dspaceObject);
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
@@ -126,7 +154,7 @@ export class SearchService implements OnDestroy {
|
|||||||
let co = DSpaceObject;
|
let co = DSpaceObject;
|
||||||
if (dsos.payload[index]) {
|
if (dsos.payload[index]) {
|
||||||
const constructor: GenericConstructor<ListableObject> = dsos.payload[index].constructor as GenericConstructor<ListableObject>;
|
const constructor: GenericConstructor<ListableObject> = dsos.payload[index].constructor as GenericConstructor<ListableObject>;
|
||||||
co = getSearchResultFor(constructor);
|
co = getSearchResultFor(constructor, searchOptions.configuration);
|
||||||
return Object.assign(new co(), object, {
|
return Object.assign(new co(), object, {
|
||||||
dspaceObject: dsos.payload[index]
|
dspaceObject: dsos.payload[index]
|
||||||
});
|
});
|
||||||
@@ -134,6 +162,7 @@ export class SearchService implements OnDestroy {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// .filter((object) => isNotUndefined(object));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -156,7 +185,7 @@ export class SearchService implements OnDestroy {
|
|||||||
* @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded
|
* @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded
|
||||||
* @returns {Observable<RemoteData<SearchFilterConfig[]>>} The found filter configuration
|
* @returns {Observable<RemoteData<SearchFilterConfig[]>>} The found filter configuration
|
||||||
*/
|
*/
|
||||||
getConfig(scope?: string): Observable<RemoteData<SearchFilterConfig[]>> {
|
getConfig(scope?: string, configuration?: string): Observable<RemoteData<SearchFilterConfig[]>> {
|
||||||
const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe(
|
const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe(
|
||||||
map((url: string) => {
|
map((url: string) => {
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
@@ -165,6 +194,10 @@ export class SearchService implements OnDestroy {
|
|||||||
args.push(`scope=${scope}`);
|
args.push(`scope=${scope}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isNotEmpty(configuration)) {
|
||||||
|
args.push(`configuration=${configuration}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (isNotEmpty(args)) {
|
if (isNotEmpty(args)) {
|
||||||
url = new URLCombiner(url, `?${args.join('&')}`).toString();
|
url = new URLCombiner(url, `?${args.join('&')}`).toString();
|
||||||
}
|
}
|
||||||
@@ -176,7 +209,7 @@ export class SearchService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
configureRequest(this.requestService)
|
configureRequest(this.requestService, this.forceBypassCache)
|
||||||
);
|
);
|
||||||
|
|
||||||
const requestEntryObs = requestObs.pipe(
|
const requestEntryObs = requestObs.pipe(
|
||||||
@@ -202,6 +235,7 @@ export class SearchService implements OnDestroy {
|
|||||||
* @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values
|
* @returns {Observable<RemoteData<PaginatedList<FacetValue>>>} Emits the given page of facet values
|
||||||
*/
|
*/
|
||||||
getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable<RemoteData<PaginatedList<FacetValue>>> {
|
getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable<RemoteData<PaginatedList<FacetValue>>> {
|
||||||
|
console.log('getFacetValuesFor');
|
||||||
const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe(
|
const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe(
|
||||||
map((url: string) => {
|
map((url: string) => {
|
||||||
const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`];
|
const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`];
|
||||||
@@ -219,7 +253,8 @@ export class SearchService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
configureRequest(this.requestService)
|
configureRequest(this.requestService, this.forceBypassCache),
|
||||||
|
first()
|
||||||
);
|
);
|
||||||
|
|
||||||
const requestEntryObs = requestObs.pipe(
|
const requestEntryObs = requestObs.pipe(
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { SearchService } from '../search-service/search.service';
|
import { SearchService } from '../search-service/search.service';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
||||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||||
|
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-settings',
|
selector: 'ds-search-settings',
|
||||||
@@ -30,7 +31,7 @@ export class SearchSettingsComponent implements OnInit {
|
|||||||
constructor(private service: SearchService,
|
constructor(private service: SearchService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private searchConfigurationService: SearchConfigurationService) {
|
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigurationService: SearchConfigurationService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
<div id="search-sidebar-content">
|
<div id="search-sidebar-content">
|
||||||
<ds-view-mode-switch class="d-none d-md-block"></ds-view-mode-switch>
|
<ds-view-mode-switch class="d-none d-md-block"></ds-view-mode-switch>
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
|
<ds-search-switch-configuration *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration>
|
||||||
<ds-search-filters></ds-search-filters>
|
<ds-search-filters></ds-search-filters>
|
||||||
<ds-search-settings></ds-search-settings>
|
<ds-search-settings></ds-search-settings>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -8,6 +8,9 @@
|
|||||||
ds-view-mode-switch {
|
ds-view-mode-switch {
|
||||||
margin-bottom: $spacer;
|
margin-bottom: $spacer;
|
||||||
}
|
}
|
||||||
|
ds-search-switch-configuration {
|
||||||
|
margin-bottom: 2*$spacer !important;
|
||||||
|
}
|
||||||
.sidebar-content > *:not(:last-child) {
|
.sidebar-content > *:not(:last-child) {
|
||||||
margin-bottom: 4*$spacer;
|
margin-bottom: 4*$spacer;
|
||||||
display: block;
|
display: block;
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
|
||||||
|
import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
* The route parameter 'id' is used to request the item it represents.
|
* The route parameter 'id' is used to request the item it represents.
|
||||||
@@ -17,6 +19,11 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|||||||
*/
|
*/
|
||||||
export class SearchSidebarComponent {
|
export class SearchSidebarComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available configuration options
|
||||||
|
*/
|
||||||
|
@Input() configurationList: SearchConfigurationOption[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The total amount of results
|
* The total amount of results
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
export interface SearchConfigurationOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
<div *ngIf="configurationList?.length > 1" class="search-switch-configuration">
|
||||||
|
<h5>{{ 'search.switch-configuration.title' | translate}}</h5>
|
||||||
|
|
||||||
|
<select class="form-control"
|
||||||
|
[compareWith]="compare"
|
||||||
|
[(ngModel)]="selectedOption"
|
||||||
|
(change)="onSelect($event)">
|
||||||
|
<option *ngFor="let option of configurationList;" [ngValue]="option.value">
|
||||||
|
{{option.label | translate}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
@@ -0,0 +1,103 @@
|
|||||||
|
// import { SearchService } from '../../search-service/search.service';
|
||||||
|
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
// import { SearchSettingsComponent } from '../../search-settings/search-settings.component';
|
||||||
|
// import { Observable } from 'rxjs/Observable';
|
||||||
|
// import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
|
// import { SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||||
|
// import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
// import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
// import { ActivatedRoute } from '@angular/router';
|
||||||
|
// import { SearchSidebarService } from '../../search-sidebar/search-sidebar.service';
|
||||||
|
// import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
// import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe';
|
||||||
|
// import { By } from '@angular/platform-browser';
|
||||||
|
//
|
||||||
|
// describe('SearchSettingsComponent', () => {
|
||||||
|
//
|
||||||
|
// let comp: SearchSettingsComponent;
|
||||||
|
// let fixture: ComponentFixture<SearchSettingsComponent>;
|
||||||
|
// let searchServiceObject: SearchService;
|
||||||
|
//
|
||||||
|
// const pagination: PaginationComponentOptions = new PaginationComponentOptions();
|
||||||
|
// pagination.id = 'search-results-pagination';
|
||||||
|
// pagination.currentPage = 1;
|
||||||
|
// pagination.pageSize = 10;
|
||||||
|
// const sort: SortOptions = new SortOptions();
|
||||||
|
// const mockResults = [ 'test', 'data' ];
|
||||||
|
// const searchServiceStub = {
|
||||||
|
// searchOptions: { pagination: pagination, sort: sort },
|
||||||
|
// search: () => mockResults
|
||||||
|
// };
|
||||||
|
// const queryParam = 'test query';
|
||||||
|
// const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
||||||
|
// const activatedRouteStub = {
|
||||||
|
// queryParams: Observable.of({
|
||||||
|
// query: queryParam,
|
||||||
|
// scope: scopeParam
|
||||||
|
// })
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const sidebarService = {
|
||||||
|
// isCollapsed: Observable.of(true),
|
||||||
|
// collapse: () => this.isCollapsed = Observable.of(true),
|
||||||
|
// expand: () => this.isCollapsed = Observable.of(false)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// beforeEach(async(() => {
|
||||||
|
// TestBed.configureTestingModule({
|
||||||
|
// imports: [ TranslateModule.forRoot(), RouterTestingModule.withRoutes([]) ],
|
||||||
|
// declarations: [ SearchSettingsComponent, EnumKeysPipe ],
|
||||||
|
// providers: [
|
||||||
|
// { provide: SearchService, useValue: searchServiceStub },
|
||||||
|
//
|
||||||
|
// { provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||||
|
// {
|
||||||
|
// provide: SearchSidebarService,
|
||||||
|
// useValue: sidebarService
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
// }).compileComponents();
|
||||||
|
// }));
|
||||||
|
//
|
||||||
|
// beforeEach(() => {
|
||||||
|
// fixture = TestBed.createComponent(SearchSettingsComponent);
|
||||||
|
// comp = fixture.componentInstance;
|
||||||
|
//
|
||||||
|
// // SearchPageComponent test instance
|
||||||
|
// fixture.detectChanges();
|
||||||
|
// searchServiceObject = (comp as any).service;
|
||||||
|
// spyOn(comp, 'reloadRPP');
|
||||||
|
// spyOn(comp, 'reloadOrder');
|
||||||
|
// spyOn(searchServiceObject, 'search').and.callThrough();
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it('it should show the order settings with the respective selectable options', () => {
|
||||||
|
// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings'));
|
||||||
|
// expect(orderSetting).toBeDefined();
|
||||||
|
// const childElements = orderSetting.query(By.css('.form-control')).children;
|
||||||
|
// expect(childElements.length).toEqual(2);
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it('it should show the size settings with the respective selectable options', () => {
|
||||||
|
// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings'));
|
||||||
|
// expect(pageSizeSetting).toBeDefined();
|
||||||
|
// const childElements = pageSizeSetting.query(By.css('.form-control')).children;
|
||||||
|
// expect(childElements.length).toEqual(7);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it('should have the proper order value selected by default', () => {
|
||||||
|
// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings'));
|
||||||
|
// const childElementToBeSelected = orderSetting.query(By.css('.form-control option[value="0"][selected="selected"]'))
|
||||||
|
// expect(childElementToBeSelected).toBeDefined();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// it('should have the proper rpp value selected by default', () => {
|
||||||
|
// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings'));
|
||||||
|
// const childElementToBeSelected = pageSizeSetting.query(By.css('.form-control option[value="10"][selected="selected"]'))
|
||||||
|
// expect(childElementToBeSelected).toBeDefined();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// });
|
@@ -0,0 +1,54 @@
|
|||||||
|
import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { NavigationExtras, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||||
|
import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
|
||||||
|
import { SearchConfigurationOption } from './search-configuration-option.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-search-switch-configuration',
|
||||||
|
styleUrls: ['./search-switch-configuration.component.scss'],
|
||||||
|
templateUrl: './search-switch-configuration.component.html',
|
||||||
|
})
|
||||||
|
export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available configuration options
|
||||||
|
*/
|
||||||
|
@Input() configurationList: SearchConfigurationOption[] = [];
|
||||||
|
|
||||||
|
public selectedOption: string;
|
||||||
|
|
||||||
|
private sub: Subscription;
|
||||||
|
|
||||||
|
constructor(private router: Router,
|
||||||
|
@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.searchConfigService.getCurrentConfiguration('default')
|
||||||
|
.subscribe((currentConfiguration) => this.selectedOption = currentConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(event: Event) {
|
||||||
|
const navigationExtras: NavigationExtras = {
|
||||||
|
queryParams: {configuration: this.selectedOption},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.router.navigate([MYDSPACE_ROUTE], navigationExtras);
|
||||||
|
}
|
||||||
|
|
||||||
|
compare(item1: MyDSpaceConfigurationValueType, item2: MyDSpaceConfigurationValueType) {
|
||||||
|
return item1 === item2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (hasValue(this.sub)) {
|
||||||
|
this.sub.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,7 @@ export function getItemModulePath() {
|
|||||||
{ path: 'communities', loadChildren: './+community-page/community-page.module#CommunityPageModule' },
|
{ path: 'communities', loadChildren: './+community-page/community-page.module#CommunityPageModule' },
|
||||||
{ path: 'collections', loadChildren: './+collection-page/collection-page.module#CollectionPageModule' },
|
{ path: 'collections', loadChildren: './+collection-page/collection-page.module#CollectionPageModule' },
|
||||||
{ path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule' },
|
{ path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule' },
|
||||||
|
{ path: 'mydspace', loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule', canActivate: [AuthenticatedGuard] },
|
||||||
{ path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' },
|
{ path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' },
|
||||||
{ path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' },
|
{ path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' },
|
||||||
{ path: 'admin', loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] },
|
{ path: 'admin', loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] },
|
||||||
|
@@ -2,7 +2,8 @@ import { Injectable } from '@angular/core';
|
|||||||
|
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs';
|
import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs';
|
||||||
import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
import { filter, find, first, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||||
|
import { remove } from 'lodash';
|
||||||
|
|
||||||
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
@@ -123,7 +124,10 @@ export class RequestService {
|
|||||||
// TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
|
// TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
|
||||||
configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
|
configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
|
||||||
const isGetRequest = request.method === RestRequestMethod.GET;
|
const isGetRequest = request.method === RestRequestMethod.GET;
|
||||||
if (!isGetRequest || !this.isCachedOrPending(request) || (forceBypassCache && !this.isPending(request))) {
|
if (forceBypassCache) {
|
||||||
|
this.clearRequestsOnTheirWayToTheStore(request);
|
||||||
|
}
|
||||||
|
if (!isGetRequest || (forceBypassCache && !this.isPending(request)) || !this.isCachedOrPending(request)) {
|
||||||
this.dispatchRequest(request);
|
this.dispatchRequest(request);
|
||||||
if (isGetRequest) {
|
if (isGetRequest) {
|
||||||
this.trackRequestsOnTheirWayToTheStore(request);
|
this.trackRequestsOnTheirWayToTheStore(request);
|
||||||
@@ -248,6 +252,19 @@ export class RequestService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will store the href of every GET request that gets configured in a local variable, and
|
||||||
|
* remove it as soon as it can be found in the store.
|
||||||
|
*/
|
||||||
|
private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
|
||||||
|
this.store.pipe(select(this.entryFromUUIDSelector(request.uuid)),
|
||||||
|
find((re: RequestEntry) => hasValue(re)))
|
||||||
|
.subscribe((re: RequestEntry) => {
|
||||||
|
if (!re.responsePending) {
|
||||||
|
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Dispatch commit action to send all changes (for a certain method) to the server (buffer)
|
* Dispatch commit action to send all changes (for a certain method) to the server (buffer)
|
||||||
* @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed
|
* @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed
|
||||||
|
@@ -37,9 +37,5 @@ export class UserMenuComponent implements OnInit {
|
|||||||
// set user
|
// set user
|
||||||
this.user$ = this.store.pipe(select(getAuthenticatedUser));
|
this.user$ = this.store.pipe(select(getAuthenticatedUser));
|
||||||
|
|
||||||
this.user$.subscribe((user) => {
|
|
||||||
console.log(user, user.name);
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,11 +12,19 @@ export class PaginationComponentOptions extends NgbPaginationConfig {
|
|||||||
*/
|
*/
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of pages to display.
|
||||||
|
*/
|
||||||
|
maxSize = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A number array that represents options for a context pagination limit.
|
* A number array that represents options for a context pagination limit.
|
||||||
*/
|
*/
|
||||||
pageSizeOptions: number[] = [5, 10, 20, 40, 60, 80, 100];
|
pageSizeOptions: number[] = [5, 10, 20, 40, 60, 80, 100];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of items per page.
|
||||||
|
*/
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
114
src/app/shared/roles/role.directive.ts
Normal file
114
src/app/shared/roles/role.directive.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Directive,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
SimpleChanges,
|
||||||
|
TemplateRef,
|
||||||
|
ViewContainerRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
|
import { filter, first, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
import { RoleService } from '../../core/roles/role.service';
|
||||||
|
import { RoleType } from '../../core/roles/role-types';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[dsShowOnlyForRole],[dsShowExceptForRole]'
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Structural Directive for showing or hiding a template based on current user role
|
||||||
|
*/
|
||||||
|
export class RoleDirective implements OnChanges, OnDestroy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The role or list of roles that can show template
|
||||||
|
*/
|
||||||
|
@Input() dsShowOnlyForRole: RoleType | RoleType[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The role or list of roles that cannot show template
|
||||||
|
*/
|
||||||
|
@Input() dsShowExceptForRole: RoleType | RoleType[];
|
||||||
|
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private roleService: RoleService,
|
||||||
|
private viewContainer: ViewContainerRef,
|
||||||
|
private changeDetector: ChangeDetectorRef,
|
||||||
|
private templateRef: TemplateRef<any>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const onlyChanges = changes.dsShowOnlyForRole;
|
||||||
|
const exceptChanges = changes.dsShowExceptForRole;
|
||||||
|
this.hasRoles(this.dsShowOnlyForRole);
|
||||||
|
if (changes.dsShowOnlyForRole) {
|
||||||
|
this.validateOnly()
|
||||||
|
} else if (changes.dsShowExceptForRole) {
|
||||||
|
this.validateExcept()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs
|
||||||
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show template in view container
|
||||||
|
*/
|
||||||
|
private showTemplateBlockInView(): void {
|
||||||
|
this.viewContainer.clear();
|
||||||
|
if (!this.templateRef) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.viewContainer.createEmbeddedView(this.templateRef);
|
||||||
|
this.changeDetector.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the list of roles that can show template
|
||||||
|
*/
|
||||||
|
private validateOnly(): void {
|
||||||
|
this.subs.push(this.hasRoles(this.dsShowOnlyForRole).pipe(filter((hasRole) => hasRole))
|
||||||
|
.subscribe((hasRole) => {
|
||||||
|
this.showTemplateBlockInView();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the list of roles that cannot show template
|
||||||
|
*/
|
||||||
|
private validateExcept(): void {
|
||||||
|
this.subs.push(this.hasRoles(this.dsShowExceptForRole).pipe(filter((hasRole) => !hasRole))
|
||||||
|
.subscribe((hasRole) => {
|
||||||
|
this.showTemplateBlockInView();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current user role is included in the specified role list
|
||||||
|
*
|
||||||
|
* @param roles
|
||||||
|
* The role or the list of roles
|
||||||
|
* @returns {Observable<boolean>}
|
||||||
|
* observable of true if current user role is included in the specified role list, observable of false otherwise
|
||||||
|
*/
|
||||||
|
private hasRoles(roles: RoleType | RoleType[]): Observable<boolean> {
|
||||||
|
const toValidate: RoleType[] = (Array.isArray(roles)) ? roles : [roles];
|
||||||
|
const checks: Array<Observable<boolean>> = toValidate.map((role) => this.roleService.checkRole(role));
|
||||||
|
|
||||||
|
return combineLatest(checks).pipe(
|
||||||
|
map((permissions: boolean[]) => permissions.includes(true)),
|
||||||
|
first()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { hasValue, isNotEmpty } from '../empty.util';
|
import { hasValue, isNotEmpty } from '../empty.util';
|
||||||
import { QueryParamsHandling } from '@angular/router/src/config';
|
import { QueryParamsHandling } from '@angular/router/src/config';
|
||||||
|
import { MYDSPACE_ROUTE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a simple item page.
|
* This component renders a simple item page.
|
||||||
@@ -64,7 +65,7 @@ export class SearchFormComponent {
|
|||||||
updateSearch(data: any) {
|
updateSearch(data: any) {
|
||||||
const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search';
|
const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search';
|
||||||
let handling: QueryParamsHandling = '' ;
|
let handling: QueryParamsHandling = '' ;
|
||||||
if (this.currentUrl === '/search') {
|
if (this.currentUrl === '/search' || this.currentUrl === MYDSPACE_ROUTE) {
|
||||||
handling = 'merge';
|
handling = 'merge';
|
||||||
}
|
}
|
||||||
this.router.navigate([newUrl], {
|
this.router.navigate([newUrl], {
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationEnd, Params, Router, } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Params, Router, RouterStateSnapshot, } from '@angular/router';
|
||||||
|
|
||||||
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
import { AddUrlToHistoryAction } from '../history/history.actions';
|
import { AddUrlToHistoryAction } from '../history/history.actions';
|
||||||
@@ -16,35 +17,35 @@ export class RouteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getQueryParameterValues(paramName: string): Observable<string[]> {
|
getQueryParameterValues(paramName: string): Observable<string[]> {
|
||||||
return this.route.queryParamMap.pipe(
|
return this.getQueryParamMap().pipe(
|
||||||
map((params) => [...params.getAll(paramName)]),
|
map((params) => [...params.getAll(paramName)]),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getQueryParameterValue(paramName: string): Observable<string> {
|
getQueryParameterValue(paramName: string): Observable<string> {
|
||||||
return this.route.queryParamMap.pipe(
|
return this.getQueryParamMap().pipe(
|
||||||
map((params) => params.get(paramName)),
|
map((params) => params.get(paramName)),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasQueryParam(paramName: string): Observable<boolean> {
|
hasQueryParam(paramName: string): Observable<boolean> {
|
||||||
return this.route.queryParamMap.pipe(
|
return this.getQueryParamMap().pipe(
|
||||||
map((params) => params.has(paramName)),
|
map((params) => params.has(paramName)),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasQueryParamWithValue(paramName: string, paramValue: string): Observable<boolean> {
|
hasQueryParamWithValue(paramName: string, paramValue: string): Observable<boolean> {
|
||||||
return this.route.queryParamMap.pipe(
|
return this.getQueryParamMap().pipe(
|
||||||
map((params) => params.getAll(paramName).indexOf(paramValue) > -1),
|
map((params) => params.getAll(paramName).indexOf(paramValue) > -1),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getQueryParamsWithPrefix(prefix: string): Observable<Params> {
|
getQueryParamsWithPrefix(prefix: string): Observable<Params> {
|
||||||
return this.route.queryParamMap.pipe(
|
return this.getQueryParamMap().pipe(
|
||||||
map((qparams) => {
|
map((qparams) => {
|
||||||
const params = {};
|
const params = {};
|
||||||
qparams.keys
|
qparams.keys
|
||||||
@@ -57,6 +58,19 @@ export class RouteService {
|
|||||||
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)));
|
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getQueryParamMap(): Observable<any> {
|
||||||
|
return this.route.queryParamMap.pipe(
|
||||||
|
map((paramMap) => {
|
||||||
|
const snapshot: RouterStateSnapshot = this.router.routerState.snapshot;
|
||||||
|
// Due to an Angular bug, sometimes change of QueryParam is not detected so double checks with route snapshot
|
||||||
|
if (!isEqual(paramMap, snapshot.root.queryParamMap)) {
|
||||||
|
return snapshot.root.queryParamMap;
|
||||||
|
} else {
|
||||||
|
return paramMap;
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
public saveRouting(): void {
|
public saveRouting(): void {
|
||||||
this.router.events
|
this.router.events
|
||||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||||
|
@@ -77,6 +77,22 @@ import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/
|
|||||||
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
||||||
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
||||||
import { AlertsComponent } from './alerts/alerts.component';
|
import { AlertsComponent } from './alerts/alerts.component';
|
||||||
|
import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component';
|
||||||
|
import { MessageBoardComponent } from './message-board/message-board.component';
|
||||||
|
import { MessageComponent } from './message-board/message/message.component';
|
||||||
|
import { MyDSpaceResultDetailElementComponent } from './object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component';
|
||||||
|
import { ClaimedTaskActionsComponent } from './mydspace-actions/claimed-task/claimed-task-actions.component';
|
||||||
|
import { PoolTaskActionsComponent } from './mydspace-actions/pool-task/pool-task-actions.component';
|
||||||
|
import { ObjectDetailComponent } from './object-detail/object-detail.component';
|
||||||
|
import { WrapperDetailElementComponent } from './object-detail/wrapper-detail-element/wrapper-detail-element.component';
|
||||||
|
import { ItemDetailPreviewComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component';
|
||||||
|
import { MyDSpaceItemStatusComponent } from './object-collection/shared/mydspace-item-status/my-dspace-item-status.component';
|
||||||
|
import { WorkspaceitemActionsComponent } from './mydspace-actions/workspaceitem/workspaceitem-actions.component';
|
||||||
|
import { WorkflowitemActionsComponent } from './mydspace-actions/workflowitem/workflowitem-actions.component';
|
||||||
|
import { ItemSubmitterComponent } from './object-collection/shared/mydspace-item-submitter/item-submitter.component';
|
||||||
|
import { ItemActionsComponent } from './mydspace-actions/item/item-actions.component';
|
||||||
|
import { ClaimedTaskActionsApproveComponent } from './mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component';
|
||||||
|
import { ClaimedTaskActionsRejectComponent } from './mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component';
|
||||||
import { ObjNgFor } from './utils/object-ngfor.pipe';
|
import { ObjNgFor } from './utils/object-ngfor.pipe';
|
||||||
import { BrowseByComponent } from './browse-by/browse-by.component';
|
import { BrowseByComponent } from './browse-by/browse-by.component';
|
||||||
import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component';
|
import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component';
|
||||||
@@ -95,6 +111,20 @@ import { EditComColPageComponent } from './comcol-forms/edit-comcol-page/edit-co
|
|||||||
import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component';
|
import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component';
|
||||||
import { LangSwitchComponent } from './lang-switch/lang-switch.component';
|
import { LangSwitchComponent } from './lang-switch/lang-switch.component';
|
||||||
import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component';
|
import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component';
|
||||||
|
import { ItemListPreviewComponent } from './object-list/item-list-preview/item-list-preview.component';
|
||||||
|
import { ItemPageAuthorFieldComponent } from '../+item-page/simple/field-components/specific-field/author/item-page-author-field.component';
|
||||||
|
import { ItemPageDateFieldComponent } from '../+item-page/simple/field-components/specific-field/date/item-page-date-field.component';
|
||||||
|
import { ItemPageAbstractFieldComponent } from '../+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component';
|
||||||
|
import { ItemPageUriFieldComponent } from '../+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component';
|
||||||
|
import { ItemPageTitleFieldComponent } from '../+item-page/simple/field-components/specific-field/title/item-page-title-field.component';
|
||||||
|
import { ItemPageSpecificFieldComponent } from '../+item-page/simple/field-components/specific-field/item-page-specific-field.component';
|
||||||
|
import { FileSectionComponent } from '../+item-page/simple/field-components/file-section/file-section.component';
|
||||||
|
import { MetadataFieldWrapperComponent } from '../+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
|
import { CollectionsComponent } from '../+item-page/field-components/collections/collections.component';
|
||||||
|
import { MetadataValuesComponent } from '../+item-page/field-components/metadata-values/metadata-values.component';
|
||||||
|
import { MetadataUriValuesComponent } from '../+item-page/field-components/metadata-uri-values/metadata-uri-values.component';
|
||||||
|
import { RoleDirective } from './roles/role.directive';
|
||||||
|
import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -136,6 +166,7 @@ const COMPONENTS = [
|
|||||||
// put shared components here
|
// put shared components here
|
||||||
AlertsComponent,
|
AlertsComponent,
|
||||||
AuthNavMenuComponent,
|
AuthNavMenuComponent,
|
||||||
|
UserMenuComponent,
|
||||||
ChipsComponent,
|
ChipsComponent,
|
||||||
ComcolPageContentComponent,
|
ComcolPageContentComponent,
|
||||||
ComcolPageHeaderComponent,
|
ComcolPageHeaderComponent,
|
||||||
@@ -163,11 +194,15 @@ const COMPONENTS = [
|
|||||||
LoadingComponent,
|
LoadingComponent,
|
||||||
LogInComponent,
|
LogInComponent,
|
||||||
LogOutComponent,
|
LogOutComponent,
|
||||||
|
MessageBoardComponent,
|
||||||
|
MessageComponent,
|
||||||
NumberPickerComponent,
|
NumberPickerComponent,
|
||||||
ObjectListComponent,
|
ObjectListComponent,
|
||||||
|
ObjectDetailComponent,
|
||||||
|
ObjectGridComponent,
|
||||||
AbstractListableElementComponent,
|
AbstractListableElementComponent,
|
||||||
WrapperListElementComponent,
|
WrapperListElementComponent,
|
||||||
ObjectGridComponent,
|
WrapperDetailElementComponent,
|
||||||
WrapperGridElementComponent,
|
WrapperGridElementComponent,
|
||||||
ObjectCollectionComponent,
|
ObjectCollectionComponent,
|
||||||
PaginationComponent,
|
PaginationComponent,
|
||||||
@@ -176,6 +211,17 @@ const COMPONENTS = [
|
|||||||
GridThumbnailComponent,
|
GridThumbnailComponent,
|
||||||
UploaderComponent,
|
UploaderComponent,
|
||||||
WrapperListElementComponent,
|
WrapperListElementComponent,
|
||||||
|
ItemListPreviewComponent,
|
||||||
|
MyDSpaceItemStatusComponent,
|
||||||
|
ItemSubmitterComponent,
|
||||||
|
ItemDetailPreviewComponent,
|
||||||
|
ClaimedTaskActionsComponent,
|
||||||
|
ClaimedTaskActionsApproveComponent,
|
||||||
|
ClaimedTaskActionsRejectComponent,
|
||||||
|
ItemActionsComponent,
|
||||||
|
PoolTaskActionsComponent,
|
||||||
|
WorkflowitemActionsComponent,
|
||||||
|
WorkspaceitemActionsComponent,
|
||||||
ViewModeSwitchComponent,
|
ViewModeSwitchComponent,
|
||||||
TruncatableComponent,
|
TruncatableComponent,
|
||||||
TruncatablePartComponent,
|
TruncatablePartComponent,
|
||||||
@@ -188,12 +234,15 @@ const ENTRY_COMPONENTS = [
|
|||||||
ItemListElementComponent,
|
ItemListElementComponent,
|
||||||
CollectionListElementComponent,
|
CollectionListElementComponent,
|
||||||
CommunityListElementComponent,
|
CommunityListElementComponent,
|
||||||
|
MyDSpaceResultListElementComponent,
|
||||||
SearchResultListElementComponent,
|
SearchResultListElementComponent,
|
||||||
ItemGridElementComponent,
|
ItemGridElementComponent,
|
||||||
CollectionGridElementComponent,
|
CollectionGridElementComponent,
|
||||||
CommunityGridElementComponent,
|
CommunityGridElementComponent,
|
||||||
SearchResultGridElementComponent,
|
SearchResultGridElementComponent,
|
||||||
BrowseEntryListElementComponent,
|
BrowseEntryListElementComponent,
|
||||||
|
MyDSpaceResultDetailElementComponent,
|
||||||
|
SearchResultGridElementComponent,
|
||||||
DsDynamicListComponent,
|
DsDynamicListComponent,
|
||||||
DsDynamicLookupComponent,
|
DsDynamicLookupComponent,
|
||||||
DsDynamicScrollableDropdownComponent,
|
DsDynamicScrollableDropdownComponent,
|
||||||
@@ -206,6 +255,20 @@ const ENTRY_COMPONENTS = [
|
|||||||
DsDatePickerInlineComponent
|
DsDatePickerInlineComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||||
|
CollectionsComponent,
|
||||||
|
FileSectionComponent,
|
||||||
|
ItemPageAuthorFieldComponent,
|
||||||
|
ItemPageDateFieldComponent,
|
||||||
|
ItemPageAbstractFieldComponent,
|
||||||
|
ItemPageUriFieldComponent,
|
||||||
|
ItemPageTitleFieldComponent,
|
||||||
|
ItemPageSpecificFieldComponent,
|
||||||
|
MetadataFieldWrapperComponent,
|
||||||
|
MetadataValuesComponent,
|
||||||
|
MetadataUriValuesComponent
|
||||||
|
];
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
TruncatableService,
|
TruncatableService,
|
||||||
MockAdminGuard,
|
MockAdminGuard,
|
||||||
@@ -220,7 +283,8 @@ const DIRECTIVES = [
|
|||||||
DragClickDirective,
|
DragClickDirective,
|
||||||
DebounceDirective,
|
DebounceDirective,
|
||||||
ClickOutsideDirective,
|
ClickOutsideDirective,
|
||||||
AuthorityConfidenceStateDirective
|
AuthorityConfidenceStateDirective,
|
||||||
|
RoleDirective
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -232,6 +296,7 @@ const DIRECTIVES = [
|
|||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
...DIRECTIVES,
|
...DIRECTIVES,
|
||||||
...ENTRY_COMPONENTS,
|
...ENTRY_COMPONENTS,
|
||||||
|
...SHARED_ITEM_PAGE_COMPONENTS
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
...PROVIDERS
|
...PROVIDERS
|
||||||
@@ -240,6 +305,7 @@ const DIRECTIVES = [
|
|||||||
...MODULES,
|
...MODULES,
|
||||||
...PIPES,
|
...PIPES,
|
||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
|
...SHARED_ITEM_PAGE_COMPONENTS,
|
||||||
...DIRECTIVES
|
...DIRECTIVES
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse">
|
<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
Reference in New Issue
Block a user