#885 add media viewer

This commit is contained in:
Dániel Péter Sipos
2020-10-08 14:15:16 +02:00
parent 5bf6d0f287
commit a157552a13
23 changed files with 511 additions and 13 deletions

View File

@@ -104,6 +104,7 @@
"ng2-file-upload": "1.4.0",
"ng2-nouislider": "^1.8.2",
"ngx-bootstrap": "^5.3.2",
"ngx-gallery": "^5.10.0",
"ngx-infinite-scroll": "6.0.1",
"ngx-moment": "^3.4.0",
"ngx-pagination": "3.0.3",

View File

@@ -31,6 +31,11 @@ import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/
import { StatisticsModule } from '../statistics/statistics.module';
import { AbstractIncrementalListComponent } from './simple/abstract-incremental-list/abstract-incremental-list.component';
import { MediaViewerComponent } from './media-viewer/media-viewer.component';
import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
import { NgxGalleryModule } from 'ngx-gallery';
@NgModule({
imports: [
CommonModule,
@@ -38,7 +43,8 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental-
ItemPageRoutingModule,
EditItemPageModule,
SearchPageModule,
StatisticsModule.forRoot()
StatisticsModule.forRoot(),
NgxGalleryModule,
],
declarations: [
ItemPageComponent,
@@ -62,6 +68,9 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental-
UploadBitstreamComponent,
TabbedRelatedEntitiesSearchComponent,
AbstractIncrementalListComponent,
MediaViewerComponent,
MediaViewerVideoComponent,
MediaViewerImageComponent,
],
exports: [
ItemComponent,
@@ -72,12 +81,8 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental-
RelatedItemsComponent,
MetadataRepresentationListComponent,
ItemPageTitleFieldComponent,
TabbedRelatedEntitiesSearchComponent
TabbedRelatedEntitiesSearchComponent,
],
entryComponents: [
PublicationComponent
]
entryComponents: [PublicationComponent],
})
export class ItemPageModule {
}
export class ItemPageModule {}

View File

@@ -0,0 +1 @@
<ngx-gallery [options]="galleryOptions" [images]="galleryImages"></ngx-gallery>

View File

@@ -0,0 +1,41 @@
import { DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MediaViewerImageComponent } from './media-viewer-image.component';
describe('MediaViewerImageComponent', () => {
let component: MediaViewerImageComponent;
let fixture: ComponentFixture<MediaViewerImageComponent>;
let debugElement: DebugElement;
let htmlElement: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MediaViewerImageComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MediaViewerImageComponent);
component = fixture.componentInstance;
component.galleryOptions = [
{
image: true,
imageSize: 'contain',
thumbnails: false,
imageArrows: false,
width: '340px',
height: '279px',
},
];
debugElement = fixture.debugElement.query(By.css('ngx-gallery'));
htmlElement = debugElement.nativeElement;
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe;
});

View File

@@ -0,0 +1,43 @@
import { Component, Input, OnInit } from '@angular/core';
import { NgxGalleryOptions, NgxGalleryImage } from 'ngx-gallery';
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
@Component({
selector: 'ds-media-viewer-image',
templateUrl: './media-viewer-image.component.html',
styleUrls: ['./media-viewer-image.component.scss'],
})
export class MediaViewerImageComponent implements OnInit {
@Input() images: MediaViewerItem[];
constructor() {}
galleryOptions: NgxGalleryOptions[];
galleryImages: NgxGalleryImage[];
ngOnInit(): void {
this.galleryImages = new Array<NgxGalleryImage>();
this.galleryOptions = [
{
image: true,
imageSize: 'contain',
thumbnails: false,
imageArrows: false,
width: '340px',
height: '279px',
},
];
for (const image of this.images) {
this.galleryImages = [
...this.galleryImages,
{
small: image.thumbnail
? image.thumbnail
: './assets/images/replacements_image.svg',
medium: image.bitstream._links.content.href,
big: image.bitstream._links.content.href,
},
];
}
}
}

View File

@@ -0,0 +1,48 @@
<ng-container>
<video
#media
[src]="medias[currentIndex].bitstream._links.content.href"
id="singleVideo"
[poster]="
medias[currentIndex].thumbnail ||
this.replacements[this.medias[this.currentIndex].format]
"
preload="auto"
controls
></video>
<div class="buttons" *ngIf="medias?.length > 1">
<button
class="btn btn-primary"
[disabled]="currentIndex === 0"
(click)="prevMedia()"
>
{{ "media-viewer.previus" | translate }}
</button>
<button
class="btn btn-primary"
[disabled]="currentIndex === medias.length - 1"
(click)="nextMedia()"
>
{{ "media-viewer.next" | translate }}
</button>
<div ngbDropdown class="d-inline-block">
<button
class="btn btn-outline-primary"
id="dropdownBasic1"
ngbDropdownToggle
>
{{ "media-viewer.playlist" | translate }}
</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button
ngbDropdownItem
*ngFor="let item of medias; let i = index"
(click)="selectedMedia(i)"
>
{{ item.bitstream.name }}
</button>
</div>
</div>
</div>
</ng-container>

View File

@@ -0,0 +1,4 @@
video {
width: 340px;
height: 279px;
}

View File

@@ -0,0 +1,46 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
import { FileSizePipe } from '../../../shared/utils/file-size-pipe';
import { VarDirective } from '../../../shared/utils/var.directive';
import { MetadataFieldWrapperComponent } from '../../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
import { MediaViewerVideoComponent } from './media-viewer-video.component';
describe('MediaViewerVideoComponent', () => {
let component: MediaViewerVideoComponent;
let fixture: ComponentFixture<MediaViewerVideoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock,
},
}),
BrowserAnimationsModule,
],
declarations: [
MediaViewerVideoComponent,
VarDirective,
FileSizePipe,
MetadataFieldWrapperComponent,
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MediaViewerVideoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,38 @@
import { Component, Input, OnInit } from '@angular/core';
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
@Component({
selector: 'ds-media-viewer-video',
templateUrl: './media-viewer-video.component.html',
styleUrls: ['./media-viewer-video.component.scss'],
})
export class MediaViewerVideoComponent implements OnInit {
@Input() medias: MediaViewerItem[];
isCollapsed: boolean;
currentIndex = 0;
replacements = {
video: './assets/images/replacement_video.svg',
audio: './assets/images/replacement_audio.svg',
document: './assets/images/replacement_document.svg',
};
replacementThumbnail: string;
constructor() {}
ngOnInit() {
this.isCollapsed = false;
}
selectedMedia(index: number) {
console.log(index);
this.currentIndex = index;
}
nextMedia() {
this.currentIndex++;
}
prevMedia() {
this.currentIndex--;
}
}

View File

@@ -0,0 +1,19 @@
<ng-container *ngVar="mediaList$ | async as mediaList">
<ds-loading
*ngIf="isLoading"
message="{{ 'loading.default' | translate }}"
[showMessage]="false"
></ds-loading>
<div class="media-viewer" *ngIf="mediaList.length > 0">
<ng-container
*ngIf="
mediaList[0]?.format === 'video' || mediaList[0]?.format === 'audio'
"
>
<ds-media-viewer-video [medias]="mediaList"></ds-media-viewer-video>
</ng-container>
<ng-container *ngIf="mediaList[0]?.format === 'image'">
<ds-media-viewer-image [images]="mediaList"></ds-media-viewer-image>
</ng-container>
</div>
</ng-container>

View File

@@ -0,0 +1,106 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Bitstream } from '../../core/shared/bitstream.model';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { createPaginatedList } from '../../shared/testing/utils.test';
import { of as observableOf } from 'rxjs';
import { By } from '@angular/platform-browser';
import { MediaViewerComponent } from './media-viewer.component';
import { MockBitstreamFormat1 } from '../../shared/mocks/item.mock';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
import { VarDirective } from '../../shared/utils/var.directive';
import { MetadataFieldWrapperComponent } from '../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
import { FileSizePipe } from '../../shared/utils/file-size-pipe';
describe('MediaViewerComponent', () => {
let comp: MediaViewerComponent;
let fixture: ComponentFixture<MediaViewerComponent>;
const bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(
createPaginatedList([])
),
});
const mockBitstream: Bitstream = Object.assign(new Bitstream(), {
sizeBytes: 10201,
content:
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content',
format: observableOf(MockBitstreamFormat1),
bundleName: 'ORIGINAL',
_links: {
self: {
href:
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713',
},
content: {
href:
'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content',
},
},
id: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
uuid: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
type: 'bitstream',
metadata: {
'dc.title': [
{
language: null,
value: 'test_word.docx',
},
],
},
});
const mockMediaViewerItem: MediaViewerItem = Object.assign(
new MediaViewerItem(),
{ bitstream: mockBitstream, format: 'image', thumbnail: null }
);
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock,
},
}),
BrowserAnimationsModule,
],
declarations: [
MediaViewerComponent,
VarDirective,
FileSizePipe,
MetadataFieldWrapperComponent,
],
providers: [
{ provide: BitstreamDataService, useValue: bitstreamDataService },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MediaViewerComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
describe('when the bitstreams are loading', () => {
beforeEach(() => {
comp.mediaList$.next([mockMediaViewerItem]);
comp.isLoading = true;
fixture.detectChanges();
});
it('should display a loading component', () => {
const loading = fixture.debugElement.query(By.css('ds-loading'));
expect(loading.nativeElement).toBeDefined();
});
});
});

View File

@@ -0,0 +1,76 @@
import { Component, Input, OnInit } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter, takeWhile } from 'rxjs/operators';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { BitstreamFormatDataService } from '../../core/data/bitstream-format-data.service';
import { PaginatedList } from '../../core/data/paginated-list';
import { RemoteData } from '../../core/data/remote-data';
import { Bitstream } from '../../core/shared/bitstream.model';
import { Item } from '../../core/shared/item.model';
import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
import { hasNoValue, hasValue } from '../../shared/empty.util';
@Component({
selector: 'ds-media-viewer',
templateUrl: './media-viewer.component.html',
styleUrls: ['./media-viewer.component.scss'],
})
export class MediaViewerComponent implements OnInit {
@Input() item: Item;
mediaList$: BehaviorSubject<MediaViewerItem[]>;
isLoading: boolean;
constructor(
protected bitstreamDataService: BitstreamDataService,
protected bitstreamFormatDataService: BitstreamFormatDataService
) {}
ngOnInit(): void {
this.mediaList$ = new BehaviorSubject([]);
this.isLoading = true;
this.loadRemoteData('ORIGINAL').subscribe((bitstreamsRD) => {
console.log(bitstreamsRD);
this.loadRemoteData('THUMBNAIL').subscribe((thumbnailsRD) => {
for (let index = 0; index < bitstreamsRD.payload.page.length; index++) {
this.bitstreamFormatDataService
.findByBitstream(bitstreamsRD.payload.page[index])
.pipe(getFirstSucceededRemoteDataPayload())
.subscribe((format) => {
const current = this.mediaList$.getValue();
const mediaItem = new MediaViewerItem();
mediaItem.bitstream = bitstreamsRD.payload.page[index];
mediaItem.format = format.mimetype.split('/')[0];
mediaItem.thumbnail =
thumbnailsRD.payload && thumbnailsRD.payload.page[index]
? thumbnailsRD.payload.page[index]._links.content.href
: null;
this.mediaList$.next([...current, mediaItem]);
});
}
this.isLoading = false;
});
});
}
loadRemoteData(
bundleName: string
): Observable<RemoteData<PaginatedList<Bitstream>>> {
return this.bitstreamDataService
.findAllByItemAndBundleName(this.item, bundleName)
.pipe(
filter((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) =>
hasValue(bitstreamsRD)
),
takeWhile(
(bitstreamsRD_1: RemoteData<PaginatedList<Bitstream>>) =>
hasNoValue(bitstreamsRD_1.payload) &&
hasNoValue(bitstreamsRD_1.error),
true
)
);
}
}

View File

@@ -3,9 +3,10 @@
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<!-- <ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
</ds-metadata-field-wrapper> -->
<ds-media-viewer [item]="object"></ds-media-viewer>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
<ds-item-page-author-field [item]="object"></ds-item-page-author-field>

View File

@@ -0,0 +1,21 @@
import { Bitstream } from './bitstream.model';
/**
* Model representing a media viewer item
*/
export class MediaViewerItem {
/**
* Incoming Bitsream
*/
bitstream: Bitstream;
/**
* Incoming Bitsream format type
*/
format: string;
/**
* Incoming Bitsream thumbnail
*/
thumbnail: string;
}

View File

@@ -2449,6 +2449,13 @@
"publication.search.results.head": "Publication Search Results",
"publication.search.title": "DSpace Angular :: Publication Search",
"media-viewer.next": "Next",
"media-viewer.previus": "Previus",
"media-viewer.playlist": "Playlist",
"register-email.title": "New user registration",

View File

@@ -0,0 +1,9 @@
<svg width="240" height="180" viewBox="0 0 240 180" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="240" height="180" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.00416667 0.00555556)"/>
</pattern>
<image id="image0" width="240" height="180" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAYAAACqnKHoAAAHbUlEQVR4nO3dD0/TWhzG8Sp3YFgMZLKgy4R44/t/RcSJLMjCrowgRFjQm6dxxr/sd2rX9lm/n8TkJuoFKl9Oe85p+2h2cfElA2DpMf9sgC8CBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwY+4d/PMh8Ps8+ffqU//eTJ0+yTqfDcTFAwMhml5fZ6enpDweiu72d9Xq97OnTpxygBiPglru5ufklXrm+ucl/bXY62V6/n+3u7LT9UDUS18At99+HDw8egLv5PA/8+Pg4jx3NQsAt9/n+PnQANBq/PT7OJpNJdh/8O1g9AkYSjdij0ejbhBfqRcAtV2S2WafVb0ajfPIL9SLgltNMc1G6Nv7dBBiq82h2cfGF4+1N16SXl5ffrk273W62vb0d/pqurq7yEO8/fy50HDRDvb+/n21sbKz/wW4YAjana1HNEP8cn9Zxh8NhUlQ6JZ6en+enyKm0+ePw4ICIK0bAxv4U78KzXi8fGVMVDVkj8WAwaPW/SdW4Bja1LF6ZzWaFvjiF+Pr166y/t5f09363owurRcCGIvFK0WvahX6/n/376lV+ehyliD8s2RyC8hCwmWi8ZVlc26bsiT6bTFgnrggBG6k63gVNTL0cDpP2Q4/HY3ZsVYCATdQV7/c0QRWNWBNg0+m0qk+ttQjYQBPiXUiJWNsuuQFitQi44ZoU74Iijl4T63oYq0PADdbEeBcGL16EZqf1NbBnenUIuKGqilfrtkUC08SWIo7QphCsBgE3UJUj7/y7G/ZTZ401Akc2e2hCi1F4NQi4Yeo6bdYN+0dHR8nrt9rssRm4JfGy4K4wPIyAG6Tua1593CKPztEzs5bRD4h5gZsk8DAealcyfZPq9jzFmPoNq79T94SVPv7JyUl2eHgY3kKpZaXIzQ/aYlnk5gr8GQGXSNd5k7OzRs4ap1iMxLqhIXp7oEbhZTcy6AcbAZeLU+iSaLRdh3gX9HVoO2SURuGNxw9/O9199/B4lIOAS6Jb99Yl3gVdt6bMHkc2d7Azq1wEXJJ1naBJWcONBHxNwKUi4JKsa8Apa7gKeNlp9M31dUmfGTICRkTKGu6ymWtdZnCbYXkIGEulrOFGnoZ5e3vLQS8JASMkeu0aWTsu8tRL/B4BI+Q2uPwTWTee391x0EtCwAiJrt+mPFAef4+AAWMEDBgjYMAYAQPGCBgwRsAIid4bzM0K1SJghGwFA45sk+xsbnLQS0LACOkG13cj68WRZ2ghhoCxlOLtBKOLnEJvbW1x0EtCwFhqZ3c3fJCWjcAafXmLf3kIuCTREcqNgou+C0nPvFr2VJLotTRiCLgkKS/BdhJ5ZOyCAl4mei2NGAIuSeRpFG4UW8o7gSMBc7NDuQi4JDqF3n/+fG0i1tcxHA7Df16P3Vl2+qzT8XU9U6kLz4UukUYrjVr5tWCBx8bowedNeLKl4tWD3VMmmyIPv4u+khRxBFwyjcS9Xq/Q/1Tf4HW/TnQRb8pIqdE38pSNoscFf8YpdIMomnzkq+k0vEi8WXD0TVlLRhwBN0xdEWv016tUUuM9D7wTKUtcS0YcATdQlRHrY70cDvNfqRsstGnjfDpd+udS1pKRhoAbqqqI9bKxIpNLmqQ7ff8+9GdT1pKRhoAbrO5r4oco3siNC/oaGH1Xh4AbrokR6zWikU0b8pzXia4UARtoUsSKN/qupGe9HjuvVoyATTQh4pR4NXG1t7e38s+p7QjYSF0Ra8LqZDxOelfwsMCsNtIRsJnUiP/2taeaqDp+9y58zZt9ve5lz3M1CNhQNGL9/t/sftImjTejUfi1KtnX/eBsmawOAZuKRLxbcPeTTpWPjo5CmzR++Hg7O9lgMFiDo+vj0ezi4kvbD4IzjY7j8fiX7YxFYlK40+DWyJ/lP1AODrjurRgBrwFNMl19/PjttZ3dbjdp+UbXt5phLnoXlH5YaEcX8VaP2wnXgMIputtJI7hmmIvitLleBNxyeohAUQqXbZL1IuCWK7LMpE0aWudlqah+BIwk2h6pHVZc7zYDAbfc42CIeqJGv99nb3PDEHDLaUR9aJdVvqe53+dat6FYRkK+/js5O/thGYkR1wMBA8bYSgkYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBowRMGCMgAFjBAwYI2DAGAEDxggYMEbAgDECBlxlWfY/kg/To/rpY0MAAAAASUVORK5CYII="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,9 @@
<svg width="240" height="180" viewBox="0 0 240 180" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="240" height="180" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.00416667 0.00555556)"/>
</pattern>
<image id="image0" width="240" height="180" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAYAAACqnKHoAAAESElEQVR4nO3dX2saWQDG4dNYFzFIQ7YhVLoV8v0/z/bamxBp1yiiSEPTclyE0otl0ZiZ13ke8H5mnJ9z5s8Z3yweH38UINKFrw1yCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCCRiCvfXltctiuSyb9bo8PT2d3boNh8MyGo3KYDBowdKchzeLx8cfXd8IbbBarcpsNivfzjDc310Oh2U8Hpd+v9+uBQsk4Bao4f4zn3dqnXsXF2UymTgaH8k5cMPqkLlr8Vbfn5/LdDo9y1OF1yTgBtWdd/bw0Nn1rxHf39+3YElyCbhB8/l8txN32XqzKdvtttPb4BgCblC9cIXtcAwBN6gLV5z/j81m0/6FbCkBQzABQzABQzABQzABQzCTGYLcvH9fbm5uWr/Af3/+3IKl6AZHYAgmYAgmYAjmHPhAdSbN+j+eIKpzXut0OTglR2AIJmAIJmAI5hw4yJevX3cf2HMEhmAChmAChmAChmAChmAChmAChmDuAwepz1fXPwhrO/eqX4+Ag9R4Eyb0C/j1GEJDMAFDMAFDMAFDMAFDMAFDMLeRgtQ/BPdPfvxKwEEWy+XuA3uG0BBMwBBMwBBMwBBMwBBMwBBMwBDMfeAgV+/elaurq5Mu8MNsVrbb7ZltufMl4CD9fv/kb+ToXRiUJfFtQTABQzABQzABQzABQzABQzC3kYIsl8uTT+h3DziLgIN8e3rafWDPEBqCCRiCCRiCCRiCCbhBJg78q07S4DD2oAYNLy87u+6/sh0OJ+AG/Xl93dl13/uj39/Nc+YwAm5Qnds7Go06u/7V7e1tC5Yil4AbNv7woQwGg06uex2BdP0H7FgCbliv1yuTT586tSPXi3fj8djR9wV4lLIFasR/ffy4e855sViU1WpVvj8/n9161vPd+kN1fX3tyvMLEXCL1HPiU7/zivNiCA3BBAzBBAzBBAzBBAzBBAzBBAzB3Ac+kfpyuOl0epbr9rvJZNKuBeoQAZ9IfZJqfeI3SIIh9IE8MUUbCPhAZtHQBgI+UJ0CeOkoTMMEfIQ6Jc57rWiSve8IdUpcvQIrYppizztSHUrf3d0ZTtMIt5FewP5IXO/91sn4p/4DMtgT8AuqR+Ouvt+KZhhCQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQ6pSyk/71rEEHrIkFQAAAABJRU5ErkJggg=="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,9 @@
<svg width="240" height="180" viewBox="0 0 240 180" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="240" height="180" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.00416667 0.00555556)"/>
</pattern>
<image id="image0" width="240" height="180" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAYAAACqnKHoAAAE5UlEQVR4nO3d70pbSRzH4eNaxKB06RLJ4iIVBO//hrwAqWysWOIbcZlAwLX+SeIkZ745z/OmFmoxYz6e8xsnunc7nT51QKQ/fNogl4AhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAhmIAh2BefvM15fHzs7n/96h5ms/nbs9lsVx/q/4xGo25/f787HI2646Oj+dtshoA35Pr6uvtxczMPd2jKF62FEu/JeNxNJpPBrcM27N1Op0+7/zC3pwR7dXXVzR4ehvKQlzI6POwuLi5cjSszA1cm3teVNSlrQ10CrqjcNov3bWVtyhpRj4ArKbfOZeblfUPdF9gUAVdSNm48MT+22JmnDgFX8jCQbxHVYK3qEXAlrr70QcCVDOWQBm0RMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMAQTMHN/fftmIQJ9GfoC0HV/TybdZDKZ/4rUn3d3ViSIK/DA/fn16zze4uzsrDs4OBj6kkQR8ICVWEu0C/v7+9359+9DX5YoAh6oRazlz+dGo1H3z+np0JcnhoAH6vT0dB7ra8bj8fzWmvYJOMDo8LDqB1l2nD/adTYPZxBw48qV8PLyslrE5f95Pve+xTycQcANex7bxcXFp6+I8yjPz5f+9+bh9gm4USW2Eu9ik+mtTadVlPdf9YuAebhtAm7Ua5tM5e/L3P6+phzWOD4+Xut9zcPtEnCDTsbjNzeZytVw1dva54c11mEebpeAG1Pm3tMPAh2/E/hLLw9rrMs83CYBN6Rc6cpm1TJKlB/tTNeYm58zD7dHwA1ZNbaPdqbfO6yxLvNwWwTciHJ7uuom03tX2GUOa6zDPNwWATeg3JaW29N1vLYzvexhjXWZh9sh4J7ViO35zvSqhzXWZR5ug4B79PKwxmcsdqa3OaOah/sn4B7V3mQqQW3zqmge7p+Ae/LeYY0k5uF+CbgHyxzWSGIe7o+At2yVwxpJzMP9EPCW1TwZ1RLzcD/8WNktWsy89/f3O/0Y/51OG/hIhkHAW1Se2J7c1OQWGoIJGIIJGIIJuJLaL9uDZQi4kl381hDtE3Alh67AS7NW9Qi4kuOjI1fhJZQ1cuyyHgFXUp6YJ2u+KH9IrFFdAq6o/OjW2r/HaJeUtfnMj7fldwKurLxQQcS/K2uyiy/i6Nve7XT6NOwl2Izr6+vux81N9/j4uIsPb2mL0cKVdzMEvGE/7+66h9lspx/jW8pusw2rzRIwBDMDQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQzABQ6qu6/4DYMqyrZ0wRgUAAAAASUVORK5CYII="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,9 @@
<svg width="240" height="180" viewBox="0 0 240 180" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="240" height="180" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.00416667 0.00555556)"/>
</pattern>
<image id="image0" width="240" height="180" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAC0CAYAAACqnKHoAAAFZ0lEQVR4nO3dbU/UWByH4QqLMRCQQBACMfL9v5JZYiRr0AkPgQwxEHTzn41GdxfsYGeYX3tdiS98Y9oDt+2ZOT19dnF+/rUBIi35sUEuAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUMwAUOwP/zwHu/29ra5vLpqvtzdNZ8/f27u7u5ST2WulpeXmxcvXjRLy8vNxvp6s7KyMqCz79azi/Pzr306oXmocD+NRs3FxUX/T3YONjc3m1c7O0J+BAFPqa607969a+6+fIk67kW3vLTUHB4eTq7MtGcOPAXxzk6NaY1tjTHtCXgKf334IN4ZqrGtMaY9Abd0dXXl6jAHNcY11rQj4JYu/VLNjQ8H2xNwS9fjccRx9oE7nfYE3NLN7W3EcfaBsW7PQo6O1cKEnZ0dX4f8S11VR6ORqUjHXIE7VAsSXr9+Ld7/UWNSY1NjRHcE3JFaiLC3u9uLc5mlGqMaK7phJDtSV5ha48vDvq2DphsC7sja2lovzoMsAoZgAoZgAoZgAoZgAoZgAoZgllIGqqd1Ts/OJssTa1HE+saGLWkGSsBh6oH3Hx+3q4fg6+9Xl5fN9vZ2s7W1ZUHJgLiFDjIej+99VrZCro32jo6OPE87IAIOcn19/cuDrUfx6iptf6lhEHCQaYIcX183fx4d/bOPl/2qe8scOMhjQvxxflzPKdMvrsAD8G1+/PbtWxvG9YyAB6Tmx++Pj82Pe0TAA2R+3B/mwANmfpzPFXjgfpwfu63OI2Aman58fHzsljqMgPmuIvYpdRYB85Nbm6pHETAEEzA/WV1dNSBBBMx39dYE2+Nm8T0wE9tbW83e3p7BCCPggVtbXW0ODg7s5hFKwAP1fGWl2d/fd8scTsADU3to7bx6NbllJp+AB6SirTXP9szqDwEPgHlufwk4yLRXTvPc/hNwkHqv7mWLtcrmucMh4CC10OL09HTyCOB9zHOHRcBBag5biy1OTk7+E7F57jAJOMy35Y51K10P4Nc8t9Yvm+cOk4AD1VXW/JbGwwyQTcAQTMAQTMAQTMAQTMAdubEZHE9AwB25vbnpxXmQRcAdqfcN2ZL112qMxi1eVE47Au7Qe282eFCNTY0R3bESq0O1tLHeMVTLHdfX13tzXl2oNz7Uy9QeehCD6Qm4Y/ULenp2NvnD49TjkLRjpFqqZ3Ex1otGwC2teWPB3Ai4PQG3VC/BxlgvGgG35BG++agxtilBewKeQm1V4/Zudmpsa4xpT8BTqH2mDt+8aTZ8RdS5GtMaW3t5TefZxfn516QDXhT1vean0Wjy3S+PVx8O1pzX9+aPI+DfVKuLZhXxycePT/4fRN3W7u3uzuzfdsX9PRZy/Kb6BZzVhnKLsKChjsGGeYvLHBiCCRiCCRiCCXiBrTx//uQHtwjHwP0EvMA2X7588oNbhGPgfgJeYPXp78H+/pMd4IFXky483wMHqG1o6mH4eapNCaxJXnwChmBuoSGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCGYgCFV0zR/A9c/gdi8GSXTAAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -17,9 +17,9 @@ export const environment: GlobalConfig = {
// The REST API server settings.
// NOTE: these must be "synced" with the 'dspace.server.url' setting in your backend's local.cfg.
rest: {
ssl: true,
host: 'dspace7.4science.cloud',
port: 443,
ssl: false,
host: 'localhost',
port: 8080,
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: '/server',
},

View File

@@ -6928,6 +6928,11 @@ ngx-bootstrap@^5.3.2:
resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-5.3.2.tgz#0668b01202610657e998b3ca87669645e0b31dc9"
integrity sha512-gSMf8EXYl99Q3gqkq4RVhoTNSTYHz2Or6Cig2BJRbLJyqk15ZQE5qcq/ldHS8zzx/wgCA3HQeI63t2j2mEU9PA==
ngx-gallery@^5.10.0:
version "5.10.0"
resolved "https://registry.yarnpkg.com/ngx-gallery/-/ngx-gallery-5.10.0.tgz#21f623cb788578dbb5a3625c869712de2b95258c"
integrity sha512-+2DnsBfkIzNQoReOHf6+OMf06+qyQQMyVVN4iQAtL0+KykjVqDZiBwLQtmwajDWMGph6O1HNKLrqTcmgqw+d2A==
ngx-infinite-scroll@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-6.0.1.tgz#571e54860ce32839451569bcf6e7a63cfae327bd"