From cc08a2829ec05e211d4604173c6fd4a1c37893c0 Mon Sep 17 00:00:00 2001 From: Pratik Rajkotiya Date: Wed, 13 Apr 2022 17:48:26 +0530 Subject: [PATCH] [CST-5668] ORCID Authorizations added. --- .../data/feature-authorization/feature-id.ts | 1 + .../item-pages/person/person.component.html | 1 + src/app/item-page/item-page-routing-paths.ts | 1 + src/app/item-page/item-page-routing.module.ts | 10 +- src/app/item-page/item-page.module.ts | 6 + .../orcid-auth/orcid-auth.component.html | 71 +++++++ .../orcid-auth/orcid-auth.component.scss | 0 .../orcid-auth/orcid-auth.component.ts | 98 +++++++++ .../orcid-page/orcid-page.component.html | 1 + .../orcid-page/orcid-page.component.scss | 0 .../orcid-page/orcid-page.component.ts | 9 + .../item-page/orcid-page/orcid-page.guard.ts | 31 +++ .../dso-page-orcid-button.component.html | 5 + .../dso-page-orcid-button.component.scss | 0 .../dso-page-orcid-button.component.spec.ts | 76 +++++++ .../dso-page-orcid-button.component.ts | 43 ++++ src/app/shared/shared.module.ts | 2 + src/assets/i18n/en.json5 | 191 ++++++++++++++++++ src/assets/images/orcid.logo.icon.svg | 21 ++ src/styles/_global-styles.scss | 11 + 20 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html create mode 100644 src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.scss create mode 100644 src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts create mode 100644 src/app/item-page/orcid-page/orcid-page.component.html create mode 100644 src/app/item-page/orcid-page/orcid-page.component.scss create mode 100644 src/app/item-page/orcid-page/orcid-page.component.ts create mode 100644 src/app/item-page/orcid-page/orcid-page.guard.ts create mode 100644 src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.html create mode 100644 src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.scss create mode 100644 src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.spec.ts create mode 100644 src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.ts create mode 100644 src/assets/images/orcid.logo.icon.svg diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 029c75d9cb..1576c37a83 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -28,4 +28,5 @@ export enum FeatureID { CanCreateVersion = 'canCreateVersion', CanViewUsageStatistics = 'canViewUsageStatistics', CanSendFeedback = 'canSendFeedback', + CanSynchronizeWithORCID = 'canSynchronizeWithORCID' } diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index 5c2fd227fd..31ad9b2463 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -3,6 +3,7 @@ {{'person.page.titleprefix' | translate}}
+
diff --git a/src/app/item-page/item-page-routing-paths.ts b/src/app/item-page/item-page-routing-paths.ts index 74ad0aae07..9da2f91431 100644 --- a/src/app/item-page/item-page-routing-paths.ts +++ b/src/app/item-page/item-page-routing-paths.ts @@ -50,3 +50,4 @@ export const ITEM_EDIT_PATH = 'edit'; export const ITEM_EDIT_VERSIONHISTORY_PATH = 'versionhistory'; export const ITEM_VERSION_PATH = 'version'; export const UPLOAD_BITSTREAM_PATH = 'bitstreams/new'; +export const ORCID_PATH = 'orcid'; \ No newline at end of file diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 7d7912bb42..011d7bd83d 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -7,7 +7,7 @@ import { VersionResolver } from './version-page/version.resolver'; import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service'; import { LinkService } from '../core/cache/builders/link.service'; import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component'; -import { ITEM_EDIT_PATH, UPLOAD_BITSTREAM_PATH } from './item-page-routing-paths'; +import { ITEM_EDIT_PATH, ORCID_PATH, UPLOAD_BITSTREAM_PATH } from './item-page-routing-paths'; import { ItemPageAdministratorGuard } from './item-page-administrator.guard'; import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; @@ -16,6 +16,8 @@ import { ThemedFullItemPageComponent } from './full/themed-full-item-page.compon import { VersionPageComponent } from './version-page/version-page/version-page.component'; import { BitstreamRequestACopyPageComponent } from '../shared/bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; +import { OrcidPageComponent } from './orcid-page/orcid-page.component'; +import { OrcidPageGuard } from './orcid-page/orcid-page.guard'; @NgModule({ imports: [ @@ -50,6 +52,11 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; { path: REQUEST_COPY_MODULE_PATH, component: BitstreamRequestACopyPageComponent, + }, + { + path: ORCID_PATH, + component: OrcidPageComponent, + canActivate: [OrcidPageGuard] } ], data: { @@ -88,6 +95,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; LinkService, ItemPageAdministratorGuard, VersionResolver, + OrcidPageGuard ] }) diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index 80cb1f61a2..f584164c97 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -34,6 +34,9 @@ import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.componen import { VersionPageComponent } from './version-page/version-page/version-page.component'; import { VersionedItemComponent } from './simple/item-types/versioned-item/versioned-item.component'; import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component'; +import { OrcidAuthComponent } from './orcid-page/orcid-auth/orcid-auth.component'; +import { OrcidPageComponent } from './orcid-page/orcid-page.component'; +import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; const ENTRY_COMPONENTS = [ @@ -67,6 +70,8 @@ const DECLARATIONS = [ MediaViewerImageComponent, MiradorViewerComponent, VersionPageComponent, + OrcidPageComponent, + OrcidAuthComponent ]; @NgModule({ @@ -79,6 +84,7 @@ const DECLARATIONS = [ JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(), NgxGalleryModule, + NgbAccordionModule ], declarations: [ ...DECLARATIONS, diff --git a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html new file mode 100644 index 0000000000..8e538b66aa --- /dev/null +++ b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.html @@ -0,0 +1,71 @@ +
+ + + +
+ +
+
+
+
+ + +
+
+
{{ 'person.page.orcid.granted-authorizations'| translate }}
+
+
+
    +
  • {{getAuthorizationDescription(auth) | translate}}
  • +
+
+
+
+
+
{{ 'person.page.orcid.missing-authorizations'| translate }}
+
+
+
+ {{'person.page.orcid.no-missing-authorizations-message' | translate}} +
+
+ {{'person.page.orcid.missing-authorizations-message' | translate}} +
    +
  • {{getAuthorizationDescription(auth) | translate }}
  • +
+
+
+
+
+
+
+ {{ 'person.page.orcid.remove-orcid-message' | translate}} +
+
+
+ + +
+
+
+ + +
+
orcid-logo
+
{{ getOrcidNotLinkedMessage() | async }}
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.scss b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts new file mode 100644 index 0000000000..9651215dc6 --- /dev/null +++ b/src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts @@ -0,0 +1,98 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { RemoteData } from '../../../core/data/remote-data'; +import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service'; +import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service'; +import { Item } from '../../../core/shared/item.model'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; + +@Component({ + selector: 'ds-orcid-auth', + templateUrl: './orcid-auth.component.html', + styleUrls: ['./orcid-auth.component.scss'] +}) +export class OrcidAuthComponent implements OnInit { + + missingAuthorizations$ = new BehaviorSubject([]); + + unlinkProcessing = false; + + item: Item + + constructor( + private configurationService: ConfigurationDataService, + private researcherProfileService: ResearcherProfileService, + protected translateService: TranslateService, + private notificationsService: NotificationsService, + private itemService: ItemDataService, + private route: ActivatedRoute, + @Inject(NativeWindowService) private _window: NativeWindowRef, + ) { + this.itemService.findById(this.route.snapshot.paramMap.get('id'), true, true).pipe(getFirstCompletedRemoteData()).subscribe((data: RemoteData) => { + this.item = data.payload; + }); + } + + ngOnInit() { + const scopes = this.getOrcidAuthorizations(); + return this.configurationService.findByPropertyName('orcid.scope') + .pipe(getFirstSucceededRemoteDataPayload(), + map((configurationProperty) => configurationProperty.values), + map((allScopes) => allScopes.filter((scope) => !scopes.includes(scope)))) + .subscribe((missingScopes) => this.missingAuthorizations$.next(missingScopes)); + } + + getOrcidAuthorizations(): string[] { + return this.item.allMetadataValues('cris.orcid.scope'); + } + + isLinkedToOrcid(): boolean { + return this.researcherProfileService.isLinkedToOrcid(this.item); + } + + getOrcidNotLinkedMessage(): Observable { + const orcid = this.item.firstMetadataValue('person.identifier.orcid'); + if (orcid) { + return this.translateService.get('person.page.orcid.orcid-not-linked-message', { 'orcid': orcid }); + } else { + return this.translateService.get('person.page.orcid.no-orcid-message'); + } + } + + getAuthorizationDescription(scope: string) { + return 'person.page.orcid.scope.' + scope.substring(1).replace('/', '-'); + } + + onlyAdminCanDisconnectProfileFromOrcid(): Observable { + return this.researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid(); + } + + ownerCanDisconnectProfileFromOrcid(): Observable { + return this.researcherProfileService.ownerCanDisconnectProfileFromOrcid(); + } + + linkOrcid(): void { + this.researcherProfileService.getOrcidAuthorizeUrl(this.item).subscribe((authorizeUrl) => { + this._window.nativeWindow.location.href = authorizeUrl; + }); + } + + unlinkOrcid(): void { + this.unlinkProcessing = true; + this.researcherProfileService.unlinkOrcid(this.item).subscribe((remoteData) => { + this.unlinkProcessing = false; + if (remoteData.isSuccess) { + this.notificationsService.success(this.translateService.get('person.page.orcid.unlink.success')); + } else { + this.notificationsService.error(this.translateService.get('person.page.orcid.unlink.error')); + } + }); + } + +} diff --git a/src/app/item-page/orcid-page/orcid-page.component.html b/src/app/item-page/orcid-page/orcid-page.component.html new file mode 100644 index 0000000000..ba9f445ec2 --- /dev/null +++ b/src/app/item-page/orcid-page/orcid-page.component.html @@ -0,0 +1 @@ + diff --git a/src/app/item-page/orcid-page/orcid-page.component.scss b/src/app/item-page/orcid-page/orcid-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/item-page/orcid-page/orcid-page.component.ts b/src/app/item-page/orcid-page/orcid-page.component.ts new file mode 100644 index 0000000000..32b47e069f --- /dev/null +++ b/src/app/item-page/orcid-page/orcid-page.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-orcid-page', + templateUrl: './orcid-page.component.html', + styleUrls: ['./orcid-page.component.scss'] +}) +export class OrcidPageComponent { +} diff --git a/src/app/item-page/orcid-page/orcid-page.guard.ts b/src/app/item-page/orcid-page/orcid-page.guard.ts new file mode 100644 index 0000000000..97c528e9ae --- /dev/null +++ b/src/app/item-page/orcid-page/orcid-page.guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable, of as observableOf } from 'rxjs'; +import { AuthService } from '../../core/auth/auth.service'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { Item } from '../../core/shared/item.model'; +import { ItemPageResolver } from '../item-page.resolver'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Guard for preventing unauthorized access to certain {@link Item} pages requiring administrator rights + */ +export class OrcidPageGuard extends DsoPageSingleFeatureGuard { + constructor(protected resolver: ItemPageResolver, + protected authorizationService: AuthorizationDataService, + protected router: Router, + protected authService: AuthService) { + super(resolver, authorizationService, router, authService); + } + + /** + * Check administrator authorization rights + */ + getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanSynchronizeWithORCID); + } +} diff --git a/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.html b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.html new file mode 100644 index 0000000000..7a3383fd1a --- /dev/null +++ b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.html @@ -0,0 +1,5 @@ +ORCID + diff --git a/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.scss b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.spec.ts b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.spec.ts new file mode 100644 index 0000000000..9de3333b7f --- /dev/null +++ b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.spec.ts @@ -0,0 +1,76 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { Item } from '../../../core/shared/item.model'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { of as observableOf } from 'rxjs'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { By } from '@angular/platform-browser'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { DsoPageOrcidButtonComponent } from './dso-page-orcid-button.component'; + +describe('DsoPageOrcidButtonComponent', () => { + let component: DsoPageOrcidButtonComponent; + let fixture: ComponentFixture; + + let authorizationService: AuthorizationDataService; + let dso: DSpaceObject; + + beforeEach(waitForAsync(() => { + dso = Object.assign(new Item(), { + id: 'test-item', + _links: { + self: { href: 'test-item-selflink' } + } + }); + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + TestBed.configureTestingModule({ + declarations: [DsoPageOrcidButtonComponent], + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule], + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService } + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DsoPageOrcidButtonComponent); + component = fixture.componentInstance; + component.dso = dso; + component.pageRoute = 'test'; + fixture.detectChanges(); + }); + + it('should check the authorization of the current user', () => { + expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanEditOrcid, dso.self); + }); + + describe('when the user is authorized', () => { + beforeEach(() => { + (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true)); + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should render a link', () => { + const link = fixture.debugElement.query(By.css('a')); + expect(link).not.toBeNull(); + }); + }); + + describe('when the user is not authorized', () => { + beforeEach(() => { + (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should not render a link', () => { + const link = fixture.debugElement.query(By.css('a')); + expect(link).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.ts b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.ts new file mode 100644 index 0000000000..9f244da7c9 --- /dev/null +++ b/src/app/shared/dso-page/dso-page-orcid-button/dso-page-orcid-button.component.ts @@ -0,0 +1,43 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; + +@Component({ + selector: 'ds-dso-page-orcid-button', + templateUrl: './dso-page-orcid-button.component.html', + styleUrls: ['./dso-page-orcid-button.component.scss'] +}) +export class DsoPageOrcidButtonComponent implements OnInit { + /** + * The DSpaceObject to display a button to the edit page for + */ + @Input() dso: DSpaceObject; + + /** + * The prefix of the route to the edit page (before the object's UUID, e.g. "items") + */ + @Input() pageRoute: string; + + /** + * A message for the tooltip on the button + * Supports i18n keys + */ + @Input() tooltipMsg: string; + + /** + * Whether or not the current user is authorized to edit the DSpaceObject + */ + isAuthorized: BehaviorSubject = new BehaviorSubject(false); + + constructor(protected authorizationService: AuthorizationDataService) { } + + ngOnInit() { + this.authorizationService.isAuthorized(FeatureID.CanSynchronizeWithORCID, this.dso.self).pipe(take(1)).subscribe((isAuthorized: boolean) => { + this.isAuthorized.next(isAuthorized); + }); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 01649ee947..fcd80683b7 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -174,6 +174,7 @@ import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; import { ClaimItemSelectorComponent } from './dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component'; +import { DsoPageOrcidButtonComponent } from './dso-page/dso-page-orcid-button/dso-page-orcid-button.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -414,6 +415,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ GenericItemPageFieldComponent, MetadataRepresentationListComponent, RelatedItemsComponent, + DsoPageOrcidButtonComponent ]; diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d0ff85ba51..8ef1135910 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4208,4 +4208,195 @@ "researcherprofile.success.claim.body" : "Profile claimed with success", "researcherprofile.success.claim.title" : "Success", + + "person.page.orcid": "ORCID", + + "person.page.orcid.create": "Create an ORCID ID", + + "person.page.orcid.granted-authorizations": "Granted authorizations", + + "person.page.orcid.grant-authorizations" : "Grant authorizations", + + "person.page.orcid.link": "Connect to ORCID ID", + + "person.page.orcid.orcid-not-linked-message": "The ORCID iD of this profile ({{ orcid }}) has not yet been connected to an account on the ORCID registry or the connection is expired.", + + "person.page.orcid.unlink": "Disconnect from ORCID", + + "person.page.orcid.unlink.processing": "Processing...", + + "person.page.orcid.missing-authorizations": "Missing authorizations", + + "person.page.orcid.missing-authorizations-message": "The following authorizations are missing:", + + "person.page.orcid.no-missing-authorizations-message": "Great! This box is empty, so you have granted all access rights to use all functions offers by your institution.", + + "person.page.orcid.no-orcid-message": "No ORCID iD associated yet. By clicking on the button below it is possible to link this profile with an ORCID account.", + + "person.page.orcid.profile-preferences": "Profile preferences", + + "person.page.orcid.funding-preferences": "Funding preferences", + + "person.page.orcid.publications-preferences": "Publication preferences", + + "person.page.orcid.remove-orcid-message": "If you need to remove your ORCID, please contact the repository administrator", + + "person.page.orcid.save.preference.changes": "Update settings", + + "person.page.orcid.sync-profile.affiliation" : "Affiliation", + + "person.page.orcid.sync-profile.biographical" : "Biographical data", + + "person.page.orcid.sync-profile.education" : "Education", + + "person.page.orcid.sync-profile.identifiers" : "Identifiers", + + "person.page.orcid.sync-fundings.all" : "All fundings", + + "person.page.orcid.sync-fundings.mine" : "My fundings", + + "person.page.orcid.sync-fundings.my_selected" : "Selected fundings", + + "person.page.orcid.sync-fundings.disabled" : "Disabled", + + "person.page.orcid.sync-publications.all" : "All publications", + + "person.page.orcid.sync-publications.mine" : "My publications", + + "person.page.orcid.sync-publications.my_selected" : "Selected publications", + + "person.page.orcid.sync-publications.disabled" : "Disabled", + + "person.page.orcid.sync-queue.discard" : "Discard the change and do not synchronize with the ORCID registry", + + "person.page.orcid.sync-queue.discard.error": "The discarding of the ORCID queue record failed", + + "person.page.orcid.sync-queue.discard.success": "The ORCID queue record have been discarded successfully", + + "person.page.orcid.sync-queue.empty-message": "The ORCID queue registry is empty", + + "person.page.orcid.sync-queue.description" : "Description", + + "person.page.orcid.sync-queue.description.affiliation": "Affiliations", + + "person.page.orcid.sync-queue.description.country": "Country", + + "person.page.orcid.sync-queue.description.education": "Educations", + + "person.page.orcid.sync-queue.description.external_ids": "External ids", + + "person.page.orcid.sync-queue.description.other_names": "Other names", + + "person.page.orcid.sync-queue.description.qualification": "Qualifications", + + "person.page.orcid.sync-queue.description.researcher_urls": "Researcher urls", + + "person.page.orcid.sync-queue.description.keywords": "Keywords", + + "person.page.orcid.sync-queue.tooltip.insert": "Add a new entry in the ORCID registry", + + "person.page.orcid.sync-queue.tooltip.update": "Update this entry on the ORCID registry", + + "person.page.orcid.sync-queue.tooltip.delete": "Remove this entry from the ORCID registry", + + "person.page.orcid.sync-queue.tooltip.publication": "Publication", + + "person.page.orcid.sync-queue.tooltip.funding": "Funding", + + "person.page.orcid.sync-queue.tooltip.affiliation": "Affiliation", + + "person.page.orcid.sync-queue.tooltip.education": "Education", + + "person.page.orcid.sync-queue.tooltip.qualification": "Qualification", + + "person.page.orcid.sync-queue.tooltip.other_names": "Other name", + + "person.page.orcid.sync-queue.tooltip.country": "Country", + + "person.page.orcid.sync-queue.tooltip.keywords": "Keyword", + + "person.page.orcid.sync-queue.tooltip.external_ids": "External identifier", + + "person.page.orcid.sync-queue.tooltip.researcher_urls": "Researcher url", + + "person.page.orcid.sync-queue.send" : "Synchronize with ORCID registry", + + "person.page.orcid.sync-queue.send.unauthorized-error.title": "The submission to ORCID failed for missing authorizations.", + + "person.page.orcid.sync-queue.send.unauthorized-error.content": "Click here to grant again the required permissions. If the problem persists, contact the administrator", + + "person.page.orcid.sync-queue.send.bad-request-error": "The submission to ORCID failed because the resource sent to ORCID registry is not valid", + + "person.page.orcid.sync-queue.send.error": "The submission to ORCID failed", + + "person.page.orcid.sync-queue.send.conflict-error": "The submission to ORCID failed because the resource is already present on the ORCID registry", + + "person.page.orcid.sync-queue.send.not-found-warning": "The resource does not exists anymore on the ORCID registry.", + + "person.page.orcid.sync-queue.send.success": "The submission to ORCID was completed successfully", + + "person.page.orcid.sync-queue.send.validation-error": "The data that you want to synchronize with ORCID is not valid", + + "person.page.orcid.sync-queue.send.validation-error.amount-currency.required": "The amount's currency is required", + + "person.page.orcid.sync-queue.send.validation-error.external-id.required": "The resource to be sent requires at least one identifier", + + "person.page.orcid.sync-queue.send.validation-error.title.required": "The title is required", + + "person.page.orcid.sync-queue.send.validation-error.type.required": "The type is required", + + "person.page.orcid.sync-queue.send.validation-error.start-date.required": "The start date is required", + + "person.page.orcid.sync-queue.send.validation-error.funder.required": "The funder is required", + + "person.page.orcid.sync-queue.send.validation-error.organization.required": "The organization is required", + + "person.page.orcid.sync-queue.send.validation-error.organization.name-required": "The organization's name is required", + + "person.page.orcid.sync-queue.send.validation-error.organization.address-required": "The organization to be sent requires an address", + + "person.page.orcid.sync-queue.send.validation-error.organization.city-required": "The address of the organization to be sent requires a city", + + "person.page.orcid.sync-queue.send.validation-error.organization.country-required": "The address of the organization to be sent requires a country", + + "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.required": "An identifier to disambiguate organizations is required. Supported ids are GRID, Ringgold, Legal Entity identifiers (LEIs) and Crossref Funder Registry identifiers", + + "person.page.orcid.sync-queue.send.validation-error.disambiguated-organization.value-required": "The organization's identifiers requires a value", + + "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.required": "The organization's identifiers requires a source", + + "person.page.orcid.sync-queue.send.validation-error.disambiguation-source.invalid": "The source of one of the organization identifiers is invalid. Supported sources are RINGGOLD, GRID, LEI and FUNDREF", + + "person.page.orcid.synchronization-mode": "Synchronization mode", + + "person.page.orcid.synchronization-mode.batch": "Batch", + + "person.page.orcid.synchronization-mode.label": "Synchronization mode", + + "person.page.orcid.synchronization-mode-message": "Enable 'Manual' Synchronization mode to disable batch synchronization, so you must send your data to ORCID Registry manually", + + "person.page.orcid.synchronization-settings-update.success": "The synchronization settings have been updated successfully", + + "person.page.orcid.synchronization-settings-update.error": "The update of the synchronization settings failed", + + "person.page.orcid.synchronization-mode.manual": "Manual", + + "person.page.orcid.scope.authenticate": "Get your ORCID iD", + + "person.page.orcid.scope.read-limited": "Read your information with visibility set to Trusted Parties", + + "person.page.orcid.scope.activities-update": "Add/update your research activities", + + "person.page.orcid.scope.person-update": "Add/update other information about you", + + "person.page.orcid.unlink.success": "The disconnection between the profile and the ORCID registry was successful", + + "person.page.orcid.unlink.error": "An error occurred while disconnecting between the profile and the ORCID registry. Try again", + + "person.orcid.sync.setting": "ORCID Synchronization settings", + + "person.orcid.registry.queue": "ORCID Registry Queue", + + "person.orcid.registry.auth": "ORCID Authorizations", + } diff --git a/src/assets/images/orcid.logo.icon.svg b/src/assets/images/orcid.logo.icon.svg new file mode 100644 index 0000000000..8aec5959e5 --- /dev/null +++ b/src/assets/images/orcid.logo.icon.svg @@ -0,0 +1,21 @@ + + + + Orcid logo + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/styles/_global-styles.scss b/src/styles/_global-styles.scss index cf251204e2..7d9550c0fd 100644 --- a/src/styles/_global-styles.scss +++ b/src/styles/_global-styles.scss @@ -97,4 +97,15 @@ ngb-modal-backdrop { } .researcher-profile-switch .switch.checked{ color: #fff; +} + +.custom-accordion .card-header button { + -webkit-box-shadow: none!important; + box-shadow: none!important; + width: 100%; +} +.custom-accordion .card:first-of-type { + border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color)!important; + border-bottom-left-radius: var(--bs-card-border-radius)!important; + border-bottom-right-radius: var(--bs-card-border-radius)!important; } \ No newline at end of file