From e97679859634d411d66cc49025738102c63795bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Janis=20Daniel=20Da=CC=88hne?=
 <janis.daehne2@student.uni-halle.de>
Date: Mon, 30 Sep 2019 14:38:48 +0200
Subject: [PATCH] - added feature to download personal data  data

---
 i18n/en.ts                                    |  3 +
 i18n/i18nRoot.ts                              |  3 +
 .../dialogs/changeUserDataDialog.tsx          | 31 ++++++-
 .../dialogs/changeUserDataView.tsx            |  3 +-
 .../ownSettingsSite/miscSettingsView.tsx      | 17 ++++
 .../ownInformationSettingsView.tsx            |  2 +-
 .../dialogs/changeSystemRoleView.tsx          | 81 +++++++++++--------
 .../systemRolesSite/systemRolesCardsView.tsx  |  2 +
 src/constants.ts                              | 54 +++++++++----
 .../dialog/systemRoleSetActions.ts            | 32 ++++++--
 .../changeSystemRoleDIalogActionTypes.ts      |  1 +
 .../dialog/changeSystemRolesDialogReducer.ts  |  1 +
 .../dialog/systemRoleReducer.ts               | 14 +++-
 .../dialog/systemRoleReducerValidation.ts     |  5 +-
 src/types/userData.ts                         |  7 +-
 15 files changed, 190 insertions(+), 66 deletions(-)

diff --git a/i18n/en.ts b/i18n/en.ts
index 179f804d..fc4f55ec 100644
--- a/i18n/en.ts
+++ b/i18n/en.ts
@@ -64,6 +64,8 @@ export const lang_en: LangObj = {
   "Owner" : "Owner",
   "Owner was not loaded, id:" : "Owner was not loaded, id:",
   "Privacy policy" : "Privacy policy",
+  "Download personal data" : "Download personal data",
+  "This will download all your personal data that we store into a zip file. This can take some time!" : "This will download all your personal data that we store into a zip file. This can take some time!",
   "Copyright page" : "Copyright page",
   "About page" : "About page",
 
@@ -536,6 +538,7 @@ export const lang_en: LangObj = {
   "Can change system settings": "Can change system settings",
   "Can manage tags": "Can manage tags",
   "Can view dashboard": "Can view dashboard",
+  "Can download personal data from others" : "Can download personal data from others",
 
   //tutorViewSite
   "Save manual assessment": "Save manual assessment",
diff --git a/i18n/i18nRoot.ts b/i18n/i18nRoot.ts
index 0c04761f..069fe7cb 100644
--- a/i18n/i18nRoot.ts
+++ b/i18n/i18nRoot.ts
@@ -67,6 +67,8 @@ export interface LangObj {
   "Owner": string
   "Owner was not loaded, id:": string
   "Privacy policy": string
+  "Download personal data": string
+  "This will download all your personal data that we store into a zip file. This can take some time!": string
   "Copyright page": string
   "About page": string
 
@@ -547,6 +549,7 @@ export interface LangObj {
   "Can change system settings": string
   "Can manage tags": string
   "Can view dashboard": string
+  "Can download personal data from others": string
 
   //tutorViewSite
   "Save manual assessment": string
diff --git a/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataDialog.tsx b/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataDialog.tsx
index b84a74cb..1861adfe 100644
--- a/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataDialog.tsx
+++ b/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataDialog.tsx
@@ -8,7 +8,7 @@ import {bindActionCreators, Dispatch} from "redux";
 import {returntypeof} from 'react-redux-typescript';
 import {RootState} from "../../../../state/reducers";
 import ChangeUserDataView from './changeUserDataView'
-import {Divider} from "semantic-ui-react";
+import {Divider, Icon} from "semantic-ui-react";
 import SaveButton from "../../../buttons/saveButton";
 import CancelButton from "../../../buttons/cancelButton";
 import {setIsChangeUserDataDialogDisplayed} from "../../../../state/actions/manageActivatedUsersSite/manageActivatedUsersSiteActions";
@@ -20,6 +20,8 @@ import Spinner from "../../../helpers/spinner";
 import {getI18n} from "../../../../../i18n/i18nRoot";
 import Logger from '../../../../helpers/logger'
 import {errorDialog} from '../../../../helpers/dialogHelper'
+import {getPersonalDataDownloadLinkPart} from '../../../../constants'
+import {HelpPopup} from '../../../helpers/helpPopup'
 
 //const css = require('./styles.styl');
 
@@ -36,6 +38,10 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
     userData: rootState.changeActivatedUsersDataDialogState.userData,
     systemRoles: rootState.manageActivatedUsersState.systemRoles,
     currentUserId: rootState.userDataSettingsSate.userData.id,
+    currentUserSystemRole: rootState.userDataSettingsSate.userData
+                           && rootState.userDataSettingsSate.userData.systemRole
+                           ? rootState.userDataSettingsSate.userData.systemRole
+                           : null,
     langId: rootState.i18nState.langId
   }
 }
@@ -63,7 +69,8 @@ class ChangeUserDataDialog extends React.Component<Props, any> {
         }
 
         <div className="view-padding">
-          <div>
+          <div className="view-head-line">
+
             <div className="view-caption">
               <h2>
                 {
@@ -71,6 +78,26 @@ class ChangeUserDataDialog extends React.Component<Props, any> {
                 }
               </h2>
             </div>
+
+            <div className="view-options">
+
+              {
+                this.props.currentUserSystemRole && this.props.currentUserSystemRole.canDownloadPersonalDataFromOthers &&
+                <div className="flexed view-head-line-centered mar-left">
+                  <a href={`${getPersonalDataDownloadLinkPart(this.props.userData.id)}`} download target="_blank">
+                    <div className="clickable">
+                      <Icon name="download"/>
+                      <span>
+                      {
+                        getI18n(this.props.langId, 'Download personal data')
+                      }
+                    </span>
+                    </div>
+                  </a>
+                </div>
+              }
+
+            </div>
           </div>
 
           <div className="view-content">
diff --git a/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataView.tsx b/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataView.tsx
index 24592e0d..f4dd6db9 100644
--- a/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataView.tsx
+++ b/src/components/sites/manageActivatedUsersSite/dialogs/changeUserDataView.tsx
@@ -44,7 +44,8 @@ export const noneSystemRole: SystemRoleFullBase = {
   canChangeUserData: false,
   canChangeSystemSettings: false,
   canManageTags: false,
-  canViewDashboard: false
+  canViewDashboard: false,
+  canDownloadPersonalDataFromOthers: false,
 }
 
 export interface MyProps {
diff --git a/src/components/sites/ownSettingsSite/miscSettingsView.tsx b/src/components/sites/ownSettingsSite/miscSettingsView.tsx
index 3f745a99..a9a4d4fb 100644
--- a/src/components/sites/ownSettingsSite/miscSettingsView.tsx
+++ b/src/components/sites/ownSettingsSite/miscSettingsView.tsx
@@ -22,6 +22,9 @@ import {LangFullBase} from "../../../types/languages";
 
 import {getEditorInternalThemeName} from "../../../helpers/themes";
 import {getI18n} from "../../../../i18n/i18nRoot";
+import {debugOriginUrl} from '../../../../debug'
+import {HelpPopup} from '../../helpers/helpPopup'
+import {getPersonalDataDownloadLinkPart} from '../../../constants'
 
 //const css = require('./styles.styl');
 
@@ -154,6 +157,20 @@ class MiscSettingsView extends React.Component<Props, any> {
               </div>}
             </div>
 
+            <div className="flexed view-head-line-centered mar-left">
+              <a href={`${getPersonalDataDownloadLinkPart(null)}`} download target="_blank">
+                <div className="clickable">
+                  <Icon name="download"/>
+                  <span>
+                      {
+                        getI18n(this.props.langId, 'Download personal data')
+                      }
+                    </span>
+                </div>
+              </a>
+              <HelpPopup className="mar-left-half" defaultText={getI18n(this.props.langId, "This will download all your personal data that we store into a zip file. This can take some time!")} />
+            </div>
+
           </div>
 
           <div className="view-content">
diff --git a/src/components/sites/ownSettingsSite/ownInformationSettingsView.tsx b/src/components/sites/ownSettingsSite/ownInformationSettingsView.tsx
index e9700344..2f667733 100644
--- a/src/components/sites/ownSettingsSite/ownInformationSettingsView.tsx
+++ b/src/components/sites/ownSettingsSite/ownInformationSettingsView.tsx
@@ -43,7 +43,7 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
     systemRoleDisplayName: rootState.userDataSettingsSate.userData.systemRole
                            ? rootState.userDataSettingsSate.userData.systemRole.displayName
                            : null,
-    isLoading: rootState.userDataSettingsSate.isLoadingUserData,
+    isLoading: rootState.userDataSettingsSate.isLoadingUserData || rootState.langsState.isLoading,
     changeState: rootState.userDataSettingsSate.userDataChangeState,
     userDataCopy: rootState.userDataSettingsSate.userDataCopy,
     langId: rootState.i18nState.langId
diff --git a/src/components/sites/systemRolesSite/dialogs/changeSystemRoleView.tsx b/src/components/sites/systemRolesSite/dialogs/changeSystemRoleView.tsx
index 6e7e0eaf..0224f7d3 100644
--- a/src/components/sites/systemRolesSite/dialogs/changeSystemRoleView.tsx
+++ b/src/components/sites/systemRolesSite/dialogs/changeSystemRoleView.tsx
@@ -18,7 +18,7 @@ import {
   setCanCreateRoles,
   setCanDeleteActivatedUsers,
   setCanDeleteGroups,
-  setCanDeleteRoles,
+  setCanDeleteRoles, setCanDownloadPersonalDataFromOthers,
   setCanManageNewUsers,
   setCanManageTags,
   setCanViewDashboard,
@@ -50,22 +50,23 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => {
 }
 
 const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({
-  //imported reducer funcs here
-  setDisplayName,
-  setEmail,
-  setCanCreateGroups,
-  setCanDeleteGroups,
-  setCanCreateRoles,
-  setCanChangeRoles,
-  setCanDeleteRoles,
-  setCanManageNewUsers,
-  setCanChangeOtherUsersSystemRole,
-  setCanDeleteActivatedUsers,
-  setCanChangeUserData,
-  setCanChangeSystemSettings,
-  setCanManageTags,
-  setCanViewDashboard
-}, dispatch)
+                                                                        //imported reducer funcs here
+                                                                        setDisplayName,
+                                                                        setEmail,
+                                                                        setCanCreateGroups,
+                                                                        setCanDeleteGroups,
+                                                                        setCanCreateRoles,
+                                                                        setCanChangeRoles,
+                                                                        setCanDeleteRoles,
+                                                                        setCanManageNewUsers,
+                                                                        setCanChangeOtherUsersSystemRole,
+                                                                        setCanDeleteActivatedUsers,
+                                                                        setCanChangeUserData,
+                                                                        setCanChangeSystemSettings,
+                                                                        setCanManageTags,
+                                                                        setCanViewDashboard,
+                                                                        setCanDownloadPersonalDataFromOthers,
+                                                                      }, dispatch)
 
 
 const stateProps = returntypeof(mapStateToProps);
@@ -82,7 +83,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
             <Form.Field required>
               <label>
                 {
-                  getI18n(this.props.langId,'Name')
+                  getI18n(this.props.langId, 'Name')
                 }
               </label>
               <MaterialInput value={this.props.systemRole.displayName}
@@ -94,7 +95,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
             <Form.Field>
               <label>
                 {
-                  getI18n(this.props.langId,'Email')
+                  getI18n(this.props.langId, 'Email')
                 }
               </label>
               <MaterialInput value={this.props.systemRole.email}
@@ -110,12 +111,12 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
             <Form.Field>
               <h4 className="ui dividing header">
                 {
-                  getI18n(this.props.langId,'Groups')
+                  getI18n(this.props.langId, 'Groups')
                 }
               </h4>
 
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can create groups')}
+                <Checkbox label={getI18n(this.props.langId, 'Can create groups')}
                           checked={this.props.systemRole.canCreateGroups}
                           onChange={(e, data) => {
                             this.props.setCanCreateGroups(data.checked)
@@ -123,7 +124,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can delete groups')}
+                <Checkbox label={getI18n(this.props.langId, 'Can delete groups')}
                           checked={this.props.systemRole.canDeleteGroups}
                           onChange={(e, data) => {
                             this.props.setCanDeleteGroups(data.checked)
@@ -134,12 +135,12 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
             <Form.Field>
               <h4 className="ui dividing header">
                 {
-                  getI18n(this.props.langId,'System roles')
+                  getI18n(this.props.langId, 'System roles')
                 }
               </h4>
 
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can create system & group roles')}
+                <Checkbox label={getI18n(this.props.langId, 'Can create system & group roles')}
                           checked={this.props.systemRole.canCreateRoles}
                           onChange={(e, data) => {
                             this.props.setCanCreateRoles(data.checked)
@@ -147,7 +148,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can change system & group  roles')}
+                <Checkbox label={getI18n(this.props.langId, 'Can change system & group  roles')}
                           checked={this.props.systemRole.canChangeRoles}
                           onChange={(e, data) => {
                             this.props.setCanChangeRoles(data.checked)
@@ -155,7 +156,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can delete system & group  roles')}
+                <Checkbox label={getI18n(this.props.langId, 'Can delete system & group  roles')}
                           checked={this.props.systemRole.canDeleteRoles}
                           onChange={(e, data) => {
                             this.props.setCanDeleteRoles(data.checked)
@@ -168,7 +169,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
 
           <h4 className="ui dividing header">
             {
-              getI18n(this.props.langId,'Miscellaneous')
+              getI18n(this.props.langId, 'Miscellaneous')
             }
           </h4>
           <Form.Group widths='equal'>
@@ -176,7 +177,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
 
             <Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can manage new users')}
+                <Checkbox label={getI18n(this.props.langId, 'Can manage new users')}
                           checked={this.props.systemRole.canManageNewUsers}
                           onChange={(e, data) => {
                             this.props.setCanManageNewUsers(data.checked)
@@ -184,7 +185,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can change other users system role')}
+                <Checkbox label={getI18n(this.props.langId, 'Can change other users system role')}
                           checked={this.props.systemRole.canChangeOtherUsersSystemRole}
                           onChange={(e, data) => {
                             this.props.setCanChangeOtherUsersSystemRole(data.checked)
@@ -192,7 +193,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can delete & view activated users')}
+                <Checkbox label={getI18n(this.props.langId, 'Can delete & view activated users')}
                           checked={this.props.systemRole.canDeleteActivatedUsers}
                           onChange={(e, data) => {
                             this.props.setCanDeleteActivatedUsers(data.checked)
@@ -202,7 +203,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
             </Form.Field>
             <Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can change other user data & view activated users')}
+                <Checkbox label={getI18n(this.props.langId, 'Can change other user data & view activated users')}
                           checked={this.props.systemRole.canChangeUserData}
                           onChange={(e, data) => {
                             this.props.setCanChangeUserData(data.checked)
@@ -210,7 +211,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can change system settings')}
+                <Checkbox label={getI18n(this.props.langId, 'Can change system settings')}
                           checked={this.props.systemRole.canChangeSystemSettings}
                           onChange={(e, data) => {
                             this.props.setCanChangeSystemSettings(data.checked)
@@ -218,7 +219,7 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
                 />
               </Form.Field>
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can manage tags')}
+                <Checkbox label={getI18n(this.props.langId, 'Can manage tags')}
                           checked={this.props.systemRole.canManageTags}
                           onChange={(e, data) => {
                             this.props.setCanManageTags(data.checked)
@@ -227,13 +228,23 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
               </Form.Field>
 
               <Form.Field>
-                <Checkbox label={getI18n(this.props.langId,'Can view dashboard')}
+                <Checkbox label={getI18n(this.props.langId, 'Can view dashboard')}
                           checked={this.props.systemRole.canViewDashboard}
                           onChange={(e, data) => {
                             this.props.setCanViewDashboard(data.checked)
                           }}
                 />
               </Form.Field>
+
+              <Form.Field>
+                <Checkbox label={getI18n(this.props.langId, 'Can download personal data from others')}
+                          checked={this.props.systemRole.canDownloadPersonalDataFromOthers}
+                          onChange={(e, data) => {
+                            this.props.setCanDownloadPersonalDataFromOthers(data.checked)
+                          }}
+                />
+              </Form.Field>
+
             </Form.Field>
           </Form.Group>
         </div>
@@ -242,4 +253,4 @@ class ChangeSystemRoleView extends React.Component<Props, any> {
   }
 }
 
-export default connect(mapStateToProps, mapDispatchToProps)(ChangeSystemRoleView)
\ No newline at end of file
+export default connect(mapStateToProps, mapDispatchToProps)(ChangeSystemRoleView)
diff --git a/src/components/sites/systemRolesSite/systemRolesCardsView.tsx b/src/components/sites/systemRolesSite/systemRolesCardsView.tsx
index 638c9e02..1127c134 100644
--- a/src/components/sites/systemRolesSite/systemRolesCardsView.tsx
+++ b/src/components/sites/systemRolesSite/systemRolesCardsView.tsx
@@ -121,6 +121,8 @@ class SystemRolesCardsView extends React.Component<Props, any> {
                       <Checkbox label={getI18n(this.props.langId, 'Can view dashboard')} disabled
                                 checked={systemRole.canViewDashboard}/>
 
+                      <Checkbox label={getI18n(this.props.langId, 'Can download personal data from others')} disabled
+                                checked={systemRole.canDownloadPersonalDataFromOthers}/>
 
                     </Card.Description>
 
diff --git a/src/constants.ts b/src/constants.ts
index 835ec1c3..b3fed1f7 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -13,7 +13,7 @@ import Logger from './helpers/logger'
  * y - breaking changes / new features
  * z - fixes, small changes
  */
-export const versionString = '2.4.11'
+export const versionString = '2.5.1'
 
 
 export const supportMail = 'yapex@informatik.uni-halle.de'
@@ -29,6 +29,7 @@ export const defaultEditorMode = "text"
  */
 export const tempPrintDivId = 'temp-print-div-id'
 
+declare var safari: any
 /**
  * we need this for printing... safari will not emit onafterprint
  */
@@ -40,7 +41,9 @@ export function browser_isSafari(): boolean {
 
   // Safari 3.0+ "[object HTMLElementConstructor]"
   // @ts-ignore
-  const isSafari = /constructor/i.test(window.HTMLElement) || (function (p: any): any { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
+  const isSafari = /constructor/i.test(window.HTMLElement) || (function (p: any): any {
+    return p.toString() === "[object SafariRemoteNotification]";
+  })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
 
   return isSafari
 }
@@ -48,6 +51,7 @@ export function browser_isSafari(): boolean {
 
 /**
  * when we create files in the editor view this is used to validate file names... should be the same as in backend check
+ * no spaces because they are bad for paths...
  */
 export const validFilenameRegex = /^([a-z0-9_.-]+\.[^.]+)|^([a-z0-9_-]+)$/i
 
@@ -91,6 +95,19 @@ export const privacyPolicyPageLinkPath = '/privacypolicy/privacy.html' //see web
 export const aboutPageLinkPath = '/about/about.html'
 
 
+/**
+ * returns the download personal data link for the given user (id)
+ * @param userId null: to download own personal data, id (>0) to download other users data (requires specific system role permissions)
+ */
+export function getPersonalDataDownloadLinkPart(userId: number | null): string {
+
+  if (userId === null) {
+    return `${debugOriginUrl}/api/users/privacy/download`
+  }
+
+  return `${debugOriginUrl}/api/users/privacy/download/${userId}`
+}
+
 // noinspection TsLint
 /**
  * the interval when editing with some editor (currently only do exercise editors)
@@ -112,17 +129,17 @@ export const tutorFeedbackEditorDebounceInMs = 500
 
 export const paginationPageSizeOptionAll = -1
 // noinspection TsLint
-export const paginationPageSizeOptions = [10,15,25,50,100,200,paginationPageSizeOptionAll] //-1 is all
+export const paginationPageSizeOptions = [10, 15, 25, 50, 100, 200, paginationPageSizeOptionAll] //-1 is all
 
 export function getInitialPaginationData<T>(): PaginationStateData<T> {
-    return {
-      searchText: '',
-      currentPage: 1, //starts with 1
-      isOrderByDesc: false, //ascending
-      orderByProp: null,
-      pageSize: paginationPageSizeOptions[0],
-      totalPages: 0,
-      totalItems: 0
+  return {
+    searchText: '',
+    currentPage: 1, //starts with 1
+    isOrderByDesc: false, //ascending
+    orderByProp: null,
+    pageSize: paginationPageSizeOptions[0],
+    totalPages: 0,
+    totalItems: 0
   }
 }
 
@@ -210,7 +227,9 @@ export function getExerciseReleaseLinkPath(exerciseId: number, comesFromOwnExerc
 
 export const submissionsSiteLinkPath = `${exerciseReleasesLinkPath}/:releaseId/submissions`
 
-export function getSubmissionsSiteToLinkPath(exerciseId: number, comesFromOwnExercisesSite: boolean, exerciseReleaseId: number): string {
+export function getSubmissionsSiteToLinkPath(exerciseId: number, comesFromOwnExercisesSite: boolean,
+                                             exerciseReleaseId: number
+): string {
   return `${exerciseReleasesToLinkPath}/${exerciseId}/${comesFromOwnExercisesSite}/releases/${exerciseReleaseId}/submissions`
 }
 
@@ -218,7 +237,9 @@ export function getSubmissionsSiteToLinkPath(exerciseId: number, comesFromOwnExe
 export const tutorViewSiteLinkPartPath = '/editor/tutor'
 export const tutorViewSiteLinkMatchPath = '/editor/tutor/:comesFromOwnExercisesSite/:releaseId/:userId/:pLangId'
 
-export function getTutorViewForSubmission(comesFromOwnExercisesSite: boolean | null, releaseId: number, userId: number, pLangId: number): string {
+export function getTutorViewForSubmission(comesFromOwnExercisesSite: boolean | null, releaseId: number, userId: number,
+                                          pLangId: number
+): string {
   return `${tutorViewSiteLinkPartPath}/${comesFromOwnExercisesSite}/${releaseId}/${userId}/${pLangId}`
 }
 
@@ -248,12 +269,11 @@ export const manageTagsLinkPath = '/manage/tags'
 export const aboutLinkPath = '/about'
 
 
-
 export const exerciseEditorCreateNewLogoutHint = '/editor/exercises/create'
 export const exerciseEditorCreateNewLinkPath = `${exerciseEditorCreateNewLogoutHint}/:comesFromOwnExercisesSite`
 
 export function getExerciseEditorCreateNewLinkPath(comesFromOwnExercisesSite: boolean): string {
-    return `/editor/exercises/create/${comesFromOwnExercisesSite}`
+  return `/editor/exercises/create/${comesFromOwnExercisesSite}`
 }
 
 
@@ -315,7 +335,9 @@ export function getAllSubmissionDownloadLink(releaseCode: string): string {
 
 export const assessmentResultsDownloadLinkPart = '/api/submissions/export/csv'
 
-export function getAssessmentResultsDownloadLink(releaseCode: string, sortKey: keyof AssessmentFullBase, ascending: boolean): string {
+export function getAssessmentResultsDownloadLink(releaseCode: string, sortKey: keyof AssessmentFullBase,
+                                                 ascending: boolean
+): string {
   return `${debugOriginUrl}${assessmentResultsDownloadLinkPart}/${releaseCode}/${sortKey}/${ascending}`
 }
 
diff --git a/src/state/actions/systemRolesSite/dialog/systemRoleSetActions.ts b/src/state/actions/systemRolesSite/dialog/systemRoleSetActions.ts
index 0794d9f6..2824d533 100644
--- a/src/state/actions/systemRolesSite/dialog/systemRoleSetActions.ts
+++ b/src/state/actions/systemRolesSite/dialog/systemRoleSetActions.ts
@@ -2,13 +2,24 @@
  * Created by janis dähne.
  */
 import {
-  SET_idAction, SET_displayNameAction,
-  SET_emailAction, SET_canCreateGroupsAction, SET_canDeleteGroupsAction,
-  SET_canCreateRolesAction, SET_canChangeRolesAction, SET_canDeleteRolesAction, SET_canManageNewUsersAction,
-  SET_canChangeOtherUsersSystemRoleAction, SET_canDeleteActivatedUsersAction, SET_canChangeUserDataAction,
-  SET_canChangeSystemSettingsAction, SET_canManageTagsAction, SET_canViewDashboardAction
+  SET_idAction,
+  SET_displayNameAction,
+  SET_emailAction,
+  SET_canCreateGroupsAction,
+  SET_canDeleteGroupsAction,
+  SET_canCreateRolesAction,
+  SET_canChangeRolesAction,
+  SET_canDeleteRolesAction,
+  SET_canManageNewUsersAction,
+  SET_canChangeOtherUsersSystemRoleAction,
+  SET_canDeleteActivatedUsersAction,
+  SET_canChangeUserDataAction,
+  SET_canChangeSystemSettingsAction,
+  SET_canManageTagsAction,
+  SET_canViewDashboardAction,
+  SET_canDownloadPersonalDataFromOthersAction
 } from "../../../reducers/systemRolesSite/dialog/systemRoleReducer";
-import {ActionType} from "../../../reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes";
+import {ActionBase, ActionType} from "../../../reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes";
 import {SystemRoleFullBase} from "../../../../types/userData";
 import {ThunkAction} from "redux-thunk";
 import {MultiActions} from "../../types";
@@ -141,4 +152,11 @@ export function setCanViewDashboard(can: boolean): SET_canViewDashboardAction {
     type: ActionType.SET_canViewDashboard,
     can
   }
-}
\ No newline at end of file
+}
+
+export function setCanDownloadPersonalDataFromOthers(can: boolean): SET_canDownloadPersonalDataFromOthersAction {
+  return {
+    type: ActionType.SET_canDownloadPersonalDataFromOthers,
+    can
+  }
+}
diff --git a/src/state/reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes.ts b/src/state/reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes.ts
index 466c26d2..30481bf4 100644
--- a/src/state/reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes.ts
+++ b/src/state/reducers/systemRolesSite/dialog/changeSystemRoleDIalogActionTypes.ts
@@ -27,6 +27,7 @@ export enum ActionType {
   SET_canChangeSystemSettings = 'systemRolesChangeDialogReducer_SET_canChangeSystemSettings',
   SET_canManageTags = 'systemRolesChangeDialogReducer_SET_canManageTags',
   SET_canViewDashboard = 'systemRolesChangeDialogReducer_SET_canViewDashboard',
+  SET_canDownloadPersonalDataFromOthers = 'systemRolesChangeDialogReducer_SET_canDownloadPersonalDataFromOthers',
 
 
   CREATE_systemRole = 'systemRolesChangeDialogReducer_CREATE_systemRole',
diff --git a/src/state/reducers/systemRolesSite/dialog/changeSystemRolesDialogReducer.ts b/src/state/reducers/systemRolesSite/dialog/changeSystemRolesDialogReducer.ts
index 7aece656..c4e3161e 100644
--- a/src/state/reducers/systemRolesSite/dialog/changeSystemRolesDialogReducer.ts
+++ b/src/state/reducers/systemRolesSite/dialog/changeSystemRolesDialogReducer.ts
@@ -85,6 +85,7 @@ export function reducer(state: State = initial, action: AllActions): State {
     case ActionType.SET_canChangeSystemSettings:
     case ActionType.SET_canManageTags:
     case ActionType.SET_canViewDashboard:
+    case ActionType.SET_canDownloadPersonalDataFromOthers:
       const temp = systemRoleReducer(state.systemRole, action)
       return {
         ...state,
diff --git a/src/state/reducers/systemRolesSite/dialog/systemRoleReducer.ts b/src/state/reducers/systemRolesSite/dialog/systemRoleReducer.ts
index 299321af..588fd5f0 100644
--- a/src/state/reducers/systemRolesSite/dialog/systemRoleReducer.ts
+++ b/src/state/reducers/systemRolesSite/dialog/systemRoleReducer.ts
@@ -30,7 +30,8 @@ export const initial: State = {
   canChangeUserData: false,
   canChangeSystemSettings: false,
   canManageTags: false,
-  canViewDashboard: false
+  canViewDashboard: false,
+  canDownloadPersonalDataFromOthers: false,
 }
 
 export const validationRules = getValidationCollection<SystemRoleFullBase>({
@@ -53,6 +54,7 @@ export const validationRules = getValidationCollection<SystemRoleFullBase>({
                                                                              canChangeSystemSettings: [],
                                                                              canManageTags: [],
                                                                              canViewDashboard: [],
+                                                                             canDownloadPersonalDataFromOthers: []
                                                                            })
 
 
@@ -134,6 +136,10 @@ export interface SET_canViewDashboardAction extends ActionBase {
   readonly can: boolean
 }
 
+export interface SET_canDownloadPersonalDataFromOthersAction extends ActionBase {
+  readonly type: ActionType.SET_canDownloadPersonalDataFromOthers
+  readonly can: boolean
+}
 
 export type AllActions =
   SET_idAction
@@ -151,6 +157,7 @@ export type AllActions =
   | SET_canChangeSystemSettingsAction
   | SET_canManageTagsAction
   | SET_canViewDashboardAction
+  | SET_canDownloadPersonalDataFromOthersAction
 
 
 export function reducer(state: State = initial, action: AllActions): State {
@@ -241,6 +248,11 @@ export function reducer(state: State = initial, action: AllActions): State {
         canViewDashboard: action.can
       }
 
+    case ActionType.SET_canDownloadPersonalDataFromOthers:
+      return {
+        ...state,
+        canDownloadPersonalDataFromOthers: action.can
+      }
 
     default:
       notExhaustive(action)
diff --git a/src/state/reducers/systemRolesSite/dialog/systemRoleReducerValidation.ts b/src/state/reducers/systemRolesSite/dialog/systemRoleReducerValidation.ts
index 604231bc..3ba0e68d 100644
--- a/src/state/reducers/systemRolesSite/dialog/systemRoleReducerValidation.ts
+++ b/src/state/reducers/systemRolesSite/dialog/systemRoleReducerValidation.ts
@@ -22,5 +22,6 @@ export const validationMessageKeys = getValidationMessagesCollection<SystemRoleF
   canChangeUserData: '',
   canChangeSystemSettings: '',
   canManageTags: '',
-  canViewDashboard: ''
-})
\ No newline at end of file
+  canViewDashboard: '',
+  canDownloadPersonalDataFromOthers: '',
+})
diff --git a/src/types/userData.ts b/src/types/userData.ts
index fe080ce6..50301141 100644
--- a/src/types/userData.ts
+++ b/src/types/userData.ts
@@ -76,6 +76,11 @@ export interface SystemRoleFullBase {
 
   readonly canViewDashboard: boolean
 
+  /**
+   * true: can download personal data from others
+   */
+  readonly canDownloadPersonalDataFromOthers: boolean
+
 
 }
 
@@ -173,4 +178,4 @@ export interface BaseUserData {
    * the email
    */
   readonly email: string
-}
\ No newline at end of file
+}
-- 
GitLab