Merge pull request #615 from atmire/administrative_search

Add Administrative search
This commit is contained in:
Tim Donohue
2020-03-17 15:35:24 -05:00
committed by GitHub
75 changed files with 1622 additions and 388 deletions

View File

@@ -170,6 +170,34 @@
"admin.search.breadcrumbs": "Administrative Search",
"admin.search.collection.edit": "Edit",
"admin.search.community.edit": "Edit",
"admin.search.item.delete": "Delete",
"admin.search.item.edit": "Edit",
"admin.search.item.make-private": "Make Private",
"admin.search.item.make-public": "Make Public",
"admin.search.item.move": "Move",
"admin.search.item.private": "Private",
"admin.search.item.reinstate": "Reinstate",
"admin.search.item.withdraw": "Withdraw",
"admin.search.item.withdrawn": "Withdrawn",
"admin.search.title": "Administrative Search",
"auth.errors.invalid-user": "Invalid email address or password.", "auth.errors.invalid-user": "Invalid email address or password.",
"auth.messages.expired": "Your session has expired. Please log in again.", "auth.messages.expired": "Your session has expired. Please log in again.",
@@ -1117,6 +1145,10 @@
"menu.section.admin_search": "Admin Search",
"menu.section.browse_community": "This Community", "menu.section.browse_community": "This Community",
"menu.section.browse_community_by_author": "By Author", "menu.section.browse_community_by_author": "By Author",
@@ -1167,18 +1199,10 @@
"menu.section.find": "Find",
"menu.section.find_items": "Items",
"menu.section.find_private_items": "Private Items",
"menu.section.find_withdrawn_items": "Withdrawn Items",
"menu.section.icon.access_control": "Access Control menu section", "menu.section.icon.access_control": "Access Control menu section",
"menu.section.icon.admin_search": "Admin search menu section",
"menu.section.icon.control_panel": "Control Panel menu section", "menu.section.icon.control_panel": "Control Panel menu section",
"menu.section.icon.curation_task": "Curation Task menu section", "menu.section.icon.curation_task": "Curation Task menu section",
@@ -1498,6 +1522,8 @@
"search.filters.applied.f.dateSubmitted": "Date submitted", "search.filters.applied.f.dateSubmitted": "Date submitted",
"search.filters.applied.f.discoverable": "Private",
"search.filters.applied.f.entityType": "Item Type", "search.filters.applied.f.entityType": "Item Type",
"search.filters.applied.f.has_content_in_original_bundle": "Has files", "search.filters.applied.f.has_content_in_original_bundle": "Has files",
@@ -1509,10 +1535,15 @@
"search.filters.applied.f.subject": "Subject", "search.filters.applied.f.subject": "Subject",
"search.filters.applied.f.submitter": "Submitter", "search.filters.applied.f.submitter": "Submitter",
"search.filters.applied.f.jobTitle": "Job Title", "search.filters.applied.f.jobTitle": "Job Title",
"search.filters.applied.f.birthDate.max": "End birth date", "search.filters.applied.f.birthDate.max": "End birth date",
"search.filters.applied.f.birthDate.min": "Start birth date", "search.filters.applied.f.birthDate.min": "Start birth date",
"search.filters.applied.f.withdrawn": "Withdrawn",
"search.filters.filter.author.head": "Author", "search.filters.filter.author.head": "Author",
@@ -1549,6 +1580,10 @@
"search.filters.filter.dateSubmitted.placeholder": "Date submitted", "search.filters.filter.dateSubmitted.placeholder": "Date submitted",
"search.filters.filter.discoverable.head": "Private",
"search.filters.filter.withdrawn.head": "Withdrawn",
"search.filters.filter.entityType.head": "Item Type", "search.filters.filter.entityType.head": "Item Type",
"search.filters.filter.entityType.placeholder": "Item Type", "search.filters.filter.entityType.placeholder": "Item Type",
@@ -1605,6 +1640,25 @@
"search.filters.entityType.JournalIssue": "Journal Issue",
"search.filters.entityType.JournalVolume": "Journal Volume",
"search.filters.entityType.OrgUnit": "Organizational Unit",
"search.filters.has_content_in_original_bundle.true": "Yes",
"search.filters.has_content_in_original_bundle.false": "No",
"search.filters.discoverable.true": "No",
"search.filters.discoverable.false": "Yes",
"search.filters.withdrawn.true": "Yes",
"search.filters.withdrawn.false": "No",
"search.filters.head": "Filters", "search.filters.head": "Filters",
"search.filters.reset": "Reset filters", "search.filters.reset": "Reset filters",
@@ -1984,6 +2038,8 @@
"title": "DSpace", "title": "DSpace",
"administrativeView.search.results.head": "Administrative Search",
"uploader.browse": "browse", "uploader.browse": "browse",

View File

@@ -2,6 +2,8 @@ import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { URLCombiner } from '../core/url-combiner/url-combiner'; import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getAdminModulePath } from '../app-routing.module'; import { getAdminModulePath } from '../app-routing.module';
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
const REGISTRIES_MODULE_PATH = 'registries'; const REGISTRIES_MODULE_PATH = 'registries';
@@ -15,7 +17,13 @@ export function getRegistriesModulePath() {
{ {
path: REGISTRIES_MODULE_PATH, path: REGISTRIES_MODULE_PATH,
loadChildren: './admin-registries/admin-registries.module#AdminRegistriesModule' loadChildren: './admin-registries/admin-registries.module#AdminRegistriesModule'
} },
{
path: 'search',
resolve: { breadcrumb: I18nBreadcrumbResolver },
component: AdminSearchPageComponent,
data: { title: 'admin.search.title', breadcrumbKey: 'admin.search' }
},
]) ])
] ]
}) })

View File

@@ -0,0 +1 @@
<ds-configuration-search-page configuration="administrativeView" [context]="context"></ds-configuration-search-page>

View File

@@ -0,0 +1,27 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminSearchPageComponent } from './admin-search-page.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
describe('AdminSearchPageComponent', () => {
let component: AdminSearchPageComponent;
let fixture: ComponentFixture<AdminSearchPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AdminSearchPageComponent ],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AdminSearchPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { Context } from '../../core/shared/context.model';
@Component({
selector: 'ds-admin-search-page',
templateUrl: './admin-search-page.component.html',
styleUrls: ['./admin-search-page.component.scss']
})
/**
* Component that represents a search page for administrators
*/
export class AdminSearchPageComponent {
/**
* The context of this page
*/
context: Context = Context.AdminSearch;
}

View File

@@ -0,0 +1,13 @@
<ds-collection-search-result-grid-element [object]="object"
[index]="index"
[linkType]="linkType"
[listID]="listID">
<ul class="list-group list-group-flush">
<li class="list-group-item text-center">
<a class="btn btn-light btn-sm btn-auto my-1 edit-link" [routerLink]="[editPath]">
<i class="fa fa-edit"></i>
</a>
</li>
</ul>
</ds-collection-search-result-grid-element>

View File

@@ -0,0 +1,66 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service';
import { SharedModule } from '../../../../../shared/shared.module';
import { CollectionAdminSearchResultGridElementComponent } from './collection-admin-search-result-grid-element.component';
import { TranslateModule } from '@ngx-translate/core';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
import { Collection } from '../../../../../core/shared/collection.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
describe('CollectionAdminSearchResultGridElementComponent', () => {
let component: CollectionAdminSearchResultGridElementComponent;
let fixture: ComponentFixture<CollectionAdminSearchResultGridElementComponent>;
let id;
let searchResult;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new CollectionSearchResult();
searchResult.indexableObject = new Collection();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([]),
SharedModule
],
declarations: [CollectionAdminSearchResultGridElementComponent],
providers: [
{ provide: TruncatableService, useValue: mockTruncatableService },
{ provide: BitstreamDataService, useValue: {} },
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CollectionAdminSearchResultGridElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render an edit button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.edit-link'));
const link = a.nativeElement.href;
expect(link).toContain(getCollectionEditPath(id));
})
});

View File

@@ -0,0 +1,26 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
import { Collection } from '../../../../../core/shared/collection.model';
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
@listableObjectComponent(CollectionSearchResult, ViewMode.GridElement, Context.AdminSearch)
@Component({
selector: 'ds-collection-admin-search-result-list-element',
styleUrls: ['./collection-admin-search-result-grid-element.component.scss'],
templateUrl: './collection-admin-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for a collection search result on the admin search page
*/
export class CollectionAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> {
editPath: string;
ngOnInit() {
super.ngOnInit();
this.editPath = getCollectionEditPath(this.dso.uuid);
}
}

View File

@@ -0,0 +1,13 @@
<ds-community-search-result-grid-element [object]="object"
[index]="index"
[linkType]="linkType"
[listID]="listID">
<ul class="list-group list-group-flush">
<li class="list-group-item text-center">
<a class="btn btn-light btn-sm btn-auto my-1 edit-link" [routerLink]="[editPath]">
<i class="fa fa-edit"></i>
</a>
</li>
</ul>
</ds-community-search-result-grid-element>

View File

@@ -0,0 +1,70 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service';
import { SharedModule } from '../../../../../shared/shared.module';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component';
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
import { Community } from '../../../../../core/shared/community.model';
import { CommunityAdminSearchResultListElementComponent } from '../../admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component';
describe('CommunityAdminSearchResultGridElementComponent', () => {
let component: CommunityAdminSearchResultGridElementComponent;
let fixture: ComponentFixture<CommunityAdminSearchResultGridElementComponent>;
let id;
let searchResult;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new CommunitySearchResult();
searchResult.indexableObject = new Community();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([]),
SharedModule
],
declarations: [CommunityAdminSearchResultGridElementComponent],
providers: [
{ provide: TruncatableService, useValue: mockTruncatableService },
{ provide: BitstreamDataService, useValue: {} },
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CommunityAdminSearchResultGridElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render an edit button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.edit-link'));
const link = a.nativeElement.href;
expect(link).toContain(getCommunityEditPath(id));
})
});

View File

@@ -0,0 +1,26 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
import { Community } from '../../../../../core/shared/community.model';
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
@listableObjectComponent(CommunitySearchResult, ViewMode.GridElement, Context.AdminSearch)
@Component({
selector: 'ds-community-admin-search-result-grid-element',
styleUrls: ['./community-admin-search-result-grid-element.component.scss'],
templateUrl: './community-admin-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for a community search result on the admin search page
*/
export class CommunityAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> {
editPath: string;
ngOnInit() {
super.ngOnInit();
this.editPath = getCommunityEditPath(this.dso.uuid);
}
}

View File

@@ -0,0 +1,15 @@
<ng-template dsListableObject>
</ng-template>
<div #badges class="position-absolute ml-1">
<div *ngIf="dso && !dso.isDiscoverable" class="private-badge">
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
</div>
<div *ngIf="dso && dso.isWithdrawn" class="withdrawn-badge">
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
</div>
</div>
<ul #buttons class="list-group list-group-flush">
<li class="list-group-item">
<ds-item-admin-search-result-actions-element class="d-flex justify-content-between" [item]="dso" [small]="true"></ds-item-admin-search-result-actions-element>
</li>
</ul>

View File

@@ -0,0 +1,121 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs/internal/Observable';
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
import { RemoteData } from '../../../../../core/data/remote-data';
import { Bitstream } from '../../../../../core/shared/bitstream.model';
import { Item } from '../../../../../core/shared/item.model';
import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service';
import { SharedModule } from '../../../../../shared/shared.module';
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/testing/utils';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { ItemAdminSearchResultGridElementComponent } from './item-admin-search-result-grid-element.component';
describe('ItemAdminSearchResultGridElementComponent', () => {
let component: ItemAdminSearchResultGridElementComponent;
let fixture: ComponentFixture<ItemAdminSearchResultGridElementComponent>;
let id;
let searchResult;
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new ItemSearchResult();
searchResult.indexableObject = new Item();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule(
{
declarations: [ItemAdminSearchResultGridElementComponent],
imports: [
NoopAnimationsModule,
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([]),
SharedModule
],
providers: [
{ provide: TruncatableService, useValue: mockTruncatableService },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemAdminSearchResultGridElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('when the item is not withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = false;
fixture.detectChanges();
});
it('should not show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = true;
fixture.detectChanges();
});
it('should show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).not.toBeNull();
});
});
describe('when the item is not private', () => {
beforeEach(() => {
component.dso.isDiscoverable = true;
fixture.detectChanges();
});
it('should not show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
component.dso.isDiscoverable = false;
fixture.detectChanges();
});
it('should show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).not.toBeNull();
});
})
});

View File

@@ -0,0 +1,75 @@
import { Component, ComponentFactoryResolver, ElementRef, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { getListableObjectComponent, listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { getItemEditPath } from '../../../../../+item-page/item-page-routing.module';
import { URLCombiner } from '../../../../../core/url-combiner/url-combiner';
import {
ITEM_EDIT_DELETE_PATH,
ITEM_EDIT_MOVE_PATH,
ITEM_EDIT_PRIVATE_PATH,
ITEM_EDIT_PUBLIC_PATH,
ITEM_EDIT_REINSTATE_PATH,
ITEM_EDIT_WITHDRAW_PATH
} from '../../../../../+item-page/edit-item-page/edit-item-page.routing.module';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
@listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch)
@Component({
selector: 'ds-item-admin-search-result-grid-element',
styleUrls: ['./item-admin-search-result-grid-element.component.scss'],
templateUrl: './item-admin-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for an item search result on the admin search page
*/
export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> implements OnInit {
@ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective;
@ViewChild('badges', { static: true }) badges: ElementRef;
@ViewChild('buttons', { static: true }) buttons: ElementRef;
constructor(protected truncatableService: TruncatableService,
protected bitstreamDataService: BitstreamDataService,
private componentFactoryResolver: ComponentFactoryResolver
) {
super(truncatableService, bitstreamDataService);
}
/**
* Setup the dynamic child component
*/
ngOnInit(): void {
super.ngOnInit();
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(
componentFactory,
0,
undefined,
[
[this.badges.nativeElement],
[this.buttons.nativeElement]
]);
(componentRef.instance as any).object = this.object;
(componentRef.instance as any).index = this.index;
(componentRef.instance as any).linkType = this.linkType;
(componentRef.instance as any).listID = this.listID;
}
/**
* Fetch the component depending on the item's relationship type, view mode and context
* @returns {GenericConstructor<Component>}
*/
private getComponent(): GenericConstructor<Component> {
return getListableObjectComponent(this.object.getRenderTypes(), ViewMode.GridElement, undefined)
}
}

View File

@@ -0,0 +1,9 @@
<ds-collection-search-result-list-element [object]="object"
[index]="index"
[linkType]="linkType"
[listID]="listID"></ds-collection-search-result-list-element>
<div>
<a class="btn btn-light mt-1" [routerLink]="[editPath]">
<i class="fa fa-edit"></i> {{"admin.search.collection.edit" | translate}}
</a>
</div>

View File

@@ -0,0 +1,60 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { CollectionAdminSearchResultListElementComponent } from './collection-admin-search-result-list-element.component';
import { TranslateModule } from '@ngx-translate/core';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
import { Collection } from '../../../../../core/shared/collection.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
describe('CollectionAdminSearchResultListElementComponent', () => {
let component: CollectionAdminSearchResultListElementComponent;
let fixture: ComponentFixture<CollectionAdminSearchResultListElementComponent>;
let id;
let searchResult;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new CollectionSearchResult();
searchResult.indexableObject = new Collection();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([])
],
declarations: [CollectionAdminSearchResultListElementComponent],
providers: [{ provide: TruncatableService, useValue: {} }],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CollectionAdminSearchResultListElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render an edit button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a'));
const link = a.nativeElement.href;
expect(link).toContain(getCollectionEditPath(id));
})
});

View File

@@ -0,0 +1,26 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
import { Collection } from '../../../../../core/shared/collection.model';
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
@listableObjectComponent(CollectionSearchResult, ViewMode.ListElement, Context.AdminSearch)
@Component({
selector: 'ds-collection-admin-search-result-list-element',
styleUrls: ['./collection-admin-search-result-list-element.component.scss'],
templateUrl: './collection-admin-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for a collection search result on the admin search page
*/
export class CollectionAdminSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> {
editPath: string;
ngOnInit() {
super.ngOnInit();
this.editPath = getCollectionEditPath(this.dso.uuid);
}
}

View File

@@ -0,0 +1,9 @@
<ds-community-search-result-list-element [object]="object"
[index]="index"
[linkType]="linkType"
[listID]="listID"></ds-community-search-result-list-element>
<div>
<a class="btn btn-light mt-1" [routerLink]="[editPath]">
<i class="fa fa-edit"></i> {{"admin.search.community.edit" | translate}}
</a>
</div>

View File

@@ -0,0 +1,60 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { CommunityAdminSearchResultListElementComponent } from './community-admin-search-result-list-element.component';
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
import { Community } from '../../../../../core/shared/community.model';
describe('CommunityAdminSearchResultListElementComponent', () => {
let component: CommunityAdminSearchResultListElementComponent;
let fixture: ComponentFixture<CommunityAdminSearchResultListElementComponent>;
let id;
let searchResult;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new CommunitySearchResult();
searchResult.indexableObject = new Community();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([])
],
declarations: [CommunityAdminSearchResultListElementComponent],
providers: [{ provide: TruncatableService, useValue: {} }],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CommunityAdminSearchResultListElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render an edit button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a'));
const link = a.nativeElement.href;
expect(link).toContain(getCommunityEditPath(id));
})
});

View File

@@ -0,0 +1,26 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
import { Community } from '../../../../../core/shared/community.model';
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
@listableObjectComponent(CommunitySearchResult, ViewMode.ListElement, Context.AdminSearch)
@Component({
selector: 'ds-community-admin-search-result-list-element',
styleUrls: ['./community-admin-search-result-list-element.component.scss'],
templateUrl: './community-admin-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for a community search result on the admin search page
*/
export class CommunityAdminSearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> {
editPath: string;
ngOnInit() {
super.ngOnInit();
this.editPath = getCommunityEditPath(this.dso.uuid);
}
}

View File

@@ -0,0 +1,12 @@
<div *ngIf="dso && !dso.isDiscoverable" class="private-badge">
<span class="badge badge-danger">{{ "admin.search.item.private" | translate }}</span>
</div>
<div *ngIf="dso && dso.isWithdrawn" class="withdrawn-badge">
<span class="badge badge-warning">{{ "admin.search.item.withdrawn" | translate }}</span>
</div>
<ds-listable-object-component-loader [object]="object"
[viewMode]="viewModes.ListElement"
[index]="index"
[linkType]="linkType"
[listID]="listID"></ds-listable-object-component-loader>
<ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element>

View File

@@ -0,0 +1,101 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { ItemAdminSearchResultListElementComponent } from './item-admin-search-result-list-element.component';
import { Item } from '../../../../../core/shared/item.model';
describe('ItemAdminSearchResultListElementComponent', () => {
let component: ItemAdminSearchResultListElementComponent;
let fixture: ComponentFixture<ItemAdminSearchResultListElementComponent>;
let id;
let searchResult;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
searchResult = new ItemSearchResult();
searchResult.indexableObject = new Item();
searchResult.indexableObject.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([])
],
declarations: [ItemAdminSearchResultListElementComponent],
providers: [{ provide: TruncatableService, useValue: {} }],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemAdminSearchResultListElementComponent);
component = fixture.componentInstance;
component.object = searchResult;
component.linkTypes = CollectionElementLinkType;
component.index = 0;
component.viewModes = ViewMode;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('when the item is not withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = false;
fixture.detectChanges();
});
it('should not show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
component.dso.isWithdrawn = true;
fixture.detectChanges();
});
it('should show the withdrawn badge', () => {
const badge = fixture.debugElement.query(By.css('div.withdrawn-badge'));
expect(badge).not.toBeNull();
});
});
describe('when the item is not private', () => {
beforeEach(() => {
component.dso.isDiscoverable = true;
fixture.detectChanges();
});
it('should not show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
component.dso.isDiscoverable = false;
fixture.detectChanges();
});
it('should show the private badge', () => {
const badge = fixture.debugElement.query(By.css('div.private-badge'));
expect(badge).not.toBeNull();
});
})
});

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Context } from '../../../../../core/shared/context.model';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
@listableObjectComponent(ItemSearchResult, ViewMode.ListElement, Context.AdminSearch)
@Component({
selector: 'ds-item-admin-search-result-list-element',
styleUrls: ['./item-admin-search-result-list-element.component.scss'],
templateUrl: './item-admin-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result on the admin search page
*/
export class ItemAdminSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,27 @@
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 edit-link" [routerLink]="[getEditPath()]" [title]="'admin.search.item.edit' | translate">
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-light my-1 withdraw-link" [routerLink]="[getWithdrawPath()]" [title]="'admin.search.item.withdraw' | translate">
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-light my-1 reinstate-link" [routerLink]="[getReinstatePath()]" [title]="'admin.search.item.reinstate' | translate">
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-light my-1 private-link" [routerLink]="[getPrivatePath()]" [title]="'admin.search.item.make-private' | translate">
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-light my-1 public-link" [routerLink]="[getPublicPath()]" [title]="'admin.search.item.make-public' | translate">
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeletePath()]" [title]="'admin.search.item.delete' | translate">
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
</a>
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 move-link" [routerLink]="[getMovePath()]" [title]="'admin.search.item.move' | translate">
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
</a>

View File

@@ -0,0 +1,144 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { ItemAdminSearchResultActionsComponent } from './item-admin-search-result-actions.component';
import { Item } from '../../../core/shared/item.model';
import {
ITEM_EDIT_DELETE_PATH,
ITEM_EDIT_MOVE_PATH,
ITEM_EDIT_PRIVATE_PATH,
ITEM_EDIT_PUBLIC_PATH,
ITEM_EDIT_REINSTATE_PATH,
ITEM_EDIT_WITHDRAW_PATH
} from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
import { getItemEditPath } from '../../../+item-page/item-page-routing.module';
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
describe('ItemAdminSearchResultActionsComponent', () => {
let component: ItemAdminSearchResultActionsComponent;
let fixture: ComponentFixture<ItemAdminSearchResultActionsComponent>;
let id;
let item;
function init() {
id = '780b2588-bda5-4112-a1cd-0b15000a5339';
item = new Item();
item.uuid = id;
}
beforeEach(async(() => {
init();
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
RouterTestingModule.withRoutes([])
],
declarations: [ItemAdminSearchResultActionsComponent],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemAdminSearchResultActionsComponent);
component = fixture.componentInstance;
component.item = item;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render an edit button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.edit-link'));
const link = button.nativeElement.href;
expect(link).toContain(getItemEditPath(id));
});
it('should render a delete button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.delete-link'));
const link = button.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_DELETE_PATH).toString());
});
it('should render a move button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.move-link'));
const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_MOVE_PATH).toString());
});
describe('when the item is not withdrawn', () => {
beforeEach(() => {
component.item.isWithdrawn = false;
fixture.detectChanges();
});
it('should render a withdraw button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.withdraw-link'));
const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_WITHDRAW_PATH).toString());
});
it('should not render a reinstate button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.reinstate-link'));
expect(a).toBeNull();
});
});
describe('when the item is withdrawn', () => {
beforeEach(() => {
component.item.isWithdrawn = true;
fixture.detectChanges();
});
it('should not render a withdraw button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.withdraw-link'));
expect(a).toBeNull();
});
it('should render a reinstate button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.reinstate-link'));
const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_REINSTATE_PATH).toString());
});
});
describe('when the item is not private', () => {
beforeEach(() => {
component.item.isDiscoverable = true;
fixture.detectChanges();
});
it('should render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.private-link'));
const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_PRIVATE_PATH).toString());
});
it('should not render a make public button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.public-link'));
expect(a).toBeNull();
});
});
describe('when the item is private', () => {
beforeEach(() => {
component.item.isDiscoverable = false;
fixture.detectChanges();
});
it('should not render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.private-link'));
expect(a).toBeNull();
});
it('should render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.public-link'));
const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_PUBLIC_PATH).toString());
});
})
});

View File

@@ -0,0 +1,81 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { getItemEditPath } from '../../../+item-page/item-page-routing.module';
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
import {
ITEM_EDIT_DELETE_PATH,
ITEM_EDIT_MOVE_PATH,
ITEM_EDIT_PRIVATE_PATH,
ITEM_EDIT_PUBLIC_PATH,
ITEM_EDIT_REINSTATE_PATH,
ITEM_EDIT_WITHDRAW_PATH
} from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
@Component({
selector: 'ds-item-admin-search-result-actions-element',
styleUrls: ['./item-admin-search-result-actions.component.scss'],
templateUrl: './item-admin-search-result-actions.component.html'
})
/**
* The component for displaying the actions for a list element for an item search result on the admin search page
*/
export class ItemAdminSearchResultActionsComponent {
/**
* The item to perform the actions on
*/
@Input() public item: Item;
/**
* Whether or not to use small buttons
*/
@Input() public small: boolean;
/**
* Returns the path to the edit page of this item
*/
getEditPath(): string {
return getItemEditPath(this.item.uuid)
}
/**
* Returns the path to the move page of this item
*/
getMovePath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_MOVE_PATH).toString();
}
/**
* Returns the path to the delete page of this item
*/
getDeletePath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_DELETE_PATH).toString();
}
/**
* Returns the path to the withdraw page of this item
*/
getWithdrawPath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_WITHDRAW_PATH).toString();
}
/**
* Returns the path to the reinstate page of this item
*/
getReinstatePath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_REINSTATE_PATH).toString();
}
/**
* Returns the path to the page where the user can make this item private
*/
getPrivatePath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_PRIVATE_PATH).toString();
}
/**
* Returns the path to the page where the user can make this item public
*/
getPublicPath(): string {
return new URLCombiner(this.getEditPath(), ITEM_EDIT_PUBLIC_PATH).toString();
}
}

View File

@@ -350,53 +350,19 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
link: '' link: ''
} as LinkMenuItemModel, } as LinkMenuItemModel,
}, },
/* Admin Search */
/* Search */
{ {
id: 'find', id: 'admin_search',
active: false, active: false,
visible: true, visible: true,
model: { model: {
type: MenuItemType.TEXT, type: MenuItemType.LINK,
text: 'menu.section.find' text: 'menu.section.admin_search',
} as TextMenuItemModel, link: '/admin/search'
} as LinkMenuItemModel,
icon: 'search', icon: 'search',
index: 5 index: 5
}, },
{
id: 'find_items',
parentID: 'find',
active: false,
visible: true,
model: {
type: MenuItemType.LINK,
text: 'menu.section.find_items',
link: '/search'
} as LinkMenuItemModel,
},
{
id: 'find_withdrawn_items',
parentID: 'find',
active: false,
visible: true,
model: {
type: MenuItemType.LINK,
text: 'menu.section.find_withdrawn_items',
link: ''
} as LinkMenuItemModel,
},
{
id: 'find_private_items',
parentID: 'find',
active: false,
visible: true,
model: {
type: MenuItemType.LINK,
text: 'menu.section.find_private_items',
link: ''
} as LinkMenuItemModel,
},
/* Registries */ /* Registries */
{ {
id: 'registries', id: 'registries',

View File

@@ -2,13 +2,42 @@ import { NgModule } from '@angular/core';
import { AdminRegistriesModule } from './admin-registries/admin-registries.module'; import { AdminRegistriesModule } from './admin-registries/admin-registries.module';
import { AdminRoutingModule } from './admin-routing.module'; import { AdminRoutingModule } from './admin-routing.module';
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from '../shared/shared.module';
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
import { SearchPageModule } from '../+search-page/search-page.module';
import { ItemAdminSearchResultListElementComponent } from './admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component';
import { CommunityAdminSearchResultListElementComponent } from './admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component';
import { CollectionAdminSearchResultListElementComponent } from './admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component';
import { ItemAdminSearchResultGridElementComponent } from './admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component';
import { CommunityAdminSearchResultGridElementComponent } from './admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component';
import { CollectionAdminSearchResultGridElementComponent } from './admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component';
import { ItemAdminSearchResultActionsComponent } from './admin-search-page/admin-search-results/item-admin-search-result-actions.component';
@NgModule({ @NgModule({
imports: [ imports: [
AdminRegistriesModule, AdminRegistriesModule,
AdminRoutingModule, AdminRoutingModule,
SharedModule, SharedModule,
SearchPageModule
], ],
declarations: [
AdminSearchPageComponent,
ItemAdminSearchResultListElementComponent,
CommunityAdminSearchResultListElementComponent,
CollectionAdminSearchResultListElementComponent,
ItemAdminSearchResultGridElementComponent,
CommunityAdminSearchResultGridElementComponent,
CollectionAdminSearchResultGridElementComponent,
ItemAdminSearchResultActionsComponent
],
entryComponents: [
ItemAdminSearchResultListElementComponent,
CommunityAdminSearchResultListElementComponent,
CollectionAdminSearchResultListElementComponent,
ItemAdminSearchResultGridElementComponent,
CommunityAdminSearchResultGridElementComponent,
CollectionAdminSearchResultGridElementComponent,
ItemAdminSearchResultActionsComponent
]
}) })
export class AdminModule { export class AdminModule {

View File

@@ -40,6 +40,7 @@ const COLLECTION_EDIT_PATH = 'edit';
dso: CollectionPageResolver, dso: CollectionPageResolver,
breadcrumb: CollectionBreadcrumbResolver breadcrumb: CollectionBreadcrumbResolver
}, },
runGuardsAndResolvers: 'always',
children: [ children: [
{ {
path: COLLECTION_EDIT_PATH, path: COLLECTION_EDIT_PATH,

View File

@@ -39,6 +39,7 @@ const COMMUNITY_EDIT_PATH = 'edit';
dso: CommunityPageResolver, dso: CommunityPageResolver,
breadcrumb: CommunityBreadcrumbResolver breadcrumb: CommunityBreadcrumbResolver
}, },
runGuardsAndResolvers: 'always',
children: [ children: [
{ {
path: COMMUNITY_EDIT_PATH, path: COMMUNITY_EDIT_PATH,

View File

@@ -15,12 +15,12 @@ import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component'; import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
const ITEM_EDIT_WITHDRAW_PATH = 'withdraw'; export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
const ITEM_EDIT_REINSTATE_PATH = 'reinstate'; export const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
const ITEM_EDIT_PRIVATE_PATH = 'private'; export const ITEM_EDIT_PRIVATE_PATH = 'private';
const ITEM_EDIT_PUBLIC_PATH = 'public'; export const ITEM_EDIT_PUBLIC_PATH = 'public';
const ITEM_EDIT_DELETE_PATH = 'delete'; export const ITEM_EDIT_DELETE_PATH = 'delete';
const ITEM_EDIT_MOVE_PATH = 'move'; export const ITEM_EDIT_MOVE_PATH = 'move';
/** /**
* Routing module that handles the routing for the Edit Item page administrator functionality * Routing module that handles the routing for the Edit Item page administrator functionality

View File

@@ -30,6 +30,7 @@ const ITEM_EDIT_PATH = 'edit';
item: ItemPageResolver, item: ItemPageResolver,
breadcrumb: ItemBreadcrumbResolver breadcrumb: ItemBreadcrumbResolver
}, },
runGuardsAndResolvers: 'always',
children: [ children: [
{ {
path: '', path: '',

View File

@@ -1,3 +1,4 @@
import { switchMap } from 'rxjs/operators';
import { HostWindowService } from '../shared/host-window.service'; import { HostWindowService } from '../shared/host-window.service';
import { SidebarService } from '../shared/sidebar/sidebar.service'; import { SidebarService } from '../shared/sidebar/sidebar.service';
import { SearchComponent } from './search.component'; import { SearchComponent } from './search.component';

View File

@@ -14,6 +14,7 @@ import { SearchPageComponent } from './search-page.component';
import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service'; import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service';
import { SearchFilterService } from '../core/shared/search/search-filter.service'; import { SearchFilterService } from '../core/shared/search/search-filter.service';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
import { TranslateModule } from '@ngx-translate/core';
const components = [ const components = [
SearchPageComponent, SearchPageComponent,
@@ -28,7 +29,7 @@ const components = [
CommonModule, CommonModule,
SharedModule, SharedModule,
CoreModule.forRoot(), CoreModule.forRoot(),
StatisticsModule.forRoot(), StatisticsModule.forRoot()
], ],
declarations: components, declarations: components,
providers: [ providers: [

View File

@@ -22,7 +22,8 @@
<ds-search-results [searchResults]="resultsRD$ | async" <ds-search-results [searchResults]="resultsRD$ | async"
[searchConfig]="searchOptions$ | async" [searchConfig]="searchOptions$ | async"
[configuration]="configuration$ | async" [configuration]="configuration$ | async"
[disableHeader]="!searchEnabled"></ds-search-results> [disableHeader]="!searchEnabled"
[context]="context"></ds-search-results>
</div> </div>
</div> </div>
</ds-page-with-sidebar> </ds-page-with-sidebar>

View File

@@ -17,6 +17,7 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu
import { SearchService } from '../core/shared/search/search.service'; import { SearchService } from '../core/shared/search/search.service';
import { currentPath } from '../shared/utils/route.utils'; import { currentPath } from '../shared/utils/route.utils';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Context } from '../core/shared/context.model';
@Component({ @Component({
selector: 'ds-search', selector: 'ds-search',
@@ -84,6 +85,12 @@ export class SearchComponent implements OnInit {
@Input() @Input()
configuration$: Observable<string>; configuration$: Observable<string>;
/**
* The current context
*/
@Input()
context: Context;
/** /**
* Link to the search page * Link to the search page
*/ */

View File

@@ -3,7 +3,6 @@ import { RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component'; import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
import { AuthenticatedGuard } from './core/auth/authenticated.guard'; import { AuthenticatedGuard } from './core/auth/authenticated.guard';
import { Breadcrumb } from './breadcrumbs/breadcrumb/breadcrumb.model';
import { DSpaceObject } from './core/shared/dspace-object.model'; import { DSpaceObject } from './core/shared/dspace-object.model';
import { Community } from './core/shared/community.model'; import { Community } from './core/shared/community.model';
import { getCommunityPageRoute } from './+community-page/community-page-routing.module'; import { getCommunityPageRoute } from './+community-page/community-page-routing.module';
@@ -11,7 +10,6 @@ import { Collection } from './core/shared/collection.model';
import { Item } from './core/shared/item.model'; import { Item } from './core/shared/item.model';
import { getItemPageRoute } from './+item-page/item-page-routing.module'; import { getItemPageRoute } from './+item-page/item-page-routing.module';
import { getCollectionPageRoute } from './+collection-page/collection-page-routing.module'; import { getCollectionPageRoute } from './+collection-page/collection-page-routing.module';
import { BrowseByDSOBreadcrumbResolver } from './+browse-by/browse-by-dso-breadcrumb.resolver';
const ITEM_MODULE_PATH = 'items'; const ITEM_MODULE_PATH = 'items';
@@ -69,7 +67,10 @@ export function getDSOPath(dso: DSpaceObject): string {
{ path: 'workspaceitems', loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule' }, { path: 'workspaceitems', loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule' },
{ path: 'workflowitems', loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule' }, { path: 'workflowitems', loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule' },
{ path: '**', pathMatch: 'full', component: PageNotFoundComponent }, { path: '**', pathMatch: 'full', component: PageNotFoundComponent },
]) ],
{
onSameUrlNavigation: 'reload',
})
], ],
exports: [RouterModule], exports: [RouterModule],
}) })

View File

@@ -20,7 +20,7 @@ import { ITEM } from '../shared/item.resource-type';
import { import {
configureRequest, configureRequest,
filterSuccessfulResponses, filterSuccessfulResponses,
getRequestFromRequestHref, getRequestFromRequestHref, getRequestFromRequestUUID,
getResponseFromEntry getResponseFromEntry
} from '../shared/operators'; } from '../shared/operators';
import { URLCombiner } from '../url-combiner/url-combiner'; import { URLCombiner } from '../url-combiner/url-combiner';
@@ -180,14 +180,17 @@ export class ItemDataService extends DataService<Item> {
const patchOperation = [{ const patchOperation = [{
op: 'replace', path: '/withdrawn', value: withdrawn op: 'replace', path: '/withdrawn', value: withdrawn
}]; }];
this.requestService.removeByHrefSubstring('/discover');
return this.getItemWithdrawEndpoint(itemId).pipe( return this.getItemWithdrawEndpoint(itemId).pipe(
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => map((endpointURL: string) =>
new PatchRequest(this.requestService.generateRequestId(), endpointURL, patchOperation) new PatchRequest(this.requestService.generateRequestId(), endpointURL, patchOperation)
), ),
configureRequest(this.requestService), configureRequest(this.requestService),
map((request: RestRequest) => request.href), map((request: RestRequest) => request.uuid),
getRequestFromRequestHref(this.requestService), getRequestFromRequestUUID(this.requestService),
filter((requestEntry: RequestEntry) => requestEntry.completed),
map((requestEntry: RequestEntry) => requestEntry.response) map((requestEntry: RequestEntry) => requestEntry.response)
); );
} }
@@ -201,14 +204,17 @@ export class ItemDataService extends DataService<Item> {
const patchOperation = [{ const patchOperation = [{
op: 'replace', path: '/discoverable', value: discoverable op: 'replace', path: '/discoverable', value: discoverable
}]; }];
this.requestService.removeByHrefSubstring('/discover');
return this.getItemDiscoverableEndpoint(itemId).pipe( return this.getItemDiscoverableEndpoint(itemId).pipe(
distinctUntilChanged(), distinctUntilChanged(),
map((endpointURL: string) => map((endpointURL: string) =>
new PatchRequest(this.requestService.generateRequestId(), endpointURL, patchOperation) new PatchRequest(this.requestService.generateRequestId(), endpointURL, patchOperation)
), ),
configureRequest(this.requestService), configureRequest(this.requestService),
map((request: RestRequest) => request.href), map((request: RestRequest) => request.uuid),
getRequestFromRequestHref(this.requestService), getRequestFromRequestUUID(this.requestService),
filter((requestEntry: RequestEntry) => requestEntry.completed),
map((requestEntry: RequestEntry) => requestEntry.response) map((requestEntry: RequestEntry) => requestEntry.response)
); );
} }

View File

@@ -9,6 +9,7 @@ import {
SetQueryParameterAction, SetQueryParameterAction,
SetQueryParametersAction SetQueryParametersAction
} from './route.actions'; } from './route.actions';
import { isNotEmpty } from '../../shared/empty.util';
/** /**
* Interface to represent the parameter state of a current route in the store * Interface to represent the parameter state of a current route in the store
@@ -81,7 +82,8 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
* @param paramType The type of parameter to set: route or query parameter * @param paramType The type of parameter to set: route or query parameter
*/ */
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState { function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
return Object.assign({}, state, { [paramType]: { [action.payload.key]: action.payload.value } }); const param = isNotEmpty(action.payload) ? { [paramType]: { [action.payload.key]: action.payload.value } } : {};
return Object.assign({}, state, param);
} }
/** /**

View File

@@ -6,7 +6,7 @@ import { combineLatest, Observable } from 'rxjs';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { AddParameterAction, SetParameterAction, SetParametersAction, SetQueryParametersAction } from './route.actions'; import { AddParameterAction, SetParameterAction, SetParametersAction, SetQueryParameterAction, SetQueryParametersAction } from './route.actions';
import { CoreState } from '../core.reducers'; import { CoreState } from '../core.reducers';
import { coreSelector } from '../core.selectors'; import { coreSelector } from '../core.selectors';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
@@ -194,6 +194,10 @@ export class RouteService {
this.store.dispatch(new SetParameterAction(key, value)); this.store.dispatch(new SetParameterAction(key, value));
} }
public setQueryParameter(key, value) {
this.store.dispatch(new SetQueryParameterAction(key, value));
}
/** /**
* Sets the current route parameters and query parameters in the store * Sets the current route parameters and query parameters in the store
*/ */

View File

@@ -10,4 +10,5 @@ export enum Context {
Workspace = 'workspace', Workspace = 'workspace',
AdminMenu = 'adminMenu', AdminMenu = 'adminMenu',
SubmissionModal = 'submissionModal', SubmissionModal = 'submissionModal',
AdminSearch = 'adminSearch',
} }

View File

@@ -1,6 +1,10 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
</ds-grid-thumbnail> </ds-grid-thumbnail>
@@ -17,7 +21,8 @@
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4> <h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part> </ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted"> <p *ngIf="dso.hasMetadata('creativework.datePublished')"
class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1"> <ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span> <span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part> </ds-truncatable-part>
@@ -28,9 +33,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -1,6 +1,10 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
</ds-grid-thumbnail> </ds-grid-thumbnail>
@@ -17,7 +21,8 @@
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4> <h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part> </ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted"> <p *ngIf="dso.hasMetadata('creativework.datePublished')"
class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1"> <ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span> <span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part> </ds-truncatable-part>
@@ -28,9 +33,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -1,6 +1,10 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
</ds-grid-thumbnail> </ds-grid-thumbnail>
@@ -33,9 +37,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -1,6 +1,10 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
</ds-grid-thumbnail> </ds-grid-thumbnail>
@@ -17,7 +21,8 @@
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('organization.legalName')"></h4> <h4 class="card-title" [innerHTML]="firstMetadataValue('organization.legalName')"></h4>
</ds-truncatable-part> </ds-truncatable-part>
<p *ngIf="dso.hasMetadata('organization.foundingDate')" class="item-date card-text text-muted"> <p *ngIf="dso.hasMetadata('organization.foundingDate')"
class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1"> <ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('organization.foundingDate')"></span> <span [innerHTML]="firstMetadataValue('organization.foundingDate')"></span>
</ds-truncatable-part> </ds-truncatable-part>
@@ -25,7 +30,8 @@
<p *ngIf="dso.hasMetadata('organization.address.addressCountry')" <p *ngIf="dso.hasMetadata('organization.address.addressCountry')"
class="item-location card-text"> class="item-location card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3"> <ds-truncatable-part [id]="dso.id" [minLines]="3">
<span class="item-country">{{firstMetadataValue('organization.address.addressCountry')}}</span> <span
class="item-country">{{firstMetadataValue('organization.address.addressCountry')}}</span>
<span *ngIf="dso.hasMetadata('organization.address.addressLocality')" class="item-city"> <span *ngIf="dso.hasMetadata('organization.address.addressLocality')" class="item-city">
<span>, </span> <span>, </span>
{{firstMetadataValue('organization.address.addressLocality')}} {{firstMetadataValue('organization.address.addressLocality')}}
@@ -33,9 +39,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -1,6 +1,9 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -16,7 +19,8 @@
<div class="card-body"> <div class="card-body">
<ds-item-type-badge [object]="dso"></ds-item-type-badge> <ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"></h4> <h4 class="card-title"
[innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')"></h4>
</ds-truncatable-part> </ds-truncatable-part>
<p *ngIf="dso.hasMetadata('person.email')" class="item-email card-text text-muted"> <p *ngIf="dso.hasMetadata('person.email')" class="item-email card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1"> <ds-truncatable-part [id]="dso.id" [minLines]="1">
@@ -29,9 +33,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -1,6 +1,10 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width"> <ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
</ds-grid-thumbnail> </ds-grid-thumbnail>
@@ -23,9 +27,11 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -0,0 +1,19 @@
import { of as observableOf } from 'rxjs/internal/observable/of';
export const mockTruncatableService: any = {
/* tslint:disable:no-empty */
isCollapsed: (id: string) => {
if (id === '1') {
return observableOf(true)
} else {
return observableOf(false);
}
},
expand: (id: string) => {
},
collapse: (id: string) => {
},
toggle: (id: string) => {
}
/* tslint:enable:no-empty */
};

View File

@@ -1,6 +1,8 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { ListableObject } from '../listable-object.model'; import { ListableObject } from '../listable-object.model';
import { CollectionElementLinkType } from '../../collection-element-link.type'; import { CollectionElementLinkType } from '../../collection-element-link.type';
import { Context } from '../../../../core/shared/context.model';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@Component({ @Component({
selector: 'ds-abstract-object-element', selector: 'ds-abstract-object-element',
@@ -22,8 +24,23 @@ export class AbstractListableElementComponent<T extends ListableObject> {
*/ */
@Input() listID: string; @Input() listID: string;
/**
* The index of this element
*/
@Input() index: number;
/** /**
* The available link types * The available link types
*/ */
linkTypes = CollectionElementLinkType; linkTypes = CollectionElementLinkType;
/**
* The available view modes
*/
viewModes = ViewMode;
/**
* The available contexts
*/
contexts = Context;
} }

View File

@@ -14,4 +14,5 @@
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', dso.id]" class="lead btn btn-primary viewButton">View</a> <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', dso.id]" class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
<ng-content></ng-content>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { Store } from '@ngrx/store';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service'; import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service'; import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
@@ -62,6 +63,7 @@ describe('CollectionSearchResultGridElementComponent', () => {
{ provide: UUIDService, useValue: {} }, { provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} }, { provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} },
{ provide: BitstreamDataService, useValue: {} },
{ provide: CommunityDataService, useValue: {} }, { provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} }, { provide: HALEndpointService, useValue: {} },
{ provide: NotificationsService, useValue: {} }, { provide: NotificationsService, useValue: {} },

View File

@@ -14,4 +14,5 @@
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', dso.id]" class="lead btn btn-primary viewButton">View</a> <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', dso.id]" class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
<ng-content></ng-content>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { Store } from '@ngrx/store';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service'; import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service'; import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
@@ -62,6 +63,7 @@ describe('CommunitySearchResultGridElementComponent', () => {
{ provide: UUIDService, useValue: {} }, { provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} }, { provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} },
{ provide: BitstreamDataService, useValue: {} },
{ provide: CommunityDataService, useValue: {} }, { provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} }, { provide: HALEndpointService, useValue: {} },
{ provide: NotificationsService, useValue: {} }, { provide: NotificationsService, useValue: {} },

View File

@@ -1,5 +1,6 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'"> <div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<ds-truncatable [id]="dso.id">
<ng-content></ng-content>
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
@@ -37,5 +38,7 @@
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>
</div>
</ds-truncatable> </ds-truncatable>
<ng-content></ng-content>
</div>

View File

@@ -7,6 +7,7 @@ import { Item } from '../../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
@listableObjectComponent('PublicationSearchResult', ViewMode.GridElement) @listableObjectComponent('PublicationSearchResult', ViewMode.GridElement)
@listableObjectComponent(ItemSearchResult, ViewMode.GridElement)
@Component({ @Component({
selector: 'ds-publication-search-result-grid-element', selector: 'ds-publication-search-result-grid-element',
styleUrls: ['./publication-search-result-grid-element.component.scss'], styleUrls: ['./publication-search-result-grid-element.component.scss'],

View File

@@ -27,9 +27,9 @@ describe('PaginatedSearchOptions', () => {
'query=search query&' + 'query=search query&' +
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' + 'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
'dsoType=ITEM&' + 'dsoType=ITEM&' +
'f.test=value,query&' + 'f.test=value&' +
'f.example=another value,query&' + 'f.example=another value&' +
'f.example=second value,query' 'f.example=second value'
); );
}); });

View File

@@ -1,7 +1,6 @@
/** /**
* Represents a search filter * Represents a search filter
*/ */
import { hasValue } from '../empty.util';
export class SearchFilter { export class SearchFilter {
key: string; key: string;
@@ -11,10 +10,6 @@ export class SearchFilter {
constructor(key: string, values: string[], operator?: string) { constructor(key: string, values: string[], operator?: string) {
this.key = key; this.key = key;
this.values = values; this.values = values;
if (hasValue(operator)) {
this.operator = operator; this.operator = operator;
} else {
this.operator = 'query';
}
} }
} }

View File

@@ -2,7 +2,9 @@
[routerLink]="[searchLink]" [routerLink]="[searchLink]"
[queryParams]="addQueryParams" queryParamsHandling="merge"> [queryParams]="addQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/> <input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{filterValue.value}}</span> <span class="filter-value px-1">
{{ 'search.filters.' + filterConfig.name + '.' + filterValue.value | translate: {default: filterValue.value} }}
</span>
<span class="float-right filter-value-count ml-auto"> <span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span> <span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
</span> </span>

View File

@@ -2,5 +2,7 @@
[routerLink]="[searchLink]" [routerLink]="[searchLink]"
[queryParams]="removeQueryParams" queryParamsHandling="merge"> [queryParams]="removeQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/> <input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1 text-capitalize">{{selectedValue.label}}</span> <span class="filter-value pl-1 text-capitalize">
{{ 'search.filters.' + filterConfig.name + '.' + selectedValue.value | translate: {default: selectedValue.value} }}
</span>
</a> </a>

View File

@@ -1,6 +1,6 @@
<a class="badge badge-primary mr-1 mb-1 text-capitalize" <a class="badge badge-primary mr-1 mb-1 text-capitalize"
[routerLink]="searchLink" [routerLink]="searchLink"
[queryParams]="(removeParameters | async)" queryParamsHandling="merge"> [queryParams]="(removeParameters | async)" queryParamsHandling="merge">
{{('search.filters.applied.' + key) | translate}}: {{normalizeFilterValue(value)}} {{('search.filters.applied.' + key) | translate}}: {{'search.filters.' + filterName + '.' + value | translate: {default: normalizeFilterValue(value)} }}
<span> ×</span> <span> ×</span>
</a> </a>

View File

@@ -22,6 +22,11 @@ export class SearchLabelComponent implements OnInit {
searchLink: string; searchLink: string;
removeParameters: Observable<Params>; removeParameters: Observable<Params>;
/**
* The name of the filter without the f. prefix
*/
filterName: string;
/** /**
* Initialize the instance variable * Initialize the instance variable
*/ */
@@ -33,6 +38,7 @@ export class SearchLabelComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.searchLink = this.getSearchLink(); this.searchLink = this.getSearchLink();
this.removeParameters = this.getRemoveParams(); this.removeParameters = this.getRemoveParams();
this.filterName = this.getFilterName();
} }
/** /**
@@ -74,4 +80,8 @@ export class SearchLabelComponent implements OnInit {
const pattern = /,authority*$/g; const pattern = /,authority*$/g;
return value.replace(pattern, ''); return value.replace(pattern, '');
} }
private getFilterName(): string {
return this.key.startsWith('f.') ? this.key.substring(2) : this.key;
}
} }

View File

@@ -21,9 +21,9 @@ describe('SearchOptions', () => {
'query=search query&' + 'query=search query&' +
'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' + 'scope=0fde1ecb-82cc-425a-b600-ac3576d76b47&' +
'dsoType=ITEM&' + 'dsoType=ITEM&' +
'f.test=value,query&' + 'f.test=value&' +
'f.example=another value,query&' + 'f.example=another value&' +
'f.example=second value,query' 'f.example=second value'
); );
}); });

View File

@@ -50,7 +50,7 @@ export class SearchOptions {
if (isNotEmpty(this.filters)) { if (isNotEmpty(this.filters)) {
this.filters.forEach((filter: SearchFilter) => { this.filters.forEach((filter: SearchFilter) => {
filter.values.forEach((value) => { filter.values.forEach((value) => {
const filterValue = value.includes(',') ? `${value}` : `${value},${filter.operator}`; const filterValue = value.includes(',') ? `${value}` : value + (filter.operator ? ',' + filter.operator : '');
args.push(`${filter.key}=${filterValue}`) args.push(`${filter.key}=${filterValue}`)
}); });
}); });

View File

@@ -6,7 +6,7 @@ import { NouisliderModule } from 'ng2-nouislider';
import { NgbDatepickerModule, NgbModule, NgbTimepickerModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbDatepickerModule, NgbModule, NgbTimepickerModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core';
import { NgxPaginationModule } from 'ngx-pagination'; import { NgxPaginationModule } from 'ngx-pagination';
import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component'; import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component';
@@ -177,6 +177,7 @@ import { ImportableListItemControlComponent } from './object-collection/shared/i
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component'; import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
import { SortablejsModule } from 'ngx-sortablejs'; import { SortablejsModule } from 'ngx-sortablejs';
import { MissingTranslationHelper } from './translate/missing-translation.helper';
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -194,7 +195,6 @@ const MODULES = [
NgxPaginationModule, NgxPaginationModule,
ReactiveFormsModule, ReactiveFormsModule,
RouterModule, RouterModule,
TranslateModule,
NouisliderModule, NouisliderModule,
MomentModule, MomentModule,
TextMaskModule, TextMaskModule,
@@ -203,7 +203,11 @@ const MODULES = [
]; ];
const ROOT_MODULES = [ const ROOT_MODULES = [
TooltipModule.forRoot() TranslateModule.forChild({
missingTranslationHandler: { provide: MissingTranslationHandler, useClass: MissingTranslationHelper },
useDefaultLang: true
}),
TooltipModule.forRoot(),
]; ];
const PIPES = [ const PIPES = [
@@ -339,7 +343,8 @@ const COMPONENTS = [
SelectableListItemControlComponent, SelectableListItemControlComponent,
ExternalSourceEntryImportModalComponent, ExternalSourceEntryImportModalComponent,
ImportableListItemControlComponent, ImportableListItemControlComponent,
ExistingMetadataListElementComponent ExistingMetadataListElementComponent,
PublicationSearchResultListElementComponent,
]; ];
const ENTRY_COMPONENTS = [ const ENTRY_COMPONENTS = [
@@ -402,7 +407,7 @@ const ENTRY_COMPONENTS = [
DsDynamicLookupRelationSearchTabComponent, DsDynamicLookupRelationSearchTabComponent,
DsDynamicLookupRelationSelectionTabComponent, DsDynamicLookupRelationSelectionTabComponent,
DsDynamicLookupRelationExternalSourceTabComponent, DsDynamicLookupRelationExternalSourceTabComponent,
ExternalSourceEntryImportModalComponent ExternalSourceEntryImportModalComponent,
]; ];
const SHARED_ITEM_PAGE_COMPONENTS = [ const SHARED_ITEM_PAGE_COMPONENTS = [
@@ -435,8 +440,8 @@ const DIRECTIVES = [
@NgModule({ @NgModule({
imports: [ imports: [
...ROOT_MODULES,
...MODULES, ...MODULES,
...ROOT_MODULES
], ],
declarations: [ declarations: [
...PIPES, ...PIPES,
@@ -444,8 +449,7 @@ const DIRECTIVES = [
...DIRECTIVES, ...DIRECTIVES,
...ENTRY_COMPONENTS, ...ENTRY_COMPONENTS,
...SHARED_ITEM_PAGE_COMPONENTS, ...SHARED_ITEM_PAGE_COMPONENTS,
PublicationSearchResultListElementComponent,
ExistingMetadataListElementComponent
], ],
providers: [ providers: [
...PROVIDERS ...PROVIDERS

View File

@@ -0,0 +1,18 @@
import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx-translate/core';
/**
* Class to handle missing translations for the ngx-translate library
*/
export class MissingTranslationHelper implements MissingTranslationHandler {
/**
* Called when there is not translation for a specific key
* Will return the 'default' parameter of the translate pipe, if there is one available
* @param params
*/
handle(params: MissingTranslationHandlerParams) {
if (params.interpolateParams) {
return (params.interpolateParams as any).default || params.key;
}
return params.key;
}
}

View File

@@ -1,5 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { mockTruncatableService } from '../mocks/mock-trucatable.service';
import { TruncatableComponent } from './truncatable.component'; import { TruncatableComponent } from './truncatable.component';
import { TruncatableService } from './truncatable.service'; import { TruncatableService } from './truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
@@ -10,29 +11,12 @@ describe('TruncatableComponent', () => {
let fixture: ComponentFixture<TruncatableComponent>; let fixture: ComponentFixture<TruncatableComponent>;
const identifier = '1234567890'; const identifier = '1234567890';
let truncatableService; let truncatableService;
const truncatableServiceStub: any = {
/* tslint:disable:no-empty */
isCollapsed: (id: string) => {
if (id === '1') {
return observableOf(true)
} else {
return observableOf(false);
}
},
expand: (id: string) => {
},
collapse: (id: string) => {
},
toggle: (id: string) => {
}
/* tslint:enable:no-empty */
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [TruncatableComponent], declarations: [TruncatableComponent],
providers: [ providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub }, { provide: TruncatableService, useValue: mockTruncatableService },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(TruncatableComponent, { }).overrideComponent(TruncatableComponent, {