Merge branch 'master' into Keep-virtual-metadata-on-relationship-delete

This commit is contained in:
Samuel
2020-01-09 14:08:40 +01:00
534 changed files with 12706 additions and 2801 deletions

View File

@@ -2,7 +2,8 @@ import { browser, element, by } from 'protractor';
export class ProtractorPage {
navigateTo() {
return browser.get('/');
return browser.get('/')
.then(() => browser.waitForAngular());
}
getPageTitleText() {

View File

@@ -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: '',

View File

@@ -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",

View File

@@ -0,0 +1,3 @@
# Supported font formats
DSpace supports EOT, TTF, OTF, SVG, WOFF and WOFF2 fonts.

View File

@@ -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",
@@ -1680,7 +1853,7 @@
"uploader.drag-message": "Drag & Drop your files here",
"uploader.or": ", or",
"uploader.or": ", or ",
"uploader.processing": "Processing",

View File

@@ -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;

View File

@@ -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';

View File

@@ -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);
}
}

View File

@@ -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();
}));

View File

@@ -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',

View File

@@ -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',

View File

@@ -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"

View File

@@ -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';

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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 */
}

View File

@@ -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>

View File

@@ -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/');
})
});
});

View File

@@ -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);
}
}

View File

@@ -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 */
}

View File

@@ -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 */
}

View File

@@ -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>

View File

@@ -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');
})
});
});

View File

@@ -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)
}
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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);
}
}

View File

@@ -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',

View File

@@ -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>

View File

@@ -6,26 +6,29 @@ import { SharedModule } from '../shared/shared.module';
import { CommunityPageComponent } from './community-page.component';
import { CommunityPageSubCollectionListComponent } from './sub-collection-list/community-page-sub-collection-list.component';
import { CommunityPageRoutingModule } from './community-page-routing.module';
import {CommunityPageSubCommunityListComponent} from './sub-community-list/community-page-sub-community-list.component';
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
]
})

View File

@@ -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>

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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 */
}

View File

@@ -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>

View File

@@ -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/');
})
});
});

View File

@@ -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);
}
}

View File

@@ -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 */
}

View File

@@ -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>

View File

@@ -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');
})
});
});

View File

@@ -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)
}
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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>

View File

@@ -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),
);
}
}

View File

@@ -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,

View 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));
}
}

View File

@@ -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;

View File

@@ -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',

View File

@@ -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;

View File

@@ -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',

View File

@@ -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]);
})
});
});

View File

@@ -1,31 +1,32 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemRelationshipsComponent } from './item-relationships.component';
import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
import { NotificationType } from '../../../shared/notifications/models/notification-type';
import { RouterStub } from '../../../shared/testing/router-stub';
import { TestScheduler } from 'rxjs/testing';
import { SharedModule } from '../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { ItemDataService } from '../../../core/data/item-data.service';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
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 { RemoteData } from '../../../core/data/remote-data';
import { Item } from '../../../core/shared/item.model';
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 { RelationshipService } from '../../../core/data/relationship.service';
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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ItemRelationshipsComponent} from './item-relationships.component';
import {ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';
import {INotification, Notification} from '../../../shared/notifications/models/notification.model';
import {NotificationType} from '../../../shared/notifications/models/notification-type';
import {RouterStub} from '../../../shared/testing/router-stub';
import {TestScheduler} from 'rxjs/testing';
import {SharedModule} from '../../../shared/shared.module';
import {TranslateModule} from '@ngx-translate/core';
import {ItemDataService} from '../../../core/data/item-data.service';
import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
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 {Relationship} from '../../../core/shared/item-relationships/relationship.model';
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';
import {PageInfo} from '../../../core/shared/page-info.model';
import {FieldChangeType} from '../../../core/data/object-updates/object-updates.actions';
import {RelationshipService} from '../../../core/data/relationship.service';
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

View File

@@ -1,33 +1,29 @@
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 { Observable } from 'rxjs/internal/Observable';
import {filter, map, switchMap, take, tap} from 'rxjs/operators';
import { combineLatest as observableCombineLatest, 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';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
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 {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 {Observable} from 'rxjs/internal/Observable';
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';
import {ActivatedRoute, Router} from '@angular/router';
import {NotificationsService} from '../../../shared/notifications/notifications.service';
import {TranslateService} from '@ngx-translate/core';
import {GLOBAL_CONFIG, GlobalConfig} from '../../../../config';
import {RelationshipService} from '../../../core/data/relationship.service';
import {ErrorResponse, RestResponse} from '../../../core/cache/response.models';
import {RemoteData} from '../../../core/data/remote-data';
import {ObjectCacheService} from '../../../core/cache/object-cache.service';
import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators';
import { RequestService } from '../../../core/data/request.service';
import { Subscription } from 'rxjs/internal/Subscription';
import {RequestService} from '../../../core/data/request.service';
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) => {

View File

@@ -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">

View File

@@ -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;

View File

@@ -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]">

View File

@@ -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

View File

@@ -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();
});
}
}
}

View File

@@ -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>

View File

@@ -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]

View File

@@ -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

View File

@@ -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))
))
);

View File

@@ -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]

View File

@@ -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({

View File

@@ -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>

View File

@@ -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);
});
});

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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);
})
});

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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'
});
});
});
});

View File

@@ -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'
});
}
}

View File

@@ -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 }));
}
}

View File

@@ -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>

View File

@@ -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);
});
});

View File

@@ -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', () => {

View File

@@ -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;

View File

@@ -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

View File

@@ -1 +1 @@
@import '../+search-page/search-page.component.scss';
@import '../+search-page/search.component.scss';

View File

@@ -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, {

View File

@@ -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';

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
if (hasValue(this.configuration)) {
this.routeService.setParameter('configuration', this.configuration);
}
/**
* 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 });
})
);
}
}

View File

@@ -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();
});
});

View File

@@ -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 });
})
);
}
}

View File

@@ -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