diff --git a/deploy/csp/index.js b/deploy/csp/index.js index 0aab2676c279c4bdb3c9eccf5a22528066692787..546a3758ab73c16da38d045f204289ac90dd86d0 100644 --- a/deploy/csp/index.js +++ b/deploy/csp/index.js @@ -35,7 +35,9 @@ try { const defaultAllowedSites = [ "'self'", '*.apps.hbp.eu', - '*.apps-dev.hbp.eu' + '*.apps-dev.hbp.eu', + 'stats.humanbrainproject.eu', + 'stats-dev.humanbrainproject.eu' ] const dataSource = [ diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 7e07c0079a961e828fd75351add23bb1a3038cdf..ff229e7b66b4005c181f4b52562fcb34942917a0 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -23,7 +23,7 @@ <!-- cookie --> <ng-template #cookieAgreementComponent> - <h2 mat-dialog-title>Cookie Disclaimer</h2> + <h2 mat-dialog-title>Privacy Policy</h2> <mat-dialog-content> <small> <cookie-agreement> diff --git a/src/components/markdown/markdown.component.ts b/src/components/markdown/markdown.component.ts index 1e3d918f15280ff9b75c348aa9319da6550ae755..35f53517540acced1613864dc71be4faa2570744 100644 --- a/src/components/markdown/markdown.component.ts +++ b/src/components/markdown/markdown.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnChanges, OnInit, ViewChild } from '@angular/core' +import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, AfterViewChecked } from '@angular/core' import * as showdown from 'showdown' @Component({ @@ -10,24 +10,29 @@ import * as showdown from 'showdown' changeDetection : ChangeDetectionStrategy.OnPush, }) -export class MarkdownDom implements OnChanges, OnInit { +export class MarkdownDom implements AfterViewChecked { @Input() public markdown: string = `` public innerHtml: string = `` private converter = new showdown.Converter() - constructor() { + constructor( + private cdr: ChangeDetectorRef + ) { this.converter.setFlavor('github') } - public ngOnChanges() { - this.innerHtml = this.converter.makeHtml(this.markdown) + private getMarkdown(){ + if (this.markdown) return this.markdown + if (this.contentWrapper.nativeElement.innerHTML.replace(/\w|\n/g, '') !== '') return this.contentWrapper.nativeElement.innerHTML + return '' } - public ngOnInit() { - if (this.contentWrapper.nativeElement.innerHTML.replace(/\w|\n/g, '') !== '') { - this.innerHtml = this.converter.makeHtml(this.contentWrapper.nativeElement.innerHTML) - } + public ngAfterViewChecked(){ + this.innerHtml = this.converter.makeHtml( + this.getMarkdown() + ) + this.cdr.markForCheck() } @ViewChild('ngContentWrapper', {read : ElementRef, static: true}) diff --git a/src/components/markdown/markdown.template.html b/src/components/markdown/markdown.template.html index bb29d3be594d16c2b49a920a81a81fccec40c6ae..f4a643338901034ec807c46393c68e4c7b8b29df 100644 --- a/src/components/markdown/markdown.template.html +++ b/src/components/markdown/markdown.template.html @@ -1,7 +1,6 @@ -<div [innerHTML] = "innerHtml | safeHtml"> - +<div [innerHTML]="innerHtml | safeHtml"> </div> -<div class = "hidden" #ngContentWrapper> +<div class="d-none" #ngContentWrapper> <ng-content> </ng-content> diff --git a/src/main-aot.ts b/src/main-aot.ts index 292c911a660728e31f71cfd75a42f899c9fe6bec..69a43c88abeb40bca199d80e29c449aa1469808d 100644 --- a/src/main-aot.ts +++ b/src/main-aot.ts @@ -1 +1,19 @@ import './main-common' +if (MATAMO_URL && MATAMO_ID) { + const _paq = window['_paq'] || []; + /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ + + // See DNT header and not counting visitor: https://github.com/matomo-org/matomo/issues/12001 + // Should also use main.js numbers to find number of actual visitors + _paq.push(["setDoNotTrack", true]); + + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); + (function() { + const u=MATAMO_URL; + _paq.push(['setTrackerUrl', u+'matomo.php']); + _paq.push(['setSiteId', MATAMO_ID]); + const d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); + })(); +} \ No newline at end of file diff --git a/src/ui/cookieAgreement/cookieAgreement.component.ts b/src/ui/cookieAgreement/cookieAgreement.component.ts index 0ec8787459f39e26d4ee887ef9e64568f2e2866f..6a74d27389fc399065ee709a3f1766a7ec546c3e 100644 --- a/src/ui/cookieAgreement/cookieAgreement.component.ts +++ b/src/ui/cookieAgreement/cookieAgreement.component.ts @@ -1,4 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' +import info from '!!raw-loader!./data/info.md' +import readmore from '!!raw-loader!./data/readmore.md' +import matomoInfo from '!!raw-loader!./data/aboutMatomo.md' @Component({ selector: 'cookie-agreement', @@ -7,7 +10,23 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' './cookieAgreement.style.css', ], changeDetection: ChangeDetectionStrategy.OnPush, + preserveWhitespaces: true }) export class CookieAgreement { public showMore: boolean = false + public showMamoto: boolean = false + + public matomoUrl: string = MATAMO_URL + + public markdownInfo: string = info + public markdownReadmore: string = readmore + public matomoInfo: string = matomoInfo + + public optOutUrl: string + + constructor(){ + if (this.matomoUrl) { + this.optOutUrl = `${this.matomoUrl}index.php?module=CoreAdminHome&action=optOut&language=en` + } + } } diff --git a/src/ui/cookieAgreement/cookieAgreement.style.css b/src/ui/cookieAgreement/cookieAgreement.style.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..598236cc6692ef607823535c8bd16b786761c8db 100644 --- a/src/ui/cookieAgreement/cookieAgreement.style.css +++ b/src/ui/cookieAgreement/cookieAgreement.style.css @@ -0,0 +1,24 @@ +:host +{ + display: block; + margin-bottom:1rem; +} + +.expansion-panel .mat-expansion-panel-header-title, +.expansion-panel .mat-expansion-panel-header-description +{ + flex-basis: 0; +} + +.expansion-panel .mat-expansion-panel-header-description +{ + justify-content: space-between; + align-items: center; +} + +iframe +{ + width: 100%; + padding: 1rem; + height: 20rem; +} diff --git a/src/ui/cookieAgreement/cookieAgreement.template.html b/src/ui/cookieAgreement/cookieAgreement.template.html index b92a7d8f8c7d592823ad9b7fcd4ff198685f1c1e..1b85496e96fb7b2200ed0dbbcabf008a8009e807 100644 --- a/src/ui/cookieAgreement/cookieAgreement.template.html +++ b/src/ui/cookieAgreement/cookieAgreement.template.html @@ -1,116 +1,46 @@ -<div> - <p>The HBP Atlas Viewer uses ‘cookies’ (text files placed on your computer) to verify login details, - remember user choices and preferences, and in some instances determine site permissions. Cookies also provide, - in anonymous form, the number of visitors accessing the HBP Public Website, features users access during website - visits, and the general location of the user based on IP address.</p> - <p>HBP Atlas Viewer uses at least the following cookies:</p> - <p><strong>connect.sid</strong> : verify login details</p> - <p>To find out more about cookies, including how to determine the cookies that have been set on - your computer and how to manage - or delete them, visit <a href="www.aboutcookies.org">www.aboutcookies.org </a> .</p> - <p>To opt-out of being tracked by Google Analytics across all websites, visit - <a href="http://tools.google.com/dlpage/gaoptout">http://tools.google.com/dlpage/gaoptout</a> .</p> +<markdown-dom [markdown]="markdownInfo"> +</markdown-dom> - <div class="d-flex"> +<!-- additional information --> +<mat-accordion class="expansion-panel"> - <button - mat-stroked-button - color="primary" - class="d-flex justify-content-center flex-grow-1" - (click)="showMore = !showMore"> - Show {{showMore? "less" : "more"}} - </button> - - </div> + <!-- IAV specific --> + <mat-expansion-panel> - <div *ngIf="showMore"> - <small> - - <div> - <strong>Data controller(s):</strong> - <span> - HBP Atlas Viewer is the data controller for your login information. - </span> - </div> - - <div> - <strong>List of partners responsible:</strong> - <span> - FZJ-INM1 <a href="mailto:inm1-bda@fz-juelich.de">inm1-bda@fz-juelich.de</a> - </span> - </div> - - <div> - <strong>HBP Data Protection Officer (HBP DPO):</strong> - <span> - You can find out about HBP Data Protection policies here: - <a href="https://www.humanbrainproject.eu/en/social-ethical-reflective/ethics-support/data-protection/"> - https://www.humanbrainproject.eu/en/social-ethical-reflective/ethics-support/data-protection/ - </a> - and you can contact HBP Data Protection Officer at - <a href="mailto:data.protection@humanbrainproject.eu"> - data.protection@humanbrainproject.eu - </a> - </span> - </div> - - <div> - <strong>Legal basis for data processing:</strong> - <span> - consent - </span> - </div> - - <div> - <strong>General categories of personal data collected:</strong> - <span> - Name, ORC ID, JWT ID Token - </span> - </div> - - <div> - <strong>Data shared within the HBP Consortium:</strong> - <span> - JWT ID Token is shared with ChunMa (Chunk Master), enabling upload and retrieval of user uploaded data. - </span> - </div> - - <div> - <strong>Data Shared with Third Parties:</strong> - <span>none</span> - </div> - - <div> - <strong>Transfer of any personal data to third countries:</strong> - <span> - Data are stored in HBP infrastructure, and will not be transferred to Third countries. - </span> - </div> - - <div> - <strong>Retention periods:</strong> - <div> - 24 hours - </div> - </div> - - <div> - <strong>Lodging a complaint:</strong> - <span> - The HBP DPO and HBP partners will make every reasonable effort to address your data protection concerns. - However, you have a right to lodge a complaint with a data protection authority. Contact information for the - European Data Protection Board and EU DPAs are available here: - <ul> - <li><a - href="http://ec.europa.eu/newsroom/article29/item-detail.cfm?item_id=612080">http://ec.europa.eu/newsroom/article29/item-detail.cfm?item_id=612080</a> - </li> - <li data-v-fc2c56ac=""><a data-v-fc2c56ac="" - href="https://edpb.europa.eu/about-edpb/board/members_en">https://edpb.europa.eu/about-edpb/board/members_en</a> - </li> - </ul> - </span> - </div> + <mat-expansion-panel-header> + <mat-panel-title> + About IAV + </mat-panel-title> + <mat-panel-description> + More details + </mat-panel-description> + </mat-expansion-panel-header> + <small> + <markdown-dom [markdown]="markdownReadmore"> + </markdown-dom> </small> - </div> -</div> + </mat-expansion-panel> + + <!-- matomo specific --> + <mat-expansion-panel *ngIf="matomoUrl"> + <mat-expansion-panel-header> + <mat-panel-title> + About Matomo + </mat-panel-title> + <mat-panel-description> + Usage tracking + </mat-panel-description> + </mat-expansion-panel-header> + <ng-container *ngTemplateOutlet="matomoReadMore"> + </ng-container> + </mat-expansion-panel> + +</mat-accordion> + +<!-- template for matomo --> +<ng-template #matomoReadMore> + <markdown-dom [markdown]="matomoInfo"> + </markdown-dom> + <iframe [src]="optOutUrl | safeResource"></iframe> +</ng-template> \ No newline at end of file diff --git a/src/ui/cookieAgreement/data/aboutMatomo.md b/src/ui/cookieAgreement/data/aboutMatomo.md new file mode 100644 index 0000000000000000000000000000000000000000..502b845a046adef8d0a3c8d97a78e65c987d7438 --- /dev/null +++ b/src/ui/cookieAgreement/data/aboutMatomo.md @@ -0,0 +1,5 @@ +Interactive atlas viewer uses opensource <https://matomo.org/> hosted on HBP infrastructure to track usage statistics. + +**do not track**: if you enable [do not track](https://en.wikipedia.org/wiki/Do_Not_Track), matomo is configured to not track you on this site. + +You can opt out of tracking manually below. \ No newline at end of file diff --git a/src/ui/cookieAgreement/data/info.md b/src/ui/cookieAgreement/data/info.md new file mode 100644 index 0000000000000000000000000000000000000000..51623e9b4e8a9ea1cdf3b4fb2d3b1620142484d8 --- /dev/null +++ b/src/ui/cookieAgreement/data/info.md @@ -0,0 +1,7 @@ +The HBP Interactive Atlas Viewer (IAV) uses ‘cookies’ (text files placed on your computer) to verify login details, remember user choices and preferences, and in some instances determine site permissions. Cookies also provide, in anonymous form, the number of visitors accessing the HBP Public Website, features users access during website visits, and the general location of the user based on IP address. + +HBP Atlas Viewer uses at least the following cookies: + +- **connect.sid** verify login details + +To find out more about cookies, including how to determine the cookies that have been set on your computer and how to manage or delete them, visit <www.aboutcookies.org> \ No newline at end of file diff --git a/src/ui/cookieAgreement/data/readmore.md b/src/ui/cookieAgreement/data/readmore.md new file mode 100644 index 0000000000000000000000000000000000000000..243cfe802f6e3ea3bef24c0238b5e6c12308e54a --- /dev/null +++ b/src/ui/cookieAgreement/data/readmore.md @@ -0,0 +1,12 @@ +**Data controller(s)**: HBP Atlas Viewer is the data controller for your login information. +**List of partners responsible**: HBP Atlas Viewer is the data controller for your login information. +**HBP Data Protection Officer (HBP DPO)**: FZJ-INM1 <inm1-bda@fz-juelich.de> +**Legal basis for data processing**: You can find out about HBP Data Protection policies here: <https://www.humanbrainproject.eu/en/social-ethical-reflective/ethics-support/data-protection/> and you can contact HBP Data Protection Officer at <data.protection@humanbrainproject.eu> +**General categories of personal data collected**: consent +**Data shared within the HBP Consortium**: Name, ORC ID, JWT ID Token +**Data Shared with Third Parties**: JWT ID Token is shared with ChunMa (Chunk Master), enabling upload and retrieval of user uploaded data. +**Transfer of any personal data to third countries**: None +**Retention periods**: 24 hours +**Lodging a complaint**: The HBP DPO and HBP partners will make every reasonable effort to address your data protection concerns. However, you have a right to lodge a complaint with a data protection authority. Contact information for the European Data Protection Board and EU DPAs are available here: +- <http://ec.europa.eu/newsroom/article29/item-detail.cfm?item_id=612080> +- <https://edpb.europa.eu/about-edpb/board/members_en> \ No newline at end of file diff --git a/src/util/pipes/safeHtml.pipe.ts b/src/util/pipes/safeHtml.pipe.ts index 73b69a7afbf648cfef4e1d2442e93095e3e00e92..272c1c31623f99ab9168366be311af4b8bbc8d6b 100644 --- a/src/util/pipes/safeHtml.pipe.ts +++ b/src/util/pipes/safeHtml.pipe.ts @@ -1,12 +1,15 @@ import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Pipe({ name : 'safeHtml', }) export class SafeHtmlPipe implements PipeTransform { - public transform(html: string): string { - return html - // return this.ds.bypassSecurityTrustHtml(html) + constructor(private ds: DomSanitizer){ + + } + public transform(html: string): SafeHtml { + return this.ds.bypassSecurityTrustHtml(html) } } diff --git a/src/util/pipes/safeResource.pipe.ts b/src/util/pipes/safeResource.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce977d0df070e52c0c5312e774cf53bc4b8fd984 --- /dev/null +++ b/src/util/pipes/safeResource.pipe.ts @@ -0,0 +1,18 @@ +import { Pipe, PipeTransform } from "@angular/core"; +import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; + +@Pipe({ + name: 'safeResource' +}) + +export class SafeResourcePipe implements PipeTransform{ + constructor( + private ds: DomSanitizer + ){ + + } + + transform(input: string): SafeResourceUrl{ + return this.ds.bypassSecurityTrustResourceUrl(input) + } +} \ No newline at end of file diff --git a/src/util/util.module.ts b/src/util/util.module.ts index 8507cd6a04c54d25eea4bc80aae81bffa595b75f..5594549241c4bf0c48f0c95ad07813d6875717af 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -6,6 +6,7 @@ import { MouseHoverDirective, MouseOverIconPipe, MouseOverTextPipe } from "./dir import { StopPropagationDirective } from "./directives/stopPropagation.directive"; import { FilterNullPipe } from "./pipes/filterNull.pipe"; import { IncludesPipe } from "./pipes/includes.pipe"; +import { SafeResourcePipe } from "./pipes/safeResource.pipe"; @NgModule({ declarations: [ @@ -18,6 +19,7 @@ import { IncludesPipe } from "./pipes/includes.pipe"; MouseOverIconPipe, KeyListner, IncludesPipe, + SafeResourcePipe, ], exports: [ FilterNullPipe, @@ -29,6 +31,7 @@ import { IncludesPipe } from "./pipes/includes.pipe"; MouseOverIconPipe, KeyListner, IncludesPipe, + SafeResourcePipe, ], }) diff --git a/typings/index.d.ts b/typings/index.d.ts index cf1b4e30fca132c62ee8261a800088f789b392e1..04b3413e197ef93f6f27f42f10436bbbc45c91e8 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -8,6 +8,8 @@ declare module '*.css' { export = contents } +declare module '*.md' + declare var PLUGINDEV : string declare var BUNDLEDPLUGINS : string[] declare var VERSION : string @@ -15,3 +17,5 @@ declare var PRODUCTION: boolean declare var BACKEND_URL: string declare var USE_LOGO: string declare var DATASET_PREVIEW_URL: string +declare var MATAMO_URL: string +declare var MATAMO_ID: string diff --git a/webpack.staticassets.js b/webpack.staticassets.js index eb5271a3c176e8783ed3107745f8531ab31097f8..0186436a5746b10c26781643a987899f27ea644b 100644 --- a/webpack.staticassets.js +++ b/webpack.staticassets.js @@ -71,7 +71,9 @@ module.exports = { : false, BACKEND_URL: (process.env.BACKEND_URL && JSON.stringify(process.env.BACKEND_URL)) || 'null', USE_LOGO: JSON.stringify(process.env.USE_LOGO || 'hbp' || 'ebrains' ), - DATASET_PREVIEW_URL: JSON.stringify(process.env.DATASET_PREVIEW_URL || 'https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview') + DATASET_PREVIEW_URL: JSON.stringify(process.env.DATASET_PREVIEW_URL || 'https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview'), + MATAMO_URL: JSON.stringify(process.env.MATAMO_URL || null), + MATAMO_ID: JSON.stringify(process.env.MATAMO_ID || null) }) ], resolve: {