diff --git a/config/config.example.yml b/config/config.example.yml index 407b5b958ed..82c061dab22 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -407,10 +407,11 @@ mediaViewer: # 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. -# And whether the privacy statement should exist or not. +# And whether the privacy statement/COAR notify support page should exist or not. info: enableEndUserAgreement: true enablePrivacyStatement: true + enableCOARNotifySupport: true # 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. diff --git a/src/app/admin/admin-search-page/admin-search-page.component.html b/src/app/admin/admin-search-page/admin-search-page.component.html index 69ff132fe3f..516799ddf90 100644 --- a/src/app/admin/admin-search-page/admin-search-page.component.html +++ b/src/app/admin/admin-search-page/admin-search-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/admin/admin-search-page/admin-search-page.component.spec.ts b/src/app/admin/admin-search-page/admin-search-page.component.spec.ts index dd502ed1123..d3a39f12f4a 100644 --- a/src/app/admin/admin-search-page/admin-search-page.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-page.component.spec.ts @@ -6,7 +6,7 @@ import { } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; -import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { AdminSearchPageComponent } from './admin-search-page.component'; @@ -23,7 +23,7 @@ describe('AdminSearchPageComponent', () => { schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(AdminSearchPageComponent, { remove: { - imports: [ConfigurationSearchPageComponent], + imports: [ThemedConfigurationSearchPageComponent], }, }) .compileComponents(); diff --git a/src/app/admin/admin-search-page/admin-search-page.component.ts b/src/app/admin/admin-search-page/admin-search-page.component.ts index d6215be971a..99909b8257f 100644 --- a/src/app/admin/admin-search-page/admin-search-page.component.ts +++ b/src/app/admin/admin-search-page/admin-search-page.component.ts @@ -1,14 +1,14 @@ import { Component } from '@angular/core'; import { Context } from '../../core/shared/context.model'; -import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; @Component({ selector: 'ds-admin-search-page', templateUrl: './admin-search-page.component.html', styleUrls: ['./admin-search-page.component.scss'], standalone: true, - imports: [ConfigurationSearchPageComponent], + imports: [ThemedConfigurationSearchPageComponent], }) /** diff --git a/src/app/admin/admin-workflow-page/admin-workflow-page.component.html b/src/app/admin/admin-workflow-page/admin-workflow-page.component.html index d12cefb3313..c16ed311681 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-page.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/admin/admin-workflow-page/admin-workflow-page.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-page.component.spec.ts index ff326fbc270..252a38e7716 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-page.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-page.component.spec.ts @@ -5,7 +5,7 @@ import { waitForAsync, } from '@angular/core/testing'; -import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; import { AdminWorkflowPageComponent } from './admin-workflow-page.component'; describe('AdminSearchPageComponent', () => { @@ -20,7 +20,7 @@ describe('AdminSearchPageComponent', () => { .overrideComponent(AdminWorkflowPageComponent, { remove: { imports: [ - ConfigurationSearchPageComponent, + ThemedConfigurationSearchPageComponent, ], }, }) diff --git a/src/app/admin/admin-workflow-page/admin-workflow-page.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-page.component.ts index fdc34fe4ab9..62a66039aff 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-page.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-page.component.ts @@ -1,14 +1,14 @@ import { Component } from '@angular/core'; import { Context } from '../../core/shared/context.model'; -import { ConfigurationSearchPageComponent } from '../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../search-page/themed-configuration-search-page.component'; @Component({ selector: 'ds-admin-workflow-page', templateUrl: './admin-workflow-page.component.html', styleUrls: ['./admin-workflow-page.component.scss'], standalone: true, - imports: [ConfigurationSearchPageComponent], + imports: [ThemedConfigurationSearchPageComponent], }) /** diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index d6df53ffaf4..7d202f16e9c 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -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'; diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index ea34e02d56e..0d678299e14 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -7,7 +7,7 @@ -
+
@@ -71,7 +71,7 @@ -
+
{{ dsoNameService.getName(node.payload) }}
diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index b937b2ce60c..35487f91ae2 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -82,10 +82,10 @@
Footer Content
-
+
- COAR Notify + {{ 'footer.link.coar-notify-support' | translate }}
diff --git a/src/app/footer/footer.component.spec.ts b/src/app/footer/footer.component.spec.ts index 8608eeaf5e3..527e5703643 100644 --- a/src/app/footer/footer.component.spec.ts +++ b/src/app/footer/footer.component.spec.ts @@ -1,9 +1,3 @@ -// ... test imports -import { CommonModule } from '@angular/common'; -import { - CUSTOM_ELEMENTS_SCHEMA, - DebugElement, -} from '@angular/core'; import { ComponentFixture, fakeAsync, @@ -13,28 +7,19 @@ import { } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; -import { StoreModule } from '@ngrx/store'; -import { - TranslateLoader, - TranslateModule, -} from '@ngx-translate/core'; +import { TranslateModule } from '@ngx-translate/core'; 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 { 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 { AuthorizationDataServiceStub } from '../shared/testing/authorization-service.stub'; -// Load the implementations that should be tested import { FooterComponent } from './footer.component'; let comp: FooterComponent; -let compAny: any; let fixture: ComponentFixture; -let de: DebugElement; -let el: HTMLElement; let notifyInfoService = { isCoarConfigEnabled: () => of(true), @@ -43,19 +28,16 @@ let notifyInfoService = { describe('Footer component', () => { beforeEach(waitForAsync(() => { return TestBed.configureTestingModule({ - imports: [CommonModule, StoreModule.forRoot({}, storeModuleConfig), TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock, - }, - }), FooterComponent], + imports: [ + TranslateModule.forRoot(), + ], providers: [ FooterComponent, { provide: AuthorizationDataService, useClass: AuthorizationDataServiceStub }, { provide: NotifyInfoService, useValue: notifyInfoService }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, + { provide: APP_CONFIG, useValue: environment }, ], - schemas: [CUSTOM_ELEMENTS_SCHEMA], }); })); @@ -63,10 +45,6 @@ describe('Footer component', () => { beforeEach(() => { fixture = TestBed.createComponent(FooterComponent); comp = fixture.componentInstance; - compAny = comp as any; - // query for the title

by CSS element selector - de = fixture.debugElement.query(By.css('p')); - el = de.nativeElement; }); 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', () => { + comp.ngOnInit(); expect(comp.showPrivacyPolicy).toBe(environment.info.enablePrivacyStatement); }); it('should set showEndUserAgreement to the value of environment.info.enableEndUserAgreement', () => { + comp.ngOnInit(); expect(comp.showEndUserAgreement).toBe(environment.info.enableEndUserAgreement); }); describe('showCookieSettings', () => { it('should call cookies.showSettings() if cookies is defined', () => { const cookies = jasmine.createSpyObj('cookies', ['showSettings']); - compAny.cookies = cookies; + comp.cookies = cookies; comp.showCookieSettings(); expect(cookies.showSettings).toHaveBeenCalled(); }); it('should not call cookies.showSettings() if cookies is undefined', () => { - compAny.cookies = undefined; + comp.cookies = undefined; expect(() => comp.showCookieSettings()).not.toThrow(); }); @@ -107,9 +87,7 @@ describe('Footer component', () => { fixture.detectChanges(); }); - it('should set coarLdnEnabled based on notifyInfoService', () => { - expect(comp.coarLdnEnabled).toBeTruthy(); - // Check if COAR Notify section is rendered + it('should render COAR notify support link', () => { const notifySection = fixture.debugElement.query(By.css('.notify-enabled')); expect(notifySection).toBeTruthy(); }); diff --git a/src/app/footer/footer.component.ts b/src/app/footer/footer.component.ts index 3043cf32847..f9fdde78346 100644 --- a/src/app/footer/footer.component.ts +++ b/src/app/footer/footer.component.ts @@ -5,13 +5,21 @@ import { } from '@angular/common'; import { Component, + Inject, + OnInit, Optional, } from '@angular/core'; import { RouterLink } from '@angular/router'; 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 { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; @@ -25,27 +33,31 @@ import { hasValue } from '../shared/empty.util'; standalone: true, imports: [NgIf, RouterLink, AsyncPipe, DatePipe, TranslateModule], }) -export class FooterComponent { +export class FooterComponent implements OnInit { dateObj: number = Date.now(); /** * A boolean representing if to show or not the top footer container */ showTopFooter = false; - showPrivacyPolicy = environment.info.enablePrivacyStatement; - showEndUserAgreement = environment.info.enableEndUserAgreement; + showPrivacyPolicy: boolean; + showEndUserAgreement: boolean; showSendFeedback$: Observable; - coarLdnEnabled: boolean; + coarLdnEnabled$: Observable; constructor( - @Optional() private cookies: KlaroService, - private authorizationService: AuthorizationDataService, - private notifyInfoService: NotifyInfoService, + @Optional() public cookies: KlaroService, + protected authorizationService: AuthorizationDataService, + 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.notifyInfoService.isCoarConfigEnabled().subscribe(coarLdnEnabled => { - this.coarLdnEnabled = coarLdnEnabled; - }); } showCookieSettings() { diff --git a/src/app/home-page/home-coar/home-coar.component.ts b/src/app/home-page/home-coar/home-coar.component.ts new file mode 100644 index 00000000000..f8efa29d070 --- /dev/null +++ b/src/app/home-page/home-coar/home-coar.component.ts @@ -0,0 +1,92 @@ +import { isPlatformServer } from '@angular/common'; +import { + Component, + Inject, + OnDestroy, + OnInit, + PLATFORM_ID, +} from '@angular/core'; +import { + of as observableOf, + Subscription, +} from 'rxjs'; +import { switchMap } from 'rxjs/operators'; + +import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service'; +import { + LinkDefinition, + LinkHeadService, +} from '../../core/services/link-head.service'; +import { ServerResponseService } from '../../core/services/server-response.service'; +import { isNotEmpty } from '../../shared/empty.util'; + +@Component({ + selector: 'ds-home-coar', + template: '', + standalone: true, +}) +export class HomeCoarComponent implements OnInit, OnDestroy { + + /** + * An array of LinkDefinition objects representing inbox links for the home page. + */ + inboxLinks: LinkDefinition[] = []; + + subs: Subscription[] = []; + + constructor( + protected linkHeadService: LinkHeadService, + protected notifyInfoService: NotifyInfoService, + protected responseService: ServerResponseService, + @Inject(PLATFORM_ID) protected platformId: string, + ) { + } + + ngOnInit(): void { + // Get COAR REST API URLs from REST configuration + // only if COAR configuration is enabled + this.subs.push(this.notifyInfoService.isCoarConfigEnabled().pipe( + switchMap((coarLdnEnabled: boolean) => coarLdnEnabled ? this.notifyInfoService.getCoarLdnLocalInboxUrls() : observableOf([])), + ).subscribe((coarRestApiUrls: string[]) => { + if (coarRestApiUrls.length > 0) { + this.initPageLinks(coarRestApiUrls); + } + })); + } + + /** + * It removes the inbox links from the head of the html. + */ + ngOnDestroy(): void { + this.subs.forEach((sub: Subscription) => sub.unsubscribe()); + this.inboxLinks.forEach((link: LinkDefinition) => { + this.linkHeadService.removeTag(`href='${link.href}'`); + }); + } + + /** + * Initializes page links for COAR REST API URLs. + * @param coarRestApiUrls An array of COAR REST API URLs. + */ + private initPageLinks(coarRestApiUrls: string[]): void { + const rel = this.notifyInfoService.getInboxRelationLink(); + let links = ''; + coarRestApiUrls.forEach((coarRestApiUrl: string) => { + // Add link to head + const tag: LinkDefinition = { + href: coarRestApiUrl, + rel: rel, + }; + this.inboxLinks.push(tag); + this.linkHeadService.addTag(tag); + + links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`; + }); + + if (isPlatformServer(this.platformId)) { + // Add link to response header + this.responseService.setHeader('Link', links); + } + } + +} diff --git a/src/app/home-page/home-page.component.default.scss b/src/app/home-page/home-page.component.default.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index c1d4b8a98c7..f0760f20aa3 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -1,19 +1,29 @@ + -

-
-
- -
-
+
+ +
+ + [searchPlaceholder]="'home.search-form.placeholder' | translate"> +
-
+
+ + +
+ + +
+
diff --git a/src/app/home-page/home-page.component.scss b/src/app/home-page/home-page.component.scss index 29a5e65b4c2..653de42b44b 100644 --- a/src/app/home-page/home-page.component.scss +++ b/src/app/home-page/home-page.component.scss @@ -1,2 +1,5 @@ -:host { -} \ No newline at end of file +:host ::ng-deep { + .container-fluid .container { + padding: 0; + } +} diff --git a/src/app/home-page/home-page.component.ts b/src/app/home-page/home-page.component.ts index afc71ae381d..4fa0e89e8ee 100644 --- a/src/app/home-page/home-page.component.ts +++ b/src/app/home-page/home-page.component.ts @@ -1,44 +1,32 @@ import { AsyncPipe, - isPlatformServer, NgClass, NgIf, } from '@angular/common'; import { Component, Inject, - OnDestroy, OnInit, - PLATFORM_ID, } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { - EMPTY, - Observable, -} from 'rxjs'; -import { - map, - switchMap, -} from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { APP_CONFIG, AppConfig, } from 'src/config/app-config.interface'; -import { environment } from '../../environments/environment'; -import { NotifyInfoService } from '../core/coar-notify/notify-info/notify-info.service'; -import { - LinkDefinition, - LinkHeadService, -} from '../core/services/link-head.service'; -import { ServerResponseService } from '../core/services/server-response.service'; import { Site } from '../core/shared/site.model'; import { SuggestionsPopupComponent } from '../notifications/suggestions-popup/suggestions-popup.component'; import { ConfigurationSearchPageComponent } from '../search-page/configuration-search-page.component'; -import { isNotEmpty } from '../shared/empty.util'; +import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component'; +import { HostWindowService } from '../shared/host-window.service'; import { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.component'; +import { PageWithSidebarComponent } from '../shared/sidebar/page-with-sidebar.component'; +import { SidebarService } from '../shared/sidebar/sidebar.service'; import { ViewTrackerComponent } from '../statistics/angulartics/dspace/view-tracker.component'; +import { HomeCoarComponent } from './home-coar/home-coar.component'; import { ThemedHomeNewsComponent } from './home-news/themed-home-news.component'; import { RecentItemListComponent } from './recent-item-list/recent-item-list.component'; import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component'; @@ -48,80 +36,30 @@ import { ThemedTopLevelCommunityListComponent } from './top-level-community-list styleUrls: ['./home-page.component.scss'], templateUrl: './home-page.component.html', standalone: true, - imports: [ThemedHomeNewsComponent, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, ConfigurationSearchPageComponent, SuggestionsPopupComponent], + imports: [ThemedHomeNewsComponent, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, ConfigurationSearchPageComponent, SuggestionsPopupComponent, ThemedConfigurationSearchPageComponent, PageWithSidebarComponent, HomeCoarComponent], }) -export class HomePageComponent implements OnInit, OnDestroy { +export class HomePageComponent implements OnInit { site$: Observable; + isXsOrSm$: Observable; recentSubmissionspageSize: number; - /** - * An array of LinkDefinition objects representing inbox links for the home page. - */ - inboxLinks: LinkDefinition[] = []; + showDiscoverFilters: boolean; constructor( @Inject(APP_CONFIG) protected appConfig: AppConfig, - private route: ActivatedRoute, - private responseService: ServerResponseService, - private notifyInfoService: NotifyInfoService, - protected linkHeadService: LinkHeadService, - @Inject(PLATFORM_ID) private platformId: string, + protected route: ActivatedRoute, + protected sidebarService: SidebarService, + protected windowService: HostWindowService, ) { - this.recentSubmissionspageSize = environment.homePage.recentSubmissions.pageSize; - // Get COAR REST API URLs from REST configuration - // only if COAR configuration is enabled - this.notifyInfoService.isCoarConfigEnabled().pipe( - switchMap((coarLdnEnabled: boolean) => coarLdnEnabled ? this.notifyInfoService.getCoarLdnLocalInboxUrls() : EMPTY, /*{ - if (coarLdnEnabled) { - return this.notifyInfoService.getCoarLdnLocalInboxUrls(); - } else { - return of([]); - } - }*/), - ).subscribe((coarRestApiUrls: string[]) => { - if (coarRestApiUrls?.length > 0) { - this.initPageLinks(coarRestApiUrls); - } - }); + this.recentSubmissionspageSize = this.appConfig.homePage.recentSubmissions.pageSize; + this.showDiscoverFilters = this.appConfig.homePage.showDiscoverFilters; } ngOnInit(): void { + this.isXsOrSm$ = this.windowService.isXsOrSm(); this.site$ = this.route.data.pipe( map((data) => data.site as Site), ); } - /** - * Initializes page links for COAR REST API URLs. - * @param coarRestApiUrls An array of COAR REST API URLs. - */ - private initPageLinks(coarRestApiUrls: string[]): void { - const rel = this.notifyInfoService.getInboxRelationLink(); - let links = ''; - coarRestApiUrls.forEach((coarRestApiUrl: string) => { - // Add link to head - const tag: LinkDefinition = { - href: coarRestApiUrl, - rel: rel, - }; - this.inboxLinks.push(tag); - this.linkHeadService.addTag(tag); - - links = links + (isNotEmpty(links) ? ', ' : '') + `<${coarRestApiUrl}> ; rel="${rel}"`; - }); - - if (isPlatformServer(this.platformId)) { - // Add link to response header - this.responseService.setHeader('Link', links); - } - } - - /** - * It removes the inbox links from the head of the html. - */ - ngOnDestroy(): void { - this.inboxLinks.forEach((link: LinkDefinition) => { - this.linkHeadService.removeTag(`href='${link.href}'`); - }); - } } diff --git a/src/app/info/info-routes.ts b/src/app/info/info-routes.ts index 423a93ea451..8d70ac740fc 100644 --- a/src/app/info/info-routes.ts +++ b/src/app/info/info-routes.ts @@ -1,17 +1,26 @@ +import { + Route, + Routes, +} from '@angular/router'; + import { environment } from '../../environments/environment'; 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 { hasValue } from '../shared/empty.util'; import { ThemedEndUserAgreementComponent } from './end-user-agreement/themed-end-user-agreement.component'; import { ThemedFeedbackComponent } from './feedback/themed-feedback.component'; import { + COAR_NOTIFY_SUPPORT, END_USER_AGREEMENT_PATH, FEEDBACK_PATH, PRIVACY_PATH, } from './info-routing-paths'; +import { NotifyInfoComponent } from './notify-info/notify-info.component'; import { ThemedPrivacyComponent } from './privacy/themed-privacy.component'; -export const ROUTES = [ +export const ROUTES: Routes = [ { path: FEEDBACK_PATH, component: ThemedFeedbackComponent, @@ -31,4 +40,16 @@ export const ROUTES = [ resolve: { breadcrumb: i18nBreadcrumbResolver }, data: { title: 'info.privacy.title', breadcrumbKey: 'info.privacy' }, } : 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)); diff --git a/src/app/info/info-routing-paths.ts b/src/app/info/info-routing-paths.ts index a7fba45a6c3..cd42dd9c1d4 100644 --- a/src/app/info/info-routing-paths.ts +++ b/src/app/info/info-routing-paths.ts @@ -3,6 +3,7 @@ import { getInfoModulePath } from '../app-routing-paths'; export const END_USER_AGREEMENT_PATH = 'end-user-agreement'; export const PRIVACY_PATH = 'privacy'; export const FEEDBACK_PATH = 'feedback'; +export const COAR_NOTIFY_SUPPORT = 'coar-notify-support'; export function getEndUserAgreementPath() { return getSubPath(END_USER_AGREEMENT_PATH); @@ -16,6 +17,10 @@ export function getFeedbackPath() { return getSubPath(FEEDBACK_PATH); } +export function getCOARNotifySupportPath(): string { + return getSubPath(COAR_NOTIFY_SUPPORT); +} + function getSubPath(path: string) { return `${getInfoModulePath()}/${path}`; } diff --git a/src/app/core/coar-notify/notify-info/notify-info.component.html b/src/app/info/notify-info/notify-info.component.html similarity index 100% rename from src/app/core/coar-notify/notify-info/notify-info.component.html rename to src/app/info/notify-info/notify-info.component.html diff --git a/src/app/core/coar-notify/notify-info/notify-info.component.scss b/src/app/info/notify-info/notify-info.component.scss similarity index 100% rename from src/app/core/coar-notify/notify-info/notify-info.component.scss rename to src/app/info/notify-info/notify-info.component.scss diff --git a/src/app/core/coar-notify/notify-info/notify-info.component.spec.ts b/src/app/info/notify-info/notify-info.component.spec.ts similarity index 87% rename from src/app/core/coar-notify/notify-info/notify-info.component.spec.ts rename to src/app/info/notify-info/notify-info.component.spec.ts index 337a874866a..010227b326a 100644 --- a/src/app/core/coar-notify/notify-info/notify-info.component.spec.ts +++ b/src/app/info/notify-info/notify-info.component.spec.ts @@ -6,9 +6,9 @@ import { ActivatedRoute } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; 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 { NotifyInfoService } from './notify-info.service'; describe('NotifyInfoComponent', () => { let component: NotifyInfoComponent; diff --git a/src/app/core/coar-notify/notify-info/notify-info.component.ts b/src/app/info/notify-info/notify-info.component.ts similarity index 93% rename from src/app/core/coar-notify/notify-info/notify-info.component.ts rename to src/app/info/notify-info/notify-info.component.ts index 9822a07f9ba..38bbf445951 100644 --- a/src/app/core/coar-notify/notify-info/notify-info.component.ts +++ b/src/app/info/notify-info/notify-info.component.ts @@ -11,7 +11,7 @@ import { of, } from 'rxjs'; -import { NotifyInfoService } from './notify-info.service'; +import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service'; @Component({ selector: 'ds-notify-info', diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html index 36340bebfa0..939d502d99c 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.html @@ -1,7 +1,7 @@ - - + diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts index 8f918c29164..6771665362e 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts @@ -9,7 +9,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; import { Item } from '../../../../core/shared/item.model'; -import { ConfigurationSearchPageComponent } from '../../../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; import { RelatedEntitiesSearchComponent } from './related-entities-search.component'; describe('RelatedEntitiesSearchComponent', () => { @@ -30,7 +30,7 @@ describe('RelatedEntitiesSearchComponent', () => { }) .overrideComponent(RelatedEntitiesSearchComponent, { remove: { - imports: [ConfigurationSearchPageComponent], + imports: [ThemedConfigurationSearchPageComponent], }, }) .compileComponents(); diff --git a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts index 341d90f9c02..5f4966587b6 100644 --- a/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts +++ b/src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -5,7 +5,7 @@ import { } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { ConfigurationSearchPageComponent } from '../../../../search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; import { isNotEmpty } from '../../../../shared/empty.util'; import { getFilterByRelation } from '../../../../shared/utils/relation-query.utils'; @@ -13,7 +13,7 @@ import { getFilterByRelation } from '../../../../shared/utils/relation-query.uti selector: 'ds-related-entities-search', templateUrl: './related-entities-search.component.html', standalone: true, - imports: [ConfigurationSearchPageComponent], + imports: [ThemedConfigurationSearchPageComponent], }) /** * A component to show related items as search results. diff --git a/src/app/notifications/suggestions.service.ts b/src/app/notifications/suggestions.service.ts index f456914e180..f128c877048 100644 --- a/src/app/notifications/suggestions.service.ts +++ b/src/app/notifications/suggestions.service.ts @@ -38,6 +38,7 @@ import { import { WorkspaceItem } from '../core/submission/models/workspaceitem.model'; import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service'; import { + hasNoValue, hasValue, isNotEmpty, } from '../shared/empty.util'; @@ -165,6 +166,9 @@ export class SuggestionsService { * The EPerson id for which to retrieve suggestion targets */ public retrieveCurrentUserSuggestions(userUuid: string): Observable { + if (hasNoValue(userUuid)) { + return of([]); + } return this.researcherProfileService.findById(userUuid, true).pipe( getFirstCompletedRemoteData(), mergeMap((profile: RemoteData ) => { diff --git a/src/app/search-page/themed-configuration-search-page.component.ts b/src/app/search-page/themed-configuration-search-page.component.ts index fc9497268c3..0d39dca4303 100644 --- a/src/app/search-page/themed-configuration-search-page.component.ts +++ b/src/app/search-page/themed-configuration-search-page.component.ts @@ -2,9 +2,12 @@ import { Component, Input, } from '@angular/core'; -import { Observable } from 'rxjs'; import { Context } from '../core/shared/context.model'; +import { ViewMode } from '../core/shared/view-mode.model'; +import { CollectionElementLinkType } from '../shared/object-collection/collection-element-link.type'; +import { SelectionConfig } from '../shared/search/search-results/search-results.component'; +import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model'; import { ThemedComponent } from '../shared/theme-support/themed.component'; import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; @@ -13,53 +16,85 @@ import { ConfigurationSearchPageComponent } from './configuration-search-page.co */ @Component({ selector: 'ds-themed-configuration-search-page', - styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, }) export class ThemedConfigurationSearchPageComponent extends ThemedComponent { - /** - * The configuration to use for the search options - * If empty, the configuration will be determined by the route parameter called 'configuration' - */ + + @Input() configurationList: SearchConfigurationOption[] = []; + + @Input() context: Context; + @Input() configuration: string; - /** - * The actual query for the fixed filter. - * If empty, the query will be determined by the route parameter called 'filter' - */ @Input() fixedFilterQuery: string; - /** - * True when the search component should show results on the current page - */ + @Input() useCachedVersionIfAvailable: boolean; + @Input() inPlaceSearch: boolean; - /** - * Whether or not the search bar should be visible - */ + @Input() linkType: CollectionElementLinkType; + + @Input() paginationId: string; + @Input() searchEnabled: boolean; - /** - * The width of the sidebar (bootstrap columns) - */ - @Input() - sideBarWidth: number; - - /** - * The currently applied configuration (determines title of search) - */ - @Input() - configuration$: Observable; - - /** - * The current context - */ - @Input() - context: Context; - - protected inAndOutputNames: (keyof ConfigurationSearchPageComponent & keyof this)[] = - ['context', 'configuration', 'fixedFilterQuery', 'inPlaceSearch', 'searchEnabled', 'sideBarWidth']; + @Input() sideBarWidth: number; + + @Input() searchFormPlaceholder: string; + + @Input() selectable: boolean; + + @Input() selectionConfig: SelectionConfig; + + @Input() showCsvExport: boolean; + + @Input() showSidebar: boolean; + + @Input() showThumbnails: boolean; + + @Input() showViewModes: boolean; + + @Input() useUniquePageId: boolean; + + @Input() viewModeList: ViewMode[]; + + @Input() showScopeSelector: boolean; + + @Input() trackStatistics: boolean; + + @Input() query: string; + + @Input() scope: string; + + @Input() hideScopeInUrl: boolean; + + protected inAndOutputNames: (keyof ConfigurationSearchPageComponent & keyof this)[] = [ + 'configurationList', + 'context', + 'configuration', + 'fixedFilterQuery', + 'useCachedVersionIfAvailable', + 'inPlaceSearch', + 'linkType', + 'paginationId', + 'searchEnabled', + 'sideBarWidth', + 'searchFormPlaceholder', + 'selectable', + 'selectionConfig', + 'showCsvExport', + 'showSidebar', + 'showThumbnails', + 'showViewModes', + 'useUniquePageId', + 'viewModeList', + 'showScopeSelector', + 'trackStatistics', + 'query', + 'scope', + 'hideScopeInUrl', + ]; protected getComponentName(): string { return 'ConfigurationSearchPageComponent'; diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html index 3f70a759bcb..695299686d5 100644 --- a/src/app/shared/search/search-filters/search-filters.component.html +++ b/src/app/shared/search/search-filters/search-filters.component.html @@ -7,4 +7,6 @@

{{filterLabel+'.filters.head' | translate}}

- {{"search.filters.reset" | translate}} + + {{"search.filters.reset" | translate}} + diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index c51acf0a7e7..a8a4bc1a6ae 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -1,4 +1,4 @@ -
+
@@ -18,7 +18,9 @@
- + + +
@@ -84,7 +86,7 @@ -
- +
diff --git a/src/app/shared/sidebar/page-with-sidebar.component.html b/src/app/shared/sidebar/page-with-sidebar.component.html index 853258e6dd0..42f1b538193 100644 --- a/src/app/shared/sidebar/page-with-sidebar.component.html +++ b/src/app/shared/sidebar/page-with-sidebar.component.html @@ -2,12 +2,13 @@
-
diff --git a/src/app/shared/sidebar/page-with-sidebar.component.ts b/src/app/shared/sidebar/page-with-sidebar.component.ts index b53193d10e9..d806c37f619 100644 --- a/src/app/shared/sidebar/page-with-sidebar.component.ts +++ b/src/app/shared/sidebar/page-with-sidebar.component.ts @@ -1,5 +1,6 @@ import { AsyncPipe, + NgIf, NgTemplateOutlet, } from '@angular/common'; import { @@ -21,7 +22,11 @@ import { SidebarService } from './sidebar.service'; templateUrl: './page-with-sidebar.component.html', animations: [pushInOut], standalone: true, - imports: [NgTemplateOutlet, AsyncPipe], + imports: [ + AsyncPipe, + NgTemplateOutlet, + NgIf, + ], }) /** * This component takes care of displaying the sidebar properly on all viewports. It does not diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 43e1d48fb0e..d4731f9bb92 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1886,6 +1886,8 @@ "footer.link.feedback": "Send Feedback", + "footer.link.coar-notify-support": "COAR Notify", + "forgot-email.form.header": "Forgot Password", "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.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.withdrawn": "This item has been withdrawn", @@ -6173,10 +6179,6 @@ "ldn-register-new-service.notification.success.title": "Success", "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 }}", "item.page.endorsement": "Endorsement", diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index d148dee148c..d826c9848b3 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -468,9 +468,13 @@ export class DefaultAppConfig implements AppConfig { // 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 // - 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 = { enableEndUserAgreement: true, enablePrivacyStatement: true, + enableCOARNotifySupport: true, }; // Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/) diff --git a/src/config/info-config.interface.ts b/src/config/info-config.interface.ts index b1831962b5b..445936ff0a1 100644 --- a/src/config/info-config.interface.ts +++ b/src/config/info-config.interface.ts @@ -3,4 +3,5 @@ import { Config } from './config.interface'; export interface InfoConfig extends Config { enableEndUserAgreement: boolean; enablePrivacyStatement: boolean; + enableCOARNotifySupport: boolean; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 0fc03f2a4cb..3610755bb12 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -314,6 +314,7 @@ export const environment: BuildConfig = { info: { enableEndUserAgreement: true, enablePrivacyStatement: true, + enableCOARNotifySupport: true, }, markdown: { enabled: false, diff --git a/src/themes/custom/app/home-page/home-page.component.ts b/src/themes/custom/app/home-page/home-page.component.ts index 7c24350acde..53ccbf4526e 100644 --- a/src/themes/custom/app/home-page/home-page.component.ts +++ b/src/themes/custom/app/home-page/home-page.component.ts @@ -6,13 +6,16 @@ import { import { Component } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; +import { HomeCoarComponent } from '../../../../app/home-page/home-coar/home-coar.component'; import { ThemedHomeNewsComponent } from '../../../../app/home-page/home-news/themed-home-news.component'; import { HomePageComponent as BaseComponent } from '../../../../app/home-page/home-page.component'; import { RecentItemListComponent } from '../../../../app/home-page/recent-item-list/recent-item-list.component'; import { ThemedTopLevelCommunityListComponent } from '../../../../app/home-page/top-level-community-list/themed-top-level-community-list.component'; import { SuggestionsPopupComponent } from '../../../../app/notifications/suggestions-popup/suggestions-popup.component'; import { ConfigurationSearchPageComponent } from '../../../../app/search-page/configuration-search-page.component'; +import { ThemedConfigurationSearchPageComponent } from '../../../../app/search-page/themed-configuration-search-page.component'; import { ThemedSearchFormComponent } from '../../../../app/shared/search-form/themed-search-form.component'; +import { PageWithSidebarComponent } from '../../../../app/shared/sidebar/page-with-sidebar.component'; import { ViewTrackerComponent } from '../../../../app/statistics/angulartics/dspace/view-tracker.component'; @Component({ @@ -22,7 +25,7 @@ import { ViewTrackerComponent } from '../../../../app/statistics/angulartics/dsp // templateUrl: './home-page.component.html' templateUrl: '../../../../app/home-page/home-page.component.html', standalone: true, - imports: [ThemedHomeNewsComponent, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, ConfigurationSearchPageComponent, SuggestionsPopupComponent], + imports: [ThemedHomeNewsComponent, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, ConfigurationSearchPageComponent, SuggestionsPopupComponent, ThemedConfigurationSearchPageComponent, PageWithSidebarComponent, HomeCoarComponent], }) export class HomePageComponent extends BaseComponent {