Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dspace-cris-2023_02_x' into task…
Browse files Browse the repository at this point in the history
…/dspace-cris-2023_02_x/LM-46
  • Loading branch information
steph-ieffam committed Aug 14, 2024
2 parents 3d5f802 + 640dc02 commit f81e178
Show file tree
Hide file tree
Showing 96 changed files with 897 additions and 328 deletions.
4 changes: 4 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,12 @@ browseBy:
fiveYearLimit: 30
# The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
defaultLowerLimit: 1900
# Whether to add item badges to BOTH browse and search result lists.
showLabels: true
# If true, thumbnail images for items will be added to BOTH search and browse result lists.
showThumbnails: true
# Whether to add item thumbnail images to BOTH browse and search result lists.
showMetrics: false
# The number of entries in a paginated browse results list.
# Rounded to the nearest size in the list of selectable sizes on the
# settings menu.
Expand Down
6 changes: 5 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
import { ServerCheckGuard } from './core/server-check/server-check.guard';
import { MenuResolver } from './menu.resolver';
import { ThemedPageErrorComponent } from './page-error/themed-page-error.component';
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
import { RedirectService } from './redirect/redirect.service';

Expand Down Expand Up @@ -99,7 +100,10 @@ import { RedirectService } from './redirect/redirect.service';
path: FORGOT_PASSWORD_PATH,
loadChildren: () => import('./forgot-password/forgot-password.module')
.then((m) => m.ForgotPasswordModule),
canActivate: [EndUserAgreementCurrentUserGuard]
canActivate: [
ForgotPasswordCheckGuard,
EndUserAgreementCurrentUserGuard
]
},
{
path: COMMUNITY_MODULE_PATH,
Expand Down
1 change: 1 addition & 0 deletions src/app/core/data/feature-authorization/feature-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export enum FeatureID {
CanEditItem = 'canEditItem',
CanRegisterDOI = 'canRegisterDOI',
CanSubscribe = 'canSubscribeDso',
EPersonForgotPassword = 'epersonForgotPassword',
ShowClaimItem = 'showClaimItem',
CanCorrectItem = 'canCorrectItem',
}
3 changes: 1 addition & 2 deletions src/app/core/data/processes/process-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ export class ProcessDataService extends IdentifiableDataService<Process> impleme
* @param processId The ID of the process
*/
getProcess(processId: string): Observable<RemoteData<Process>> {
const href$ = this.getProcessEndpoint(processId);
return this.findByHref(href$,false);
return this.findById(processId, false);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/app/core/layout/models/section.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface TopSection extends SectionComponent {
componentType: 'top';
numberOfItems: number;
showThumbnails: boolean;
template: TopSectionTemplateType;
}

export interface SearchSection extends SectionComponent {
Expand Down Expand Up @@ -96,3 +97,10 @@ export interface TopSectionColumn {
metadataField: string;
titleKey: string;
}

/**
* Represents the type of template to use for the section
*/
export enum TopSectionTemplateType {
DEFAULT = 'default', // CRIS default template
}
1 change: 1 addition & 0 deletions src/app/core/locale/locale.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class LocaleService {
this.routeService.getQueryParameterValue('lang').subscribe(lang => {
if (lang && this.translate.getLangs().includes(lang)) {
this.setCurrentLanguageCode(lang);
this.routeService.removeQueryParam('lang');
}
});
}
Expand Down
31 changes: 31 additions & 0 deletions src/app/core/rest-property/forgot-password-check-guard.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service';
import { FeatureID } from '../data/feature-authorization/feature-id';
import {
SingleFeatureAuthorizationGuard
} from '../data/feature-authorization/feature-authorization-guard/single-feature-authorization.guard';
import { AuthService } from '../auth/auth.service';

@Injectable({
providedIn: 'root'
})
/**
* Guard that checks if the forgot-password feature is enabled
*/
export class ForgotPasswordCheckGuard extends SingleFeatureAuthorizationGuard {

constructor(
protected readonly authorizationService: AuthorizationDataService,
protected readonly router: Router,
protected readonly authService: AuthService
) {
super(authorizationService, router, authService);
}

getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return of(FeatureID.EPersonForgotPassword);
}

}
2 changes: 1 addition & 1 deletion src/app/core/services/internal-link.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('InternalLinkService', () => {

it('should return unchanged link for external link', () => {
const result = service.getRelativePath('https://externalDomain/my-link');
expect(result).toBe('https://externalDomain/my-link');
expect(result).toBe('/https://externalDomain/my-link');
});

it('should return unchanged link for internal link with leading "/"', () => {
Expand Down
36 changes: 29 additions & 7 deletions src/app/core/services/internal-link.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,41 @@ export class InternalLinkService {
* @returns The relative path for the given internal link.
*/
public getRelativePath(link: string): string {
// Create a Domain object for the provided link
// Obtaining the base URL, disregarding query parameters
const baseUrl = link.split('?')[0];
const currentDomain = new URL(this.currentURL).hostname;

if (link.startsWith(this.currentURL)) {
const currentSegments = link.substring(this.currentURL.length);
if (baseUrl.startsWith(this.currentURL) || baseUrl.startsWith(currentDomain)) {
const base = baseUrl.startsWith(this.currentURL) ? this.currentURL : currentDomain;
const currentSegments = baseUrl.substring(base.length);
return currentSegments.startsWith('/') ? currentSegments : `/${currentSegments}`;
}

if (link.startsWith(currentDomain)) {
const currentSegments = link.substring(currentDomain.length);
return currentSegments.startsWith('/') ? currentSegments : `/${currentSegments}`;
return baseUrl.startsWith('/') ? baseUrl : `/${baseUrl}`;
}

/**
* Parse the query parameters from a given URL link.
*
* @param link The URL link containing query parameters.
* @returns An object containing the parsed query parameters.
*/
public getQueryParams(link: string): Record<string, string> {
const queryParams: Record<string, string> = {};

const queryStringStartIndex = link.indexOf('?');
if (queryStringStartIndex !== -1) {
const paramsString = link.substring(queryStringStartIndex + 1);
const paramsArray = paramsString.split('&');

paramsArray.forEach(param => {
const [key, value] = param.split('=');
if (key && value) {
queryParams[key] = decodeURIComponent(value.replace(/\+/g, ' '));
}
});
}

return link;
return queryParams;
}
}
19 changes: 19 additions & 0 deletions src/app/core/services/route.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,23 @@ export class RouteService {
}
);
}

/**
* Remove a parameter from the current route
* @param key The parameter name
*/
removeQueryParam(key: string) {
let queryParams = { ...this.route.snapshot.queryParams };
delete queryParams[key];

// Navigate to the same route with the updated queryParams
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams: queryParams,
}
);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<ds-themed-item-list-preview [item]="match"
[object]="itemPreviewObject"
[metadataList]="metadataList"
[showLabel]="showLabel"
[showMetrics]="showMetrics"
[showThumbnails]="showThumbnails">
</ds-themed-item-list-preview>
<div class="offset-2">
Expand Down
3 changes: 2 additions & 1 deletion src/app/explore-page/explore-page.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
import { RemoteData } from '../core/data/remote-data';
import { BrowseSection, FacetSection, SearchSection, Section, TopSection } from '../core/layout/models/section.model';
import { BrowseSection, FacetSection, SearchSection, Section, TopSectionTemplateType, TopSection } from '../core/layout/models/section.model';
import { SectionDataService } from '../core/layout/section-data.service';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { ExplorePageComponent } from './explore-page.component';
Expand All @@ -37,6 +37,7 @@ describe('ExploreComponent', () => {
numberOfItems: 5,
titleKey: 'lastPublications',
showThumbnails: false,
template: TopSectionTemplateType.DEFAULT
};

const searchComponent: SearchSection = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { followLink } from '../utils/follow-link-config.model';
import { CollectionElementLinkType } from '../object-collection/collection-element-link.type';
import { TopSection } from '../../core/layout/models/section.model';
import { Component, Input, OnChanges, OnInit, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformServer } from '@angular/common';

import { SearchService } from '../../core/shared/search/search.service';
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { SearchResult } from '../search/models/search-result.model';
import { Context } from '../../core/shared/context.model';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import {
getAllCompletedRemoteData,
getPaginatedListPayload,
getRemoteDataPayload,
toDSpaceObjectListRD,
} from '../../core/shared/operators';
import { APP_CONFIG } from '../../../config/app-config.interface';
import { BehaviorSubject, Observable, mergeMap } from 'rxjs';
import { Item } from '../../core/shared/item.model';
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';

@Component({
template: ''
})
export abstract class AbstractBrowseElementsComponent implements OnInit, OnChanges {

protected readonly appConfig = inject(APP_CONFIG);
protected readonly platformId = inject(PLATFORM_ID);
protected readonly searchService = inject(SearchService);

protected followThumbnailLink: boolean; // to be overridden

@Input() paginatedSearchOptions: PaginatedSearchOptions;

@Input() context: Context;

@Input() topSection: TopSection;

public collectionElementLinkTypeEnum = CollectionElementLinkType;

paginatedSearchOptionsBS: BehaviorSubject<PaginatedSearchOptions>;

searchResults$: Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>;

searchResultArray$: Observable<DSpaceObject[]>;

ngOnChanges() {
if (isPlatformServer(this.platformId)) {
return;
}
this.paginatedSearchOptionsBS?.next(this.paginatedSearchOptions);
}

ngOnInit() {
const followLinks = this.followThumbnailLink ? [followLink('thumbnail')] : [];
this.paginatedSearchOptionsBS = new BehaviorSubject<PaginatedSearchOptions>(this.paginatedSearchOptions);
this.searchResults$ = this.paginatedSearchOptionsBS.asObservable().pipe(
mergeMap((paginatedSearchOptions) =>
this.searchService.search(paginatedSearchOptions, null, true, true, ...followLinks),
),
getAllCompletedRemoteData(),
);

this.searchResultArray$ = this.searchResults$.pipe(
toDSpaceObjectListRD(),
getRemoteDataPayload(),
getPaginatedListPayload(),
);
}

getItemPageRoute(item: DSpaceObject | Item) {
return getItemPageRoute(item as Item);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
<div class="m-2">
<ng-container *ngIf="searchResults">
<ul *ngIf="searchResults?.hasSucceeded" class="list-unstyled">
<li *ngFor="let object of searchResults?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="true && !last">
<ds-listable-object-component-loader [context]="context"
[index]="i"
[listID]="paginatedSearchOptions.configuration"
[object]="object"
[showMetrics]="showMetrics"
[showThumbnails]="showThumbnails"
[viewMode]="paginatedSearchOptions.view"></ds-listable-object-component-loader>
</li>
</ul>
<div *ngIf="searchResults?.hasFailed">
{{ 'remote.error' | translate }}
</div>
</ng-container>
<ds-loading *ngIf="!searchResults"></ds-loading>
<div [ngSwitch]="(sectionTemplateType | lowercase)">

<ng-container *ngSwitchDefault>
<ds-themed-default-browse-elements
[showMetrics]="showMetrics"
[showThumbnails]="topSection.showThumbnails"
[topSection]="topSection"
[showLabel]="showLabel"
[paginatedSearchOptions]="paginatedSearchOptionsBS.asObservable() | async"
[context]="context"
></ds-themed-default-browse-elements>
</ng-container>

</div>
Loading

0 comments on commit f81e178

Please sign in to comment.