mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge pull request #4251 from atmire/fix-embargoed-thumbnails_contribute-8.1
[Port dspace-8_x] Show restricted thumbnails to users with access rights
This commit is contained in:
@@ -16,12 +16,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #showThumbnail>
|
<ng-template #showThumbnail>
|
||||||
<ds-media-viewer-image *ngIf="mediaOptions.image && mediaOptions.video"
|
<ds-thumbnail [thumbnail]="(thumbnailsRD$ | async)?.payload?.page[0]">
|
||||||
[image]="(thumbnailsRD$ | async)?.payload?.page[0]?._links.content.href || thumbnailPlaceholder"
|
|
||||||
[preview]="false"
|
|
||||||
></ds-media-viewer-image>
|
|
||||||
<ds-thumbnail *ngIf="!(mediaOptions.image && mediaOptions.video)"
|
|
||||||
[thumbnail]="(thumbnailsRD$ | async)?.payload?.page[0]">
|
|
||||||
</ds-thumbnail>
|
</ds-thumbnail>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import {
|
||||||
|
NO_ERRORS_SCHEMA,
|
||||||
|
PLATFORM_ID,
|
||||||
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ComponentFixture,
|
ComponentFixture,
|
||||||
TestBed,
|
TestBed,
|
||||||
@@ -14,7 +17,9 @@ import { of as observableOf } from 'rxjs';
|
|||||||
|
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { Bitstream } from '../../core/shared/bitstream.model';
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
|
import { FileService } from '../../core/shared/file.service';
|
||||||
import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
|
import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
|
||||||
import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component';
|
import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
import { AuthServiceMock } from '../../shared/mocks/auth.service.mock';
|
import { AuthServiceMock } from '../../shared/mocks/auth.service.mock';
|
||||||
@@ -31,6 +36,9 @@ import { MediaViewerComponent } from './media-viewer.component';
|
|||||||
describe('MediaViewerComponent', () => {
|
describe('MediaViewerComponent', () => {
|
||||||
let comp: MediaViewerComponent;
|
let comp: MediaViewerComponent;
|
||||||
let fixture: ComponentFixture<MediaViewerComponent>;
|
let fixture: ComponentFixture<MediaViewerComponent>;
|
||||||
|
let authService;
|
||||||
|
let authorizationService;
|
||||||
|
let fileService;
|
||||||
|
|
||||||
const mockBitstream: Bitstream = Object.assign(new Bitstream(), {
|
const mockBitstream: Bitstream = Object.assign(new Bitstream(), {
|
||||||
sizeBytes: 10201,
|
sizeBytes: 10201,
|
||||||
@@ -55,7 +63,7 @@ describe('MediaViewerComponent', () => {
|
|||||||
'dc.title': [
|
'dc.title': [
|
||||||
{
|
{
|
||||||
language: null,
|
language: null,
|
||||||
value: 'test_word.docx',
|
value: 'test_image.jpg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -73,6 +81,15 @@ describe('MediaViewerComponent', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
authService = jasmine.createSpyObj('AuthService', {
|
||||||
|
isAuthenticated: observableOf(true),
|
||||||
|
});
|
||||||
|
authorizationService = jasmine.createSpyObj('AuthorizationService', {
|
||||||
|
isAuthorized: observableOf(true),
|
||||||
|
});
|
||||||
|
fileService = jasmine.createSpyObj('FileService', {
|
||||||
|
retrieveFileDownloadLink: null,
|
||||||
|
});
|
||||||
return TestBed.configureTestingModule({
|
return TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
@@ -88,6 +105,10 @@ describe('MediaViewerComponent', () => {
|
|||||||
MetadataFieldWrapperComponent,
|
MetadataFieldWrapperComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: AuthService, useValue: authService },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: FileService, useValue: fileService },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
{ provide: BitstreamDataService, useValue: bitstreamDataService },
|
{ provide: BitstreamDataService, useValue: bitstreamDataService },
|
||||||
{ provide: ThemeService, useValue: getMockThemeService() },
|
{ provide: ThemeService, useValue: getMockThemeService() },
|
||||||
{ provide: AuthService, useValue: new AuthServiceMock() },
|
{ provide: AuthService, useValue: new AuthServiceMock() },
|
||||||
@@ -150,9 +171,9 @@ describe('MediaViewerComponent', () => {
|
|||||||
expect(mediaItem.thumbnail).toBe(null);
|
expect(mediaItem.thumbnail).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a default, thumbnail', () => {
|
it('should display a default thumbnail', () => {
|
||||||
const defaultThumbnail = fixture.debugElement.query(
|
const defaultThumbnail = fixture.debugElement.query(
|
||||||
By.css('ds-media-viewer-image'),
|
By.css('ds-thumbnail'),
|
||||||
);
|
);
|
||||||
expect(defaultThumbnail.nativeElement).toBeDefined();
|
expect(defaultThumbnail.nativeElement).toBeDefined();
|
||||||
});
|
});
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
<div class="thumbnail" [class.limit-width]="limitWidth">
|
<div class="thumbnail" [class.limit-width]="limitWidth">
|
||||||
<div *ngIf="isLoading" class="thumbnail-content outer">
|
<div *ngIf="(isLoading$ | async)" class="thumbnail-content outer">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="centered">
|
<div class="centered">
|
||||||
<ds-loading [spinner]="true"></ds-loading>
|
<ds-loading [spinner]="true"></ds-loading>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- don't use *ngIf="!isLoading" so the thumbnail can load in while the animation is playing -->
|
<!-- don't use *ngIf="!(isLoading$ | async)" so the thumbnail can load in while the animation is playing -->
|
||||||
<img *ngIf="src !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': isLoading}"
|
<img *ngIf="(src$ | async) !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': (isLoading$ | async)}"
|
||||||
[src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
|
[src]="(src$ | async) | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
|
||||||
<div *ngIf="src === null && !isLoading" class="thumbnail-content outer">
|
<div *ngIf="(src$ | async) === null && (isLoading$ | async) === false" class="thumbnail-content outer">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="thumbnail-placeholder centered lead">
|
<div class="thumbnail-placeholder centered lead">
|
||||||
{{ placeholder | translate }}
|
{{ placeholder | translate }}
|
||||||
|
@@ -100,31 +100,31 @@ describe('ThumbnailComponent', () => {
|
|||||||
|
|
||||||
describe('loading', () => {
|
describe('loading', () => {
|
||||||
it('should start out with isLoading$ true', () => {
|
it('should start out with isLoading$ true', () => {
|
||||||
expect(comp.isLoading).toBeTrue();
|
expect(comp.isLoading$.getValue()).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set isLoading$ to false once an image is successfully loaded', () => {
|
it('should set isLoading$ to false once an image is successfully loaded', () => {
|
||||||
comp.setSrc('http://bit.stream');
|
comp.setSrc('http://bit.stream');
|
||||||
fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load'));
|
fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load'));
|
||||||
expect(comp.isLoading).toBeFalse();
|
expect(comp.isLoading$.getValue()).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set isLoading$ to false once the src is set to null', () => {
|
it('should set isLoading$ to false once the src is set to null', () => {
|
||||||
comp.setSrc(null);
|
comp.setSrc(null);
|
||||||
expect(comp.isLoading).toBeFalse();
|
expect(comp.isLoading$.getValue()).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show a loading animation while isLoading$ is true', () => {
|
it('should show a loading animation while isLoading$ is true', () => {
|
||||||
expect(de.query(By.css('ds-loading'))).toBeTruthy();
|
expect(de.query(By.css('ds-loading'))).toBeTruthy();
|
||||||
|
|
||||||
comp.isLoading = false;
|
comp.isLoading$.next(false);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy();
|
expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with a thumbnail image', () => {
|
describe('with a thumbnail image', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
comp.src = 'https://bit.stream';
|
comp.src$.next('https://bit.stream');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ describe('ThumbnailComponent', () => {
|
|||||||
expect(img).toBeTruthy();
|
expect(img).toBeTruthy();
|
||||||
expect(img.classes['d-none']).toBeTrue();
|
expect(img.classes['d-none']).toBeTrue();
|
||||||
|
|
||||||
comp.isLoading = false;
|
comp.isLoading$.next(false);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
img = fixture.debugElement.query(By.css('img.thumbnail-content'));
|
img = fixture.debugElement.query(By.css('img.thumbnail-content'));
|
||||||
expect(img).toBeTruthy();
|
expect(img).toBeTruthy();
|
||||||
@@ -144,14 +144,14 @@ describe('ThumbnailComponent', () => {
|
|||||||
|
|
||||||
describe('without a thumbnail image', () => {
|
describe('without a thumbnail image', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
comp.src = null;
|
comp.src$.next(null);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only show the HTML placeholder once done loading', () => {
|
it('should only show the HTML placeholder once done loading', () => {
|
||||||
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy();
|
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy();
|
||||||
|
|
||||||
comp.isLoading = false;
|
comp.isLoading$.next(false);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy();
|
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy();
|
||||||
});
|
});
|
||||||
@@ -247,14 +247,14 @@ describe('ThumbnailComponent', () => {
|
|||||||
describe('fallback', () => {
|
describe('fallback', () => {
|
||||||
describe('if there is a default image', () => {
|
describe('if there is a default image', () => {
|
||||||
it('should display the default image', () => {
|
it('should display the default image', () => {
|
||||||
comp.src = 'http://bit.stream';
|
comp.src$.next('http://bit.stream');
|
||||||
comp.defaultImage = 'http://default.img';
|
comp.defaultImage = 'http://default.img';
|
||||||
comp.errorHandler();
|
comp.errorHandler();
|
||||||
expect(comp.src).toBe(comp.defaultImage);
|
expect(comp.src$.getValue()).toBe(comp.defaultImage);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include the alt text', () => {
|
it('should include the alt text', () => {
|
||||||
comp.src = 'http://bit.stream';
|
comp.src$.next('http://bit.stream');
|
||||||
comp.defaultImage = 'http://default.img';
|
comp.defaultImage = 'http://default.img';
|
||||||
comp.errorHandler();
|
comp.errorHandler();
|
||||||
|
|
||||||
@@ -266,10 +266,10 @@ describe('ThumbnailComponent', () => {
|
|||||||
|
|
||||||
describe('if there is no default image', () => {
|
describe('if there is no default image', () => {
|
||||||
it('should display the HTML placeholder', () => {
|
it('should display the HTML placeholder', () => {
|
||||||
comp.src = 'http://default.img';
|
comp.src$.next('http://default.img');
|
||||||
comp.defaultImage = null;
|
comp.defaultImage = null;
|
||||||
comp.errorHandler();
|
comp.errorHandler();
|
||||||
expect(comp.src).toBe(null);
|
expect(comp.src$.getValue()).toBe(null);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement;
|
const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement;
|
||||||
@@ -361,7 +361,7 @@ describe('ThumbnailComponent', () => {
|
|||||||
it('should show the default image', () => {
|
it('should show the default image', () => {
|
||||||
comp.defaultImage = 'default/image.jpg';
|
comp.defaultImage = 'default/image.jpg';
|
||||||
comp.ngOnChanges({});
|
comp.ngOnChanges({});
|
||||||
expect(comp.src).toBe('default/image.jpg');
|
expect(comp.src$.getValue()).toBe('default/image.jpg');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -417,7 +417,7 @@ describe('ThumbnailComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should start out with isLoading$ true', () => {
|
it('should start out with isLoading$ true', () => {
|
||||||
expect(comp.isLoading).toBeTrue();
|
expect(comp.isLoading$.getValue()).toBeTrue();
|
||||||
expect(de.query(By.css('ds-loading'))).toBeTruthy();
|
expect(de.query(By.css('ds-loading'))).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -11,7 +11,10 @@ import {
|
|||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
of as observableOf,
|
||||||
|
} from 'rxjs';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
import { AuthService } from '../core/auth/auth.service';
|
||||||
@@ -55,7 +58,7 @@ export class ThumbnailComponent implements OnChanges {
|
|||||||
/**
|
/**
|
||||||
* The src attribute used in the template to render the image.
|
* The src attribute used in the template to render the image.
|
||||||
*/
|
*/
|
||||||
src: string = undefined;
|
src$: BehaviorSubject<string> = new BehaviorSubject<string>(undefined);
|
||||||
|
|
||||||
retriedWithToken = false;
|
retriedWithToken = false;
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ export class ThumbnailComponent implements OnChanges {
|
|||||||
* Whether the thumbnail is currently loading
|
* Whether the thumbnail is currently loading
|
||||||
* Start out as true to avoid flashing the alt text while a thumbnail is being loaded.
|
* Start out as true to avoid flashing the alt text while a thumbnail is being loaded.
|
||||||
*/
|
*/
|
||||||
isLoading = true;
|
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PLATFORM_ID) private platformID: any,
|
@Inject(PLATFORM_ID) private platformID: any,
|
||||||
@@ -134,7 +137,7 @@ export class ThumbnailComponent implements OnChanges {
|
|||||||
* Otherwise, fall back to the default image or a HTML placeholder
|
* Otherwise, fall back to the default image or a HTML placeholder
|
||||||
*/
|
*/
|
||||||
errorHandler() {
|
errorHandler() {
|
||||||
const src = this.src;
|
const src = this.src$.getValue();
|
||||||
const thumbnail = this.bitstream;
|
const thumbnail = this.bitstream;
|
||||||
const thumbnailSrc = thumbnail?._links?.content?.href;
|
const thumbnailSrc = thumbnail?._links?.content?.href;
|
||||||
|
|
||||||
@@ -186,9 +189,22 @@ export class ThumbnailComponent implements OnChanges {
|
|||||||
* @param src
|
* @param src
|
||||||
*/
|
*/
|
||||||
setSrc(src: string): void {
|
setSrc(src: string): void {
|
||||||
this.src = src;
|
// only update the src if it has changed (the parent component may fire the same one multiple times
|
||||||
if (src === null) {
|
if (this.src$.getValue() !== src) {
|
||||||
this.isLoading = false;
|
// every time the src changes we need to start the loading animation again, as it's possible
|
||||||
|
// that it is first set to null when the parent component initializes and then set to
|
||||||
|
// the actual value
|
||||||
|
//
|
||||||
|
// isLoading$ will be set to false by the error or success handler afterwards, except in the
|
||||||
|
// case where src is null, then we have to set it manually here (because those handlers won't
|
||||||
|
// trigger)
|
||||||
|
if (src !== null && this.isLoading$.getValue() === false) {
|
||||||
|
this.isLoading$.next(true);
|
||||||
|
}
|
||||||
|
this.src$.next(src);
|
||||||
|
if (src === null && this.isLoading$.getValue() === true) {
|
||||||
|
this.isLoading$.next(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +212,6 @@ export class ThumbnailComponent implements OnChanges {
|
|||||||
* Stop the loading animation once the thumbnail is successfully loaded
|
* Stop the loading animation once the thumbnail is successfully loaded
|
||||||
*/
|
*/
|
||||||
successHandler() {
|
successHandler() {
|
||||||
this.isLoading = false;
|
this.isLoading$.next(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user