├── .gitignore ├── Documentation ├── Customization.md ├── Figures │ ├── Fig01-Create-Modern-Site.png │ ├── Fig02-VSCode.png │ ├── Fig03-VSCode-Write-Manifest.png │ ├── Fig04-App-Catalog-Deploy.png │ ├── Fig05-Add-New-App.png │ ├── Fig06-Add-App-From-Catalog.png │ ├── Fig07-Add-New-Page.png │ ├── Fig08-Add-SPFx-Client-Side-Web-Parts.png │ ├── Fig09-Configure-Power-BI-Data-Source.png │ ├── Fig09-Power-BI-Template-URL-Prompt.png │ ├── Fig10-Configure-Power-BI-Data-Source-2.png │ ├── Fig10-Power-BI-Select-Target.png │ ├── Fig11-Home-Page.png │ ├── Fig12-Add-Insert-Event-Web-Part.png │ ├── Fig13-Add-Item-Confirmation.png │ ├── Fig14-Add-Insert-Request-Web-Part.png │ ├── Fig15-Tasks-Web-Part.png │ ├── Fig16-Hierarchy-Web-Part.png │ ├── Fig18-Term-Store-Management.png │ ├── Fig19-Power-BI-Desktop-Privacy-Issue.png │ ├── Fig20-Power-BI-Desktop-Open-Data-Source-Settings.png │ ├── Fig21-Power-BI-Desktop-Edit-Data-Source-Settings.png │ ├── Fig22-Power-BI-Desktop-Edit-Privacy-Settings.png │ └── Fig23-Power-BI-Desktop-Licensing-Issue.png ├── Setup-Guide-Office-365-CDN.md ├── Setup-Guide.md ├── Troubleshooting-Guide.md └── User-Guide.md ├── GDPRStarterKit ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .vscode │ ├── launch.json │ └── settings.json ├── .yo-rc.json ├── README.md ├── config │ ├── config.json │ ├── copy-assets.json │ ├── deploy-azure-storage.json │ ├── package-solution.json │ ├── serve.json │ ├── tslint.json │ └── write-manifests.json ├── gulpfile-update-manifest.js ├── gulpfile.js ├── package.json ├── src │ ├── components │ │ ├── GDPRBaseWebPart.ts │ │ ├── GDPRDataManager.ts │ │ ├── GDPRStyles.module.scss │ │ ├── GDPRUtility.ts │ │ ├── IGDPRBaseWebPartProps.ts │ │ ├── ISPDateTimePickerProps.ts │ │ ├── ISPDateTimePickerState.ts │ │ ├── ISPLookupItemsPickerProps.ts │ │ ├── ISPLookupItemsPickerState.ts │ │ ├── ISPPeoplePickerProps.ts │ │ ├── ISPPeoplePickerState.ts │ │ ├── ISPTaxonomyPickerProps.ts │ │ ├── ISPTaxonomyPickerState.ts │ │ ├── PeopleSearchQuery.ts │ │ ├── SPDateTimePicker.tsx │ │ ├── SPLookupItemsPicker.tsx │ │ ├── SPPeoplePicker.tsx │ │ ├── SPTaxonomyPicker.tsx │ │ └── SPTermStoreService.ts │ ├── domain_model │ │ └── GDPR_domain_model.ts │ └── webparts │ │ ├── gdprDashboard │ │ ├── GdprDashboardWebPart.manifest.json │ │ ├── GdprDashboardWebPart.ts │ │ ├── assets │ │ │ └── GDPR-Phases.png │ │ ├── components │ │ │ ├── GdprDashboard.module.scss │ │ │ ├── GdprDashboard.tsx │ │ │ ├── IGdprDashboardProps.ts │ │ │ ├── IGdprDashboardState.ts │ │ │ ├── TaskList │ │ │ │ ├── ITaskListProps.ts │ │ │ │ ├── ITaskListState.ts │ │ │ │ └── TaskList.tsx │ │ │ └── TaskListItem │ │ │ │ ├── ITaskListItemProps.ts │ │ │ │ ├── ITaskListItemState.ts │ │ │ │ └── TaskListItem.tsx │ │ ├── loc │ │ │ ├── en-us.js │ │ │ └── mystrings.d.ts │ │ ├── models │ │ │ ├── ITaskItem.ts │ │ │ └── TaskOperationCallback.ts │ │ └── tests │ │ │ └── GdprDashboard.test.ts │ │ ├── gdprHierarchy │ │ ├── GdprHierarchyWebPart.manifest.json │ │ ├── GdprHierarchyWebPart.ts │ │ ├── components │ │ │ ├── GdprHierarchy.module.scss │ │ │ ├── GdprHierarchy.tsx │ │ │ ├── HierarchyListItem │ │ │ │ ├── HierarchyListItem.tsx │ │ │ │ ├── IHierarchyListItemProps.ts │ │ │ │ └── IHierarchyListItemState.ts │ │ │ ├── IGdprHierarchyProps.ts │ │ │ └── IGdprHierarchyState.ts │ │ ├── loc │ │ │ ├── en-us.js │ │ │ └── mystrings.d.ts │ │ ├── models │ │ │ └── IHierarchyItem.ts │ │ └── tests │ │ │ └── GdprHierarchy.test.ts │ │ ├── gdprInsertEvent │ │ ├── GdprInsertEventWebPart.manifest.json │ │ ├── GdprInsertEventWebPart.ts │ │ ├── components │ │ │ ├── GdprInsertEvent.module.scss │ │ │ ├── GdprInsertEvent.tsx │ │ │ ├── IGdprInsertEventProps.ts │ │ │ └── IGdprInsertEventState.ts │ │ ├── loc │ │ │ ├── en-us.js │ │ │ └── mystrings.d.ts │ │ └── tests │ │ │ └── GdprInsertEvent.test.ts │ │ └── gdprInsertRequest │ │ ├── GdprInsertRequestWebPart.manifest.json │ │ ├── GdprInsertRequestWebPart.ts │ │ ├── components │ │ ├── GdprInsertRequest.module.scss │ │ ├── GdprInsertRequest.tsx │ │ ├── IGdprInsertRequestProps.ts │ │ └── IGdprInsertRequestState.ts │ │ ├── loc │ │ ├── en-us.js │ │ └── mystrings.d.ts │ │ └── tests │ │ └── GdprInsertRequest.test.ts └── tsconfig.json ├── LICENSE ├── README.md └── Scripts ├── GDPR-Activity-Hub-Information-Architecture-Full.xml ├── GDPR-Activity-Hub-Workflows.xml ├── GDPR.pbit ├── GDPRActivityHub ├── 3f3fac84-cbd6-44d7-880f-4ab5a65ad143.json ├── 50c8ec09-6a38-4831-a537-fdedd8e560f3.json ├── 8805cdf9-9e29-4c17-b0a5-797950a6a4bd.json ├── d67d6088-e294-4a23-af02-c4ea8783c23f.json ├── gdpr-dashboard.bundle_4852c96f474c692beabc9e7eb3c48a83.js ├── gdpr-hierarchy.bundle_a98dc4439b48f1c5878853333a59b943.js ├── gdpr-insert-event.bundle_760ac1e4850f245ef623df4e18dac5eb.js ├── gdpr-insert-request.bundle_6e7ccdea4dbc35a40a24586ba7cb0588.js ├── gdprstarterkit-gdprdashboardstrings_en-us_64e22a6c9a837771ed30bcdcbed47707.js ├── gdprstarterkit-gdprinserteventstrings_en-us_6d7a4d8cdb400fea3e89e9ace1ef0ad7.js └── gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js ├── Provision-GDPRActivityHub.ps1 ├── ac552ede-9be6-4905-8264-b07f8fa0cc44.xaml ├── e8d4ef94-75ef-4d39-883d-c6661f768ac7.xaml └── ef2f50ba-7a12-4f34-9e97-35cd6de3d4b7.xaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /Documentation/Customization.md: -------------------------------------------------------------------------------- 1 | # GDPR Activity Hub - Customization 2 | 3 | The GDPR Activity Hub is a sample starter kit solution that you can use to take inspiration from. However, you should consider that, in order to satisfy your real business requirements, you most likely will need to customize the solution. In this document you can find some useful information about how to apply the main customizations. 4 | 5 | ## Taxonomy Customization 6 | Most of the metadata available in the UI of the client-side web parts are customizable and are based on a SharePoint Online Term Group. 7 | In fact, after installing the GDPR Activity Hub, in the Term Store Management of your SharePoint Online tenant, you will find the following Term Sets: 8 | * Breach Type: defines the various flavors for a data breach incident. You can customize the terms of this term set. 9 | * Consent Type: defines the various flavors of a consent. You can customize the terms of this term set. 10 | * Delivery Format: defines the various delivery formats for a data export. You can customize the terms of this term set. 11 | * Delivery Methods: defines the various delivery methods for a data export. You can customize the terms of this term set. 12 | * GDPR Role: defines the roles of users, from a GDPR perspective. You should not customize this term set, unless you do not customize the solution code accordingly. 13 | * Processing Type: defines the various processing types for a data processing event. You can customize the terms of this term set. 14 | * Risk Type: defines the various risk types for an identity risk incident. You can customize the terms of this term set. 15 | * Sensitive Data Type: defines the various kind of sensitive data. You can customize the terms of this term set. 16 | * Severity: defines the severity levels (Low, Medium, High, Critical), of an event. You should not customize this term set, unless you do not customize the solution code accordingly. 17 | 18 | In the following screenshot you can see the _"GDPR"_ Term Group, together with its Term Sets. 19 | 20 | ![The UI of the Tasks client-side web part](./Figures/Fig18-Term-Store-Management.png) 21 | 22 | ## Workflows Customization 23 | In order to customize the workflow processes, you can create a Classic Team Site, apply the PnP provisioning templates that define the solution (files [GDPR-Activity-Hub-Information-Architecture-Full.xml](../Scripts/GDPR-Activity-Hub-Information-Architecture-Full.xml) and [GDPR-Activity-Hub-Workflows.xml](../Scripts/GDPR-Activity-Hub-Workflows.xml)) by using the following syntax: 24 | 25 | ```PowerShell 26 | # Connect to the target site 27 | Connect-PnPOnline -Credentials $Credentials 28 | 29 | # Provision taxonomy items, fields, content types, and lists 30 | Apply-PnPProvisioningTemplate -Path .\GDPR-Activity-Hub-Information-Architecture-Full.xml -Handlers Fields,ContentTypes,Lists,TermGroups 31 | 32 | # Provision workflows 33 | Apply-PnPProvisioningTemplate -Path .\GDPR-Activity-Hub-Workflows.xml -Handlers Workflows 34 | ``` 35 | 36 | Once you have done that, open SharePoint Designer, open the Classic Team Site and customize the workflows. 37 | Upon completion of the customization, you can use the following PowerShell excerpt to export the new workflow definitions. 38 | 39 | ```PowerShell 40 | # Connect to the source site 41 | Connect-PnPOnline -Credentials $Credentials 42 | 43 | # Export the updated workflows 44 | Get-PnPProvisioningTemplate -Out .\GDPR-Activity-Hub-Workflows.xml -Handlers Workflows 45 | ``` 46 | 47 | ## Data Provider Customization 48 | At the time of this writing, the GDPR Activity Hub relies on SharePoint Online lists and libraries for storing data. However, from an architectural perspective you can change an abstraction layer and target any external DBMS. For example, you could use Microsoft SQL Azure Database to store many thousands of items. 49 | If that is the case, you can simply define a new .ts file in the src\Components folder of this solution and implement the business logic to save data in the external DBMS. 50 | Out of the box, the GDPR Activity Hub uses the _GDPRDataManager.ts_ file, which implements the concrete persistence layer for SharePoint Online by implementing the interface _IGDPRDataManager_. Any new custom data provider should implement the same interface. 51 | Moreover, if you plan to use the Power BI report, you should replace the connection to the SharePoint Online site with a new connection that target the new DBMS. 52 | 53 | ![](https://telemetry.sharepointpnp.com/sp-dev-gdpr-activity-hub/customization) 54 | -------------------------------------------------------------------------------- /Documentation/Figures/Fig01-Create-Modern-Site.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig01-Create-Modern-Site.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig02-VSCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig02-VSCode.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig03-VSCode-Write-Manifest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig03-VSCode-Write-Manifest.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig04-App-Catalog-Deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig04-App-Catalog-Deploy.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig05-Add-New-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig05-Add-New-App.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig06-Add-App-From-Catalog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig06-Add-App-From-Catalog.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig07-Add-New-Page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig07-Add-New-Page.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig08-Add-SPFx-Client-Side-Web-Parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig08-Add-SPFx-Client-Side-Web-Parts.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig09-Configure-Power-BI-Data-Source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig09-Configure-Power-BI-Data-Source.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig09-Power-BI-Template-URL-Prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig09-Power-BI-Template-URL-Prompt.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig10-Configure-Power-BI-Data-Source-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig10-Configure-Power-BI-Data-Source-2.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig10-Power-BI-Select-Target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig10-Power-BI-Select-Target.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig11-Home-Page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig11-Home-Page.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig12-Add-Insert-Event-Web-Part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig12-Add-Insert-Event-Web-Part.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig13-Add-Item-Confirmation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig13-Add-Item-Confirmation.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig14-Add-Insert-Request-Web-Part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig14-Add-Insert-Request-Web-Part.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig15-Tasks-Web-Part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig15-Tasks-Web-Part.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig16-Hierarchy-Web-Part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig16-Hierarchy-Web-Part.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig18-Term-Store-Management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig18-Term-Store-Management.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig19-Power-BI-Desktop-Privacy-Issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig19-Power-BI-Desktop-Privacy-Issue.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig20-Power-BI-Desktop-Open-Data-Source-Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig20-Power-BI-Desktop-Open-Data-Source-Settings.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig21-Power-BI-Desktop-Edit-Data-Source-Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig21-Power-BI-Desktop-Edit-Data-Source-Settings.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig22-Power-BI-Desktop-Edit-Privacy-Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig22-Power-BI-Desktop-Edit-Privacy-Settings.png -------------------------------------------------------------------------------- /Documentation/Figures/Fig23-Power-BI-Desktop-Licensing-Issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Documentation/Figures/Fig23-Power-BI-Desktop-Licensing-Issue.png -------------------------------------------------------------------------------- /Documentation/Setup-Guide-Office-365-CDN.md: -------------------------------------------------------------------------------- 1 | # GDPR Activity Hub - Setup Guide Using Office 365 CDN 2 | In order to setup the GDPR Activity Hub, you have to accomplish few steps. Some of these steps are automatic, some of them are manual. 3 | Please follow carefully the following guidance in order to succeed with the project installation. 4 | 5 | ## Create the Modern Site 6 | First of all, you need to create a new Microsoft Office 365 Group, which includes a modern SharePoint Online Group site (GROUP#0). 7 | Open your preferred web browser and go to the following URL: 8 | 9 | ```PowerShell 10 | https://.sharepoint.com/_layouts/15/sharepoint.aspx 11 | ``` 12 | 13 | Where the placeholder needs to be replaced with the name of your Office 365 tenant. 14 | Click on the "+ Create Site" command button in the upper left corner of the screen, in order to create a new SharePoint Online Modern Site. 15 | 16 | ![The UI to create a new Modern Site within SharePoint Online](./Figures/Fig01-Create-Modern-Site.png) 17 | 18 | Select to create a new "Team Site" by clicking on the icon on the left side of the "Create a Site" panel. 19 | Provide a Name and a Description for the new site and press the "Next" button. 20 | 21 | > It is up to you to select whether the Office 365 Group will be Public or Private. 22 | 23 | If you like to invite other people to share the site, you can select them through the "Add group members" panel that follows in the site creation flow. Once you have selected any additional members or owners, click the "Finish" button. 24 | 25 | The Office 365 Group, together with its Modern Site, will be created in a matter of few seconds. Copy and save the URL of the just created site, because you will use it pretty soon. 26 | 27 | # Prepare the Development Environment 28 | In order to install the GDPR Activity Hub, eventually customizing the solution and hosting it in your own Office 365 CDN, you will need a development machine ready to build SharePoint Framework (SPFx) solutions. You can find detailed and updated instructions about how to setup up an SPFx development machine in the document ["Set up your SharePoint client-side web part development environment"](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) 29 | 30 | Now, download the source code of the project either using [this repository](https://github.com/SharePoint/sp-dev-gdpr-activity-hub) and a GIT client, or simply by downloading [a ZIP file](https://github.com/SharePoint/sp-dev-gdpr-activity-hub/archive/master.zip) with the source code of the solution. 31 | 32 | If you downloaded the ZIP file, please unzip it somewhere on your local file system. 33 | 34 | In order to execute the PowerShell scripts needed to setup the solution, you have to install a couple of PowerShell extensions, which are listed below: 35 | * PowerShell extensions for SharePoint Online, which are included in the ["SharePoint Online Management Shell"](https://www.microsoft.com/en-us/download/details.aspx?id=35588) 36 | * PnP PowerShell extensions, which can be installed following the [instructions provided here](https://github.com/SharePoint/PnP-PowerShell#installation). 37 | 38 | # Build the SharePoint Framework Solution 39 | Once you have prepared your development environment, open a PowerShell console, and go to the folder where you downloaded and eventually unzipped the source code of the solution. Move to the sub-folder named _GDPRStarterKit_ and execute the following command: 40 | 41 | ```PowerShell 42 | Code . 43 | ``` 44 | 45 | It will start Visual Studio Code, opening the GDPR solution. 46 | 47 | ![The UI of Visual Studio Code while managing the GDPR solution](./Figures/Fig02-VSCode.png) 48 | 49 | To host the SPFx assets on the Microsoft Office 365 CDN, follow these steps: 50 | * Choose a name for a SharePoint Online site collection - which will be created in the next step - and for a document library, within that site, and write them down in a safe place. Usually a good name for such a site is **"CDN"** and a meaningful name for the document library is **"CDNFiles"**. The overall base URL of your CDN will be something like: 51 | 52 | ```PowerShell 53 | https://publiccdn.sharepointonline.com/.sharepoint.com/sites/// 54 | ``` 55 | 56 | * Open the _write-manifest.json_ file that is available under the _config_ folder of the solution and replace the **cdnBasePath** property with the value of your public CDN URL, followed by the name of the project (which is **GDPRActivityHub**). The overall URL will be something like: 57 | 58 | ```PowerShell 59 | https://publiccdn.sharepointonline.com/.sharepoint.com/sites///GDPRActivityHub 60 | ``` 61 | 62 | In the following Figure you can see the _write-manifest.json_ file in the UI of Visual Studio Code. 63 | 64 | ![The UI of Visual Studio Code while editing the write-manifest.json file](./Figures/Fig03-VSCode-Write-Manifest.png) 65 | 66 | * Save the _write-manifest.json_ file and go back to the PowerShell console, which should still be located under the solution's folder 67 | 68 | * From within the console, execute the following commands in order to install the dependencies and generating the bundle: 69 | 70 | ```PowerShell 71 | npm install 72 | gulp bundle --ship 73 | gulp package-solution --ship 74 | ``` 75 | 76 | * By default the gulp tool will prepare the bundled files under the folder _temp/deploy_ of your solution. Copy the full path of that folder, because you will use it in the following section. 77 | 78 | > You can find further details about hosting your SPFx solutions with Microsoft Office 365 CDN in the document ["Hosting client-side web part from Office 365 CDN"](https://dev.office.com/sharepoint/docs/spfx/web-parts/get-started/hosting-webpart-from-office-365-cdn). 79 | 80 | # Provision Artifacts 81 | From within the previously opened PowerShell console, go to the folder where you downloaded and eventually unzipped the source code of the solution, and move to the _Scripts_ sub-folder of the solution. 82 | 83 | Now, you can execute the following PowerShell command: 84 | 85 | ```PowerShell 86 | .\Provision-GDPRActivityHub.ps1 -GroupSiteUrl "https://.sharepoint.com/sites/" ` 87 | -Credentials $credentials ` 88 | -ConfigureCDN ` 89 | -CDNSiteName "CDN" ` 90 | -CDNLibraryName "CDNFiles" ` 91 | ``` 92 | 93 | The PowerShell script requires some input arguments, which are: 94 | * **GroupSiteUrl**: the URL of the Modern Site that you created before. 95 | * **Credentials**: the credentials of a tenant administrator, which will be used to setup the solution. If you don't provide this argument, the script will prompt you for credentials during execution. 96 | * **ConfigureCDN**: an optional switch to instruct the script to create and configure an Office 365 CDN that will host the SPFx client-side web parts. 97 | * **CDNSiteName**: the relative URL of a SharePoint site collection that will be create to host the CDN files, if you select to create and configure the Office 365 CDN. 98 | * **CDNLibraryName**: the name of the document library that will be created to host the CDN files. 99 | 100 | > Notice that you can skip the configuration of the CDN in Microsoft Office 365, and you can instead use whatever else hosting solution of your choice. 101 | 102 | # Upload the SPFx package 103 | You are now ready to upload the SPFx solution package to SharePoint Online. In order to do that, follow these steps: 104 | 105 | * Open the _App Catalog_ site for your tenant. If you don't have one or if you don't know the URL of your _App Catalog_ site, please follow the instructions illustrated in the document ["Use the App Catalog to make custom business apps available for your SharePoint Online environment"](https://support.office.com/en-us/article/Use-the-App-Catalog-to-make-custom-business-apps-available-for-your-SharePoint-Online-environment-0b6ab336-8b83-423f-a06b-bcc52861cba0?ui=en-US&rs=en-US&ad=US) to create a new _App Catalog_ site. 106 | * Go to the "Apps for SharePoint" library of the _App Catalog_ and upload the file named _gdpr-starter-kit.sppkg_ that you can find under the _sharepoint/solution_ sub-folder of the main solution folder. 107 | * Accept to trust and deploy the solution in your tenant by pressing the **Deploy** button in the following popup dialog. 108 | 109 | ![The UI of App Catalog while deploying a new App](./Figures/Fig04-App-Catalog-Deploy.png) 110 | 111 | # Add App and Create Modern Pages 112 | Now browse to the Modern Site that you created before, Add an App by clicking the New -> App menu item highligthed in the following Figure. 113 | 114 | ![The UI of App Catalog while deploying a new App](./Figures/Fig05-Add-New-App.png) 115 | 116 | In the left quick launch menu select the _From Your Organization_ menu item and select to install the app called _gdpr-starter-kit-client-side-solution_ as depicted in the following Figure. 117 | 118 | ![The UI to add the App from the App Catalog](./Figures/Fig06-Add-App-From-Catalog.png) 119 | 120 | Now go back to the home page of the site, and create four Modern Pages, by selecting the option New -> Page in the upper left menu, as like as you can see in the following Figure. 121 | 122 | ![The UI to add a new Page](./Figures/Fig07-Add-New-Page.png) 123 | 124 | The pages to create should be: 125 | * Insert Event/Incident: in this page you should add the client-side web part named "GDPR Insert Event". Configure the client-side web part in order to target the _Events_ list. 126 | * Insert Request: in this page you should add the client-side web part named "GDPR Insert Request". Configure the client-side web part in order to target the _Requests_ list. 127 | * GDPR Hierarchy: in this page you should add the client-side web part named "GDPR Hierarchy". Configure the client-side web part in order to target the _Hierarchy_ list. 128 | * Dashboard of Tasks: in this page you should add the client-side web part named "GDPR Dashboard". Configure the client-side web part in order to target the _Tasks_ list. 129 | 130 | In the following Figure you can see the SPFx client-side web parts defined in the GDPR Activity Hub project and available for insertion in your environment. 131 | 132 | ![The UI to add a new SPFx client-side web part](./Figures/Fig08-Add-SPFx-Client-Side-Web-Parts.png) 133 | 134 | Go back to the home page of the site, edit the home page and remove all of the already existing client-side web parts. Add a "Power BI" client-side web part, in the next section you will configure it. 135 | 136 | # Upload and Configuration of the Power BI dashboard 137 | 138 | In order to install the GDPR Activity Hub dashboard based on Power BI, you need to have at least a FREE license of Power BI. Thus [download and install the Power BI desktop client](https://powerbi.microsoft.com/en-us/desktop/). 139 | 140 | Once the Power BI Desktop client is installed, run it and open the _GDPR.pbix_ file that is located under the _Scripts_ folder of the solution that you downloaded from GitHub or from the path where you unzipped the solution. 141 | 142 | Open the _"Home"_ ribbon, select the _"Edit Queries"_ command and click on the _"Data Source settings"_ menu item. 143 | 144 | ![The UI of Power BI to edit the connection settings](./Figures/Fig09-Configure-Power-BI-Data-Source.png) 145 | 146 | Now add a new data connection, targeting your Modern Site and provide a set of valid credentials to access the content of the site. 147 | 148 | ![The UI of Power BI to edit the connection settings](./Figures/Fig10-Configure-Power-BI-Data-Source-2.png) 149 | 150 | Close the windows and select _"Publish"_ command on the _"Home"_ ribbon. Follow the instructions to publish the PBIX file on Power BI. Once the report has been published, save the URL on Power BI. 151 | 152 | Now, go back to the home page of the site and configure the Power BI client-side web part to target the URL of the report that you published in the previous section. 153 | 154 | Save and Publish the new home page and you are done! 155 | 156 | # Play with the Solution 157 | Now you are ready to play with the solution. You can find a detailed help in the ["How to Use the GDPR Activity Hub"](./Documentation/How-To-Use-GDPR-Activity-Hub.md) document. 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Documentation/Setup-Guide.md: -------------------------------------------------------------------------------- 1 | # GDPR Activity Hub - Setup Guide Using a Shared CDN 2 | In order to setup the GDPR Activity Hub, you have to follow a few steps. Some of these steps are automatic and some are manual. 3 | Please follow carefully the guidance below in order to succeed with the project installation. 4 | 5 | Before digging into the installation steps, you should consider the main requirements of the solution: 6 | * You need to have a valid Microsoft Office 365 E3 subscription 7 | * You need to have a development machine configured to create SharePoint Framework solutions (see the following steps) 8 | * You need to install the PowerShell extensions for SharePoint Online 9 | * You need to install the PnP PowerShell extensions 10 | 11 | Aside from the Office 365 subscription, you will find further details about the other requirements in the following installation steps. 12 | 13 | ## Create the Modern Site 14 | First of all, you need to create a new Microsoft Office 365 Group, which includes a modern SharePoint Online Group site (GROUP#0). 15 | Open your preferred web browser and go to the following URL: 16 | 17 | ```PowerShell 18 | https://.sharepoint.com/_layouts/15/sharepoint.aspx 19 | ``` 20 | 21 | Where the placeholder needs to be replaced with the name of your Office 365 tenant. 22 | Click on the "+ Create Site" command button in the upper left corner of the screen, in order to create a new SharePoint Online Modern Site. 23 | 24 | ![The UI to create a new Modern Site within SharePoint Online](./Figures/Fig01-Create-Modern-Site.png) 25 | 26 | Select to create a new "Team Site" by clicking on the icon on the left side of the "Create a Site" panel. 27 | Provide a Name and a Description for the new site and press the "Next" button. 28 | 29 | > It is up to you to select whether the Office 365 Group will be Public or Private. 30 | 31 | If you like to invite other people to share the site, you can select them through the "Add group members" panel that follows in the site creation flow. Once you have selected any additional members or owners, click the "Finish" button. 32 | 33 | The Office 365 Group, together with its Modern Site, will be created in a matter of few seconds. Copy and save the URL of the just created site, because you will use it in the upcoming sections of this guide. 34 | 35 | ## Prepare the Development Environment 36 | In order to install the GDPR Activity Hub, eventually customizing the solution and hosting it in your own hosting environment, you will need a development machine ready to build SharePoint Framework (SPFx) solutions. You can find detailed and updated instructions about how to setup up an SPFx development machine in the document ["Set up your SharePoint client-side web part development environment"](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment) 37 | 38 | Now, pull the source code of the project either using [this repository](https://github.com/SharePoint/sp-dev-gdpr-activity-hub) and a GIT client, or simply download [a ZIP file](https://github.com/SharePoint/sp-dev-gdpr-activity-hub/archive/master.zip) with the source code of the solution. 39 | 40 | If you downloaded the ZIP file, please unzip it somewhere on your local file system. 41 | 42 | In order to execute the PowerShell scripts needed to setup the solution, you have to install a couple of PowerShell extensions, which are listed below: 43 | * PowerShell extensions for SharePoint Online, which are included in the ["SharePoint Online Management Shell"](https://www.microsoft.com/en-us/download/details.aspx?id=35588) 44 | * PnP PowerShell extensions, which can be installed following the [instructions provided here](https://github.com/SharePoint/PnP-PowerShell#installation). 45 | 46 | ## Provision Artifacts 47 | Open a PowerShell console, go to the folder where you downloaded and eventually unzipped the source code of the solution, and move to the _Scripts_ sub-folder of the solution. 48 | 49 | > Notice that you need to have an account that is Term Store Administrator in order to provision the solution, otherwise you will get an authorization exception and the setup process will fail. 50 | 51 | Now, you can execute the following PowerShell command: 52 | 53 | ```PowerShell 54 | .\Provision-GDPRActivityHub.ps1 -GroupSiteUrl "https://.sharepoint.com/sites/" ` 55 | -Credentials $credentials ` 56 | -ConfigureCDN ` 57 | -CDNSiteName "CDN" ` 58 | -CDNLibraryName "CDNFiles" 59 | ``` 60 | 61 | The PowerShell script requires some input arguments, which are: 62 | * **GroupSiteUrl**: the URL of the Modern Site that you created before. 63 | * **Credentials**: the credentials of a tenant administrator, which will be used to setup the solution. If you don't provide this argument, the script will prompt you for credentials during execution. 64 | * **ConfigureCDN**: an optional switch to instruct the script to create and configure an Office 365 CDN that will host the SPFx client-side web parts. 65 | * **CDNSiteName**: the relative URL of a SharePoint site collection that will be create to host the CDN files, if you select to create and configure the Office 365 CDN. 66 | * **CDNLibraryName**: the name of the document library that will be created to host the CDN files. 67 | 68 | The PowerShell script will accomplish for you the following tasks: 69 | * Provision the Information Architecture (Taxonomy Terms, Site Columms, Content Types, Lists and Libraries) onto the target Modern Site 70 | * Create and configure the CDN in Microsoft Office 365 71 | * Build the SPFx solution targeting your CDN for hosting the assets 72 | * Upload the SPFx assets to the CDN library 73 | 74 | Once the script will be successfully executed, you will see in the console window a confirmation message like: 75 | 76 | ```PowerShell 77 | All the automatic steps are now completed! 78 | You can find the .SPPKG file at the following path: \sharepoint\solution\gdpr-starter-kit.sppkg 79 | ``` 80 | 81 | and you will be able to take a note about the .SPPKG file path. 82 | 83 | ## Upload the SPFx package 84 | You are now ready to upload the SPFx solution package to SharePoint Online. In order to do that, follow these steps: 85 | 86 | * Open the _App Catalog_ site for your tenant. If you don't have one or if you don't know the URL of your _App Catalog_ site, please follow the instructions illustrated in document ["Use the App Catalog to make custom business apps available for your SharePoint Online environment"](https://support.office.com/en-us/article/Use-the-App-Catalog-to-make-custom-business-apps-available-for-your-SharePoint-Online-environment-0b6ab336-8b83-423f-a06b-bcc52861cba0?ui=en-US&rs=en-US&ad=US) to create a new _App Catalog_ site. 87 | * Go to the "Apps for SharePoint" library of the _App Catalog_ and upload the file named _gdpr-starter-kit.sppkg_ from the path that you saved in the previous section. 88 | * Accept to trust and deploy the solution in your tenant by pressing the **Deploy** button in the following popup dialog. 89 | 90 | ![The UI of App Catalog while deploying a new App](./Figures/Fig04-App-Catalog-Deploy.png) 91 | 92 | ## Add App and Create Modern Pages 93 | Now browse to the Modern Site that you created before, Add an App by clicking the New -> App menu item highligthed in the following Figure. 94 | 95 | ![The UI of App Catalog while deploying a new App](./Figures/Fig05-Add-New-App.png) 96 | 97 | In the left quick launch menu select the _From Your Organization_ menu item and select to install the app called _gdpr-starter-kit-client-side-solution_ as depicted in the following Figure. 98 | 99 | ![The UI to add the App from the App Catalog](./Figures/Fig06-Add-App-From-Catalog.png) 100 | 101 | Now go back to the home page of the site, and create four Modern Pages, by selecting the option New -> Page in the upper left menu, as like as you can see in the following Figure. 102 | 103 | ![The UI to add a new Page](./Figures/Fig07-Add-New-Page.png) 104 | 105 | The pages to create should be: 106 | * Insert Event/Incident: in this page you should add the client-side web part named "GDPR Insert Event". Configure the client-side web part in order to target the _Events_ list. 107 | * Insert Request: in this page you should add the client-side web part named "GDPR Insert Request". Configure the client-side web part in order to target the _Requests_ list. 108 | * GDPR Hierarchy: in this page you should add the client-side web part named "GDPR Hierarchy". Configure the client-side web part in order to target the _Hierarchy_ list. 109 | * Dashboard of Tasks: in this page you should add the client-side web part named "GDPR Dashboard". Configure the client-side web part in order to target the _Tasks_ list. 110 | 111 | In the following Figure you can see the SPFx client-side web parts defined in the GDPR Activity Hub project and available for insertion in your environment. 112 | 113 | ![The UI to add a new SPFx client-side web part](./Figures/Fig08-Add-SPFx-Client-Side-Web-Parts.png) 114 | 115 | > Sometimes the creation of the Office 365 CDN can take a while to become ready and fully functional. In case of any failure while adding the client-side web parts to the Modern Site, please wait up to 15 minutes to allow the CDN to be ready. 116 | 117 | Go back to the home of the site, edit the home page and remove all of the already existing client-side web parts. Add a "Power BI" client-side web part, which you will configure in the next section. 118 | 119 | ## Upload and Configuration of the Power BI dashboard 120 | In order to install the GDPR Activity Hub dashboard based on Power BI, you need to have at least a FREE license of Power BI. Thus [download and install the Power BI desktop client](https://powerbi.microsoft.com/en-us/desktop/). 121 | 122 | > Notice that it is highly suggested to load some data in the SharePoint Online site (eventually fake data that you can delete at the end of this section) in order to allow the dashboard to find all the reference items in the data source site. 123 | 124 | Once the Power BI Desktop client is installed, run it and open the _GDPR.pbit_ file that is located under the _Scripts_ folder of the solution that you downloaded from GitHub or from the path where you unzipped the solution. 125 | 126 | When you open the file, it will prompt you for the URL of the GDPR Activity Hub site, as you can see in the following Figure. 127 | 128 | ![The UI of Power BI to configure the GDPR Activity Hub URL](./Figures/Fig09-Power-BI-Template-URL-Prompt.png) 129 | 130 | Provide the URL of the Modern Site that you created and press the _"Load"_ button. In case of need, the Power BI Desktop client will ask you for a Work and School account to use in order to access the target site. Moreover, in case the Power BI Desktop client will ask you, select the _"Organizational"_ Privacy Level. 131 | 132 | Wait for the Power BI Desktop client to download and refresh data from your data source. 133 | 134 | Double-check to be using the right Power BI account, in the upper right corner of the UI of Power BI Desktop, you need to target an account valid for your target tenant. If you don't have an account, you can sign-up for a new one for FREE, or you can eventually subscribe for a 60 days trial of the Power BI Pro service. 135 | 136 | Once the refresh is completed and once you are using the right target account, click the _"Publish"_ command in the _"Home"_ ribbon. Follow the instructions to publish the report on Power BI. In particular, select the Modern Site for the GDPR Activity Hub as the destination for your report, as you can see in the following Figure. 137 | 138 | ![The UI of Power BI to publish the report on a target](./Figures/Fig10-Power-BI-Select-Target.png) 139 | 140 | Once the report has been published, click on the link _"Open [name of your report] in Power BI"_ and see your report in Power BI. Save the URL of the page, and go back to the home page of the site. 141 | 142 | There configure the Power BI client-side web part to target the URL of the report. 143 | 144 | Save and Publish the new home page and you are done! 145 | 146 | ## Play with the Solution 147 | Now you are ready to play with the solution. You can find a detailed help in the [User Guide](./User-Guide.md) document. In case of any issues during the setup process, please refer to the [Troubleshooting Guide](./Troubleshooting-Guide.md) 148 | 149 | ![](https://telemetry.sharepointpnp.com/sp-dev-gdpr-activity-hub/setup-guide) 150 | -------------------------------------------------------------------------------- /Documentation/Troubleshooting-Guide.md: -------------------------------------------------------------------------------- 1 | # GDPR Activity Hub - Troubleshooting Guide 2 | While installing or using the GDPR Activity Hub there could be some unexpected issues. In this page we are collecting the known ones, together with suggested workarounds. 3 | 4 | ## Power BI - Privacy Settings 5 | In case you will experience the issue highlighted in the following Figure, while refreshing data in the Power BI dashboard, please follow the instructions below to fix it. 6 | 7 | ![The issue in Power BI while refreshing the data source](./Figures/Fig19-Power-BI-Desktop-Privacy-Issue.png) 8 | 9 | First of all, go to the _"Home"_ ribbon and expand the _"Edit Queries"_ ribbon command, in order to select the _"Data Source Settings"_ command, as it is illustrated in the following Figure. 10 | 11 | ![The ribbon command to edit data source setting](./Figures/Fig20-Power-BI-Desktop-Open-Data-Source-Settings.png) 12 | 13 | Now, select the data source and click the _"Edit Permissions"_ button in the lower part of the screen. See the following Figure. 14 | 15 | ![The edit permissions button](./Figures/Fig21-Power-BI-Desktop-Edit-Data-Source-Settings.png) 16 | 17 | In the _"Edit Permissions"_ popup dialog that will show up, select a value of _"Organizational"_ for the _"Privacy Level"_ option, as depicted in the following Figure. 18 | 19 | ![The privacy level option](./Figures/Fig22-Power-BI-Desktop-Edit-Privacy-Settings.png) 20 | 21 | Save and close all of the windows and refresh your data. 22 | 23 | ## Power BI - Sometimes it fails while refreshing data 24 | In order to load the dashboard, you should have a full set of data in the target data source site. We plan to add a bunch of fake items in the target lists (Events and Requests) during the PowerShell setup phase. Right now, it is better to load some real or sample data in the GDPR Activity Hub portal before configuring the Power BI dashboard. 25 | 26 | ![](https://telemetry.sharepointpnp.com/sp-dev-gdpr-activity-hub/troubleshooting-guide) 27 | -------------------------------------------------------------------------------- /Documentation/User-Guide.md: -------------------------------------------------------------------------------- 1 | # GDPR Activity Hub - User Guide 2 | In this document you can learn about the main capabilities of the GDPR Activity Hub project. 3 | The main functionalities of the project are: 4 | * Home Page and Dashboard 5 | * Tracking of events and incidents 6 | * Tracking of requests 7 | * Tasks assignment and management for GDPR processes 8 | * Hierarchy of GDPR roles in the company 9 | 10 | In the following sections you will learn about each of these items. 11 | 12 | ## Home Page and Dashboard 13 | The main entry point of the solution is the home page of the Modern Site that hosts the data. In the following Figure you can see the dashboard (built using Power BI) that allows you to measure and monitor your performance, related to the main GDPR requests and events. 14 | 15 | ![The UI of the Home Page of GDPR Activity Hub](./Figures/Fig11-Home-Page.png) 16 | 17 | The dashboard gets updated based on a schedule and loads data stored in the current Modern Site and manually loaded by you, or your end users, by leveraging the client-side web parts of this solution. 18 | 19 | > Depending on the license of Power BI you can have more or less automated update slots during a work day. For example, in the FREE edition of the product, you can have up to 8 refresh per day. However, if you buy a commercial license of Power BI, you can have more. 20 | 21 | ## Tracking of Events and Incidents 22 | You can keep track of events and incidents, using a dedicated list of _Events_ and a client-side web part to add new items. 23 | In the followig Figure you can see the UI of the "GDPR Insert Event" client-side web part. 24 | 25 | ![The UI to file a new Event/Incident](./Figures/Fig12-Add-Insert-Event-Web-Part.png) 26 | 27 | There are six flavors of events that you can keep track of: 28 | * Data Breach: it is an incident that happens whenever there is a Data Breach in your environment. You should file a new item and notify DPA within 72 hours. That's why the project includes a workflow that assigns a reminder task for DPA notification targeting the assignee of the Data Breach event. 29 | * Identity Risk: it is an incident that happens whenever there is an Identity Risk (even potential) in your environment. 30 | * Data Consent: it is an event that happens whenever a Data Subject gives you the consent to handle her/his data. 31 | * Data Consent Withdrawal: it is an event that happens whenever a Data Subject withdraws a consent to handle her/his data. 32 | * Data Processing: it is an event that happens whenever a Data Processing activity happens, involving one or more personal data items. 33 | * Data Archived: it is an event that happens when one or more personal data item are archived for recording purposes. 34 | 35 | You simply need to select the kind of event that you want to file, and the UI will adapt the form fields accordingly, as well as the validation rules. 36 | 37 | Once you have properly filled out the form, accordingly to the validation rules in place, the _Save_ button will become enabled and by pressing it a new item will be stored in the _Events_ list in your SharePoint Online Modern Site. After saving a new item, the client-side web part will notify you with a message like the one depicted in the following Figure. 38 | 39 | ![The UI to file a new Event/Incident](./Figures/Fig13-Add-Item-Confirmation.png) 40 | 41 | If you press the _"Insert next"_ button the client-side web part will allow you to add another item, and it will keep all of the fields pre-filled with previously inserted values, to speed up the insertion of a stream of data. On the contrary, if you press the _"Go home"_ button, the client-side web part will redirect you to the home page of the Modern Site. 42 | 43 | ## Tracking of Requests from Data Subjects 44 | As like as it happens with the list of _Events_, you can also keep track of _Requests_, which target the corresponding custom list in the GDPR Activity Hub Modern Site. 45 | In the followig Figure you can see the UI of the "GDPR Insert Request" client-side web part. 46 | 47 | ![The UI to file a new Request](./Figures/Fig14-Add-Insert-Request-Web-Part.png) 48 | 49 | There are five flavors of requests that you can keep track of: 50 | * Data Access: allows to keep track of a request to access personal data of a Data Subject. 51 | * Data Correct: allows to keep track of a request to correct personal data of a Data Subject. 52 | * Data Export: allows to keep track of a request to export personal data of a Data Subject. 53 | * Data Objection: allows to keep track of an objection to access personal data of a Data Subject. 54 | * Data Erase: allows to keep track of a request to erase personal data of a Data Subject. 55 | 56 | From a UI perspective, this web part behaves exactly like the one to store a new event or incident. 57 | 58 | ## Tasks Assignment and Management for GDPR Processes 59 | When some kind of events or requests are inserted into the GDPR Activity Hub, there a three sample workflows that can be triggered. The available sample workflows are: 60 | * Data Breach Management: assigns a task to the assignee of the data breach event, in order to remind the notification to DPA within 72 hours after the happening of the Data Breach. 61 | * External Data Processing: assigns a tasks to the assignee of the event, just to remind her/him to manage the data processing. 62 | * Request to Access Personal Data Management: assigns a task to the assignee of the request, so that she/he can keep track of his tasks list. 63 | 64 | The GDPR Activity Hub provides also a client-side web part to monitor the assigned tasks for the current user, or for all the users (if the current user is an admin of the Modern Site). 65 | 66 | ![The UI of the Tasks client-side web part](./Figures/Fig15-Tasks-Web-Part.png) 67 | 68 | As you can see from the UI, it is also possible to easily mark a task as completed, just by clicking on the toggle button beside every single task item. 69 | 70 | ## Hierarchy of GDPR Roles in the Company 71 | Another useful feature of the GDPR Activity Hub is the GDPR Hierarchy. In fact, there is a custom list called _Hierarchy_ that you can use to store information about people involved in managing GDPR requirements, and you can assign roles to them. The supported roles are: 72 | * Data Protection Officer 73 | * GDPR Controller 74 | * GDPR Processor 75 | 76 | In the following Figure you can see a sample hierarchy, presented by the GDPR Hierarchy client-side web part. 77 | 78 | ![The UI of the Tasks client-side web part](./Figures/Fig16-Hierarchy-Web-Part.png) 79 | 80 | ## Limitations of the Solution 81 | The GDPR Activity Hub is a sample starter kit solution that you can use to take inspiration from. However, you should consider that it is not a "ready to go" full product, and most likely you will need to customize it, in order to adhere to your real business requirements. For further information about how to customize the GDPR Activity Hub, you can read the [Customization Guide](./Customization.md). 82 | 83 | Here are highlighted some well-known limitations: 84 | * Because the GDPR Activity Hub uses SharePoint Online lists to store data about requests and events, you should keep in mind the limits of SharePoint Online in terms of maximum number of items in a list. 85 | * The home page dashboard is built on top of Microsoft Power BI, which can be leveraged in its FREE version, as well. However, with the FREE version of Power BI you are limited to a maximum of 1GB of data per user, and you cannot have collaboration on reports by multiple users. 86 | * The workflows processes are just for the sake of making examples, and most likely you will need to extend or customize them in order to target your real business goals. The workflows have been defined using Workflow Manager and SharePoint Designer, in order to being able to apply them on the fly on any target site where you install the GDPR Activity Hub. However, the GDPR Activity Hub targets Modern Sites, where you cannot use SharePoint Designer. Thus, in order to edit or customize a workflow you should work on a Classic Team Site, then export the workflow definitions using the PnP Provisioning Engine and import them in the target Modern Site, still using the PnP Provisioning Engine. 87 | 88 | ![](https://telemetry.sharepointpnp.com/sp-dev-gdpr-activity-hub/user-guide) 89 | -------------------------------------------------------------------------------- /GDPRStarterKit/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 -------------------------------------------------------------------------------- /GDPRStarterKit/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /GDPRStarterKit/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules 8 | 9 | # Build generated files 10 | dist 11 | lib 12 | solution 13 | temp 14 | *.sppkg 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | # Visual Studio files 23 | .ntvs_analysis.dat 24 | .vs 25 | bin 26 | obj 27 | 28 | # Resx Generated Code 29 | *.resx.ts 30 | 31 | # Styles Generated Code 32 | *.scss.ts 33 | -------------------------------------------------------------------------------- /GDPRStarterKit/.npmignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | .vscode 3 | coverage 4 | node_modules 5 | sharepoint 6 | src 7 | temp 8 | 9 | # Files 10 | *.csproj 11 | .git* 12 | .yo-rc.json 13 | gulpfile.js 14 | tsconfig.json 15 | -------------------------------------------------------------------------------- /GDPRStarterKit/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Local workbench", 6 | "type": "chrome", 7 | "request": "launch", 8 | "url": "https://localhost:4321/temp/workbench.html", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///../../../src/*": "${webRoot}/src/*", 13 | "webpack:///../../../../src/*": "${webRoot}/src/*", 14 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 15 | }, 16 | "runtimeArgs": [ 17 | "--remote-debugging-port=9222" 18 | ] 19 | }, 20 | { 21 | "name": "Hosted workbench", 22 | "type": "chrome", 23 | "request": "launch", 24 | "url": "https://mstnd677827.sharepoint.com/sites/GDPRIA/_layouts/15/workbench.aspx", 25 | "webRoot": "${workspaceRoot}", 26 | "sourceMaps": true, 27 | "sourceMapPathOverrides": { 28 | "webpack:///../../../src/*": "${webRoot}/src/*", 29 | "webpack:///../../../../src/*": "${webRoot}/src/*", 30 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 31 | }, 32 | "runtimeArgs": [ 33 | "--remote-debugging-port=9222" 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /GDPRStarterKit/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | } -------------------------------------------------------------------------------- /GDPRStarterKit/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@microsoft/generator-sharepoint": { 3 | "libraryName": "gdpr-starter-kit", 4 | "framework": "react", 5 | "version": "1.0.2", 6 | "libraryId": "2abca7c4-a732-4e27-b969-d465026ffbb5" 7 | } 8 | } -------------------------------------------------------------------------------- /GDPRStarterKit/README.md: -------------------------------------------------------------------------------- 1 | ## gdpr-starter-kit 2 | 3 | This is where you include your WebPart documentation. 4 | 5 | ### Building the code 6 | 7 | ```bash 8 | git clone the repo 9 | npm i 10 | npm i -g gulp 11 | gulp 12 | ``` 13 | 14 | This package produces the following: 15 | 16 | * lib/* - intermediate-stage commonjs build artifacts 17 | * dist/* - the bundled script, along with other resources 18 | * deploy/* - all resources which should be uploaded to a CDN. 19 | 20 | ### Build options 21 | 22 | gulp clean - TODO 23 | gulp test - TODO 24 | gulp serve - TODO 25 | gulp bundle - TODO 26 | gulp package-solution - TODO 27 | -------------------------------------------------------------------------------- /GDPRStarterKit/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "entries": [ 3 | { 4 | "entry": "./lib/webparts/gdprDashboard/GdprDashboardWebPart.js", 5 | "manifest": "./src/webparts/gdprDashboard/GdprDashboardWebPart.manifest.json", 6 | "outputPath": "./dist/gdpr-dashboard.bundle.js" 7 | }, 8 | { 9 | "entry": "./lib/webparts/gdprHierarchy/GdprHierarchyWebPart.js", 10 | "manifest": "./src/webparts/gdprHierarchy/GdprHierarchyWebPart.manifest.json", 11 | "outputPath": "./dist/gdpr-hierarchy.bundle.js" 12 | }, 13 | { 14 | "entry": "./lib/webparts/gdprInsertRequest/GdprInsertRequestWebPart.js", 15 | "manifest": "./src/webparts/gdprInsertRequest/GdprInsertRequestWebPart.manifest.json", 16 | "outputPath": "./dist/gdpr-insert-request.bundle.js" 17 | }, 18 | { 19 | "entry": "./lib/webparts/gdprInsertEvent/GdprInsertEventWebPart.js", 20 | "manifest": "./src/webparts/gdprInsertEvent/GdprInsertEventWebPart.manifest.json", 21 | "outputPath": "./dist/gdpr-insert-event.bundle.js" 22 | } 23 | ], 24 | "externals": { 25 | "sp-client-custom-fields": "node_modules/sp-client-custom-fields/dist/sp-client-custom-fields.bundle.js" 26 | }, 27 | "localizedResources": { 28 | "gdprDashboardStrings": "webparts/gdprDashboard/loc/{locale}.js", 29 | "gdprHierarchyStrings": "webparts/gdprHierarchy/loc/{locale}.js", 30 | "gdprInsertRequestStrings": "webparts/gdprInsertRequest/loc/{locale}.js", 31 | "gdprInsertEventStrings": "webparts/gdprInsertEvent/loc/{locale}.js", 32 | "sp-client-custom-fields/strings": "../node_modules/sp-client-custom-fields/lib/loc/{locale}.js" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /GDPRStarterKit/config/copy-assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "deployCdnPath": "temp/deploy" 3 | } 4 | -------------------------------------------------------------------------------- /GDPRStarterKit/config/deploy-azure-storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "workingDir": "./temp/deploy/", 3 | "account": "", 4 | "container": "gdpr-starter-kit", 5 | "accessKey": "" 6 | } -------------------------------------------------------------------------------- /GDPRStarterKit/config/package-solution.json: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "name": "gdpr-starter-kit-client-side-solution", 4 | "id": "2abca7c4-a732-4e27-b969-d465026ffbb5", 5 | "version": "1.0.0.0" 6 | }, 7 | "paths": { 8 | "zippedPackage": "solution/gdpr-starter-kit.sppkg" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /GDPRStarterKit/config/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 4321, 3 | "initialPage": "https://localhost:5432/workbench", 4 | "https": true, 5 | "api": { 6 | "port": 5432, 7 | "entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /GDPRStarterKit/config/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // Display errors as warnings 3 | "displayAsWarning": true, 4 | // The TSLint task may have been configured with several custom lint rules 5 | // before this config file is read (for example lint rules from the tslint-microsoft-contrib 6 | // project). If true, this flag will deactivate any of these rules. 7 | "removeExistingRules": true, 8 | // When true, the TSLint task is configured with some default TSLint "rules.": 9 | "useDefaultConfigAsBase": false, 10 | // Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules 11 | // which are active, other than the list of rules below. 12 | "lintConfig": { 13 | // Opt-in to Lint rules which help to eliminate bugs in JavaScript 14 | "rules": { 15 | "class-name": false, 16 | "export-name": false, 17 | "forin": false, 18 | "label-position": false, 19 | "member-access": true, 20 | "no-arg": false, 21 | "no-console": false, 22 | "no-construct": false, 23 | "no-duplicate-case": true, 24 | "no-duplicate-variable": true, 25 | "no-eval": false, 26 | "no-function-expression": true, 27 | "no-internal-module": true, 28 | "no-shadowed-variable": true, 29 | "no-switch-case-fall-through": true, 30 | "no-unnecessary-semicolons": true, 31 | "no-unused-expression": true, 32 | "no-unused-imports": true, 33 | "no-use-before-declare": true, 34 | "no-with-statement": true, 35 | "semicolon": true, 36 | "trailing-comma": false, 37 | "typedef": false, 38 | "typedef-whitespace": false, 39 | "use-named-parameter": true, 40 | "valid-typeof": true, 41 | "variable-name": false, 42 | "whitespace": false 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /GDPRStarterKit/config/write-manifests.json: -------------------------------------------------------------------------------- 1 | {"cdnBasePath":"https://publiccdn.sharepointonline.com/tenant.sharepoint.com/sites/CDN/CDNFiles/GDPRActivityHub"} -------------------------------------------------------------------------------- /GDPRStarterKit/gulpfile-update-manifest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('@microsoft/sp-build-web'); 4 | const fs = require('fs'); 5 | 6 | build.task('update-manifest', { 7 | execute: (config) => { 8 | return new Promise((resolve, reject) => { 9 | const cdnPath = config.args['cdnpath'] || ""; 10 | let json = JSON.parse(fs.readFileSync('./config/write-manifests.json')); 11 | json.cdnBasePath = cdnPath; 12 | fs.writeFileSync('./config/write-manifests.json', JSON.stringify(json)); 13 | resolve(); 14 | }); 15 | } 16 | }); -------------------------------------------------------------------------------- /GDPRStarterKit/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('gulp'); 4 | const build = require('@microsoft/sp-build-web'); 5 | 6 | require('./gulpfile-update-manifest.js'); 7 | 8 | build.initialize(gulp); 9 | -------------------------------------------------------------------------------- /GDPRStarterKit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gdpr-starter-kit", 3 | "version": "0.0.1", 4 | "private": true, 5 | "engines": { 6 | "node": ">=0.10.0" 7 | }, 8 | "dependencies": { 9 | "@microsoft/sp-client-base": "~1.0.0", 10 | "@microsoft/sp-core-library": "~1.0.0", 11 | "@microsoft/sp-webpart-base": "~1.0.0", 12 | "@types/react": "0.14.46", 13 | "@types/react-addons-shallow-compare": "0.14.17", 14 | "@types/react-addons-test-utils": "0.14.15", 15 | "@types/react-addons-update": "0.14.14", 16 | "@types/react-dom": "0.14.18", 17 | "@types/webpack-env": ">=1.12.1 <1.14.0", 18 | "react": "15.4.2", 19 | "react-dom": "15.4.2", 20 | "sp-client-custom-fields": "^1.3.4", 21 | "sp-pnp-js": "^2.0.5" 22 | }, 23 | "devDependencies": { 24 | "@microsoft/sp-build-web": "~1.0.1", 25 | "@microsoft/sp-module-interfaces": "~1.0.0", 26 | "@microsoft/sp-webpart-workbench": "~1.0.0", 27 | "gulp": "~3.9.1", 28 | "@types/chai": ">=3.4.34 <3.6.0", 29 | "@types/mocha": ">=2.2.33 <2.6.0" 30 | }, 31 | "scripts": { 32 | "build": "gulp bundle", 33 | "clean": "gulp clean", 34 | "test": "gulp test" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/GDPRBaseWebPart.ts: -------------------------------------------------------------------------------- 1 | import { Version } from '@microsoft/sp-core-library'; 2 | import { 3 | BaseClientSideWebPart, 4 | IPropertyPaneConfiguration, 5 | PropertyPaneDropdown, 6 | IPropertyPaneDropdownOption 7 | } from '@microsoft/sp-webpart-base'; 8 | 9 | import * as strings from 'gdprInsertRequestStrings'; 10 | import { IGDPRBaseWebPartProps } from './IGDPRBaseWebPartProps'; 11 | 12 | import pnp from "sp-pnp-js"; 13 | 14 | export abstract class GdprBaseWebPart extends BaseClientSideWebPart { 15 | 16 | private listsOptions: IPropertyPaneDropdownOption[]; 17 | 18 | public onInit(): Promise { 19 | 20 | return super.onInit().then(_ => { 21 | pnp.setup({ 22 | spfxContext: this.context 23 | }); 24 | }); 25 | } 26 | 27 | protected get dataVersion(): Version { 28 | return Version.parse('1.0'); 29 | } 30 | 31 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 32 | 33 | this.fetchLists().then((response) => { 34 | this.listsOptions = response; 35 | this.context.propertyPane.refresh(); 36 | }); 37 | 38 | return { 39 | pages: [ 40 | { 41 | header: { 42 | description: strings.PropertyPaneDescription 43 | }, 44 | groups: [ 45 | { 46 | groupName: strings.BasicGroupName, 47 | groupFields: [ 48 | PropertyPaneDropdown('targetList', { 49 | label: strings.TargetListFieldLabel, 50 | options: this.listsOptions 51 | }) 52 | ] 53 | } 54 | ] 55 | } 56 | ] 57 | }; 58 | } 59 | 60 | private fetchLists(): Promise { 61 | 62 | return pnp.sp.web.lists.filter("Hidden eq false").get().then((response) => { 63 | var options: Array = new Array(); 64 | response.map((list: any) => { 65 | options.push( { key: list.Id, text: list.Title }); 66 | }); 67 | 68 | return options; 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/GDPRDataManager.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { 4 | // IItem, 5 | // IGDPREvent, 6 | // IGDPRRequest, 7 | // IGDPRIncident, 8 | ITaxonomyTerm, 9 | IIncidentDataBreach, 10 | IIncidentIdentityRisk, 11 | IEventDataArchived, 12 | IEventDataConsent, 13 | IEventDataConsentWithdrawal, 14 | IEventDataProcessing, 15 | IRequestAccessPersonalData, 16 | IRequestCorrectPersonalData, 17 | IRequestErasePersonalData, 18 | IRequestExportPersonalData, 19 | IRequestObjectionToProcessing 20 | } from '../domain_model/GDPR_domain_model'; 21 | import { default as pnp, ItemAddResult, WebEnsureUserResult } from "sp-pnp-js"; 22 | 23 | export interface IGDPRDataManager { 24 | setup(settings: any): void; 25 | insertNewRequest(request: IRequestAccessPersonalData | IRequestCorrectPersonalData | 26 | IRequestErasePersonalData | IRequestExportPersonalData | 27 | IRequestObjectionToProcessing): Promise; 28 | insertNewEvent(event: IEventDataConsent | IEventDataConsentWithdrawal | 29 | IEventDataProcessing | IEventDataArchived | 30 | IIncidentDataBreach | IIncidentIdentityRisk): Promise; 31 | } 32 | 33 | export interface IGDPRDataManagerSharePointSettings { 34 | requestsListId?: string; 35 | eventsListId?: string; 36 | } 37 | 38 | export class GDPRDataManager implements IGDPRDataManager { 39 | 40 | private requestsListId: string; 41 | private eventsListId: string; 42 | 43 | public setup(settings: any): void { 44 | let s = settings; 45 | if (s != null) 46 | { 47 | if (s.requestsListId != null && s.requestsListId.length > 0) this.requestsListId = s.requestsListId; 48 | if (s.eventsListId != null && s.eventsListId.length > 0) this.eventsListId = s.eventsListId; 49 | } 50 | } 51 | 52 | public insertNewRequest(request: IRequestAccessPersonalData | IRequestCorrectPersonalData | 53 | IRequestErasePersonalData | IRequestExportPersonalData | 54 | IRequestObjectionToProcessing): Promise { 55 | 56 | return(new Promise((resolve, reject) => { 57 | this.resolveUserId(request.requestAssignedTo).then((requestAssignedToId: number) => { 58 | 59 | let mappedRequest: any = { 60 | Title: request.title, 61 | GDPRDataSubject: request.dataSubject, 62 | GDPRDataSubjectEmail: request.dataSubjectEmail, 63 | GDPRVerifiedDataSubject: request.verifiedDataSubject, 64 | GDPRRequestAssignedToId: requestAssignedToId, 65 | GDPRRequestInsertionDate: request.requestInsertionDate, 66 | GDPRRequestDueDate: request.requestDueDate, 67 | GDPRNotes: request.additionalNotes, 68 | }; 69 | 70 | switch (request.kind) 71 | { 72 | case "Access": 73 | mappedRequest.ContentTypeId = "0x0100A16621D3EDF4F141847640F9058A5B730100CAD083221153F240A5DA2C3CFDF2C451"; 74 | mappedRequest.GDPRDeliveryMethod = { 75 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 76 | Label: request.deliveryMethod.Label, 77 | TermGuid: request.deliveryMethod.TermGuid, 78 | WssId: -1 79 | }; 80 | break; 81 | case "Correct": 82 | mappedRequest.ContentTypeId = "0x0100A16621D3EDF4F141847640F9058A5B7302000457A3587EA71A4BA545274EF3432D89"; 83 | mappedRequest.GDPRCorrectionDefinition = request.correctionDefinition; 84 | break; 85 | case "Erase": 86 | mappedRequest.ContentTypeId = "0x0100A16621D3EDF4F141847640F9058A5B730300185B78202037C741B91654045C963841"; 87 | mappedRequest.GDPRNotifyExternalProcessor = request.notifyThirdParties; 88 | mappedRequest.GDPRReason = request.reason; 89 | break; 90 | case "Export": 91 | mappedRequest.ContentTypeId = "0x0100A16621D3EDF4F141847640F9058A5B730400CA60A3E25D2AFB429FCCAB901C990383"; 92 | mappedRequest.GDPRDeliveryMethod = { 93 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 94 | Label: request.deliveryMethod.Label, 95 | TermGuid: request.deliveryMethod.TermGuid, 96 | WssId: -1 97 | }; 98 | mappedRequest.GDPRDeliveryFormat = { 99 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 100 | Label: request.deliveryFormat.Label, 101 | TermGuid: request.deliveryFormat.TermGuid, 102 | WssId: -1 103 | }; 104 | break; 105 | case "Objection": 106 | mappedRequest.ContentTypeId = "0x0100A16621D3EDF4F141847640F9058A5B730500CCB415E01CEA9C41B6C9E7979D9EF06C"; 107 | mappedRequest.GDPRPersonalData = request.personalData; 108 | mappedRequest['f9daa214b1dd4b0dafb30c8f454778ec'] = this.prepareTaxonomyMultivalues(request.processingType); 109 | mappedRequest.GDPRReason = request.reason; 110 | break; 111 | } 112 | 113 | pnp.sp.web.lists.getById(this.requestsListId).items.add(mappedRequest).then((iar: ItemAddResult) => { 114 | resolve(iar.data.Id); 115 | }).catch((ex: any) => { 116 | reject(ex); 117 | }); 118 | }); 119 | })); 120 | } 121 | 122 | public insertNewEvent(event: IEventDataConsent | IEventDataConsentWithdrawal | 123 | IEventDataProcessing | IEventDataArchived | 124 | IIncidentDataBreach | IIncidentIdentityRisk): Promise { 125 | 126 | return(new Promise((resolve, reject) => { 127 | 128 | let dataProcessors: number[] = []; 129 | 130 | if (event.kind == "DataProcessing") 131 | { 132 | let processorsResolvers = event.processors.map(p => { 133 | return(this.resolveUserId(p)); 134 | }); 135 | Promise.all(processorsResolvers).then(processors => { 136 | dataProcessors = processors; 137 | }); 138 | } 139 | 140 | this.resolveUserId(event.eventAssignedTo).then((eventAssignedToId: number) => { 141 | 142 | let mappedEvent: any = { 143 | Title: event.title, 144 | GDPRNotifiedBy: event.notifiedBy, 145 | GDPRReportAssignedToId: eventAssignedToId, 146 | GDPRReportStartDateTime: event.eventStartDate, 147 | GDPRReportEndDateTime: event.eventEndDate, 148 | GDPRPostEventReport: event.postReport, 149 | GDPRNotes: event.additionalNotes, 150 | }; 151 | 152 | switch (event.kind) 153 | { 154 | case "DataConsent": 155 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC892040086569F519007EB40AE2411EDAB6A61E8"; 156 | mappedEvent.GDPRConsentIsInternal = event.consentIsInternal; 157 | if (event.includesSensitiveData) 158 | { 159 | mappedEvent.GDPRIncludesSensitiveData = { 160 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 161 | Label: event.includesSensitiveData.Label, 162 | TermGuid: event.includesSensitiveData.TermGuid, 163 | WssId: -1 164 | }; 165 | } 166 | mappedEvent.GDPRDataSubjectIsChild = event.dataSubjectIsChild; 167 | mappedEvent.GDPRIndirectDataProvider = event.indirectDataProvider; 168 | mappedEvent.GDPRDataProvider = event.dataProvider; 169 | mappedEvent.GDPRConsentNotes = event.consentNotes; 170 | mappedEvent['a0dce788628b4762be4c0fae9bee3096'] = this.prepareTaxonomyMultivalues(event.consentType); 171 | break; 172 | case "DataConsentWithdrawal": 173 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC8920500EBBB731827924A4E8CEF1F4569BFCA2D"; 174 | // mappedEvent.GDPRWithdrawalType = event.withdrawalType; // TODO: Replace with a direct call like CSOM 175 | mappedEvent['b900b350bfe4474e923938eb73598802'] = this.prepareTaxonomyMultivalues(event.withdrawalType); 176 | mappedEvent.GDPRWithdrawalNotes = event.withdrawalNotes; 177 | mappedEvent.GDPRConsentLookupAvailable = event.originalConsentAvailable; 178 | mappedEvent.GDPRConsentLookupId = event.originalConsentId; 179 | mappedEvent.GDPRNotifyExternalProcessor = event.notifyThirdParties; 180 | break; 181 | case "DataProcessing": 182 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC8920300BA08C9FB525ED848AC38713D45981877"; 183 | mappedEvent['f9daa214b1dd4b0dafb30c8f454778ec'] = this.prepareTaxonomyMultivalues(event.processingType); 184 | mappedEvent.GDPRProcessorsId = { 185 | results: dataProcessors, 186 | }; 187 | break; 188 | case "DataArchived": 189 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC89207001AD2CE357E64F546B5BEE2786EB34571"; 190 | if (event.includesSensitiveData) 191 | { 192 | mappedEvent.GDPRIncludesSensitiveData = { 193 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 194 | Label: event.includesSensitiveData.Label, 195 | TermGuid: event.includesSensitiveData.TermGuid, 196 | WssId: -1 197 | }; 198 | } 199 | mappedEvent.GDPRArchivedData = event.archivedData; 200 | mappedEvent.GDPRDataDeIdentified = event.anonymize; 201 | mappedEvent.GDPRArchivingNotes = event.archivingNotes; 202 | mappedEvent.GDPRIncludesChildrenData = event.includesChildrenData; 203 | break; 204 | case "DataBreach": 205 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC8920100CEADB5787C515947893D97E56E3E2CEC"; 206 | mappedEvent.GDPRBreachType = { 207 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 208 | Label: event.breachType.Label, 209 | TermGuid: event.breachType.TermGuid, 210 | WssId: -1 211 | }; 212 | mappedEvent.GDPRSeverity = { 213 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 214 | Label: event.severity.Label, 215 | TermGuid: event.severity.TermGuid, 216 | WssId: -1 217 | }; 218 | mappedEvent.GDPRDPANotified = event.dpaNotified; 219 | mappedEvent.GDPRDPANotificationDate = event.dpaNotificationDate; 220 | mappedEvent.GDPREstimatedAffectedDataSubject = event.estimatedNumberOfAffectedDataSubjects; // TODO: Fix during provisioning 221 | mappedEvent.GDPRIncidentToBeDetermined = event.toBeDetermined; 222 | mappedEvent.GDPRIncludesChildrenData = event.includesChildrenData; 223 | mappedEvent.GDPRIncidentInProgress = event.inProgress; 224 | mappedEvent.GDPRPlannedRecoveryAction = event.actionPlan; 225 | mappedEvent.GDPRBreachSolved = event.breachResolved; 226 | mappedEvent.GDPRActionsTaken = event.actionsTaken; 227 | break; 228 | case "IdentityRisk": 229 | mappedEvent.ContentTypeId = "0x0100B506463210A9D340A2E0E0A0889DC8920200A6F17E1F42E84E409416BF705FB5DC41"; 230 | mappedEvent['m23b2cf910684f13aff8b226db423e20'] = this.prepareTaxonomyMultivalues(event.riskType); 231 | mappedEvent.GDPRSeverity = { 232 | __metadata: { type: 'SP.Taxonomy.TaxonomyFieldValue' }, 233 | Label: event.severity.Label, 234 | TermGuid: event.severity.TermGuid, 235 | WssId: -1 236 | }; 237 | break; 238 | } 239 | 240 | pnp.sp.web.lists.getById(this.eventsListId).items.add(mappedEvent).then((iar: ItemAddResult) => { 241 | resolve(iar.data.Id); 242 | }).catch((ex: any) => { 243 | reject(ex); 244 | }); 245 | }); 246 | })); 247 | } 248 | 249 | private resolveUserId(username: string) : Promise { 250 | return(new Promise((resolve, reject) => { 251 | if (username != undefined) 252 | { 253 | pnp.sp.web.ensureUser("i:0#.f|membership|" + username) 254 | .then((result: WebEnsureUserResult) => { 255 | resolve(result.data.Id); 256 | }) 257 | .catch((e: any) => { 258 | reject(e); 259 | }); 260 | } 261 | else 262 | { 263 | resolve(0); 264 | } 265 | })); 266 | } 267 | 268 | private prepareTaxonomyMultivalues(terms: ITaxonomyTerm[]) : string { 269 | 270 | let termsValuesString: string = ""; 271 | terms.forEach(t => { 272 | termsValuesString += "-1;#" + t.Label + "|" + t.TermGuid + ";#"; 273 | }); 274 | termsValuesString = termsValuesString.substring(0, termsValuesString.length - 1); 275 | 276 | return(termsValuesString); 277 | } 278 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/GDPRStyles.module.scss: -------------------------------------------------------------------------------- 1 | .pickerRoot { 2 | 3 | .pickerSuggestedItem { 4 | padding-top: 5px; 5 | padding-bottom: 5px; 6 | } 7 | 8 | .pickerSuggestedItemIcon, .pickerSelectedItemIcon { 9 | padding-right: 5px; 10 | } 11 | 12 | .pickerSuggestedItemText { 13 | text-overflow: ellipsis; 14 | } 15 | 16 | .pickerSelectedItem { 17 | vertical-align: top; 18 | } 19 | 20 | .pickerSelectedItemText { 21 | text-overflow: ellipsis; 22 | vertical-align: top; 23 | } 24 | 25 | .pickerSelectedItemClose { 26 | vertical-align: top; 27 | } 28 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/GDPRUtility.ts: -------------------------------------------------------------------------------- 1 | export class GDPRUtility { 2 | 3 | public static getInitials(fullname: string): string { 4 | 5 | if (!fullname) { 6 | return (null); 7 | } 8 | 9 | var parts = fullname.split(' '); 10 | 11 | var initials = ""; 12 | parts.forEach(p => { 13 | if (p.length > 0) 14 | { 15 | initials = initials.concat(p.substring(0, 1).toUpperCase()); 16 | } 17 | }); 18 | 19 | return (initials); 20 | } 21 | 22 | public static getPersonaImage(siteUrl: string, accountName: string): string { 23 | let tenantBaseUrl: string = siteUrl.substring(0, siteUrl.indexOf("sharepoint.com") + 14); 24 | let imageBaseUrl = tenantBaseUrl + "/_layouts/15/userphoto.aspx?size=S&accountname="; 25 | 26 | if (accountName.indexOf("|membership|") > 0) { 27 | accountName = accountName.substring(accountName.indexOf("|membership|") + 12); 28 | } 29 | 30 | return (imageBaseUrl + accountName); 31 | } 32 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/IGDPRBaseWebPartProps.ts: -------------------------------------------------------------------------------- 1 | export interface IGDPRBaseWebPartProps { 2 | targetList: string; 3 | } 4 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPDateTimePickerProps.ts: -------------------------------------------------------------------------------- 1 | export interface ISPDateTimePickerProps { 2 | dateLabel: string; 3 | datePlaceholder?: string; 4 | hoursLabel?: string; 5 | hoursValidationError?: string; 6 | minutesLabel?: string; 7 | minutesValidationError?: string; 8 | secondsLabel?: string; 9 | secondsValidationError?: string; 10 | isRequired: boolean; 11 | initialDateTime?: Date; 12 | showTime?: boolean; 13 | includeSeconds?: boolean; 14 | formatDate?: (date: Date) => string; 15 | 16 | onChanged?: (newValue: Date) => void; 17 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPDateTimePickerState.ts: -------------------------------------------------------------------------------- 1 | export interface ISPDateTimePickerState { 2 | date?: Date; 3 | hours?: number; 4 | minutes?: number; 5 | seconds?: number; 6 | fullDate?: string; 7 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPLookupItemsPickerProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface ISPLookupItemsPickerProps { 6 | context: IWebPartContext; 7 | sourceListId: string; 8 | label: string; 9 | placeholder: string; 10 | itemLimit?: number; 11 | required: boolean; 12 | 13 | onChanged?: (itemsIds: number[]) => void; 14 | } 15 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPLookupItemsPickerState.ts: -------------------------------------------------------------------------------- 1 | export interface ISPLookupItemsPickerState { 2 | itemsIds: number[]; 3 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPPeoplePickerProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface ISPPeoplePickerProps { 6 | context: IWebPartContext; 7 | itemLimit?: number; 8 | label: string; 9 | placeholder: string; 10 | required?: boolean; 11 | 12 | onChanged?: (items: string[]) => void; 13 | } 14 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPPeoplePickerState.ts: -------------------------------------------------------------------------------- 1 | export interface ISPPeoplePickerState { 2 | items: string[]; 3 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPTaxonomyPickerProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | import { ISPTermObject } from './SPTermStoreService'; 6 | 7 | export interface ISPTaxonomyPickerProps { 8 | context: IWebPartContext; 9 | termSetName: string; 10 | label: string; 11 | placeholder: string; 12 | required: boolean; 13 | 14 | allowMultipleSelections?: boolean; 15 | excludeOfflineTermStores?: boolean; 16 | excludeSystemGroup?: boolean; 17 | displayOnlyTermSetsAvailableForTagging?: boolean; 18 | 19 | onChanged?: (terms: ISPTermObject[]) => void; 20 | } 21 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/ISPTaxonomyPickerState.ts: -------------------------------------------------------------------------------- 1 | import { ISPTermObject } from './SPTermStoreService'; 2 | 3 | export interface ISPTaxonomyPickerState { 4 | terms: ISPTermObject[]; 5 | loaded: boolean; 6 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/PeopleSearchQuery.ts: -------------------------------------------------------------------------------- 1 | export class PeopleSearchQuery { 2 | public queryParams: PeopleSearchQueryParams; 3 | 4 | /** 5 | * Default constructor 6 | */ 7 | constructor(params: PeopleSearchQueryParams) { 8 | this.queryParams = params; 9 | } 10 | } 11 | 12 | export class PeopleSearchQueryParams { 13 | public QueryString: string; 14 | public MaximumEntitySuggestions: number; 15 | public AllowEmailAddresses: boolean; 16 | public AllowOnlyEmailAddresses: boolean; 17 | public PrincipalType: number; 18 | public PrincipalSource: number; 19 | public SharePointGroupID: number; 20 | } 21 | 22 | export interface IPeopleSearchQueryResult { 23 | value: string; 24 | } 25 | 26 | export interface IPeopleSearchQueryResultItem { 27 | Key : string; 28 | Description: string; 29 | DisplayText: string; 30 | EntityType: string; 31 | ProviderDisplayName: string; 32 | ProviderName: string; 33 | IsResolved: boolean; 34 | EntityData: IPeopleSearchQueryResultItemEntityData; 35 | } 36 | 37 | export interface IPeopleSearchQueryResultItemEntityData { 38 | IsAltSecIdPresent: boolean; 39 | Title: string; 40 | Email: string; 41 | MobilePhone: string; 42 | ObjectId: string; 43 | Department: string; 44 | MultipleMatches: boolean; 45 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/SPDateTimePicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { ISPDateTimePickerProps } from './ISPDateTimePickerProps'; 4 | import { ISPDateTimePickerState } from './ISPDateTimePickerState'; 5 | 6 | /** 7 | * Common Infrastructure 8 | */ 9 | import { 10 | BaseComponent, 11 | assign, 12 | autobind 13 | } from 'office-ui-fabric-react/lib/Utilities'; 14 | 15 | /** 16 | * Label 17 | */ 18 | import { Label } from 'office-ui-fabric-react/lib/Label'; 19 | 20 | /** 21 | * Text Field 22 | */ 23 | import { TextField } from 'office-ui-fabric-react/lib/TextField'; 24 | import { Icon } from 'office-ui-fabric-react/lib/Icon'; 25 | 26 | /** 27 | * Date Picker 28 | */ 29 | import { 30 | DatePicker, 31 | DayOfWeek, 32 | IDatePickerStrings 33 | } from 'office-ui-fabric-react/lib/DatePicker'; 34 | 35 | const DayPickerStrings: IDatePickerStrings = { 36 | months: [ 37 | 'January', 38 | 'February', 39 | 'March', 40 | 'April', 41 | 'May', 42 | 'June', 43 | 'July', 44 | 'August', 45 | 'September', 46 | 'October', 47 | 'November', 48 | 'December' 49 | ], 50 | 51 | shortMonths: [ 52 | 'Jan', 53 | 'Feb', 54 | 'Mar', 55 | 'Apr', 56 | 'May', 57 | 'Jun', 58 | 'Jul', 59 | 'Aug', 60 | 'Sep', 61 | 'Oct', 62 | 'Nov', 63 | 'Dec' 64 | ], 65 | 66 | days: [ 67 | 'Sunday', 68 | 'Monday', 69 | 'Tuesday', 70 | 'Wednesday', 71 | 'Thursday', 72 | 'Friday', 73 | 'Saturday' 74 | ], 75 | 76 | shortDays: [ 77 | 'S', 78 | 'M', 79 | 'T', 80 | 'W', 81 | 'T', 82 | 'F', 83 | 'S' 84 | ], 85 | 86 | goToToday: 'Go to today', 87 | isRequiredErrorMessage: 'Field is required.', 88 | invalidInputErrorMessage: 'Invalid date format.' 89 | }; 90 | 91 | export interface IDatePickerRequiredExampleState { 92 | firstDayOfWeek?: DayOfWeek; 93 | } 94 | 95 | export class SPDateTimePicker extends React.Component { 96 | 97 | /** 98 | * Constructor 99 | */ 100 | constructor(props: ISPDateTimePickerProps) { 101 | super(props); 102 | 103 | this.state = { 104 | date: (this.props.initialDateTime != null) ? new Date(this.props.initialDateTime) : null, 105 | hours: (this.props.initialDateTime != null) ? new Date(this.props.initialDateTime).getHours() : 0, 106 | minutes: (this.props.initialDateTime != null) ? new Date(this.props.initialDateTime).getMinutes() : 0, 107 | seconds: (this.props.initialDateTime != null) ? new Date(this.props.initialDateTime).getSeconds() : 0, 108 | }; 109 | } 110 | 111 | public render(): React.ReactElement { 112 | return ( 113 |
114 |
115 |
116 | 124 |
125 | { (this.props.showTime) ? 126 |
127 | 134 |
135 | : null 136 | } 137 | { (this.props.showTime) ? 138 |
139 | 146 |
147 | : null 148 | } 149 | { (this.props.showTime && this.props.includeSeconds) ? 150 |
151 | 158 |
159 | : null 160 | } 161 |
162 |
163 | ); 164 | } 165 | 166 | @autobind 167 | private _dateSelected (date: Date) : void { 168 | if (date == null) 169 | return; 170 | this.state.date = date; 171 | this.setState(this.state); 172 | this.saveFullDate(); 173 | } 174 | 175 | @autobind 176 | private _hoursChanged(value: string) : void { 177 | this.state.hours = Number(value); 178 | this.setState(this.state); 179 | this.saveFullDate(); 180 | } 181 | 182 | @autobind 183 | private _getErrorMessageHours(value: string): string { 184 | let hoursValue = Number(value); 185 | return (hoursValue >= 0 && hoursValue <= 23) 186 | ? '' 187 | : `${this.props.hoursValidationError}.`; 188 | } 189 | 190 | @autobind 191 | private _minutesChanged(newValue: string) : void { 192 | this.state.minutes = Number(newValue); 193 | this.setState(this.state); 194 | this.saveFullDate(); 195 | } 196 | 197 | @autobind 198 | private _getErrorMessageMinutes(value: string): string { 199 | let minutesValue = Number(value); 200 | return (minutesValue >= 0 && minutesValue <= 59) 201 | ? '' 202 | : `${this.props.minutesValidationError}.`; 203 | } 204 | 205 | @autobind 206 | private _secondsChanged(newValue: string) : void { 207 | this.state.seconds = Number(newValue); 208 | this.setState(this.state); 209 | this.saveFullDate(); 210 | } 211 | 212 | @autobind 213 | private _getErrorMessageSeconds(value: string): string { 214 | let secondsValue = Number(value); 215 | return (secondsValue >= 0 && secondsValue <= 59) 216 | ? '' 217 | : `${this.props.secondsValidationError}.`; 218 | } 219 | 220 | private saveFullDate(): void { 221 | if (this.state.date == null) 222 | return; 223 | var finalDate = new Date(this.state.date.toISOString()); 224 | finalDate.setHours(this.state.hours); 225 | finalDate.setMinutes(this.state.minutes); 226 | finalDate.setSeconds(this.props.includeSeconds ? this.state.seconds : 0); 227 | 228 | if (finalDate != null) { 229 | var finalDateAsString: string = ''; 230 | if (this.props.formatDate) { 231 | finalDateAsString = this.props.formatDate(finalDate); 232 | } 233 | else { 234 | finalDateAsString = finalDate.toString(); 235 | } 236 | } 237 | 238 | this.state.fullDate = finalDateAsString; 239 | this.setState(this.state); 240 | 241 | if (this.props.onChanged != null) 242 | { 243 | this.props.onChanged(finalDate); 244 | } 245 | }} -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/SPLookupItemsPicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './GDPRStyles.module.scss'; 3 | 4 | import pnp from "sp-pnp-js"; 5 | 6 | import { ISPLookupItemsPickerProps } from './ISPLookupItemsPickerProps'; 7 | import { ISPLookupItemsPickerState } from './ISPLookupItemsPickerState'; 8 | 9 | /** 10 | * Common Infrastructure 11 | */ 12 | import { 13 | BaseComponent, 14 | assign, 15 | autobind, 16 | css 17 | } from 'office-ui-fabric-react/lib/Utilities'; 18 | 19 | /** 20 | * Label 21 | */ 22 | import { Label } from 'office-ui-fabric-react/lib/Label'; 23 | 24 | import { 25 | IBasePickerProps, 26 | BasePickerListBelow, 27 | BaseAutoFill, 28 | IPickerItemProps 29 | } from 'office-ui-fabric-react/lib/Pickers'; 30 | 31 | import { Icon } from 'office-ui-fabric-react/lib/Icon'; 32 | 33 | export interface ISPItemProps { 34 | itemId?: number; 35 | title?: string; 36 | } 37 | 38 | export interface ISPLookupItemPickerProps extends IBasePickerProps { 39 | } 40 | 41 | export class SPLookupItemsPickerControl extends BasePickerListBelow { 42 | } 43 | 44 | export const SPLookupSuggestedItem: (itemProps: ISPItemProps) => JSX.Element = (itemProps: ISPItemProps) => { 45 | return ( 46 |
47 | 48 | 49 | { itemProps.title } 50 | 51 |
52 | ); 53 | }; 54 | 55 | export const SPLookupSelectedItem: (itemProps: IPickerItemProps) => JSX.Element = (itemProps: IPickerItemProps) => { 56 | 57 | return ( 58 |
63 | 64 | 65 | 66 | { itemProps.item.title } 67 | 68 | 69 | 70 |
71 | ); 72 | }; 73 | 74 | export class SPLookupItemsPicker extends React.Component { 75 | 76 | /** 77 | * 78 | */ 79 | constructor(props: ISPLookupItemsPickerProps) { 80 | super(props); 81 | 82 | this.state = { 83 | itemsIds: [], 84 | }; 85 | } 86 | 87 | public render(): React.ReactElement { 88 | 89 | pnp.setup({ 90 | spfxContext: this.props.context, 91 | }); 92 | 93 | return ( 94 |
95 | 96 | props.title } 102 | pickerSuggestionsProps={ 103 | { 104 | suggestionsHeaderText: 'Suggested Items', 105 | noResultsFoundText: 'No Items Found', 106 | loadingText: 'Loading', 107 | } 108 | } 109 | /> 110 |
111 | ); 112 | } 113 | 114 | @autobind 115 | private _onChangeLookupItemsPicker(items?: ISPItemProps[]): void{ 116 | 117 | /** Empty the array */ 118 | this.state.itemsIds = new Array(); 119 | 120 | /** Fill it with new items */ 121 | items.forEach((i: ISPItemProps) => { 122 | this.state.itemsIds.push(i.itemId); 123 | }); 124 | this.setState(this.state); 125 | 126 | if (this.props.onChanged != null) 127 | { 128 | this.props.onChanged(this.state.itemsIds); 129 | } 130 | } 131 | 132 | @autobind 133 | private _onFilterChangedLookupItemsPicker(filterText: string, currentItems: ISPItemProps[]) : Promise { 134 | 135 | if (filterText.length >= 3 && this.props.sourceListId) { 136 | 137 | let filteredLookupItems: ISPItemProps[] = new Array(); 138 | 139 | return(pnp.sp.web.lists.getById(this.props.sourceListId).items 140 | .filter("startswith(Title, '" + filterText + "')") 141 | .get().then((response) => { 142 | let items: Array = new Array(); 143 | response.map((i: any) => { 144 | items.push( { itemId: i.Id, title: i.Title }); 145 | }); 146 | return items; 147 | })); 148 | } 149 | } 150 | } 151 | 152 | 153 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/SPPeoplePicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { 4 | SPHttpClient, 5 | SPHttpClientResponse, 6 | ISPHttpClientOptions 7 | } from '@microsoft/sp-http'; 8 | 9 | import { ISPPeoplePickerProps } from './ISPPeoplePickerProps'; 10 | import { ISPPeoplePickerState } from './ISPPeoplePickerState'; 11 | import { GDPRUtility } from './GDPRUtility'; 12 | import * as PeopleSearch from './PeopleSearchQuery'; 13 | 14 | /** 15 | * Common Infrastructure 16 | */ 17 | import { 18 | BaseComponent, 19 | assign, 20 | autobind, 21 | css 22 | } from 'office-ui-fabric-react/lib/Utilities'; 23 | 24 | /** 25 | * People Picker 26 | */ 27 | import { IPersonaProps, PersonaPresence } from 'office-ui-fabric-react/lib/Persona'; 28 | import { 29 | IBasePickerSuggestionsProps, 30 | NormalPeoplePicker 31 | } from 'office-ui-fabric-react/lib/Pickers'; 32 | import { IPersonaWithMenu } from 'office-ui-fabric-react/lib/components/pickers/PeoplePicker/PeoplePickerItems/PeoplePickerItem.Props'; 33 | 34 | /** 35 | * Label 36 | */ 37 | import { Label } from 'office-ui-fabric-react/lib/Label'; 38 | 39 | export interface IPeoplePickerExampleState { 40 | currentPicker?: number | string; 41 | delayResults?: boolean; 42 | } 43 | 44 | const suggestionProps: IBasePickerSuggestionsProps = { 45 | suggestionsHeaderText: 'Suggested People', 46 | noResultsFoundText: 'No results found', 47 | loadingText: 'Loading' 48 | }; 49 | 50 | export class SPPeoplePicker extends React.Component { 51 | 52 | /** 53 | * Constructor 54 | */ 55 | constructor(props: ISPPeoplePickerProps) { 56 | super(props); 57 | 58 | this.state = { 59 | items: [], 60 | }; 61 | } 62 | 63 | public render(): React.ReactElement { 64 | 65 | return ( 66 |
67 | 68 | persona.primaryText } 72 | pickerSuggestionsProps={ suggestionProps } 73 | className={ 'ms-PeoplePicker' } 74 | key={ 'normal' } 75 | /> 76 |
77 | ); 78 | 79 | 80 | } 81 | 82 | @autobind 83 | private _onChangePeoplePicker(items?: IPersonaProps[]): void{ 84 | 85 | /** Empty the array */ 86 | this.state.items = new Array(); 87 | 88 | /** Fill it with new items */ 89 | items.forEach((i: IPersonaProps) => { 90 | this.state.items.push(i.secondaryText); 91 | }); 92 | this.setState(this.state); 93 | 94 | if (this.props.onChanged != null) 95 | { 96 | this.props.onChanged(this.state.items); 97 | } 98 | } 99 | 100 | @autobind 101 | private _onFilterChangedPeoplePicker(filterText: string, currentPersonas: IPersonaProps[], limitResults?: number) { 102 | 103 | if (filterText) { 104 | 105 | let filteredPersonas: IPersonaProps[] = new Array(); 106 | 107 | let siteUrl: string = this.props.context.pageContext.site.absoluteUrl; 108 | let tenantBaseUrl: string = siteUrl.substring(0, siteUrl.indexOf("sharepoint.com") + 14); 109 | let peopleSearchUrl = tenantBaseUrl + "/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.ClientPeoplePickerSearchUser"; 110 | let imageBaseUrl = tenantBaseUrl + "/_layouts/15/userphoto.aspx?size=S&accountname="; 111 | 112 | let searchRequest : ISPHttpClientOptions = { 113 | body: JSON.stringify(new PeopleSearch.PeopleSearchQuery( 114 | { 115 | QueryString: filterText, 116 | MaximumEntitySuggestions: 10, 117 | AllowEmailAddresses: true, 118 | AllowOnlyEmailAddresses: false, 119 | PrincipalType: 1, 120 | PrincipalSource: 15, 121 | SharePointGroupID: 0, 122 | }) 123 | ) 124 | }; 125 | 126 | return(this.props.context.spHttpClient.post( 127 | peopleSearchUrl, 128 | SPHttpClient.configurations.v1, 129 | searchRequest) 130 | .then((response: SPHttpClientResponse) => { 131 | return(response.json()); 132 | }) 133 | .then((people: PeopleSearch.IPeopleSearchQueryResult) => { 134 | JSON.parse(people.value).forEach(p => { 135 | filteredPersonas.push({ 136 | primaryText: p.DisplayText, 137 | secondaryText: p.Description, 138 | imageInitials: GDPRUtility.getInitials(p.DisplayText), 139 | presence: PersonaPresence.none, 140 | imageUrl: imageBaseUrl + p.Description, 141 | }); 142 | }); 143 | 144 | return (filteredPersonas); 145 | })); 146 | } else { 147 | return []; 148 | } 149 | } 150 | 151 | private getInitials(fullname: string): string { 152 | var parts = fullname.split(' '); 153 | 154 | var initials = ""; 155 | parts.forEach(p => { 156 | if (p.length > 0) 157 | { 158 | initials = initials.concat(p.substring(0, 1).toUpperCase()); 159 | } 160 | }); 161 | 162 | return (initials); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/SPTaxonomyPicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './GDPRStyles.module.scss'; 3 | 4 | import { ISPTaxonomyPickerProps } from './ISPTaxonomyPickerProps'; 5 | import { ISPTaxonomyPickerState } from './ISPTaxonomyPickerState'; 6 | import { 7 | SPTermStoreService, 8 | ISPTermObject 9 | } from './SPTermStoreService'; 10 | 11 | /** 12 | * Common Infrastructure 13 | */ 14 | import { 15 | BaseComponent, 16 | assign, 17 | autobind, 18 | css 19 | } from 'office-ui-fabric-react/lib/Utilities'; 20 | 21 | /** 22 | * Label 23 | */ 24 | import { Label } from 'office-ui-fabric-react/lib/Label'; 25 | 26 | import { 27 | IBasePickerProps, 28 | BasePickerListBelow, 29 | BaseAutoFill, 30 | IPickerItemProps 31 | } from 'office-ui-fabric-react/lib/Pickers'; 32 | 33 | import { Icon } from 'office-ui-fabric-react/lib/Icon'; 34 | 35 | export interface ISPTaxonomyTermProps { 36 | termId?: string; 37 | name?: string; 38 | } 39 | 40 | export interface ISPTaxonomyTermPickerProps extends IBasePickerProps { 41 | } 42 | 43 | export class SPTaxonomyPickerControl extends BasePickerListBelow { 44 | } 45 | 46 | export const SPTaxonomySuggestedItem: (termProps: ISPTaxonomyTermProps) => JSX.Element = (termProps: ISPTaxonomyTermProps) => { 47 | return ( 48 |
49 | 50 | 51 | { termProps.name } 52 | 53 |
54 | ); 55 | }; 56 | 57 | export const SPTaxonomySelectedItem: (termProps: IPickerItemProps) => JSX.Element = (termProps: IPickerItemProps) => { 58 | 59 | return ( 60 |
65 | 66 | 67 | 68 | { termProps.item.name } 69 | 70 | 71 | 72 |
73 | ); 74 | }; 75 | 76 | export class SPTaxonomyPicker extends React.Component { 77 | 78 | private terms: ISPTermObject[]; 79 | 80 | /** 81 | * 82 | */ 83 | constructor(props: ISPTaxonomyPickerProps) { 84 | super(props); 85 | 86 | let termsService: SPTermStoreService = new SPTermStoreService(this.props); 87 | termsService.getTermsFromTermSet(this.props.termSetName).then((response: ISPTermObject[]) => { 88 | this.terms = response; 89 | }); 90 | 91 | this.state = { 92 | terms: [], 93 | loaded: false, 94 | }; 95 | } 96 | 97 | public render(): React.ReactElement { 98 | 99 | return ( 100 |
101 | 102 | props.name } 108 | pickerSuggestionsProps={ 109 | { 110 | suggestionsHeaderText: 'Suggested Items', 111 | noResultsFoundText: 'No Items Found', 112 | loadingText: 'Loading', 113 | } 114 | } 115 | /> 116 |
117 | ); 118 | } 119 | 120 | @autobind 121 | private _onChangeTaxonomyPicker(items?: ISPTaxonomyTermProps[]): void{ 122 | 123 | /** Empty the array */ 124 | this.state.terms = new Array(); 125 | 126 | /** Fill it with new items */ 127 | items.forEach((i: ISPTaxonomyTermProps) => { 128 | this.state.terms.push( { name: i.name, guid: i.termId }); 129 | }); 130 | this.setState(this.state); 131 | 132 | if (this.props.onChanged != null) 133 | { 134 | this.props.onChanged(this.state.terms); 135 | } 136 | } 137 | 138 | @autobind 139 | private _onFilterChangedTaxonomyPicker(filterText: string, currentItems: ISPTaxonomyTermProps[]) : ISPTaxonomyTermProps[] { 140 | 141 | if (filterText.length >= 3 && this.props.termSetName && this.terms != null && this.terms.length > 0) { 142 | 143 | let items: Array = new Array(); 144 | this.terms.forEach((t: ISPTermObject) => { 145 | if (t.name.toLowerCase().indexOf(filterText.toLowerCase()) >= 0) 146 | items.push({ termId: t.guid.toString(), name: t.name }); 147 | }); 148 | 149 | return items; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/components/SPTermStoreService.ts: -------------------------------------------------------------------------------- 1 | import { IWebPartContext} from '@microsoft/sp-webpart-base'; 2 | import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; 3 | import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http'; 4 | 5 | import { ISPTaxonomyPickerProps } from './ISPTaxonomyPickerProps'; 6 | 7 | /** 8 | * @interface 9 | * Generic Term Object (abstract interface) 10 | */ 11 | export interface ISPTermObject { 12 | name: string; 13 | guid: string; 14 | } 15 | 16 | /** 17 | * @class 18 | * Service implementation to manage term stores in SharePoint 19 | * Basic implementation taken from: https://oliviercc.github.io/sp-client-custom-fields/ 20 | */ 21 | export class SPTermStoreService { 22 | 23 | private context: IWebPartContext; 24 | private props: ISPTaxonomyPickerProps; 25 | private taxonomySession: string; 26 | private formDigest: string; 27 | 28 | /** 29 | * @function 30 | * Service constructor 31 | */ 32 | constructor(props: ISPTaxonomyPickerProps){ 33 | this.props = props; 34 | this.context = props.context; 35 | } 36 | 37 | /** 38 | * @function 39 | * Gets the collection of term stores in the current SharePoint env 40 | */ 41 | public getTermsFromTermSet(termSet: string): Promise { 42 | if (Environment.type === EnvironmentType.SharePoint || 43 | Environment.type === EnvironmentType.ClassicSharePoint) { 44 | //First gets the FORM DIGEST VALUE 45 | var contextInfoUrl: string = this.context.pageContext.web.absoluteUrl + "/_api/contextinfo"; 46 | var httpPostOptions: ISPHttpClientOptions = { 47 | headers: { 48 | "accept": "application/json", 49 | "content-type": "application/json" 50 | } 51 | }; 52 | return this.context.spHttpClient.post(contextInfoUrl, SPHttpClient.configurations.v1, httpPostOptions).then((response: SPHttpClientResponse) => { 53 | return response.json().then((jsonResponse: any) => { 54 | this.formDigest = jsonResponse.FormDigestValue; 55 | 56 | //Build the Client Service Request 57 | var clientServiceUrl = this.context.pageContext.web.absoluteUrl + '/_vti_bin/client.svc/ProcessQuery'; 58 | var data = '' + termSet + '1033'; 59 | httpPostOptions = { 60 | headers: { 61 | 'accept': 'application/json', 62 | 'content-type': 'application/json', 63 | "X-RequestDigest": this.formDigest 64 | }, 65 | body: data 66 | }; 67 | return this.context.spHttpClient.post(clientServiceUrl, SPHttpClient.configurations.v1, httpPostOptions).then((serviceResponse: SPHttpClientResponse) => { 68 | return serviceResponse.json().then((serviceJSONResponse: Array) => { 69 | 70 | let result: Array = new Array(); 71 | 72 | serviceJSONResponse.forEach((child: any) => { 73 | if (child != null && child['_ObjectType_'] !== undefined) 74 | { 75 | var termSetCollectionType = child['_ObjectType_']; 76 | if (termSetCollectionType === "SP.Taxonomy.TermSetCollection") 77 | { 78 | var childTermSets = child['_Child_Items_']; 79 | childTermSets.forEach((ts: any) => { 80 | 81 | var termSetType = ts['_ObjectType_']; 82 | if (termSetType === "SP.Taxonomy.TermSet") 83 | { 84 | var termCollection = ts['Terms']; 85 | var childTerms = termCollection['_Child_Items_']; 86 | childTerms.forEach((t: any) => { 87 | var termType = t['_ObjectType_']; 88 | if (termType === "SP.Taxonomy.Term") 89 | { 90 | result.push({ guid: this.cleanGuid(t['Id']), name: t["Name"] }); 91 | } 92 | }); 93 | } 94 | }); 95 | } 96 | } 97 | }); 98 | 99 | return(result); 100 | }); 101 | }); 102 | 103 | }); 104 | }); 105 | } 106 | else 107 | { 108 | return (new Promise>((resolve, reject) => { 109 | resolve(new Array()); 110 | })); 111 | } 112 | } 113 | 114 | /** 115 | * @function 116 | * Clean the Guid from the Web Service response 117 | * @param guid 118 | */ 119 | private cleanGuid(guid: string): string { 120 | if (guid !== undefined) 121 | return guid.replace('/Guid(', '').replace('/', '').replace(')', ''); 122 | else 123 | return ''; 124 | } 125 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/domain_model/GDPR_domain_model.ts: -------------------------------------------------------------------------------- 1 | export interface ITaxonomyTerm { 2 | Label?: string | number; 3 | TermGuid: string; 4 | WssId?: number; 5 | } 6 | 7 | export interface IItem { 8 | kind: string; 9 | title: string; 10 | } 11 | 12 | export interface IGDPRContact extends IItem { 13 | role: ITaxonomyTerm; 14 | user: string; 15 | } 16 | 17 | export interface IGDPREvent extends IItem { 18 | notifiedBy: string; 19 | eventAssignedTo: string; 20 | eventStartDate: any; 21 | eventEndDate: any; 22 | postReport: string; 23 | additionalNotes: string; 24 | } 25 | 26 | export interface IGDPRRequest extends IItem { 27 | dataSubject: string; 28 | dataSubjectEmail: string; 29 | verifiedDataSubject: boolean; 30 | requestAssignedTo: string; 31 | requestInsertionDate: any; 32 | requestDueDate: any; 33 | additionalNotes: string; 34 | } 35 | 36 | export interface IGDPRIncident extends IGDPREvent { 37 | severity: ITaxonomyTerm; 38 | } 39 | 40 | export interface IIncidentDataBreach extends IGDPRIncident { 41 | kind: "DataBreach"; 42 | breachType: ITaxonomyTerm; 43 | dpaNotified: boolean; 44 | dpaNotificationDate: any; 45 | estimatedNumberOfAffectedDataSubjects: number; 46 | toBeDetermined: boolean; 47 | includesChildrenData: boolean; 48 | inProgress: boolean; 49 | actionPlan: string; 50 | breachResolved: boolean; 51 | actionsTaken: string; 52 | } 53 | 54 | export interface IIncidentIdentityRisk extends IGDPRIncident { 55 | kind: "IdentityRisk"; 56 | riskType: Array; 57 | } 58 | 59 | export interface IEventDataArchived extends IGDPREvent { 60 | kind: "DataArchived"; 61 | archivedData: string; 62 | includesSensitiveData: ITaxonomyTerm; 63 | includesChildrenData: boolean; 64 | anonymize: boolean; 65 | archivingNotes: string; 66 | } 67 | 68 | export interface IEventDataConsent extends IGDPREvent { 69 | kind: "DataConsent"; 70 | consentIsInternal: boolean; 71 | includesSensitiveData: ITaxonomyTerm; 72 | dataSubjectIsChild: boolean; 73 | indirectDataProvider: boolean; 74 | dataProvider: string; 75 | consentNotes: string; 76 | consentType: Array; 77 | } 78 | 79 | export interface IEventDataConsentWithdrawal extends IGDPREvent { 80 | kind: "DataConsentWithdrawal"; 81 | withdrawalType: Array; 82 | withdrawalNotes: string; 83 | originalConsentAvailable: boolean; 84 | originalConsentId: string; 85 | notifyThirdParties: boolean; 86 | } 87 | 88 | export interface IEventDataProcessing extends IGDPREvent { 89 | kind: "DataProcessing"; 90 | processingType: Array; 91 | processors: Array; 92 | } 93 | 94 | export interface IRequestAccessPersonalData extends IGDPRRequest { 95 | kind: "Access"; 96 | deliveryMethod: ITaxonomyTerm; 97 | } 98 | 99 | export interface IRequestCorrectPersonalData extends IGDPRRequest { 100 | kind: "Correct"; 101 | correctionDefinition: string; 102 | } 103 | 104 | export interface IRequestErasePersonalData extends IGDPRRequest { 105 | kind: "Erase"; 106 | notifyThirdParties: boolean; 107 | reason: string; 108 | } 109 | 110 | export interface IRequestExportPersonalData extends IGDPRRequest { 111 | kind: "Export"; 112 | deliveryMethod: ITaxonomyTerm; 113 | deliveryFormat: ITaxonomyTerm; 114 | } 115 | 116 | export interface IRequestObjectionToProcessing extends IGDPRRequest { 117 | kind: "Objection"; 118 | personalData: string; 119 | processingType: Array; 120 | reason: string; 121 | } 122 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/GdprDashboardWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json", 3 | 4 | "id": "8805cdf9-9e29-4c17-b0a5-797950a6a4bd", 5 | "alias": "GdprDashboardWebPart", 6 | "componentType": "WebPart", 7 | "version": "0.0.1", 8 | "manifestVersion": 2, 9 | 10 | "preconfiguredEntries": [{ 11 | "groupId": "8805cdf9-9e29-4c17-b0a5-797950a6a4bd", 12 | "group": { "default": "GDPR" }, 13 | "title": { "default": "GDPR Dashboard" }, 14 | "description": { "default": "GDPR Starter Kit Dashboard" }, 15 | "officeFabricIconFontName": "TaskManager", 16 | "properties": { 17 | "phasesImageUrl": "", 18 | "phasesMap": "" 19 | } 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/GdprDashboardWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | 4 | import GdprDashboard from './components/GdprDashboard'; 5 | import { IGdprDashboardProps } from './components/IGdprDashboardProps'; 6 | import { GdprBaseWebPart } from '../../components/GDPRBaseWebPart'; 7 | 8 | export default class GdprDashboardWebPart extends GdprBaseWebPart { 9 | 10 | private _gdprDashboardComponent: GdprDashboard; 11 | 12 | public render(): void { 13 | const element: React.ReactElement = React.createElement( 14 | GdprDashboard, 15 | { 16 | context: this.context, 17 | targetList: this.properties.targetList, 18 | } 19 | ); 20 | 21 | this._gdprDashboardComponent = ReactDom.render(element, this.domElement); 22 | } 23 | 24 | protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { 25 | /* 26 | Check the property path to see which property pane feld changed. If the property path matches the dropdown, then we set that list 27 | as the selected list for the web part. 28 | */ 29 | if (propertyPath === 'targetList') { 30 | this._gdprDashboardComponent.props.targetList = this.properties.targetList; 31 | } 32 | 33 | /* 34 | Finally, tell property pane to re-render the web part. 35 | This is valid for reactive property pane. 36 | */ 37 | super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/assets/GDPR-Phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/GDPRStarterKit/src/webparts/gdprDashboard/assets/GDPR-Phases.png -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/GdprDashboard.module.scss: -------------------------------------------------------------------------------- 1 | .gdprDashboard { 2 | .container { 3 | max-width: 700px; 4 | margin: 0px auto; 5 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 6 | } 7 | 8 | .row { 9 | padding: 20px; 10 | } 11 | 12 | .button { 13 | // Our button 14 | text-decoration: none; 15 | height: 32px; 16 | 17 | // Primary Button 18 | min-width: 80px; 19 | background-color: #0078d7; 20 | border-color: #0078d7; 21 | color: #ffffff; 22 | 23 | // Basic Button 24 | outline: transparent; 25 | position: relative; 26 | font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif; 27 | -webkit-font-smoothing: antialiased; 28 | font-size: 14px; 29 | font-weight: 400; 30 | border-width: 0; 31 | text-align: center; 32 | cursor: pointer; 33 | display: inline-block; 34 | padding: 0 16px; 35 | 36 | .label { 37 | font-weight: 600; 38 | font-size: 14px; 39 | height: 32px; 40 | line-height: 32px; 41 | margin: 0 4px; 42 | vertical-align: top; 43 | display: inline-block; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/GdprDashboard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './GdprDashboard.module.scss'; 3 | import { IGdprDashboardProps } from './IGdprDashboardProps'; 4 | import { IGdprDashboardState } from './IGdprDashboardState'; 5 | 6 | import ITaskItem from '../models/ITaskItem'; 7 | import TaskList from "./TaskList/TaskList"; 8 | 9 | import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; 10 | 11 | /** 12 | * Common Infrastructure 13 | */ 14 | import { 15 | BaseComponent, 16 | assign, 17 | autobind 18 | } from 'office-ui-fabric-react/lib/Utilities'; 19 | 20 | /** 21 | * Button 22 | */ 23 | import { PrimaryButton, DefaultButton, CommandButton, Button, IButtonProps } from 'office-ui-fabric-react/lib/Button'; 24 | 25 | import { default as pnp, PermissionKind } from "sp-pnp-js"; 26 | 27 | export default class GdprDashboard extends React.Component { 28 | 29 | /** 30 | * Main constructor for the component 31 | */ 32 | constructor(props: IGdprDashboardProps) { 33 | super(props); 34 | 35 | this.state = { 36 | taskItems : [], 37 | currentUserIsAdmin: false, 38 | filterByCurrentUser: true, 39 | }; 40 | 41 | this.readCurrentUserIsAdmin(); 42 | } 43 | 44 | public componentWillReceiveProps(props: IGdprDashboardProps) { 45 | this.refreshTasksList(); 46 | } 47 | 48 | public componentDidMount() { 49 | this.refreshTasksList(); 50 | } 51 | 52 | public render(): React.ReactElement { 53 | 54 | return ( 55 |
56 |
57 |
58 |
59 | { 60 | (this.state.currentUserIsAdmin ? 61 |
62 |
63 |
64 |
65 | 68 | My Tasks 69 | 70 |
71 |
72 | 75 | All Tasks 76 | 77 |
78 |
79 |
80 |
81 |
82 | : null) 83 | } 84 | 89 |
90 |
91 |
92 |
93 | ); 94 | } 95 | 96 | private readCurrentUserIsAdmin() { 97 | 98 | pnp.sp.web.getCurrentUserEffectivePermissions().then(perms => { 99 | if (pnp.sp.web.hasPermissions(perms, PermissionKind.ManageWeb)) { 100 | this.state.currentUserIsAdmin = true; 101 | this.setState(this.state); 102 | } 103 | }); 104 | 105 | } 106 | 107 | @autobind 108 | private _showAllTasks(){ 109 | this.state.filterByCurrentUser = false; 110 | this.state.taskItems = []; 111 | this.setState(this.state); 112 | this.refreshTasksList(); 113 | } 114 | 115 | @autobind 116 | private _showMyTasks(){ 117 | this.state.filterByCurrentUser = true; 118 | this.state.taskItems = []; 119 | this.setState(this.state); 120 | this.refreshTasksList(); 121 | } 122 | 123 | private refreshTasksList() { 124 | if (this.props.targetList) { 125 | this.fetchTasks().then((r) => { 126 | this.state.taskItems = r; 127 | this.setState(this.state); 128 | }); 129 | } 130 | } 131 | 132 | private fetchTasks(): Promise { 133 | if (Environment.type === EnvironmentType.SharePoint || 134 | Environment.type === EnvironmentType.ClassicSharePoint) { 135 | 136 | var listOfTasks = pnp.sp.web.lists.getById(this.props.targetList).items 137 | .select("ID", "Title", "AssignedTo/Id", "AssignedTo/Title", "AssignedTo/Name", "Checkmark", "DueDate") 138 | .expand("AssignedTo"); 139 | 140 | if (this.state.filterByCurrentUser) { 141 | listOfTasks = listOfTasks.filter("AssignedToId eq " + this.props.context.pageContext.legacyPageContext.userId); 142 | } 143 | 144 | return(listOfTasks.get().then((response) => { 145 | var tasks: Array = new Array(); 146 | response.map((item: any) => { 147 | tasks.push( { 148 | id: item.ID, 149 | title: item.Title, 150 | dueDate: new Date(item.DueDate), 151 | assigneeId: item.AssignedTo.length > 0 ? item.AssignedTo[0].Id : 0, 152 | assigneeLoginName: item.AssignedTo.length > 0 ? item.AssignedTo[0].Name : null, 153 | assigneeFullName: item.AssignedTo.length > 0 ? item.AssignedTo[0].Title : null, 154 | completed: (item.Checkmark == 1), 155 | }); 156 | }); 157 | 158 | return tasks; 159 | })); 160 | } 161 | else { 162 | return(new Promise((resolve, reject) => { 163 | resolve([]); 164 | })); 165 | } 166 | } 167 | 168 | @autobind 169 | private _onChangeTaskItem(task: ITaskItem) { 170 | 171 | pnp.sp.web.lists.getById(this.props.targetList).items.getById(task.id).update( 172 | { 173 | Checkmark: task.completed ? "1" : "0", 174 | Status: task.completed ? "Completed" : "Not Started", 175 | } 176 | ).then(i => { console.log(i); }); 177 | 178 | return; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/IGdprDashboardProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface IGdprDashboardProps { 6 | context: IWebPartContext; 7 | targetList: string; 8 | } 9 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/IGdprDashboardState.ts: -------------------------------------------------------------------------------- 1 | import ITaskItem from "../models/ITaskItem"; 2 | 3 | export interface IGdprDashboardState { 4 | taskItems: ITaskItem[]; 5 | currentUserIsAdmin?: boolean; 6 | filterByCurrentUser?: boolean; 7 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskList/ITaskListProps.ts: -------------------------------------------------------------------------------- 1 | import TaskOperationCallback from '../../models/TaskOperationCallback'; 2 | 3 | import ITaskItem from '../../models/ITaskItem'; 4 | 5 | import { 6 | IWebPartContext 7 | } from '@microsoft/sp-webpart-base'; 8 | 9 | interface ITaskListProps { 10 | context: IWebPartContext; 11 | taskItems: ITaskItem[]; 12 | onChangeTaskItem?: TaskOperationCallback; 13 | } 14 | 15 | export default ITaskListProps; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskList/ITaskListState.ts: -------------------------------------------------------------------------------- 1 | import ITaskItem from '../../models/ITaskItem'; 2 | 3 | interface ITaskListState { 4 | taskItems: ITaskItem[]; 5 | } 6 | 7 | export default ITaskListState; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskList/TaskList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as strings from 'gdprDashboardStrings'; 3 | 4 | import ITaskListProps from './ITaskListProps'; 5 | import ITaskListState from './ITaskListState'; 6 | 7 | import ITaskItem from '../../models/ITaskItem'; 8 | import TaskListItem from '../TaskListItem/TaskListItem'; 9 | 10 | import { List } from 'office-ui-fabric-react'; 11 | 12 | /** 13 | * Common Infrastructure 14 | */ 15 | import { 16 | BaseComponent, 17 | assign, 18 | autobind 19 | } from 'office-ui-fabric-react/lib/Utilities'; 20 | 21 | export default class TaskList extends React.Component { 22 | 23 | constructor(props: ITaskListProps) { 24 | super(props); 25 | 26 | this.state = { 27 | taskItems: [] 28 | }; 29 | } 30 | 31 | public componentWillReceiveProps(props: ITaskListProps) { 32 | if (props && props.taskItems) { 33 | this.setState({ taskItems: props.taskItems }); 34 | } 35 | } 36 | 37 | public componentDidMount() { 38 | if (this.props && this.props.taskItems) { 39 | this.setState({ taskItems: this.props.taskItems }); 40 | } 41 | } 42 | 43 | public render(): JSX.Element { 44 | 45 | // let tempItems : ITaskItem[] = [ 46 | // { id: 1, title: "Sample Task #01", assigneeId: 1, assigneeFullName: "Paolo Pialorsi", assigneeLoginName: "login", completed: true, dueDate: new Date() }, 47 | // { id: 2, title: "Sample Task #02", assigneeId: 2, assigneeFullName: "Paolo Pialorsi", assigneeLoginName: "login", completed: false, dueDate: new Date() }, 48 | // { id: 3, title: "Sample Task #03", assigneeId: 3, assigneeFullName: "Paolo Pialorsi", assigneeLoginName: "login", completed: false, dueDate: new Date() }, 49 | // { id: 4, title: "Sample Task #04", assigneeId: 4, assigneeFullName: "Paolo Pialorsi", assigneeLoginName: "login", completed: true, dueDate: new Date() }, 50 | // { id: 5, title: "Sample Task #05", assigneeId: 5, assigneeFullName: "Paolo Pialorsi", assigneeLoginName: "login", completed: false, dueDate: new Date() }, 51 | // ]; 52 | 53 | return ( 54 |
55 |
56 |
{ strings.TaskCompletedColumnTitle }
57 |
{ strings.TaskAssigneeColumnTitle }
58 |
{ strings.TaskTitleColumnTitle }
59 |
{ strings.TaskDueDateColumnTitle }
60 |
61 | 65 |
66 | ); 67 | } 68 | 69 | @autobind 70 | private _onRenderTaskItem(item: ITaskItem, index: number) { 71 | return ( 72 | 77 | ); 78 | } 79 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskListItem/ITaskListItemProps.ts: -------------------------------------------------------------------------------- 1 | import ITaskItem from '../../models/ITaskItem'; 2 | import TaskOperationCallback from '../../models/TaskOperationCallback'; 3 | 4 | import { 5 | IWebPartContext 6 | } from '@microsoft/sp-webpart-base'; 7 | 8 | interface ITaskListItemProps { 9 | context: IWebPartContext; 10 | task: ITaskItem; 11 | onChangeTaskItem: TaskOperationCallback; 12 | } 13 | 14 | export default ITaskListItemProps; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskListItem/ITaskListItemState.ts: -------------------------------------------------------------------------------- 1 | import ITaskItem from '../../models/ITaskItem'; 2 | 3 | interface ITaskListItemState { 4 | task: ITaskItem; 5 | } 6 | 7 | export default ITaskListItemState; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/components/TaskListItem/TaskListItem.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import ITaskListItemProps from './ITaskListItemProps'; 4 | import ITaskListItemState from './ITaskListItemState'; 5 | 6 | import { GDPRUtility } from '../../../../components/GDPRUtility'; 7 | 8 | /** 9 | * Common Infrastructure 10 | */ 11 | import { 12 | BaseComponent, 13 | assign, 14 | autobind 15 | } from 'office-ui-fabric-react/lib/Utilities'; 16 | 17 | /** 18 | * Toggle 19 | */ 20 | import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; 21 | 22 | /** 23 | * Label 24 | */ 25 | import { Label } from 'office-ui-fabric-react/lib/Label'; 26 | 27 | /** 28 | * Persona 29 | */ 30 | import { 31 | Persona, 32 | PersonaSize, 33 | PersonaPresence 34 | } from 'office-ui-fabric-react/lib/Persona'; 35 | 36 | export default class TaskListItem extends React.Component { 37 | 38 | /** 39 | * Main constructor for the component 40 | */ 41 | constructor(props: ITaskListItemProps) { 42 | super(); 43 | 44 | this.state = { 45 | task: props.task, 46 | }; 47 | } 48 | 49 | public render(): JSX.Element { 50 | 51 | let siteUrl: string = this.props.context.pageContext.site.absoluteUrl; 52 | 53 | return ( 54 | this.state.task ? 55 |
56 |
57 | 63 |
64 |
65 | 73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | : null 82 | ); 83 | } 84 | 85 | @autobind 86 | private _onChangedTask(newValue: boolean): void { 87 | this.state.task.completed = newValue; 88 | this.setState(this.state); 89 | 90 | if (this.props.onChangeTaskItem) { 91 | this.props.onChangeTaskItem(this.state.task); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Please configure the GDPR Dashboard", 4 | "BasicGroupName": "General Settings", 5 | 6 | "TaskCompletedColumnTitle": "Completed", 7 | "TaskAssigneeColumnTitle": "Assigned To", 8 | "TaskTitleColumnTitle": "Task Title", 9 | "TaskDueDateColumnTitle": "Due Date", 10 | } 11 | }); -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IGdprDashboardStrings { 2 | BasicGroupName: string; 3 | PropertyPaneDescription: string; 4 | 5 | TaskCompletedColumnTitle: string; 6 | TaskAssigneeColumnTitle: string; 7 | TaskTitleColumnTitle: string; 8 | TaskDueDateColumnTitle: string; 9 | } 10 | 11 | declare module 'gdprDashboardStrings' { 12 | const strings: IGdprDashboardStrings; 13 | export = strings; 14 | } 15 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/models/ITaskItem.ts: -------------------------------------------------------------------------------- 1 | interface ITaskItem { 2 | id: number; 3 | title: string; 4 | assigneeId: number; 5 | assigneeLoginName: string; 6 | assigneeFullName: string; 7 | dueDate: Date; 8 | completed: boolean; 9 | } 10 | 11 | export default ITaskItem; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/models/TaskOperationCallback.ts: -------------------------------------------------------------------------------- 1 | import ITaskItem from './ITaskItem'; 2 | 3 | type TaskOperationCallback = (task: ITaskItem) => void; 4 | 5 | export default TaskOperationCallback; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprDashboard/tests/GdprDashboard.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { assert } from 'chai'; 4 | 5 | describe('GdprDashboardWebPart', () => { 6 | it('should do something', () => { 7 | assert.ok(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/GdprHierarchyWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json", 3 | 4 | "id": "50c8ec09-6a38-4831-a537-fdedd8e560f3", 5 | "alias": "GdprHierarchyWebPart", 6 | "componentType": "WebPart", 7 | "version": "0.0.1", 8 | "manifestVersion": 2, 9 | 10 | "preconfiguredEntries": [{ 11 | "groupId": "50c8ec09-6a38-4831-a537-fdedd8e560f3", 12 | "group": { "default": "GDPR" }, 13 | "title": { "default": "GDPR Hierarchy" }, 14 | "description": { "default": "Hierarchy of Roles for GDPR Starter Kit" }, 15 | "officeFabricIconFontName": "Org", 16 | "properties": { 17 | "description": "GDPRHierarchy" 18 | } 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/GdprHierarchyWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | 4 | import GdprHierarchy from './components/GdprHierarchy'; 5 | import { IGdprHierarchyProps } from './components/IGdprHierarchyProps'; 6 | import { GdprBaseWebPart } from '../../components/GDPRBaseWebPart'; 7 | 8 | export default class GdprHierarchyWebPart extends GdprBaseWebPart { 9 | 10 | private _gdprDashboardComponent: GdprHierarchy; 11 | 12 | public render(): void { 13 | const element: React.ReactElement = React.createElement( 14 | GdprHierarchy, 15 | { 16 | context: this.context, 17 | targetList: this.properties.targetList 18 | } 19 | ); 20 | 21 | this._gdprDashboardComponent = ReactDom.render(element, this.domElement); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/GdprHierarchy.module.scss: -------------------------------------------------------------------------------- 1 | .helloWorld { 2 | .container { 3 | max-width: 700px; 4 | margin: 0px auto; 5 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 6 | } 7 | 8 | .GDPRHierarchyTitle { 9 | font-size: 12pt; 10 | font-weight: bold; 11 | } 12 | 13 | .hierarchyItem { 14 | padding-bottom: 10px !important; 15 | text-align: center; 16 | } 17 | 18 | .row { 19 | padding: 20px; 20 | text-align: center; 21 | } 22 | 23 | .listItem { 24 | max-width: 715px; 25 | margin: 5px auto 5px auto; 26 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 27 | } 28 | 29 | .button { 30 | // Our button 31 | text-decoration: none; 32 | height: 32px; 33 | 34 | // Primary Button 35 | min-width: 80px; 36 | background-color: #0078d7; 37 | border-color: #0078d7; 38 | color: #ffffff; 39 | 40 | // Basic Button 41 | outline: transparent; 42 | position: relative; 43 | font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif; 44 | -webkit-font-smoothing: antialiased; 45 | font-size: 14px; 46 | font-weight: 400; 47 | border-width: 0; 48 | text-align: center; 49 | cursor: pointer; 50 | display: inline-block; 51 | padding: 0 16px; 52 | 53 | .label { 54 | font-weight: 600; 55 | font-size: 14px; 56 | height: 32px; 57 | line-height: 32px; 58 | margin: 0 4px; 59 | vertical-align: top; 60 | display: inline-block; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/GdprHierarchy.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './GdprHierarchy.module.scss'; 3 | 4 | import IHierarchyItem from '../models/IHierarchyItem'; 5 | import { IGdprHierarchyProps } from './IGdprHierarchyProps'; 6 | import { IGdprHierarchyState } from './IGdprHierarchyState'; 7 | 8 | import pnp from "sp-pnp-js"; 9 | 10 | import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; 11 | 12 | import { List } from 'office-ui-fabric-react'; 13 | import HierarchyListItem from './HierarchyListItem/HierarchyListItem'; 14 | 15 | /** 16 | * Common Infrastructure 17 | */ 18 | import { 19 | BaseComponent, 20 | assign, 21 | autobind, 22 | css 23 | } from 'office-ui-fabric-react/lib/Utilities'; 24 | 25 | export default class GdprHierarchy extends React.Component { 26 | 27 | private roles = [ 28 | { TermGuid: "8fb4774c-2f1a-4bea-b7a4-75ebfe74cbdc", Name: "Data Protection Officer", SortOrder: 1 }, 29 | { TermGuid: "0cc951b5-612f-4d83-9e90-7993c3c0d15e", Name: "GDPR Controller", SortOrder: 2 }, 30 | { TermGuid: "e28a2c76-eb00-4f5f-95e7-673b24b9b4dd", Name: "GDPR Processor", SortOrder: 3 }, 31 | ]; 32 | 33 | constructor(props: IGdprHierarchyProps) { 34 | super(props); 35 | 36 | this.state = { 37 | hierarchyItems: [] 38 | }; 39 | } 40 | 41 | public componentWillReceiveProps(props: IGdprHierarchyProps) { 42 | this._refreshHierarchy(); 43 | } 44 | 45 | public componentDidMount() { 46 | this._refreshHierarchy(); 47 | } 48 | 49 | public render(): React.ReactElement { 50 | 51 | let dpoContacts: IHierarchyItem[] = this.state.hierarchyItems.filter(i => i.role == "Data Protection Officer"); 52 | let controllerContacts: IHierarchyItem[] = this.state.hierarchyItems.filter(i => i.role == "GDPR Controller"); 53 | let processorContacts: IHierarchyItem[] = this.state.hierarchyItems.filter(i => i.role == "GDPR Processor"); 54 | 55 | return ( 56 |
57 |
58 |
59 |
60 |
GDPR Hierarchy
61 |
62 |
63 |
64 |
65 | 69 |
70 |
71 |
72 |
73 | 77 |
78 |
79 |
80 |
81 | 85 |
86 |
87 |
88 |
89 | ); 90 | } 91 | 92 | private _refreshHierarchy() { 93 | if (this.props.targetList) { 94 | this.fetchHierarchy().then((r) => { 95 | this.state.hierarchyItems = r; 96 | this.setState(this.state); 97 | }); 98 | } 99 | } 100 | 101 | private fetchHierarchy(): Promise { 102 | if (Environment.type === EnvironmentType.SharePoint || 103 | Environment.type === EnvironmentType.ClassicSharePoint) { 104 | 105 | return(pnp.sp.web.lists.getById(this.props.targetList).items 106 | .select("ID", "Title", "GDPRContactRole", "GDPRContactUser/Title", "GDPRContactUser/Name") 107 | .expand("GDPRContactUser") 108 | .get().then((response) => { 109 | var items: Array = new Array(); 110 | response.map((item: any) => { 111 | items.push( { 112 | id: item.ID, 113 | fullName: item.GDPRContactUser ? item.GDPRContactUser.Title : '', 114 | loginName: item.GDPRContactUser ? item.GDPRContactUser.Name : 0, 115 | role: this.roles.filter(r => r.TermGuid == item.GDPRContactRole.TermGuid)[0].Name, 116 | roleSortOrder: this.roles.filter(r => r.TermGuid == item.GDPRContactRole.TermGuid)[0].SortOrder, 117 | }); 118 | }); 119 | 120 | items = items.sort((i, t) => i.roleSortOrder - t.roleSortOrder); 121 | 122 | return items; 123 | })); 124 | } 125 | else { 126 | return(new Promise((resolve, reject) => { 127 | resolve([]); 128 | })); 129 | } 130 | } 131 | 132 | @autobind 133 | private _onRenderHierarchyItem(item: IHierarchyItem, index: number) { 134 | return ( 135 | 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/HierarchyListItem/HierarchyListItem.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from '../GdprHierarchy.module.scss'; 3 | 4 | import IHierarchyListItemProps from './IHierarchyListItemProps'; 5 | import IHierarchyListItemState from './IHierarchyListItemState'; 6 | 7 | import { GDPRUtility } from '../../../../components/GDPRUtility'; 8 | 9 | /** 10 | * Common Infrastructure 11 | */ 12 | import { 13 | BaseComponent, 14 | assign, 15 | autobind, 16 | css 17 | } from 'office-ui-fabric-react/lib/Utilities'; 18 | 19 | /** 20 | * Label 21 | */ 22 | import { Label } from 'office-ui-fabric-react/lib/Label'; 23 | 24 | /** 25 | * Persona 26 | */ 27 | import { 28 | Persona, 29 | PersonaSize, 30 | PersonaPresence 31 | } from 'office-ui-fabric-react/lib/Persona'; 32 | 33 | export default class HierarchyListItem extends React.Component { 34 | 35 | /** 36 | * Main constructor for the component 37 | */ 38 | constructor(props: IHierarchyListItemProps) { 39 | super(); 40 | 41 | this.state = { 42 | item: props.item, 43 | }; 44 | } 45 | 46 | public render(): JSX.Element { 47 | 48 | let siteUrl: string = this.props.context.pageContext.site.absoluteUrl; 49 | 50 | return ( 51 | this.state.item ? 52 |
53 |
54 | 63 |
64 |
65 | : null 66 | ); 67 | } 68 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/HierarchyListItem/IHierarchyListItemProps.ts: -------------------------------------------------------------------------------- 1 | import IHierarchyItem from '../../models/IHierarchyItem'; 2 | 3 | import { 4 | IWebPartContext 5 | } from '@microsoft/sp-webpart-base'; 6 | 7 | interface IHierarchyListItemProps { 8 | context: IWebPartContext; 9 | item: IHierarchyItem; 10 | } 11 | 12 | export default IHierarchyListItemProps; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/HierarchyListItem/IHierarchyListItemState.ts: -------------------------------------------------------------------------------- 1 | import IHierarchyItem from '../../models/IHierarchyItem'; 2 | 3 | interface IHierarchyListItemState { 4 | item: IHierarchyItem; 5 | } 6 | 7 | export default IHierarchyListItemState; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/IGdprHierarchyProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface IGdprHierarchyProps { 6 | context: IWebPartContext; 7 | targetList: string; 8 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/components/IGdprHierarchyState.ts: -------------------------------------------------------------------------------- 1 | import IHierarchyItem from '../models/IHierarchyItem'; 2 | 3 | export interface IGdprHierarchyState { 4 | hierarchyItems: IHierarchyItem[]; 5 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IGdprHierarchyStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | } 6 | 7 | declare module 'gdprHierarchyStrings' { 8 | const strings: IGdprHierarchyStrings; 9 | export = strings; 10 | } 11 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/models/IHierarchyItem.ts: -------------------------------------------------------------------------------- 1 | interface IHierarchyItem { 2 | id: number; 3 | loginName: string; 4 | fullName: string; 5 | role: string; 6 | roleSortOrder: number; 7 | } 8 | 9 | export default IHierarchyItem; -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprHierarchy/tests/GdprHierarchy.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { assert } from 'chai'; 4 | 5 | describe('GdprHierarchyWebPart', () => { 6 | it('should do something', () => { 7 | assert.ok(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/GdprInsertEventWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json", 3 | 4 | "id": "d67d6088-e294-4a23-af02-c4ea8783c23f", 5 | "alias": "GdprInsertEventWebPart", 6 | "componentType": "WebPart", 7 | "version": "0.0.1", 8 | "manifestVersion": 2, 9 | 10 | "preconfiguredEntries": [{ 11 | "groupId": "d67d6088-e294-4a23-af02-c4ea8783c23f", 12 | "group": { "default": "GDPR" }, 13 | "title": { "default": "GDPR Insert Event" }, 14 | "description": { "default": "Allows to insert a new GDPR Event or Incident" }, 15 | "officeFabricIconFontName": "PageAdd", 16 | "properties": { 17 | "description": "GDPRInsertEvent" 18 | } 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/GdprInsertEventWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | 4 | import GdprInsertEvent from './components/GdprInsertEvent'; 5 | import { IGdprInsertEventProps } from './components/IGdprInsertEventProps'; 6 | import { GdprBaseWebPart } from '../../components/GDPRBaseWebPart'; 7 | 8 | export default class GdprInsertEventWebPart extends GdprBaseWebPart { 9 | 10 | public render(): void { 11 | const element: React.ReactElement = React.createElement( 12 | GdprInsertEvent, 13 | { 14 | context: this.context, 15 | targetList: this.properties.targetList, 16 | } 17 | ); 18 | 19 | ReactDom.render(element, this.domElement); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/components/GdprInsertEvent.module.scss: -------------------------------------------------------------------------------- 1 | .gdprEvent { 2 | .container { 3 | max-width: 100%; 4 | margin: 0px auto; 5 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 6 | } 7 | 8 | .row { 9 | padding: 10px; 10 | } 11 | 12 | .listItem { 13 | max-width: 100%; 14 | margin: 5px auto 5px auto; 15 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 16 | } 17 | } 18 | 19 | :global .ms-ChoiceField--image { 20 | margin-bottom: 6px; 21 | } 22 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image { 23 | padding: 10px; 24 | min-width: auto; 25 | width: 130px; 26 | min-height: 100px; 27 | } 28 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image .ms-ChoiceField-labelWrapper { 29 | line-height: normal; 30 | margin-top: 5px; 31 | padding: 0 20px; 32 | display: table-cell; 33 | vertical-align: middle; 34 | height: 36px; 35 | } 36 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image .ms-ChoiceField-innerField .ms-ChoiceField-iconWrapper { 37 | font-size: 32px; 38 | line-height: normal; 39 | } 40 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/components/IGdprInsertEventProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface IGdprInsertEventProps { 6 | context: IWebPartContext; 7 | targetList: string; 8 | } 9 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/components/IGdprInsertEventState.ts: -------------------------------------------------------------------------------- 1 | import { ISPTermObject } from '../../../components/SPTermStoreService'; 2 | 3 | export interface IGdprInsertEventState { 4 | currentEventType: string; 5 | isValid: boolean; 6 | showDialogResult: boolean; 7 | 8 | title?: string; 9 | notifiedBy?: string; 10 | eventAssignedTo?: string; 11 | eventStartDate?: Date; 12 | eventEndDate?: Date; 13 | postEventReport?: string; 14 | additionalNotes?: string; 15 | breachType?: ISPTermObject; 16 | riskType?: ISPTermObject[]; 17 | severity?: ISPTermObject; 18 | dpaNotified?: boolean; 19 | dpaNotificationDate?: Date; 20 | estimatedAffectedSubjects?: Number; 21 | toBeDetermined?: boolean; 22 | includesChildren?: boolean; 23 | includesChildrenInProgress?: boolean; 24 | actionPlan?: string; 25 | breachResolved?: boolean; 26 | actionsTaken?: string; 27 | includesSensitiveData?: ISPTermObject; 28 | dataSubjectIsChild?: boolean; 29 | indirectDataProvider?: boolean; 30 | dataProvider?: string; 31 | consentNotes?: string; 32 | consentType?: ISPTermObject[]; 33 | consentIsInternal?: boolean; 34 | consentWithdrawalType?: ISPTermObject[]; 35 | consentWithdrawalNotes?: string; 36 | originalConsentAvailable?: boolean; 37 | originalConsent?: number; 38 | notifyApplicable?: boolean; 39 | processingType?: ISPTermObject[]; 40 | processors?: string[]; 41 | archivedData?: string; 42 | anonymize?: boolean; 43 | archivingNotes?: string; 44 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | 7 | "YesText": "Yes", 8 | "NoText": "No", 9 | 10 | "EventTypeFieldLabel": "Pick one Event/Incident Type for Personal Data", 11 | "EventTypeDataBreachLabel": "Data Breach", 12 | "EventTypeIdentityRiskLabel": "Identity Risk", 13 | "EventTypeDataConsentLabel": "Data Consent", 14 | "EventTypeDataConsentWithdrawalLabel": "Data Consent Withdrawal", 15 | "EventTypeDataProcessingLabel": "Data Processing", 16 | "EventTypeDataArchivedLabel": "Data Archived", 17 | 18 | "TitleFieldLabel": "Title", 19 | "TitleFieldPlaceholder": "Provide a Title for the Event", 20 | "TitleFieldValidationErrorMessage": "The length of the Title should be at leat 10 characters, actual is", 21 | "NotifiedByFieldLabel": "Notified By", 22 | "NotifiedByFieldPlaceholder": "Provide a description of who notified the Event", 23 | "NotifiedByFieldValidationErrorMessage": "The length of the Notified By should be at least 5 characters, actual is", 24 | "EventAssignedToFieldLabel": "Event Assigned To", 25 | "EventAssignedToFieldPlaceholder": "Provide the assignee of the Event", 26 | "EventStartDateFieldLabel": "Event Start Date", 27 | "EventStartDateFieldPlaceholder": "Provide the Start Date of the Event", 28 | "EventStartTimeHoursFieldLabel": "Hours", 29 | "EventStartTimeMinutesFieldLabel": "Minutes", 30 | "EventEndDateFieldLabel": "Event End Date", 31 | "EventEndDateFieldPlaceholder": "Provide the End Date of the Event", 32 | "EventEndTimeHoursFieldLabel": "Hours", 33 | "EventEndTimeMinutesFieldLabel": "Minutes", 34 | "PostEventReportFieldLabel": "Post Event/Incident Report", 35 | "AdditionalNotesFieldLabel": "Additional Notes", 36 | "BreachTypeFieldLabel": "Breach Type", 37 | "BreachTypeFieldPlaceholder": "Select the Breach Type", 38 | "SeverityFieldLabel": "Severity", 39 | "SeverityFieldPlaceholder": "Select the Severity level", 40 | "DPANotifiedFieldLabel": "DPA Notified", 41 | "DPANotificationDateFieldLabel": "DPA Notification Date", 42 | "DPANotificationDateFieldPlaceholder": "Provide the DPA Notification date", 43 | "DPANotificationTimeHoursFieldLabel": "Hours", 44 | "DPANotificationTimeMinutesFieldLabel": "Minutes", 45 | "EstimatedAffectedSubjectsFieldLabel": "Estimated Number of Affected Data Subjects", 46 | "EstimatedAffectedSubjectsFieldValidationErrorMessage": "Please, provide a valid number", 47 | "ToBeDeterminedFieldLabel": "To Be Determined", 48 | "IncludesChildrenFieldLabel": "Includes Children", 49 | "IncludesChildrenInProgressFieldLabel": "In Progress", 50 | "ActionPlanFieldLabel": "Action Plan", 51 | "BreachResolvedFieldLabel": "Breach Resolved", 52 | "ActionsTakenFieldLabel": "Actions Taken", 53 | "RiskTypeFieldLabel": "Risk Type", 54 | "RiskTypeFieldPlaceholder": "Select the Risk Type", 55 | "IncludesSensitiveDataFieldLabel": "Includes Sensitive Data", 56 | "IncludesSensitiveDataFieldPlaceholder": "Select any included Sensitive Data", 57 | "DataSubjectIsChildFieldLabel": "Data Subject is Child", 58 | "IndirectDataProviderFieldLabel": "Indirect Data Provider", 59 | "DataProviderFieldLabel": "Data Provider", 60 | "ConsentNotesFieldLabel": "Consent Notes", 61 | "ConsentTypeFieldLabel": "Consent Type", 62 | "ConsentTypeFieldPlaceholder": "Select the Consent Types", 63 | "ConsentIsInternalFieldLabel": "Internal or External Data Subject", 64 | "InternalConsentText": "Internal", 65 | "ExternalConsentText": "External", 66 | "ConsentWithdrawalTypeFieldLabel": "Withdrawal Type", 67 | "ConsentWithdrawalTypeFieldPlaceholder": "Select the Withdrawal Type", 68 | "ConsentWithdrawalNotesFieldLabel": "Withdrawal Notes", 69 | "OriginalConsentAvailableFieldLabel": "Original Consent Available", 70 | "OriginalConsentFieldLabel": "Original Consent", 71 | "OriginalConsentFieldPlaceholder": "Select the Original Consent", 72 | "NotifyApplicableFieldLabel": "Notify Applicable Controllers /Processors & Sub Processors", 73 | "ProcessingTypeFieldLabel": "Processing Type", 74 | "ProcessingTypeFieldPlaceholder": "Select the Processing Type", 75 | "ProcessorsFieldLabel": "GDPR Processors", 76 | "ProcessorsFieldPlaceholder": "Select the GDPR Processors", 77 | "ArchivedDataFieldLabel": "Archived Data", 78 | "AnonymizeFieldLabel": "Anonymize", 79 | "ArchivingNotesFieldLabel": "Archiving Notes", 80 | 81 | "SaveButtonText": "Save", 82 | "CancelButtonText": "Cancel", 83 | "ItemSavedMessage": "Item saved!", 84 | "InsertNextLabel": "Insert next", 85 | "GoHomeLabel": "Go home", 86 | "ItemInsertedDialogTitle": "Item inserted", 87 | "ItemInsertedDialogSubText": "Your item has been inserted. Now you can add another one, or you can go back to the home page.", 88 | 89 | "HoursValidationError": "Please provide a valid hours value", 90 | "MinutesValidationError": "Please provide a valid minutes value", 91 | } 92 | }); -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IGdprInsertEventStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | 6 | YesText: string; 7 | NoText: string; 8 | 9 | EventTypeFieldLabel: string; 10 | EventTypeDataBreachLabel: string; 11 | EventTypeIdentityRiskLabel: string; 12 | EventTypeDataConsentLabel: string; 13 | EventTypeDataConsentWithdrawalLabel: string; 14 | EventTypeDataProcessingLabel: string; 15 | EventTypeDataArchivedLabel: string; 16 | 17 | TitleFieldLabel: string; 18 | TitleFieldPlaceholder: string; 19 | TitleFieldValidationErrorMessage: string; 20 | NotifiedByFieldLabel: string; 21 | NotifiedByFieldPlaceholder: string; 22 | NotifiedByFieldValidationErrorMessage: string; 23 | EventAssignedToFieldLabel: string; 24 | EventAssignedToFieldPlaceholder: string; 25 | EventStartDateFieldLabel: string; 26 | EventStartDateFieldPlaceholder: string; 27 | EventStartTimeHoursFieldLabel: string; 28 | EventStartTimeMinutesFieldLabel: string; 29 | EventEndDateFieldLabel: string; 30 | EventEndDateFieldPlaceholder: string; 31 | EventEndTimeHoursFieldLabel: string; 32 | EventEndTimeMinutesFieldLabel: string; 33 | PostEventReportFieldLabel: string; 34 | AdditionalNotesFieldLabel: string; 35 | BreachTypeFieldLabel: string; 36 | BreachTypeFieldPlaceholder: string; 37 | SeverityFieldLabel: string; 38 | SeverityFieldPlaceholder: string; 39 | DPANotifiedFieldLabel: string; 40 | DPANotificationDateFieldLabel: string; 41 | DPANotificationDateFieldPlaceholder: string; 42 | DPANotificationTimeHoursFieldLabel: string; 43 | DPANotificationTimeMinutesFieldLabel: string; 44 | EstimatedAffectedSubjectsFieldLabel: string; 45 | EstimatedAffectedSubjectsFieldValidationErrorMessage: string; 46 | ToBeDeterminedFieldLabel: string; 47 | IncludesChildrenFieldLabel: string; 48 | IncludesChildrenInProgressFieldLabel: string; 49 | ActionPlanFieldLabel: string; 50 | BreachResolvedFieldLabel: string; 51 | ActionsTakenFieldLabel: string; 52 | RiskTypeFieldLabel: string; 53 | RiskTypeFieldPlaceholder: string; 54 | IncludesSensitiveDataFieldLabel: string; 55 | IncludesSensitiveDataFieldPlaceholder: string; 56 | DataSubjectIsChildFieldLabel: string; 57 | IndirectDataProviderFieldLabel: string; 58 | DataProviderFieldLabel: string; 59 | ConsentNotesFieldLabel: string; 60 | ConsentTypeFieldLabel: string; 61 | ConsentTypeFieldPlaceholder: string; 62 | ConsentIsInternalFieldLabel: string; 63 | InternalConsentText: string; 64 | ExternalConsentText: string; 65 | ConsentWithdrawalTypeFieldLabel: string; 66 | ConsentWithdrawalTypeFieldPlaceholder: string; 67 | ConsentWithdrawalNotesFieldLabel: string; 68 | OriginalConsentAvailableFieldLabel: string; 69 | OriginalConsentFieldLabel: string; 70 | OriginalConsentFieldPlaceholder: string; 71 | NotifyApplicableFieldLabel: string; 72 | ProcessingTypeFieldLabel: string; 73 | ProcessingTypeFieldPlaceholder: string; 74 | ProcessorsFieldLabel: string; 75 | ProcessorsFieldPlaceholder: string; 76 | ArchivedDataFieldLabel: string; 77 | AnonymizeFieldLabel: string; 78 | ArchivingNotesFieldLabel: string; 79 | 80 | SaveButtonText: string; 81 | CancelButtonText: string; 82 | ItemSavedMessage: string; 83 | InsertNextLabel: string; 84 | GoHomeLabel: string; 85 | ItemInsertedDialogTitle: string; 86 | ItemInsertedDialogSubText: string; 87 | 88 | HoursValidationError: string; 89 | MinutesValidationError: string; 90 | } 91 | 92 | declare module 'gdprInsertEventStrings' { 93 | const strings: IGdprInsertEventStrings; 94 | export = strings; 95 | } 96 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertEvent/tests/GdprInsertEvent.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { assert } from 'chai'; 4 | 5 | describe('GdprInsertEventWebPart', () => { 6 | it('should do something', () => { 7 | assert.ok(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/GdprInsertRequestWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json", 3 | 4 | "id": "3f3fac84-cbd6-44d7-880f-4ab5a65ad143", 5 | "alias": "GdprInsertRequestWebPart", 6 | "componentType": "WebPart", 7 | "version": "0.0.1", 8 | "manifestVersion": 2, 9 | 10 | "preconfiguredEntries": [{ 11 | "groupId": "3f3fac84-cbd6-44d7-880f-4ab5a65ad143", 12 | "group": { "default": "GDPR" }, 13 | "title": { "default": "GDPR Insert Request" }, 14 | "description": { "default": "Allows to insert a new GDPR Request" }, 15 | "officeFabricIconFontName": "AddFriend", 16 | "properties": { 17 | "description": "GDPRInsertRequest" 18 | } 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/GdprInsertRequestWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | 4 | import GdprInsertRequest from './components/GdprInsertRequest'; 5 | import { IGdprInsertRequestProps } from './components/IGdprInsertRequestProps'; 6 | import { GdprBaseWebPart } from '../../components/GDPRBaseWebPart'; 7 | 8 | export default class GdprInsertRequestWebPart extends GdprBaseWebPart { 9 | 10 | public render(): void { 11 | const element: React.ReactElement = React.createElement( 12 | GdprInsertRequest, 13 | { 14 | context: this.context, 15 | targetList: this.properties.targetList, 16 | } 17 | ); 18 | 19 | ReactDom.render(element, this.domElement); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/components/GdprInsertRequest.module.scss: -------------------------------------------------------------------------------- 1 | .gdprRequest { 2 | .container { 3 | max-width: 100%; 4 | margin: 0px auto; 5 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 6 | } 7 | 8 | .row { 9 | padding: 10px; 10 | } 11 | 12 | .listItem { 13 | max-width: 100%; 14 | margin: 5px auto 5px auto; 15 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 16 | } 17 | } 18 | 19 | :global .ms-ChoiceField--image { 20 | margin-bottom: 6px; 21 | } 22 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image { 23 | padding: 10px; 24 | min-width: auto; 25 | width: 130px; 26 | min-height: 100px; 27 | } 28 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image .ms-ChoiceField-labelWrapper { 29 | line-height: normal; 30 | margin-top: 5px; 31 | padding: 0 20px; 32 | display: table-cell; 33 | vertical-align: middle; 34 | height: 36px; 35 | } 36 | :global .ms-ChoiceField--image .ms-ChoiceField-field--image .ms-ChoiceField-innerField .ms-ChoiceField-iconWrapper { 37 | font-size: 32px; 38 | line-height: normal; 39 | } 40 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/components/IGdprInsertRequestProps.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IWebPartContext 3 | } from '@microsoft/sp-webpart-base'; 4 | 5 | export interface IGdprInsertRequestProps { 6 | context: IWebPartContext; 7 | targetList: string; 8 | } 9 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/components/IGdprInsertRequestState.ts: -------------------------------------------------------------------------------- 1 | import { ISPTermObject } from '../../../components/SPTermStoreService'; 2 | 3 | export interface IGdprInsertRequestState { 4 | currentRequestType: string; 5 | isValid: boolean; 6 | showDialogResult: boolean; 7 | 8 | title?: string; 9 | dataSubject?: string; 10 | dataSubjectEmail?: string; 11 | verifiedDataSubject?: boolean; 12 | requestAssignedTo?: string; 13 | requestInsertionDate?: Date; 14 | requestDueDate?: Date; 15 | additionalNotes?: string; 16 | deliveryMethod?: ISPTermObject; 17 | correctionDefinition?: string; 18 | deliveryFormat?: ISPTermObject; 19 | personalData?: string; 20 | processingType?: ISPTermObject[]; 21 | notifyApplicable?: boolean; 22 | reason?: string; 23 | } -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Insert Request Configuration", 4 | "BasicGroupName": "Basic Properties", 5 | "TargetListFieldLabel": "Target List", 6 | 7 | "YesText": "Yes", 8 | "NoText": "No", 9 | 10 | "RequestTypeFieldLabel": "Pick one Request Type for Personal Data", 11 | "RequestTypeAccessLabel": "Access", 12 | "RequestTypeCorrectLabel": "Correct", 13 | "RequestTypeExportLabel": "Export", 14 | "RequestTypeObjectionLabel": "Objection", 15 | "RequestTypeEraseLabel": "Erase", 16 | 17 | "TitleFieldLabel": "Title", 18 | "TitleFieldPlaceholder": "Provide a Title for the Request", 19 | "TitleFieldValidationErrorMessage": "The length of the Title should be at leat 10 characters, actual is", 20 | "DataSubjectFieldLabel": "Data Subject", 21 | "DataSubjectFieldPlaceholder": "Provide a target Data Subject", 22 | "DataSubjectEmailFieldLabel": "Data Subject Email", 23 | "DataSubjectEmailFieldPlaceholder": "Provide a valid email address for the Data Subject", 24 | "DataSubjectEmailFieldValidationErrorMessage": "Please, provide a valid email address", 25 | "VerifiedDataSubjectFieldLabel": "Verified Data Subject", 26 | "RequestAssignedToFieldLabel": "Request Assigned To", 27 | "RequestAssignedToFieldPlaceholder": "Provide the assignee for the Request", 28 | "RequestInsertionDateFieldLabel": "Request Insertion Date", 29 | "RequestInsertionDateFieldPlaceholder": "Provide the Insertion Date of the Request", 30 | "RequestDueDateFieldLabel": "Request Due Date", 31 | "RequestDueDateFieldPlaceholder": "Provide the Due Date of the Request", 32 | "AdditionalNotesFieldLabel": "Additional Notes", 33 | "DeliveryMethodFieldLabel": "Delivery Method", 34 | "DeliveryMethodFieldPlaceholder": "Provide the Delivery Method for the Request", 35 | "CorrectionDefinitionFieldLabel": "Correction Definition", 36 | "DeliveryFormatFieldLabel": "Delivery Format", 37 | "DeliveryFormatFieldPlaceholder": "Provide the Delivery Format for the Request", 38 | "PersonalDataFieldLabel": "Personal Data", 39 | "ProcessingTypeFieldLabel": "Processing Type", 40 | "ProcessingTypeFieldPlaceholder": "Provide the Processing Type for the Request", 41 | "ReasonFieldLabel": "Reason", 42 | "NotifyApplicableFieldLabel": "Notify Applicable Controllers /Processors & Sub Processors", 43 | 44 | "SaveButtonText": "Save", 45 | "CancelButtonText": "Cancel", 46 | "ItemSavedMessage": "Item saved!", 47 | "InsertNextLabel": "Insert next", 48 | "GoHomeLabel": "Go home", 49 | "ItemInsertedDialogTitle": "Item inserted", 50 | "ItemInsertedDialogSubText": "Your item has been inserted. Now you can add another one, or you can go back to the home page.", 51 | } 52 | }); -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IGdprInsertRequestStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | TargetListFieldLabel: string; 5 | 6 | YesText: string; 7 | NoText: string; 8 | 9 | RequestTypeFieldLabel: string; 10 | RequestTypeAccessLabel: string; 11 | RequestTypeCorrectLabel: string; 12 | RequestTypeExportLabel: string; 13 | RequestTypeObjectionLabel: string; 14 | RequestTypeEraseLabel: string; 15 | 16 | TitleFieldLabel: string; 17 | TitleFieldPlaceholder: string; 18 | TitleFieldValidationErrorMessage: string; 19 | DataSubjectFieldLabel: string; 20 | DataSubjectFieldPlaceholder: string; 21 | DataSubjectEmailFieldLabel: string; 22 | DataSubjectEmailFieldPlaceholder: string; 23 | DataSubjectEmailFieldValidationErrorMessage: string; 24 | VerifiedDataSubjectFieldLabel: string; 25 | RequestAssignedToFieldLabel: string; 26 | RequestAssignedToFieldPlaceholder: string; 27 | RequestInsertionDateFieldLabel: string; 28 | RequestInsertionDateFieldPlaceholder: string; 29 | RequestDueDateFieldLabel: string; 30 | RequestDueDateFieldPlaceholder: string; 31 | AdditionalNotesFieldLabel: string; 32 | DeliveryMethodFieldLabel: string; 33 | DeliveryMethodFieldPlaceholder: string; 34 | CorrectionDefinitionFieldLabel: string; 35 | DeliveryFormatFieldLabel: string; 36 | DeliveryFormatFieldPlaceholder: string; 37 | PersonalDataFieldLabel: string; 38 | ProcessingTypeFieldLabel: string; 39 | ProcessingTypeFieldPlaceholder: string; 40 | ReasonFieldLabel: string; 41 | NotifyApplicableFieldLabel: string; 42 | 43 | SaveButtonText: string; 44 | CancelButtonText: string; 45 | ItemSavedMessage: string; 46 | InsertNextLabel: string; 47 | GoHomeLabel: string; 48 | ItemInsertedDialogTitle: string; 49 | ItemInsertedDialogSubText: string; 50 | } 51 | 52 | declare module 'gdprInsertRequestStrings' { 53 | const strings: IGdprInsertRequestStrings; 54 | export = strings; 55 | } 56 | -------------------------------------------------------------------------------- /GDPRStarterKit/src/webparts/gdprInsertRequest/tests/GdprInsertRequest.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { assert } from 'chai'; 4 | 5 | describe('GdprInsertRequestWebPart', () => { 6 | it('should do something', () => { 7 | assert.ok(true); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /GDPRStarterKit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "forceConsistentCasingInFileNames": true, 5 | "module": "commonjs", 6 | "jsx": "react", 7 | "declaration": true, 8 | "sourceMap": true, 9 | "types": [ 10 | "es6-promise", 11 | "es6-collections", 12 | "webpack-env" 13 | ], 14 | "experimentalDecorators": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-sp 5 | languages: 6 | - typescript 7 | extensions: 8 | contentType: samples 9 | createdDate: 6/29/2017 8:02:59 AM 10 | --- 11 | # GDPR Activity Hub (January 2018) 12 | This is the repository for the GDPR Activity Hub project. 13 | 14 | ![The UI of the Home Page of GDPR Activity Hub](./Documentation/Figures/Fig11-Home-Page.png) 15 | 16 | The GDPR Activity Hub is an open source project that you can consider as a starter kit for building a management hub for EU GDPR 17 | (European Global Data Protection Regulation). The goal of this project is to give customers and partners something to start with 18 | and to play with in order to keep track of all the fundamental events, requests, tasks and activities required to be compliant with 19 | the GDPR. 20 | 21 | Furthermore, one fundamental goal of this project is to show how to use the latest Microsoft technologies (like the SharePoint Framework, Office UI Fabric, Office 365 Developer Patterns and Practices, etc.) to easily build real-life solutions. 22 | 23 | ## Main Components 24 | The solution targets Microsoft Office 365 and it is founded on Microsoft SharePoint Online. 25 | The main components of the solution are a SharePoint Online Modern Site (i.e. an Office 365 Group Site), some custom developed 26 | SharePoint Framework client-side web parts, a few sample workflows built using Workflow Manager for SharePoint Online, and a Power BI 27 | dashboard. Moreover, there are some PowerShell script files that you can use to speed up the setup process of the solution in your own Office 365 tenants. 28 | 29 | ## User Guide 30 | Please, refer to the [User Guide](./Documentation/User-Guide.md) document to understand what you can do with the GDPR Activity Hub. 31 | 32 | ## Target Audience 33 | The target audience for this project are Microsoft Partners and customers with an internal IT development team, which can install and customize the sample solution based on the real customer's needs. If you are an end-user company, you should ask a Microsoft Partner for support to customize, install, and use this solution. 34 | 35 | ## Setup 36 | In order to setup the solution, you can follow the instructions provided in the [Setup Guide](./Documentation/Setup-Guide.md) document. 37 | 38 | ## Architecture 39 | Out of the box, the solution uses SharePoint Online to host the UI components, as well as to store data about events, requests, tasks and activities required by GDPR. However, you should keep in mind that SharePoint is not a replacement for a relational DBMS. Thus, you can use or consider to use this solution for small or eventually medium businesses, but you cannot use the solution "as is" for big enterprises or companies with many thousands of items to keep track of. 40 | Nevertheless, the architecture of the solution has been defined with a clear decoupling between the UI and the data persistence storage, so that you can eventually customize the solution (because it is an open source project) and replace the SharePoint Online persistence layer with a DBMS based storage system. The same approach can be used to connect the Power BI dashboard with a DBMS instead of using SharePoint Online as the main data source. 41 | 42 | ## Resources 43 | 44 | * [European Global Data Protection Regulation (GDPR)](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation) 45 | * [SharePoint Framework](https://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) 46 | * [Power BI](https://powerbi.microsoft.com/en-us/) 47 | 48 | ## Disclaimer 49 | This GDPR Activity Hub is intended to assist organizations with their GDPR compliance progress. This GDPR Activity Hub should not be relied upon to determine how GDPR applies to an organization or an organization’s compliance with GDPR. This GDPR Activity Hub does not constitute legal advice, nor does it provide any certifications or guarantees regarding GDPR compliance. Instead, we hope the GDPR Activity Hub identifies steps that organizations can implement to simplify their GDPR compliance efforts. The application of GDPR is highly fact-specific. We encourage all organizations using this GDPR Activity Hub to work with a legally qualified professional to discuss GDPR, how it applies specifically to their organization, and how best to ensure compliance. 50 | 51 | MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, AS TO THE INFORMATION IN THIS GDPR Activity Hub. Microsoft disclaims any conditions, express or implied, or other terms that use of the Microsoft products or services will ensure the organization’s compliance with the GDPR. This GDPR Activity Hub is provided “as-is.” Information and recommendations expressed in this GDPR Activity Hub may change without notice. 52 | 53 | This GDPR Activity Hub does not provide the user with any legal rights to any intellectual property in any Microsoft product or service. Organizations may use this GDPR Activity Hub for internal, reference purposes only. 54 | 55 | © 2017 Microsoft. All rights reserved. 56 | 57 | ![](https://telemetry.sharepointpnp.com/sp-dev-gdpr-activity-hub/readme) 58 | -------------------------------------------------------------------------------- /Scripts/GDPR.pbit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Scripts/GDPR.pbit -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/3f3fac84-cbd6-44d7-880f-4ab5a65ad143.json: -------------------------------------------------------------------------------- 1 | {"id":"3f3fac84-cbd6-44d7-880f-4ab5a65ad143","alias":"GdprInsertRequestWebPart","componentType":"WebPart","version":"0.0.1","manifestVersion":2,"preconfiguredEntries":[{"groupId":"3f3fac84-cbd6-44d7-880f-4ab5a65ad143","group":{"default":"GDPR"},"title":{"default":"GDPR Insert Request"},"description":{"default":"Allows to insert a new GDPR Request"},"officeFabricIconFontName":"AddFriend","properties":{"description":"GDPRInsertRequest"}}],"loaderConfig":{"entryModuleId":"gdpr-insert-request.bundle","internalModuleBaseUrls":["https://publiccdn.sharepointonline.com/mstnd677827.sharepoint.com/sites/CDN/CDNFiles/GDPRActivityHub/"],"scriptResources":{"gdpr-insert-request.bundle":{"type":"path","path":"gdpr-insert-request.bundle_6e7ccdea4dbc35a40a24586ba7cb0588.js"},"gdprInsertRequestStrings":{"defaultPath":"gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js","type":"localizedPath","paths":{}},"react":{"type":"component","version":"15.4.2","id":"0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d"},"react-dom":{"type":"component","version":"15.4.2","id":"aa0a46ec-1505-43cd-a44a-93f3a5aa460a"},"@microsoft/sp-core-library":{"type":"component","version":"1.0.0","id":"7263c7d0-1d6a-45ec-8d85-d4d1d234171b"},"@microsoft/sp-webpart-base":{"type":"component","version":"1.0.0","id":"974a7777-0990-4136-8fa6-95d80114c2e0"},"@microsoft/sp-http":{"type":"component","version":"1.0.0","id":"c07208f0-ea3b-4c1a-9965-ac1b825211a6"}}}} -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/50c8ec09-6a38-4831-a537-fdedd8e560f3.json: -------------------------------------------------------------------------------- 1 | {"id":"50c8ec09-6a38-4831-a537-fdedd8e560f3","alias":"GdprHierarchyWebPart","componentType":"WebPart","version":"0.0.1","manifestVersion":2,"preconfiguredEntries":[{"groupId":"50c8ec09-6a38-4831-a537-fdedd8e560f3","group":{"default":"GDPR"},"title":{"default":"GDPR Hierarchy"},"description":{"default":"Hierarchy of Roles for GDPR Starter Kit"},"officeFabricIconFontName":"Org","properties":{"description":"GDPRHierarchy"}}],"loaderConfig":{"entryModuleId":"gdpr-hierarchy.bundle","internalModuleBaseUrls":["https://publiccdn.sharepointonline.com/mstnd677827.sharepoint.com/sites/CDN/CDNFiles/GDPRActivityHub/"],"scriptResources":{"gdpr-hierarchy.bundle":{"type":"path","path":"gdpr-hierarchy.bundle_a98dc4439b48f1c5878853333a59b943.js"},"gdprInsertRequestStrings":{"defaultPath":"gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js","type":"localizedPath","paths":{}},"react":{"type":"component","version":"15.4.2","id":"0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d"},"react-dom":{"type":"component","version":"15.4.2","id":"aa0a46ec-1505-43cd-a44a-93f3a5aa460a"},"@microsoft/sp-core-library":{"type":"component","version":"1.0.0","id":"7263c7d0-1d6a-45ec-8d85-d4d1d234171b"},"@microsoft/sp-webpart-base":{"type":"component","version":"1.0.0","id":"974a7777-0990-4136-8fa6-95d80114c2e0"}}}} -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/8805cdf9-9e29-4c17-b0a5-797950a6a4bd.json: -------------------------------------------------------------------------------- 1 | {"id":"8805cdf9-9e29-4c17-b0a5-797950a6a4bd","alias":"GdprDashboardWebPart","componentType":"WebPart","version":"0.0.1","manifestVersion":2,"preconfiguredEntries":[{"groupId":"8805cdf9-9e29-4c17-b0a5-797950a6a4bd","group":{"default":"GDPR"},"title":{"default":"GDPR Dashboard"},"description":{"default":"GDPR Starter Kit Dashboard"},"officeFabricIconFontName":"TaskManager","properties":{"phasesImageUrl":"","phasesMap":""}}],"loaderConfig":{"entryModuleId":"gdpr-dashboard.bundle","internalModuleBaseUrls":["https://publiccdn.sharepointonline.com/mstnd677827.sharepoint.com/sites/CDN/CDNFiles/GDPRActivityHub/"],"scriptResources":{"gdpr-dashboard.bundle":{"type":"path","path":"gdpr-dashboard.bundle_4852c96f474c692beabc9e7eb3c48a83.js"},"gdprDashboardStrings":{"defaultPath":"gdprstarterkit-gdprdashboardstrings_en-us_64e22a6c9a837771ed30bcdcbed47707.js","type":"localizedPath","paths":{}},"gdprInsertRequestStrings":{"defaultPath":"gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js","type":"localizedPath","paths":{}},"react":{"type":"component","version":"15.4.2","id":"0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d"},"react-dom":{"type":"component","version":"15.4.2","id":"aa0a46ec-1505-43cd-a44a-93f3a5aa460a"},"@microsoft/sp-core-library":{"type":"component","version":"1.0.0","id":"7263c7d0-1d6a-45ec-8d85-d4d1d234171b"},"@microsoft/sp-webpart-base":{"type":"component","version":"1.0.0","id":"974a7777-0990-4136-8fa6-95d80114c2e0"}}}} -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/d67d6088-e294-4a23-af02-c4ea8783c23f.json: -------------------------------------------------------------------------------- 1 | {"id":"d67d6088-e294-4a23-af02-c4ea8783c23f","alias":"GdprInsertEventWebPart","componentType":"WebPart","version":"0.0.1","manifestVersion":2,"preconfiguredEntries":[{"groupId":"d67d6088-e294-4a23-af02-c4ea8783c23f","group":{"default":"GDPR"},"title":{"default":"GDPR Insert Event"},"description":{"default":"Allows to insert a new GDPR Event or Incident"},"officeFabricIconFontName":"PageAdd","properties":{"description":"GDPRInsertEvent"}}],"loaderConfig":{"entryModuleId":"gdpr-insert-event.bundle","internalModuleBaseUrls":["https://publiccdn.sharepointonline.com/mstnd677827.sharepoint.com/sites/CDN/CDNFiles/GDPRActivityHub/"],"scriptResources":{"gdpr-insert-event.bundle":{"type":"path","path":"gdpr-insert-event.bundle_760ac1e4850f245ef623df4e18dac5eb.js"},"gdprInsertRequestStrings":{"defaultPath":"gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js","type":"localizedPath","paths":{}},"gdprInsertEventStrings":{"defaultPath":"gdprstarterkit-gdprinserteventstrings_en-us_6d7a4d8cdb400fea3e89e9ace1ef0ad7.js","type":"localizedPath","paths":{}},"react":{"type":"component","version":"15.4.2","id":"0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d"},"react-dom":{"type":"component","version":"15.4.2","id":"aa0a46ec-1505-43cd-a44a-93f3a5aa460a"},"@microsoft/sp-core-library":{"type":"component","version":"1.0.0","id":"7263c7d0-1d6a-45ec-8d85-d4d1d234171b"},"@microsoft/sp-webpart-base":{"type":"component","version":"1.0.0","id":"974a7777-0990-4136-8fa6-95d80114c2e0"},"@microsoft/sp-http":{"type":"component","version":"1.0.0","id":"c07208f0-ea3b-4c1a-9965-ac1b825211a6"}}}} -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/gdprstarterkit-gdprdashboardstrings_en-us_64e22a6c9a837771ed30bcdcbed47707.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Please configure the GDPR Dashboard", 4 | "BasicGroupName": "General Settings", 5 | 6 | "TaskCompletedColumnTitle": "Completed", 7 | "TaskAssigneeColumnTitle": "Assigned To", 8 | "TaskTitleColumnTitle": "Task Title", 9 | "TaskDueDateColumnTitle": "Due Date", 10 | } 11 | }); -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/gdprstarterkit-gdprinserteventstrings_en-us_6d7a4d8cdb400fea3e89e9ace1ef0ad7.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | 7 | "YesText": "Yes", 8 | "NoText": "No", 9 | 10 | "EventTypeFieldLabel": "Pick one Event/Incident Type for Personal Data", 11 | "EventTypeDataBreachLabel": "Data Breach", 12 | "EventTypeIdentityRiskLabel": "Identity Risk", 13 | "EventTypeDataConsentLabel": "Data Consent", 14 | "EventTypeDataConsentWithdrawalLabel": "Data Consent Withdrawal", 15 | "EventTypeDataProcessingLabel": "Data Processing", 16 | "EventTypeDataArchivedLabel": "Data Archived", 17 | 18 | "TitleFieldLabel": "Title", 19 | "TitleFieldPlaceholder": "Provide a Title for the Event", 20 | "TitleFieldValidationErrorMessage": "The length of the Title should be at leat 10 characters, actual is", 21 | "NotifiedByFieldLabel": "Notified By", 22 | "NotifiedByFieldPlaceholder": "Provide a description of who notified the Event", 23 | "NotifiedByFieldValidationErrorMessage": "The length of the Notified By should be at least 5 characters, actual is", 24 | "EventAssignedToFieldLabel": "Event Assigned To", 25 | "EventAssignedToFieldPlaceholder": "Provide the assignee of the Event", 26 | "EventStartDateFieldLabel": "Event Start Date", 27 | "EventStartDateFieldPlaceholder": "Provide the Start Date of the Event", 28 | "EventStartTimeHoursFieldLabel": "Hours", 29 | "EventStartTimeMinutesFieldLabel": "Minutes", 30 | "EventEndDateFieldLabel": "Event End Date", 31 | "EventEndDateFieldPlaceholder": "Provide the End Date of the Event", 32 | "EventEndTimeHoursFieldLabel": "Hours", 33 | "EventEndTimeMinutesFieldLabel": "Minutes", 34 | "PostEventReportFieldLabel": "Post Event/Incident Report", 35 | "AdditionalNotesFieldLabel": "Additional Notes", 36 | "BreachTypeFieldLabel": "Breach Type", 37 | "BreachTypeFieldPlaceholder": "Select the Breach Type", 38 | "SeverityFieldLabel": "Severity", 39 | "SeverityFieldPlaceholder": "Select the Severity level", 40 | "DPANotifiedFieldLabel": "DPA Notified", 41 | "DPANotificationDateFieldLabel": "DPA Notification Date", 42 | "DPANotificationDateFieldPlaceholder": "Provide the DPA Notification date", 43 | "DPANotificationTimeHoursFieldLabel": "Hours", 44 | "DPANotificationTimeMinutesFieldLabel": "Minutes", 45 | "EstimatedAffectedSubjectsFieldLabel": "Estimated Number of Affected Data Subjects", 46 | "EstimatedAffectedSubjectsFieldValidationErrorMessage": "Please, provide a valid number", 47 | "ToBeDeterminedFieldLabel": "To Be Determined", 48 | "IncludesChildrenFieldLabel": "Includes Children", 49 | "IncludesChildrenInProgressFieldLabel": "In Progress", 50 | "ActionPlanFieldLabel": "Action Plan", 51 | "BreachResolvedFieldLabel": "Breach Resolved", 52 | "ActionsTakenFieldLabel": "Actions Taken", 53 | "RiskTypeFieldLabel": "Risk Type", 54 | "RiskTypeFieldPlaceholder": "Select the Risk Type", 55 | "IncludesSensitiveDataFieldLabel": "Includes Sensitive Data", 56 | "IncludesSensitiveDataFieldPlaceholder": "Select any included Sensitive Data", 57 | "DataSubjectIsChildFieldLabel": "Data Subject is Child", 58 | "IndirectDataProviderFieldLabel": "Indirect Data Provider", 59 | "DataProviderFieldLabel": "Data Provider", 60 | "ConsentNotesFieldLabel": "Consent Notes", 61 | "ConsentTypeFieldLabel": "Consent Type", 62 | "ConsentTypeFieldPlaceholder": "Select the Consent Types", 63 | "ConsentIsInternalFieldLabel": "Internal or External Data Subject", 64 | "InternalConsentText": "Internal", 65 | "ExternalConsentText": "External", 66 | "ConsentWithdrawalTypeFieldLabel": "Withdrawal Type", 67 | "ConsentWithdrawalTypeFieldPlaceholder": "Select the Withdrawal Type", 68 | "ConsentWithdrawalNotesFieldLabel": "Withdrawal Notes", 69 | "OriginalConsentAvailableFieldLabel": "Original Consent Available", 70 | "OriginalConsentFieldLabel": "Original Consent", 71 | "OriginalConsentFieldPlaceholder": "Select the Original Consent", 72 | "NotifyApplicableFieldLabel": "Notify Applicable Controllers /Processors & Sub Processors", 73 | "ProcessingTypeFieldLabel": "Processing Type", 74 | "ProcessingTypeFieldPlaceholder": "Select the Processing Type", 75 | "ProcessorsFieldLabel": "GDPR Processors", 76 | "ProcessorsFieldPlaceholder": "Select the GDPR Processors", 77 | "ArchivedDataFieldLabel": "Archived Data", 78 | "AnonymizeFieldLabel": "Anonymize", 79 | "ArchivingNotesFieldLabel": "Archiving Notes", 80 | 81 | "SaveButtonText": "Save", 82 | "CancelButtonText": "Cancel", 83 | "ItemSavedMessage": "Item saved!", 84 | "InsertNextLabel": "Insert next", 85 | "GoHomeLabel": "Go home", 86 | "ItemInsertedDialogTitle": "Item inserted", 87 | "ItemInsertedDialogSubText": "Your item has been inserted. Now you can add another one, or you can go back to the home page.", 88 | 89 | "HoursValidationError": "Please provide a valid hours value", 90 | "MinutesValidationError": "Please provide a valid minutes value", 91 | } 92 | }); -------------------------------------------------------------------------------- /Scripts/GDPRActivityHub/gdprstarterkit-gdprinsertrequeststrings_en-us_de4671729bfc15d1892a4267350ceb45.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Insert Request Configuration", 4 | "BasicGroupName": "Basic Properties", 5 | "TargetListFieldLabel": "Target List", 6 | 7 | "YesText": "Yes", 8 | "NoText": "No", 9 | 10 | "RequestTypeFieldLabel": "Pick one Request Type for Personal Data", 11 | "RequestTypeAccessLabel": "Access", 12 | "RequestTypeCorrectLabel": "Correct", 13 | "RequestTypeExportLabel": "Export", 14 | "RequestTypeObjectionLabel": "Objection", 15 | "RequestTypeEraseLabel": "Erase", 16 | 17 | "TitleFieldLabel": "Title", 18 | "TitleFieldPlaceholder": "Provide a Title for the Request", 19 | "TitleFieldValidationErrorMessage": "The length of the Title should be at leat 10 characters, actual is", 20 | "DataSubjectFieldLabel": "Data Subject", 21 | "DataSubjectFieldPlaceholder": "Provide a target Data Subject", 22 | "DataSubjectEmailFieldLabel": "Data Subject Email", 23 | "DataSubjectEmailFieldPlaceholder": "Provide a valid email address for the Data Subject", 24 | "DataSubjectEmailFieldValidationErrorMessage": "Please, provide a valid email address", 25 | "VerifiedDataSubjectFieldLabel": "Verified Data Subject", 26 | "RequestAssignedToFieldLabel": "Request Assigned To", 27 | "RequestAssignedToFieldPlaceholder": "Provide the assignee for the Request", 28 | "RequestInsertionDateFieldLabel": "Request Insertion Date", 29 | "RequestInsertionDateFieldPlaceholder": "Provide the Insertion Date of the Request", 30 | "RequestDueDateFieldLabel": "Request Due Date", 31 | "RequestDueDateFieldPlaceholder": "Provide the Due Date of the Request", 32 | "AdditionalNotesFieldLabel": "Additional Notes", 33 | "DeliveryMethodFieldLabel": "Delivery Method", 34 | "DeliveryMethodFieldPlaceholder": "Provide the Delivery Method for the Request", 35 | "CorrectionDefinitionFieldLabel": "Correction Definition", 36 | "DeliveryFormatFieldLabel": "Delivery Format", 37 | "DeliveryFormatFieldPlaceholder": "Provide the Delivery Format for the Request", 38 | "PersonalDataFieldLabel": "Personal Data", 39 | "ProcessingTypeFieldLabel": "Processing Type", 40 | "ProcessingTypeFieldPlaceholder": "Provide the Processing Type for the Request", 41 | "ReasonFieldLabel": "Reason", 42 | "NotifyApplicableFieldLabel": "Notify Applicable Controllers /Processors & Sub Processors", 43 | 44 | "SaveButtonText": "Save", 45 | "CancelButtonText": "Cancel", 46 | "ItemSavedMessage": "Item saved!", 47 | "InsertNextLabel": "Insert next", 48 | "GoHomeLabel": "Go home", 49 | "ItemInsertedDialogTitle": "Item inserted", 50 | "ItemInsertedDialogSubText": "Your item has been inserted. Now you can add another one, or you can go back to the home page.", 51 | } 52 | }); -------------------------------------------------------------------------------- /Scripts/Provision-GDPRActivityHub.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Provisions a GDPR Activity Hub site 4 | 5 | .EXAMPLE 6 | PS C:\> .\Provision-GDPRActivityHub.ps1 -SiteName "GDPRActivityHub" -SiteDescription "My GDPR Activity Hub" -Credentials $credentials 7 | 8 | .EXAMPLE 9 | PS C:\> .\Provision-GDPRActivityHub.ps1 -SiteName "GDPRActivityHub" -SiteDescription "My GDPR Activity Hub" -ConfigureCDN -CDNSiteName "CDN" -Credentials $credentials 10 | 11 | #> 12 | [CmdletBinding()] 13 | param 14 | ( 15 | [Parameter(Mandatory = $true, HelpMessage="The URL of the already created Modern Site")] 16 | [String] 17 | $GroupSiteUrl, 18 | 19 | [Parameter(ParameterSetName = "CDN", Mandatory = $true, HelpMessage="Declares whether to create and configure a CDN in the target Office 365 tenant")] 20 | [Switch] 21 | $ConfigureCDN, 22 | 23 | [Parameter(ParameterSetName = "CDN", Mandatory = $true, HelpMessage="The name of the Team Site that will be created to support the CDN, e.g. ""CDN""")] 24 | [String] 25 | $CDNSiteName, 26 | 27 | [Parameter(ParameterSetName = "CDN", Mandatory = $true, HelpMessage="The name of the Docuemnt Library that will be created to support the CDN, e.g. ""CDNFiles""")] 28 | [String] 29 | $CDNLibraryName, 30 | 31 | [Parameter(Mandatory = $false, HelpMessage="Optional tenant administration credentials")] 32 | [PSCredential] 33 | $Credentials 34 | ) 35 | 36 | try 37 | { 38 | # ********************************************** 39 | # Prompt for Disclaimer 40 | # ********************************************** 41 | 42 | $wscript = New-Object -comobject wscript.shell 43 | $disclaimer = $wscript.popup("This GDPR Activity Hub is intended to assist organizations with their GDPR compliance progress. This GDPR Activity Hub should not be relied upon to determine how GDPR applies to an organization or an organization’s compliance with GDPR. This GDPR Activity Hub does not constitute legal advice, nor does it provide any certifications or guarantees regarding GDPR compliance. Instead, we hope the GDPR Activity Hub identifies steps that organizations can implement to simplify their GDPR compliance efforts. The application of GDPR is highly fact-specific. We encourage all organizations using this GDPR Activity Hub to work with a legally qualified professional to discuss GDPR, how it applies specifically to their organization, and how best to ensure compliance. 44 | 45 | MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, AS TO THE INFORMATION IN THIS GDPR Activity Hub. Microsoft disclaims any conditions, express or implied, or other terms that use of the Microsoft products or services will ensure the organization’s compliance with the GDPR. This GDPR Activity Hub is provided ""as-is."" Information and recommendations expressed in this GDPR Activity Hub may change without notice. 46 | 47 | This GDPR Activity Hub does not provide the user with any legal rights to any intellectual property in any Microsoft product or service. Organizations may use this GDPR Activity Hub for internal, reference purposes only. 48 | 49 | © 2017 Microsoft. All rights reserved.", ` 50 | 60, "DISCLAIMER", 1 + 64) 51 | 52 | If ($disclaimer -eq 1) { 53 | 54 | # ********************************************** 55 | # Provision SharePoint Online artifacts 56 | # ********************************************** 57 | 58 | Write-Host "Creating artifacts on target site" $GroupSiteUrl 59 | 60 | # Get credentials to connect to SharePoint Online, if they are missing 61 | if($Credentials -eq $null) 62 | { 63 | $Credentials = Get-Credential -Message "Enter Tenant Admin Credentials" 64 | } 65 | 66 | # Connect to the target site 67 | Connect-PnPOnline $GroupSiteUrl -Credentials $Credentials 68 | 69 | # Provision taxonomy items, fields, content types, and lists 70 | Apply-PnPProvisioningTemplate -Path .\GDPR-Activity-Hub-Information-Architecture-Full.xml -Handlers Fields,ContentTypes,Lists,TermGroups 71 | 72 | # Provision workflows 73 | Apply-PnPProvisioningTemplate -Path .\GDPR-Activity-Hub-Workflows.xml -Handlers Workflows 74 | 75 | # ********************************************** 76 | # Configure the Office 365 CDN, if requested 77 | # ********************************************** 78 | 79 | if ($ConfigureCDN.IsPresent) 80 | { 81 | Write-Host "Configuring the Office 365 CDN Settings" 82 | $CDNDescription = "Content Delivery Network" 83 | 84 | $spoAdminCenterUrl = $GroupSiteUrl.replace(".sharepoint", "-admin.sharepoint") 85 | $spoAdminCenterUrl = $spoAdminCenterUrl.substring(0, $spoAdminCenterUrl.IndexOf("sharepoint.com/") + 15) 86 | 87 | $spoRootSiteUrl = $GroupSiteUrl.substring(0, $GroupSiteUrl.IndexOf("sharepoint.com/") + 15) 88 | $spoTenantName = $spoRootSiteUrl.Substring(8, $spoRootSiteUrl.LastIndexOf("/") - 8) 89 | 90 | # Create the CDN Site 91 | Connect-PnPOnline $spoAdminCenterUrl -Credentials $Credentials 92 | 93 | # Determine the current username 94 | $web = Get-PnPWeb 95 | $context = Get-PnPContext 96 | $user = $web.CurrentUser 97 | $context.Load($user) 98 | Execute-PnPQuery 99 | 100 | $currentUser = $user.Email 101 | 102 | # Create a new Site Collection 103 | Write-Host "Creating CDN Site Collection" 104 | $cdnSiteURL = $spoRootSiteUrl + "sites/" + $CDNSiteName 105 | New-PnPTenantSite -Title $CDNDescription -Url $cdnSiteURL -Description $CDNDescription -Owner $currentUser -Lcid 1033 -Template STS#0 -TimeZone 0 -RemoveDeletedSite -Wait 106 | 107 | # Create the CDN Files library in the CDN site 108 | Connect-PnPOnline $cdnSiteURL -Credentials $Credentials 109 | New-PnPList -Title $CDNLibraryName -Url $CDNLibraryName -Template DocumentLibrary 110 | 111 | # Build and package the solution 112 | Write-Host "Building SPFx package and bundling" 113 | Push-Location ..\GDPRStarterKit 114 | 115 | $cdnSiteAssetsFullUrl = "https://publiccdn.sharepointonline.com/" + $spoTenantName + "/sites/" + $CDNSiteName + "/" + $CDNLibraryName + "/GDPRActivityHub" 116 | & npm install --save 117 | & gulp update-manifest --cdnpath "$cdnSiteAssetsFullUrl" 118 | & gulp clean 119 | & gulp bundle --ship 120 | & gulp package-solution --ship 121 | 122 | Pop-Location 123 | 124 | # Create a folder in the CDNFiles document library 125 | Write-Host "Uploading SPFx assets to the CDN" 126 | $cdnFilesLibrary = Get-PnPList -Identity $CDNLibraryName 127 | $packageFolder = $cdnFilesLibrary.RootFolder.Folders.Add("GDPRActivityHub") 128 | $context = Get-PnPContext 129 | $context.Load($packageFolder) 130 | Execute-PnPQuery 131 | 132 | $cdnSiteAssetsUploadUrl = $CDNLibraryName + "/GDPRActivityHub" 133 | foreach ($file in (dir ..\GDPRStarterKit\temp\deploy -File)) 134 | { 135 | $uploadedFile = Add-PnPFile -Path $file.FullName -Folder $cdnSiteAssetsUploadUrl 136 | } 137 | 138 | # Configure the CDN at the tenant level 139 | Connect-SPOService -Url $spoAdminCenterUrl -Credential $Credentials 140 | Set-SPOTenantCdnEnabled -CdnType Public -Confirm:$false 141 | 142 | Add-SPOTenantCdnOrigin -CdnType Public -OriginUrl sites/$CDNSiteName/$CDNLibraryName -Confirm:$false 143 | } 144 | 145 | Write-Host -ForegroundColor Green "All the automatic steps are now completed!" 146 | Write-Host "Please proceed with the manual steps documented on the Setup Guide!" 147 | 148 | $sppkgPath = (Get-Item -Path "..\GDPRStarterKit\sharepoint\solution\gdpr-starter-kit.sppkg" -Verbose).FullName 149 | Write-Host "You can find the .SPPKG file at the following path:" $sppkgPath 150 | 151 | 152 | } else { 153 | Write-Host "The operation has been cancelled." 154 | } 155 | } 156 | catch 157 | { 158 | Write-Host -ForegroundColor Red "Exception occurred!" 159 | Write-Host -ForegroundColor Red "Exception Type: $($_.Exception.GetType().FullName)" 160 | Write-Host -ForegroundColor Red "Exception Message: $($_.Exception.Message)" 161 | } 162 | -------------------------------------------------------------------------------- /Scripts/ac552ede-9be6-4905-8264-b07f8fa0cc44.xaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Scripts/ac552ede-9be6-4905-8264-b07f8fa0cc44.xaml -------------------------------------------------------------------------------- /Scripts/e8d4ef94-75ef-4d39-883d-c6661f768ac7.xaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Scripts/e8d4ef94-75ef-4d39-883d-c6661f768ac7.xaml -------------------------------------------------------------------------------- /Scripts/ef2f50ba-7a12-4f34-9e97-35cd6de3d4b7.xaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharePoint/sp-dev-gdpr-activity-hub/10096c6a984436352c93223b733d1c034037268c/Scripts/ef2f50ba-7a12-4f34-9e97-35cd6de3d4b7.xaml --------------------------------------------------------------------------------