Skip to content

Commit ac30ad8

Browse files
Merge branch 'dspace-cris-2023_02_x' into task/dspace-cris-2023_02_x/DSC-1872
# Conflicts: # src/app/shared/shared.module.ts
2 parents 13d13f5 + c4e09bf commit ac30ad8

36 files changed

Lines changed: 2041 additions & 8 deletions

src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[attr.aria-labelledby]="'sidebarName-' + section.id"
66
[title]="('menu.section.icon.' + section.id) | translate"
77
[routerLink]="itemModel.link"
8+
[queryParams]="itemModel.queryParams"
89
(keyup.space)="navigate($event)"
910
(keyup.enter)="navigate($event)"
1011
href="javascript:void(0);"
@@ -21,3 +22,4 @@
2122
</div>
2223
</a>
2324
</div>
25+

src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
4949
navigate(event: any): void {
5050
event.preventDefault();
5151
if (!this.isDisabled) {
52-
this.router.navigate(this.itemModel.link);
52+
const extras = {
53+
queryParams: this.itemModel.queryParams
54+
};
55+
this.router.navigate(this.itemModel.link, extras);
5356
}
5457
}
5558
}

src/app/item-page/item.resolver.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
2020
),
2121
followLink('relationships'),
2222
followLink('version', {}, followLink('versionhistory')),
23+
followLink('bundles', {}, followLink('bitstreams')),
2324
followLink('thumbnail'),
2425
followLink('metrics')
2526
];
@@ -45,7 +46,7 @@ export class ItemResolver implements Resolve<RemoteData<Item>> {
4546
*/
4647
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
4748
const itemRD$ = this.itemService.findById(route.params.id,
48-
true,
49+
false,
4950
false,
5051
...ITEM_PAGE_LINKS_TO_FOLLOW
5152
).pipe(

src/app/menu.resolver.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { createSuccessfulRemoteDataObject$ } from './shared/remote-data.utils';
1919
import { createPaginatedList } from './shared/testing/utils.test';
2020
import { SectionDataService } from './core/layout/section-data.service';
2121
import createSpy = jasmine.createSpy;
22+
import { ConfigurationDataService } from './core/data/configuration-data.service';
2223

2324
const BOOLEAN = { t: true, f: false };
2425
const MENU_STATE = {
@@ -68,6 +69,7 @@ describe('MenuResolver', () => {
6869
let sectionsService;
6970
let authorizationService;
7071
let scriptService;
72+
let configService;
7173

7274
beforeEach(waitForAsync(() => {
7375
menuService = new MenuServiceStub();
@@ -83,6 +85,9 @@ describe('MenuResolver', () => {
8385
scriptService = jasmine.createSpyObj('scriptService', {
8486
scriptWithNameExistsAndCanExecute: observableOf(true)
8587
});
88+
configService = jasmine.createSpyObj('ConfigurationDataService', {
89+
findByPropertyName: observableOf({})
90+
});
8691

8792
TestBed.configureTestingModule({
8893
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
@@ -92,6 +97,7 @@ describe('MenuResolver', () => {
9297
{ provide: SectionDataService, useValue: sectionsService },
9398
{ provide: AuthorizationDataService, useValue: authorizationService },
9499
{ provide: ScriptDataService, useValue: scriptService },
100+
{ provide: ConfigurationDataService, useValue: configService },
95101
{
96102
provide: NgbModal, useValue: {
97103
open: () => {/*comment*/

src/app/menu.resolver.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ import { environment } from '../environments/environment';
5252
import { SectionDataService } from './core/layout/section-data.service';
5353
import { Section } from './core/layout/models/section.model';
5454
import { NOTIFICATIONS_RECITER_SUGGESTION_PATH } from './admin/admin-notifications/admin-notifications-routing-paths';
55+
import { ConfigurationDataService } from './core/data/configuration-data.service';
56+
import { ConfigurationProperty } from './core/shared/configuration-property.model';
5557

5658
/**
5759
* Creates all of the app's menus
@@ -70,6 +72,7 @@ export class MenuResolver implements Resolve<boolean> {
7072
protected modalService: NgbModal,
7173
protected scriptDataService: ScriptDataService,
7274
protected sectionDataService: SectionDataService,
75+
protected configService: ConfigurationDataService,
7376
) {
7477
}
7578

@@ -254,6 +257,7 @@ export class MenuResolver implements Resolve<boolean> {
254257
this.createExportMenuSections();
255258
this.createImportMenuSections();
256259
this.createAccessControlMenuSections();
260+
this.createDLExporterMenuItem();
257261

258262
return this.waitForMenu$(MenuID.ADMIN);
259263
}
@@ -609,6 +613,40 @@ export class MenuResolver implements Resolve<boolean> {
609613
});
610614
}
611615

616+
/**
617+
* Add the DL Exporter menu item to the admin menu
618+
*/
619+
createDLExporterMenuItem() {
620+
observableCombineLatest([
621+
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
622+
this.getDLExporterURL(),
623+
this.getDLExporterAccessToken()
624+
]).subscribe(([authorized, url, accesstoken]) => {
625+
console.log(accesstoken);
626+
const urlSegments = url.split('?');
627+
const queryParamSegments = urlSegments[1].split('=');
628+
this.menuService.addSection(MenuID.ADMIN,
629+
{
630+
id: 'loginmiur_dlexporter_url',
631+
index: 15,
632+
active: false,
633+
visible: authorized && (hasValue(url) && url.length > 0) && (hasValue(accesstoken) && accesstoken.length > 0),
634+
model: {
635+
type: MenuItemType.LINK,
636+
text: 'menu.section.loginmiur_dlexporter_url',
637+
disabled: false,
638+
link: urlSegments[0],
639+
queryParams: {
640+
[queryParamSegments[0]]: queryParamSegments[1]
641+
}
642+
} as LinkMenuItemModel,
643+
icon: 'fa-solid fa-arrows-spin',
644+
shouldPersistOnRouteChange: true
645+
}
646+
);
647+
});
648+
}
649+
612650
/**
613651
* Create menu sections dependent on whether or not the current user is a site administrator and on whether or not
614652
* the import scripts exist and the current user is allowed to execute them
@@ -974,4 +1012,25 @@ export class MenuResolver implements Resolve<boolean> {
9741012
const object = data.site ? data.site : data.dso?.payload;
9751013
return object?._links?.self?.href;
9761014
}
1015+
1016+
/**
1017+
* Get the DL Exporter URL from the configuration
1018+
*/
1019+
getDLExporterURL(): Observable<string> {
1020+
return this.configService.findByPropertyName('loginmiur.dlexporter.url').pipe(
1021+
getFirstCompletedRemoteData(),
1022+
map((res: RemoteData<ConfigurationProperty>) => {
1023+
return res?.payload?.values[0];
1024+
})
1025+
);
1026+
}
1027+
1028+
private getDLExporterAccessToken() {
1029+
return this.configService.findByPropertyName('loginmiur.dlexporter.accesstoken').pipe(
1030+
getFirstCompletedRemoteData(),
1031+
map((res: RemoteData<ConfigurationProperty>) => {
1032+
return res?.payload?.values[0];
1033+
})
1034+
);
1035+
}
9771036
}

src/app/shared/metadata-link-view/metadata-link-view.component.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
[ngTemplateOutletContext]="{metadataView: metadataView}"></ng-container>
44
</div>
55
<ng-template class="d-flex" #linkToAuthority let-metadataView="metadataView">
6-
<span [ngbPopover]="popContent"
7-
[closeDelay]="500"
8-
[openDelay]="1000"
6+
<span [dsStickyPopover]="popContent"
7+
[openDelay]="100"
98
[animation]="true"
10-
[autoClose]="'outside'"
9+
[autoClose]="true"
1110
container="body"
12-
triggers="mouseenter:mouseleave">
11+
triggers="mouseenter">
1312
<a rel="noopener noreferrer" data-test="linkToAuthority"
1413
[routerLink]="['/items/' + metadataView.authority]">
1514
<span dsEntityIcon
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {
2+
ElementRef,
3+
Directive,
4+
Input,
5+
TemplateRef,
6+
Renderer2,
7+
OnInit,
8+
OnDestroy,
9+
Injector,
10+
ViewContainerRef,
11+
NgZone,
12+
ChangeDetectorRef,
13+
ApplicationRef, Inject
14+
} from '@angular/core';
15+
import {NgbPopover, NgbPopoverConfig} from '@ng-bootstrap/ng-bootstrap';
16+
import {DOCUMENT} from '@angular/common';
17+
import {NavigationStart, Router} from '@angular/router';
18+
import {Subscription} from 'rxjs';
19+
20+
/**
21+
* Directive to create a sticky popover using NgbPopover.
22+
* The popover remains open when the mouse is over its content and closes when the mouse leaves.
23+
*/
24+
@Directive({
25+
selector: '[dsStickyPopover]'
26+
})
27+
export class StickyPopoverDirective extends NgbPopover implements OnInit, OnDestroy {
28+
/** Template for the sticky popover content */
29+
@Input() dsStickyPopover: TemplateRef<any>;
30+
31+
/** Subscriptions to manage router events */
32+
subs: Subscription[] = [];
33+
34+
/** Flag to determine if the popover can be closed */
35+
private canClosePopover: boolean;
36+
37+
/** Reference to the element the directive is applied to */
38+
private readonly _elRef;
39+
40+
/** Renderer to listen to and manipulate DOM elements */
41+
private readonly _render;
42+
43+
constructor(
44+
_elementRef: ElementRef<HTMLElement>,
45+
_renderer: Renderer2, injector: Injector,
46+
viewContainerRef: ViewContainerRef,
47+
config: NgbPopoverConfig,
48+
_ngZone: NgZone,
49+
@Inject(DOCUMENT) _document: Document,
50+
_changeDetector: ChangeDetectorRef,
51+
applicationRef: ApplicationRef,
52+
private router: Router
53+
) {
54+
super(_elementRef, _renderer, injector, viewContainerRef, config, _ngZone, document, _changeDetector, applicationRef);
55+
this._elRef = _elementRef;
56+
this._render = _renderer;
57+
this.triggers = 'manual';
58+
this.container = 'body';
59+
}
60+
61+
/**
62+
* Sets up event listeners for mouse enter, mouse leave, and click events.
63+
*/
64+
ngOnInit(): void {
65+
super.ngOnInit();
66+
this.ngbPopover = this.dsStickyPopover;
67+
68+
this._render.listen(this._elRef.nativeElement, 'mouseenter', () => {
69+
this.canClosePopover = true;
70+
this.open();
71+
});
72+
73+
this._render.listen(this._elRef.nativeElement, 'mouseleave', () => {
74+
setTimeout(() => {
75+
if (this.canClosePopover) {
76+
this.close();
77+
}
78+
}, 100);
79+
});
80+
81+
this._render.listen(this._elRef.nativeElement, 'click', () => {
82+
this.close();
83+
});
84+
85+
this.subs.push(
86+
this.router.events.subscribe((event) => {
87+
if (event instanceof NavigationStart) {
88+
this.close();
89+
}
90+
})
91+
);
92+
}
93+
94+
/**
95+
* Opens the popover and sets up event listeners for mouse over and mouse out events on the popover.
96+
*/
97+
open() {
98+
super.open();
99+
const popover = window.document.querySelector('.popover');
100+
this._render.listen(popover, 'mouseover', () => {
101+
this.canClosePopover = false;
102+
});
103+
104+
this._render.listen(popover, 'mouseout', () => {
105+
this.canClosePopover = true;
106+
setTimeout(() => {
107+
if (this.canClosePopover) {
108+
this.close();
109+
}
110+
}, 0);
111+
});
112+
}
113+
114+
/**
115+
* Unsubscribes from all subscriptions when the directive is destroyed.
116+
*/
117+
ngOnDestroy() {
118+
super.ngOnDestroy();
119+
this.subs.forEach((sub) => sub.unsubscribe());
120+
}
121+
}

src/app/shared/shared.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ import { MetadataLinkViewPopoverComponent } from './metadata-link-view/metadata-
358358
import { MetadataLinkViewAvatarPopoverComponent } from './metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component';
359359
import { MetadataLinkViewOrcidComponent } from './metadata-link-view/metadata-link-view-orcid/metadata-link-view-orcid.component';
360360
import { SwitchComponent } from './switch/switch.component';
361+
import {StickyPopoverDirective} from './metadata-link-view/sticky-popover.directive';
361362

362363
const MODULES = [
363364
CommonModule,
@@ -625,6 +626,7 @@ const DIRECTIVES = [
625626
ContextHelpDirective,
626627
EntityIconDirective,
627628
MarkdownDirective,
629+
StickyPopoverDirective
628630
];
629631

630632
@NgModule({

0 commit comments

Comments
 (0)