From caa855777d1813b98a85ceea7c6e395147dbe28f Mon Sep 17 00:00:00 2001
From: Markus Scheidgen <markus.scheidgen@gmail.com>
Date: Thu, 18 Jun 2020 17:20:35 +0200
Subject: [PATCH] Adapted consent in GUI for use of matomo. #199

---
 gui/public/env.js                            |   2 +-
 gui/src/components/App.js                    | 135 ++++++++++---------
 gui/src/config.js                            |   8 +-
 gui/src/index.js                             |   8 +-
 ops/helm/nomad/templates/api-deployment.yaml |   2 +-
 ops/helm/nomad/values.yaml                   |   4 +-
 6 files changed, 87 insertions(+), 72 deletions(-)

diff --git a/gui/public/env.js b/gui/public/env.js
index 505a0d4b48..88fc04f4ae 100644
--- a/gui/public/env.js
+++ b/gui/public/env.js
@@ -4,7 +4,7 @@ window.nomadEnv = {
   'keycloakClientId': 'nomad_gui_dev',
   'appBase': 'http://localhost:8000/fairdi/nomad/latest',
   'debug': false,
-  'sendTrackingData': true,
+  'matomoEnabled': true,
   'matomoUrl': 'https://repository.nomad-coe.eu/fairdi/stat',
   'matomoSiteId': '2'
 }
diff --git a/gui/src/components/App.js b/gui/src/components/App.js
index 9b5e8cf726..715d971ade 100644
--- a/gui/src/components/App.js
+++ b/gui/src/components/App.js
@@ -1,13 +1,13 @@
 // trigger rebuild
 
 import React, { useEffect, useState, useContext, useCallback, useRef } from 'react'
-import PropTypes, { instanceOf } from 'prop-types'
+import PropTypes from 'prop-types'
 import { compose } from 'recompose'
 import classNames from 'classnames'
 import { MuiThemeProvider, withStyles, makeStyles } from '@material-ui/core/styles'
 import { LinearProgress, MenuList, Typography,
   AppBar, Toolbar, Button, DialogContent, DialogTitle, DialogActions, Dialog, Tooltip,
-  Snackbar, SnackbarContent } from '@material-ui/core'
+  Snackbar, SnackbarContent, FormGroup, FormControlLabel, Switch } from '@material-ui/core'
 import { Route, Link, withRouter, useLocation } from 'react-router-dom'
 import BackupIcon from '@material-ui/icons/Backup'
 import SearchIcon from '@material-ui/icons/Search'
@@ -17,6 +17,7 @@ import FAQIcon from '@material-ui/icons/QuestionAnswer'
 import MetainfoIcon from '@material-ui/icons/Info'
 import DocIcon from '@material-ui/icons/Help'
 import CodeIcon from '@material-ui/icons/Code'
+import TermsIcon from '@material-ui/icons/Assignment'
 import {help as searchHelp, default as SearchPage} from './search/SearchPage'
 import HelpDialog from './Help'
 import { ApiProvider, withApi, apiContext } from './api'
@@ -27,8 +28,6 @@ import LoginLogout from './LoginLogout'
 import { guiBase, consent, nomadTheme, appBase } from '../config'
 import {help as metainfoHelp, default as MetaInfoBrowser} from './metaInfoBrowser/MetaInfoBrowser'
 import packageJson from '../../package.json'
-import { Cookies, withCookies } from 'react-cookie'
-import Markdown from './Markdown'
 import {help as uploadHelp, default as UploadPage} from './uploads/UploadPage'
 import ResolvePID from './entry/ResolvePID'
 import DatasetPage from './DatasetPage'
@@ -37,6 +36,9 @@ import {help as userdataHelp, default as UserdataPage} from './UserdataPage'
 import ResolveDOI from './dataset/ResolveDOI'
 import FAQ from './FAQ'
 import EntryQuery from './entry/EntryQuery'
+import {matomo} from '../index'
+import { useCookies } from 'react-cookie'
+import Markdown from './Markdown'
 
 export const ScrollContext = React.createContext({scrollParentRef: null})
 
@@ -80,7 +82,7 @@ const useMainMenuItemStyles = makeStyles(theme => ({
   }
 }))
 
-function MainMenuItem({tooltip, title, path, href, icon}) {
+function MainMenuItem({tooltip, title, path, href, onClick, icon}) {
   const {pathname} = useLocation()
   const classes = useMainMenuItemStyles()
   const selected = path === pathname || (path !== '/' && pathname.startsWith(path))
@@ -91,6 +93,7 @@ function MainMenuItem({tooltip, title, path, href, icon}) {
       color={selected ? 'primary' : 'default'}
       size="small"
       startIcon={icon}
+      onClick={onClick}
       {...rest}
     >
       {title}
@@ -102,9 +105,73 @@ MainMenuItem.propTypes = {
   'title': PropTypes.string.isRequired,
   'path': PropTypes.string,
   'href': PropTypes.string,
+  'onClick': PropTypes.func,
   'icon': PropTypes.element.isRequired
 }
 
+function Consent() {
+  const [cookies, setCookie] = useCookies()
+  const [accepted, setAccepted] = useState(cookies['terms-accepted'])
+  const [optOut, setOptOut] = useState(cookies['tracking-enabled'] === 'false')
+
+  useEffect(() => {
+    if (!optOut) {
+      matomo.push(['setConsentGiven'])
+    } else {
+      matomo.push(['requireConsent'])
+    }
+  })
+
+  const handleClosed = accepted => {
+    if (accepted) {
+      setCookie('terms-accepted', true)
+      setCookie('tracking-enabled', !optOut)
+      setAccepted(true)
+    }
+  }
+  const handleOpen = () => {
+    setCookie('terms-accepted', false)
+    setAccepted(false)
+  }
+
+  return (
+    <React.Fragment>
+      <MainMenuItem
+        title="Terms"
+        onClick={handleOpen}
+        tooltip="NOMAD's terms"
+        icon={<TermsIcon/>}
+      />
+      <Dialog
+        disableBackdropClick disableEscapeKeyDown
+        open={!accepted}
+      >
+        <DialogTitle>Terms of Use</DialogTitle>
+        <DialogContent>
+          <Markdown>{consent}</Markdown>
+          <FormGroup>
+            <FormControlLabel
+              control={<Switch
+                checked={optOut}
+                onChange={(e) => {
+                  setOptOut(!optOut)
+                }}
+                color="primary"
+              />}
+              label="Do not provide information about your use of NOMAD (opt-out)."
+            />
+          </FormGroup>
+        </DialogContent>
+        <DialogActions>
+          <Button onClick={() => handleClosed(true)} color="primary">
+            Accept
+          </Button>
+        </DialogActions>
+      </Dialog>
+    </React.Fragment>
+  )
+}
+
 const useMainMenuStyles = makeStyles(theme => ({
   root: {
     display: 'inline-flex',
@@ -186,6 +253,7 @@ function MainMenu() {
       tooltip="NOMAD's Gitlab project"
       icon={<CodeIcon/>}
     />
+    <Consent />
   </MenuList>
 }
 
@@ -378,62 +446,6 @@ class NavigationUnstyled extends React.Component {
 
 const Navigation = compose(withRouter, withErrors, withApi(false), withStyles(NavigationUnstyled.styles))(NavigationUnstyled)
 
-class LicenseAgreementUnstyled extends React.Component {
-  static propTypes = {
-    classes: PropTypes.object.isRequired,
-    cookies: instanceOf(Cookies).isRequired
-  }
-
-  static styles = theme => ({
-    content: {
-      backgroundColor: theme.palette.primary.main
-    },
-    button: {
-      color: 'white'
-    }
-  })
-
-  constructor(props) {
-    super(props)
-
-    this.handleClosed = this.handleClosed.bind(this)
-  }
-
-  state = {
-    accepted: this.props.cookies.get('terms-accepted')
-  }
-
-  handleClosed(accepted) {
-    if (accepted) {
-      this.props.cookies.set('terms-accepted', true)
-      this.setState({accepted: true})
-    }
-  }
-
-  render() {
-    return (
-      <div>
-        <Dialog
-          disableBackdropClick disableEscapeKeyDown
-          open={!this.state.accepted}
-        >
-          <DialogTitle>Terms of Use</DialogTitle>
-          <DialogContent>
-            <Markdown>{consent}</Markdown>
-          </DialogContent>
-          <DialogActions>
-            <Button onClick={() => this.handleClosed(true)} color="primary">
-              Accept
-            </Button>
-          </DialogActions>
-        </Dialog>
-      </div>
-    )
-  }
-}
-
-const LicenseAgreement = compose(withCookies, withStyles(LicenseAgreementUnstyled.styles))(LicenseAgreementUnstyled)
-
 const routes = {
   'about': {
     exact: true,
@@ -518,7 +530,6 @@ class App extends React.PureComponent {
             </Navigation>
           </ApiProvider>
         </ErrorSnacks>
-        <LicenseAgreement />
       </MuiThemeProvider>
     )
   }
diff --git a/gui/src/config.js b/gui/src/config.js
index 756adab388..7b3f7c9f17 100644
--- a/gui/src/config.js
+++ b/gui/src/config.js
@@ -12,7 +12,7 @@ export const keycloakBase = window.nomadEnv.keycloakBase
 export const keycloakRealm = window.nomadEnv.keycloakRealm
 export const keycloakClientId = window.nomadEnv.keycloakClientId
 export const debug = window.nomadEnv.debug || false
-export const sendTrackingData = window.nomadEnv.sendTrackingData
+export const matomoEnabled = window.nomadEnv.matomoEnabled
 export const email = 'webmaster@nomad-coe.eu'
 export const maxLogsToShow = 50
 
@@ -27,8 +27,10 @@ you and users you share your data with. The *embargo period* lasts up to 36 mont
 After the *embargo* your published data will be public. **Note that public data
 is visible to others and files become downloadable by everyone.**
 
-This web-site uses *cookies*. By using this web-site you agree to our use
-of *cookies*. [Learn more](https://www.cookiesandyou.com/).
+This web-site uses *cookies*. We use cookies to track you login status for all NOMAD services
+and optionally to store information about your use of NOMAD. None of this information is
+shared with other parties. By using this web-site you agree to the described use of *cookies*.
+[Learn more](https://www.cookiesandyou.com/).
 `
 export const nomadPrimaryColor = {
   main: '#008DC3',
diff --git a/gui/src/index.js b/gui/src/index.js
index f3e72b28f4..e204c5f483 100644
--- a/gui/src/index.js
+++ b/gui/src/index.js
@@ -8,12 +8,12 @@ import { Router, Route } from 'react-router-dom'
 import { QueryParamProvider } from 'use-query-params'
 import history from './history'
 import PiwikReactRouter from 'piwik-react-router'
-import { sendTrackingData, matomoUrl, matomoSiteId, keycloakBase, keycloakRealm, keycloakClientId } from './config'
+import { matomoEnabled, matomoUrl, matomoSiteId, keycloakBase, keycloakRealm, keycloakClientId } from './config'
 import Keycloak from 'keycloak-js'
 import { KeycloakProvider } from 'react-keycloak'
 import * as serviceWorker from './serviceWorker'
 
-const matomo = sendTrackingData ? PiwikReactRouter({
+export const matomo = matomoEnabled ? PiwikReactRouter({
   url: matomoUrl,
   siteId: matomoSiteId,
   clientTrackerName: 'stat.js',
@@ -26,9 +26,11 @@ const keycloak = Keycloak({
   clientId: keycloakClientId
 })
 
+// matomo.push('requireConsent')
+
 ReactDOM.render(
   <KeycloakProvider keycloak={keycloak} initConfig={{onLoad: 'check-sso'}} LoadingComponent={<div />}>
-    <Router history={sendTrackingData ? matomo.connectToHistory(history) : history}>
+    <Router history={matomoEnabled ? matomo.connectToHistory(history) : history}>
       <QueryParamProvider ReactRouterRoute={Route}>
         <App />
       </QueryParamProvider>
diff --git a/ops/helm/nomad/templates/api-deployment.yaml b/ops/helm/nomad/templates/api-deployment.yaml
index 156728abbc..b76dc30ad6 100644
--- a/ops/helm/nomad/templates/api-deployment.yaml
+++ b/ops/helm/nomad/templates/api-deployment.yaml
@@ -92,7 +92,7 @@ data:
       "keycloakClientId": "{{ .Values.keycloak.guiClientId }}",
       "matomoSiteId": {{ .Values.gui.matomoSiteId }},
       "matomoUrl": "{{ .Values.gui.matomoUrl }}",
-      "sendTrackingData": {{ .Values.gui.sendTrackingData }},
+      "matomoEnabled": {{ .Values.gui.matomoEnabled }},
       "debug": {{ .Values.gui.debug }}
     };
 ---
diff --git a/ops/helm/nomad/values.yaml b/ops/helm/nomad/values.yaml
index 47ff4191fa..65051d5653 100644
--- a/ops/helm/nomad/values.yaml
+++ b/ops/helm/nomad/values.yaml
@@ -68,11 +68,11 @@ gui:
   ## This variable is used in the GUI to show or hide additional information
   debug: false
   ## URL for matomo(piwik) user tracking
-  matomoUrl: 'http://nowhere.no'
+  matomoUrl: 'https://repository.nomad-coe.eu/fairdi/stat'
   ## site id for matomo(piwik) user tracking
   matomoSiteId: 1
   ## send matomo(piwik) user tracking data
-  sendTrackingData: false
+  matomoEnabled: false
 
 ## Everything concerning the nginx that serves the gui, proxies the api
 #  It is run via NodePort service
-- 
GitLab