diff --git a/e2e/app.po.ts b/e2e/app.po.ts
index 54b5b55af3..c76bef118f 100644
--- a/e2e/app.po.ts
+++ b/e2e/app.po.ts
@@ -2,7 +2,8 @@ import { browser, element, by } from 'protractor';
export class ProtractorPage {
navigateTo() {
- return browser.get('/');
+ return browser.get('/')
+ .then(() => browser.waitForAngular());
}
getPageTitleText() {
diff --git a/package.json b/package.json
index 2a247f13ad..aaabc0271a 100644
--- a/package.json
+++ b/package.json
@@ -75,6 +75,7 @@
},
"dependencies": {
"@angular/animations": "^6.1.4",
+ "@angular/cdk": "^6.4.7",
"@angular/cli": "^6.1.5",
"@angular/common": "^6.1.4",
"@angular/core": "^6.1.4",
@@ -229,7 +230,7 @@
"rollup-plugin-node-globals": "1.2.1",
"rollup-plugin-node-resolve": "^3.0.3",
"rollup-plugin-terser": "^2.0.2",
- "sass-loader": "7.1.0",
+ "sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.0.1",
"source-map": "0.7.3",
"source-map-loader": "0.2.4",
diff --git a/resources/fonts/README.md b/resources/fonts/README.md
new file mode 100644
index 0000000000..e4817b8572
--- /dev/null
+++ b/resources/fonts/README.md
@@ -0,0 +1,3 @@
+# Supported font formats
+
+DSpace supports EOT, TTF, OTF, SVG, WOFF and WOFF2 fonts.
diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5
index 7fa9c96010..76ffe955a7 100644
--- a/resources/i18n/en.json5
+++ b/resources/i18n/en.json5
@@ -340,6 +340,14 @@
+ "communityList.tabTitle": "DSpace - Community List",
+
+ "communityList.title": "List of Communities",
+
+ "communityList.showMore": "Show More",
+
+
+
"community.create.head": "Create a Community",
"community.create.sub-head": "Create a Sub-Community for Community {{ parent }}",
@@ -825,6 +833,14 @@
"item.page.related-items.view-less": "View less",
+ "item.page.relationships.isAuthorOfPublication": "Publications",
+
+ "item.page.relationships.isJournalOfPublication": "Publications",
+
+ "item.page.relationships.isOrgUnitOfPerson": "Authors",
+
+ "item.page.relationships.isOrgUnitOfProject": "Research Projects",
+
"item.page.subject": "Keywords",
"item.page.uri": "URI",
@@ -1275,6 +1291,8 @@
"project.page.titleprefix": "Research Project: ",
+ "project.search.results.head": "Project Search Results",
+
"publication.listelement.badge": "Publication",
diff --git a/src/app/+admin/admin-registries/bitstream-formats/bitstream-formats.component.ts b/src/app/+admin/admin-registries/bitstream-formats/bitstream-formats.component.ts
index cb7aa1ef91..ec4003c108 100644
--- a/src/app/+admin/admin-registries/bitstream-formats/bitstream-formats.component.ts
+++ b/src/app/+admin/admin-registries/bitstream-formats/bitstream-formats.component.ts
@@ -5,7 +5,7 @@ import { PaginatedList } from '../../../core/data/paginated-list';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service';
-import { FindAllOptions } from '../../../core/data/request.models';
+import { FindListOptions } from '../../../core/data/request.models';
import { map, switchMap, take } from 'rxjs/operators';
import { hasValue } from '../../../shared/empty.util';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
@@ -35,7 +35,7 @@ export class BitstreamFormatsComponent implements OnInit {
* The current pagination configuration for the page used by the FindAll method
* Currently simply renders all bitstream formats
*/
- config: FindAllOptions = Object.assign(new FindAllOptions(), {
+ config: FindListOptions = Object.assign(new FindListOptions(), {
elementsPerPage: 20
});
@@ -145,7 +145,7 @@ export class BitstreamFormatsComponent implements OnInit {
* @param event The page change event
*/
onPageChange(event) {
- this.config = Object.assign(new FindAllOptions(), this.config, {
+ this.config = Object.assign(new FindListOptions(), this.config, {
currentPage: event,
});
this.pageConfig.currentPage = event;
diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html
index 436cd351a0..12d5c200fd 100644
--- a/src/app/+collection-page/collection-page.component.html
+++ b/src/app/+collection-page/collection-page.component.html
@@ -3,6 +3,7 @@
*ngVar="(collectionRD$ | async) as collectionRD">
diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts
index 4c7c3cd030..b2ba10fb98 100644
--- a/src/app/navbar/navbar.component.ts
+++ b/src/app/navbar/navbar.component.ts
@@ -53,17 +53,18 @@ export class NavbarComponent extends MenuComponent implements OnInit {
} as TextMenuItemModel,
index: 0
},
- // {
- // id: 'browse_global_communities_and_collections',
- // parentID: 'browse_global',
- // active: false,
- // visible: true,
- // model: {
- // type: MenuItemType.LINK,
- // text: 'menu.section.browse_global_communities_and_collections',
- // link: '#'
- // } as LinkMenuItemModel,
- // },
+ /* Communities & Collections tree */
+ {
+ id: `browse_global_communities_and_collections`,
+ parentID: 'browse_global',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: `menu.section.browse_global_communities_and_collections`,
+ link: `/community-list`
+ } as LinkMenuItemModel
+ },
/* Statistics */
{
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
index 54cd4e2f86..65645e0739 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
@@ -8,7 +8,7 @@ import { RemoteData } from '../../../../../../core/data/remote-data';
import { PaginatedList } from '../../../../../../core/data/paginated-list';
import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model';
import { ExternalSource } from '../../../../../../core/shared/external-source.model';
-import { switchMap } from 'rxjs/operators';
+import { startWith, switchMap } from 'rxjs/operators';
import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model';
import { Context } from '../../../../../../core/shared/context.model';
import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
@@ -130,7 +130,7 @@ export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit
ngOnInit(): void {
this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
switchMap((searchOptions: PaginatedSearchOptions) =>
- this.externalSourceService.getExternalSourceEntries(this.externalSource.id, searchOptions))
+ this.externalSourceService.getExternalSourceEntries(this.externalSource.id, searchOptions).pipe(startWith(undefined)))
)
}
diff --git a/src/app/shared/mocks/mock-angulartics.service.ts b/src/app/shared/mocks/mock-angulartics.service.ts
index 99a8b96b22..5581e183d1 100644
--- a/src/app/shared/mocks/mock-angulartics.service.ts
+++ b/src/app/shared/mocks/mock-angulartics.service.ts
@@ -1,4 +1,5 @@
/* tslint:disable:no-empty */
export class AngularticsMock {
public eventTrack(action, properties) { }
+ public startTracking():void {}
}
diff --git a/src/app/shared/mocks/mock-request.service.ts b/src/app/shared/mocks/mock-request.service.ts
index ce4b167249..103ab14d88 100644
--- a/src/app/shared/mocks/mock-request.service.ts
+++ b/src/app/shared/mocks/mock-request.service.ts
@@ -1,8 +1,9 @@
import {of as observableOf, Observable } from 'rxjs';
import { RequestService } from '../../core/data/request.service';
import { RequestEntry } from '../../core/data/request.reducer';
+import SpyObj = jasmine.SpyObj;
-export function getMockRequestService(requestEntry$: Observable = observableOf(new RequestEntry())): RequestService {
+export function getMockRequestService(requestEntry$: Observable = observableOf(new RequestEntry())): SpyObj {
return jasmine.createSpyObj('requestService', {
configure: false,
generateRequestId: 'clients/b186e8ce-e99c-4183-bc9a-42b4821bdb78',
diff --git a/src/app/statistics/angulartics/dspace-provider.spec.ts b/src/app/statistics/angulartics/dspace-provider.spec.ts
new file mode 100644
index 0000000000..d89d2d9fc6
--- /dev/null
+++ b/src/app/statistics/angulartics/dspace-provider.spec.ts
@@ -0,0 +1,26 @@
+import { Angulartics2DSpace } from './dspace-provider';
+import { Angulartics2 } from 'angulartics2';
+import { StatisticsService } from '../statistics.service';
+import { filter } from 'rxjs/operators';
+import { of as observableOf } from 'rxjs';
+
+describe('Angulartics2DSpace', () => {
+ let provider:Angulartics2DSpace;
+ let angulartics2:Angulartics2;
+ let statisticsService:jasmine.SpyObj;
+
+ beforeEach(() => {
+ angulartics2 = {
+ eventTrack: observableOf({action: 'pageView', properties: {object: 'mock-object'}}),
+ filterDeveloperMode: () => filter(() => true)
+ } as any;
+ statisticsService = jasmine.createSpyObj('statisticsService', {trackViewEvent: null});
+ provider = new Angulartics2DSpace(angulartics2, statisticsService);
+ });
+
+ it('should use the statisticsService', () => {
+ provider.startTracking();
+ expect(statisticsService.trackViewEvent).toHaveBeenCalledWith('mock-object');
+ });
+
+});
diff --git a/src/app/statistics/angulartics/dspace-provider.ts b/src/app/statistics/angulartics/dspace-provider.ts
new file mode 100644
index 0000000000..9ab01f6023
--- /dev/null
+++ b/src/app/statistics/angulartics/dspace-provider.ts
@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import { Angulartics2 } from 'angulartics2';
+import { StatisticsService } from '../statistics.service';
+
+/**
+ * Angulartics2DSpace is a angulartics2 plugin that provides DSpace with the events.
+ */
+@Injectable({providedIn: 'root'})
+export class Angulartics2DSpace {
+
+ constructor(
+ private angulartics2:Angulartics2,
+ private statisticsService:StatisticsService,
+ ) {
+ }
+
+ /**
+ * Activates this plugin
+ */
+ startTracking():void {
+ this.angulartics2.eventTrack
+ .pipe(this.angulartics2.filterDeveloperMode())
+ .subscribe((event) => this.eventTrack(event));
+ }
+
+ private eventTrack(event) {
+ if (event.action === 'pageView') {
+ this.statisticsService.trackViewEvent(event.properties.object);
+ } else if (event.action === 'search') {
+ this.statisticsService.trackSearchEvent(
+ event.properties.searchOptions,
+ event.properties.page,
+ event.properties.sort,
+ event.properties.filters
+ );
+ }
+ }
+}
diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.html b/src/app/statistics/angulartics/dspace/view-tracker.component.html
new file mode 100644
index 0000000000..c0c0ffe181
--- /dev/null
+++ b/src/app/statistics/angulartics/dspace/view-tracker.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.scss b/src/app/statistics/angulartics/dspace/view-tracker.component.scss
new file mode 100644
index 0000000000..c76cafbe44
--- /dev/null
+++ b/src/app/statistics/angulartics/dspace/view-tracker.component.scss
@@ -0,0 +1,3 @@
+:host {
+ display: none
+}
diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.ts b/src/app/statistics/angulartics/dspace/view-tracker.component.ts
new file mode 100644
index 0000000000..1151287ea8
--- /dev/null
+++ b/src/app/statistics/angulartics/dspace/view-tracker.component.ts
@@ -0,0 +1,27 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Angulartics2 } from 'angulartics2';
+import { DSpaceObject } from '../../../core/shared/dspace-object.model';
+
+/**
+ * This component triggers a page view statistic
+ */
+@Component({
+ selector: 'ds-view-tracker',
+ styleUrls: ['./view-tracker.component.scss'],
+ templateUrl: './view-tracker.component.html',
+})
+export class ViewTrackerComponent implements OnInit {
+ @Input() object:DSpaceObject;
+
+ constructor(
+ public angulartics2:Angulartics2
+ ) {
+ }
+
+ ngOnInit():void {
+ this.angulartics2.eventTrack.next({
+ action: 'pageView',
+ properties: {object: this.object},
+ });
+ }
+}
diff --git a/src/app/statistics/statistics.module.ts b/src/app/statistics/statistics.module.ts
new file mode 100644
index 0000000000..a67ff7613c
--- /dev/null
+++ b/src/app/statistics/statistics.module.ts
@@ -0,0 +1,36 @@
+import { ModuleWithProviders, NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CoreModule } from '../core/core.module';
+import { SharedModule } from '../shared/shared.module';
+import { ViewTrackerComponent } from './angulartics/dspace/view-tracker.component';
+import { StatisticsService } from './statistics.service';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ CoreModule.forRoot(),
+ SharedModule,
+ ],
+ declarations: [
+ ViewTrackerComponent,
+ ],
+ exports: [
+ ViewTrackerComponent,
+ ],
+ providers: [
+ StatisticsService
+ ]
+})
+/**
+ * This module handles the statistics
+ */
+export class StatisticsModule {
+ static forRoot():ModuleWithProviders {
+ return {
+ ngModule: StatisticsModule,
+ providers: [
+ StatisticsService
+ ]
+ };
+ }
+}
diff --git a/src/app/statistics/statistics.service.spec.ts b/src/app/statistics/statistics.service.spec.ts
new file mode 100644
index 0000000000..c6cc4c10b5
--- /dev/null
+++ b/src/app/statistics/statistics.service.spec.ts
@@ -0,0 +1,144 @@
+import { StatisticsService } from './statistics.service';
+import { RequestService } from '../core/data/request.service';
+import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service-stub';
+import { getMockRequestService } from '../shared/mocks/mock-request.service';
+import { TrackRequest } from './track-request.model';
+import { isEqual } from 'lodash';
+import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
+import { SearchOptions } from '../shared/search/search-options.model';
+
+describe('StatisticsService', () => {
+ let service:StatisticsService;
+ let requestService:jasmine.SpyObj;
+ const restURL = 'https://rest.api';
+ const halService:any = new HALEndpointServiceStub(restURL);
+
+ function initTestService() {
+ return new StatisticsService(
+ requestService,
+ halService,
+ );
+ }
+
+ describe('trackViewEvent', () => {
+ requestService = getMockRequestService();
+ service = initTestService();
+
+ it('should send a request to track an item view ', () => {
+ const mockItem:any = {uuid: 'mock-item-uuid', type: 'item'};
+ service.trackViewEvent(mockItem);
+ const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+ expect(request.body).toBeDefined('request.body');
+ const body = JSON.parse(request.body);
+ expect(body.targetId).toBe('mock-item-uuid');
+ expect(body.targetType).toBe('item');
+ });
+ });
+
+ describe('trackSearchEvent', () => {
+ requestService = getMockRequestService();
+ service = initTestService();
+
+ const mockSearch:any = new SearchOptions({
+ query: 'mock-query',
+ });
+
+ const page = {
+ size: 10,
+ totalElements: 248,
+ totalPages: 25,
+ number: 4
+ };
+ const sort = {by: 'search-field', order: 'ASC'};
+ service.trackSearchEvent(mockSearch, page, sort);
+ const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+ const body = JSON.parse(request.body);
+
+ it('should specify the right query', () => {
+ expect(body.query).toBe('mock-query');
+ });
+
+ it('should specify the pagination info', () => {
+ expect(body.page).toEqual({
+ size: 10,
+ totalElements: 248,
+ totalPages: 25,
+ number: 4
+ });
+ });
+
+ it('should specify the sort options', () => {
+ expect(body.sort).toEqual({
+ by: 'search-field',
+ order: 'asc'
+ });
+ });
+ });
+
+ describe('trackSearchEvent with optional parameters', () => {
+ requestService = getMockRequestService();
+ service = initTestService();
+
+ const mockSearch:any = new SearchOptions({
+ query: 'mock-query',
+ configuration: 'mock-configuration',
+ dsoType: DSpaceObjectType.ITEM,
+ scope: 'mock-scope'
+ });
+
+ const page = {
+ size: 10,
+ totalElements: 248,
+ totalPages: 25,
+ number: 4
+ };
+ const sort = {by: 'search-field', order: 'ASC'};
+ const filters = [
+ {
+ filter: 'title',
+ operator: 'notcontains',
+ value: 'dolor sit',
+ label: 'dolor sit'
+ },
+ {
+ filter: 'author',
+ operator: 'authority',
+ value: '9zvxzdm4qru17or5a83wfgac',
+ label: 'Amet, Consectetur'
+ }
+ ];
+ service.trackSearchEvent(mockSearch, page, sort, filters);
+ const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+ const body = JSON.parse(request.body);
+
+ it('should specify the dsoType', () => {
+ expect(body.dsoType).toBe('item');
+ });
+
+ it('should specify the scope', () => {
+ expect(body.scope).toBe('mock-scope');
+ });
+
+ it('should specify the configuration', () => {
+ expect(body.configuration).toBe('mock-configuration');
+ });
+
+ it('should specify the filters', () => {
+ expect(isEqual(body.appliedFilters, [
+ {
+ filter: 'title',
+ operator: 'notcontains',
+ value: 'dolor sit',
+ label: 'dolor sit'
+ },
+ {
+ filter: 'author',
+ operator: 'authority',
+ value: '9zvxzdm4qru17or5a83wfgac',
+ label: 'Amet, Consectetur'
+ }
+ ])).toBe(true);
+ });
+ });
+
+});
diff --git a/src/app/statistics/statistics.service.ts b/src/app/statistics/statistics.service.ts
new file mode 100644
index 0000000000..ae9118e1b7
--- /dev/null
+++ b/src/app/statistics/statistics.service.ts
@@ -0,0 +1,93 @@
+import { RequestService } from '../core/data/request.service';
+import { Injectable } from '@angular/core';
+import { DSpaceObject } from '../core/shared/dspace-object.model';
+import { map, take } from 'rxjs/operators';
+import { TrackRequest } from './track-request.model';
+import { hasValue, isNotEmpty } from '../shared/empty.util';
+import { HALEndpointService } from '../core/shared/hal-endpoint.service';
+import { RestRequest } from '../core/data/request.models';
+import { SearchOptions } from '../shared/search/search-options.model';
+
+/**
+ * The statistics service
+ */
+@Injectable()
+export class StatisticsService {
+
+ constructor(
+ protected requestService:RequestService,
+ protected halService:HALEndpointService,
+ ) {
+ }
+
+ private sendEvent(linkPath:string, body:any) {
+ const requestId = this.requestService.generateRequestId();
+ this.halService.getEndpoint(linkPath).pipe(
+ map((endpoint:string) => new TrackRequest(requestId, endpoint, JSON.stringify(body))),
+ take(1) // otherwise the previous events will fire again
+ ).subscribe((request:RestRequest) => this.requestService.configure(request));
+ }
+
+ /**
+ * To track a page view
+ * @param dso: The dso which was viewed
+ */
+ trackViewEvent(dso:DSpaceObject) {
+ this.sendEvent('/statistics/viewevents', {
+ targetId: dso.uuid,
+ targetType: (dso as any).type
+ });
+ }
+
+ /**
+ * To track a search
+ * @param searchOptions: The query, scope, dsoType and configuration of the search. Filters from this object are ignored in favor of the filters parameter of this method.
+ * @param page: An object that describes the pagination status
+ * @param sort: An object that describes the sort status
+ * @param filters: An array of search filters used to filter the result set
+ */
+ trackSearchEvent(
+ searchOptions:SearchOptions,
+ page:{ size:number, totalElements:number, totalPages:number, number:number },
+ sort:{ by:string, order:string },
+ filters?:Array<{ filter:string, operator:string, value:string, label:string }>
+ ) {
+ const body = {
+ query: searchOptions.query,
+ page: {
+ size: page.size,
+ totalElements: page.totalElements,
+ totalPages: page.totalPages,
+ number: page.number
+ },
+ sort: {
+ by: sort.by,
+ order: sort.order.toLowerCase()
+ },
+ };
+ if (hasValue(searchOptions.configuration)) {
+ Object.assign(body, {configuration: searchOptions.configuration})
+ }
+ if (hasValue(searchOptions.dsoType)) {
+ Object.assign(body, {dsoType: searchOptions.dsoType.toLowerCase()})
+ }
+ if (hasValue(searchOptions.scope)) {
+ Object.assign(body, {scope: searchOptions.scope})
+ }
+ if (isNotEmpty(filters)) {
+ const bodyFilters = [];
+ for (let i = 0, arrayLength = filters.length; i < arrayLength; i++) {
+ const filter = filters[i];
+ bodyFilters.push({
+ filter: filter.filter,
+ operator: filter.operator,
+ value: filter.value,
+ label: filter.label
+ })
+ }
+ Object.assign(body, {appliedFilters: bodyFilters})
+ }
+ this.sendEvent('/statistics/searchevents', body);
+ }
+
+}
diff --git a/src/app/statistics/track-request.model.ts b/src/app/statistics/track-request.model.ts
new file mode 100644
index 0000000000..df3e51c070
--- /dev/null
+++ b/src/app/statistics/track-request.model.ts
@@ -0,0 +1,4 @@
+import { PostRequest } from '../core/data/request.models';
+
+export class TrackRequest extends PostRequest {
+}
diff --git a/src/app/submission/form/collection/submission-form-collection.component.ts b/src/app/submission/form/collection/submission-form-collection.component.ts
index e40ea82163..d318bfe687 100644
--- a/src/app/submission/form/collection/submission-form-collection.component.ts
+++ b/src/app/submission/form/collection/submission-form-collection.component.ts
@@ -16,7 +16,7 @@ import { SubmissionService } from '../../submission.service';
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
import { CollectionDataService } from '../../../core/data/collection-data.service';
-import { FindAllOptions } from '../../../core/data/request.models';
+import { FindListOptions } from '../../../core/data/request.models';
/**
* An interface to represent a collection entry
@@ -185,7 +185,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
map((collectionRD: RemoteData) => collectionRD.payload.name)
);
- const findOptions: FindAllOptions = {
+ const findOptions: FindListOptions = {
elementsPerPage: 1000
};
diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts
index ede2b53e74..87b830ee7d 100644
--- a/src/modules/app/browser-app.module.ts
+++ b/src/modules/app/browser-app.module.ts
@@ -21,6 +21,8 @@ import { AuthService } from '../../app/core/auth/auth.service';
import { Angulartics2Module } from 'angulartics2';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { SubmissionService } from '../../app/submission/submission.service';
+import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider';
+import { StatisticsModule } from '../../app/statistics/statistics.module';
export const REQ_KEY = makeStateKey('req');
@@ -47,7 +49,8 @@ export function getRequest(transferState: TransferState): any {
preloadingStrategy:
IdlePreload
}),
- Angulartics2Module.forRoot([Angulartics2GoogleAnalytics]),
+ StatisticsModule.forRoot(),
+ Angulartics2Module.forRoot([Angulartics2GoogleAnalytics, Angulartics2DSpace]),
BrowserAnimationsModule,
DSpaceBrowserTransferStateModule,
TranslateModule.forRoot({
diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts
index 02abf6449b..44b21859bd 100644
--- a/src/modules/app/server-app.module.ts
+++ b/src/modules/app/server-app.module.ts
@@ -22,6 +22,8 @@ import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { AngularticsMock } from '../../app/shared/mocks/mock-angulartics.service';
import { SubmissionService } from '../../app/submission/submission.service';
import { ServerSubmissionService } from '../../app/submission/server-submission.service';
+import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider';
+import { Angulartics2Module } from 'angulartics2';
export function createTranslateLoader() {
return new TranslateJson5UniversalLoader('dist/assets/i18n/', '.json5');
@@ -45,6 +47,7 @@ export function createTranslateLoader() {
deps: []
}
}),
+ Angulartics2Module.forRoot([Angulartics2GoogleAnalytics, Angulartics2DSpace]),
ServerModule,
AppModule
],
@@ -53,6 +56,10 @@ export function createTranslateLoader() {
provide: Angulartics2GoogleAnalytics,
useClass: AngularticsMock
},
+ {
+ provide: Angulartics2DSpace,
+ useClass: AngularticsMock
+ },
{
provide: AuthService,
useClass: ServerAuthService
diff --git a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
index 99d92d2af8..907f70b941 100644
--- a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
+++ b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html
@@ -4,7 +4,7 @@
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
similarity index 79%
rename from themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html
rename to themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
index 15529a1bd5..ee78d9c653 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
@@ -53,18 +53,6 @@
+
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
similarity index 86%
rename from themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss
rename to themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
index 54651aede0..4a1d2516da 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
@@ -1,4 +1,4 @@
-@import 'src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss';
+@import 'src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss';
:host {
> * {
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
index bb5cb1b787..1679f9354d 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
@@ -79,7 +79,10 @@
{{"item.page.person.search.title" | translate}}
-
-
+
+
diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js
index 028815d958..e63ae024ed 100644
--- a/webpack/webpack.common.js
+++ b/webpack/webpack.common.js
@@ -15,6 +15,9 @@ module.exports = (env) => {
let copyWebpackOptions = [{
from: path.join(__dirname, '..', 'node_modules', '@fortawesome', 'fontawesome-free', 'webfonts'),
to: path.join('assets', 'fonts')
+ }, {
+ from: path.join(__dirname, '..', 'resources', 'fonts'),
+ to: path.join('assets', 'fonts')
}, {
from: path.join(__dirname, '..', 'resources', 'images'),
to: path.join('assets', 'images')
@@ -24,6 +27,15 @@ module.exports = (env) => {
}
];
+ const themeFonts = path.join(themePath, 'resources', 'fonts');
+ if(theme && fs.existsSync(themeFonts)) {
+ copyWebpackOptions.push({
+ from: themeFonts,
+ to: path.join('assets', 'fonts') ,
+ force: true,
+ });
+ }
+
const themeImages = path.join(themePath, 'resources', 'images');
if(theme && fs.existsSync(themeImages)) {
copyWebpackOptions.push({
@@ -107,12 +119,6 @@ module.exports = (env) => {
sourceMap: true
}
},
- {
- loader: 'resolve-url-loader',
- options: {
- sourceMap: true
- }
- },
{
loader: 'sass-loader',
options: {
@@ -120,6 +126,12 @@ module.exports = (env) => {
includePaths: [projectRoot('./'), path.join(themePath, 'styles')]
}
},
+ {
+ loader: 'resolve-url-loader',
+ options: {
+ sourceMap: true
+ }
+ },
{
loader: 'sass-resources-loader',
options: {
@@ -145,23 +157,23 @@ module.exports = (env) => {
sourceMap: true
}
},
- {
- loader: 'resolve-url-loader',
- options: {
- sourceMap: true
- }
- },
{
loader: 'sass-loader',
options: {
sourceMap: true,
includePaths: [projectRoot('./'), path.join(themePath, 'styles')]
}
- }
+ },
+ {
+ loader: 'resolve-url-loader',
+ options: {
+ sourceMap: true
+ }
+ },
]
},
{
- test: /\.html$/,
+ test: /\.(html|eot|ttf|otf|svg|woff|woff2)$/,
loader: 'raw-loader'
}
]
diff --git a/webpack/webpack.test.js b/webpack/webpack.test.js
index 83e6e44e79..de53de31c4 100644
--- a/webpack/webpack.test.js
+++ b/webpack/webpack.test.js
@@ -160,12 +160,6 @@ module.exports = function (env) {
sourceMap: true
}
},
- {
- loader: 'resolve-url-loader',
- options: {
- sourceMap: true
- }
- },
{
loader: 'sass-loader',
options: {
@@ -173,6 +167,12 @@ module.exports = function (env) {
includePaths: [projectRoot('./'), path.join(themePath, 'styles')]
}
},
+ {
+ loader: 'resolve-url-loader',
+ options: {
+ sourceMap: true
+ }
+ },
{
loader: 'sass-resources-loader',
options: {
@@ -198,19 +198,19 @@ module.exports = function (env) {
sourceMap: true
}
},
- {
- loader: 'resolve-url-loader',
- options: {
- sourceMap: true
- }
- },
{
loader: 'sass-loader',
options: {
sourceMap: true,
includePaths: [projectRoot('./'), path.join(themePath, 'styles')]
}
- }
+ },
+ {
+ loader: 'resolve-url-loader',
+ options: {
+ sourceMap: true
+ }
+ },
]
},
diff --git a/yarn.lock b/yarn.lock
index 26480cccca..b4ec416395 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -35,6 +35,13 @@
dependencies:
tslib "^1.9.0"
+"@angular/cdk@^6.4.7":
+ version "6.4.7"
+ resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-6.4.7.tgz#1549b304dd412e82bd854cc55a7d5c6772ee0411"
+ integrity sha512-18x0U66fLD5kGQWZ9n3nb75xQouXlWs7kUDaTd8HTrHpT1s2QIAqlLd1KxfrYiVhsEC2jPQaoiae7VnBlcvkBg==
+ dependencies:
+ tslib "^1.7.1"
+
"@angular/cli@^6.1.5":
version "6.1.5"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-6.1.5.tgz#312c062631285ff06fd07ecde8afe22cdef5a0e1"
@@ -2093,15 +2100,14 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
-clone-deep@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
- integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
dependencies:
- for-own "^1.0.0"
is-plain-object "^2.0.4"
- kind-of "^6.0.0"
- shallow-clone "^1.0.0"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
clone-stats@^0.0.1:
version "0.0.1"
@@ -4121,11 +4127,6 @@ font-awesome@4.7.0:
resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=
-for-in@^0.1.3:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
- integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=
-
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -4138,13 +4139,6 @@ for-own@^0.1.4:
dependencies:
for-in "^1.0.1"
-for-own@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b"
- integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=
- dependencies:
- for-in "^1.0.1"
-
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -6148,7 +6142,7 @@ loader-utils@^0.2.12, loader-utils@^0.2.15, loader-utils@^0.2.16:
json5 "^0.5.0"
object-assign "^4.0.1"
-loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0:
+loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=
@@ -6157,7 +6151,7 @@ loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1
emojis-list "^2.0.0"
json5 "^0.5.0"
-loader-utils@^1.0.4:
+loader-utils@^1.0.1, loader-utils@^1.0.4:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
dependencies:
@@ -6371,11 +6365,6 @@ lodash.startswith@^4.2.1:
resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c"
integrity sha1-xZjErc4YiiflMUVzHNxsDnF3YAw=
-lodash.tail@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
- integrity sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=
-
lodash.template@^3.0.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f"
@@ -6849,14 +6838,6 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mixin-object@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
- integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=
- dependencies:
- for-in "^0.1.3"
- is-extendable "^0.1.1"
-
mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -9659,17 +9640,16 @@ sass-graph@^2.2.4:
scss-tokenizer "^0.2.3"
yargs "^7.0.0"
-sass-loader@7.1.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.1.0.tgz#16fd5138cb8b424bf8a759528a1972d72aad069d"
- integrity sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==
+sass-loader@^7.1.0:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f"
+ integrity sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==
dependencies:
- clone-deep "^2.0.1"
+ clone-deep "^4.0.1"
loader-utils "^1.0.1"
- lodash.tail "^4.1.1"
neo-async "^2.5.0"
- pify "^3.0.0"
- semver "^5.5.0"
+ pify "^4.0.1"
+ semver "^6.3.0"
sass-resources-loader@^2.0.0:
version "2.0.0"
@@ -9774,7 +9754,7 @@ semver-intersect@^1.1.2:
dependencies:
semver "^5.0.0"
-"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0:
+"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0:
version "5.5.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
@@ -9784,7 +9764,12 @@ semver@^5.0.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
-semver@^6.1.1:
+semver@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.1.1, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
@@ -9915,14 +9900,12 @@ sha.js@^2.4.0, sha.js@^2.4.8:
inherits "^2.0.1"
safe-buffer "^5.0.1"
-shallow-clone@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
- integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
dependencies:
- is-extendable "^0.1.1"
- kind-of "^5.0.0"
- mixin-object "^2.0.1"
+ kind-of "^6.0.2"
shebang-command@^1.2.0:
version "1.2.0"
@@ -10838,6 +10821,11 @@ tsickle@^0.32.1:
source-map "^0.6.0"
source-map-support "^0.5.0"
+tslib@^1.7.1:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
+ integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"