Skip to content

Commit 8523977

Browse files
UoE/feat: show embargo badge on embargoed bitstreams in file section
UoE/feat: show embargo badge on embargoed bitstreams in file section
2 parents c34360d + 3007f6f commit 8523977

22 files changed

Lines changed: 185 additions & 57 deletions

File tree

src/app/core/data/access-status-data.service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badg
55
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
66
import { ObjectCacheService } from '../cache/object-cache.service';
77
import { HALEndpointService } from '../shared/hal-endpoint.service';
8+
import { Bitstream } from '../shared/bitstream.model';
89
import { Item } from '../shared/item.model';
910
import { BaseDataService } from './base/base-data.service';
1011
import { RemoteData } from './remote-data';
1112
import { RequestService } from './request.service';
1213

1314
/**
14-
* Data service responsible for retrieving the access status of Items
15+
* Data service responsible for retrieving the access status of Items and Bitstreams
1516
*/
1617
@Injectable({ providedIn: 'root' })
1718
export class AccessStatusDataService extends BaseDataService<AccessStatusObject> {
@@ -32,4 +33,12 @@ export class AccessStatusDataService extends BaseDataService<AccessStatusObject>
3233
findAccessStatusFor(item: Item): Observable<RemoteData<AccessStatusObject>> {
3334
return this.findByHref(item._links.accessStatus.href);
3435
}
36+
37+
/**
38+
* Returns {@link RemoteData} of {@link AccessStatusObject} that is the access status of the given bitstream
39+
* @param bitstream Bitstream we want the access status of
40+
*/
41+
findAccessStatusForBitstream(bitstream: Bitstream): Observable<RemoteData<AccessStatusObject>> {
42+
return this.findByHref(bitstream._links.accessStatus.href);
43+
}
3544
}

src/app/core/shared/bitstream.model.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
typedObject,
1111
} from '../cache/builders/build-decorators';
1212
import { RemoteData } from '../data/remote-data';
13+
import { AccessStatusObject } from '../../shared/object-collection/shared/badges/access-status-badge/access-status.model';
14+
import { ACCESS_STATUS } from '../../shared/object-collection/shared/badges/access-status-badge/access-status.resource-type';
1315
import { BITSTREAM } from './bitstream.resource-type';
1416
import { BitstreamFormat } from './bitstream-format.model';
1517
import { BITSTREAM_FORMAT } from './bitstream-format.resource-type';
@@ -52,6 +54,7 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
5254
format: HALLink;
5355
content: HALLink;
5456
thumbnail: HALLink;
57+
accessStatus: HALLink;
5558
};
5659

5760
/**
@@ -75,6 +78,13 @@ export class Bitstream extends DSpaceObject implements ChildHALResource {
7578
@link(BUNDLE)
7679
bundle?: Observable<RemoteData<Bundle>>;
7780

81+
/**
82+
* The access status for this Bitstream
83+
* Will be undefined unless the accessStatus {@link HALLink} has been resolved.
84+
*/
85+
@link(ACCESS_STATUS, false, 'accessStatus')
86+
accessStatus?: Observable<RemoteData<AccessStatusObject>>;
87+
7888
getParentLinkKey(): keyof this['_links'] {
7989
return 'format';
8090
}

src/app/item-page/full/field-components/file-section/full-file-section.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ <h3 class="h5 simple-view-element-header">{{"item.page.filesection.original.bund
3333
</dl>
3434
</div>
3535
<div class="col-2">
36-
<ds-file-download-link [bitstream]="file" [item]="item">
36+
<ds-file-download-link [bitstream]="file" [item]="item" [showAccessStatusBadge]="true">
3737
{{"item.page.filesection.download" | translate}}
3838
</ds-file-download-link>
3939
</div>

src/app/item-page/simple/field-components/file-section/file-section.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
22
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
33
<div class="file-section">
4-
<ds-file-download-link *ngFor="let file of bitstreams; let last=last;" [bitstream]="file" [item]="item">
4+
<ds-file-download-link *ngFor="let file of bitstreams; let last=last;" [bitstream]="file" [item]="item" [showAccessStatusBadge]="true">
55
<span>
66
<span *ngIf="primaryBitsreamId === file.id" class="badge badge-primary">{{ 'item.page.bitstreams.primary' | translate }}</span>
77
{{ dsoNameService.getName(file) }}

src/app/shared/file-download-link/file-download-link.component.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
@if (showAccessStatusBadge) {
2+
<ds-access-status-badge [object]="bitstream"></ds-access-status-badge>
3+
}
14
<a [routerLink]="(bitstreamPath$| async)?.routerLink" class="dont-break-out"
25
[queryParams]="(bitstreamPath$| async)?.queryParams"
36
[target]="isBlank ? '_blank': '_self'"
47
[ngClass]="cssClasses"
58
[attr.aria-label]="('file-download-link.download' | translate) + dsoNameService.getName(bitstream)"
69
role="link"
710
tabindex="0">
8-
<span role="img" *ngIf="(canDownload$ |async) !== true" [attr.aria-label]="'file-download-link.restricted' | translate" class="pr-1"><i class="fas fa-lock"></i></span>
11+
<span role="img" *ngIf="(canDownload$ |async) !== true" [attr.aria-label]="'file-download-link.restricted' | translate" class="pl-1"><i class="fas fa-lock"></i></span>
912
<ng-container *ngTemplateOutlet="content"></ng-container>
1013
</a>
1114

src/app/shared/file-download-link/file-download-link.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ import {
3131
hasValue,
3232
isNotEmpty,
3333
} from '../empty.util';
34+
import { ThemedAccessStatusBadgeComponent } from '../object-collection/shared/badges/access-status-badge/themed-access-status-badge.component';
3435

3536
@Component({
3637
selector: 'ds-base-file-download-link',
3738
templateUrl: './file-download-link.component.html',
3839
styleUrls: ['./file-download-link.component.scss'],
3940
standalone: true,
40-
imports: [RouterLink, NgClass, NgIf, NgTemplateOutlet, AsyncPipe, TranslateModule],
41+
imports: [RouterLink, NgClass, NgIf, NgTemplateOutlet, AsyncPipe, TranslateModule, ThemedAccessStatusBadgeComponent],
4142
})
4243
/**
4344
* Component displaying a download link
@@ -65,6 +66,11 @@ export class FileDownloadLinkComponent implements OnInit {
6566

6667
@Input() enableRequestACopy = true;
6768

69+
/**
70+
* A boolean indicating whether the access status badge is displayed
71+
*/
72+
@Input() showAccessStatusBadge = true;
73+
6874
bitstreamPath$: Observable<{
6975
routerLink: string,
7076
queryParams: any,

src/app/shared/file-download-link/themed-file-download-link.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export class ThemedFileDownloadLinkComponent extends ThemedComponent<FileDownloa
2727

2828
@Input() enableRequestACopy: boolean;
2929

30-
protected inAndOutputNames: (keyof FileDownloadLinkComponent & keyof this)[] = ['bitstream', 'item', 'cssClasses', 'isBlank', 'enableRequestACopy'];
30+
@Input() showAccessStatusBadge: boolean;
31+
32+
protected inAndOutputNames: (keyof FileDownloadLinkComponent & keyof this)[] = ['bitstream', 'item', 'cssClasses', 'isBlank', 'enableRequestACopy', 'showAccessStatusBadge'];
3133

3234
protected getComponentName(): string {
3335
return 'FileDownloadLinkComponent';
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
<ng-container *ngIf="showAccessStatus">
2-
<span *ngIf="accessStatus$ | async as accessStatus">
3-
<span [class]="'badge badge-secondary access-status-list-element-badge ' + accessStatusClass">
1+
@if (showAccessStatus) {
2+
@if ((accessStatus$ | async); as status) {
3+
<span [class]="'badge badge-secondary dont-break-out access-status-list-element-badge ' + accessStatusClass">
44
<span class="sr-only">{{ 'listelement.badge.access-status' | translate }}</span>
5-
{{ accessStatus | translate }}
5+
{{ status | translate: { date: (embargoDate$ | async) } }}
66
<span class="sr-only">, </span>
77
</span>
8-
</span>
9-
</ng-container>
8+
}
9+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,35 @@
1+
span {
2+
white-space: normal;
3+
}
4+
5+
:host {
6+
::ng-deep .access-status-list-element-badge {
7+
margin-right: 0.35rem;
8+
}
9+
10+
// Embargo — DataShare blue with white text
11+
::ng-deep .access-status-embargo-listelement-badge,
12+
::ng-deep .embargo-listelement-badge {
13+
background-color: #004f71 !important;
14+
color: #fff !important;
15+
}
16+
17+
// Open access — DataShare success green
18+
::ng-deep .access-status-open-access-listelement-badge {
19+
background-color: #5cb85c !important;
20+
color: #fff !important;
21+
}
22+
23+
// Restricted — DataShare darker blue
24+
::ng-deep .access-status-restricted-listelement-badge {
25+
background-color: #002b3e !important;
26+
color: #fff !important;
27+
}
28+
29+
// Metadata only — DataShare muted
30+
::ng-deep .access-status-metadata-only-listelement-badge {
31+
background-color: #495057 !important;
32+
color: #fff !important;
33+
}
34+
}
135

src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { By } from '@angular/platform-browser';
88
import { TranslateModule } from '@ngx-translate/core';
99
import { environment } from 'src/environments/environment';
1010

11-
import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service';
11+
import { LinkService } from '../../../../../core/cache/builders/link.service';
1212
import { Item } from '../../../../../core/shared/item.model';
1313
import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils';
1414
import { TruncatePipe } from '../../../../utils/truncate.pipe';
@@ -25,7 +25,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
2525
let embargoStatus: AccessStatusObject;
2626
let restrictedStatus: AccessStatusObject;
2727

28-
let accessStatusDataService: AccessStatusDataService;
28+
let linkService: LinkService;
2929

3030
let item: Item;
3131

@@ -50,13 +50,14 @@ describe('ItemAccessStatusBadgeComponent', () => {
5050
status: 'restricted',
5151
});
5252

53-
accessStatusDataService = jasmine.createSpyObj('accessStatusDataService', {
54-
findAccessStatusFor: createSuccessfulRemoteDataObject$(unknownStatus),
53+
linkService = jasmine.createSpyObj('linkService', {
54+
resolveLink: {},
5555
});
5656

5757
item = Object.assign(new Item(), {
5858
uuid: 'item-uuid',
5959
type: 'item',
60+
accessStatus: createSuccessfulRemoteDataObject$(unknownStatus),
6061
});
6162
}
6263

@@ -65,7 +66,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
6566
imports: [TranslateModule.forRoot(), AccessStatusBadgeComponent, TruncatePipe],
6667
schemas: [NO_ERRORS_SCHEMA],
6768
providers: [
68-
{ provide: AccessStatusDataService, useValue: accessStatusDataService },
69+
{ provide: LinkService, useValue: linkService },
6970
],
7071
}).compileComponents();
7172
}
@@ -113,7 +114,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
113114
describe('When the findAccessStatusFor method returns metadata.only', () => {
114115
beforeEach(waitForAsync(() => {
115116
init();
116-
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(metadataOnlyStatus));
117+
item.accessStatus = createSuccessfulRemoteDataObject$(metadataOnlyStatus);
117118
initTestBed();
118119
}));
119120
beforeEach(() => {
@@ -127,7 +128,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
127128
describe('When the findAccessStatusFor method returns open.access', () => {
128129
beforeEach(waitForAsync(() => {
129130
init();
130-
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(openAccessStatus));
131+
item.accessStatus = createSuccessfulRemoteDataObject$(openAccessStatus);
131132
initTestBed();
132133
}));
133134
beforeEach(() => {
@@ -141,7 +142,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
141142
describe('When the findAccessStatusFor method returns embargo', () => {
142143
beforeEach(waitForAsync(() => {
143144
init();
144-
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(embargoStatus));
145+
item.accessStatus = createSuccessfulRemoteDataObject$(embargoStatus);
145146
initTestBed();
146147
}));
147148
beforeEach(() => {
@@ -155,7 +156,7 @@ describe('ItemAccessStatusBadgeComponent', () => {
155156
describe('When the findAccessStatusFor method returns restricted', () => {
156157
beforeEach(waitForAsync(() => {
157158
init();
158-
(accessStatusDataService.findAccessStatusFor as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$(restrictedStatus));
159+
item.accessStatus = createSuccessfulRemoteDataObject$(restrictedStatus);
159160
initTestBed();
160161
}));
161162
beforeEach(() => {

0 commit comments

Comments
 (0)