mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge remote-tracking branch '4Science-bitbucket/main' into CST-5339
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal, NgbModalConfig } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Angulartics2GoogleAnalytics } from 'angulartics2';
|
import { Angulartics2GoogleAnalytics } from 'angulartics2';
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service';
|
|||||||
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
||||||
import { getDefaultThemeConfig } from '../config/config.util';
|
import { getDefaultThemeConfig } from '../config/config.util';
|
||||||
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
|
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
import { ModalBeforeDismiss } from './shared/interfaces/modal-before-dismiss.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
@@ -106,6 +107,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
private localeService: LocaleService,
|
private localeService: LocaleService,
|
||||||
private breadcrumbsService: BreadcrumbsService,
|
private breadcrumbsService: BreadcrumbsService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
|
private modalConfig: NgbModalConfig,
|
||||||
@Optional() private cookiesService: KlaroService,
|
@Optional() private cookiesService: KlaroService,
|
||||||
@Optional() private googleAnalyticsService: GoogleAnalyticsService,
|
@Optional() private googleAnalyticsService: GoogleAnalyticsService,
|
||||||
) {
|
) {
|
||||||
@@ -166,6 +168,16 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
/** Implement behavior for interface {@link ModalBeforeDismiss} */
|
||||||
|
this.modalConfig.beforeDismiss = async function () {
|
||||||
|
if (typeof this?.componentInstance?.beforeDismiss === 'function') {
|
||||||
|
return this.componentInstance.beforeDismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back to default behavior
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
this.isAuthBlocking$ = this.store.pipe(select(isAuthenticationBlocking)).pipe(
|
this.isAuthBlocking$ = this.store.pipe(select(isAuthenticationBlocking)).pipe(
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
|
@@ -3,6 +3,9 @@
|
|||||||
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
|
|
||||||
@listableObjectComponent('JournalIssue', ViewMode.StandalonePage)
|
@listableObjectComponent('JournalIssue', ViewMode.StandalonePage)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Journal Issue
|
* The component for displaying metadata and relations of an item of the type Journal Issue
|
||||||
*/
|
*/
|
||||||
export class JournalIssueComponent extends ItemComponent {
|
export class JournalIssueComponent extends VersionedItemComponent {
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,9 @@
|
|||||||
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
|
|
||||||
@listableObjectComponent('JournalVolume', ViewMode.StandalonePage)
|
@listableObjectComponent('JournalVolume', ViewMode.StandalonePage)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Journal Volume
|
* The component for displaying metadata and relations of an item of the type Journal Volume
|
||||||
*/
|
*/
|
||||||
export class JournalVolumeComponent extends ItemComponent {
|
export class JournalVolumeComponent extends VersionedItemComponent {
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,9 @@
|
|||||||
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,7 +47,8 @@
|
|||||||
<ds-tabbed-related-entities-search [item]="object"
|
<ds-tabbed-related-entities-search [item]="object"
|
||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isJournalOfPublication',
|
label: 'isJournalOfPublication',
|
||||||
filter: 'isJournalOfPublication'
|
filter: 'isJournalOfPublication',
|
||||||
|
configuration: 'default-relationships'
|
||||||
}]">
|
}]">
|
||||||
</ds-tabbed-related-entities-search>
|
</ds-tabbed-related-entities-search>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -29,6 +29,11 @@ import { TruncatableService } from '../../../../shared/truncatable/truncatable.s
|
|||||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||||
import { JournalComponent } from './journal.component';
|
import { JournalComponent } from './journal.component';
|
||||||
import { RouteService } from '../../../../core/services/route.service';
|
import { RouteService } from '../../../../core/services/route.service';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||||
|
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||||
|
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
|
||||||
let comp: JournalComponent;
|
let comp: JournalComponent;
|
||||||
let fixture: ComponentFixture<JournalComponent>;
|
let fixture: ComponentFixture<JournalComponent>;
|
||||||
@@ -65,12 +70,15 @@ describe('JournalComponent', () => {
|
|||||||
};
|
};
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useClass: TranslateLoaderMock
|
useClass: TranslateLoaderMock
|
||||||
}
|
}
|
||||||
})],
|
}),
|
||||||
|
RouterTestingModule,
|
||||||
|
],
|
||||||
declarations: [JournalComponent, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [JournalComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ItemDataService, useValue: {} },
|
{ provide: ItemDataService, useValue: {} },
|
||||||
@@ -86,7 +94,11 @@ describe('JournalComponent', () => {
|
|||||||
{ provide: DSOChangeAnalyzer, useValue: {} },
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
{ provide: NotificationsService, useValue: {} },
|
{ provide: NotificationsService, useValue: {} },
|
||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: VersionHistoryDataService, useValue: {} },
|
||||||
|
{ provide: VersionDataService, useValue: {} },
|
||||||
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
|
{ provide: WorkspaceitemDataService, useValue: {} },
|
||||||
|
{ provide: SearchService, useValue: {} },
|
||||||
{ provide: RouteService, useValue: {} }
|
{ provide: RouteService, useValue: {} }
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
|
|
||||||
@listableObjectComponent('Journal', ViewMode.StandalonePage)
|
@listableObjectComponent('Journal', ViewMode.StandalonePage)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Journal
|
* The component for displaying metadata and relations of an item of the type Journal
|
||||||
*/
|
*/
|
||||||
export class JournalComponent extends ItemComponent {
|
export class JournalComponent extends VersionedItemComponent {
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,9 @@
|
|||||||
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,12 +57,12 @@
|
|||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isOrgUnitOfPerson',
|
label: 'isOrgUnitOfPerson',
|
||||||
filter: 'isOrgUnitOfPerson',
|
filter: 'isOrgUnitOfPerson',
|
||||||
configuration: 'person'
|
configuration: 'person-relationships'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'isOrgUnitOfProject',
|
label: 'isOrgUnitOfProject',
|
||||||
filter: 'isOrgUnitOfProject',
|
filter: 'isOrgUnitOfProject',
|
||||||
configuration: 'project'
|
configuration: 'project-relationships'
|
||||||
}]">
|
}]">
|
||||||
</ds-tabbed-related-entities-search>
|
</ds-tabbed-related-entities-search>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
|
|
||||||
@listableObjectComponent('OrgUnit', ViewMode.StandalonePage)
|
@listableObjectComponent('OrgUnit', ViewMode.StandalonePage)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
||||||
*/
|
*/
|
||||||
export class OrgUnitComponent extends ItemComponent {
|
export class OrgUnitComponent extends VersionedItemComponent {
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,11 @@
|
|||||||
<h2 class="item-page-title-field mr-auto">
|
<h2 class="item-page-title-field mr-auto">
|
||||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="getTitleMetadataValues()" [separator]="', '"></ds-metadata-values>
|
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="getTitleMetadataValues()" [separator]="', '"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2">
|
<div class="pl-2 space-children-mr">
|
||||||
<ds-dso-page-orcid-button [pageRoute]="itemPageRoute" [dso]="object" class="mr-2"></ds-dso-page-orcid-button>
|
<ds-dso-page-orcid-button [pageRoute]="itemPageRoute" [dso]="object" class="mr-2"></ds-dso-page-orcid-button>
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
|
||||||
<ds-person-page-claim-button [object]="object"></ds-person-page-claim-button>
|
<ds-person-page-claim-button [object]="object"></ds-person-page-claim-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,7 +66,8 @@
|
|||||||
<ds-tabbed-related-entities-search [item]="object"
|
<ds-tabbed-related-entities-search [item]="object"
|
||||||
[relationTypes]="[{
|
[relationTypes]="[{
|
||||||
label: 'isAuthorOfPublication',
|
label: 'isAuthorOfPublication',
|
||||||
filter: 'isAuthorOfPublication'
|
filter: 'isAuthorOfPublication',
|
||||||
|
configuration: 'default-relationships'
|
||||||
}]">
|
}]">
|
||||||
</ds-tabbed-related-entities-search>
|
</ds-tabbed-related-entities-search>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import {
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
listableObjectComponent
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
} from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
|
||||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
||||||
|
|
||||||
@listableObjectComponent('Person', ViewMode.StandalonePage)
|
@listableObjectComponent('Person', ViewMode.StandalonePage)
|
||||||
@@ -15,7 +13,7 @@ import { MetadataValue } from '../../../../core/shared/metadata.models';
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Person
|
* The component for displaying metadata and relations of an item of the type Person
|
||||||
*/
|
*/
|
||||||
export class PersonComponent extends ItemComponent {
|
export class PersonComponent extends VersionedItemComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata values to be used for the page title.
|
* Returns the metadata values to be used for the page title.
|
||||||
@@ -36,4 +34,5 @@ export class PersonComponent extends ItemComponent {
|
|||||||
}
|
}
|
||||||
return metadataValues;
|
return metadataValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,9 @@
|
|||||||
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../../../../item-page/simple/item-types/versioned-item/versioned-item.component';
|
||||||
|
|
||||||
@listableObjectComponent('Project', ViewMode.StandalonePage)
|
@listableObjectComponent('Project', ViewMode.StandalonePage)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,5 +12,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
/**
|
/**
|
||||||
* The component for displaying metadata and relations of an item of the type Project
|
* The component for displaying metadata and relations of an item of the type Project
|
||||||
*/
|
*/
|
||||||
export class ProjectComponent extends ItemComponent {
|
export class ProjectComponent extends VersionedItemComponent {
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,9 @@
|
|||||||
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pl-2 space-children-mr">
|
<div class="pl-2 space-children-mr">
|
||||||
|
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||||
|
[tooltipMsgCreate]="'item.page.version.create'"
|
||||||
|
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -33,6 +33,11 @@ import {
|
|||||||
import { PublicationComponent } from './publication.component';
|
import { PublicationComponent } from './publication.component';
|
||||||
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
||||||
import { RouteService } from '../../../../core/services/route.service';
|
import { RouteService } from '../../../../core/services/route.service';
|
||||||
|
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||||
|
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
|
||||||
const iiifEnabledMap: MetadataMap = {
|
const iiifEnabledMap: MetadataMap = {
|
||||||
'dspace.iiif.enabled': [iiifEnabled],
|
'dspace.iiif.enabled': [iiifEnabled],
|
||||||
@@ -64,12 +69,15 @@ describe('PublicationComponent', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useClass: TranslateLoaderMock
|
useClass: TranslateLoaderMock
|
||||||
}
|
}
|
||||||
})],
|
}),
|
||||||
|
RouterTestingModule,
|
||||||
|
],
|
||||||
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ItemDataService, useValue: {} },
|
{ provide: ItemDataService, useValue: {} },
|
||||||
@@ -85,7 +93,11 @@ describe('PublicationComponent', () => {
|
|||||||
{ provide: HttpClient, useValue: {} },
|
{ provide: HttpClient, useValue: {} },
|
||||||
{ provide: DSOChangeAnalyzer, useValue: {} },
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: VersionHistoryDataService, useValue: {} },
|
||||||
|
{ provide: VersionDataService, useValue: {} },
|
||||||
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
|
{ provide: WorkspaceitemDataService, useValue: {} },
|
||||||
|
{ provide: SearchService, useValue: {} },
|
||||||
{ provide: RouteService, useValue: mockRouteService }
|
{ provide: RouteService, useValue: mockRouteService }
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { ItemComponent } from '../shared/item.component';
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { VersionedItemComponent } from '../versioned-item/versioned-item.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents a publication Item page
|
* Component that represents a publication Item page
|
||||||
@@ -14,6 +14,6 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
templateUrl: './publication.component.html',
|
templateUrl: './publication.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PublicationComponent extends ItemComponent {
|
export class PublicationComponent extends VersionedItemComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import {Observable, of as observableOf} from 'rxjs';
|
import { Observable, 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 { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
@@ -32,8 +32,13 @@ import { ItemComponent } from './item.component';
|
|||||||
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../../../shared/testing/utils.test';
|
||||||
import { RouteService } from '../../../../core/services/route.service';
|
import { RouteService } from '../../../../core/services/route.service';
|
||||||
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
import { MetadataValue } from '../../../../core/shared/metadata.models';
|
||||||
import {AuthorizationDataService} from '../../../../core/data/feature-authorization/authorization-data.service';
|
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
||||||
import {ResearcherProfileService} from '../../../../core/profile/researcher-profile.service';
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||||
|
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ResearcherProfileService } from '../../../../core/profile/researcher-profile.service';
|
||||||
|
|
||||||
export const iiifEnabled = Object.assign(new MetadataValue(),{
|
export const iiifEnabled = Object.assign(new MetadataValue(),{
|
||||||
'value': 'true',
|
'value': 'true',
|
||||||
@@ -77,12 +82,15 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
useClass: TranslateLoaderMock
|
useClass: TranslateLoaderMock
|
||||||
}
|
}
|
||||||
})],
|
}),
|
||||||
|
RouterTestingModule,
|
||||||
|
],
|
||||||
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ItemDataService, useValue: {} },
|
{ provide: ItemDataService, useValue: {} },
|
||||||
@@ -96,9 +104,13 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
|||||||
{ provide: HALEndpointService, useValue: {} },
|
{ provide: HALEndpointService, useValue: {} },
|
||||||
{ provide: HttpClient, useValue: {} },
|
{ provide: HttpClient, useValue: {} },
|
||||||
{ provide: DSOChangeAnalyzer, useValue: {} },
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: VersionHistoryDataService, useValue: {} },
|
||||||
|
{ provide: VersionDataService, useValue: {} },
|
||||||
{ provide: NotificationsService, useValue: {} },
|
{ provide: NotificationsService, useValue: {} },
|
||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
|
{ provide: WorkspaceitemDataService, useValue: {} },
|
||||||
|
{ provide: SearchService, useValue: {} },
|
||||||
{ provide: RouteService, useValue: {} },
|
{ provide: RouteService, useValue: {} },
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationService },
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
{ provide: ResearcherProfileService, useValue: {} }
|
{ provide: ResearcherProfileService, useValue: {} }
|
||||||
|
@@ -62,6 +62,8 @@ export class VersionedItemComponent extends ItemComponent {
|
|||||||
activeModal.componentInstance.createVersionEvent.pipe(
|
activeModal.componentInstance.createVersionEvent.pipe(
|
||||||
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
|
// close model (should be displaying loading/waiting indicator) when version creation failed/succeeded
|
||||||
|
tap(() => activeModal.close()),
|
||||||
// show success/failure notification
|
// show success/failure notification
|
||||||
tap((res: RemoteData<Version>) => { this.itemVersionShared.notifyCreateNewVersion(res); }),
|
tap((res: RemoteData<Version>) => { this.itemVersionShared.notifyCreateNewVersion(res); }),
|
||||||
// get workspace item
|
// get workspace item
|
||||||
|
25
src/app/shared/interfaces/modal-before-dismiss.interface.ts
Normal file
25
src/app/shared/interfaces/modal-before-dismiss.interface.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { NgbModalConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a component implementing this interface is used to create a modal (i.e. it is passed to {@link NgbModal#open}),
|
||||||
|
* and that modal is dismissed, then {@link #beforeDismiss} will be called.
|
||||||
|
*
|
||||||
|
* This behavior is implemented for the whole app, by setting a default value for {@link NgbModalConfig#beforeDismiss}
|
||||||
|
* in {@link AppComponent}.
|
||||||
|
*
|
||||||
|
* Docs: https://ng-bootstrap.github.io/#/components/modal/api
|
||||||
|
*/
|
||||||
|
export interface ModalBeforeDismiss {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback right before the modal will be dismissed.
|
||||||
|
* If this function returns:
|
||||||
|
* - false
|
||||||
|
* - a promise resolved with false
|
||||||
|
* - a promise that is rejected
|
||||||
|
* then the modal won't be dismissed.
|
||||||
|
* Docs: https://ng-bootstrap.github.io/#/components/modal/api#NgbModalOptions
|
||||||
|
*/
|
||||||
|
beforeDismiss(): boolean | Promise<boolean>;
|
||||||
|
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
<div>
|
<div *ngIf="!(this.submitted$ | async); else waiting">
|
||||||
<div class="modal-header">{{'item.version.create.modal.header' | translate}}
|
<div class="modal-header">{{'item.version.create.modal.header' | translate}}
|
||||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
@@ -34,3 +34,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ng-template #waiting>
|
||||||
|
<div class="modal-header">{{'item.version.create.modal.submitted.header' | translate}}</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>{{'item.version.create.modal.submitted.text' | translate}}</p>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<ds-loading [showMessage]="false"></ds-loading>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
import { Component, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { ModalBeforeDismiss } from '../../../interfaces/modal-before-dismiss.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-versions-summary-modal',
|
selector: 'ds-item-versions-summary-modal',
|
||||||
templateUrl: './item-versions-summary-modal.component.html',
|
templateUrl: './item-versions-summary-modal.component.html',
|
||||||
styleUrls: ['./item-versions-summary-modal.component.scss']
|
styleUrls: ['./item-versions-summary-modal.component.scss']
|
||||||
})
|
})
|
||||||
export class ItemVersionsSummaryModalComponent {
|
export class ItemVersionsSummaryModalComponent implements OnInit, ModalBeforeDismiss {
|
||||||
|
|
||||||
versionNumber: number;
|
versionNumber: number;
|
||||||
newVersionSummary: string;
|
newVersionSummary: string;
|
||||||
firstVersion = true;
|
firstVersion = true;
|
||||||
|
submitted$: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
@Output() createVersionEvent: EventEmitter<string> = new EventEmitter<string>();
|
@Output() createVersionEvent: EventEmitter<string> = new EventEmitter<string>();
|
||||||
|
|
||||||
@@ -19,13 +22,24 @@ export class ItemVersionsSummaryModalComponent {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.submitted$ = new BehaviorSubject<boolean>(false);
|
||||||
|
}
|
||||||
|
|
||||||
onModalClose() {
|
onModalClose() {
|
||||||
this.activeModal.dismiss();
|
this.activeModal.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeDismiss(): boolean | Promise<boolean> {
|
||||||
|
// prevent the modal from being dismissed after version creation is initiated
|
||||||
|
return !this.submitted$.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
onModalSubmit() {
|
onModalSubmit() {
|
||||||
this.createVersionEvent.emit(this.newVersionSummary);
|
this.createVersionEvent.emit(this.newVersionSummary);
|
||||||
this.activeModal.close();
|
this.submitted$.next(true);
|
||||||
|
// NOTE: the caller of this modal is responsible for closing it,
|
||||||
|
// e.g. after the version creation POST request completed.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -340,6 +340,9 @@ export class ItemVersionsComponent implements OnInit {
|
|||||||
version.item.pipe(getFirstSucceededRemoteDataPayload())
|
version.item.pipe(getFirstSucceededRemoteDataPayload())
|
||||||
])),
|
])),
|
||||||
mergeMap(([summary, item]: [string, Item]) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
mergeMap(([summary, item]: [string, Item]) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
// close model (should be displaying loading/waiting indicator) when version creation failed/succeeded
|
||||||
|
tap(() => activeModal.close()),
|
||||||
// show success/failure notification
|
// show success/failure notification
|
||||||
tap((newVersionRD: RemoteData<Version>) => {
|
tap((newVersionRD: RemoteData<Version>) => {
|
||||||
this.itemVersionShared.notifyCreateNewVersion(newVersionRD);
|
this.itemVersionShared.notifyCreateNewVersion(newVersionRD);
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
<button *ngIf="shouldShowButton$ | async"
|
||||||
|
class="export-button btn btn-dark btn-sm"
|
||||||
|
[ngbTooltip]="tooltipMsg | translate"
|
||||||
|
(click)="export()"
|
||||||
|
[title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
|
||||||
|
<i class="fas fa-file-export fa-fw"></i>
|
||||||
|
</button>
|
@@ -0,0 +1,4 @@
|
|||||||
|
.export-button {
|
||||||
|
background: var(--ds-admin-sidebar-bg);
|
||||||
|
border-color: var(--ds-admin-sidebar-bg);
|
||||||
|
}
|
@@ -0,0 +1,182 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { SearchExportCsvComponent } from './search-export-csv.component';
|
||||||
|
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
|
||||||
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||||
|
import { Script } from '../../../process-page/scripts/script.model';
|
||||||
|
import { Process } from '../../../process-page/processes/process.model';
|
||||||
|
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
|
||||||
|
import { SearchFilter } from '../models/search-filter.model';
|
||||||
|
import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
|
||||||
|
|
||||||
|
describe('SearchExportCsvComponent', () => {
|
||||||
|
let component: SearchExportCsvComponent;
|
||||||
|
let fixture: ComponentFixture<SearchExportCsvComponent>;
|
||||||
|
|
||||||
|
let scriptDataService: ScriptDataService;
|
||||||
|
let authorizationDataService: AuthorizationDataService;
|
||||||
|
let notificationsService;
|
||||||
|
let router;
|
||||||
|
|
||||||
|
const script = Object.assign(new Script(), {id: 'metadata-export-search', name: 'metadata-export-search'});
|
||||||
|
const process = Object.assign(new Process(), {processId: 5, scriptName: 'metadata-export-search'});
|
||||||
|
|
||||||
|
const searchConfig = new PaginatedSearchOptions({
|
||||||
|
configuration: 'test-configuration',
|
||||||
|
scope: 'test-scope',
|
||||||
|
query: 'test-query',
|
||||||
|
filters: [
|
||||||
|
new SearchFilter('f.filter1', ['filter1value1,equals', 'filter1value2,equals']),
|
||||||
|
new SearchFilter('f.filter2', ['filter2value1,contains']),
|
||||||
|
new SearchFilter('f.filter3', ['[2000 TO 2001]'], 'equals')
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
function initBeforeEachAsync() {
|
||||||
|
scriptDataService = jasmine.createSpyObj('scriptDataService', {
|
||||||
|
findById: createSuccessfulRemoteDataObject$(script),
|
||||||
|
invoke: createSuccessfulRemoteDataObject$(process)
|
||||||
|
});
|
||||||
|
authorizationDataService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationsService = new NotificationsServiceStub();
|
||||||
|
|
||||||
|
router = jasmine.createSpyObj('authorizationService', ['navigateByUrl']);
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchExportCsvComponent],
|
||||||
|
imports: [TranslateModule.forRoot(), NgbModule],
|
||||||
|
providers: [
|
||||||
|
{provide: ScriptDataService, useValue: scriptDataService},
|
||||||
|
{provide: AuthorizationDataService, useValue: authorizationDataService},
|
||||||
|
{provide: NotificationsService, useValue: notificationsService},
|
||||||
|
{provide: Router, useValue: router},
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initBeforeEach() {
|
||||||
|
fixture = TestBed.createComponent(SearchExportCsvComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.searchConfig = searchConfig;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('init', () => {
|
||||||
|
describe('comp', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should init the comp', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the user is an admin and the metadata-export-search script is present ', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should add the button', () => {
|
||||||
|
const debugElement = fixture.debugElement.query(By.css('button.export-button'));
|
||||||
|
expect(debugElement).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the user is not an admin', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
(authorizationDataService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should not add the button', () => {
|
||||||
|
const debugElement = fixture.debugElement.query(By.css('button.export-button'));
|
||||||
|
expect(debugElement).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the metadata-export-search script is not present', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
(scriptDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Not found', 404));
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should should not add the button', () => {
|
||||||
|
const debugElement = fixture.debugElement.query(By.css('button.export-button'));
|
||||||
|
expect(debugElement).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('export', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should call the invoke script method with the correct parameters', () => {
|
||||||
|
component.export();
|
||||||
|
expect(scriptDataService.invoke).toHaveBeenCalledWith('metadata-export-search',
|
||||||
|
[
|
||||||
|
{name: '-q', value: searchConfig.query},
|
||||||
|
{name: '-s', value: searchConfig.scope},
|
||||||
|
{name: '-c', value: searchConfig.configuration},
|
||||||
|
{name: '-f', value: 'filter1,equals=filter1value1'},
|
||||||
|
{name: '-f', value: 'filter1,equals=filter1value2'},
|
||||||
|
{name: '-f', value: 'filter2,contains=filter2value1'},
|
||||||
|
{name: '-f', value: 'filter3,equals=[2000 TO 2001]'},
|
||||||
|
], []);
|
||||||
|
|
||||||
|
component.searchConfig = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.export();
|
||||||
|
expect(scriptDataService.invoke).toHaveBeenCalledWith('metadata-export-search', [], []);
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should show a success message when the script was invoked successfully and redirect to the corresponding process page', () => {
|
||||||
|
component.export();
|
||||||
|
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
expect(router.navigateByUrl).toHaveBeenCalledWith(getProcessDetailRoute(process.processId));
|
||||||
|
});
|
||||||
|
it('should show an error message when the script was not invoked successfully and stay on the current page', () => {
|
||||||
|
(scriptDataService.invoke as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Error', 500));
|
||||||
|
|
||||||
|
component.export();
|
||||||
|
|
||||||
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
expect(router.navigateByUrl).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('clicking the button', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
initBeforeEachAsync();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
initBeforeEach();
|
||||||
|
});
|
||||||
|
it('should trigger the export function', () => {
|
||||||
|
spyOn(component, 'export');
|
||||||
|
|
||||||
|
const debugElement = fixture.debugElement.query(By.css('button.export-button'));
|
||||||
|
debugElement.triggerEventHandler('click', null);
|
||||||
|
|
||||||
|
expect(component.export).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,110 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
|
import { ScriptDataService } from '../../../core/data/processes/script-data.service';
|
||||||
|
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { Process } from '../../../process-page/processes/process.model';
|
||||||
|
import { getProcessDetailRoute } from '../../../process-page/process-page-routing.paths';
|
||||||
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-search-export-csv',
|
||||||
|
styleUrls: ['./search-export-csv.component.scss'],
|
||||||
|
templateUrl: './search-export-csv.component.html',
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Display a button to export the current search results as csv
|
||||||
|
*/
|
||||||
|
export class SearchExportCsvComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current configuration of the search
|
||||||
|
*/
|
||||||
|
@Input() searchConfig: PaginatedSearchOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable used to determine whether the button should be shown
|
||||||
|
*/
|
||||||
|
shouldShowButton$: Observable<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message key used for the tooltip of the button
|
||||||
|
*/
|
||||||
|
tooltipMsg = 'metadata-export-search.tooltip';
|
||||||
|
|
||||||
|
constructor(private scriptDataService: ScriptDataService,
|
||||||
|
private authorizationDataService: AuthorizationDataService,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const scriptExists$ = this.scriptDataService.findById('metadata-export-search').pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((rd) => rd.isSuccess && hasValue(rd.payload))
|
||||||
|
);
|
||||||
|
|
||||||
|
const isAuthorized$ = this.authorizationDataService.isAuthorized(FeatureID.AdministratorOf);
|
||||||
|
|
||||||
|
this.shouldShowButton$ = observableCombineLatest([scriptExists$, isAuthorized$]).pipe(
|
||||||
|
map(([scriptExists, isAuthorized]: [boolean, boolean]) => scriptExists && isAuthorized)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the export of the items based on the current search configuration
|
||||||
|
*/
|
||||||
|
export() {
|
||||||
|
const parameters = [];
|
||||||
|
if (hasValue(this.searchConfig)) {
|
||||||
|
if (isNotEmpty(this.searchConfig.query)) {
|
||||||
|
parameters.push({name: '-q', value: this.searchConfig.query});
|
||||||
|
}
|
||||||
|
if (isNotEmpty(this.searchConfig.scope)) {
|
||||||
|
parameters.push({name: '-s', value: this.searchConfig.scope});
|
||||||
|
}
|
||||||
|
if (isNotEmpty(this.searchConfig.configuration)) {
|
||||||
|
parameters.push({name: '-c', value: this.searchConfig.configuration});
|
||||||
|
}
|
||||||
|
if (isNotEmpty(this.searchConfig.filters)) {
|
||||||
|
this.searchConfig.filters.forEach((filter) => {
|
||||||
|
if (hasValue(filter.values)) {
|
||||||
|
filter.values.forEach((value) => {
|
||||||
|
let operator;
|
||||||
|
let filterValue;
|
||||||
|
if (hasValue(filter.operator)) {
|
||||||
|
operator = filter.operator;
|
||||||
|
filterValue = value;
|
||||||
|
} else {
|
||||||
|
operator = value.substring(value.lastIndexOf(',') + 1);
|
||||||
|
filterValue = value.substring(0, value.lastIndexOf(','));
|
||||||
|
}
|
||||||
|
const valueToAdd = `${filter.key.substring(2)},${operator}=${filterValue}`;
|
||||||
|
parameters.push({name: '-f', value: valueToAdd});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scriptDataService.invoke('metadata-export-search', parameters, []).pipe(
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
).subscribe((rd: RemoteData<Process>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
this.notificationsService.success(this.translateService.get('metadata-export-search.submit.success'));
|
||||||
|
this.router.navigateByUrl(getProcessDetailRoute(rd.payload.processId));
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error(this.translateService.get('metadata-export-search.submit.error'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,7 @@
|
|||||||
|
<div class="d-flex justify-content-between">
|
||||||
<h2 *ngIf="!disableHeader">{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h2>
|
<h2 *ngIf="!disableHeader">{{ (configuration ? configuration + '.search.results.head' : 'search.results.head') | translate }}</h2>
|
||||||
|
<ds-search-export-csv [searchConfig]="searchConfig"></ds-search-export-csv>
|
||||||
|
</div>
|
||||||
<div *ngIf="searchResults && searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
|
<div *ngIf="searchResults && searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
|
||||||
<ds-viewable-collection
|
<ds-viewable-collection
|
||||||
[config]="searchConfig.pagination"
|
[config]="searchConfig.pagination"
|
||||||
|
@@ -290,6 +290,7 @@ import { DsoPageOrcidButtonComponent } from './dso-page/dso-page-orcid-button/ds
|
|||||||
import { LogInOrcidComponent } from './log-in/methods/orcid/log-in-orcid.component';
|
import { LogInOrcidComponent } from './log-in/methods/orcid/log-in-orcid.component';
|
||||||
import { BrowserOnlyPipe } from './utils/browser-only.pipe';
|
import { BrowserOnlyPipe } from './utils/browser-only.pipe';
|
||||||
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
|
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
|
||||||
|
import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -401,6 +402,7 @@ const COMPONENTS = [
|
|||||||
|
|
||||||
CollectionSearchResultGridElementComponent,
|
CollectionSearchResultGridElementComponent,
|
||||||
CommunitySearchResultGridElementComponent,
|
CommunitySearchResultGridElementComponent,
|
||||||
|
SearchExportCsvComponent,
|
||||||
PageSizeSelectorComponent,
|
PageSizeSelectorComponent,
|
||||||
ListableObjectComponentLoaderComponent,
|
ListableObjectComponentLoaderComponent,
|
||||||
CollectionListElementComponent,
|
CollectionListElementComponent,
|
||||||
|
@@ -2361,6 +2361,10 @@
|
|||||||
|
|
||||||
"item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version",
|
"item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version",
|
||||||
|
|
||||||
|
"item.version.create.modal.submitted.header": "Creating new version...",
|
||||||
|
|
||||||
|
"item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.",
|
||||||
|
|
||||||
"item.version.create.notification.success" : "New version has been created with version number {{version}}",
|
"item.version.create.notification.success" : "New version has been created with version number {{version}}",
|
||||||
|
|
||||||
"item.version.create.notification.failure" : "New version has not been created",
|
"item.version.create.notification.failure" : "New version has not been created",
|
||||||
@@ -2407,6 +2411,8 @@
|
|||||||
|
|
||||||
"journal.search.results.head": "Journal Search Results",
|
"journal.search.results.head": "Journal Search Results",
|
||||||
|
|
||||||
|
"journal-relationships.search.results.head": "Journal Search Results",
|
||||||
|
|
||||||
"journal.search.title": "Journal Search",
|
"journal.search.title": "Journal Search",
|
||||||
|
|
||||||
|
|
||||||
@@ -2717,6 +2723,11 @@
|
|||||||
"menu.section.workflow": "Administer Workflow",
|
"menu.section.workflow": "Administer Workflow",
|
||||||
|
|
||||||
|
|
||||||
|
"metadata-export-search.tooltip": "Export search results as CSV",
|
||||||
|
"metadata-export-search.submit.success": "The export was started successfully",
|
||||||
|
"metadata-export-search.submit.error": "Starting the export has failed",
|
||||||
|
|
||||||
|
|
||||||
"mydspace.breadcrumbs": "MyDSpace",
|
"mydspace.breadcrumbs": "MyDSpace",
|
||||||
|
|
||||||
"mydspace.description": "",
|
"mydspace.description": "",
|
||||||
@@ -2892,6 +2903,8 @@
|
|||||||
|
|
||||||
"person.search.results.head": "Person Search Results",
|
"person.search.results.head": "Person Search Results",
|
||||||
|
|
||||||
|
"person-relationships.search.results.head": "Person Search Results",
|
||||||
|
|
||||||
"person.search.title": "Person Search",
|
"person.search.title": "Person Search",
|
||||||
|
|
||||||
|
|
||||||
@@ -3069,6 +3082,8 @@
|
|||||||
|
|
||||||
"project.search.results.head": "Project Search Results",
|
"project.search.results.head": "Project Search Results",
|
||||||
|
|
||||||
|
"project-relationships.search.results.head": "Project Search Results",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"publication.listelement.badge": "Publication",
|
"publication.listelement.badge": "Publication",
|
||||||
@@ -3089,6 +3104,8 @@
|
|||||||
|
|
||||||
"publication.search.results.head": "Publication Search Results",
|
"publication.search.results.head": "Publication Search Results",
|
||||||
|
|
||||||
|
"publication-relationships.search.results.head": "Publication Search Results",
|
||||||
|
|
||||||
"publication.search.title": "Publication Search",
|
"publication.search.title": "Publication Search",
|
||||||
|
|
||||||
|
|
||||||
@@ -3565,6 +3582,8 @@
|
|||||||
|
|
||||||
"default.search.results.head": "Search Results",
|
"default.search.results.head": "Search Results",
|
||||||
|
|
||||||
|
"default-relationships.search.results.head": "Search Results",
|
||||||
|
|
||||||
|
|
||||||
"search.sidebar.close": "Back to results",
|
"search.sidebar.close": "Back to results",
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user