From 52906e71c49a14eebcc53375f41fd20c5aa4eb46 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 8 Mar 2019 19:49:07 +0100 Subject: [PATCH] Intermediate commit --- resources/i18n/en.json | 131 +++++++++++++++++- src/app/+item-page/item-page.module.ts | 22 --- .../+item-page/simple/item-page.component.ts | 2 +- .../my-dspace-configuration.service.ts | 48 ++++++- .../my-dspace-page.component.ts | 21 +-- .../normalized-search-result.model.ts | 4 +- .../paginated-search-options.model.ts | 2 +- .../search-facet-filter.component.ts | 3 +- .../search-filter.component.html | 2 +- .../search-filter/search-filter.component.ts | 7 +- .../search-range-filter.component.ts | 5 +- .../search-filters.component.html | 8 +- .../search-filters.component.ts | 72 ++++++++-- .../search-labels/search-labels.component.ts | 7 +- src/app/+search-page/search-options.model.ts | 7 +- src/app/+search-page/search-page.component.ts | 15 +- src/app/+search-page/search-page.module.ts | 53 +++---- .../search-service/facet-value.model.ts | 8 +- .../search-configuration.service.ts | 46 ++++-- .../search-query-response.model.ts | 2 +- .../search-result-element-decorator.ts | 20 ++- .../search-service/search.service.ts | 59 ++++++-- .../search-settings.component.ts | 5 +- .../search-sidebar.component.html | 1 + .../search-sidebar.component.scss | 3 + .../search-sidebar.component.ts | 7 + .../search-configuration-option.model.ts | 4 + ...search-switch-configuration.component.html | 13 ++ ...search-switch-configuration.component.scss | 0 ...rch-switch-configuration.component.spec.ts | 103 ++++++++++++++ .../search-switch-configuration.component.ts | 54 ++++++++ src/app/app-routing.module.ts | 1 + src/app/core/data/request.service.ts | 21 ++- .../user-menu/user-menu.component.ts | 4 - .../pagination-component-options.model.ts | 8 ++ src/app/shared/roles/role.directive.ts | 114 +++++++++++++++ .../search-form/search-form.component.ts | 3 +- src/app/shared/services/route.service.ts | 26 +++- src/app/shared/shared.module.ts | 70 +++++++++- .../truncatable/truncatable.component.html | 4 +- 40 files changed, 845 insertions(+), 140 deletions(-) create mode 100644 src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts create mode 100644 src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html create mode 100644 src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss create mode 100644 src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts create mode 100644 src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts create mode 100644 src/app/shared/roles/role.directive.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a05cb6c24e..4d95a0e1d1 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -236,6 +236,7 @@ }, "login": "Log In", "logout": "Log Out", + "mydspace": "MyDSpace", "language": "Language switch", "search": "Search" }, @@ -272,12 +273,62 @@ "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": { "title": "DSpace Angular :: Search", "description": "", "form": { "search": "Search", - "search_dspace": "Search DSpace" + "search_dspace": "Search DSpace", + "search_mydspace": "Search MyDSpace" }, "results": { "head": "Search Results", @@ -295,11 +346,18 @@ "title": "Settings", "sort-by": "Sort By", "rpp": "Results per page" + }, + "tab":{ + "title":"Show" } }, + "switch-configuration": { + "title":"Show" + }, "view-switch": { "show-list": "Show as list", - "show-grid": "Show as grid" + "show-grid": "Show as grid", + "show-detail": "Show detail" }, "filters": { "head": "Filters", @@ -309,7 +367,10 @@ "f.dateIssued.min": "Start date", "f.dateIssued.max": "End date", "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": { "show-more": "Show more", @@ -337,6 +398,26 @@ }, "has_content_in_original_bundle": { "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...", "objects": "Loading...", "search-results": "Loading search results...", + "mydspace-results": "Loading items...", "browse-by": "Loading items..." }, "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 not 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": { diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index c60f9d3583..f56f45d753 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -5,17 +5,6 @@ import { SharedModule } from '../shared/shared.module'; import { ItemPageComponent } from './simple/item-page.component'; 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 { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; 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: [ ItemPageComponent, FullItemPageComponent, - MetadataValuesComponent, - MetadataUriValuesComponent, - MetadataFieldWrapperComponent, - ItemPageAuthorFieldComponent, - ItemPageDateFieldComponent, - ItemPageAbstractFieldComponent, - ItemPageUriFieldComponent, - ItemPageTitleFieldComponent, - ItemPageSpecificFieldComponent, - FileSectionComponent, - CollectionsComponent, FullFileSectionComponent ] }) diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 35162b011f..d280d2c0db 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -51,6 +51,6 @@ export class ItemPageComponent implements OnInit { this.thumbnail$ = this.itemRD$.pipe( map((rd: RemoteData) => rd.payload), filter((item: Item) => hasValue(item)), - mergeMap((item: Item) => item.getThumbnail()),); + mergeMap((item: Item) => item.getThumbnail())); } } diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts index 56db2a8b34..4a06582425 100644 --- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts @@ -1,25 +1,67 @@ import { Injectable } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { combineLatest, Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; + import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { RoleService } from '../core/roles/role.service'; 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 */ @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; private isController$: Observable; private isSubmitter$: Observable; /** - * @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.isController$ = this.roleService.isController(); this.isAdmin$ = this.roleService.isAdmin(); diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts index 33d0a0e4c9..7afb8e2586 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.ts @@ -1,6 +1,6 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; -import { distinctUntilChanged, filter, flatMap, map, switchMap, tap, } from 'rxjs/operators'; +import { ChangeDetectionStrategy, Component, Inject, InjectionToken, OnInit } from '@angular/core'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { switchMap, } from 'rxjs/operators'; import { PaginatedList } from '../core/data/paginated-list'; import { RemoteData } from '../core/data/remote-data'; 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 { SearchConfigurationService } from '../+search-page/search-service/search-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 SEARCH_CONFIG_SERVICE: InjectionToken = new InjectionToken('searchConfigurationService'); /** * This component renders a simple item page. @@ -33,7 +33,13 @@ export const MYDSPACE_ROUTE = '/mydspace'; styleUrls: ['./my-dspace-page.component.scss'], templateUrl: './my-dspace-page.component.html', 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 windowService: HostWindowService, private filterService: SearchFilterService, - private configurationService: MyDSpaceConfigurationService, - private searchConfigService: SearchConfigurationService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); 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 */ ngOnInit(): void { - this.configurationList$ = this.configurationService.getAvailableConfigurationOptions(); + this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions(); this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; this.sub = this.searchOptions$.pipe( diff --git a/src/app/+search-page/normalized-search-result.model.ts b/src/app/+search-page/normalized-search-result.model.ts index 3c1a46872c..f6ab366d95 100644 --- a/src/app/+search-page/normalized-search-result.model.ts +++ b/src/app/+search-page/normalized-search-result.model.ts @@ -1,4 +1,4 @@ -import { autoserialize } from 'cerialize'; +import { autoserialize, autoserializeAs } from 'cerialize'; import { MetadataMap } from '../core/shared/metadata.interfaces'; 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 */ - @autoserialize + @autoserializeAs(String, 'rObject') dspaceObject: string; /** diff --git a/src/app/+search-page/paginated-search-options.model.ts b/src/app/+search-page/paginated-search-options.model.ts index 8f4d93b0df..a34229f9b4 100644 --- a/src/app/+search-page/paginated-search-options.model.ts +++ b/src/app/+search-page/paginated-search-options.model.ts @@ -12,7 +12,7 @@ export class PaginatedSearchOptions extends SearchOptions { pagination?: PaginationComponentOptions; 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); this.pagination = options.pagination; this.sort = options.sort; diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index fd5a75e7d1..f48cad15cc 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -21,6 +21,7 @@ import { SearchService } from '../../../search-service/search.service'; import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service'; import { SearchConfigurationService } from '../../../search-service/search-configuration.service'; import { getSucceededRemoteData } from '../../../../core/shared/operators'; +import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-facet-filter', @@ -74,9 +75,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { constructor(protected searchService: SearchService, protected filterService: SearchFilterService, - protected searchConfigService: SearchConfigurationService, protected rdbs: RemoteDataBuildService, protected router: Router, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) { } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html index 1013bf7e28..13fc6d9c7c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -4,4 +4,4 @@
- \ No newline at end of file + diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index dcc01f2b46..385f83eae2 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -1,9 +1,10 @@ - -import { take } from 'rxjs/operators'; 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 { SearchFilterService } from './search-filter.service'; -import { Observable } from 'rxjs'; import { slide } from '../../../shared/animations/slide'; import { isNotEmpty } from '../../../shared/empty.util'; diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts index 6cb04c6c1f..af0676ffe2 100644 --- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts @@ -22,6 +22,7 @@ import * as moment from 'moment'; import { RouteService } from '../../../../shared/services/route.service'; import { hasValue } from '../../../../shared/empty.util'; 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. @@ -67,13 +68,13 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple constructor(protected searchService: SearchService, protected filterService: SearchFilterService, - protected searchConfigService: SearchConfigurationService, protected router: Router, protected rdbs: RemoteDataBuildService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(PLATFORM_ID) private platformId: any, private route: RouteService) { - super(searchService, filterService, searchConfigService, rdbs, router, filterConfig); + super(searchService, filterService, rdbs, router, searchConfigService, filterConfig); } diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 0522c1fba0..310d6502c7 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

{{"search.filters.head" | translate}}

-
-
- +
+
+
-{{"search.filters.reset" | translate}} \ No newline at end of file +{{"search.filters.reset" | translate}} diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index f16faff1f3..d6116843be 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -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 { RemoteData } from '../../core/data/remote-data'; import { SearchFilterConfig } from '../search-service/search-filter-config.model'; 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 { 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({ 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. */ -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>; + filters: SearchFilterConfig[] = []; /** * List of all filters that are currently active with their value set to null. @@ -31,15 +35,44 @@ export class SearchFiltersComponent { */ clearParams; + /** + * A boolean representing load state of filters configuration + */ + isLoadingFilters$: BehaviorSubject = new BehaviorSubject(true); + + /** + * The current paginated search options + */ + searchOptions$: Observable; + + private sub: Subscription; + /** * Initialize instance variables + * @param {ChangeDetectorRef} cdr * @param {SearchService} searchService * @param {SearchConfigurationService} searchConfigService * @param {SearchFilterService} filterService */ - constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) { - this.filters = searchService.getConfig().pipe(getSucceededRemoteData()); - this.clearParams = searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { + constructor( + private cdr: ChangeDetectorRef, + 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) => { + this.filters = filtersRD.payload; + this.isLoadingFilters$.next(false); + }); + + this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { Object.keys(filters).forEach((f) => filters[f] = null); return filters; })); @@ -58,12 +91,13 @@ export class SearchFiltersComponent { * @returns {Observable} Emits true whenever a given filter config should be shown */ isActive(filterConfig: SearchFilterConfig): Observable { + console.log('isActive', filterConfig); return this.filterService.getSelectedValuesForFilter(filterConfig).pipe( mergeMap((isActive) => { if (isNotEmpty(isActive)) { return observableOf(true); } else { - return this.searchConfigService.searchOptions.pipe( + return this.searchOptions$.pipe( switchMap((options) => { return this.searchService.getFacetValuesFor(filterConfig, 1, options).pipe( 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(); + } + } + } diff --git a/src/app/+search-page/search-labels/search-labels.component.ts b/src/app/+search-page/search-labels/search-labels.component.ts index 08e07cce3d..fd82de326c 100644 --- a/src/app/+search-page/search-labels/search-labels.component.ts +++ b/src/app/+search-page/search-labels/search-labels.component.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { Observable } from 'rxjs'; import { Params } from '@angular/router'; import { map } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-labels', @@ -24,7 +25,9 @@ export class SearchLabelsComponent { /** * 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(); } diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index 123cf950f8..e56cec1724 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -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 */ export class SearchOptions { + configuration?: string; scope?: string; query?: string; dsoType?: DSpaceObjectType; 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.query = options.query; this.dsoType = options.dsoType; @@ -28,6 +30,9 @@ export class SearchOptions { */ toRestUrl(url: string, args: string[] = []): string { + if (isNotEmpty(this.configuration)) { + args.push(`configuration=${this.configuration}`); + } if (isNotEmpty(this.query)) { args.push(`query=${this.query}`); } diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 816e3d67bf..333faacc47 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -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 { switchMap, } from 'rxjs/operators'; 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 { HostWindowService } from '../shared/host-window.service'; import { PaginatedSearchOptions } from './paginated-search-options.model'; -import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { SearchResult } from './search-result.model'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { hasValue } from '../shared/empty.util'; import { SearchConfigurationService } from './search-service/search-configuration.service'; 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. @@ -26,7 +26,13 @@ import { getSucceededRemoteData } from '../core/shared/operators'; styleUrls: ['./search-page.component.scss'], templateUrl: './search-page.component.html', 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, private sidebarService: SearchSidebarService, private windowService: HostWindowService, - private filterService: SearchFilterService, - private searchConfigService: SearchConfigurationService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 0c8a4ee306..ff23f92b2c 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -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 { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; import { SearchConfigurationService } from './search-service/search-configuration.service'; +import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component'; const effects = [ 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({ imports: [ SearchPageRoutingModule, @@ -41,29 +67,7 @@ const effects = [ EffectsModule.forFeature(effects), CoreModule.forRoot() ], - declarations: [ - SearchPageComponent, - SearchResultsComponent, - SearchSidebarComponent, - SearchSettingsComponent, - ItemSearchResultListElementComponent, - CollectionSearchResultListElementComponent, - CommunitySearchResultListElementComponent, - ItemSearchResultGridElementComponent, - CollectionSearchResultGridElementComponent, - CommunitySearchResultGridElementComponent, - CommunitySearchResultListElementComponent, - SearchFiltersComponent, - SearchFilterComponent, - SearchFacetFilterComponent, - SearchLabelsComponent, - SearchFacetFilterComponent, - SearchFacetFilterWrapperComponent, - SearchRangeFilterComponent, - SearchTextFilterComponent, - SearchHierarchyFilterComponent, - SearchBooleanFilterComponent, - ], + declarations: components, providers: [ SearchService, SearchSidebarService, @@ -82,7 +86,8 @@ const effects = [ SearchTextFilterComponent, SearchHierarchyFilterComponent, SearchBooleanFilterComponent, - ] + ], + exports: components }) /** diff --git a/src/app/+search-page/search-service/facet-value.model.ts b/src/app/+search-page/search-service/facet-value.model.ts index a597528d50..d5102ec68d 100644 --- a/src/app/+search-page/search-service/facet-value.model.ts +++ b/src/app/+search-page/search-service/facet-value.model.ts @@ -6,7 +6,13 @@ import { autoserialize, autoserializeAs } from 'cerialize'; */ 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') value: string; diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index 292f26724d..31ba839eb5 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -1,3 +1,6 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { ActivatedRoute, Params } from '@angular/router'; + import { BehaviorSubject, combineLatest as observableCombineLatest, @@ -7,12 +10,11 @@ import { Subscription } from 'rxjs'; import { filter, map } from 'rxjs/operators'; + import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../search-options.model'; -import { ActivatedRoute, Params } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; -import { Injectable, OnDestroy } from '@angular/core'; import { RouteService } from '../../shared/services/route.service'; import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { RemoteData } from '../../core/data/remote-data'; @@ -28,7 +30,7 @@ export class SearchConfigurationService implements OnDestroy { /** * Default pagination settings */ - private defaultPagination = Object.assign(new PaginationComponentOptions(), { + protected defaultPagination = Object.assign(new PaginationComponentOptions(), { id: 'search-page-configuration', pageSize: 10, currentPage: 1 @@ -37,17 +39,22 @@ export class SearchConfigurationService implements OnDestroy { /** * 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 */ - private defaultScope = ''; + protected defaultScope = ''; /** * Default query setting */ - private defaultQuery = ''; + protected defaultQuery = ''; /** * Emits the current default values @@ -74,8 +81,8 @@ export class SearchConfigurationService implements OnDestroy { * @param {RouteService} routeService * @param {ActivatedRoute} route */ - constructor(private routeService: RouteService, - private route: ActivatedRoute) { + constructor(protected routeService: RouteService, + protected route: ActivatedRoute) { this.defaults .pipe(getSucceededRemoteData()) .subscribe((defRD) => { @@ -85,10 +92,20 @@ export class SearchConfigurationService implements OnDestroy { this.subs.push(this.subscribeToSearchOptions(defs)); this.subs.push(this.subscribeToPaginatedSearchOptions(defs)); + } ) } + /** + * @returns {Observable} Emits the current configuration string + */ + getCurrentConfiguration(defaultConfiguration: string) { + return this.routeService.getQueryParameterValue('configuration').pipe(map((configuration) => { + return configuration || defaultConfiguration; + })); + } + /** * @returns {Observable} Emits the current scope's identifier */ @@ -188,6 +205,7 @@ export class SearchConfigurationService implements OnDestroy { */ subscribeToSearchOptions(defaults: SearchOptions): Subscription { return observableMerge( + this.getConfigurationPart(defaults.configuration), this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), @@ -208,6 +226,7 @@ export class SearchConfigurationService implements OnDestroy { return observableMerge( this.getPaginationPart(defaults.pagination), this.getSortPart(defaults.sort), + this.getConfigurationPart(defaults.configuration), this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), @@ -226,6 +245,7 @@ export class SearchConfigurationService implements OnDestroy { if (hasNoValue(this._defaults)) { const options = new PaginatedSearchOptions({ pagination: this.defaultPagination, + configuration: this.defaultConfiguration, sort: this.defaultSort, scope: this.defaultScope, query: this.defaultQuery @@ -242,6 +262,16 @@ export class SearchConfigurationService implements OnDestroy { this.subs.forEach((sub) => { sub.unsubscribe(); }); + this.subs = []; + } + + /** + * @returns {Observable} Emits the current configuration settings as a partial SearchOptions object + */ + private getConfigurationPart(defaultConfiguration: string): Observable { + return this.getCurrentConfiguration(defaultConfiguration).pipe(map((configuration) => { + return { configuration } + })); } /** diff --git a/src/app/+search-page/search-service/search-query-response.model.ts b/src/app/+search-page/search-service/search-query-response.model.ts index ac1d8b7df3..bca6e644fc 100644 --- a/src/app/+search-page/search-service/search-query-response.model.ts +++ b/src/app/+search-page/search-service/search-query-response.model.ts @@ -34,7 +34,7 @@ export class SearchQueryResponse { * The sort parameters used in the search request */ @autoserialize - configurationName: string; + configuration: string; /** * The sort parameters used in the search request diff --git a/src/app/+search-page/search-service/search-result-element-decorator.ts b/src/app/+search-page/search-service/search-result-element-decorator.ts index 348cf7f592..59446480a3 100644 --- a/src/app/+search-page/search-service/search-result-element-decorator.ts +++ b/src/app/+search-page/search-service/search-result-element-decorator.ts @@ -1,5 +1,6 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; 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 @@ -11,12 +12,19 @@ const searchResultMap = new Map(); * @param {GenericConstructor} domainConstructor The constructor of the DSpaceObject * @returns Decorator function that performs the actual mapping on initialization of the component */ -export function searchResultFor(domainConstructor: GenericConstructor) { +export function searchResultFor(domainConstructor: GenericConstructor, configuration: string = null) { return function decorator(searchResult: any) { if (!searchResult) { 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} domainConstructor The DSpaceObject's constructor for which the search result component is requested * @returns The component's constructor that matches the given DSpaceObject */ -export function getSearchResultFor(domainConstructor: GenericConstructor) { - return searchResultMap.get(domainConstructor); +export function getSearchResultFor(domainConstructor: GenericConstructor, configuration: string = null) { + if (isNull(configuration) || configuration === 'default') { + return searchResultMap.get(domainConstructor); + } else { + return searchResultMap.get(configuration).get(domainConstructor); + } } diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 275b0b3340..1d5ff06193 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -7,7 +7,7 @@ import { Router, UrlSegmentGroup } 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 { FacetConfigSuccessResponse, @@ -23,12 +23,12 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { - configureRequest, + configureRequest, filterSuccessfulResponses, getResponseFromEntry, getSucceededRemoteData } from '../../core/shared/operators'; 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 { SearchOptions } from '../search-options.model'; import { SearchResult } from '../search-result.model'; @@ -63,6 +63,16 @@ export class SearchService implements OnDestroy { */ private facetLinkPathPrefix = 'discover/facets/'; + /** + * When true, a new search request is always dispatched + */ + private forceBypassCache = false; + + /** + * The ResponseParsingService constructor name + */ + private parser: GenericConstructor = SearchResponseParsingService; + /** * Subscription to unsubscribe from */ @@ -78,6 +88,19 @@ export class SearchService implements OnDestroy { ) { } + /** + * Method to set service options + * @param {GenericConstructor} parser The configuration necessary to perform this search + * @param {boolean} forceBypassCache When true, a new search request is always dispatched + * @returns {Observable>>>} Emits a paginated list with all search results found + */ + setServiceOptions(parser: GenericConstructor, forceBypassCache: boolean) { + if (parser) { + this.parser = parser; + } + this.forceBypassCache = forceBypassCache; + } + /** * Method to retrieve a paginated list of search results from the server * @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); } const request = new GetRequest(this.requestService.generateRequestId(), url); + const getResponseParserFn: () => GenericConstructor = () => { + return this.parser; + }; + return Object.assign(request, { - getResponseParser(): GenericConstructor { - return SearchResponseParsingService; - } + getResponseParser: getResponseParserFn }); }), - configureRequest(this.requestService) + configureRequest(this.requestService, this.forceBypassCache), ); const requestEntryObs = requestObs.pipe( 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 list of observable remote data DSO's into observable remote data object with list of DSO const dsoObs: Observable> = sqrObs.pipe( + // filter((sqr: SearchQueryResponse) => isNotUndefined(sqr)), 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); }) }), @@ -126,7 +154,7 @@ export class SearchService implements OnDestroy { let co = DSpaceObject; if (dsos.payload[index]) { const constructor: GenericConstructor = dsos.payload[index].constructor as GenericConstructor; - co = getSearchResultFor(constructor); + co = getSearchResultFor(constructor, searchOptions.configuration); return Object.assign(new co(), object, { dspaceObject: dsos.payload[index] }); @@ -134,6 +162,7 @@ export class SearchService implements OnDestroy { 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 * @returns {Observable>} The found filter configuration */ - getConfig(scope?: string): Observable> { + getConfig(scope?: string, configuration?: string): Observable> { const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe( map((url: string) => { const args: string[] = []; @@ -165,6 +194,10 @@ export class SearchService implements OnDestroy { args.push(`scope=${scope}`); } + if (isNotEmpty(configuration)) { + args.push(`configuration=${configuration}`); + } + if (isNotEmpty(args)) { 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( @@ -202,6 +235,7 @@ export class SearchService implements OnDestroy { * @returns {Observable>>} Emits the given page of facet values */ getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable>> { + console.log('getFacetValuesFor'); const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe( map((url: string) => { 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( diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 7fc5645fcc..24b2ee4778 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -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 { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { Observable } from 'rxjs'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-settings', @@ -30,7 +31,7 @@ export class SearchSettingsComponent implements OnInit { constructor(private service: SearchService, private route: ActivatedRoute, private router: Router, - private searchConfigurationService: SearchConfigurationService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigurationService: SearchConfigurationService) { } /** diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index 5ff1e3c8fa..ac9c834443 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -10,6 +10,7 @@
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss index b5bd6dd30d..960a8dfa8c 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss @@ -8,6 +8,9 @@ ds-view-mode-switch { margin-bottom: $spacer; } + ds-search-switch-configuration { + margin-bottom: 2*$spacer !important; + } .sidebar-content > *:not(:last-child) { margin-bottom: 4*$spacer; display: block; diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.ts b/src/app/+search-page/search-sidebar/search-sidebar.component.ts index 8b68cda793..bdd90633b0 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.ts @@ -1,5 +1,7 @@ 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. * 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 { + /** + * The list of available configuration options + */ + @Input() configurationList: SearchConfigurationOption[]; + /** * The total amount of results */ diff --git a/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts new file mode 100644 index 0000000000..7f9b4acd96 --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts @@ -0,0 +1,4 @@ +export interface SearchConfigurationOption { + value: string; + label: string; +} diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html new file mode 100644 index 0000000000..5b1bdc1ddd --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html @@ -0,0 +1,13 @@ +
+
{{ 'search.switch-configuration.title' | translate}}
+ + + +
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts new file mode 100644 index 0000000000..c7367c5f3f --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts @@ -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; +// 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(); +// }); +// +// }); diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts new file mode 100644 index 0000000000..d894b4f454 --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts @@ -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(); + } + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 03edd698fd..b0c9305a66 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ export function getItemModulePath() { { path: 'communities', loadChildren: './+community-page/community-page.module#CommunityPageModule' }, { path: 'collections', loadChildren: './+collection-page/collection-page.module#CollectionPageModule' }, { 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: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' }, { path: 'admin', loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] }, diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index da1857b1c0..cdf7dd6de0 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; 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 { 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 configure(request: RestRequest, forceBypassCache: boolean = false): void { 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); if (isGetRequest) { 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) * @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts index 41298b559f..452e5b30ca 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts @@ -37,9 +37,5 @@ export class UserMenuComponent implements OnInit { // set user this.user$ = this.store.pipe(select(getAuthenticatedUser)); - this.user$.subscribe((user) => { - console.log(user, user.name); - }) - } } diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts index 30ed2becd2..07756f1e6f 100644 --- a/src/app/shared/pagination/pagination-component-options.model.ts +++ b/src/app/shared/pagination/pagination-component-options.model.ts @@ -12,11 +12,19 @@ export class PaginationComponentOptions extends NgbPaginationConfig { */ currentPage = 1; + /** + * Maximum number of pages to display. + */ + maxSize = 10; + /** * A number array that represents options for a context pagination limit. */ pageSizeOptions: number[] = [5, 10, 20, 40, 60, 80, 100]; + /** + * Number of items per page. + */ pageSize: number; } diff --git a/src/app/shared/roles/role.directive.ts b/src/app/shared/roles/role.directive.ts new file mode 100644 index 0000000000..d71e520e35 --- /dev/null +++ b/src/app/shared/roles/role.directive.ts @@ -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 + ) { + } + + 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} + * observable of true if current user role is included in the specified role list, observable of false otherwise + */ + private hasRoles(roles: RoleType | RoleType[]): Observable { + const toValidate: RoleType[] = (Array.isArray(roles)) ? roles : [roles]; + const checks: Array> = toValidate.map((role) => this.roleService.checkRole(role)); + + return combineLatest(checks).pipe( + map((permissions: boolean[]) => permissions.includes(true)), + first() + ) + } +} diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 21a90daed4..ea96bd8114 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -3,6 +3,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Router } from '@angular/router'; import { hasValue, isNotEmpty } from '../empty.util'; 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. @@ -64,7 +65,7 @@ export class SearchFormComponent { updateSearch(data: any) { const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search'; let handling: QueryParamsHandling = '' ; - if (this.currentUrl === '/search') { + if (this.currentUrl === '/search' || this.currentUrl === MYDSPACE_ROUTE) { handling = 'merge'; } this.router.navigate([newUrl], { diff --git a/src/app/shared/services/route.service.ts b/src/app/shared/services/route.service.ts index a55a967d3b..f500b18082 100644 --- a/src/app/shared/services/route.service.ts +++ b/src/app/shared/services/route.service.ts @@ -1,9 +1,10 @@ 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 { Observable } from 'rxjs'; import { select, Store } from '@ngrx/store'; +import { isEqual } from 'lodash'; import { AppState } from '../../app.reducer'; import { AddUrlToHistoryAction } from '../history/history.actions'; @@ -16,35 +17,35 @@ export class RouteService { } getQueryParameterValues(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => [...params.getAll(paramName)]), distinctUntilChanged() ); } getQueryParameterValue(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.get(paramName)), distinctUntilChanged() ); } hasQueryParam(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.has(paramName)), distinctUntilChanged() ); } hasQueryParamWithValue(paramName: string, paramValue: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.getAll(paramName).indexOf(paramValue) > -1), distinctUntilChanged() ); } getQueryParamsWithPrefix(prefix: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((qparams) => { const params = {}; qparams.keys @@ -57,6 +58,19 @@ export class RouteService { distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))); } + public getQueryParamMap(): Observable { + 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 { this.router.events .pipe(filter((event) => event instanceof NavigationEnd)) diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index b7250a6e18..1acff996fa 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -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 { MockAdminGuard } from './mocks/mock-admin-guard.service'; 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 { BrowseByComponent } from './browse-by/browse-by.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 { LangSwitchComponent } from './lang-switch/lang-switch.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 = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -136,6 +166,7 @@ const COMPONENTS = [ // put shared components here AlertsComponent, AuthNavMenuComponent, + UserMenuComponent, ChipsComponent, ComcolPageContentComponent, ComcolPageHeaderComponent, @@ -163,11 +194,15 @@ const COMPONENTS = [ LoadingComponent, LogInComponent, LogOutComponent, + MessageBoardComponent, + MessageComponent, NumberPickerComponent, ObjectListComponent, + ObjectDetailComponent, + ObjectGridComponent, AbstractListableElementComponent, WrapperListElementComponent, - ObjectGridComponent, + WrapperDetailElementComponent, WrapperGridElementComponent, ObjectCollectionComponent, PaginationComponent, @@ -176,6 +211,17 @@ const COMPONENTS = [ GridThumbnailComponent, UploaderComponent, WrapperListElementComponent, + ItemListPreviewComponent, + MyDSpaceItemStatusComponent, + ItemSubmitterComponent, + ItemDetailPreviewComponent, + ClaimedTaskActionsComponent, + ClaimedTaskActionsApproveComponent, + ClaimedTaskActionsRejectComponent, + ItemActionsComponent, + PoolTaskActionsComponent, + WorkflowitemActionsComponent, + WorkspaceitemActionsComponent, ViewModeSwitchComponent, TruncatableComponent, TruncatablePartComponent, @@ -188,12 +234,15 @@ const ENTRY_COMPONENTS = [ ItemListElementComponent, CollectionListElementComponent, CommunityListElementComponent, + MyDSpaceResultListElementComponent, SearchResultListElementComponent, ItemGridElementComponent, CollectionGridElementComponent, CommunityGridElementComponent, SearchResultGridElementComponent, BrowseEntryListElementComponent, + MyDSpaceResultDetailElementComponent, + SearchResultGridElementComponent, DsDynamicListComponent, DsDynamicLookupComponent, DsDynamicScrollableDropdownComponent, @@ -206,6 +255,20 @@ const ENTRY_COMPONENTS = [ DsDatePickerInlineComponent ]; +const SHARED_ITEM_PAGE_COMPONENTS = [ + CollectionsComponent, + FileSectionComponent, + ItemPageAuthorFieldComponent, + ItemPageDateFieldComponent, + ItemPageAbstractFieldComponent, + ItemPageUriFieldComponent, + ItemPageTitleFieldComponent, + ItemPageSpecificFieldComponent, + MetadataFieldWrapperComponent, + MetadataValuesComponent, + MetadataUriValuesComponent +]; + const PROVIDERS = [ TruncatableService, MockAdminGuard, @@ -220,7 +283,8 @@ const DIRECTIVES = [ DragClickDirective, DebounceDirective, ClickOutsideDirective, - AuthorityConfidenceStateDirective + AuthorityConfidenceStateDirective, + RoleDirective ]; @NgModule({ @@ -232,6 +296,7 @@ const DIRECTIVES = [ ...COMPONENTS, ...DIRECTIVES, ...ENTRY_COMPONENTS, + ...SHARED_ITEM_PAGE_COMPONENTS ], providers: [ ...PROVIDERS @@ -240,6 +305,7 @@ const DIRECTIVES = [ ...MODULES, ...PIPES, ...COMPONENTS, + ...SHARED_ITEM_PAGE_COMPONENTS, ...DIRECTIVES ], entryComponents: [ diff --git a/src/app/shared/truncatable/truncatable.component.html b/src/app/shared/truncatable/truncatable.component.html index c03e93c2ce..b524e5e754 100644 --- a/src/app/shared/truncatable/truncatable.component.html +++ b/src/app/shared/truncatable/truncatable.component.html @@ -1,3 +1,3 @@ -
+
-
\ No newline at end of file +