Added functionality to disable COAR notify support link in footer

Also fixed error that showed a blank page instead of a 404 when disabling EULA/Privacy Policy pages
This commit is contained in:
Alexandre Vryghem
2024-04-12 01:38:34 +02:00
parent 2ef2e9b334
commit e10630f131
15 changed files with 83 additions and 59 deletions

View File

@@ -407,10 +407,11 @@ mediaViewer:
# Whether the end user agreement is required before users use the repository. # Whether the end user agreement is required before users use the repository.
# If enabled, the user will be required to accept the agreement before they can use the repository. # If enabled, the user will be required to accept the agreement before they can use the repository.
# And whether the privacy statement should exist or not. # And whether the privacy statement/COAR notify support page should exist or not.
info: info:
enableEndUserAgreement: true enableEndUserAgreement: true
enablePrivacyStatement: true enablePrivacyStatement: true
enableCOARNotifySupport: true
# Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/) # Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
# display in supported metadata fields. By default, only dc.description.abstract is supported. # display in supported metadata fields. By default, only dc.description.abstract is supported.

View File

@@ -34,7 +34,6 @@ export function getBitstreamRequestACopyRoute(item, bitstream): { routerLink: st
}, },
}; };
} }
export const COAR_NOTIFY_SUPPORT = 'coar-notify-support';
export const HOME_PAGE_PATH = 'home'; export const HOME_PAGE_PATH = 'home';

View File

@@ -82,10 +82,10 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="notify-enabled text-white" [hidden]="!coarLdnEnabled"> <div *ngIf="coarLdnEnabled$ | async" class="notify-enabled text-white">
<a class="coar-notify-support-route" routerLink="info/coar-notify-support"> <a class="coar-notify-support-route" routerLink="info/coar-notify-support">
<img class="n-coar" src="assets/images/n-coar.png" [attr.alt]="'menu.header.image.logo' | translate" /> <img class="n-coar" src="assets/images/n-coar.png" [attr.alt]="'menu.header.image.logo' | translate" />
COAR Notify {{ 'footer.link.coar-notify-support' | translate }}
</a> </a>
</div> </div>
</div> </div>

View File

@@ -1,9 +1,3 @@
// ... test imports
import { CommonModule } from '@angular/common';
import {
CUSTOM_ELEMENTS_SCHEMA,
DebugElement,
} from '@angular/core';
import { import {
ComponentFixture, ComponentFixture,
fakeAsync, fakeAsync,
@@ -13,28 +7,19 @@ import {
} from '@angular/core/testing'; } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { StoreModule } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core';
import {
TranslateLoader,
TranslateModule,
} from '@ngx-translate/core';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { environment } from 'src/environments/environment';
import { storeModuleConfig } from '../app.reducer'; import { APP_CONFIG } from '../../config/app-config.interface';
import { environment } from '../../environments/environment.test';
import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service'; import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
import { ActivatedRouteStub } from '../shared/testing/active-router.stub'; import { ActivatedRouteStub } from '../shared/testing/active-router.stub';
import { AuthorizationDataServiceStub } from '../shared/testing/authorization-service.stub'; import { AuthorizationDataServiceStub } from '../shared/testing/authorization-service.stub';
// Load the implementations that should be tested
import { FooterComponent } from './footer.component'; import { FooterComponent } from './footer.component';
let comp: FooterComponent; let comp: FooterComponent;
let compAny: any;
let fixture: ComponentFixture<FooterComponent>; let fixture: ComponentFixture<FooterComponent>;
let de: DebugElement;
let el: HTMLElement;
let notifyInfoService = { let notifyInfoService = {
isCoarConfigEnabled: () => of(true), isCoarConfigEnabled: () => of(true),
@@ -43,19 +28,16 @@ let notifyInfoService = {
describe('Footer component', () => { describe('Footer component', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
return TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [CommonModule, StoreModule.forRoot({}, storeModuleConfig), TranslateModule.forRoot({ imports: [
loader: { TranslateModule.forRoot(),
provide: TranslateLoader, ],
useClass: TranslateLoaderMock,
},
}), FooterComponent],
providers: [ providers: [
FooterComponent, FooterComponent,
{ provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub }, { provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub },
{ provide: NotifyInfoService, useValue: notifyInfoService }, { provide: NotifyInfoService, useValue: notifyInfoService },
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
{ provide: APP_CONFIG, useValue: environment },
], ],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}); });
})); }));
@@ -63,10 +45,6 @@ describe('Footer component', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent); fixture = TestBed.createComponent(FooterComponent);
comp = fixture.componentInstance; comp = fixture.componentInstance;
compAny = comp as any;
// query for the title <p> by CSS element selector
de = fixture.debugElement.query(By.css('p'));
el = de.nativeElement;
}); });
it('should create footer', inject([FooterComponent], (app: FooterComponent) => { it('should create footer', inject([FooterComponent], (app: FooterComponent) => {
@@ -76,23 +54,25 @@ describe('Footer component', () => {
it('should set showPrivacyPolicy to the value of environment.info.enablePrivacyStatement', () => { it('should set showPrivacyPolicy to the value of environment.info.enablePrivacyStatement', () => {
comp.ngOnInit();
expect(comp.showPrivacyPolicy).toBe(environment.info.enablePrivacyStatement); expect(comp.showPrivacyPolicy).toBe(environment.info.enablePrivacyStatement);
}); });
it('should set showEndUserAgreement to the value of environment.info.enableEndUserAgreement', () => { it('should set showEndUserAgreement to the value of environment.info.enableEndUserAgreement', () => {
comp.ngOnInit();
expect(comp.showEndUserAgreement).toBe(environment.info.enableEndUserAgreement); expect(comp.showEndUserAgreement).toBe(environment.info.enableEndUserAgreement);
}); });
describe('showCookieSettings', () => { describe('showCookieSettings', () => {
it('should call cookies.showSettings() if cookies is defined', () => { it('should call cookies.showSettings() if cookies is defined', () => {
const cookies = jasmine.createSpyObj('cookies', ['showSettings']); const cookies = jasmine.createSpyObj('cookies', ['showSettings']);
compAny.cookies = cookies; comp.cookies = cookies;
comp.showCookieSettings(); comp.showCookieSettings();
expect(cookies.showSettings).toHaveBeenCalled(); expect(cookies.showSettings).toHaveBeenCalled();
}); });
it('should not call cookies.showSettings() if cookies is undefined', () => { it('should not call cookies.showSettings() if cookies is undefined', () => {
compAny.cookies = undefined; comp.cookies = undefined;
expect(() => comp.showCookieSettings()).not.toThrow(); expect(() => comp.showCookieSettings()).not.toThrow();
}); });
@@ -107,9 +87,7 @@ describe('Footer component', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should set coarLdnEnabled based on notifyInfoService', () => { it('should render COAR notify support link', () => {
expect(comp.coarLdnEnabled).toBeTruthy();
// Check if COAR Notify section is rendered
const notifySection = fixture.debugElement.query(By.css('.notify-enabled')); const notifySection = fixture.debugElement.query(By.css('.notify-enabled'));
expect(notifySection).toBeTruthy(); expect(notifySection).toBeTruthy();
}); });

View File

@@ -5,13 +5,21 @@ import {
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
Inject,
OnInit,
Optional, Optional,
} from '@angular/core'; } from '@angular/core';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs'; import {
Observable,
of as observableOf,
} from 'rxjs';
import { environment } from '../../environments/environment'; import {
APP_CONFIG,
AppConfig,
} from '../../config/app-config.interface';
import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service'; import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { FeatureID } from '../core/data/feature-authorization/feature-id';
@@ -25,27 +33,31 @@ import { hasValue } from '../shared/empty.util';
standalone: true, standalone: true,
imports: [NgIf, RouterLink, AsyncPipe, DatePipe, TranslateModule], imports: [NgIf, RouterLink, AsyncPipe, DatePipe, TranslateModule],
}) })
export class FooterComponent { export class FooterComponent implements OnInit {
dateObj: number = Date.now(); dateObj: number = Date.now();
/** /**
* A boolean representing if to show or not the top footer container * A boolean representing if to show or not the top footer container
*/ */
showTopFooter = false; showTopFooter = false;
showPrivacyPolicy = environment.info.enablePrivacyStatement; showPrivacyPolicy: boolean;
showEndUserAgreement = environment.info.enableEndUserAgreement; showEndUserAgreement: boolean;
showSendFeedback$: Observable<boolean>; showSendFeedback$: Observable<boolean>;
coarLdnEnabled: boolean; coarLdnEnabled$: Observable<boolean>;
constructor( constructor(
@Optional() private cookies: KlaroService, @Optional() public cookies: KlaroService,
private authorizationService: AuthorizationDataService, protected authorizationService: AuthorizationDataService,
private notifyInfoService: NotifyInfoService, protected notifyInfoService: NotifyInfoService,
@Inject(APP_CONFIG) protected appConfig: AppConfig,
) { ) {
}
ngOnInit(): void {
this.showPrivacyPolicy = this.appConfig.info.enablePrivacyStatement;
this.showEndUserAgreement = this.appConfig.info.enableEndUserAgreement;
this.coarLdnEnabled$ = this.appConfig.info.enableCOARNotifySupport ? this.notifyInfoService.isCoarConfigEnabled() : observableOf(false);
this.showSendFeedback$ = this.authorizationService.isAuthorized(FeatureID.CanSendFeedback); this.showSendFeedback$ = this.authorizationService.isAuthorized(FeatureID.CanSendFeedback);
this.notifyInfoService.isCoarConfigEnabled().subscribe(coarLdnEnabled => {
this.coarLdnEnabled = coarLdnEnabled;
});
} }
showCookieSettings() { showCookieSettings() {

View File

@@ -1,17 +1,26 @@
import {
Route,
Routes,
} from '@angular/router';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { notifyInfoGuard } from '../core/coar-notify/notify-info/notify-info.guard';
import { feedbackGuard } from '../core/feedback/feedback.guard'; import { feedbackGuard } from '../core/feedback/feedback.guard';
import { hasValue } from '../shared/empty.util';
import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component'; import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component';
import { ThemedFeedbackComponent } from './feedback/themed-feedback.component'; import { ThemedFeedbackComponent } from './feedback/themed-feedback.component';
import { import {
COAR_NOTIFY_SUPPORT,
END_USER_AGREEMENT_PATH, END_USER_AGREEMENT_PATH,
FEEDBACK_PATH, FEEDBACK_PATH,
PRIVACY_PATH, PRIVACY_PATH,
} from './info-routing-paths'; } from './info-routing-paths';
import { NotifyInfoComponent } from './notify-info/notify-info.component';
import { ThemedPrivacyComponent } from './privacy/themed-privacy.component'; import { ThemedPrivacyComponent } from './privacy/themed-privacy.component';
export const ROUTES = [ export const ROUTES: Routes = [
{ {
path: FEEDBACK_PATH, path: FEEDBACK_PATH,
component: ThemedFeedbackComponent, component: ThemedFeedbackComponent,
@@ -31,4 +40,16 @@ export const ROUTES = [
resolve: { breadcrumb: i18nBreadcrumbResolver }, resolve: { breadcrumb: i18nBreadcrumbResolver },
data: { title: 'info.privacy.title', breadcrumbKey: 'info.privacy' }, data: { title: 'info.privacy.title', breadcrumbKey: 'info.privacy' },
} : undefined, } : undefined,
]; environment.info.enableCOARNotifySupport ? {
path: COAR_NOTIFY_SUPPORT,
component: NotifyInfoComponent,
canActivate: [notifyInfoGuard],
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
data: {
title: 'info.coar-notify-support.title',
breadcrumbKey: 'info.coar-notify-support',
},
} : undefined,
].filter((route: Route) => hasValue(route));

View File

@@ -3,6 +3,7 @@ import { getInfoModulePath } from '../app-routing-paths';
export const END_USER_AGREEMENT_PATH = 'end-user-agreement'; export const END_USER_AGREEMENT_PATH = 'end-user-agreement';
export const PRIVACY_PATH = 'privacy'; export const PRIVACY_PATH = 'privacy';
export const FEEDBACK_PATH = 'feedback'; export const FEEDBACK_PATH = 'feedback';
export const COAR_NOTIFY_SUPPORT = 'coar-notify-support';
export function getEndUserAgreementPath() { export function getEndUserAgreementPath() {
return getSubPath(END_USER_AGREEMENT_PATH); return getSubPath(END_USER_AGREEMENT_PATH);
@@ -16,6 +17,10 @@ export function getFeedbackPath() {
return getSubPath(FEEDBACK_PATH); return getSubPath(FEEDBACK_PATH);
} }
export function getCOARNotifySupportPath(): string {
return getSubPath(COAR_NOTIFY_SUPPORT);
}
function getSubPath(path: string) { function getSubPath(path: string) {
return `${getInfoModulePath()}/${path}`; return `${getInfoModulePath()}/${path}`;
} }

View File

@@ -6,9 +6,9 @@ import { ActivatedRoute } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
import { NotifyInfoComponent } from './notify-info.component'; import { NotifyInfoComponent } from './notify-info.component';
import { NotifyInfoService } from './notify-info.service';
describe('NotifyInfoComponent', () => { describe('NotifyInfoComponent', () => {
let component: NotifyInfoComponent; let component: NotifyInfoComponent;

View File

@@ -11,7 +11,7 @@ import {
of, of,
} from 'rxjs'; } from 'rxjs';
import { NotifyInfoService } from './notify-info.service'; import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
@Component({ @Component({
selector: 'ds-notify-info', selector: 'ds-notify-info',

View File

@@ -1886,6 +1886,8 @@
"footer.link.feedback": "Send Feedback", "footer.link.feedback": "Send Feedback",
"footer.link.coar-notify-support": "COAR Notify",
"forgot-email.form.header": "Forgot Password", "forgot-email.form.header": "Forgot Password",
"forgot-email.form.info": "Enter the email address associated with the account.", "forgot-email.form.info": "Enter the email address associated with the account.",
@@ -2150,6 +2152,10 @@
"info.feedback.page_help": "The page related to your feedback", "info.feedback.page_help": "The page related to your feedback",
"info.coar-notify-support.title": "COAR Notify Support",
"info.coar-notify-support.breadcrumbs": "COAR Notify Support",
"item.alerts.private": "This item is non-discoverable", "item.alerts.private": "This item is non-discoverable",
"item.alerts.withdrawn": "This item has been withdrawn", "item.alerts.withdrawn": "This item has been withdrawn",
@@ -6173,10 +6179,6 @@
"ldn-register-new-service.notification.success.title": "Success", "ldn-register-new-service.notification.success.title": "Success",
"ldn-register-new-service.notification.success.content": "The process was successfully created", "ldn-register-new-service.notification.success.content": "The process was successfully created",
"info.coar-notify-support.title": "Notify Support",
"info.coar-notify.breadcrumbs": "Notify Support",
"submission.sections.notify.info": "The selected service is compatible with the item according to its current status. {{ service.name }}: {{ service.description }}", "submission.sections.notify.info": "The selected service is compatible with the item according to its current status. {{ service.name }}: {{ service.description }}",
"item.page.endorsement": "Endorsement", "item.page.endorsement": "Endorsement",

View File

@@ -468,9 +468,13 @@ export class DefaultAppConfig implements AppConfig {
// Disabling the privacy policy feature will result in: // Disabling the privacy policy feature will result in:
// - A 404 page if you manually try to navigate to the privacy policy page at info/privacy // - A 404 page if you manually try to navigate to the privacy policy page at info/privacy
// - All mentions of the privacy policy being removed from the UI (e.g. in the footer) // - All mentions of the privacy policy being removed from the UI (e.g. in the footer)
// Disabling the COAR notify support page feature will result in:
// - A 404 page if you manually try to navigate to the COAR notify support page
// - All mentions of the COAR notify support page being removed from the UI (e.g. in the footer)
info: InfoConfig = { info: InfoConfig = {
enableEndUserAgreement: true, enableEndUserAgreement: true,
enablePrivacyStatement: true, enablePrivacyStatement: true,
enableCOARNotifySupport: true,
}; };
// Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/) // Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)

View File

@@ -3,4 +3,5 @@ import { Config } from './config.interface';
export interface InfoConfig extends Config { export interface InfoConfig extends Config {
enableEndUserAgreement: boolean; enableEndUserAgreement: boolean;
enablePrivacyStatement: boolean; enablePrivacyStatement: boolean;
enableCOARNotifySupport: boolean;
} }

View File

@@ -314,6 +314,7 @@ export const environment: BuildConfig = {
info: { info: {
enableEndUserAgreement: true, enableEndUserAgreement: true,
enablePrivacyStatement: true, enablePrivacyStatement: true,
enableCOARNotifySupport: true,
}, },
markdown: { markdown: {
enabled: false, enabled: false,