mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
74647: Generator meta
This commit is contained in:
@@ -172,6 +172,8 @@ import { EndUserAgreementCookieGuard } from './end-user-agreement/end-user-agree
|
|||||||
import { EndUserAgreementService } from './end-user-agreement/end-user-agreement.service';
|
import { EndUserAgreementService } from './end-user-agreement/end-user-agreement.service';
|
||||||
import { SiteRegisterGuard } from './data/feature-authorization/feature-authorization-guard/site-register.guard';
|
import { SiteRegisterGuard } from './data/feature-authorization/feature-authorization-guard/site-register.guard';
|
||||||
import { UsageReport } from './statistics/models/usage-report.model';
|
import { UsageReport } from './statistics/models/usage-report.model';
|
||||||
|
import { RootDataService } from './data/root-data.service';
|
||||||
|
import { Root } from './data/root.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When not in production, endpoint responses can be mocked for testing purposes
|
* When not in production, endpoint responses can be mocked for testing purposes
|
||||||
@@ -302,6 +304,7 @@ const PROVIDERS = [
|
|||||||
EndUserAgreementCurrentUserGuard,
|
EndUserAgreementCurrentUserGuard,
|
||||||
EndUserAgreementCookieGuard,
|
EndUserAgreementCookieGuard,
|
||||||
EndUserAgreementService,
|
EndUserAgreementService,
|
||||||
|
RootDataService,
|
||||||
// register AuthInterceptor as HttpInterceptor
|
// register AuthInterceptor as HttpInterceptor
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
@@ -374,6 +377,7 @@ export const models =
|
|||||||
VocabularyEntryDetail,
|
VocabularyEntryDetail,
|
||||||
ConfigurationProperty,
|
ConfigurationProperty,
|
||||||
UsageReport,
|
UsageReport,
|
||||||
|
Root,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
38
src/app/core/data/root-data.service.spec.ts
Normal file
38
src/app/core/data/root-data.service.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { RootDataService } from './root-data.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
|
import { Root } from './root.model';
|
||||||
|
|
||||||
|
describe('RootDataService', () => {
|
||||||
|
let service: RootDataService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let rootEndpoint;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
rootEndpoint = 'root-endpoint';
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getRootHref: rootEndpoint
|
||||||
|
});
|
||||||
|
service = new RootDataService(null, null, null, null, halService, null, null, null);
|
||||||
|
(service as any).dataService = jasmine.createSpyObj('dataService', {
|
||||||
|
findByHref: createSuccessfulRemoteDataObject$({})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findRoot', () => {
|
||||||
|
let result$: Observable<RemoteData<Root>>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
result$ = service.findRoot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call findByHref using the root endpoint', (done) => {
|
||||||
|
result$.subscribe(() => {
|
||||||
|
expect((service as any).dataService.findByHref).toHaveBeenCalledWith(rootEndpoint);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
72
src/app/core/data/root-data.service.ts
Normal file
72
src/app/core/data/root-data.service.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { DataService } from './data.service';
|
||||||
|
import { Root } from './root.model';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ROOT } from './root.resource-type';
|
||||||
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
|
import { RequestService } from './request.service';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private DataService implementation to delegate specific methods to.
|
||||||
|
*/
|
||||||
|
class DataServiceImpl extends DataService<Root> {
|
||||||
|
protected linkPath = '';
|
||||||
|
protected responseMsToLive = 6 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<Root>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service to retrieve the {@link Root} object from the REST API.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(ROOT)
|
||||||
|
export class RootDataService {
|
||||||
|
/**
|
||||||
|
* A private DataService instance to delegate specific methods to.
|
||||||
|
*/
|
||||||
|
private dataService: DataServiceImpl;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<Root>) {
|
||||||
|
this.dataService = new DataServiceImpl(requestService, rdbService, null, objectCache, halService, notificationsService, http, comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the {@link Root} object of the REST API
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
findRoot(...linksToFollow: Array<FollowLinkConfig<Root>>): Observable<RemoteData<Root>> {
|
||||||
|
return this.dataService.findByHref(this.halService.getRootHref(), ...linksToFollow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
55
src/app/core/data/root.model.ts
Normal file
55
src/app/core/data/root.model.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { typedObject } from '../cache/builders/build-decorators';
|
||||||
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
|
import { ROOT } from './root.resource-type';
|
||||||
|
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||||
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
|
import { ResourceType } from '../shared/resource-type';
|
||||||
|
import { HALLink } from '../shared/hal-link.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root rest api resource
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class Root implements CacheableObject {
|
||||||
|
static type = ROOT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object type
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for the dspace UI
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
dspaceUI: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository Name
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
dspaceName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for the rest api
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
dspaceServer: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current DSpace version
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
dspaceVersion: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link HALLink}s for the root object
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
[k: string]: HALLink | HALLink[];
|
||||||
|
}
|
||||||
|
}
|
9
src/app/core/data/root.resource-type.ts
Normal file
9
src/app/core/data/root.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for the root endpoint
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const ROOT = new ResourceType('root');
|
@@ -54,6 +54,8 @@ import { environment } from '../../../environments/environment';
|
|||||||
import { storeModuleConfig } from '../../app.reducer';
|
import { storeModuleConfig } from '../../app.reducer';
|
||||||
import { HardRedirectService } from '../services/hard-redirect.service';
|
import { HardRedirectService } from '../services/hard-redirect.service';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
|
import { RootDataService } from '../data/root-data.service';
|
||||||
|
import { Root } from '../data/root.model';
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -92,6 +94,7 @@ describe('MetadataService', () => {
|
|||||||
let remoteDataBuildService: RemoteDataBuildService;
|
let remoteDataBuildService: RemoteDataBuildService;
|
||||||
let itemDataService: ItemDataService;
|
let itemDataService: ItemDataService;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
|
let rootService: RootDataService;
|
||||||
|
|
||||||
let location: Location;
|
let location: Location;
|
||||||
let router: Router;
|
let router: Router;
|
||||||
@@ -131,6 +134,11 @@ describe('MetadataService', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
rootService = jasmine.createSpyObj('rootService', {
|
||||||
|
findRoot: createSuccessfulRemoteDataObject$(Object.assign(new Root(), {
|
||||||
|
dspaceVersion: 'mock-dspace-version'
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -169,6 +177,7 @@ describe('MetadataService', () => {
|
|||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
{ provide: BitstreamFormatDataService, useValue: mockBitstreamFormatDataService },
|
{ provide: BitstreamFormatDataService, useValue: mockBitstreamFormatDataService },
|
||||||
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
|
{ provide: RootDataService, useValue: rootService },
|
||||||
Meta,
|
Meta,
|
||||||
Title,
|
Title,
|
||||||
// tslint:disable-next-line:no-empty
|
// tslint:disable-next-line:no-empty
|
||||||
@@ -223,6 +232,13 @@ describe('MetadataService', () => {
|
|||||||
expect(tagStore.get('citation_technical_report_institution')[0].content).toEqual('Mock Publisher');
|
expect(tagStore.get('citation_technical_report_institution')[0].content).toEqual('Mock Publisher');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('items page should set meta tag for Generator containing the current DSpace version', fakeAsync(() => {
|
||||||
|
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock));
|
||||||
|
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
|
||||||
|
tick();
|
||||||
|
expect(tagStore.get('Generator')[0].content).toEqual('mock-dspace-version');
|
||||||
|
}));
|
||||||
|
|
||||||
it('other navigation should title and description', fakeAsync(() => {
|
it('other navigation should title and description', fakeAsync(() => {
|
||||||
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock));
|
spyOn(itemDataService, 'findById').and.returnValue(mockRemoteData(ItemMock));
|
||||||
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
|
router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']);
|
||||||
|
@@ -22,6 +22,7 @@ import { Item } from '../shared/item.model';
|
|||||||
import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload } from '../shared/operators';
|
import { getFirstSucceededRemoteDataPayload, getFirstSucceededRemoteListPayload } from '../shared/operators';
|
||||||
import { HardRedirectService } from '../services/hard-redirect.service';
|
import { HardRedirectService } from '../services/hard-redirect.service';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
|
import { RootDataService } from '../data/root-data.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetadataService {
|
export class MetadataService {
|
||||||
@@ -40,7 +41,8 @@ export class MetadataService {
|
|||||||
private dsoNameService: DSONameService,
|
private dsoNameService: DSONameService,
|
||||||
private bitstreamDataService: BitstreamDataService,
|
private bitstreamDataService: BitstreamDataService,
|
||||||
private bitstreamFormatDataService: BitstreamFormatDataService,
|
private bitstreamFormatDataService: BitstreamFormatDataService,
|
||||||
private redirectService: HardRedirectService
|
private redirectService: HardRedirectService,
|
||||||
|
private rootService: RootDataService
|
||||||
) {
|
) {
|
||||||
// TODO: determine what open graph meta tags are needed and whether
|
// TODO: determine what open graph meta tags are needed and whether
|
||||||
// the differ per route. potentially add image based on DSpaceObject
|
// the differ per route. potentially add image based on DSpaceObject
|
||||||
@@ -136,6 +138,8 @@ export class MetadataService {
|
|||||||
this.setCitationTechReportInstitutionTag();
|
this.setCitationTechReportInstitutionTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setGenerator();
|
||||||
|
|
||||||
// this.setCitationJournalTitleTag();
|
// this.setCitationJournalTitleTag();
|
||||||
// this.setCitationVolumeTag();
|
// this.setCitationVolumeTag();
|
||||||
// this.setCitationIssueTag();
|
// this.setCitationIssueTag();
|
||||||
@@ -290,6 +294,15 @@ export class MetadataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add <meta name="Generator" ... > to the <head> containing the current DSpace version
|
||||||
|
*/
|
||||||
|
private setGenerator(): void {
|
||||||
|
this.rootService.findRoot().pipe(getFirstSucceededRemoteDataPayload()).subscribe((root) => {
|
||||||
|
this.addMetaTag('Generator', root.dspaceVersion);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private hasType(value: string): boolean {
|
private hasType(value: string): boolean {
|
||||||
return this.currentObject.value.hasMetadata('dc.type', { value: value, ignoreCase: true });
|
return this.currentObject.value.hasMetadata('dc.type', { value: value, ignoreCase: true });
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ export class HALEndpointService {
|
|||||||
constructor(private requestService: RequestService) {
|
constructor(private requestService: RequestService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getRootHref(): string {
|
public getRootHref(): string {
|
||||||
return new RESTURLCombiner('/api').toString();
|
return new RESTURLCombiner('/api').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user