mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'master' into Keep-virtual-metadata-on-relationship-delete
This commit is contained in:
@@ -2,7 +2,8 @@ import { browser, element, by } from 'protractor';
|
||||
|
||||
export class ProtractorPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
return browser.get('/')
|
||||
.then(() => browser.waitForAngular());
|
||||
}
|
||||
|
||||
getPageTitleText() {
|
||||
|
@@ -15,7 +15,11 @@ module.exports = function (config) {
|
||||
};
|
||||
|
||||
var configuration = {
|
||||
|
||||
client: {
|
||||
jasmine: {
|
||||
random: false
|
||||
}
|
||||
},
|
||||
// base path that will be used to resolve all patterns (e.g. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
@@ -75,6 +75,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^6.1.4",
|
||||
"@angular/cdk": "^6.4.7",
|
||||
"@angular/cli": "^6.1.5",
|
||||
"@angular/common": "^6.1.4",
|
||||
"@angular/core": "^6.1.4",
|
||||
@@ -139,6 +140,7 @@
|
||||
"text-mask-core": "5.0.1",
|
||||
"ts-loader": "^5.2.1",
|
||||
"ts-md5": "^1.2.4",
|
||||
"url-parse": "^1.4.7",
|
||||
"uuid": "^3.2.1",
|
||||
"webfontloader": "1.6.28",
|
||||
"webpack-cli": "^3.1.0",
|
||||
@@ -228,7 +230,7 @@
|
||||
"rollup-plugin-node-globals": "1.2.1",
|
||||
"rollup-plugin-node-resolve": "^3.0.3",
|
||||
"rollup-plugin-terser": "^2.0.2",
|
||||
"sass-loader": "7.1.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.0.1",
|
||||
"source-map": "0.7.3",
|
||||
"source-map-loader": "0.2.4",
|
||||
|
3
resources/fonts/README.md
Normal file
3
resources/fonts/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Supported font formats
|
||||
|
||||
DSpace supports EOT, TTF, OTF, SVG, WOFF and WOFF2 fonts.
|
@@ -244,6 +244,8 @@
|
||||
|
||||
"collection.create.head": "Create a Collection",
|
||||
|
||||
"collection.create.notifications.success": "Successfully created the Collection",
|
||||
|
||||
"collection.create.sub-head": "Create a Collection for Community {{ parent }}",
|
||||
|
||||
"collection.delete.cancel": "Cancel",
|
||||
@@ -302,6 +304,46 @@
|
||||
|
||||
|
||||
|
||||
"collection.edit.logo.label": "Collection logo",
|
||||
|
||||
"collection.edit.logo.notifications.add.error": "Uploading Collection logo failed. Please verify the content before retrying.",
|
||||
|
||||
"collection.edit.logo.notifications.add.success": "Upload Collection logo successful.",
|
||||
|
||||
"collection.edit.logo.notifications.delete.success.title": "Logo deleted",
|
||||
|
||||
"collection.edit.logo.notifications.delete.success.content": "Successfully deleted the collection's logo",
|
||||
|
||||
"collection.edit.logo.notifications.delete.error.title": "Error deleting logo",
|
||||
|
||||
"collection.edit.logo.upload": "Drop a Collection Logo to upload",
|
||||
|
||||
|
||||
|
||||
"collection.edit.notifications.success": "Successfully edited the Collection",
|
||||
|
||||
"collection.edit.return": "Return",
|
||||
|
||||
|
||||
|
||||
"collection.edit.tabs.curate.head": "Curate",
|
||||
|
||||
"collection.edit.tabs.curate.title": "Collection Edit - Curate",
|
||||
|
||||
"collection.edit.tabs.metadata.head": "Edit Metadata",
|
||||
|
||||
"collection.edit.tabs.metadata.title": "Collection Edit - Metadata",
|
||||
|
||||
"collection.edit.tabs.roles.head": "Assign Roles",
|
||||
|
||||
"collection.edit.tabs.roles.title": "Collection Edit - Roles",
|
||||
|
||||
"collection.edit.tabs.source.head": "Content Source",
|
||||
|
||||
"collection.edit.tabs.source.title": "Collection Edit - Content Source",
|
||||
|
||||
|
||||
|
||||
"collection.form.abstract": "Short Description",
|
||||
|
||||
"collection.form.description": "Introductory text (HTML)",
|
||||
@@ -340,8 +382,18 @@
|
||||
|
||||
|
||||
|
||||
"communityList.tabTitle": "DSpace - Community List",
|
||||
|
||||
"communityList.title": "List of Communities",
|
||||
|
||||
"communityList.showMore": "Show More",
|
||||
|
||||
|
||||
|
||||
"community.create.head": "Create a Community",
|
||||
|
||||
"community.create.notifications.success": "Successfully created the Community",
|
||||
|
||||
"community.create.sub-head": "Create a Sub-Community for Community {{ parent }}",
|
||||
|
||||
"community.delete.cancel": "Cancel",
|
||||
@@ -360,6 +412,44 @@
|
||||
|
||||
"community.edit.head": "Edit Community",
|
||||
|
||||
|
||||
|
||||
"community.edit.logo.label": "Community logo",
|
||||
|
||||
"community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.",
|
||||
|
||||
"community.edit.logo.notifications.add.success": "Upload Community logo successful.",
|
||||
|
||||
"community.edit.logo.notifications.delete.success.title": "Logo deleted",
|
||||
|
||||
"community.edit.logo.notifications.delete.success.content": "Successfully deleted the community's logo",
|
||||
|
||||
"community.edit.logo.notifications.delete.error.title": "Error deleting logo",
|
||||
|
||||
"community.edit.logo.upload": "Drop a Community Logo to upload",
|
||||
|
||||
|
||||
|
||||
"community.edit.notifications.success": "Successfully edited the Community",
|
||||
|
||||
"community.edit.return": "Return",
|
||||
|
||||
|
||||
|
||||
"community.edit.tabs.curate.head": "Curate",
|
||||
|
||||
"community.edit.tabs.curate.title": "Community Edit - Curate",
|
||||
|
||||
"community.edit.tabs.metadata.head": "Edit Metadata",
|
||||
|
||||
"community.edit.tabs.metadata.title": "Community Edit - Metadata",
|
||||
|
||||
"community.edit.tabs.roles.head": "Assign Roles",
|
||||
|
||||
"community.edit.tabs.roles.title": "Community Edit - Roles",
|
||||
|
||||
|
||||
|
||||
"community.form.abstract": "Short Description",
|
||||
|
||||
"community.form.description": "Introductory text (HTML)",
|
||||
@@ -451,6 +541,9 @@
|
||||
"footer.link.duraspace": "DuraSpace",
|
||||
|
||||
|
||||
"form.add": "Add",
|
||||
|
||||
"form.add-help": "Click here to add the current entry and to add another one",
|
||||
|
||||
"form.cancel": "Cancel",
|
||||
|
||||
@@ -476,6 +569,10 @@
|
||||
|
||||
"form.loading": "Loading...",
|
||||
|
||||
"form.lookup": "Lookup",
|
||||
|
||||
"form.lookup-help": "Click here to look up an existing relation",
|
||||
|
||||
"form.no-results": "No results found",
|
||||
|
||||
"form.no-value": "No value entered",
|
||||
@@ -814,9 +911,17 @@
|
||||
|
||||
"item.page.person.search.title": "Articles by this author",
|
||||
|
||||
"item.page.related-items.view-more": "View more",
|
||||
"item.page.related-items.view-more": "Show {{ amount }} more",
|
||||
|
||||
"item.page.related-items.view-less": "View less",
|
||||
"item.page.related-items.view-less": "Hide last {{ amount }}",
|
||||
|
||||
"item.page.relationships.isAuthorOfPublication": "Publications",
|
||||
|
||||
"item.page.relationships.isJournalOfPublication": "Publications",
|
||||
|
||||
"item.page.relationships.isOrgUnitOfPerson": "Authors",
|
||||
|
||||
"item.page.relationships.isOrgUnitOfProject": "Research Projects",
|
||||
|
||||
"item.page.subject": "Keywords",
|
||||
|
||||
@@ -1268,6 +1373,8 @@
|
||||
|
||||
"project.page.titleprefix": "Research Project: ",
|
||||
|
||||
"project.search.results.head": "Project Search Results",
|
||||
|
||||
|
||||
|
||||
"publication.listelement.badge": "Publication",
|
||||
@@ -1343,6 +1450,9 @@
|
||||
"search.filters.applied.f.subject": "Subject",
|
||||
|
||||
"search.filters.applied.f.submitter": "Submitter",
|
||||
"search.filters.applied.f.jobTitle": "Job Title",
|
||||
"search.filters.applied.f.birthDate.max": "End birth date",
|
||||
"search.filters.applied.f.birthDate.min": "Start birth date",
|
||||
|
||||
|
||||
|
||||
@@ -1511,6 +1621,69 @@
|
||||
"submission.general.save-later": "Save for later",
|
||||
|
||||
|
||||
"submission.sections.describe.relationship-lookup.close": "Close",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Deselect page",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.loading": "Loading...",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.placeholder": "Search query",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.search": "Go",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.select-all": "Select all",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.select-page": "Select page",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selected": "Selected {{ size }} items",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Author": "Search for Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Search for Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Search for Journal Issues",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Search for Journal Volumes",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding Agency": "Search for Funding Agencies",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding": "Search for Funding",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.tab-title": "Current Selection ({{ count }})",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal Issue": "Journal Issues",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal Volume": "Journal Volumes",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Journal": "Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Author": "Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding Agency": "Funding Agency",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.title.Funding": "Funding",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.no-selection": "Your selection is currently empty.",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Author": "Selected Authors",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal": "Selected Journals",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal Volume": "Selected Journal Volume",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.selection-tab.title.Journal Issue": "Selected Issue",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
|
||||
|
||||
"submission.sections.describe.relationship-lookup.name-variant.notification.decline": "Use only for this submission",
|
||||
|
||||
"submission.sections.general.add-more": "Add more",
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
||||
import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service';
|
||||
import { FindAllOptions } from '../../../core/data/request.models';
|
||||
import { FindListOptions } from '../../../core/data/request.models';
|
||||
import { map, switchMap, take } from 'rxjs/operators';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
@@ -35,7 +35,7 @@ export class BitstreamFormatsComponent implements OnInit {
|
||||
* The current pagination configuration for the page used by the FindAll method
|
||||
* Currently simply renders all bitstream formats
|
||||
*/
|
||||
config: FindAllOptions = Object.assign(new FindAllOptions(), {
|
||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||
elementsPerPage: 20
|
||||
});
|
||||
|
||||
@@ -145,7 +145,7 @@ export class BitstreamFormatsComponent implements OnInit {
|
||||
* @param event The page change event
|
||||
*/
|
||||
onPageChange(event) {
|
||||
this.config = Object.assign(new FindAllOptions(), this.config, {
|
||||
this.config = Object.assign(new FindListOptions(), this.config, {
|
||||
currentPage: event,
|
||||
});
|
||||
this.pageConfig.currentPage = event;
|
||||
|
@@ -13,7 +13,6 @@ import { combineLatest as combineLatestObservable } from 'rxjs';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
||||
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
||||
import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
|
||||
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
||||
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
||||
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
|
||||
|
@@ -1,9 +1,15 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
||||
import { DynamicFormService, DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
||||
import { NormalizedCollection } from '../../core/cache/models/normalized-collection.model';
|
||||
import { Location } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { ObjectCacheService } from '../../core/cache/object-cache.service';
|
||||
|
||||
/**
|
||||
* Form used for creating and editing collections
|
||||
@@ -22,7 +28,7 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
|
||||
/**
|
||||
* @type {Collection.type} This is a collection-type form
|
||||
*/
|
||||
protected type = Collection.type;
|
||||
type = Collection.type;
|
||||
|
||||
/**
|
||||
* The dynamic form fields used for creating/editing a collection
|
||||
@@ -65,4 +71,15 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
|
||||
name: 'dc.description.provenance',
|
||||
}),
|
||||
];
|
||||
|
||||
public constructor(protected location: Location,
|
||||
protected formService: DynamicFormService,
|
||||
protected translate: TranslateService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected authService: AuthService,
|
||||
protected dsoService: CommunityDataService,
|
||||
protected requestService: RequestService,
|
||||
protected objectCache: ObjectCacheService) {
|
||||
super(location, formService, translate, notificationsService, authService, requestService, objectCache);
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,23 @@
|
||||
import { CollectionItemMapperComponent } from './collection-item-mapper.component';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { SearchFormComponent } from '../../shared/search-form/search-form.component';
|
||||
import { SearchPageModule } from '../../+search-page/search-page.module';
|
||||
import { ObjectCollectionComponent } from '../../shared/object-collection/object-collection.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { RouterStub } from '../../shared/testing/router-stub';
|
||||
import { SearchConfigurationService } from '../../+search-page/search-service/search-configuration.service';
|
||||
import { SearchService } from '../../+search-page/search-service/search.service';
|
||||
import { SearchServiceStub } from '../../shared/testing/search-service-stub';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||
import { ItemDataService } from '../../core/data/item-data.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { EventEmitter, NgModule } from '@angular/core';
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { HostWindowService } from '../../shared/host-window.service';
|
||||
import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
|
||||
import { By } from '@angular/platform-browser';
|
||||
@@ -36,13 +30,14 @@ import { ItemSelectComponent } from '../../shared/object-select/item-select/item
|
||||
import { ObjectSelectService } from '../../shared/object-select/object-select.service';
|
||||
import { ObjectSelectServiceStub } from '../../shared/testing/object-select-service-stub';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { of as observableOf, of } from 'rxjs/internal/observable/of';
|
||||
import { RestResponse } from '../../core/cache/response.models';
|
||||
import { SearchFixedFilterService } from '../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { ErrorComponent } from '../../shared/error/error.component';
|
||||
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
|
||||
describe('CollectionItemMapperComponent', () => {
|
||||
let comp: CollectionItemMapperComponent;
|
||||
@@ -135,7 +130,6 @@ describe('CollectionItemMapperComponent', () => {
|
||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||
{ provide: RouteService, useValue: routeServiceStub },
|
||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
@@ -5,12 +5,9 @@ import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { SearchConfigurationService } from '../../+search-page/search-service/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../core/shared/operators';
|
||||
import { SearchService } from '../../+search-page/search-service/search.service';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
@@ -22,6 +19,9 @@ import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { RestResponse } from '../../core/cache/response.models';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-collection-item-mapper',
|
||||
|
@@ -5,7 +5,6 @@ import { CollectionPageComponent } from './collection-page.component';
|
||||
import { CollectionPageResolver } from './collection-page.resolver';
|
||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||
import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
|
||||
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||
@@ -39,12 +38,8 @@ const COLLECTION_EDIT_PATH = ':id/edit';
|
||||
},
|
||||
{
|
||||
path: COLLECTION_EDIT_PATH,
|
||||
pathMatch: 'full',
|
||||
component: EditCollectionPageComponent,
|
||||
canActivate: [AuthenticatedGuard],
|
||||
resolve: {
|
||||
dso: CollectionPageResolver
|
||||
}
|
||||
loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule',
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: ':id/delete',
|
||||
|
@@ -3,16 +3,19 @@
|
||||
*ngVar="(collectionRD$ | async) as collectionRD">
|
||||
<div *ngIf="collectionRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="collectionRD?.payload as collection">
|
||||
<ds-view-tracker [object]="collection"></ds-view-tracker>
|
||||
<header class="comcol-header border-bottom mb-4 pb-4">
|
||||
<!-- Collection logo -->
|
||||
<ds-comcol-page-logo *ngIf="logoRD$"
|
||||
[logo]="(logoRD$ | async)?.payload" [alternateText]="'Collection Logo'">
|
||||
[alternateText]="'Collection Logo'">
|
||||
</ds-comcol-page-logo>
|
||||
<!-- Collection Name -->
|
||||
<ds-comcol-page-header
|
||||
[name]="collection.name">
|
||||
</ds-comcol-page-header>
|
||||
<!-- Collection logo -->
|
||||
<ds-comcol-page-logo *ngIf="logoRD$"
|
||||
[logo]="(logoRD$ | async)?.payload"
|
||||
[alternateText]="'Collection Logo'"
|
||||
[alternateText]="'Collection Logo'">
|
||||
</ds-comcol-page-logo>
|
||||
|
||||
<!-- Handle -->
|
||||
<ds-comcol-page-handle
|
||||
[content]="collection.handle"
|
||||
|
@@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BehaviorSubject, of as observableOf, Observable, Subject } from 'rxjs';
|
||||
import { filter, flatMap, map, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||
import { PaginatedList } from '../core/data/paginated-list';
|
||||
|
@@ -7,29 +7,30 @@ import { CollectionPageComponent } from './collection-page.component';
|
||||
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
||||
import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
|
||||
import { CollectionFormComponent } from './collection-form/collection-form.component';
|
||||
import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
|
||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
CollectionPageRoutingModule
|
||||
CollectionPageRoutingModule,
|
||||
StatisticsModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
CollectionPageComponent,
|
||||
CreateCollectionPageComponent,
|
||||
EditCollectionPageComponent,
|
||||
DeleteCollectionPageComponent,
|
||||
CollectionFormComponent,
|
||||
CollectionItemMapperComponent
|
||||
],
|
||||
exports: [
|
||||
CollectionFormComponent
|
||||
],
|
||||
providers: [
|
||||
SearchService,
|
||||
SearchFixedFilterService
|
||||
]
|
||||
})
|
||||
export class CollectionPageModule {
|
||||
|
@@ -4,5 +4,5 @@
|
||||
<h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: (parentRD$| async)?.payload.name } }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<ds-collection-form (submitForm)="onSubmit($event)"></ds-collection-form>
|
||||
<ds-collection-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-collection-form>
|
||||
</div>
|
||||
|
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { CreateCollectionPageComponent } from './create-collection-page.component';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||
|
||||
describe('CreateCollectionPageComponent', () => {
|
||||
let comp: CreateCollectionPageComponent;
|
||||
@@ -27,6 +29,7 @@ describe('CreateCollectionPageComponent', () => {
|
||||
},
|
||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||
{ provide: Router, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
@@ -5,6 +5,8 @@ import { Router } from '@angular/router';
|
||||
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Component that represents the page where a user can create a new Collection
|
||||
@@ -16,13 +18,16 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
})
|
||||
export class CreateCollectionPageComponent extends CreateComColPageComponent<Collection> {
|
||||
protected frontendURL = '/collections/';
|
||||
protected type = Collection.type;
|
||||
|
||||
public constructor(
|
||||
protected communityDataService: CommunityDataService,
|
||||
protected collectionDataService: CollectionDataService,
|
||||
protected routeService: RouteService,
|
||||
protected router: Router
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService
|
||||
) {
|
||||
super(collectionDataService, communityDataService, routeService, router);
|
||||
super(collectionDataService, communityDataService, routeService, router, notificationsService, translate);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for managing a collection's curation tasks
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-collection-curate',
|
||||
templateUrl: './collection-curate.component.html',
|
||||
})
|
||||
export class CollectionCurateComponent {
|
||||
/* TODO: Implement Collection Edit - Curate */
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<ds-collection-form (submitForm)="onSubmit($event)"
|
||||
[dso]="(dsoRD$ | async)?.payload"
|
||||
(finish)="navigateToHomePage()"></ds-collection-form>
|
||||
<a class="btn btn-danger"
|
||||
[routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
|
||||
| translate}}</a>
|
@@ -0,0 +1,42 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { CollectionMetadataComponent } from './collection-metadata.component';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
|
||||
describe('CollectionMetadataComponent', () => {
|
||||
let comp: CollectionMetadataComponent;
|
||||
let fixture: ComponentFixture<CollectionMetadataComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||
declarations: [CollectionMetadataComponent],
|
||||
providers: [
|
||||
{ provide: CollectionDataService, useValue: {} },
|
||||
{ provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CollectionMetadataComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('frontendURL', () => {
|
||||
it('should have the right frontendURL set', () => {
|
||||
expect((comp as any).frontendURL).toEqual('/collections/');
|
||||
})
|
||||
});
|
||||
});
|
@@ -0,0 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Component for editing a collection's metadata
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-collection-metadata',
|
||||
templateUrl: './collection-metadata.component.html',
|
||||
})
|
||||
export class CollectionMetadataComponent extends ComcolMetadataComponent<Collection> {
|
||||
protected frontendURL = '/collections/';
|
||||
protected type = Collection.type;
|
||||
|
||||
public constructor(
|
||||
protected collectionDataService: CollectionDataService,
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService
|
||||
) {
|
||||
super(collectionDataService, router, route, notificationsService, translate);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for managing a collection's roles
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-collection-roles',
|
||||
templateUrl: './collection-roles.component.html',
|
||||
})
|
||||
export class CollectionRolesComponent {
|
||||
/* TODO: Implement Collection Edit - Roles */
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for managing the content source of the collection
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-collection-source',
|
||||
templateUrl: './collection-source.component.html',
|
||||
})
|
||||
export class CollectionSourceComponent {
|
||||
/* TODO: Implement Collection Edit - Content Source */
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 pb-4">
|
||||
<h2 id="header" class="border-bottom pb-2">{{ 'collection.edit.head' | translate }}</h2>
|
||||
<ds-collection-form (submitForm)="onSubmit($event)" [dso]="(dsoRD$ | async)?.payload"></ds-collection-form>
|
||||
<a class="btn btn-danger"
|
||||
[routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
|
||||
| translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1 +0,0 @@
|
||||
|
@@ -13,13 +13,29 @@ describe('EditCollectionPageComponent', () => {
|
||||
let comp: EditCollectionPageComponent;
|
||||
let fixture: ComponentFixture<EditCollectionPageComponent>;
|
||||
|
||||
const routeStub = {
|
||||
data: observableOf({
|
||||
dso: { payload: {} }
|
||||
}),
|
||||
routeConfig: {
|
||||
children: []
|
||||
},
|
||||
snapshot: {
|
||||
firstChild: {
|
||||
routeConfig: {
|
||||
path: 'mockUrl'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||
declarations: [EditCollectionPageComponent],
|
||||
providers: [
|
||||
{ provide: CollectionDataService, useValue: {} },
|
||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
||||
{ provide: ActivatedRoute, useValue: routeStub },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
@@ -31,9 +47,9 @@ describe('EditCollectionPageComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('frontendURL', () => {
|
||||
it('should have the right frontendURL set', () => {
|
||||
expect((comp as any).frontendURL).toEqual('/collections/');
|
||||
describe('type', () => {
|
||||
it('should have the right type set', () => {
|
||||
expect((comp as any).type).toEqual('collection');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@@ -2,24 +2,30 @@ import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
import { getCollectionPageRoute } from '../collection-page-routing.module';
|
||||
|
||||
/**
|
||||
* Component that represents the page where a user can edit an existing Collection
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-edit-collection',
|
||||
styleUrls: ['./edit-collection-page.component.scss'],
|
||||
templateUrl: './edit-collection-page.component.html'
|
||||
templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
|
||||
})
|
||||
export class EditCollectionPageComponent extends EditComColPageComponent<Collection> {
|
||||
protected frontendURL = '/collections/';
|
||||
type = 'collection';
|
||||
|
||||
public constructor(
|
||||
protected collectionDataService: CollectionDataService,
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute
|
||||
) {
|
||||
super(collectionDataService, router, route);
|
||||
super(router, route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection page url
|
||||
* @param collection The collection for which the url is requested
|
||||
*/
|
||||
getPageUrl(collection: Collection): string {
|
||||
return getCollectionPageRoute(collection.id)
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,32 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EditCollectionPageComponent } from './edit-collection-page.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { EditCollectionPageRoutingModule } from './edit-collection-page.routing.module';
|
||||
import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
|
||||
import { CollectionPageModule } from '../collection-page.module';
|
||||
import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
|
||||
import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
|
||||
import { CollectionSourceComponent } from './collection-source/collection-source.component';
|
||||
|
||||
/**
|
||||
* Module that contains all components related to the Edit Collection page administrator functionality
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
EditCollectionPageRoutingModule,
|
||||
CollectionPageModule
|
||||
],
|
||||
declarations: [
|
||||
EditCollectionPageComponent,
|
||||
CollectionMetadataComponent,
|
||||
CollectionRolesComponent,
|
||||
CollectionCurateComponent,
|
||||
CollectionSourceComponent
|
||||
]
|
||||
})
|
||||
export class EditCollectionPageModule {
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EditCollectionPageComponent } from './edit-collection-page.component';
|
||||
import { CollectionPageResolver } from '../collection-page.resolver';
|
||||
import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
|
||||
import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
|
||||
import { CollectionSourceComponent } from './collection-source/collection-source.component';
|
||||
import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
|
||||
|
||||
/**
|
||||
* Routing module that handles the routing for the Edit Collection page administrator functionality
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: EditCollectionPageComponent,
|
||||
resolve: {
|
||||
dso: CollectionPageResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'metadata',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'metadata',
|
||||
component: CollectionMetadataComponent,
|
||||
data: {
|
||||
title: 'collection.edit.tabs.metadata.title',
|
||||
hideReturnButton: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'roles',
|
||||
component: CollectionRolesComponent,
|
||||
data: { title: 'collection.edit.tabs.roles.title' }
|
||||
},
|
||||
{
|
||||
path: 'source',
|
||||
component: CollectionSourceComponent,
|
||||
data: { title: 'collection.edit.tabs.source.title' }
|
||||
},
|
||||
{
|
||||
path: 'curate',
|
||||
component: CollectionCurateComponent,
|
||||
data: { title: 'collection.edit.tabs.curate.title' }
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
CollectionPageResolver,
|
||||
]
|
||||
})
|
||||
export class EditCollectionPageRoutingModule {
|
||||
|
||||
}
|
@@ -1,9 +1,16 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
||||
import { DynamicFormService, DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
|
||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
||||
import { Community } from '../../core/shared/community.model';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
|
||||
import { Location } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { ObjectCacheService } from '../../core/cache/object-cache.service';
|
||||
|
||||
/**
|
||||
* Form used for creating and editing communities
|
||||
@@ -22,7 +29,7 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
|
||||
/**
|
||||
* @type {Community.type} This is a community-type form
|
||||
*/
|
||||
protected type = Community.type;
|
||||
type = Community.type;
|
||||
|
||||
/**
|
||||
* The dynamic form fields used for creating/editing a community
|
||||
@@ -57,4 +64,15 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
|
||||
name: 'dc.description.tableofcontents',
|
||||
}),
|
||||
];
|
||||
|
||||
public constructor(protected location: Location,
|
||||
protected formService: DynamicFormService,
|
||||
protected translate: TranslateService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected authService: AuthService,
|
||||
protected dsoService: CommunityDataService,
|
||||
protected requestService: RequestService,
|
||||
protected objectCache: ObjectCacheService) {
|
||||
super(location, formService, translate, notificationsService, authService, requestService, objectCache);
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import { CommunityPageComponent } from './community-page.component';
|
||||
import { CommunityPageResolver } from './community-page.resolver';
|
||||
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||
import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
|
||||
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||
@@ -38,12 +37,8 @@ const COMMUNITY_EDIT_PATH = ':id/edit';
|
||||
},
|
||||
{
|
||||
path: COMMUNITY_EDIT_PATH,
|
||||
pathMatch: 'full',
|
||||
component: EditCommunityPageComponent,
|
||||
canActivate: [AuthenticatedGuard],
|
||||
resolve: {
|
||||
dso: CommunityPageResolver
|
||||
}
|
||||
loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule',
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: ':id/delete',
|
||||
|
@@ -1,13 +1,13 @@
|
||||
<div class="container" *ngVar="(communityRD$ | async) as communityRD">
|
||||
<div class="community-page" *ngIf="communityRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="communityRD?.payload; let communityPayload">
|
||||
<ds-view-tracker [object]="communityPayload"></ds-view-tracker>
|
||||
<header class="comcol-header border-bottom mb-4 pb-4">
|
||||
<!-- Community name -->
|
||||
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
||||
<!-- Community logo -->
|
||||
<ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'">
|
||||
</ds-comcol-page-logo>
|
||||
|
||||
<!-- Community name -->
|
||||
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
|
||||
<!-- Handle -->
|
||||
<ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'">
|
||||
</ds-comcol-page-handle>
|
||||
|
@@ -9,23 +9,26 @@ import { CommunityPageRoutingModule } from './community-page-routing.module';
|
||||
import { CommunityPageSubCommunityListComponent } from './sub-community-list/community-page-sub-community-list.component';
|
||||
import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
|
||||
import { CommunityFormComponent } from './community-form/community-form.component';
|
||||
import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
|
||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
CommunityPageRoutingModule
|
||||
CommunityPageRoutingModule,
|
||||
StatisticsModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
CommunityPageComponent,
|
||||
CommunityPageSubCollectionListComponent,
|
||||
CommunityPageSubCommunityListComponent,
|
||||
CreateCommunityPageComponent,
|
||||
EditCommunityPageComponent,
|
||||
DeleteCommunityPageComponent,
|
||||
CommunityFormComponent
|
||||
],
|
||||
exports: [
|
||||
CommunityFormComponent
|
||||
]
|
||||
})
|
||||
|
||||
|
@@ -7,5 +7,5 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<ds-community-form (submitForm)="onSubmit($event)"></ds-community-form>
|
||||
<ds-community-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-community-form>
|
||||
</div>
|
||||
|
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { CreateCommunityPageComponent } from './create-community-page.component';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
|
||||
|
||||
describe('CreateCommunityPageComponent', () => {
|
||||
let comp: CreateCommunityPageComponent;
|
||||
@@ -23,6 +25,7 @@ describe('CreateCommunityPageComponent', () => {
|
||||
{ provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
|
||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||
{ provide: Router, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
@@ -4,6 +4,8 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Component that represents the page where a user can create a new Community
|
||||
@@ -15,12 +17,15 @@ import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comc
|
||||
})
|
||||
export class CreateCommunityPageComponent extends CreateComColPageComponent<Community> {
|
||||
protected frontendURL = '/communities/';
|
||||
protected type = Community.type;
|
||||
|
||||
public constructor(
|
||||
protected communityDataService: CommunityDataService,
|
||||
protected routeService: RouteService,
|
||||
protected router: Router
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService
|
||||
) {
|
||||
super(communityDataService, communityDataService, routeService, router);
|
||||
super(communityDataService, communityDataService, routeService, router, notificationsService, translate);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for managing a community's curation tasks
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-community-curate',
|
||||
templateUrl: './community-curate.component.html',
|
||||
})
|
||||
export class CommunityCurateComponent {
|
||||
/* TODO: Implement Community Edit - Curate */
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<ds-community-form (submitForm)="onSubmit($event)"
|
||||
[dso]="(dsoRD$ | async)?.payload"
|
||||
(finish)="navigateToHomePage()"></ds-community-form>
|
||||
<a class="btn btn-danger"
|
||||
[routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
|
||||
| translate}}</a>
|
@@ -0,0 +1,42 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { CommunityMetadataComponent } from './community-metadata.component';
|
||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
|
||||
describe('CommunityMetadataComponent', () => {
|
||||
let comp: CommunityMetadataComponent;
|
||||
let fixture: ComponentFixture<CommunityMetadataComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||
declarations: [CommunityMetadataComponent],
|
||||
providers: [
|
||||
{ provide: CommunityDataService, useValue: {} },
|
||||
{ provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CommunityMetadataComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('frontendURL', () => {
|
||||
it('should have the right frontendURL set', () => {
|
||||
expect((comp as any).frontendURL).toEqual('/communities/');
|
||||
})
|
||||
});
|
||||
});
|
@@ -0,0 +1,29 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Community } from '../../../core/shared/community.model';
|
||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Component for editing a community's metadata
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-community-metadata',
|
||||
templateUrl: './community-metadata.component.html',
|
||||
})
|
||||
export class CommunityMetadataComponent extends ComcolMetadataComponent<Community> {
|
||||
protected frontendURL = '/communities/';
|
||||
protected type = Community.type;
|
||||
|
||||
public constructor(
|
||||
protected communityDataService: CommunityDataService,
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService
|
||||
) {
|
||||
super(communityDataService, router, route, notificationsService, translate);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component for managing a community's roles
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-community-roles',
|
||||
templateUrl: './community-roles.component.html',
|
||||
})
|
||||
export class CommunityRolesComponent {
|
||||
/* TODO: Implement Community Edit - Roles */
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 pb-4">
|
||||
<h2 id="header" class="border-bottom pb-2">{{ 'community.edit.head' | translate }}</h2>
|
||||
<ds-community-form (submitForm)="onSubmit($event)"
|
||||
[dso]="(dsoRD$ | async)?.payload"></ds-community-form>
|
||||
<a class="btn btn-danger"
|
||||
[routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
|
||||
| translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1 +0,0 @@
|
||||
|
@@ -13,13 +13,29 @@ describe('EditCommunityPageComponent', () => {
|
||||
let comp: EditCommunityPageComponent;
|
||||
let fixture: ComponentFixture<EditCommunityPageComponent>;
|
||||
|
||||
const routeStub = {
|
||||
data: observableOf({
|
||||
dso: { payload: {} }
|
||||
}),
|
||||
routeConfig: {
|
||||
children: []
|
||||
},
|
||||
snapshot: {
|
||||
firstChild: {
|
||||
routeConfig: {
|
||||
path: 'mockUrl'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||
declarations: [EditCommunityPageComponent],
|
||||
providers: [
|
||||
{ provide: CommunityDataService, useValue: {} },
|
||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
||||
{ provide: ActivatedRoute, useValue: routeStub },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
@@ -31,9 +47,9 @@ describe('EditCommunityPageComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('frontendURL', () => {
|
||||
it('should have the right frontendURL set', () => {
|
||||
expect((comp as any).frontendURL).toEqual('/communities/');
|
||||
describe('type', () => {
|
||||
it('should have the right type set', () => {
|
||||
expect((comp as any).type).toEqual('community');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@@ -1,25 +1,31 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Community } from '../../core/shared/community.model';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||
import { getCommunityPageRoute } from '../community-page-routing.module';
|
||||
|
||||
/**
|
||||
* Component that represents the page where a user can edit an existing Community
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-edit-community',
|
||||
styleUrls: ['./edit-community-page.component.scss'],
|
||||
templateUrl: './edit-community-page.component.html'
|
||||
templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
|
||||
})
|
||||
export class EditCommunityPageComponent extends EditComColPageComponent<Community> {
|
||||
protected frontendURL = '/communities/';
|
||||
type = 'community';
|
||||
|
||||
public constructor(
|
||||
protected communityDataService: CommunityDataService,
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute
|
||||
) {
|
||||
super(communityDataService, router, route);
|
||||
super(router, route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the community page url
|
||||
* @param community The community for which the url is requested
|
||||
*/
|
||||
getPageUrl(community: Community): string {
|
||||
return getCommunityPageRoute(community.id)
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { EditCommunityPageRoutingModule } from './edit-community-page.routing.module';
|
||||
import { CommunityPageModule } from '../community-page.module';
|
||||
import { EditCommunityPageComponent } from './edit-community-page.component';
|
||||
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
||||
import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
|
||||
import { CommunityRolesComponent } from './community-roles/community-roles.component';
|
||||
|
||||
/**
|
||||
* Module that contains all components related to the Edit Community page administrator functionality
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
EditCommunityPageRoutingModule,
|
||||
CommunityPageModule
|
||||
],
|
||||
declarations: [
|
||||
EditCommunityPageComponent,
|
||||
CommunityCurateComponent,
|
||||
CommunityMetadataComponent,
|
||||
CommunityRolesComponent
|
||||
]
|
||||
})
|
||||
export class EditCommunityPageModule {
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import { CommunityPageResolver } from '../community-page.resolver';
|
||||
import { EditCommunityPageComponent } from './edit-community-page.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
|
||||
import { CommunityRolesComponent } from './community-roles/community-roles.component';
|
||||
import { CommunityCurateComponent } from './community-curate/community-curate.component';
|
||||
|
||||
/**
|
||||
* Routing module that handles the routing for the Edit Community page administrator functionality
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: EditCommunityPageComponent,
|
||||
resolve: {
|
||||
dso: CommunityPageResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'metadata',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'metadata',
|
||||
component: CommunityMetadataComponent,
|
||||
data: {
|
||||
title: 'community.edit.tabs.metadata.title',
|
||||
hideReturnButton: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'roles',
|
||||
component: CommunityRolesComponent,
|
||||
data: { title: 'community.edit.tabs.roles.title' }
|
||||
},
|
||||
{
|
||||
path: 'curate',
|
||||
component: CommunityCurateComponent,
|
||||
data: { title: 'community.edit.tabs.curate.title' }
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
CommunityPageResolver,
|
||||
]
|
||||
})
|
||||
export class EditCommunityPageRoutingModule {
|
||||
|
||||
}
|
@@ -2,12 +2,25 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { HomePageComponent } from './home-page.component';
|
||||
import { HomePageResolver } from './home-page.resolver';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{ path: '', component: HomePageComponent, pathMatch: 'full', data: { title: 'home.title' } }
|
||||
{
|
||||
path: '',
|
||||
component: HomePageComponent,
|
||||
pathMatch: 'full',
|
||||
data: {title: 'home.title'},
|
||||
resolve: {
|
||||
site: HomePageResolver
|
||||
}
|
||||
}
|
||||
])
|
||||
],
|
||||
providers: [
|
||||
HomePageResolver
|
||||
]
|
||||
})
|
||||
export class HomePageRoutingModule { }
|
||||
export class HomePageRoutingModule {
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
<ds-home-news></ds-home-news>
|
||||
<div class="container">
|
||||
<ng-container *ngIf="(site$ | async) as site">
|
||||
<ds-view-tracker [object]="site"></ds-view-tracker>
|
||||
</ng-container>
|
||||
<ds-search-form [inPlaceSearch]="false"></ds-search-form>
|
||||
<ds-top-level-community-list></ds-top-level-community-list>
|
||||
</div>
|
||||
|
@@ -1,9 +1,26 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Site } from '../core/shared/site.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-home-page',
|
||||
styleUrls: ['./home-page.component.scss'],
|
||||
templateUrl: './home-page.component.html'
|
||||
})
|
||||
export class HomePageComponent {
|
||||
export class HomePageComponent implements OnInit {
|
||||
|
||||
site$:Observable<Site>;
|
||||
|
||||
constructor(
|
||||
private route:ActivatedRoute,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.site$ = this.route.data.pipe(
|
||||
map((data) => data.site as Site),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,14 @@ import { HomePageRoutingModule } from './home-page-routing.module';
|
||||
|
||||
import { HomePageComponent } from './home-page.component';
|
||||
import { TopLevelCommunityListComponent } from './top-level-community-list/top-level-community-list.component';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
HomePageRoutingModule
|
||||
HomePageRoutingModule,
|
||||
StatisticsModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
HomePageComponent,
|
||||
|
25
src/app/+home-page/home-page.resolver.ts
Normal file
25
src/app/+home-page/home-page.resolver.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { SiteDataService } from '../core/data/site-data.service';
|
||||
import { Site } from '../core/shared/site.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* The class that resolve the Site object for a route
|
||||
*/
|
||||
@Injectable()
|
||||
export class HomePageResolver implements Resolve<Site> {
|
||||
constructor(private siteService:SiteDataService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for resolving a site object
|
||||
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||
* @returns Observable<Site> Emits the found Site object, or an error if something went wrong
|
||||
*/
|
||||
resolve(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<Site> | Promise<Site> | Site {
|
||||
return this.siteService.find().pipe(take(1));
|
||||
}
|
||||
}
|
@@ -1,15 +1,12 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||
@@ -19,7 +16,6 @@ import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
import { HostWindowService } from '../../../shared/host-window.service';
|
||||
@@ -28,7 +24,6 @@ import { By } from '@angular/platform-browser';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
||||
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
|
||||
@@ -39,6 +34,9 @@ import { SearchFormComponent } from '../../../shared/search-form/search-form.com
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { ErrorComponent } from '../../../shared/error/error.component';
|
||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
|
||||
describe('ItemCollectionMapperComponent', () => {
|
||||
let comp: ItemCollectionMapperComponent;
|
||||
|
@@ -2,15 +2,12 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
|
||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@@ -19,6 +16,9 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'
|
||||
import { isNotEmpty } from '../../../shared/empty.util';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-collection-mapper',
|
||||
|
@@ -9,7 +9,6 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ItemMoveComponent } from './item-move.component';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
@@ -18,6 +17,7 @@ import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
|
||||
describe('ItemMoveComponent', () => {
|
||||
let comp: ItemMoveComponent;
|
||||
|
@@ -1,12 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
||||
import { SearchOptions } from '../../../+search-page/search-options.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
@@ -17,9 +14,10 @@ import { getItemEditPath } from '../../item-page-routing.module';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { RestResponse } from '../../../core/cache/response.models';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { tap } from 'rxjs/internal/operators/tap';
|
||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../../../core/shared/search/search.service';
|
||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||
import { SearchResult } from '../../../shared/search/search-result.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-move',
|
||||
|
@@ -12,8 +12,6 @@ import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {CoreState} from '../../../../core/core.reducers';
|
||||
|
||||
let objectUpdatesService;
|
||||
const url = 'http://test-url.com/test-url';
|
||||
@@ -95,9 +93,11 @@ describe('EditRelationshipComponent', () => {
|
||||
itemSelection[relatedItem.uuid] = false;
|
||||
itemSelection[item.uuid] = true;
|
||||
|
||||
const store = new Store<CoreState>(undefined, undefined, undefined);
|
||||
|
||||
objectUpdatesService = new ObjectUpdatesService(store);
|
||||
objectUpdatesService = {
|
||||
isSelectedVirtualMetadata: () => null,
|
||||
removeSingleFieldUpdate: () => null,
|
||||
saveRemoveFieldUpdate: () => null,
|
||||
};
|
||||
|
||||
spyOn(objectUpdatesService, 'isSelectedVirtualMetadata').and.callFake((a, b, uuid) => observableOf(itemSelection[uuid]));
|
||||
|
||||
@@ -178,18 +178,18 @@ describe('EditRelationshipComponent', () => {
|
||||
});
|
||||
|
||||
it('should close the virtual metadata modal and call saveRemoveFieldUpdate with the correct arguments', () => {
|
||||
fixture.whenStable(() => {
|
||||
expect(comp.closeVirtualMetadataModal).toHaveBeenCalled();
|
||||
expect(objectUpdatesService.saveRemoveFieldUpdate).toHaveBeenCalledWith(url, Object.assign({}, fieldUpdate1.field, {
|
||||
keepLeftVirtualMetadata: false,
|
||||
keepRightVirtualMetadata: true,
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('undo', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(objectUpdatesService, 'removeSingleFieldUpdate');
|
||||
comp.undo();
|
||||
comp.ngOnChanges();
|
||||
});
|
||||
@@ -197,7 +197,8 @@ describe('EditRelationshipComponent', () => {
|
||||
it('should call removeSingleFieldUpdate with the correct arguments', () => {
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(objectUpdatesService.removeSingleFieldUpdate).toHaveBeenCalledWith(url, fieldUpdate1[0]);
|
||||
expect(spyOn(objectUpdatesService, 'removeSingleFieldUpdate'))
|
||||
.toHaveBeenCalledWith(url, fieldUpdate1[0]);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@@ -13,9 +13,8 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {NotificationsService} from '../../../shared/notifications/notifications.service';
|
||||
import {GLOBAL_CONFIG} from '../../../../config';
|
||||
import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import {Relationship} from '../../../core/shared/item-relationships/relationship.model';
|
||||
import { of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import {combineLatest as observableCombineLatest, of as observableOf} from 'rxjs';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {Item} from '../../../core/shared/item.model';
|
||||
import {PaginatedList} from '../../../core/data/paginated-list';
|
||||
@@ -26,6 +25,8 @@ import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||
import {getTestScheduler} from 'jasmine-marbles';
|
||||
import {RestResponse} from '../../../core/cache/response.models';
|
||||
import {RequestService} from '../../../core/data/request.service';
|
||||
import {EntityTypeService} from '../../../core/data/entity-type.service';
|
||||
import {ItemType} from '../../../core/shared/item-relationships/item-type.model';
|
||||
|
||||
let comp: any;
|
||||
let fixture: ComponentFixture<ItemRelationshipsComponent>;
|
||||
@@ -34,6 +35,7 @@ let el: HTMLElement;
|
||||
let objectUpdatesService;
|
||||
let relationshipService;
|
||||
let requestService;
|
||||
let entityTypeService;
|
||||
let objectCache;
|
||||
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
|
||||
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
|
||||
@@ -58,6 +60,7 @@ let author1;
|
||||
let author2;
|
||||
let fieldUpdate1;
|
||||
let fieldUpdate2;
|
||||
let entityType;
|
||||
let relationships;
|
||||
let relationshipType;
|
||||
|
||||
@@ -95,6 +98,10 @@ describe('ItemRelationshipsComponent', () => {
|
||||
lastModified: date
|
||||
});
|
||||
|
||||
entityType = Object.assign(new ItemType(), {
|
||||
id: 'entityType',
|
||||
});
|
||||
|
||||
author1 = Object.assign(new Item(), {
|
||||
id: 'author1',
|
||||
uuid: 'author1'
|
||||
@@ -159,7 +166,9 @@ describe('ItemRelationshipsComponent', () => {
|
||||
getRelatedItemsByLabel: observableOf([author1, author2]),
|
||||
getItemRelationshipsArray: observableOf(relationships),
|
||||
deleteRelationship: observableOf(new RestResponse(true, 200, 'OK')),
|
||||
getItemResolvedRelatedItemsAndRelationships: observableCombineLatest(observableOf([author1, author2]), observableOf([item, item]), observableOf(relationships))
|
||||
getItemResolvedRelatedItemsAndRelationships: observableCombineLatest(observableOf([author1, author2]), observableOf([item, item]), observableOf(relationships)),
|
||||
getRelationshipsByRelatedItemIds: observableOf(relationships),
|
||||
getRelationshipTypeLabelsByItem: observableOf([relationshipType.leftwardType])
|
||||
}
|
||||
);
|
||||
|
||||
@@ -174,6 +183,25 @@ describe('ItemRelationshipsComponent', () => {
|
||||
remove: undefined
|
||||
});
|
||||
|
||||
entityTypeService = jasmine.createSpyObj('entityTypeService',
|
||||
{
|
||||
getEntityTypeByLabel: observableOf(new RemoteData(
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
entityType,
|
||||
)),
|
||||
getEntityTypeRelationships: observableOf(new RemoteData(
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
new PaginatedList(new PageInfo(), [relationshipType]),
|
||||
)),
|
||||
}
|
||||
);
|
||||
|
||||
scheduler = getTestScheduler();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [SharedModule, TranslateModule.forRoot()],
|
||||
@@ -186,6 +214,7 @@ describe('ItemRelationshipsComponent', () => {
|
||||
{ provide: NotificationsService, useValue: notificationsService },
|
||||
{ provide: GLOBAL_CONFIG, useValue: { item: { edit: { undoTimeout: 10 } } } as any },
|
||||
{ provide: RelationshipService, useValue: relationshipService },
|
||||
{ provide: EntityTypeService, useValue: entityTypeService },
|
||||
{ provide: ObjectCacheService, useValue: objectCache },
|
||||
{ provide: RequestService, useValue: requestService },
|
||||
ChangeDetectorRef
|
||||
|
@@ -1,13 +1,9 @@
|
||||
import {ChangeDetectorRef, Component, Inject, OnDestroy} from '@angular/core';
|
||||
import {Item} from '../../../core/shared/item.model';
|
||||
import {
|
||||
DeleteRelationship,
|
||||
FieldUpdate,
|
||||
FieldUpdates
|
||||
} from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import {DeleteRelationship, FieldUpdate, FieldUpdates} from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import {Observable} from 'rxjs/internal/Observable';
|
||||
import {filter, map, switchMap, take, tap} from 'rxjs/operators';
|
||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||
import {filter, map, switchMap, take} from 'rxjs/operators';
|
||||
import {zip as observableZip} from 'rxjs';
|
||||
import {AbstractItemUpdateComponent} from '../abstract-item-update/abstract-item-update.component';
|
||||
import {ItemDataService} from '../../../core/data/item-data.service';
|
||||
import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
|
||||
@@ -16,10 +12,7 @@ import { NotificationsService } from '../../../shared/notifications/notification
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {GLOBAL_CONFIG, GlobalConfig} from '../../../../config';
|
||||
import {RelationshipService} from '../../../core/data/relationship.service';
|
||||
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
|
||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||
import {ErrorResponse, RestResponse} from '../../../core/cache/response.models';
|
||||
import { isNotEmptyOperator } from '../../../shared/empty.util';
|
||||
import {RemoteData} from '../../../core/data/remote-data';
|
||||
import {ObjectCacheService} from '../../../core/cache/object-cache.service';
|
||||
import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators';
|
||||
@@ -28,6 +21,9 @@ import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model';
|
||||
import {ItemType} from '../../../core/shared/item-relationships/item-type.model';
|
||||
import {EntityTypeService} from '../../../core/data/entity-type.service';
|
||||
import {isNotEmptyOperator} from '../../../shared/empty.util';
|
||||
import {FieldChangeType} from '../../../core/data/object-updates/object-updates.actions';
|
||||
import {Relationship} from '../../../core/shared/item-relationships/relationship.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-relationships',
|
||||
@@ -63,6 +59,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected entityTypeService: EntityTypeService,
|
||||
protected cdr: ChangeDetectorRef,
|
||||
) {
|
||||
super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route);
|
||||
}
|
||||
@@ -78,6 +75,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
getSucceededRemoteData(),
|
||||
).subscribe((itemRD: RemoteData<Item>) => {
|
||||
this.item = itemRD.payload;
|
||||
this.cdr.detectChanges();
|
||||
this.initializeUpdates();
|
||||
});
|
||||
}
|
||||
@@ -118,7 +116,7 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
*/
|
||||
public submit(): void {
|
||||
// Get all the relationships that should be removed
|
||||
const removedRelationshipIDs$ = this.relationshipService.getItemRelationshipsArray(this.item).pipe(
|
||||
this.relationshipService.getItemRelationshipsArray(this.item).pipe(
|
||||
map((relationships: Relationship[]) => relationships.map((relationship) =>
|
||||
Object.assign(new Relationship(), relationship, {uuid: relationship.id})
|
||||
)),
|
||||
@@ -131,8 +129,6 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
||||
.map((fieldUpdate: FieldUpdate) => fieldUpdate.field as DeleteRelationship)
|
||||
),
|
||||
isNotEmptyOperator(),
|
||||
);
|
||||
removedRelationshipIDs$.pipe(
|
||||
take(1),
|
||||
switchMap((deleteRelationships: DeleteRelationship[]) =>
|
||||
observableZip(...deleteRelationships.map((deleteRelationship) => {
|
||||
|
@@ -11,7 +11,7 @@
|
||||
class="d-flex flex-row">
|
||||
<div class="m-2">
|
||||
<label>
|
||||
<input class="select" type="checkbox">
|
||||
<input class="select" type="checkbox" [checked]="selected">
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-column">
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {of as observableOf} from 'rxjs/internal/observable/of';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';
|
||||
@@ -8,7 +8,7 @@ import {Item} from '../../../core/shared/item.model';
|
||||
import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
|
||||
import {VarDirective} from '../../../shared/utils/var.directive';
|
||||
|
||||
fdescribe('VirtualMetadataComponent', () => {
|
||||
describe('VirtualMetadataComponent', () => {
|
||||
|
||||
let comp: VirtualMetadataComponent;
|
||||
let fixture: ComponentFixture<VirtualMetadataComponent>;
|
||||
@@ -50,6 +50,7 @@ fdescribe('VirtualMetadataComponent', () => {
|
||||
NO_ERRORS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(VirtualMetadataComponent);
|
||||
comp = fixture.componentInstance;
|
||||
de = fixture.debugElement;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
|
||||
<div class="simple-view-link my-3">
|
||||
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id]">
|
||||
|
@@ -26,6 +26,9 @@ import { MetadataRepresentationListComponent } from './simple/metadata-represent
|
||||
import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component';
|
||||
import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
|
||||
import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||
import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
import { AbstractIncrementalListComponent } from './simple/abstract-incremental-list/abstract-incremental-list.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -33,7 +36,8 @@ import { MetadataFieldWrapperComponent } from './field-components/metadata-field
|
||||
SharedModule,
|
||||
ItemPageRoutingModule,
|
||||
EditItemPageModule,
|
||||
SearchPageModule
|
||||
SearchPageModule,
|
||||
StatisticsModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
ItemPageComponent,
|
||||
@@ -53,7 +57,9 @@ import { MetadataFieldWrapperComponent } from './field-components/metadata-field
|
||||
ItemComponent,
|
||||
GenericItemPageFieldComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
RelatedEntitiesSearchComponent
|
||||
RelatedEntitiesSearchComponent,
|
||||
TabbedRelatedEntitiesSearchComponent,
|
||||
AbstractIncrementalListComponent
|
||||
],
|
||||
exports: [
|
||||
ItemComponent,
|
||||
@@ -63,7 +69,8 @@ import { MetadataFieldWrapperComponent } from './field-components/metadata-field
|
||||
RelatedEntitiesSearchComponent,
|
||||
RelatedItemsComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
ItemPageTitleFieldComponent
|
||||
ItemPageTitleFieldComponent,
|
||||
TabbedRelatedEntitiesSearchComponent
|
||||
],
|
||||
entryComponents: [
|
||||
PublicationComponent
|
||||
|
@@ -0,0 +1,73 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-abstract-incremental-list',
|
||||
template: ``,
|
||||
})
|
||||
/**
|
||||
* An abstract component for displaying an incremental list of objects
|
||||
*/
|
||||
export class AbstractIncrementalListComponent<T> implements OnInit, OnDestroy {
|
||||
/**
|
||||
* The amount to increment the list by
|
||||
* Define this amount in the child component overriding this component
|
||||
*/
|
||||
incrementBy: number;
|
||||
|
||||
/**
|
||||
* All pages of objects to display as an array
|
||||
*/
|
||||
objects: T[];
|
||||
|
||||
/**
|
||||
* A list of open subscriptions
|
||||
*/
|
||||
subscriptions: Subscription[];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.objects = [];
|
||||
this.subscriptions = [];
|
||||
this.increase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific page
|
||||
* > Override this method to return a specific page
|
||||
* @param page The page to fetch
|
||||
*/
|
||||
getPage(page: number): T {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the amount displayed
|
||||
*/
|
||||
increase() {
|
||||
const page = this.getPage(this.objects.length + 1);
|
||||
if (hasValue(page)) {
|
||||
this.objects.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the amount displayed
|
||||
*/
|
||||
decrease() {
|
||||
if (this.objects.length > 1) {
|
||||
this.objects.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from any open subscriptions
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (isNotEmpty(this.subscriptions)) {
|
||||
this.subscriptions.forEach((sub: Subscription) => {
|
||||
sub.unsubscribe();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,7 +4,6 @@ import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loa
|
||||
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
@@ -15,6 +14,7 @@ import { createRelationshipsObservable } from '../shared/item.component.spec';
|
||||
import { PublicationComponent } from './publication.component';
|
||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||
@@ -26,12 +26,6 @@ describe('PublicationComponent', () => {
|
||||
let comp: PublicationComponent;
|
||||
let fixture: ComponentFixture<PublicationComponent>;
|
||||
|
||||
const searchFixedFilterServiceStub = {
|
||||
/* tslint:disable:no-empty */
|
||||
getQueryByRelations: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
@@ -43,8 +37,8 @@ describe('PublicationComponent', () => {
|
||||
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||
providers: [
|
||||
{provide: ItemDataService, useValue: {}},
|
||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
||||
{provide: TruncatableService, useValue: {}}
|
||||
{provide: TruncatableService, useValue: {}},
|
||||
{provide: RelationshipService, useValue: {}}
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
|
||||
/**
|
||||
* Component that represents a publication Item page
|
||||
|
@@ -1,14 +1,12 @@
|
||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
||||
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
|
||||
import { zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
|
||||
/**
|
||||
* Operator for comparing arrays using a mapping function
|
||||
@@ -37,36 +35,6 @@ export const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
|
||||
export const compareArraysUsingIds = <T extends { id: string }>() =>
|
||||
compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined);
|
||||
|
||||
/**
|
||||
* Fetch the relationships which match the type label given
|
||||
* @param {string} label Type label
|
||||
* @param thisId The item's id of which the relations belong to
|
||||
* @returns {(source: Observable<[Relationship[] , RelationshipType[]]>) => Observable<Relationship[]>}
|
||||
*/
|
||||
export const filterRelationsByTypeLabel = (label: string, thisId?: string) =>
|
||||
(source: Observable<[Relationship[], RelationshipType[]]>): Observable<Relationship[]> =>
|
||||
source.pipe(
|
||||
switchMap(([relsCurrentPage, relTypesCurrentPage]) => {
|
||||
const relatedItems$ = observableZip(...relsCurrentPage.map((rel: Relationship) =>
|
||||
observableCombineLatest(
|
||||
rel.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
|
||||
rel.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()))
|
||||
)
|
||||
);
|
||||
return relatedItems$.pipe(
|
||||
map((arr) => relsCurrentPage.filter((rel: Relationship, idx: number) =>
|
||||
hasValue(relTypesCurrentPage[idx]) && (
|
||||
(hasNoValue(thisId) && (relTypesCurrentPage[idx].leftwardType === label ||
|
||||
relTypesCurrentPage[idx].rightwardType === label)) ||
|
||||
(thisId === arr[idx][0].id && relTypesCurrentPage[idx].leftwardType === label) ||
|
||||
(thisId === arr[idx][1].id && relTypesCurrentPage[idx].rightwardType === label)
|
||||
)
|
||||
))
|
||||
);
|
||||
}),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for turning a list of relationships into a list of the relevant items
|
||||
* @param {string} thisId The item's id of which the relations belong to
|
||||
@@ -128,17 +96,3 @@ export const paginatedRelationsToItems = (thisId: string) =>
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for fetching an item's relationships, but filtered by related item IDs (essentially performing a reverse lookup)
|
||||
* Only relationships where leftItem or rightItem's ID is present in the list provided will be returned
|
||||
* @param item
|
||||
* @param relationshipService
|
||||
*/
|
||||
export const getRelationsByRelatedItemIds = (item: Item, relationshipService: RelationshipService) =>
|
||||
(source: Observable<string[]>): Observable<Relationship[]> =>
|
||||
source.pipe(
|
||||
flatMap((relatedItemIds: string[]) => relationshipService.getItemResolvedRelatedItemsAndRelationships(item).pipe(
|
||||
map(([leftItems, rightItems, rels]) => rels.filter((rel: Relationship, index: number) => relatedItemIds.indexOf(leftItems[index].uuid) > -1 || relatedItemIds.indexOf(rightItems[index].uuid) > -1))
|
||||
))
|
||||
);
|
||||
|
@@ -9,7 +9,6 @@ import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loa
|
||||
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
@@ -24,6 +23,7 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
|
||||
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
|
||||
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||
|
||||
/**
|
||||
* Create a generic test for an item-page-fields component using a mockItem and the type of component
|
||||
@@ -37,12 +37,6 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
let comp: any;
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
const searchFixedFilterServiceStub = {
|
||||
/* tslint:disable:no-empty */
|
||||
getQueryByRelations: () => {}
|
||||
/* tslint:enable:no-empty */
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
@@ -54,8 +48,8 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
||||
providers: [
|
||||
{provide: ItemDataService, useValue: {}},
|
||||
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
|
||||
{provide: TruncatableService, useValue: {}}
|
||||
{provide: TruncatableService, useValue: {}},
|
||||
{provide: RelationshipService, useValue: {}}
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, Inject, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
|
||||
@Component({
|
||||
|
@@ -1,11 +1,20 @@
|
||||
<ds-metadata-field-wrapper *ngIf="representations$ && (representations$ | async)?.length > 0" [label]="label">
|
||||
<ds-metadata-representation-loader *ngFor="let rep of (representations$ | async)"
|
||||
<ds-metadata-field-wrapper [label]="label">
|
||||
<ng-container *ngFor="let objectPage of objects; let i = index">
|
||||
<ng-container *ngVar="(objectPage | async) as representations">
|
||||
<ds-metadata-representation-loader *ngFor="let rep of representations"
|
||||
[mdRepresentation]="rep">
|
||||
</ds-metadata-representation-loader>
|
||||
<div *ngIf="(representations$ | async)?.length < total" class="mt-2">
|
||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||
<ds-loading *ngIf="(i + 1) === objects.length && (i > 0) && (!representations || representations?.length === 0)" message="{{'loading.default' | translate}}"></ds-loading>
|
||||
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && representations?.length > 0">
|
||||
<div *ngIf="(objects.length * incrementBy) < total" class="float-left">
|
||||
<a [routerLink]="" (click)="increase()">{{'item.page.related-items.view-more' |
|
||||
translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }}</a>
|
||||
</div>
|
||||
<div *ngIf="limit > originalLimit" class="mt-2">
|
||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||
<div *ngIf="objects.length > 1" class="float-right">
|
||||
<a [routerLink]="" (click)="decrease()">{{'item.page.related-items.view-less' |
|
||||
translate:{ amount: representations?.length } }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -7,6 +7,8 @@ import { Item } from '../../../core/shared/item.model';
|
||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const itemType = 'Person';
|
||||
const metadataField = 'dc.contributor.author';
|
||||
@@ -64,7 +66,7 @@ describe('MetadataRepresentationListComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [MetadataRepresentationListComponent],
|
||||
declarations: [MetadataRepresentationListComponent, VarDirective],
|
||||
providers: [
|
||||
{ provide: RelationshipService, useValue: relationshipService }
|
||||
],
|
||||
@@ -88,33 +90,29 @@ describe('MetadataRepresentationListComponent', () => {
|
||||
expect(fields.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should initialize the original limit', () => {
|
||||
expect(comp.originalLimit).toEqual(comp.limit);
|
||||
it('should contain one page of items', () => {
|
||||
expect(comp.objects.length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when viewMore is called', () => {
|
||||
describe('when increase is called', () => {
|
||||
beforeEach(() => {
|
||||
comp.viewMore();
|
||||
comp.increase();
|
||||
});
|
||||
|
||||
it('should set the limit to a high number in order to retrieve all metadata representations', () => {
|
||||
expect(comp.limit).toBeGreaterThanOrEqual(999);
|
||||
it('should add a new page to the list', () => {
|
||||
expect(comp.objects.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when viewLess is called', () => {
|
||||
let originalLimit;
|
||||
|
||||
describe('when decrease is called', () => {
|
||||
beforeEach(() => {
|
||||
// Store the original value of limit
|
||||
originalLimit = comp.limit;
|
||||
// Set limit to a random number
|
||||
comp.limit = 458;
|
||||
comp.viewLess();
|
||||
// Add a second page
|
||||
comp.objects.push(observableOf(undefined));
|
||||
comp.decrease();
|
||||
});
|
||||
|
||||
it('should reset the limit to the original value', () => {
|
||||
expect(comp.limit).toEqual(originalLimit);
|
||||
it('should decrease the list of pages', () => {
|
||||
expect(comp.objects.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, zip as observableZip } from 'rxjs';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { combineLatest as observableCombineLatest, of as observableOf, zip as observableZip } from 'rxjs';
|
||||
import { MetadataValue } from '../../../core/shared/metadata.models';
|
||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-metadata-representation-list',
|
||||
@@ -22,7 +22,7 @@ import { ItemMetadataRepresentation } from '../../../core/shared/metadata-repres
|
||||
* It expects an itemType to resolve the metadata to a an item
|
||||
* It expects a label to put on top of the list
|
||||
*/
|
||||
export class MetadataRepresentationListComponent implements OnInit {
|
||||
export class MetadataRepresentationListComponent extends AbstractIncrementalListComponent<Observable<MetadataRepresentation[]>> {
|
||||
/**
|
||||
* The parent of the list of related items to display
|
||||
*/
|
||||
@@ -44,22 +44,11 @@ export class MetadataRepresentationListComponent implements OnInit {
|
||||
@Input() label: string;
|
||||
|
||||
/**
|
||||
* The max amount of representations to display
|
||||
* The amount to increment the list by when clicking "view more"
|
||||
* Defaults to 10
|
||||
* The default can optionally be overridden by providing the limit as input to the component
|
||||
*/
|
||||
@Input() limit = 10;
|
||||
|
||||
/**
|
||||
* A list of metadata-representations to display
|
||||
*/
|
||||
representations$: Observable<MetadataRepresentation[]>;
|
||||
|
||||
/**
|
||||
* The originally provided limit
|
||||
* Used for resetting the limit to the original value when collapsing the list
|
||||
*/
|
||||
originalLimit: number;
|
||||
@Input() incrementBy = 10;
|
||||
|
||||
/**
|
||||
* The total amount of metadata values available
|
||||
@@ -67,30 +56,28 @@ export class MetadataRepresentationListComponent implements OnInit {
|
||||
total: number;
|
||||
|
||||
constructor(public relationshipService: RelationshipService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.originalLimit = this.limit;
|
||||
this.setRepresentations();
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the metadata representations
|
||||
* Get a specific page
|
||||
* @param page The page to fetch
|
||||
*/
|
||||
setRepresentations() {
|
||||
getPage(page: number): Observable<MetadataRepresentation[]> {
|
||||
const metadata = this.parentItem.findMetadataSortedByPlace(this.metadataField);
|
||||
this.total = metadata.length;
|
||||
this.representations$ = this.resolveMetadataRepresentations(metadata);
|
||||
return this.resolveMetadataRepresentations(metadata, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of metadata values to a list of metadata representations
|
||||
* @param metadata
|
||||
* @param metadata The list of all metadata values
|
||||
* @param page The page to return representations for
|
||||
*/
|
||||
resolveMetadataRepresentations(metadata: MetadataValue[]): Observable<MetadataRepresentation[]> {
|
||||
resolveMetadataRepresentations(metadata: MetadataValue[], page: number): Observable<MetadataRepresentation[]> {
|
||||
return observableZip(
|
||||
...metadata
|
||||
.slice(0, this.limit)
|
||||
.slice((this.objects.length * this.incrementBy), (this.objects.length * this.incrementBy) + this.incrementBy)
|
||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||
.map((metadatum: MetadataValue) => {
|
||||
if (metadatum.isVirtual) {
|
||||
@@ -115,20 +102,4 @@ export class MetadataRepresentationListComponent implements OnInit {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the list to display all metadata representations
|
||||
*/
|
||||
viewMore() {
|
||||
this.limit = 9999;
|
||||
this.setRepresentations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse the list to display the originally displayed metadata representations
|
||||
*/
|
||||
viewLess() {
|
||||
this.limit = this.originalLimit;
|
||||
this.setRepresentations();
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<ds-filtered-search-page
|
||||
<ds-configuration-search-page
|
||||
[fixedFilterQuery]="fixedFilter"
|
||||
[configuration]="configuration"
|
||||
[configuration$]="configuration$"
|
||||
[searchEnabled]="searchEnabled"
|
||||
[sideBarWidth]="sideBarWidth">
|
||||
</ds-filtered-search-page>
|
||||
</ds-configuration-search-page>
|
||||
|
@@ -4,31 +4,23 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
|
||||
describe('RelatedEntitiesSearchComponent', () => {
|
||||
let comp: RelatedEntitiesSearchComponent;
|
||||
let fixture: ComponentFixture<RelatedEntitiesSearchComponent>;
|
||||
let fixedFilterService: SearchFixedFilterService;
|
||||
|
||||
const mockItem = Object.assign(new Item(), {
|
||||
id: 'id1'
|
||||
});
|
||||
const mockRelationType = 'publicationsOfAuthor';
|
||||
const mockRelationEntityType = 'publication';
|
||||
const mockConfiguration = 'publication';
|
||||
const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
|
||||
const fixedFilterServiceStub = {
|
||||
getFilterByRelation: () => mockFilter
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
|
||||
declarations: [RelatedEntitiesSearchComponent],
|
||||
providers: [
|
||||
{ provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
@@ -36,10 +28,9 @@ describe('RelatedEntitiesSearchComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RelatedEntitiesSearchComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixedFilterService = (comp as any).fixedFilterService;
|
||||
comp.relationType = mockRelationType;
|
||||
comp.item = mockItem;
|
||||
comp.relationEntityType = mockRelationEntityType;
|
||||
comp.configuration = mockConfiguration;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@@ -49,7 +40,7 @@ describe('RelatedEntitiesSearchComponent', () => {
|
||||
|
||||
it('should create a configuration$', () => {
|
||||
comp.configuration$.subscribe((configuration) => {
|
||||
expect(configuration).toEqual(mockRelationEntityType);
|
||||
expect(configuration).toEqual(mockConfiguration);
|
||||
})
|
||||
});
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-related-entities-search',
|
||||
@@ -22,18 +22,16 @@ export class RelatedEntitiesSearchComponent implements OnInit {
|
||||
*/
|
||||
@Input() relationType: string;
|
||||
|
||||
/**
|
||||
* An optional configuration to use for the search options
|
||||
*/
|
||||
@Input() configuration: string;
|
||||
|
||||
/**
|
||||
* The item to render relationships for
|
||||
*/
|
||||
@Input() item: Item;
|
||||
|
||||
/**
|
||||
* The entity type of the relationship items to be displayed
|
||||
* e.g. 'publication'
|
||||
* This determines the title of the search results (if search is enabled)
|
||||
*/
|
||||
@Input() relationEntityType: string;
|
||||
|
||||
/**
|
||||
* Whether or not the search bar and title should be displayed (defaults to true)
|
||||
* @type {boolean}
|
||||
@@ -49,15 +47,12 @@ export class RelatedEntitiesSearchComponent implements OnInit {
|
||||
fixedFilter: string;
|
||||
configuration$: Observable<string>;
|
||||
|
||||
constructor(private fixedFilterService: SearchFixedFilterService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
|
||||
this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id);
|
||||
this.fixedFilter = getFilterByRelation(this.relationType, this.item.id);
|
||||
}
|
||||
if (isNotEmpty(this.relationEntityType)) {
|
||||
this.configuration$ = of(this.relationEntityType);
|
||||
if (isNotEmpty(this.configuration)) {
|
||||
this.configuration$ = of(this.configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
<ngb-tabset *ngIf="relationTypes.length > 1" [destroyOnHide]="true" #tabs="ngbTabset" [activeId]="activeTab$ | async" (tabChange)="onTabChange($event)">
|
||||
<ngb-tab *ngFor="let relationType of relationTypes" title="{{'item.page.relationships.' + relationType.label | translate}}" [id]="relationType.filter">
|
||||
<ng-template ngbTabContent>
|
||||
<div class="mt-4">
|
||||
<ds-related-entities-search [item]="item"
|
||||
[relationType]="relationType.filter"
|
||||
[configuration]="relationType.configuration"
|
||||
[searchEnabled]="searchEnabled"
|
||||
[sideBarWidth]="sideBarWidth">
|
||||
</ds-related-entities-search>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
<div *ngIf="relationTypes.length === 1" class="mt-4">
|
||||
<ds-related-entities-search *ngVar="relationTypes[0] as relationType" [item]="item"
|
||||
[relationType]="relationType.filter"
|
||||
[configuration]="relationType.configuration"
|
||||
[searchEnabled]="searchEnabled"
|
||||
[sideBarWidth]="sideBarWidth">
|
||||
</ds-related-entities-search>
|
||||
</div>
|
@@ -0,0 +1,82 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { TabbedRelatedEntitiesSearchComponent } from './tabbed-related-entities-search.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MockRouter } from '../../../../shared/mocks/mock-router';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VarDirective } from '../../../../shared/utils/var.directive';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
describe('TabbedRelatedEntitiesSearchComponent', () => {
|
||||
let comp: TabbedRelatedEntitiesSearchComponent;
|
||||
let fixture: ComponentFixture<TabbedRelatedEntitiesSearchComponent>;
|
||||
|
||||
const mockItem = Object.assign(new Item(), {
|
||||
id: 'id1'
|
||||
});
|
||||
const mockRelationType = 'publications';
|
||||
const relationTypes = [
|
||||
{
|
||||
label: mockRelationType,
|
||||
filter: mockRelationType
|
||||
}
|
||||
];
|
||||
|
||||
const router = new MockRouter();
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule.forRoot()],
|
||||
declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {
|
||||
queryParams: observableOf({ tab: mockRelationType })
|
||||
},
|
||||
},
|
||||
{ provide: Router, useValue: router }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TabbedRelatedEntitiesSearchComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.item = mockItem;
|
||||
comp.relationTypes = relationTypes;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should initialize the activeTab depending on the current query parameters', () => {
|
||||
comp.activeTab$.subscribe((activeTab) => {
|
||||
expect(activeTab).toEqual(mockRelationType);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onTabChange', () => {
|
||||
const event = {
|
||||
currentId: mockRelationType,
|
||||
nextId: 'nextTab'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
comp.onTabChange(event);
|
||||
});
|
||||
|
||||
it('should call router natigate with the correct arguments', () => {
|
||||
expect(router.navigate).toHaveBeenCalledWith([], {
|
||||
relativeTo: (comp as any).route,
|
||||
queryParams: {
|
||||
tab: event.nextId
|
||||
},
|
||||
queryParamsHandling: 'merge'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,76 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-tabbed-related-entities-search',
|
||||
templateUrl: './tabbed-related-entities-search.component.html'
|
||||
})
|
||||
/**
|
||||
* A component to show related items as search results, split into tabs by relationship-type
|
||||
* Related items can be facetted, or queried using an
|
||||
* optional search box.
|
||||
*/
|
||||
export class TabbedRelatedEntitiesSearchComponent implements OnInit {
|
||||
/**
|
||||
* The types of relationships to fetch items for
|
||||
* e.g. 'isAuthorOfPublication'
|
||||
*/
|
||||
@Input() relationTypes: Array<{
|
||||
label: string,
|
||||
filter: string,
|
||||
configuration?: string
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The item to render relationships for
|
||||
*/
|
||||
@Input() item: Item;
|
||||
|
||||
/**
|
||||
* Whether or not the search bar and title should be displayed (defaults to true)
|
||||
* @type {boolean}
|
||||
*/
|
||||
@Input() searchEnabled = true;
|
||||
|
||||
/**
|
||||
* The ratio of the sidebar's width compared to the search results (1-12) (defaults to 4)
|
||||
* @type {number}
|
||||
*/
|
||||
@Input() sideBarWidth = 4;
|
||||
|
||||
/**
|
||||
* The active tab
|
||||
*/
|
||||
activeTab$: Observable<string>;
|
||||
|
||||
constructor(private route: ActivatedRoute,
|
||||
private router: Router) {
|
||||
}
|
||||
|
||||
/**
|
||||
* If the url contains a "tab" query parameter, set this tab to be the active tab
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.activeTab$ = this.route.queryParams.pipe(
|
||||
map((params) => params.tab)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "tab" query parameter to the URL when changing tabs
|
||||
* @param event
|
||||
*/
|
||||
onTabChange(event) {
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: {
|
||||
tab: event.nextId
|
||||
},
|
||||
queryParamsHandling: 'merge'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { FindAllOptions } from '../../../core/data/request.models';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { FindListOptions } from '../../../core/data/request.models';
|
||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-related-items',
|
||||
@@ -17,7 +17,7 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||
* This component is used for displaying relations between items
|
||||
* It expects a parent item and relationship type, as well as a label to display on top
|
||||
*/
|
||||
export class RelatedItemsComponent implements OnInit, OnDestroy {
|
||||
export class RelatedItemsComponent extends AbstractIncrementalListComponent<Observable<RemoteData<PaginatedList<Item>>>> {
|
||||
/**
|
||||
* The parent of the list of related items to display
|
||||
*/
|
||||
@@ -30,79 +30,38 @@ export class RelatedItemsComponent implements OnInit, OnDestroy {
|
||||
@Input() relationType: string;
|
||||
|
||||
/**
|
||||
* Default options to start a search request with
|
||||
* Optional input, should you wish a different page size (or other options)
|
||||
* The amount to increment the list by when clicking "view more"
|
||||
* Defaults to 5
|
||||
* The default can optionally be overridden by providing the limit as input to the component
|
||||
*/
|
||||
@Input() options = Object.assign(new FindAllOptions(), { elementsPerPage: 5 });
|
||||
@Input() incrementBy = 5;
|
||||
|
||||
/**
|
||||
* Default options to start a search request with
|
||||
* Optional input
|
||||
*/
|
||||
@Input() options = new FindListOptions();
|
||||
|
||||
/**
|
||||
* An i18n label to use as a title for the list (usually describes the relation)
|
||||
*/
|
||||
@Input() label: string;
|
||||
|
||||
/**
|
||||
* Completely hide the component until there's at least one item visible
|
||||
*/
|
||||
@HostBinding('class.d-none') hidden = true;
|
||||
|
||||
/**
|
||||
* The list of related items
|
||||
*/
|
||||
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
||||
|
||||
/**
|
||||
* Search options for displaying all elements in a list
|
||||
*/
|
||||
allOptions = Object.assign(new FindAllOptions(), { elementsPerPage: 9999 });
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
* @type {ViewMode}
|
||||
*/
|
||||
viewMode = ViewMode.ListElement;
|
||||
|
||||
/**
|
||||
* Whether or not the list is currently expanded to show all related items
|
||||
*/
|
||||
showingAll = false;
|
||||
|
||||
/**
|
||||
* Subscription on items used to update the "hidden" property of this component
|
||||
*/
|
||||
itemSub: Subscription;
|
||||
|
||||
constructor(public relationshipService: RelationshipService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
||||
this.itemSub = this.items$.subscribe((itemsRD: RemoteData<PaginatedList<Item>>) => {
|
||||
this.hidden = !(itemsRD.hasSucceeded && itemsRD.payload && itemsRD.payload.page.length > 0);
|
||||
});
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the list to display all related items
|
||||
* Get a specific page
|
||||
* @param page The page to fetch
|
||||
*/
|
||||
viewMore() {
|
||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.allOptions);
|
||||
this.showingAll = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse the list to display the originally displayed items
|
||||
*/
|
||||
viewLess() {
|
||||
this.items$ = this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, this.options);
|
||||
this.showingAll = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the item subscription
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this.itemSub) {
|
||||
this.itemSub.unsubscribe();
|
||||
}
|
||||
getPage(page: number): Observable<RemoteData<PaginatedList<Item>>> {
|
||||
return this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, Object.assign(this.options, { elementsPerPage: this.incrementBy, currentPage: page }));
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,20 @@
|
||||
<ds-metadata-field-wrapper *ngIf="(items$ | async)?.payload?.page?.length > 0" [label]="label">
|
||||
<ds-listable-object-component-loader *ngFor="let item of (items$ | async)?.payload?.page"
|
||||
<ds-metadata-field-wrapper [label]="label">
|
||||
<ng-container *ngFor="let objectPage of objects; let i = index">
|
||||
<ng-container *ngVar="(objectPage | async) as itemsRD">
|
||||
<ds-listable-object-component-loader *ngFor="let item of itemsRD?.payload?.page"
|
||||
[object]="item" [viewMode]="viewMode">
|
||||
</ds-listable-object-component-loader>
|
||||
<div *ngIf="(items$ | async)?.payload?.page?.length < (items$ | async)?.payload?.totalElements" class="mt-2" id="view-more">
|
||||
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
|
||||
<ds-loading *ngIf="(i + 1) === objects.length && (itemsRD || i > 0) && !(itemsRD?.hasSucceeded && itemsRD?.payload && itemsRD?.payload?.page?.length > 0)" message="{{'loading.default' | translate}}"></ds-loading>
|
||||
<div class="d-inline-block w-100 mt-2" *ngIf="(i + 1) === objects.length && itemsRD?.payload?.page?.length > 0">
|
||||
<div *ngIf="itemsRD?.payload?.totalPages > objects.length" class="float-left" id="view-more">
|
||||
<a [routerLink]="" (click)="increase()">{{'item.page.related-items.view-more' |
|
||||
translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }}</a>
|
||||
</div>
|
||||
<div *ngIf="showingAll" class="mt-2" id="view-less">
|
||||
<a [routerLink]="" (click)="viewLess()">{{'item.page.related-items.view-less' | translate}}</a>
|
||||
<div *ngIf="objects.length > 1" class="float-right" id="view-less">
|
||||
<a [routerLink]="" (click)="decrease()">{{'item.page.related-items.view-less' |
|
||||
translate:{ amount: itemsRD?.payload?.page?.length } }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -9,6 +9,8 @@ import { createRelationshipsObservable } from '../item-types/shared/item.compone
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const parentItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||
@@ -42,7 +44,7 @@ describe('RelatedItemsComponent', () => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [RelatedItemsComponent],
|
||||
declarations: [RelatedItemsComponent, VarDirective],
|
||||
providers: [
|
||||
{ provide: RelationshipService, useValue: relationshipService }
|
||||
],
|
||||
@@ -65,31 +67,33 @@ describe('RelatedItemsComponent', () => {
|
||||
expect(fields.length).toBe(mockItems.length);
|
||||
});
|
||||
|
||||
describe('when viewMore is called', () => {
|
||||
it('should contain one page of items', () => {
|
||||
expect(comp.objects.length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when increase is called', () => {
|
||||
beforeEach(() => {
|
||||
comp.viewMore();
|
||||
comp.increase();
|
||||
});
|
||||
|
||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.allOptions);
|
||||
it('should add a new page to the list', () => {
|
||||
expect(comp.objects.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should set showingAll to true', () => {
|
||||
expect(comp.showingAll).toEqual(true);
|
||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments (second page)', () => {
|
||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, Object.assign(comp.options, { elementsPerPage: comp.incrementBy, currentPage: 2 }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('when viewLess is called', () => {
|
||||
describe('when decrease is called', () => {
|
||||
beforeEach(() => {
|
||||
comp.viewLess();
|
||||
// Add a second page
|
||||
comp.objects.push(observableOf(undefined));
|
||||
comp.decrease();
|
||||
});
|
||||
|
||||
it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments', () => {
|
||||
expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, comp.options);
|
||||
});
|
||||
|
||||
it('should set showingAll to false', () => {
|
||||
expect(comp.showingAll).toEqual(false);
|
||||
it('should decrease the list of pages', () => {
|
||||
expect(comp.objects.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { SearchFilter } from '../+search-page/search-filter.model';
|
||||
import { SearchFilter } from '../shared/search/search-filter.model';
|
||||
import { ActivatedRouteStub } from '../shared/testing/active-router-stub';
|
||||
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
||||
import { cold, hot } from 'jasmine-marbles';
|
||||
@@ -38,12 +38,8 @@ describe('MyDSpaceConfigurationService', () => {
|
||||
|
||||
const roleService: any = new MockRoleService();
|
||||
|
||||
const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
|
||||
getQueryByFilterName: observableOf(''),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = new MyDSpaceConfigurationService(roleService, fixedFilterService, spy, activatedRoute);
|
||||
service = new MyDSpaceConfigurationService(roleService, spy, activatedRoute);
|
||||
});
|
||||
|
||||
describe('when the scope is called', () => {
|
||||
|
@@ -6,12 +6,11 @@ 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 '../core/services/route.service';
|
||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
|
||||
/**
|
||||
* Service that performs all actions that have to do with the current mydspace configuration
|
||||
@@ -55,16 +54,14 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||
* Initialize class
|
||||
*
|
||||
* @param {roleService} roleService
|
||||
* @param {SearchFixedFilterService} fixedFilterService
|
||||
* @param {RouteService} routeService
|
||||
* @param {ActivatedRoute} route
|
||||
*/
|
||||
constructor(protected roleService: RoleService,
|
||||
protected fixedFilterService: SearchFixedFilterService,
|
||||
protected routeService: RouteService,
|
||||
protected route: ActivatedRoute) {
|
||||
|
||||
super(routeService, fixedFilterService, route);
|
||||
super(routeService, route);
|
||||
|
||||
// override parent class initialization
|
||||
this._defaults = null;
|
||||
|
@@ -14,7 +14,7 @@ import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { SearchResult } from '../../+search-page/search-result.model';
|
||||
import { SearchResult } from '../../shared/search/search-result.model';
|
||||
|
||||
/**
|
||||
* This component represents the whole mydspace page header
|
||||
@@ -33,12 +33,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
||||
/**
|
||||
* The UploaderOptions object
|
||||
*/
|
||||
public uploadFilesOptions: UploaderOptions = {
|
||||
url: '',
|
||||
authToken: null,
|
||||
disableMultipart: false,
|
||||
itemAlias: null
|
||||
};
|
||||
public uploadFilesOptions: UploaderOptions = new UploaderOptions();
|
||||
|
||||
/**
|
||||
* Subscription to unsubscribe from
|
||||
|
@@ -1 +1 @@
|
||||
@import '../+search-page/search-page.component.scss';
|
||||
@import '../+search-page/search.component.scss';
|
||||
|
@@ -19,15 +19,14 @@ import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.c
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { routeServiceStub } from '../shared/testing/route-service-stub';
|
||||
import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchFilterService } from '../+search-page/search-filters/search-filter/search-filter.service';
|
||||
import { SearchFilterService } from '../core/shared/search/search-filter.service';
|
||||
import { RoleDirective } from '../shared/roles/role.directive';
|
||||
import { RoleService } from '../core/roles/role.service';
|
||||
import { MockRoleService } from '../shared/mocks/mock-role-service';
|
||||
import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/testing/utils';
|
||||
|
||||
describe('MyDSpacePageComponent', () => {
|
||||
@@ -82,8 +81,6 @@ describe('MyDSpacePageComponent', () => {
|
||||
collapse: () => this.isCollapsed = observableOf(true),
|
||||
expand: () => this.isCollapsed = observableOf(false)
|
||||
};
|
||||
const mockFixedFilterService: SearchFixedFilterService = {
|
||||
} as SearchFixedFilterService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -123,10 +120,6 @@ describe('MyDSpacePageComponent', () => {
|
||||
provide: RoleService,
|
||||
useValue: new MockRoleService()
|
||||
},
|
||||
{
|
||||
provide: SearchFixedFilterService,
|
||||
useValue: mockFixedFilterService
|
||||
}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(MyDSpacePageComponent, {
|
||||
|
@@ -15,19 +15,19 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
|
||||
import { SearchService } from '../+search-page/search-service/search.service';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
||||
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
||||
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
|
||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { RoleType } from '../core/roles/role-types';
|
||||
import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
|
||||
import { ViewMode } from '../core/shared/view-mode.model';
|
||||
import { MyDSpaceRequest } from '../core/data/request.models';
|
||||
import { SearchResult } from '../+search-page/search-result.model';
|
||||
import { SearchResult } from '../shared/search/search-result.model';
|
||||
import { Context } from '../core/shared/context.model';
|
||||
|
||||
export const MYDSPACE_ROUTE = '/mydspace';
|
||||
|
@@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
|
||||
import { MyDSpacePageComponent } from './my-dspace-page.component';
|
||||
import { SearchPageModule } from '../+search-page/search-page.module';
|
||||
import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component';
|
||||
import { WorkspaceItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component';
|
||||
import { ClaimedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component';
|
||||
@@ -27,7 +26,6 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
MyDspacePageRoutingModule,
|
||||
SearchPageModule
|
||||
],
|
||||
declarations: [
|
||||
MyDSpacePageComponent,
|
||||
|
@@ -2,12 +2,12 @@ import { Component, Input } from '@angular/core';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||
import { SearchOptions } from '../../+search-page/search-options.model';
|
||||
import { SearchOptions } from '../../shared/search/search-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
import { SearchResult } from '../../+search-page/search-result.model';
|
||||
import { Context } from '../../core/shared/context.model';
|
||||
import { SearchResult } from '../../shared/search/search-result.model';
|
||||
|
||||
/**
|
||||
* Component that represents all results for mydspace page
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { configureSearchComponentTestingModule } from './search-page.component.spec';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { configureSearchComponentTestingModule } from './search.component.spec';
|
||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
|
||||
describe('ConfigurationSearchPageComponent', () => {
|
||||
let comp: ConfigurationSearchPageComponent;
|
||||
|
@@ -1,23 +1,22 @@
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchPageComponent } from './search-page.component';
|
||||
import { SearchComponent } from './search.component';
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
|
||||
/**
|
||||
* This component renders a search page using a configuration as input.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-configuration-search-page',
|
||||
styleUrls: ['./search-page.component.scss'],
|
||||
templateUrl: './search-page.component.html',
|
||||
styleUrls: ['./search.component.scss'],
|
||||
templateUrl: './search.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [pushInOut],
|
||||
providers: [
|
||||
@@ -28,19 +27,26 @@ import { RouteService } from '../core/services/route.service';
|
||||
]
|
||||
})
|
||||
|
||||
export class ConfigurationSearchPageComponent extends SearchPageComponent implements OnInit {
|
||||
export class ConfigurationSearchPageComponent extends SearchComponent implements OnInit {
|
||||
/**
|
||||
* The configuration to use for the search options
|
||||
* If empty, the configuration will be determined by the route parameter called 'configuration'
|
||||
*/
|
||||
@Input() configuration: string;
|
||||
|
||||
/**
|
||||
* The actual query for the fixed filter.
|
||||
* If empty, the query will be determined by the route parameter called 'filter'
|
||||
*/
|
||||
@Input() fixedFilterQuery: string;
|
||||
|
||||
constructor(protected service: SearchService,
|
||||
protected sidebarService: SidebarService,
|
||||
protected windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
protected routeService: RouteService) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
||||
protected routeService: RouteService,
|
||||
protected router: Router) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService, router);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,20 +58,8 @@ export class ConfigurationSearchPageComponent extends SearchPageComponent implem
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current paginated search options after updating the configuration using the configuration input
|
||||
* This is to make sure the configuration is included in the paginated search options, as it is not part of any
|
||||
* query or route parameters
|
||||
* @returns {Observable<PaginatedSearchOptions>}
|
||||
*/
|
||||
protected getSearchOptions(): Observable<PaginatedSearchOptions> {
|
||||
return this.searchConfigService.paginatedSearchOptions.pipe(
|
||||
map((options: PaginatedSearchOptions) => {
|
||||
const config = this.configuration || options.configuration;
|
||||
return Object.assign(options, { configuration: config });
|
||||
})
|
||||
);
|
||||
if (hasValue(this.configuration)) {
|
||||
this.routeService.setParameter('configuration', this.configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +0,0 @@
|
||||
import { FilteredSearchPageComponent } from './filtered-search-page.component';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { configureSearchComponentTestingModule } from './search-page.component.spec';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
|
||||
describe('FilteredSearchPageComponent', () => {
|
||||
let comp: FilteredSearchPageComponent;
|
||||
let fixture: ComponentFixture<FilteredSearchPageComponent>;
|
||||
let searchConfigService: SearchConfigurationService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
configureSearchComponentTestingModule(FilteredSearchPageComponent);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FilteredSearchPageComponent);
|
||||
comp = fixture.componentInstance;
|
||||
searchConfigService = (comp as any).searchConfigService;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
});
|
@@ -1,73 +0,0 @@
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SearchService } from './search-service/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { SearchPageComponent } from './search-page.component';
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { pushInOut } from '../shared/animations/push';
|
||||
import { SearchConfigurationService } from './search-service/search-configuration.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from './paginated-search-options.model';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
|
||||
/**
|
||||
* This component renders a simple item page.
|
||||
* The route parameter 'id' is used to request the item it represents.
|
||||
* All fields of the item that should be displayed, are defined in its template.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-filtered-search-page',
|
||||
styleUrls: ['./search-page.component.scss'],
|
||||
templateUrl: './search-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [pushInOut],
|
||||
providers: [
|
||||
{
|
||||
provide: SEARCH_CONFIG_SERVICE,
|
||||
useClass: SearchConfigurationService
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export class FilteredSearchPageComponent extends SearchPageComponent implements OnInit {
|
||||
/**
|
||||
* The actual query for the fixed filter.
|
||||
* If empty, the query will be determined by the route parameter called 'filter'
|
||||
*/
|
||||
@Input() fixedFilterQuery: string;
|
||||
|
||||
constructor(protected service: SearchService,
|
||||
protected sidebarService: SidebarService,
|
||||
protected windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
|
||||
protected routeService: RouteService) {
|
||||
super(service, sidebarService, windowService, searchConfigService, routeService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listening to changes in the paginated search options
|
||||
* If something changes, update the search results
|
||||
*
|
||||
* Listen to changes in the scope
|
||||
* If something changes, update the list of scopes for the dropdown
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current paginated search options after updating the fixed filter using the fixedFilterQuery input
|
||||
* This is to make sure the fixed filter is included in the paginated search options, as it is not part of any
|
||||
* query or route parameters
|
||||
* @returns {Observable<PaginatedSearchOptions>}
|
||||
*/
|
||||
protected getSearchOptions(): Observable<PaginatedSearchOptions> {
|
||||
return this.searchConfigService.paginatedSearchOptions.pipe(
|
||||
map((options: PaginatedSearchOptions) => {
|
||||
const filter = this.fixedFilterQuery || options.fixedFilter;
|
||||
return Object.assign(options, { fixedFilter: filter });
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
import { SearchFixedFilterService } from './search-fixed-filter.service';
|
||||
import { RequestService } from '../../../core/data/request.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RequestEntry } from '../../../core/data/request.reducer';
|
||||
import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response.models';
|
||||
|
||||
describe('SearchFixedFilterService', () => {
|
||||
let service: SearchFixedFilterService;
|
||||
|
||||
const filterQuery = 'filter:query';
|
||||
|
||||
const requestServiceStub = Object.assign({
|
||||
/* tslint:disable:no-empty */
|
||||
configure: () => {
|
||||
},
|
||||
/* tslint:enable:no-empty */
|
||||
generateRequestId: () => 'fake-id',
|
||||
getByHref: () => observableOf(Object.assign(new RequestEntry(), {
|
||||
response: new FilteredDiscoveryQueryResponse(filterQuery, 200, 'OK')
|
||||
}))
|
||||
}) as RequestService;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new SearchFixedFilterService();
|
||||
});
|
||||
|
||||
describe('when getQueryByRelations is called', () => {
|
||||
const relationType = 'isRelationOf';
|
||||
const itemUUID = 'c5b277e6-2477-48bb-8993-356710c285f3';
|
||||
|
||||
it('should contain the relationType and itemUUID', () => {
|
||||
const query = service.getQueryByRelations(relationType, itemUUID);
|
||||
expect(query.length).toBeGreaterThan(relationType.length + itemUUID.length);
|
||||
expect(query).toContain(relationType);
|
||||
expect(query).toContain(itemUUID);
|
||||
});
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user