Add processing activity exports
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
315
pkg/docgen/data_protection_impact_assessments_template.html
Normal file
315
pkg/docgen/data_protection_impact_assessments_template.html
Normal file
@@ -0,0 +1,315 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Data Protection Impact Assessments Export</title>
|
||||
<style>
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 2.5cm;
|
||||
@bottom-right {
|
||||
content: "Page " counter(page) " of " counter(pages);
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Cover page */
|
||||
.cover-page {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.company-header {
|
||||
margin-bottom: 30px;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-height: 50px;
|
||||
max-width: 250px;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 16pt;
|
||||
color: #1a1a1a;
|
||||
margin-top: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.export-title {
|
||||
font-size: 22pt;
|
||||
font-weight: normal;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 25px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.document-meta {
|
||||
margin: 0 0 30px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.meta-table td {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #333;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.meta-table td:first-child {
|
||||
font-weight: 600;
|
||||
width: 25%;
|
||||
background: #f8f8f8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.classification {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.purpose-section {
|
||||
margin: 30px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-text {
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
|
||||
/* Assessment page */
|
||||
.assessment-page {
|
||||
page-break-before: always;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.assessment-page:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
|
||||
.assessment-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.assessment-field {
|
||||
margin-bottom: 8px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.assessment-field-label {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 6px 0;
|
||||
page-break-after: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.assessment-field-value {
|
||||
color: #000;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.empty-value {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Annex page */
|
||||
.annex-page {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
.annex-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.annex-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.annex-section-title {
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
.annex-subsection-title {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.annex-enum-list {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.annex-enum-item {
|
||||
margin-bottom: 8px;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.annex-enum-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.annex-enum-description {
|
||||
color: #000;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cover-page">
|
||||
<div class="company-header">
|
||||
{{- if .CompanyHorizontalLogoBase64}}
|
||||
{{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}}
|
||||
{{- else}}
|
||||
<div class="company-name">{{.CompanyName}}</div>
|
||||
{{- end}}
|
||||
</div>
|
||||
|
||||
<h1 class="export-title">Data Protection Impact Assessments</h1>
|
||||
|
||||
<div class="document-meta">
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td>Classification</td>
|
||||
<td>
|
||||
<span class="classification">CONFIDENTIAL</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{.Version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Published</td>
|
||||
<td>{{.PublishedAt.Format "January 2, 2006"}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="purpose-section">
|
||||
<div class="purpose-title">1. Purpose</div>
|
||||
<div class="purpose-text">
|
||||
This document contains Data Protection Impact Assessments (DPIAs) conducted for processing activities
|
||||
that present a high risk to individuals' rights and freedoms. DPIAs are systematic assessments that
|
||||
evaluate the necessity, proportionality, and risks associated with data processing operations, along
|
||||
with the measures implemented to mitigate identified risks.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{- range $index, $assessment := .Assessments}}
|
||||
<div class="assessment-page">
|
||||
{{if eq $index 0}}
|
||||
<h1 class="annex-title">2. Records</h1>
|
||||
{{end}}
|
||||
<h1 class="annex-section-title">2.{{add $index 1}} {{$assessment.ProcessingActivityName}}</h1>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Description</div>
|
||||
<div class="assessment-field-value">{{if .Description}}{{.Description}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Necessity and Proportionality</div>
|
||||
<div class="assessment-field-value">{{if .NecessityAndProportionality}}{{.NecessityAndProportionality}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Potential Risk</div>
|
||||
<div class="assessment-field-value">{{if .PotentialRisk}}{{.PotentialRisk}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Mitigations</div>
|
||||
<div class="assessment-field-value">{{if .Mitigations}}{{.Mitigations}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Residual Risk</div>
|
||||
<div class="assessment-field-value">{{if .ResidualRisk}}{{.ResidualRisk | formatResidualRisk}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end}}
|
||||
|
||||
<div class="annex-page">
|
||||
<h1 class="annex-title">3. Annexes</h1>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-section-title">3.1 Lexicon</div>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">Residual Risk</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Low:</span>
|
||||
<span class="annex-enum-description">The residual risk after implementing mitigation measures is considered low. The processing activity poses minimal risk to individuals' rights and freedoms.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Medium:</span>
|
||||
<span class="annex-enum-description">The residual risk after implementing mitigation measures is considered medium. The processing activity poses a moderate risk to individuals' rights and freedoms, requiring ongoing monitoring.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">High:</span>
|
||||
<span class="annex-enum-description">The residual risk after implementing mitigation measures is considered high. The processing activity poses significant risk to individuals' rights and freedoms, requiring enhanced safeguards and regular review.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,21 +23,36 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
gmhtml "github.com/yuin/goldmark/renderer/html"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
const (
|
||||
firstPageTOCItems = 21
|
||||
otherPageTOCItems = 28
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed template.html
|
||||
htmlTemplateContent string
|
||||
|
||||
//go:embed processing_activities_template.html
|
||||
processingActivitiesTemplateContent string
|
||||
|
||||
//go:embed data_protection_impact_assessments_template.html
|
||||
dataProtectionImpactAssessmentsTemplateContent string
|
||||
|
||||
//go:embed transfer_impact_assessments_template.html
|
||||
transferImpactAssessmentsTemplateContent string
|
||||
|
||||
templateFuncs = template.FuncMap{
|
||||
"now": func() time.Time { return time.Now() },
|
||||
"eq": func(a, b string) bool { return a == b },
|
||||
"eq": func(a, b any) bool { return a == b },
|
||||
"string": func(v fmt.Stringer) string { return v.String() },
|
||||
"lower": func(s string) string { return strings.ToLower(s) },
|
||||
"add": func(a, b int) int { return a + b },
|
||||
"classificationString": func(c Classification) string { return string(c) },
|
||||
"formatContent": func(content string) template.HTML {
|
||||
md := goldmark.New(
|
||||
@@ -56,9 +71,111 @@ var (
|
||||
"imgTag": func(src, alt, class string) template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<img src="%s" alt="%s" class="%s">`, html.EscapeString(src), html.EscapeString(alt), html.EscapeString(class)))
|
||||
},
|
||||
"formatLawfulBasis": func(basis coredata.ProcessingActivityLawfulBasis) string {
|
||||
switch basis {
|
||||
case coredata.ProcessingActivityLawfulBasisConsent:
|
||||
return "Consent"
|
||||
case coredata.ProcessingActivityLawfulBasisContractualNecessity:
|
||||
return "Contractual Necessity"
|
||||
case coredata.ProcessingActivityLawfulBasisLegalObligation:
|
||||
return "Legal Obligation"
|
||||
case coredata.ProcessingActivityLawfulBasisLegitimateInterest:
|
||||
return "Legitimate Interest"
|
||||
case coredata.ProcessingActivityLawfulBasisPublicTask:
|
||||
return "Public Task"
|
||||
case coredata.ProcessingActivityLawfulBasisVitalInterests:
|
||||
return "Vital Interests"
|
||||
default:
|
||||
return basis.String()
|
||||
}
|
||||
},
|
||||
"formatSpecialOrCriminalData": func(data coredata.ProcessingActivitySpecialOrCriminalDatum) string {
|
||||
switch data {
|
||||
case coredata.ProcessingActivitySpecialOrCriminalDatumYes:
|
||||
return "Yes"
|
||||
case coredata.ProcessingActivitySpecialOrCriminalDatumNo:
|
||||
return "No"
|
||||
case coredata.ProcessingActivitySpecialOrCriminalDatumPossible:
|
||||
return "Possible"
|
||||
default:
|
||||
return data.String()
|
||||
}
|
||||
},
|
||||
"formatTransferSafeguard": func(safeguard *coredata.ProcessingActivityTransferSafeguard) string {
|
||||
if safeguard == nil {
|
||||
return ""
|
||||
}
|
||||
switch *safeguard {
|
||||
case coredata.ProcessingActivityTransferSafeguardStandardContractualClauses:
|
||||
return "Standard Contractual Clauses"
|
||||
case coredata.ProcessingActivityTransferSafeguardBindingCorporateRules:
|
||||
return "Binding Corporate Rules"
|
||||
case coredata.ProcessingActivityTransferSafeguardAdequacyDecision:
|
||||
return "Adequacy Decision"
|
||||
case coredata.ProcessingActivityTransferSafeguardDerogations:
|
||||
return "Derogations"
|
||||
case coredata.ProcessingActivityTransferSafeguardCodesOfConduct:
|
||||
return "Codes of Conduct"
|
||||
case coredata.ProcessingActivityTransferSafeguardCertificationMechanisms:
|
||||
return "Certification Mechanisms"
|
||||
default:
|
||||
return safeguard.String()
|
||||
}
|
||||
},
|
||||
"formatDPIANeeded": func(needed coredata.ProcessingActivityDataProtectionImpactAssessment) string {
|
||||
switch needed {
|
||||
case coredata.ProcessingActivityDataProtectionImpactAssessmentNeeded:
|
||||
return "Yes"
|
||||
case coredata.ProcessingActivityDataProtectionImpactAssessmentNotNeeded:
|
||||
return "No"
|
||||
default:
|
||||
return needed.String()
|
||||
}
|
||||
},
|
||||
"formatTIANeeded": func(needed coredata.ProcessingActivityTransferImpactAssessment) string {
|
||||
switch needed {
|
||||
case coredata.ProcessingActivityTransferImpactAssessmentNeeded:
|
||||
return "Yes"
|
||||
case coredata.ProcessingActivityTransferImpactAssessmentNotNeeded:
|
||||
return "No"
|
||||
default:
|
||||
return needed.String()
|
||||
}
|
||||
},
|
||||
"formatRole": func(role coredata.ProcessingActivityRole) string {
|
||||
switch role {
|
||||
case coredata.ProcessingActivityRoleController:
|
||||
return "Controller"
|
||||
case coredata.ProcessingActivityRoleProcessor:
|
||||
return "Processor"
|
||||
default:
|
||||
return role.String()
|
||||
}
|
||||
},
|
||||
"formatResidualRisk": func(risk *coredata.DataProtectionImpactAssessmentResidualRisk) string {
|
||||
if risk == nil {
|
||||
return ""
|
||||
}
|
||||
switch *risk {
|
||||
case coredata.DataProtectionImpactAssessmentResidualRiskLow:
|
||||
return "Low"
|
||||
case coredata.DataProtectionImpactAssessmentResidualRiskMedium:
|
||||
return "Medium"
|
||||
case coredata.DataProtectionImpactAssessmentResidualRiskHigh:
|
||||
return "High"
|
||||
default:
|
||||
return risk.String()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
documentTemplate = template.Must(template.New("document").Funcs(templateFuncs).Parse(htmlTemplateContent))
|
||||
|
||||
processingActivitiesTemplate = template.Must(template.New("processingActivities").Funcs(templateFuncs).Parse(processingActivitiesTemplateContent))
|
||||
|
||||
dataProtectionImpactAssessmentsTemplate = template.Must(template.New("dataProtectionImpactAssessments").Funcs(templateFuncs).Parse(dataProtectionImpactAssessmentsTemplateContent))
|
||||
|
||||
transferImpactAssessmentsTemplate = template.Must(template.New("transferImpactAssessments").Funcs(templateFuncs).Parse(transferImpactAssessmentsTemplateContent))
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -82,6 +199,71 @@ type (
|
||||
State coredata.DocumentVersionSignatureState
|
||||
RequestedAt time.Time
|
||||
}
|
||||
|
||||
ProcessingActivityTableData struct {
|
||||
CompanyName string
|
||||
CompanyHorizontalLogoBase64 string
|
||||
Version int
|
||||
PublishedAt time.Time
|
||||
Activities []ProcessingActivityRowData
|
||||
}
|
||||
|
||||
ProcessingActivityRowData struct {
|
||||
Name string
|
||||
Purpose *string
|
||||
DataSubjectCategory *string
|
||||
PersonalDataCategory *string
|
||||
SpecialOrCriminalData coredata.ProcessingActivitySpecialOrCriminalDatum
|
||||
ConsentEvidenceLink *string
|
||||
LawfulBasis coredata.ProcessingActivityLawfulBasis
|
||||
Recipients *string
|
||||
Location *string
|
||||
InternationalTransfers bool
|
||||
TransferSafeguards *coredata.ProcessingActivityTransferSafeguard
|
||||
RetentionPeriod *string
|
||||
SecurityMeasures *string
|
||||
DataProtectionImpactAssessmentNeeded coredata.ProcessingActivityDataProtectionImpactAssessment
|
||||
TransferImpactAssessmentNeeded coredata.ProcessingActivityTransferImpactAssessment
|
||||
LastReviewDate *time.Time
|
||||
NextReviewDate *time.Time
|
||||
Role coredata.ProcessingActivityRole
|
||||
DataProtectionOfficerFullName *string
|
||||
Vendors string
|
||||
}
|
||||
|
||||
DataProtectionImpactAssessmentTableData struct {
|
||||
CompanyName string
|
||||
CompanyHorizontalLogoBase64 string
|
||||
Version int
|
||||
PublishedAt time.Time
|
||||
Assessments []DataProtectionImpactAssessmentRowData
|
||||
}
|
||||
|
||||
DataProtectionImpactAssessmentRowData struct {
|
||||
ProcessingActivityName string
|
||||
Description *string
|
||||
NecessityAndProportionality *string
|
||||
PotentialRisk *string
|
||||
Mitigations *string
|
||||
ResidualRisk *coredata.DataProtectionImpactAssessmentResidualRisk
|
||||
}
|
||||
|
||||
TransferImpactAssessmentTableData struct {
|
||||
CompanyName string
|
||||
CompanyHorizontalLogoBase64 string
|
||||
Version int
|
||||
PublishedAt time.Time
|
||||
Assessments []TransferImpactAssessmentRowData
|
||||
}
|
||||
|
||||
TransferImpactAssessmentRowData struct {
|
||||
ProcessingActivityName string
|
||||
DataSubjects *string
|
||||
LegalMechanism *string
|
||||
Transfer *string
|
||||
LocalLawRisk *string
|
||||
SupplementaryMeasures *string
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -99,3 +281,30 @@ func RenderHTML(data DocumentData) ([]byte, error) {
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func RenderProcessingActivitiesTableHTML(data ProcessingActivityTableData) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := processingActivitiesTemplate.Execute(&buf, data); err != nil {
|
||||
return nil, fmt.Errorf("cannot execute processing activities template: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func RenderDataProtectionImpactAssessmentsTableHTML(data DataProtectionImpactAssessmentTableData) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := dataProtectionImpactAssessmentsTemplate.Execute(&buf, data); err != nil {
|
||||
return nil, fmt.Errorf("cannot execute data protection impact assessments template: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func RenderTransferImpactAssessmentsTableHTML(data TransferImpactAssessmentTableData) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := transferImpactAssessmentsTemplate.Execute(&buf, data); err != nil {
|
||||
return nil, fmt.Errorf("cannot execute transfer impact assessments template: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func TestTemplateFunctions(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("eq function", func(t *testing.T) {
|
||||
eqFunc := templateFuncs["eq"].(func(string, string) bool)
|
||||
eqFunc := templateFuncs["eq"].(func(any, any) bool)
|
||||
assert.True(t, eqFunc("test", "test"))
|
||||
assert.False(t, eqFunc("test", "other"))
|
||||
})
|
||||
@@ -266,7 +266,7 @@ func TestHTMLEscaping(t *testing.T) {
|
||||
|
||||
resultStr := string(result)
|
||||
|
||||
// Verify dangerous content is escaped
|
||||
// Verify dangerous content is escaped
|
||||
assert.NotContains(t, resultStr, "<script>alert('xss')</script>")
|
||||
assert.NotContains(t, resultStr, "<malicious>tag")
|
||||
assert.Contains(t, resultStr, "<script>")
|
||||
|
||||
535
pkg/docgen/processing_activities_template.html
Normal file
535
pkg/docgen/processing_activities_template.html
Normal file
@@ -0,0 +1,535 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Processing Activities Export</title>
|
||||
<style>
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 2.5cm;
|
||||
@bottom-right {
|
||||
content: "Page " counter(page) " of " counter(pages);
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Cover page */
|
||||
.cover-page {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.company-header {
|
||||
margin-bottom: 30px;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-height: 50px;
|
||||
max-width: 250px;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 16pt;
|
||||
color: #1a1a1a;
|
||||
margin-top: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.export-title {
|
||||
font-size: 22pt;
|
||||
font-weight: normal;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 25px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.document-meta {
|
||||
margin: 0 0 30px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.meta-table td {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #333;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.meta-table td:first-child {
|
||||
font-weight: 600;
|
||||
width: 25%;
|
||||
background: #f8f8f8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.classification {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.purpose-section {
|
||||
margin: 30px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-text {
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
|
||||
/* Activity page */
|
||||
.activity-page {
|
||||
page-break-before: always;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.activity-page:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.activity-section {
|
||||
margin-bottom: 8px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.activity-section-title {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 6px 0;
|
||||
page-break-after: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.activity-field {
|
||||
margin-bottom: 8px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.activity-field-label {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 3px;
|
||||
font-size: 7.5pt;
|
||||
}
|
||||
|
||||
.activity-field-value {
|
||||
color: #000;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.empty-value {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.activity-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.activity-grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Annex page */
|
||||
.annex-page {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
.annex-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.annex-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.annex-section-title {
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
.annex-subsection-title {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.annex-enum-list {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.annex-enum-item {
|
||||
margin-bottom: 8px;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.annex-enum-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.annex-enum-description {
|
||||
color: #000;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cover-page">
|
||||
<div class="company-header">
|
||||
{{- if .CompanyHorizontalLogoBase64}}
|
||||
{{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}}
|
||||
{{- else}}
|
||||
<div class="company-name">{{.CompanyName}}</div>
|
||||
{{- end}}
|
||||
</div>
|
||||
|
||||
<h1 class="export-title">Processing Activities</h1>
|
||||
|
||||
<div class="document-meta">
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td>Classification</td>
|
||||
<td>
|
||||
<span class="classification">CONFIDENTIAL</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{.Version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Published</td>
|
||||
<td>{{.PublishedAt.Format "January 2, 2006"}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="purpose-section">
|
||||
<div class="purpose-title">1. Purpose</div>
|
||||
<div class="purpose-text">
|
||||
This document provides a comprehensive overview of all processing activities within the organization.
|
||||
It serves as a record of personal data processing operations, documenting the purposes, legal bases,
|
||||
data categories, and associated safeguards for each activity.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{- range $index, $activity := .Activities}}
|
||||
<div class="activity-page">
|
||||
{{if eq $index 0}}
|
||||
<h1 class="annex-title">2. Records</h1>
|
||||
{{end}}
|
||||
<h1 class="annex-section-title">2.{{add $index 1}} {{$activity.Name}}</h1>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.1 General Information</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Purpose</div>
|
||||
<div class="activity-field-value">{{if .Purpose}}{{.Purpose}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Role</div>
|
||||
<div class="activity-field-value">{{.Role | formatRole}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.2 Data Categories</div>
|
||||
<div class="activity-grid-3">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Data Subject Category</div>
|
||||
<div class="activity-field-value">{{if .DataSubjectCategory}}{{.DataSubjectCategory}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Personal Data Category</div>
|
||||
<div class="activity-field-value">{{if .PersonalDataCategory}}{{.PersonalDataCategory}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Special/Criminal Data</div>
|
||||
<div class="activity-field-value">{{.SpecialOrCriminalData | formatSpecialOrCriminalData}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.3 Legal Basis</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Lawful Basis</div>
|
||||
<div class="activity-field-value">{{.LawfulBasis | formatLawfulBasis}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Consent Evidence Link</div>
|
||||
<div class="activity-field-value">{{if .ConsentEvidenceLink}}{{.ConsentEvidenceLink}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.4 Data Sharing & Transfers</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Recipients</div>
|
||||
<div class="activity-field-value">{{if .Recipients}}{{.Recipients}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Location</div>
|
||||
<div class="activity-field-value">{{if .Location}}{{.Location}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">International Transfers</div>
|
||||
<div class="activity-field-value">{{if .InternationalTransfers}}Yes{{else}}No{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Transfer Safeguards</div>
|
||||
<div class="activity-field-value">{{if .TransferSafeguards}}{{.TransferSafeguards | formatTransferSafeguard}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.5 Retention & Security</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Retention Period</div>
|
||||
<div class="activity-field-value">{{if .RetentionPeriod}}{{.RetentionPeriod}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Security Measures</div>
|
||||
<div class="activity-field-value">{{if .SecurityMeasures}}{{.SecurityMeasures}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.6 Assessments & Reviews</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">DPIA Needed</div>
|
||||
<div class="activity-field-value">{{.DataProtectionImpactAssessmentNeeded | formatDPIANeeded}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">TIA Needed</div>
|
||||
<div class="activity-field-value">{{.TransferImpactAssessmentNeeded | formatTIANeeded}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Last Review Date</div>
|
||||
<div class="activity-field-value">{{if .LastReviewDate}}{{.LastReviewDate.Format "January 2, 2006"}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Next Review Date</div>
|
||||
<div class="activity-field-value">{{if .NextReviewDate}}{{.NextReviewDate.Format "January 2, 2006"}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-section">
|
||||
<div class="activity-section-title">2.{{add $index 1}}.7 Responsible Parties</div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Data Protection Officer</div>
|
||||
<div class="activity-field-value">{{if .DataProtectionOfficerFullName}}{{.DataProtectionOfficerFullName}}{{else}}<span class="empty-value">Not assigned</span>{{end}}</div>
|
||||
</div>
|
||||
<div class="activity-field">
|
||||
<div class="activity-field-label">Vendors</div>
|
||||
<div class="activity-field-value">{{if .Vendors}}{{.Vendors}}{{else}}<span class="empty-value">None</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end}}
|
||||
|
||||
<div class="annex-page">
|
||||
<h1 class="annex-title">3. Annexes</h1>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-section-title">3.1 Lexicon</div>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">Role</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Controller:</span>
|
||||
<span class="annex-enum-description">The entity that determines the purposes and means of processing personal data.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Processor:</span>
|
||||
<span class="annex-enum-description">The entity that processes personal data on behalf of the controller.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">Lawful Basis</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Consent:</span>
|
||||
<span class="annex-enum-description">The data subject has given consent to the processing of their personal data.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Contractual Necessity:</span>
|
||||
<span class="annex-enum-description">Processing is necessary for the performance of a contract to which the data subject is party.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Legal Obligation:</span>
|
||||
<span class="annex-enum-description">Processing is necessary for compliance with a legal obligation to which the controller is subject.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Legitimate Interest:</span>
|
||||
<span class="annex-enum-description">Processing is necessary for the purposes of the legitimate interests pursued by the controller or a third party.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Public Task:</span>
|
||||
<span class="annex-enum-description">Processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Vital Interests:</span>
|
||||
<span class="annex-enum-description">Processing is necessary to protect the vital interests of the data subject or of another natural person.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">Special/Criminal Data</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Yes:</span>
|
||||
<span class="annex-enum-description">The processing activity involves special categories of personal data or data relating to criminal convictions and offences.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">No:</span>
|
||||
<span class="annex-enum-description">The processing activity does not involve special categories of personal data or data relating to criminal convictions and offences.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Possible:</span>
|
||||
<span class="annex-enum-description">The processing activity may involve special categories of personal data or data relating to criminal convictions and offences.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">Transfer Safeguards</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Standard Contractual Clauses:</span>
|
||||
<span class="annex-enum-description">Standard contractual clauses approved by the European Commission are used to ensure adequate protection for international transfers.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Binding Corporate Rules:</span>
|
||||
<span class="annex-enum-description">Binding corporate rules approved by a supervisory authority are used to ensure adequate protection for international transfers.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Adequacy Decision:</span>
|
||||
<span class="annex-enum-description">The European Commission has determined that the third country ensures an adequate level of protection.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Derogations:</span>
|
||||
<span class="annex-enum-description">A derogation under Article 49 of the GDPR is used for the international transfer.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Codes of Conduct:</span>
|
||||
<span class="annex-enum-description">An approved code of conduct together with binding and enforceable commitments is used to ensure adequate protection.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Certification Mechanisms:</span>
|
||||
<span class="annex-enum-description">An approved certification mechanism together with binding and enforceable commitments is used to ensure adequate protection.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">DPIA Needed</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Yes:</span>
|
||||
<span class="annex-enum-description">A Data Protection Impact Assessment is required for this processing activity.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">No:</span>
|
||||
<span class="annex-enum-description">A Data Protection Impact Assessment is not required for this processing activity.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="annex-section">
|
||||
<div class="annex-subsection-title">TIA Needed</div>
|
||||
<ul class="annex-enum-list">
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">Yes:</span>
|
||||
<span class="annex-enum-description">A Transfer Impact Assessment is required for this processing activity.</span>
|
||||
</li>
|
||||
<li class="annex-enum-item">
|
||||
<span class="annex-enum-name">No:</span>
|
||||
<span class="annex-enum-description">A Transfer Impact Assessment is not required for this processing activity.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
288
pkg/docgen/transfer_impact_assessments_template.html
Normal file
288
pkg/docgen/transfer_impact_assessments_template.html
Normal file
@@ -0,0 +1,288 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Transfer Impact Assessments Export</title>
|
||||
<style>
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 2.5cm;
|
||||
@bottom-right {
|
||||
content: "Page " counter(page) " of " counter(pages);
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Cover page */
|
||||
.cover-page {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.company-header {
|
||||
margin-bottom: 30px;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
max-height: 50px;
|
||||
max-width: 250px;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 16pt;
|
||||
color: #1a1a1a;
|
||||
margin-top: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.export-title {
|
||||
font-size: 22pt;
|
||||
font-weight: normal;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 25px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.document-meta {
|
||||
margin: 0 0 30px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.meta-table td {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #333;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.meta-table td:first-child {
|
||||
font-weight: 600;
|
||||
width: 25%;
|
||||
background: #f8f8f8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.classification {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.purpose-section {
|
||||
margin: 30px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.purpose-text {
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
|
||||
/* Assessment page */
|
||||
.assessment-page {
|
||||
page-break-before: always;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.assessment-page:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
|
||||
.assessment-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.assessment-field {
|
||||
margin-bottom: 8px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.assessment-field-label {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 6px 0;
|
||||
page-break-after: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.assessment-field-value {
|
||||
color: #000;
|
||||
font-size: 7.5pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.empty-value {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.annex-page {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
.annex-title {
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 0 0 15px 0;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.annex-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.annex-section-title {
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
.annex-subsection-title {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.annex-enum-list {
|
||||
margin: 10px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.annex-enum-item {
|
||||
margin-bottom: 8px;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.annex-enum-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.annex-enum-description {
|
||||
color: #000;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cover-page">
|
||||
<div class="company-header">
|
||||
{{- if .CompanyHorizontalLogoBase64}}
|
||||
{{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}}
|
||||
{{- else}}
|
||||
<div class="company-name">{{.CompanyName}}</div>
|
||||
{{- end}}
|
||||
</div>
|
||||
|
||||
<h1 class="export-title">Transfer Impact Assessments</h1>
|
||||
|
||||
<div class="document-meta">
|
||||
<table class="meta-table">
|
||||
<tr>
|
||||
<td>Classification</td>
|
||||
<td>
|
||||
<span class="classification">CONFIDENTIAL</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{.Version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Published</td>
|
||||
<td>{{.PublishedAt.Format "January 2, 2006"}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="purpose-section">
|
||||
<div class="purpose-title">1. Purpose</div>
|
||||
<div class="purpose-text">
|
||||
This document contains Transfer Impact Assessments (TIAs) conducted for processing activities involving
|
||||
international transfers of personal data to countries outside the European Economic Area (EEA). TIAs
|
||||
evaluate the legal mechanisms used for transfers, assess risks related to local laws in destination
|
||||
countries, and document supplementary measures implemented to ensure an adequate level of data protection.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{- range $index, $assessment := .Assessments}}
|
||||
<div class="assessment-page">
|
||||
{{if eq $index 0}}
|
||||
<h1 class="annex-title">2. Records</h1>
|
||||
{{end}}
|
||||
<h1 class="annex-section-title">2.{{add $index 1}} {{$assessment.ProcessingActivityName}}</h1>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Data Subjects</div>
|
||||
<div class="assessment-field-value">{{if .DataSubjects}}{{.DataSubjects}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Transfer</div>
|
||||
<div class="assessment-field-value">{{if .Transfer}}{{.Transfer}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Legal Mechanism</div>
|
||||
<div class="assessment-field-value">{{if .LegalMechanism}}{{.LegalMechanism}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Local Law Risk</div>
|
||||
<div class="assessment-field-value">{{if .LocalLawRisk}}{{.LocalLawRisk}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
|
||||
<div class="assessment-field">
|
||||
<div class="assessment-field-label">Supplementary Measures</div>
|
||||
<div class="assessment-field-value">{{if .SupplementaryMeasures}}{{.SupplementaryMeasures}}{{else}}<span class="empty-value">Not specified</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end}}
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user