diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts index e55d0c0ff9..672879f436 100644 --- a/src/app/core/auth/auth.interceptor.ts +++ b/src/app/core/auth/auth.interceptor.ts @@ -196,7 +196,24 @@ export class AuthInterceptor implements HttpInterceptor { authStatus.token = new AuthTokenInfo(accessToken); } else { authStatus.authenticated = false; - authStatus.error = isNotEmpty(error) ? ((typeof error === 'string') ? JSON.parse(error) : error) : null; + if (isNotEmpty(error)) { + if (typeof error === 'string') { + try { + authStatus.error = JSON.parse(error); + } catch (e) { + console.error('Unknown auth error "', error, '" caused ', e); + authStatus.error = { + error: 'Unknown', + message: 'Unknown auth error', + status: 500, + timestamp: Date.now(), + path: '' + }; + } + } else { + authStatus.error = error; + } + } } return authStatus; } diff --git a/src/app/core/breadcrumbs/dso-name.service.ts b/src/app/core/breadcrumbs/dso-name.service.ts index d56f4a00eb..64f37baa65 100644 --- a/src/app/core/breadcrumbs/dso-name.service.ts +++ b/src/app/core/breadcrumbs/dso-name.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { hasValue, isEmpty } from '../../shared/empty.util'; import { DSpaceObject } from '../shared/dspace-object.model'; import { TranslateService } from '@ngx-translate/core'; +import { Metadata } from '../shared/metadata.utils'; /** * Returns a name for a {@link DSpaceObject} based @@ -67,4 +68,45 @@ export class DSONameService { return name; } + /** + * Gets the Hit highlight + * + * @param object + * @param dso + * + * @returns {string} html embedded hit highlight. + */ + getHitHighlights(object: any, dso: DSpaceObject): string { + const types = dso.getRenderTypes(); + const entityType = types + .filter((type) => typeof type === 'string') + .find((type: string) => (['Person', 'OrgUnit']).includes(type)) as string; + if (entityType === 'Person') { + const familyName = this.firstMetadataValue(object, dso, 'person.familyName'); + const givenName = this.firstMetadataValue(object, dso, 'person.givenName'); + if (isEmpty(familyName) && isEmpty(givenName)) { + return this.firstMetadataValue(object, dso, 'dc.title') || dso.name; + } else if (isEmpty(familyName) || isEmpty(givenName)) { + return familyName || givenName; + } + return `${familyName}, ${givenName}`; + } else if (entityType === 'OrgUnit') { + return this.firstMetadataValue(object, dso, 'organization.legalName'); + } + return this.firstMetadataValue(object, dso, 'dc.title') || dso.name || this.translateService.instant('dso.name.untitled'); + } + + /** + * Gets the first matching metadata string value from hitHighlights or dso metadata, preferring hitHighlights. + * + * @param object + * @param dso + * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]]. + * + * @returns {string} the first matching string value, or `undefined`. + */ + firstMetadataValue(object: any, dso: DSpaceObject, keyOrKeys: string | string[]): string { + return Metadata.firstValue([object.hitHighlights, dso.metadata], keyOrKeys); + } + } diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts index 7a38a02cf4..954f7bc591 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts @@ -61,7 +61,7 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes this.useNameVariants = this.context === Context.EntitySearchModalWithNameVariants; if (this.useNameVariants) { - const defaultValue = this.dsoTitle; + const defaultValue = this.dso ? this.dsoNameService.getName(this.dso) : undefined; const alternatives = this.allMetadataValues(this.alternativeField); this.allSuggestions = [defaultValue, ...alternatives]; diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts index 7d761c42dd..305407f8d2 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts @@ -55,7 +55,7 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu ngOnInit() { super.ngOnInit(); - const defaultValue = this.dsoTitle; + const defaultValue = this.dso ? this.dsoNameService.getName(this.dso) : undefined; const alternatives = this.allMetadataValues(this.alternativeField); this.allSuggestions = [defaultValue, ...alternatives]; diff --git a/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html new file mode 100644 index 0000000000..6046aec725 --- /dev/null +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.html @@ -0,0 +1,3 @@ + diff --git a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.scss b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.scss similarity index 100% rename from src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.scss rename to src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.scss diff --git a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.spec.ts b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts similarity index 88% rename from src/app/shared/log-in/methods/orcid/log-in-orcid.component.spec.ts rename to src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts index 001f0a4959..de4f62eb9e 100644 --- a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.spec.ts +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts @@ -14,18 +14,17 @@ import { AuthServiceStub } from '../../../testing/auth-service.stub'; import { storeModuleConfig } from '../../../../app.reducer'; import { AuthMethod } from '../../../../core/auth/models/auth.method'; import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInOrcidComponent } from './log-in-orcid.component'; +import { LogInExternalProviderComponent } from './log-in-external-provider.component'; import { NativeWindowService } from '../../../../core/services/window.service'; import { RouterStub } from '../../../testing/router.stub'; import { ActivatedRouteStub } from '../../../testing/active-router.stub'; import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref'; import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +describe('LogInExternalProviderComponent', () => { -describe('LogInOrcidComponent', () => { - - let component: LogInOrcidComponent; - let fixture: ComponentFixture; + let component: LogInExternalProviderComponent; + let fixture: ComponentFixture; let page: Page; let user: EPerson; let componentAsAny: any; @@ -66,7 +65,7 @@ describe('LogInOrcidComponent', () => { TranslateModule.forRoot() ], declarations: [ - LogInOrcidComponent + LogInExternalProviderComponent ], providers: [ { provide: AuthService, useClass: AuthServiceStub }, @@ -88,7 +87,7 @@ describe('LogInOrcidComponent', () => { beforeEach(() => { // create component and test fixture - fixture = TestBed.createComponent(LogInOrcidComponent); + fixture = TestBed.createComponent(LogInExternalProviderComponent); // get test component from the fixture component = fixture.componentInstance; @@ -109,7 +108,7 @@ describe('LogInOrcidComponent', () => { expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - component.redirectToOrcid(); + component.redirectToExternalProvider(); expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); @@ -124,7 +123,7 @@ describe('LogInOrcidComponent', () => { expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - component.redirectToOrcid(); + component.redirectToExternalProvider(); expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); @@ -143,7 +142,7 @@ class Page { public navigateSpy: jasmine.Spy; public passwordInput: HTMLInputElement; - constructor(private component: LogInOrcidComponent, private fixture: ComponentFixture) { + constructor(private component: LogInExternalProviderComponent, private fixture: ComponentFixture) { // use injector to get services const injector = fixture.debugElement.injector; const store = injector.get(Store); diff --git a/src/app/shared/log-in/methods/log-in-external-provider.component.ts b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts similarity index 73% rename from src/app/shared/log-in/methods/log-in-external-provider.component.ts rename to src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts index 037fc40e90..f182968457 100644 --- a/src/app/shared/log-in/methods/log-in-external-provider.component.ts +++ b/src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts @@ -4,22 +4,27 @@ import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { select, Store } from '@ngrx/store'; -import { AuthMethod } from '../../../core/auth/models/auth.method'; +import { AuthMethod } from '../../../../core/auth/models/auth.method'; -import { isAuthenticated, isAuthenticationLoading } from '../../../core/auth/selectors'; -import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service'; -import { isEmpty, isNotNull } from '../../empty.util'; -import { AuthService } from '../../../core/auth/auth.service'; -import { HardRedirectService } from '../../../core/services/hard-redirect.service'; -import { URLCombiner } from '../../../core/url-combiner/url-combiner'; -import { CoreState } from '../../../core/core-state.model'; +import { isAuthenticated, isAuthenticationLoading } from '../../../../core/auth/selectors'; +import { NativeWindowRef, NativeWindowService } from '../../../../core/services/window.service'; +import { isEmpty, isNotNull } from '../../../empty.util'; +import { AuthService } from '../../../../core/auth/auth.service'; +import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; +import { URLCombiner } from '../../../../core/url-combiner/url-combiner'; +import { CoreState } from '../../../../core/core-state.model'; +import { renderAuthMethodFor } from '../log-in.methods-decorator'; +import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; @Component({ selector: 'ds-log-in-external-provider', - template: '' - + templateUrl: './log-in-external-provider.component.html', + styleUrls: ['./log-in-external-provider.component.scss'] }) -export abstract class LogInExternalProviderComponent implements OnInit { +@renderAuthMethodFor(AuthMethodType.Oidc) +@renderAuthMethodFor(AuthMethodType.Shibboleth) +@renderAuthMethodFor(AuthMethodType.Orcid) +export class LogInExternalProviderComponent implements OnInit { /** * The authentication method data. @@ -107,4 +112,7 @@ export abstract class LogInExternalProviderComponent implements OnInit { } + getButtonLabel() { + return `login.form.${this.authMethod.authMethodType}`; + } } diff --git a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.html b/src/app/shared/log-in/methods/oidc/log-in-oidc.component.html deleted file mode 100644 index 7e78834305..0000000000 --- a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.spec.ts b/src/app/shared/log-in/methods/oidc/log-in-oidc.component.spec.ts deleted file mode 100644 index 078a58dd5a..0000000000 --- a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.spec.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { provideMockStore } from '@ngrx/store/testing'; -import { Store, StoreModule } from '@ngrx/store'; -import { TranslateModule } from '@ngx-translate/core'; - -import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { EPersonMock } from '../../../testing/eperson.mock'; -import { authReducer } from '../../../../core/auth/auth.reducer'; -import { AuthService } from '../../../../core/auth/auth.service'; -import { AuthServiceStub } from '../../../testing/auth-service.stub'; -import { storeModuleConfig } from '../../../../app.reducer'; -import { AuthMethod } from '../../../../core/auth/models/auth.method'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInOidcComponent } from './log-in-oidc.component'; -import { NativeWindowService } from '../../../../core/services/window.service'; -import { RouterStub } from '../../../testing/router.stub'; -import { ActivatedRouteStub } from '../../../testing/active-router.stub'; -import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref'; -import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; - - -describe('LogInOidcComponent', () => { - - let component: LogInOidcComponent; - let fixture: ComponentFixture; - let page: Page; - let user: EPerson; - let componentAsAny: any; - let setHrefSpy; - let oidcBaseUrl; - let location; - let initialState: any; - let hardRedirectService: HardRedirectService; - - beforeEach(() => { - user = EPersonMock; - oidcBaseUrl = 'dspace-rest.test/oidc?redirectUrl='; - location = oidcBaseUrl + 'http://dspace-angular.test/home'; - - hardRedirectService = jasmine.createSpyObj('hardRedirectService', { - getCurrentRoute: {}, - redirect: {} - }); - - initialState = { - core: { - auth: { - authenticated: false, - loaded: false, - blocking: false, - loading: false, - authMethods: [] - } - } - }; - }); - - beforeEach(waitForAsync(() => { - // refine the test module by declaring the test component - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ auth: authReducer }, storeModuleConfig), - TranslateModule.forRoot() - ], - declarations: [ - LogInOidcComponent - ], - providers: [ - { provide: AuthService, useClass: AuthServiceStub }, - { provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Oidc, location) }, - { provide: 'isStandalonePage', useValue: true }, - { provide: NativeWindowService, useFactory: NativeWindowMockFactory }, - { provide: Router, useValue: new RouterStub() }, - { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, - { provide: HardRedirectService, useValue: hardRedirectService }, - provideMockStore({ initialState }), - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ] - }) - .compileComponents(); - - })); - - beforeEach(() => { - // create component and test fixture - fixture = TestBed.createComponent(LogInOidcComponent); - - // get test component from the fixture - component = fixture.componentInstance; - componentAsAny = component; - - // create page - page = new Page(component, fixture); - setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough(); - - }); - - it('should set the properly a new redirectUrl', () => { - const currentUrl = 'http://dspace-angular.test/collections/12345'; - componentAsAny._window.nativeWindow.location.href = currentUrl; - - fixture.detectChanges(); - - expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); - expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - - component.redirectToOidc(); - - expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); - - }); - - it('should not set a new redirectUrl', () => { - const currentUrl = 'http://dspace-angular.test/home'; - componentAsAny._window.nativeWindow.location.href = currentUrl; - - fixture.detectChanges(); - - expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); - expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - - component.redirectToOidc(); - - expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); - - }); - -}); - -/** - * I represent the DOM elements and attach spies. - * - * @class Page - */ -class Page { - - public emailInput: HTMLInputElement; - public navigateSpy: jasmine.Spy; - public passwordInput: HTMLInputElement; - - constructor(private component: LogInOidcComponent, private fixture: ComponentFixture) { - // use injector to get services - const injector = fixture.debugElement.injector; - const store = injector.get(Store); - - // add spies - this.navigateSpy = spyOn(store, 'dispatch'); - } - -} diff --git a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.ts b/src/app/shared/log-in/methods/oidc/log-in-oidc.component.ts deleted file mode 100644 index 882996b207..0000000000 --- a/src/app/shared/log-in/methods/oidc/log-in-oidc.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, } from '@angular/core'; - -import { renderAuthMethodFor } from '../log-in.methods-decorator'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInExternalProviderComponent } from '../log-in-external-provider.component'; - -@Component({ - selector: 'ds-log-in-oidc', - templateUrl: './log-in-oidc.component.html', -}) -@renderAuthMethodFor(AuthMethodType.Oidc) -export class LogInOidcComponent extends LogInExternalProviderComponent { - - /** - * Redirect to orcid authentication url - */ - redirectToOidc() { - this.redirectToExternalProvider(); - } - -} diff --git a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.html b/src/app/shared/log-in/methods/orcid/log-in-orcid.component.html deleted file mode 100644 index 6f5453fd60..0000000000 --- a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.ts b/src/app/shared/log-in/methods/orcid/log-in-orcid.component.ts deleted file mode 100644 index e0b1da3db5..0000000000 --- a/src/app/shared/log-in/methods/orcid/log-in-orcid.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, } from '@angular/core'; - -import { renderAuthMethodFor } from '../log-in.methods-decorator'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInExternalProviderComponent } from '../log-in-external-provider.component'; - -@Component({ - selector: 'ds-log-in-orcid', - templateUrl: './log-in-orcid.component.html', -}) -@renderAuthMethodFor(AuthMethodType.Orcid) -export class LogInOrcidComponent extends LogInExternalProviderComponent { - - /** - * Redirect to orcid authentication url - */ - redirectToOrcid() { - this.redirectToExternalProvider(); - } - -} diff --git a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.html b/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.html deleted file mode 100644 index 3a3b935cfa..0000000000 --- a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.spec.ts b/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.spec.ts deleted file mode 100644 index 075d33d98e..0000000000 --- a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.spec.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { provideMockStore } from '@ngrx/store/testing'; -import { Store, StoreModule } from '@ngrx/store'; -import { TranslateModule } from '@ngx-translate/core'; - -import { EPerson } from '../../../../core/eperson/models/eperson.model'; -import { EPersonMock } from '../../../testing/eperson.mock'; -import { authReducer } from '../../../../core/auth/auth.reducer'; -import { AuthService } from '../../../../core/auth/auth.service'; -import { AuthServiceStub } from '../../../testing/auth-service.stub'; -import { storeModuleConfig } from '../../../../app.reducer'; -import { AuthMethod } from '../../../../core/auth/models/auth.method'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInShibbolethComponent } from './log-in-shibboleth.component'; -import { NativeWindowService } from '../../../../core/services/window.service'; -import { RouterStub } from '../../../testing/router.stub'; -import { ActivatedRouteStub } from '../../../testing/active-router.stub'; -import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref'; -import { HardRedirectService } from '../../../../core/services/hard-redirect.service'; - - -describe('LogInShibbolethComponent', () => { - - let component: LogInShibbolethComponent; - let fixture: ComponentFixture; - let page: Page; - let user: EPerson; - let componentAsAny: any; - let setHrefSpy; - let shibbolethBaseUrl; - let location; - let initialState: any; - let hardRedirectService: HardRedirectService; - - beforeEach(() => { - user = EPersonMock; - shibbolethBaseUrl = 'dspace-rest.test/shibboleth?redirectUrl='; - location = shibbolethBaseUrl + 'http://dspace-angular.test/home'; - - hardRedirectService = jasmine.createSpyObj('hardRedirectService', { - getCurrentRoute: {}, - redirect: {} - }); - - initialState = { - core: { - auth: { - authenticated: false, - loaded: false, - blocking: false, - loading: false, - authMethods: [] - } - } - }; - }); - - beforeEach(waitForAsync(() => { - // refine the test module by declaring the test component - TestBed.configureTestingModule({ - imports: [ - StoreModule.forRoot({ auth: authReducer }, storeModuleConfig), - TranslateModule.forRoot() - ], - declarations: [ - LogInShibbolethComponent - ], - providers: [ - { provide: AuthService, useClass: AuthServiceStub }, - { provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Shibboleth, location) }, - { provide: 'isStandalonePage', useValue: true }, - { provide: NativeWindowService, useFactory: NativeWindowMockFactory }, - { provide: Router, useValue: new RouterStub() }, - { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, - { provide: HardRedirectService, useValue: hardRedirectService }, - provideMockStore({ initialState }), - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ] - }) - .compileComponents(); - - })); - - beforeEach(() => { - // create component and test fixture - fixture = TestBed.createComponent(LogInShibbolethComponent); - - // get test component from the fixture - component = fixture.componentInstance; - componentAsAny = component; - - // create page - page = new Page(component, fixture); - setHrefSpy = spyOnProperty(componentAsAny._window.nativeWindow.location, 'href', 'set').and.callThrough(); - - }); - - it('should set the properly a new redirectUrl', () => { - const currentUrl = 'http://dspace-angular.test/collections/12345'; - componentAsAny._window.nativeWindow.location.href = currentUrl; - - fixture.detectChanges(); - - expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); - expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - - component.redirectToShibboleth(); - - expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); - - }); - - it('should not set a new redirectUrl', () => { - const currentUrl = 'http://dspace-angular.test/home'; - componentAsAny._window.nativeWindow.location.href = currentUrl; - - fixture.detectChanges(); - - expect(componentAsAny.injectedAuthMethodModel.location).toBe(location); - expect(componentAsAny._window.nativeWindow.location.href).toBe(currentUrl); - - component.redirectToShibboleth(); - - expect(setHrefSpy).toHaveBeenCalledWith(currentUrl); - - }); - -}); - -/** - * I represent the DOM elements and attach spies. - * - * @class Page - */ -class Page { - - public emailInput: HTMLInputElement; - public navigateSpy: jasmine.Spy; - public passwordInput: HTMLInputElement; - - constructor(private component: LogInShibbolethComponent, private fixture: ComponentFixture) { - // use injector to get services - const injector = fixture.debugElement.injector; - const store = injector.get(Store); - - // add spies - this.navigateSpy = spyOn(store, 'dispatch'); - } - -} diff --git a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts b/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts deleted file mode 100644 index dcfb3ccfc3..0000000000 --- a/src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Component, } from '@angular/core'; - -import { renderAuthMethodFor } from '../log-in.methods-decorator'; -import { AuthMethodType } from '../../../../core/auth/models/auth.method-type'; -import { LogInExternalProviderComponent } from '../log-in-external-provider.component'; - -@Component({ - selector: 'ds-log-in-shibboleth', - templateUrl: './log-in-shibboleth.component.html', - styleUrls: ['./log-in-shibboleth.component.scss'], - -}) -@renderAuthMethodFor(AuthMethodType.Shibboleth) -export class LogInShibbolethComponent extends LogInExternalProviderComponent { - - /** - * Redirect to shibboleth authentication url - */ - redirectToShibboleth() { - this.redirectToExternalProvider(); - } - -} diff --git a/src/app/shared/mocks/dso-name.service.mock.ts b/src/app/shared/mocks/dso-name.service.mock.ts index f4947cc860..cf3cf5466b 100644 --- a/src/app/shared/mocks/dso-name.service.mock.ts +++ b/src/app/shared/mocks/dso-name.service.mock.ts @@ -6,4 +6,23 @@ export class DSONameServiceMock { public getName(dso: DSpaceObject) { return UNDEFINED_NAME; } + + public getHitHighlights(object: any, dso: DSpaceObject) { + if (object.hitHighlights && object.hitHighlights['dc.title']) { + return object.hitHighlights['dc.title'][0].value; + } else if (object.hitHighlights && object.hitHighlights['organization.legalName']) { + return object.hitHighlights['organization.legalName'][0].value; + } else if (object.hitHighlights && (object.hitHighlights['person.familyName'] || object.hitHighlights['person.givenName'])) { + if (object.hitHighlights['person.familyName'] && object.hitHighlights['person.givenName']) { + return `${object.hitHighlights['person.familyName'][0].value}, ${object.hitHighlights['person.givenName'][0].value}`; + } + if (object.hitHighlights['person.familyName']) { + return `${object.hitHighlights['person.familyName'][0].value}`; + } + if (object.hitHighlights['person.givenName']) { + return `${object.hitHighlights['person.givenName'][0].value}`; + } + } + return UNDEFINED_NAME; + } } diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts index 57b863a1b1..dc42b033d8 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts @@ -28,13 +28,19 @@ import { ItemSearchResultGridElementComponent } from './item-search-result-grid- const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithMetadata.hitHighlights = {}; +const dcTitle = 'This is just another title'; mockItemWithMetadata.indexableObject = Object.assign(new Item(), { + hitHighlights: { + 'dc.title': [{ + value: dcTitle + }], + }, bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), metadata: { 'dc.title': [ { language: 'en_US', - value: 'This is just another title' + value: dcTitle } ], 'dc.contributor.author': [ @@ -57,6 +63,114 @@ mockItemWithMetadata.indexableObject = Object.assign(new Item(), { ] } }); +const mockPerson: ItemSearchResult = Object.assign(new ItemSearchResult(), { + hitHighlights: { + 'person.familyName': [{ + value: 'Michel' + }], + }, + indexableObject: + Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + entityType: 'Person', + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'dc.date.issued': [ + { + language: 'en_US', + value: '2015-06-26' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is the abstract' + } + ], + 'dspace.entity.type': [ + { + value: 'Person' + } + ], + 'person.familyName': [ + { + value: 'Michel' + } + ] + } + }) +}); +const mockOrgUnit: ItemSearchResult = Object.assign(new ItemSearchResult(), { + hitHighlights: { + 'organization.legalName': [{ + value: 'Science' + }], + }, + indexableObject: + Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + entityType: 'OrgUnit', + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'dc.date.issued': [ + { + language: 'en_US', + value: '2015-06-26' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is the abstract' + } + ], + 'organization.legalName': [ + { + value: 'Science' + } + ], + 'dspace.entity.type': [ + { + value: 'OrgUnit' + } + ] + } + }) +}); const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult(); mockItemWithoutMetadata.hitHighlights = {}; @@ -154,6 +268,41 @@ export function getEntityGridElementTestComponent(component, searchResultWithMet expect(itemAuthorField).toBeNull(); }); }); + + describe('When the item has title', () => { + beforeEach(() => { + comp.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.card-title')); + expect(titleField.nativeNode.innerHTML).toEqual(dcTitle); + }); + }); + + describe('When the item is Person and has title', () => { + beforeEach(() => { + comp.object = mockPerson; + fixture.detectChanges(); + }); + + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.card-title')); + expect(titleField.nativeNode.innerHTML).toEqual('Michel'); + }); + }); + + describe('When the item is orgUnit and has title', () => { + beforeEach(() => { + comp.object = mockOrgUnit; + fixture.detectChanges(); + }); + + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.card-title')); + expect(titleField.nativeNode.innerHTML).toEqual('Science'); + }); + }); }); }; } diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts index b5f9c016e4..303e4681a2 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts @@ -42,6 +42,6 @@ export class ItemSearchResultGridElementComponent extends SearchResultGridElemen ngOnInit(): void { super.ngOnInit(); this.itemPageRoute = getItemPageRoute(this.dso); - this.dsoTitle = this.dsoNameService.getName(this.dso); + this.dsoTitle = this.dsoNameService.getHitHighlights(this.object, this.dso); } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 04f1e24d7b..6b40678ded 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -55,7 +55,7 @@ export class ItemListPreviewComponent implements OnInit { ngOnInit(): void { this.showThumbnails = this.appConfig.browseBy.showThumbnails; - this.dsoTitle = this.dsoNameService.getName(this.item); + this.dsoTitle = this.dsoNameService.getHitHighlights(this.object, this.item); } diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts index d1e6c27ba4..7665b7d64e 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts @@ -13,8 +13,13 @@ import { APP_CONFIG } from '../../../../../../../config/app-config.interface'; let publicationListElementComponent: ItemSearchResultListElementComponent; let fixture: ComponentFixture; - +const dcTitle = 'This is just another title'; const mockItemWithMetadata: ItemSearchResult = Object.assign(new ItemSearchResult(), { + hitHighlights: { + 'dc.title': [{ + value: dcTitle + }], + }, indexableObject: Object.assign(new Item(), { bundles: observableOf({}), @@ -22,7 +27,7 @@ const mockItemWithMetadata: ItemSearchResult = Object.assign(new ItemSearchResul 'dc.title': [ { language: 'en_US', - value: 'This is just another title' + value: dcTitle } ], 'dc.contributor.author': [ @@ -59,7 +64,114 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign(new ItemSearchRe metadata: {} }) }); - +const mockPerson: ItemSearchResult = Object.assign(new ItemSearchResult(), { + hitHighlights: { + 'person.familyName': [{ + value: 'Michel' + }], + }, + indexableObject: + Object.assign(new Item(), { + bundles: observableOf({}), + entityType: 'Person', + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'dc.date.issued': [ + { + language: 'en_US', + value: '2015-06-26' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is the abstract' + } + ], + 'person.familyName': [ + { + value: 'Michel' + } + ], + 'dspace.entity.type': [ + { + value: 'Person' + } + ] + } + }) +}); +const mockOrgUnit: ItemSearchResult = Object.assign(new ItemSearchResult(), { + hitHighlights: { + 'organization.legalName': [{ + value: 'Science' + }], + }, + indexableObject: + Object.assign(new Item(), { + bundles: observableOf({}), + entityType: 'OrgUnit', + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'dc.date.issued': [ + { + language: 'en_US', + value: '2015-06-26' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is the abstract' + } + ], + 'organization.legalName': [ + { + value: 'Science' + } + ], + 'dspace.entity.type': [ + { + value: 'OrgUnit' + } + ] + } + }) +}); const environmentUseThumbs = { browseBy: { showThumbnails: true @@ -205,6 +317,42 @@ describe('ItemSearchResultListElementComponent', () => { }); }); + describe('When the item has title', () => { + beforeEach(() => { + publicationListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.item-list-title')); + expect(titleField.nativeNode.innerHTML).toEqual(dcTitle); + }); + }); + + describe('When the item is Person and has title', () => { + beforeEach(() => { + publicationListElementComponent.object = mockPerson; + fixture.detectChanges(); + }); + + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.item-list-title')); + expect(titleField.nativeNode.innerHTML).toEqual('Michel'); + }); + }); + + describe('When the item is orgUnit and has title', () => { + beforeEach(() => { + publicationListElementComponent.object = mockOrgUnit; + fixture.detectChanges(); + }); + + it('should show highlighted title', () => { + const titleField = fixture.debugElement.query(By.css('.item-list-title')); + expect(titleField.nativeNode.innerHTML).toEqual('Science'); + }); + }); + describe('When the item has no title', () => { beforeEach(() => { publicationListElementComponent.object = mockItemWithoutMetadata; diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index 72120a6b68..e56b7e970a 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -33,7 +33,7 @@ export class SearchResultListElementComponent, K exten ngOnInit(): void { if (hasValue(this.object)) { this.dso = this.object.indexableObject; - this.dsoTitle = this.dsoNameService.getName(this.dso); + this.dsoTitle = this.dsoNameService.getHitHighlights(this.object, this.dso); } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index e4abf0d907..777ad03c1d 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -186,7 +186,6 @@ import { ImportableListItemControlComponent } from './object-collection/shared/importable-list-item-control/importable-list-item-control.component'; import { LogInContainerComponent } from './log-in/container/log-in-container.component'; -import { LogInShibbolethComponent } from './log-in/methods/shibboleth/log-in-shibboleth.component'; import { LogInPasswordComponent } from './log-in/methods/password/log-in-password.component'; import { LogInComponent } from './log-in/log-in.component'; import { MissingTranslationHelper } from './translate/missing-translation.helper'; @@ -229,9 +228,7 @@ import { SearchNavbarComponent } from '../search-navbar/search-navbar.component' import { ThemedSearchNavbarComponent } from '../search-navbar/themed-search-navbar.component'; import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/scope-selector-modal.component'; import { DsSelectComponent } from './ds-select/ds-select.component'; -import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; import { RSSComponent } from './rss-feed/rss.component'; -import { LogInOrcidComponent } from './log-in/methods/orcid/log-in-orcid.component'; import { BrowserOnlyPipe } from './utils/browser-only.pipe'; import { ThemedLoadingComponent } from './loading/themed-loading.component'; import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component'; @@ -246,6 +243,8 @@ import { } from './object-list/listable-notification-object/listable-notification-object.component'; import { ThemedCollectionDropdownComponent } from './collection-dropdown/themed-collection-dropdown.component'; import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata-field-wrapper.component'; +import { LogInExternalProviderComponent } from './log-in/methods/log-in-external-provider/log-in-external-provider.component'; + const MODULES = [ CommonModule, @@ -386,9 +385,7 @@ const ENTRY_COMPONENTS = [ MetadataRepresentationListElementComponent, ItemMetadataRepresentationListElementComponent, LogInPasswordComponent, - LogInShibbolethComponent, - LogInOidcComponent, - LogInOrcidComponent, + LogInExternalProviderComponent, CollectionDropdownComponent, ThemedCollectionDropdownComponent, FileDownloadLinkComponent, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d043d61ae5..d2bd425df0 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3836,6 +3836,8 @@ "submission.import-external.source.crossref": "CrossRef", + "submission.import-external.source.datacite": "DataCite", + "submission.import-external.source.scielo": "SciELO", "submission.import-external.source.scopus": "Scopus",