diff --git a/README.md b/README.md index a241f72910..466e736de2 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,7 @@ dspace-angular ├── config * Folder for configuration files │   ├── environment.default.js * Default configuration files │   └── environment.test.js * Test configuration files +├── docs * Folder for documentation ├── e2e * Folder for e2e test files │   ├── app.e2e-spec.ts * │   ├── app.po.ts * @@ -380,6 +381,11 @@ This project makes use of [`yarn`](https://yarnpkg.com/en/) to ensure that the e As you can see above, using `yarn` commandline tools means that you should never need to modify the `package.json` manually. *We recommend always using `yarn` to keep dependencies updated / in sync.* +Further Documentation +--------------------- + +See [`./docs`](docs) for further documentation. + Frequently asked questions -------------------------- diff --git a/config/environment.default.js b/config/environment.default.js index b44da0e248..9ec5c05a64 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -28,6 +28,8 @@ module.exports = { async: true, time: false }, + // Google Analytics tracking id + gaTrackingId: '', // Log directory logDirectory: '.', // NOTE: will log all redux actions and transfers in console diff --git a/docs/Configuration.md b/docs/Configuration.md new file mode 100644 index 0000000000..712b1523a0 --- /dev/null +++ b/docs/Configuration.md @@ -0,0 +1,10 @@ +# Configuration + +## Supporting analytics services other than Google Analytics +This project makes use of [Angulartics](https://angulartics.github.io/angulartics2/) to track usage events and send them to Google Analytics. + +Angulartics can be configured to work with a number of other services besides Google Analytics as well, e.g. [Piwik](https://github.com/angulartics/angulartics2/tree/master/src/lib/providers/piwik), [Google Tag Manager](https://github.com/angulartics/angulartics2/tree/master/src/lib/providers/gtm), or [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) to name a few. + +In order to start using one of these services, select it from the [Angulartics Providers page](https://angulartics.github.io/angulartics2/#providers), and follow the instructions on how to configure it. + +The Google Analytics script was added in [`main.browser.ts`](https://github.com/DSpace/dspace-angular/blob/ff04760f4af91ac3e7add5e7424a46cb2439e874/src/main.browser.ts#L33) instead of the `` tag in `index.html` to ensure events get sent when the page is shown in a client's browser, and not when it's rendered on the universal server. Likely you'll want to do the same when adding a new service. \ No newline at end of file diff --git a/package.json b/package.json index 1e2e60a409..2878daf1c8 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "@ngx-translate/core": "9.1.1", "@ngx-translate/http-loader": "2.0.1", "angular-idle-preload": "2.0.4", + "angulartics2": "^5.2.0", "body-parser": "1.18.2", "bootstrap": "^4.0.0", "cerialize": "0.1.18", diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 5c77b34549..391ff422c8 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -30,6 +30,8 @@ import { NativeWindowRef, NativeWindowService } from './shared/window.service'; import { MockTranslateLoader } from './shared/mocks/mock-translate-loader'; import { MockMetadataService } from './shared/mocks/mock-metadata-service'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; +import { AngularticsMock } from './shared/mocks/mock-angulartics.service'; let comp: AppComponent; let fixture: ComponentFixture; @@ -56,6 +58,7 @@ describe('App component', () => { { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, { provide: NativeWindowService, useValue: new NativeWindowRef() }, { provide: MetadataService, useValue: new MockMetadataService() }, + { provide: Angulartics2GoogleAnalytics, useValue: new AngularticsMock() }, AppComponent ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 25bdde2d23..c1c84d6dbc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -17,6 +17,7 @@ import { MetadataService } from './core/metadata/metadata.service'; import { HostWindowResizeAction } from './shared/host-window.actions'; import { HostWindowState } from './shared/host-window.reducer'; import { NativeWindowRef, NativeWindowService } from './shared/window.service'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; @Component({ selector: 'ds-app', @@ -32,7 +33,8 @@ export class AppComponent implements OnInit { @Inject(NativeWindowService) private _window: NativeWindowRef, private translate: TranslateService, private store: Store, - private metadata: MetadataService + private metadata: MetadataService, + private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics ) { // this language will be used as a fallback when a translation isn't found in the current language translate.setDefaultLang('en'); diff --git a/src/app/shared/mocks/mock-angulartics.service.ts b/src/app/shared/mocks/mock-angulartics.service.ts new file mode 100644 index 0000000000..99a8b96b22 --- /dev/null +++ b/src/app/shared/mocks/mock-angulartics.service.ts @@ -0,0 +1,4 @@ +/* tslint:disable:no-empty */ +export class AngularticsMock { + public eventTrack(action, properties) { } +} diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts index bd04f9d3a7..e85f67b4ab 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/global-config.interface.ts @@ -9,6 +9,7 @@ export interface GlobalConfig extends Config { production: boolean; cache: CacheConfig; universal: UniversalConfig; + gaTrackingId: string; logDirectory: string; debug: boolean; } diff --git a/src/main.browser.ts b/src/main.browser.ts index ebb9b56169..8409a96485 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -6,7 +6,7 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { bootloader } from '@angularclass/bootloader'; import { load as loadWebFont } from 'webfontloader'; -import { hasValue } from './app/shared/empty.util'; +import { hasValue, isNotEmpty } from './app/shared/empty.util'; import { BrowserAppModule } from './modules/app/browser-app.module'; @@ -25,9 +25,25 @@ export function main() { } }); + addGoogleAnalytics(); + return platformBrowserDynamic().bootstrapModule(BrowserAppModule); } +function addGoogleAnalytics() { + // Add google analytics if key is present in config + const trackingId = ENV_CONFIG.gaTrackingId; + if (isNotEmpty(trackingId)) { + const keyScript = document.createElement('script'); + keyScript.innerHTML = `(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');` + + 'ga(\'create\', \'' + ENV_CONFIG.gaTrackingId + '\', \'auto\');'; + document.body.appendChild(keyScript); + } +} + // support async tag or hmr if (hasValue(ENV_CONFIG.universal) && ENV_CONFIG.universal.preboot === false) { bootloader(main); diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index df77dad74f..1112ebdb23 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -14,6 +14,8 @@ import { AppComponent } from '../../app/app.component'; import { AppModule } from '../../app/app.module'; import { DSpaceBrowserTransferStateModule } from '../transfer-state/dspace-browser-transfer-state.module'; import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service'; +import { Angulartics2Module } from 'angulartics2'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); @@ -34,6 +36,7 @@ export function createTranslateLoader(http: HttpClient) { preloadingStrategy: IdlePreload }), + Angulartics2Module.forRoot([Angulartics2GoogleAnalytics]), BrowserAnimationsModule, DSpaceBrowserTransferStateModule, TranslateModule.forRoot({ diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index ff8455cca0..fac1b63ada 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -16,6 +16,9 @@ import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.ser import { TranslateUniversalLoader } from '../translate-universal-loader'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; +import { AngularticsMock } from '../../app/shared/mocks/mock-angulartics.service'; + export function createTranslateLoader() { return new TranslateUniversalLoader('dist/assets/i18n/', '.json'); } @@ -42,6 +45,7 @@ export function createTranslateLoader() { AppModule ], providers: [ + { provide: Angulartics2GoogleAnalytics, useClass: AngularticsMock } ] }) export class ServerAppModule { diff --git a/yarn.lock b/yarn.lock index e6e0aedf55..bb571e8d6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,6 +427,12 @@ angular2-template-loader@0.6.2: dependencies: loader-utils "^0.2.15" +angulartics2@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/angulartics2/-/angulartics2-5.2.0.tgz#5bac82d4b6acf798b7db906488861e70b49fe04c" + dependencies: + tslib "^1.7.1" + ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"