Create complex types for the survey response to prepare for template writing
This commit is contained in:
parent
7330ebf9f2
commit
8e72759a7e
|
@ -1,40 +0,0 @@
|
||||||
/* import { EmailTemplate3Config } from './email-template-3';
|
|
||||||
* import hostSettingsRadio from './host-settings-radio';
|
|
||||||
*
|
|
||||||
* export default function ConsentProblems({
|
|
||||||
* settings,
|
|
||||||
* host_id,
|
|
||||||
* setConfig,
|
|
||||||
* pronoun,
|
|
||||||
* }: {
|
|
||||||
* host_id: string;
|
|
||||||
* setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
|
||||||
* settings: EmailTemplate3Config['hosts_settings'][string];
|
|
||||||
* pronoun: 0 | 1 | 2 | 3;
|
|
||||||
* }) {
|
|
||||||
* if (settings.legal_basis_type !== 'consent') {
|
|
||||||
* return '';
|
|
||||||
* }
|
|
||||||
* const p = pronoun;
|
|
||||||
* return (
|
|
||||||
* <div>
|
|
||||||
* {hostSettingsRadio({
|
|
||||||
* host_id,
|
|
||||||
* setConfig,
|
|
||||||
* field: 'consent_problems' as const,
|
|
||||||
* value: settings.consent_problems,
|
|
||||||
* options: {
|
|
||||||
* claims_consent_but_sends_before_consent: `Strona wysłała
|
|
||||||
* ${p == 3 ? 'nasze' : 'moje'} dane do ${host_id} zanim
|
|
||||||
* ${['wyraziłem', 'wyraziłam', 'wyraziłom', 'wyraziliśmy'][p]} na to zgodę.`,
|
|
||||||
* claims_consent_but_there_was_no_easy_refuse: `${
|
|
||||||
* ['Kliknąłem', 'Kliknęłam', 'Kliknęłom', 'Kliknęliśmy'][p]
|
|
||||||
* }
|
|
||||||
* przycisk od wyrażania zgody, ale w okienku o zgodę nie było natychmiastowo
|
|
||||||
* dostępnego przycisku do niewyrażenia zgody jednym kliknięciem.`,
|
|
||||||
* none: 'Żadne z powyższych.',
|
|
||||||
* },
|
|
||||||
* })}
|
|
||||||
* </div>
|
|
||||||
* );
|
|
||||||
* } */
|
|
|
@ -1,69 +0,0 @@
|
||||||
import { Classifications, StolenDataEntry } from '../stolen-data-entry';
|
|
||||||
|
|
||||||
export function DataPreview({
|
|
||||||
entries,
|
|
||||||
refresh,
|
|
||||||
}: {
|
|
||||||
entries: StolenDataEntry[];
|
|
||||||
refresh: () => void;
|
|
||||||
}) {
|
|
||||||
// currently not used, maybe scraped entirely in the future
|
|
||||||
return (
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Adres docelowy</th>
|
|
||||||
<th>Źródło danych</th>
|
|
||||||
<th>Treść danych</th>
|
|
||||||
<th>Klasyfikacja</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{entries.map((entry) => (
|
|
||||||
<tr
|
|
||||||
key={entry.id}
|
|
||||||
style={{
|
|
||||||
backgroundColor: entry.classification == 'id' ? 'yellow' : 'white',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<td>{entry.request.shorthost}</td>
|
|
||||||
<td style={{ overflowWrap: 'anywhere' }}>
|
|
||||||
{entry.source}:{entry.name}
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
style={{
|
|
||||||
width: '400px',
|
|
||||||
overflowWrap: 'anywhere',
|
|
||||||
backgroundColor: entry.isRelatedToID() ? '#ffff0054' : 'white',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{entry.getValuePreview()}
|
|
||||||
{/* always gonna have
|
|
||||||
one key, because unwrapEntry is called above */}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select
|
|
||||||
value={entry.classification}
|
|
||||||
onChange={(e) => {
|
|
||||||
entry.classification = e.target
|
|
||||||
.value as keyof typeof Classifications;
|
|
||||||
refresh();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{[
|
|
||||||
['history', 'Historia przeglądania'],
|
|
||||||
['id', 'Identyfikator internetowy'],
|
|
||||||
['location', 'Lokalizacja'],
|
|
||||||
].map(([key, name]) => (
|
|
||||||
<option key={key} value={key}>
|
|
||||||
{name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { RequestCluster } from '../request-cluster';
|
|
||||||
import { Classifications, Sources } from '../stolen-data-entry';
|
|
||||||
|
|
||||||
const emailClassifications: Record<keyof typeof Classifications, string> = {
|
|
||||||
id: 'mój identyfikator internetowy',
|
|
||||||
history: 'część mojej historii przeglądania',
|
|
||||||
location: 'informacje na temat mojej lokalizacji geograficznej',
|
|
||||||
};
|
|
||||||
|
|
||||||
const emailSources: Record<Sources, string> = {
|
|
||||||
header: 'w nagłówku HTTP',
|
|
||||||
cookie: 'z pliku Cookie',
|
|
||||||
pathname: 'jako części adresu URL',
|
|
||||||
queryparams: 'jako część adresu URL (query-params)',
|
|
||||||
request_body: 'w body zapytania POST',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DomainSummary({
|
|
||||||
cluster,
|
|
||||||
}: {
|
|
||||||
cluster: RequestCluster;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<li>
|
|
||||||
Właścicielowi domeny <strong>{cluster.id}</strong> zostały
|
|
||||||
ujawnione:{' '}
|
|
||||||
<ul>
|
|
||||||
{cluster.representativeStolenData
|
|
||||||
.filter((entry) => entry.isMarked)
|
|
||||||
.map((entry) => (
|
|
||||||
<li key={entry.id}>
|
|
||||||
{emailClassifications[entry.classification]}{' '}
|
|
||||||
{emailSources[entry.source]} (nazwa:{' '}
|
|
||||||
<code>{entry.name}</code>, wartość:{' '}
|
|
||||||
<code>{entry.getValuePreview()}</code>)
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
10
report-window/email-content.tsx
Normal file
10
report-window/email-content.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { ParsedAnswers } from './parse-answers';
|
||||||
|
|
||||||
|
export default function EmailContent({ answers }: { answers: ParsedAnswers }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Email template</h1>
|
||||||
|
<pre>{JSON.stringify(answers, null, 3)}</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
@import '../sidebar/fonts.scss';
|
|
||||||
@import '../sidebar/colors.scss';
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { EmailTemplate3Config } from './email-template-3';
|
|
||||||
/* import hostSettingsDropdown from './host-setting-dropdown'; */
|
|
||||||
/* import ConsentProblems from './consent-problems'; */
|
|
||||||
import LegitimateInteresProblems from './legitimate-interest-problems';
|
|
||||||
/* import { hostNeedsQuestions } from './host-needs-questions'; */
|
|
||||||
|
|
||||||
export function setHostSetting<
|
|
||||||
P1 extends keyof EmailTemplate3Config['hosts_settings'],
|
|
||||||
P2 extends keyof EmailTemplate3Config['hosts_settings'][P1]
|
|
||||||
>(
|
|
||||||
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>,
|
|
||||||
[p1, p2]: [P1, P2],
|
|
||||||
value: EmailTemplate3Config['hosts_settings'][P1][P2]
|
|
||||||
) {
|
|
||||||
setConfig((v) => {
|
|
||||||
console.log(v, {
|
|
||||||
...v,
|
|
||||||
hosts_settings: {
|
|
||||||
...v.hosts_settings,
|
|
||||||
[p1]: {
|
|
||||||
...v.hosts_settings[p2],
|
|
||||||
[p2]: value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
...v,
|
|
||||||
hosts_settings: {
|
|
||||||
...v.hosts_settings,
|
|
||||||
[p1]: {
|
|
||||||
...v.hosts_settings[p1],
|
|
||||||
[p2]: value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function emailHostSettings(
|
|
||||||
config: EmailTemplate3Config,
|
|
||||||
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>
|
|
||||||
) {
|
|
||||||
if (config.policy_readable == 'null') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const p = config.pronoun;
|
|
||||||
return (
|
|
||||||
<aside>
|
|
||||||
{Object.entries(config.hosts_settings).map(([host_id, settings]) => (
|
|
||||||
<div key={host_id}>
|
|
||||||
{!['not_mentioned', 'null'].includes(settings.legal_basis_type) ? (
|
|
||||||
<div>
|
|
||||||
{ConsentProblems({ settings, host_id, pronoun: p, setConfig })}
|
|
||||||
{LegitimateInteresProblems({
|
|
||||||
settings,
|
|
||||||
host_id,
|
|
||||||
setConfig,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,486 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { RequestCluster } from '../request-cluster';
|
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
|
||||||
import { getDate, unique } from '../util';
|
|
||||||
import DomainSummary from './domain-summary';
|
|
||||||
import EmailTemplate2Controls from './email-template-2-controls';
|
|
||||||
|
|
||||||
export type EmailTemplate2Config = {
|
|
||||||
popup_type: 'none' | 'passive_cookie_banner' | 'consent';
|
|
||||||
popup_action: 'ignored' | 'accepted' | 'closed';
|
|
||||||
popup_closed_how: string;
|
|
||||||
popup_screenshot_base64: string | null;
|
|
||||||
popup_accept_all_text: string;
|
|
||||||
popup_mentions_passive_consent: boolean;
|
|
||||||
popup_passive_consent_text: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function ClusterRangeSummary({ cluster }: { cluster: RequestCluster }) {
|
|
||||||
const range = unique(
|
|
||||||
cluster.getMarkedEntries().map((entry) => entry.classification)
|
|
||||||
);
|
|
||||||
const has_cookie_ids = cluster
|
|
||||||
.getMarkedEntries()
|
|
||||||
.filter((entry) => entry.source === 'cookie')
|
|
||||||
.some((entry) => entry.classification == 'id');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{[
|
|
||||||
range.includes('id')
|
|
||||||
? 'Pańskiego identyfikatora internetowego' +
|
|
||||||
(has_cookie_ids ? ' z cookie' : '')
|
|
||||||
: '',
|
|
||||||
range.includes('history')
|
|
||||||
? 'części Pańskiej historii przeglądania'
|
|
||||||
: '',
|
|
||||||
range.includes('location')
|
|
||||||
? 'informacji na temat Pana położenia'
|
|
||||||
: '',
|
|
||||||
]
|
|
||||||
.filter((e) => e !== '')
|
|
||||||
.join(', ')}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Placeholder({ children }: { children: string }) {
|
|
||||||
const invisbleCharacter = ' ';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
textDecoration: 'underline',
|
|
||||||
fontSize: '0.8em',
|
|
||||||
position: 'relative',
|
|
||||||
textUnderlineOffset: '4px',
|
|
||||||
bottom: '3px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{invisbleCharacter.repeat(6)}</span>
|
|
||||||
<span style={{ color: 'gray' }}>({children})</span>
|
|
||||||
<span>{invisbleCharacter.repeat(6)}</span>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Base64Image({ base64 }: { base64: string }) {
|
|
||||||
return <img style={{ maxWidth: '100%' }} {...{ src: base64 }} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function EmailTemplate2({
|
|
||||||
entries,
|
|
||||||
clusters,
|
|
||||||
}: {
|
|
||||||
entries: StolenDataEntry[];
|
|
||||||
clusters: Record<string, RequestCluster>;
|
|
||||||
version: number;
|
|
||||||
}): JSX.Element {
|
|
||||||
const [config, setConfig] = React.useState<EmailTemplate2Config>({
|
|
||||||
popup_type: 'none',
|
|
||||||
popup_action: 'ignored',
|
|
||||||
popup_screenshot_base64: null,
|
|
||||||
popup_accept_all_text: 'Zaakceptuj wszystkie',
|
|
||||||
popup_mentions_passive_consent: false,
|
|
||||||
popup_passive_consent_text: '',
|
|
||||||
popup_closed_how: 'kliknięcie przycisku „X”',
|
|
||||||
});
|
|
||||||
|
|
||||||
const visited_url = entries[0].request.originalURL;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<EmailTemplate2Controls {...{ config, setConfig }} />
|
|
||||||
<article
|
|
||||||
style={{
|
|
||||||
boxShadow: '0 20px 40px rgba(0,0,0,.2)',
|
|
||||||
padding: '4rem 3rem',
|
|
||||||
borderRadius: '0.25rem',
|
|
||||||
margin: '2rem 0',
|
|
||||||
color: 'hsl(240, 5.7%, 15.8%);',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Dzień dobry, w dniu {getDate()} odwiedziłem stronę{' '}
|
|
||||||
{visited_url}.
|
|
||||||
</p>
|
|
||||||
{config.popup_type === 'none' ? (
|
|
||||||
<p>
|
|
||||||
Nie ukazał mi się na stronie żaden mechanizm pozyskujący
|
|
||||||
zgodę na przetwarzanie moich danych osobowych lub
|
|
||||||
umożliwiający mi wyrażenie sprzeciwu wobec przetwarzania
|
|
||||||
przez stronę moich danych osobowych w zakresie
|
|
||||||
wykraczającym poza procesy konieczne do wyświetlenia
|
|
||||||
strony
|
|
||||||
</p>
|
|
||||||
) : config.popup_type == 'passive_cookie_banner' ? (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
Na stronie była widoczna informacja o plikach
|
|
||||||
Cookie.{' '}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<Base64Image
|
|
||||||
{...{ base64: config.popup_screenshot_base64 }}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
Ukazało mi się okienko z informacjami i pytaniami
|
|
||||||
dotyczącymi sposobów, w jaki strona przetwarza moje
|
|
||||||
dane osobowe.{' '}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<Base64Image
|
|
||||||
{...{ base64: config.popup_screenshot_base64 }}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{config.popup_action === 'ignored'
|
|
||||||
? /* HTML */ `Nie kliknąłem żadnego przycisku w
|
|
||||||
tym okienku. W szczególności nie kliknąłem
|
|
||||||
przycisku „${config.popup_accept_all_text}”.`
|
|
||||||
: config.popup_action === 'accepted'
|
|
||||||
? `Kliknąłem na widoczną w tym okienku opcję „${config.popup_accept_all_text}”.`
|
|
||||||
: ''}
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
W tym samym czasie rejestrowałem ruch sieciowy generowany
|
|
||||||
przez tę stronę za pomocą narzędzi w przeglądarce Firefox.
|
|
||||||
Okazało się, że Państwa strona wysłała moje dane osobowe do
|
|
||||||
następujących podmiotów:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
{Object.values(clusters)
|
|
||||||
.filter((cluster) => cluster.hasMarks())
|
|
||||||
.map((cluster) => (
|
|
||||||
<DomainSummary cluster={cluster} key={cluster.id} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
{config.popup_action === 'ignored' ? (
|
|
||||||
<p>
|
|
||||||
Dane te zostały wysłane, zanim kliknąłem cokolwiek na
|
|
||||||
tej stronie.
|
|
||||||
</p>
|
|
||||||
) : config.popup_action === 'accepted' ? (
|
|
||||||
<p>
|
|
||||||
Dane te zostały wysłane po tym, jak kliknąłem przycisk „
|
|
||||||
{config.popup_accept_all_text}”
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
W załączeniu przesyłam część zrzutów ekranu dokumentujących
|
|
||||||
fakt wysłania tych danych przez Państwa stronę.{' '}
|
|
||||||
</p>
|
|
||||||
<h3>Podstawa prawna</h3>
|
|
||||||
<p>
|
|
||||||
Ustawa Prawo Telekomunikacyjne w art. 173 reguluje warunki,
|
|
||||||
które musi spełnić administrator strony, aby jego strona
|
|
||||||
mogła zapisywać i czytać treść plików cookie. Nie reguluje
|
|
||||||
jednak tego, jakim podmiotom i w jakim zakresie dane mogą
|
|
||||||
być <em>ujawniane</em> przez stronę. Tym zajmuje się
|
|
||||||
Rozporządzenie 2016/679 Parlamentu Europejskiego i Rady (UE)
|
|
||||||
z dnia 27 kwietnia 2016 r. w sprawie ochrony osób fizycznych
|
|
||||||
w związku z przetwarzaniem danych osobowych i w sprawie
|
|
||||||
swobodnego przepływu takich danych oraz uchylenia dyrektywy
|
|
||||||
95/46/WE (ogólne rozporządzenie o ochronie danych) – RODO.
|
|
||||||
Zapis/odczyt plików cookie a ujawnianie ich treści podmiotom
|
|
||||||
trzecim to dwa różne procesy. Niniejsza wiadomość i pytania
|
|
||||||
w niej zawarte dotyczą właśnie <em>ujawniania</em> moich
|
|
||||||
danych osobowych (pochodzących m.in. z Cookies) podmiotom
|
|
||||||
trzecim.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
W kontekście stron internetowych są właściwie dopuszczalne
|
|
||||||
tylko trzy z sześciu wymienionych w Art. 6 pkt 1 RODO
|
|
||||||
podstaw prawnych dla przetwarzania danych osobowych:
|
|
||||||
</p>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
„Zgoda” — osoba, której dane dotyczą wyraziła
|
|
||||||
zgodę na przetwarzanie swoich danych osobowych w jednym
|
|
||||||
lub większej liczbie określonych celów (
|
|
||||||
<em>Art. 6 pkt 1 lit. a)</em>).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
„Niezbędność” — przetwarzanie jest niezbędne do
|
|
||||||
wykonania umowy, której stroną jest osoba, której dane
|
|
||||||
dotyczą, lub do podjęcia działań na żądanie osoby,
|
|
||||||
której dane dotyczą, przed zawarciem umowy (
|
|
||||||
<em>Art. 6 pkt 1 lit. b)</em>);{' '}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
„Uzasadniony Interes” — przetwarzanie jest
|
|
||||||
niezbędne do celów wynikających z prawnie uzasadnionych
|
|
||||||
interesów realizowanych przez administratora lub przez
|
|
||||||
stronę trzecią, z wyjątkiem sytuacji, w których
|
|
||||||
nadrzędny charakter wobec tych interesów mają interesy
|
|
||||||
lub podstawowe prawa i wolności osoby, której dane
|
|
||||||
dotyczą, wymagające ochrony danych osobowych, w
|
|
||||||
szczególności gdy osoba, której dane dotyczą, jest
|
|
||||||
dzieckiem (<em>Art. 6 pkt 1 lit. f)</em>
|
|
||||||
);
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<p>
|
|
||||||
W przypadku opisywanej przeze mnie mojej wizyty na Państwa
|
|
||||||
stronie nie ma zastosowania „Zgoda”, gdyż{' '}
|
|
||||||
{config.popup_action === 'ignored' ? (
|
|
||||||
<>
|
|
||||||
nie wyrażałem żadnej zgody na takie przetwarzanie
|
|
||||||
moich danych
|
|
||||||
{config.popup_type === 'consent' ? (
|
|
||||||
<>
|
|
||||||
— w szczególności nie kliknąłem
|
|
||||||
przycisku „{config.popup_accept_all_text}”
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
.
|
|
||||||
</>
|
|
||||||
) : config.popup_action === 'accepted' ? (
|
|
||||||
<>
|
|
||||||
o ile po wejściu na stronę wcisnąłem w wyskakującym
|
|
||||||
okienku przycisk „{config.popup_accept_all_text}”, o
|
|
||||||
tyle nie stanowi to według mnie ważnej w świetle
|
|
||||||
RODO zgody, gdyż brakowało w tym okienku równie
|
|
||||||
łatwo osiągalnego przycisku, którego kliknięcie
|
|
||||||
skutkowałoby zasygnalizowaniem braku mojej zgody na
|
|
||||||
takie przetwarzanie moich danych. Mówiąc wprost
|
|
||||||
— wyrażenie „zgody” było łatwiejsze niż jej
|
|
||||||
niewyrażenie. Niewyrażenie zgody wiąże się z
|
|
||||||
negatywną konsekwencją konieczności przechodzenia
|
|
||||||
przez dodatkowe kroki w wyskakującym okienku. Zatem
|
|
||||||
tak otrzymana przez Państwo moja „zgoda” nie jest
|
|
||||||
poprawną podstawą prawną do przetwarzania moich
|
|
||||||
danych osobowych, gdyż nie spełnia warunku
|
|
||||||
dobrowolności wspomnianego w motywie (42) RODO.
|
|
||||||
</>
|
|
||||||
) : config.popup_action === 'closed' ? (
|
|
||||||
<>
|
|
||||||
zamknąłem okienko pytające o zgodę poprzez{' '}
|
|
||||||
{config.popup_closed_how}. Nie może być to uznane za
|
|
||||||
zgodę, bo nie spełnia to warunku jednoznaczności
|
|
||||||
opisanego w motywie (32) Rozporządzenia 2016/679.{' '}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}{' '}
|
|
||||||
Za zgodę nie można też uznać posiadania włączonej obsługi
|
|
||||||
cookies w przeglądarce (gdyż aby zgoda była ważna, musi być
|
|
||||||
szczegółowa dla każdego celów z osobna), jakichkolwiek
|
|
||||||
innych ustawień przeglądarki, ani pasywnych działań z mojej
|
|
||||||
strony (np. „kontynuowanie korzystania ze strony”)
|
|
||||||
{config.popup_mentions_passive_consent ? (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
— nieprawdą więc jest zawarty na Państwa
|
|
||||||
stronie komunikat „
|
|
||||||
{config.popup_passive_consent_text.trim()}” (por.
|
|
||||||
paragraf 97.{' '}
|
|
||||||
<a href="https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_pl.pdf">
|
|
||||||
oficjalnych wytycznych EROD dotyczących zgody na
|
|
||||||
mocy rozporządzenia 2016/679
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
W mojej ocenie „Niezbędność“ nie ma zastosowania co do
|
|
||||||
opisanych powyżej sposobów przetwarzania danych. Nie widzę,
|
|
||||||
co miałoby sprawiać, aby wysyłanie moich danych osobowych do
|
|
||||||
wspomnianych powyżej podmiotów trzecich było konieczne do
|
|
||||||
wyświetlenia Państwa strony na ekranie mojego komputera
|
|
||||||
(zob.{' '}
|
|
||||||
<a href="https://edpb.europa.eu/system/files/2021-11/edpb_guidelines_082020_on_the_targeting_of_social_media_users_pl_0.pdf">
|
|
||||||
Wytyczne 8/2020 EROD dotyczące targetowania użytkowników
|
|
||||||
mediów społecznościowych
|
|
||||||
</a>
|
|
||||||
, par. 49);.{' '}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Pozostaje zatem „Uzasadniony Interes”. Aby Administrator
|
|
||||||
mógł używać uzasadnionego interesu jako podstawy prawnej
|
|
||||||
targetowania użytkowników Sieci, muszą zostać spełnione
|
|
||||||
m.in. następujące warunki:{' '}
|
|
||||||
</p>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
Administrator danych lub podmiot trzeci, któremu dane są
|
|
||||||
ujawniane musi{' '}
|
|
||||||
<strong>
|
|
||||||
faktycznie realizować dany konkretny uzasadniony
|
|
||||||
interes
|
|
||||||
</strong>{' '}
|
|
||||||
(
|
|
||||||
<a href="https://curia.europa.eu/juris/document/document.jsf?text=&docid=216555&pageIndex=0&doclang=PL&mode=lst&dir=&occ=first&part=1&cid=1254905">
|
|
||||||
Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion
|
|
||||||
ID, C-40/17, ECLI:EU:C:2019:629
|
|
||||||
</a>
|
|
||||||
, pkt 95.)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Takie przetwarzanie danych jest{' '}
|
|
||||||
<strong>konieczne</strong> dla potrzeb wynikających z
|
|
||||||
danego uzasadnionego interesu (
|
|
||||||
<a href="https://curia.europa.eu/juris/document/document.jsf?text=&docid=216555&pageIndex=0&doclang=PL&mode=lst&dir=&occ=first&part=1&cid=1254905">
|
|
||||||
Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion
|
|
||||||
ID, C-40/17, ECLI:EU:C:2019:629
|
|
||||||
</a>
|
|
||||||
, pkt 95.)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Wybrany uzasadniony interes musi mieć pierwszeństwo nad
|
|
||||||
prawami i wolnościami osoby, której dotyczą przetwarzane
|
|
||||||
dane (
|
|
||||||
<a href="https://curia.europa.eu/juris/document/document.jsf?text=&docid=216555&pageIndex=0&doclang=PL&mode=lst&dir=&occ=first&part=1&cid=1254905">
|
|
||||||
Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion
|
|
||||||
ID, C-40/17, ECLI:EU:C:2019:629
|
|
||||||
</a>
|
|
||||||
, pkt 95.)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Osoby, których dane dotyczą, powinny mieć możliwość
|
|
||||||
wyrażenia sprzeciwu wobec przetwarzania ich danych do
|
|
||||||
celów związanych z targetowaniem{' '}
|
|
||||||
<strong>przed rozpoczęciem przetwarzania</strong> (zob.{' '}
|
|
||||||
<a href="https://edpb.europa.eu/system/files/2021-11/edpb_guidelines_082020_on_the_targeting_of_social_media_users_pl_0.pdf">
|
|
||||||
Wytyczne 8/2020 EROD dotyczące targetowania
|
|
||||||
użytkowników mediów społecznościowych
|
|
||||||
</a>
|
|
||||||
, par. 54);
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
{config.popup_action !== 'accepted' ? (
|
|
||||||
<p>
|
|
||||||
Moje dane zostały ujawnione podmiotom trzecim tuż po
|
|
||||||
włączeniu strony, zatem nie jest spełniony warunek 4.
|
|
||||||
Apeluję o wdrożenie zmian na stronie, które sprawią, że
|
|
||||||
dopiero po świadomym niewyrażeniu sprzeciwu przez
|
|
||||||
użytkownika aktywowane są procesy przetwarzania danych
|
|
||||||
osobowych, których podstawą prawną jest uzasadniony
|
|
||||||
interes.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
Jeżeli istnieją jednak inne niż uzasadniony interes ważne
|
|
||||||
podstawy prawne do takiego przetwarzania moich danych
|
|
||||||
osobowych przez Państwa stronę, proszę o ich wskazanie,{' '}
|
|
||||||
<em>dla każdego z wymienionych podmiotów z osobna</em>.
|
|
||||||
(Przypominam, że Art. 173 ustawy Prawo Telekomunikacyjne nie
|
|
||||||
ma tutaj zastosowania, ponieważ nie pytam o zapis/odczyt
|
|
||||||
plików na moim komputerze, tylko o ujawnianie moich danych
|
|
||||||
osobowych podmiotom trzecim). W przeciwnym wypadku, aby
|
|
||||||
ustalić, czy moje dane były przez Państwa przetwarzane na
|
|
||||||
mocy uzasadnionego interesu zgodnie z prawem, proszę o
|
|
||||||
wypełnienie następującego szablonu (lub udzielenie tych
|
|
||||||
samych informacji w innej postaci, przy zachowaniu zakresu i
|
|
||||||
szczegółowości informacji:
|
|
||||||
</p>
|
|
||||||
<div style={{ border: '1px solid black', padding: '1rem' }}>
|
|
||||||
<p>
|
|
||||||
W dniu {getDate()} strona {visited_url}:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
{Object.values(clusters)
|
|
||||||
.filter((cluster) => cluster.hasMarks())
|
|
||||||
.map((cluster) => (
|
|
||||||
<li
|
|
||||||
key={cluster.id}
|
|
||||||
style={{ paddingBottom: '1rem' }}
|
|
||||||
>
|
|
||||||
ujawniła pańskie dane w zakresie{' '}
|
|
||||||
<em>
|
|
||||||
<ClusterRangeSummary {...{ cluster }} />
|
|
||||||
</em>{' '}
|
|
||||||
firmie{' '}
|
|
||||||
<Placeholder>nazwa firmy</Placeholder>,
|
|
||||||
która jest właścicielem domeny{' '}
|
|
||||||
<strong>{cluster.id}</strong> i swoją
|
|
||||||
politykę prywatności publikuje pod adresem{' '}
|
|
||||||
<Placeholder>
|
|
||||||
adres URL polityki prywatności tej firmy
|
|
||||||
</Placeholder>
|
|
||||||
. Podstawą prawną takiego przetwarzania
|
|
||||||
danych przez naszą stronę jest uzasadniony
|
|
||||||
interes:{' '}
|
|
||||||
<Placeholder>
|
|
||||||
na czym polega ten uzasadniony interes,
|
|
||||||
tzn. bieżące działania podejmowane przez
|
|
||||||
podmiot realizujący ten interes lub
|
|
||||||
korzyści dla podmiotu realizującego ten
|
|
||||||
interes oczekiwane w bardzo bliskiej
|
|
||||||
przyszłości
|
|
||||||
</Placeholder>{' '}
|
|
||||||
realizowany przez{' '}
|
|
||||||
<Placeholder>
|
|
||||||
kogo? jaki podmiot podejmuje wspomniane
|
|
||||||
działania lub jest beneficjentem
|
|
||||||
wspomnianych korzyści?
|
|
||||||
</Placeholder>
|
|
||||||
. Ujawnienie{' '}
|
|
||||||
<ClusterRangeSummary {...{ cluster }} />{' '}
|
|
||||||
temu podmiotowi przez naszą stronę było
|
|
||||||
konieczne dla potrzeb wynikających z tego
|
|
||||||
interesu, ponieważ
|
|
||||||
<Placeholder>
|
|
||||||
uzasadnienie konieczności
|
|
||||||
</Placeholder>
|
|
||||||
.<br />
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Proszę w szczególności zwrócić uwagę na podanie adresów do
|
|
||||||
polityk prywatności tych firm, abym wiedział, jak
|
|
||||||
skontaktować się z nimi i wnioskować o usunięcie z ich baz
|
|
||||||
wysłanych przez Państwa stronę moich danych. )
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
W odpowiedzi proszę się nie powoływać na IAB Europe i ich
|
|
||||||
rzekomą renomę w tworzeniu rozwiązań zgodnych z RODO. IAB
|
|
||||||
chroni interes reklamodawców, a nie Użytkowników, i ich
|
|
||||||
rozwiązania (np. TCF) są{' '}
|
|
||||||
<a href="https://panoptykon.org/search/site/IAB">
|
|
||||||
notorycznie niezgodne z RODO i pozbawione szacunku dla
|
|
||||||
Użytkowników
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Apeluję także o wprowadzenie stosownych zmian na stronie
|
|
||||||
tak, aby nie pozostawiać cienia wątpliwości odnośnie tego,
|
|
||||||
na mocy jakiej przesłanki legalizującej dane są przetwarzane
|
|
||||||
przez wspomniane podmioty trzecie, lub tak, aby te dane po
|
|
||||||
prostu nie były wysyłane. Pomoże to zachować prywatność
|
|
||||||
innym użytkownikom Państwa strony. Polecam Państwa uwadze
|
|
||||||
<a href="https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_pl.pdf">
|
|
||||||
{' '}
|
|
||||||
oficjalne wytyczne EROD dotyczące zgody w kontekście
|
|
||||||
RODO
|
|
||||||
</a>
|
|
||||||
. Aby na przykład zapobiec automatycznemu wysyłaniu historii
|
|
||||||
przeglądania do podmiotów trzecich przez Państwa stronę,
|
|
||||||
można po prostu ustawić odpowiednio treść nagłówka{' '}
|
|
||||||
<a href="https://developer.mozilla.org/pl/docs/Web/HTTP/Headers/Referrer-Policy">
|
|
||||||
Referrer-Policy{' '}
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,407 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import * as Survey from 'survey-react';
|
|
||||||
import { toBase64 } from '../util';
|
|
||||||
import emailHostSettings from './email-host-settings';
|
|
||||||
import { EmailTemplate3Config } from './email-template-3';
|
|
||||||
import verbs from './verbs';
|
|
||||||
|
|
||||||
function generateHostPage(
|
|
||||||
host: string,
|
|
||||||
index: number,
|
|
||||||
all_hosts: string[]
|
|
||||||
): { title: string; elements: any[] } {
|
|
||||||
function f(name: string, h = host) {
|
|
||||||
return `${h.replace(/\./g, '_')}|${name}`;
|
|
||||||
}
|
|
||||||
const previous_host: string | null = index > 0 ? all_hosts[index - 1] : null;
|
|
||||||
function defaultValue(name: string) {
|
|
||||||
if (!previous_host) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return { defaultValueExpression: `{${f(name, previous_host)}}` };
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: host,
|
|
||||||
elements: [
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
name: f('present'),
|
|
||||||
isRequired: true,
|
|
||||||
title: `Cel ujawnienia danych właścicielowi domeny ${host}`,
|
|
||||||
...defaultValue('present'),
|
|
||||||
choices: [
|
|
||||||
{ value: 'not_mentioned', text: 'nie jest podany nigdzie na stronie' },
|
|
||||||
{
|
|
||||||
value: 'mentioned_in_policy',
|
|
||||||
text: 'jest podany w polityce prywatności',
|
|
||||||
visibleIf: "{policy_readable} = 'yes' ",
|
|
||||||
},
|
|
||||||
|
|
||||||
{ value: 'mentioned_in_popup', text: 'jest podany w okienku RODO' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
name: f('legal_basis_type'),
|
|
||||||
...defaultValue('legal_basis_type'),
|
|
||||||
isRequired: true,
|
|
||||||
title: `Podstawa prawna dla tego konkretnego celu`,
|
|
||||||
visibleIf: `{${f('present')}} notempty and {${f('present')}} != "not_mentioned"`,
|
|
||||||
choices: [
|
|
||||||
{ value: 'consent', text: 'to zgoda.' },
|
|
||||||
{
|
|
||||||
value: 'legitimate_interest',
|
|
||||||
text: 'to uzasadniony interes.',
|
|
||||||
},
|
|
||||||
{ value: 'not_mentioned', text: 'nie jest wskazana nigdzie na stronie.' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'radiogroup',
|
|
||||||
name: f('consent_problems'),
|
|
||||||
...defaultValue('consent_problems'),
|
|
||||||
isRequired: true,
|
|
||||||
title: `Jak ma się ta podstawa prawna do stanu faktycznego?`,
|
|
||||||
visibleIf: `{${f('legal_basis_type')}} = "consent"`,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
value: 'claims_consent_but_sends_before_consent',
|
|
||||||
text: `Strona wysłała {moje} dane do ${host} zanim {wyraziłem} na to zgodę`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'claims_consent_but_there_was_no_easy_refuse',
|
|
||||||
text: '{Kliknąłem} przycisk od wyrażania zgody, ale w okienku o zgodę nie było natychmiastowo dostępnego przycisku do niewyrażenia zgody jednym kliknięciem',
|
|
||||||
},
|
|
||||||
{ value: 'none', text: 'żadne z powyższych.' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
name: f('legitimate_interest_activity_specified'),
|
|
||||||
...defaultValue('legitimate_interest_activity_specified'),
|
|
||||||
isRequired: true,
|
|
||||||
title: /* HTML */ `Czy administrator strony opisał szczegółowo, na czym polega
|
|
||||||
uzasadniony interes w kontekście tego celu?`,
|
|
||||||
visibleIf: `{${f('legal_basis_type')}} = "legitimate_interest"`,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
value: 'precise',
|
|
||||||
text: /* HTML */ `Tak, wskazuje jasno na bieżące działania lub korzyści
|
|
||||||
wynikające z takiego przetwarzania danych.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'vague',
|
|
||||||
text: `Wskazuje tylko ogólnie, jak np. „marketing” czy „statystyki”.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'no',
|
|
||||||
text: `Nie. Nie wiadomo, na czym ten uzasadniony interes polega.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
title: `Jak administrator opisał to, na czym polega uzasadniony interes w kontekście ${host}?`,
|
|
||||||
name: f('legitimate_interest_description'),
|
|
||||||
visibleIf: `{${f('legitimate_interest_activity_specified')}} = 'vague'`,
|
|
||||||
defaultValueExpression:
|
|
||||||
index == 0
|
|
||||||
? 'marketing'
|
|
||||||
: `{${f('legitimate_interest_description', previous_host)}}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
title: `Czy domena ${host} należy do podmiotu spoza Europy (np. Google, Facebook)?`,
|
|
||||||
name: f('outside_eu'),
|
|
||||||
...defaultValue('outside_eu'),
|
|
||||||
visibleIf: `{${f('legitimate_interest_activity_specified')}} = "precise" or {${f(
|
|
||||||
'consent_problems'
|
|
||||||
)}} = "none"`,
|
|
||||||
choices: [
|
|
||||||
{ value: 'yes', text: 'Tak' },
|
|
||||||
{ value: 'no', text: 'Nie' },
|
|
||||||
{ value: 'not_sure', text: 'Nie wiem' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function EmailTemplate3Controls({ hosts }: { hosts: string[] }) {
|
|
||||||
const [survey, setSurvey] = React.useState<Survey.Model>(null);
|
|
||||||
React.useEffect(() => {
|
|
||||||
var json = {
|
|
||||||
showQuestionNumbers: 'off',
|
|
||||||
showProgressBar: 'top',
|
|
||||||
pages: [
|
|
||||||
{
|
|
||||||
title: 'Zaimki',
|
|
||||||
elements: [
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
name: 'zaimek',
|
|
||||||
title: 'Forma czasownika:',
|
|
||||||
isRequired: true,
|
|
||||||
choices: [
|
|
||||||
{ value: 0, text: 'Wysłałem' },
|
|
||||||
{ value: 1, text: 'Wysłałam' },
|
|
||||||
{ value: 2, text: 'Wysłałom' },
|
|
||||||
{ value: 3, text: 'Wysłaliśmy' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Obowiązek informacyjny i machanizm pozyskiwania zgody',
|
|
||||||
elements: [
|
|
||||||
{
|
|
||||||
type: 'radiogroup',
|
|
||||||
title: 'Jaką formę informacji o przetwarzaniu danych osobowych stosuje ta strona?',
|
|
||||||
name: 'popup_type',
|
|
||||||
isRequired: true,
|
|
||||||
choices: [
|
|
||||||
{ value: 'none', text: 'Brak informacji' },
|
|
||||||
{
|
|
||||||
value: 'passive_popup',
|
|
||||||
text: /* HTML */ `Okienko o cookiesach, bez możliwości podjęcia
|
|
||||||
żadnego wyboru (np. tylko opcja „zamknij”)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'some_choice',
|
|
||||||
text: 'Okienko o cookiesach, z możliwością podjęcia wyboru',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'checkbox',
|
|
||||||
title: /* HTML */ `Istnieje możliwość, że okienko z informacjami i
|
|
||||||
wyborami dotyczącymi przetwarzania {Twoich} danych osobowych ukazało się
|
|
||||||
dawno temu w trakcie {twojej} wcześniejszej wizyty i wtedy je
|
|
||||||
{odkliknąłeś}. {Otwórz} tę samą stronę w Trybie Prywatnym (Incognito).
|
|
||||||
Co {widzisz}?`,
|
|
||||||
visibleIf: "{popup_type} = 'none'",
|
|
||||||
name: 'is_incognito_different',
|
|
||||||
isRequired: true,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
value: 'incognito_is_the_same',
|
|
||||||
text: 'W Trybie prywatnym {widzę} to samo, co {widziałem} w normalnym trybie',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'html',
|
|
||||||
visibleIf: '{is_incognito_different} != "no" and {popup_type} = "none"',
|
|
||||||
html: /* HTML */ `Jeżeli w trybie incognito widzisz więcej okienek z
|
|
||||||
informacjami o przetwarzaniu danych osobowych, wykonaj analizę w
|
|
||||||
normalnym trybie ponownie - ale najpierw usuń pliki cookies tej
|
|
||||||
strony.
|
|
||||||
<a
|
|
||||||
href="https://support.mozilla.org/pl/kb/usuwanie-ciasteczek-i-danych-stron-firefox?redirectslug=usuwanie-ciasteczek&redirectlocale=pl"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Zobacz, jak to zrobić
|
|
||||||
</a>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'radiogroup',
|
|
||||||
name: 'mentions_passive_consent',
|
|
||||||
isRequired: true,
|
|
||||||
visibleIf: '{popup_type} = "passive_popup"',
|
|
||||||
title: 'Czy treść okienka wskazuje na zgodę wyrażoną pasywnie, np. „Korzystając z naszej strony wyrażasz zgodę” lub „Brak zmiany ustawień przeglądarki oznacza zgodę”?',
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
value: 'yes',
|
|
||||||
text: 'Tak',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'no',
|
|
||||||
text: 'Nie',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Polityka prywatności',
|
|
||||||
elements: [
|
|
||||||
{
|
|
||||||
type: 'dropdown',
|
|
||||||
title: 'Czy polityka prywatności jest dostępna i czytelna?',
|
|
||||||
name: 'policy_readable',
|
|
||||||
isRequired: true,
|
|
||||||
choices: [
|
|
||||||
{ value: 'yes', text: 'dostępna i czytelna' },
|
|
||||||
{
|
|
||||||
value: 'entirely_obscured_by_popup',
|
|
||||||
text: 'dostępna, ale nieczytelna. Zasłania ją popup o RODO',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'cant_find',
|
|
||||||
text: `Niedostępna. {Szukałem}, ale nie {znalazłem} jej na stronie`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
...hosts.map(generateHostPage),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(json);
|
|
||||||
|
|
||||||
const survey = new Survey.Model(json);
|
|
||||||
survey.onProcessTextValue.add(function (
|
|
||||||
sender: Survey.SurveyModel,
|
|
||||||
options: { name: string; value?: string }
|
|
||||||
) {
|
|
||||||
if (verbs[options.name.toLowerCase()]) {
|
|
||||||
options.value = verbs[options.name.toLowerCase()][sender.valuesHash.zaimek];
|
|
||||||
if (options.name[0] == options.name[0].toUpperCase()) {
|
|
||||||
options.value = [
|
|
||||||
options.value[0].toUpperCase(),
|
|
||||||
...options.value.slice(1),
|
|
||||||
].join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setSurvey(survey);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!survey) {
|
|
||||||
return <div>Wczytywanie...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Survey.Survey model={survey} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function _EmailTemplate3Controls({
|
|
||||||
config,
|
|
||||||
setConfig,
|
|
||||||
}: {
|
|
||||||
config: EmailTemplate3Config;
|
|
||||||
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
|
||||||
}): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{emailHostSettings(config, setConfig)}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="poup_type">Typ okienka o RODO: </label>
|
|
||||||
<select
|
|
||||||
id="poup_type"
|
|
||||||
value={config.popup_type}
|
|
||||||
onChange={(e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_type: e.target.value as EmailTemplate3Config['popup_type'],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="none">Brak jakiejkolwiek informacji</option>
|
|
||||||
<option value="passive_cookie_banner">
|
|
||||||
Pasywne powiadomienie o cookiesach
|
|
||||||
</option>
|
|
||||||
<option value="consent">Okienko z pytaniem o zgodę</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{config.popup_type !== 'none' ? (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popup_screenshot">Zrzut ekranu okienka o RODO:</label>
|
|
||||||
<input
|
|
||||||
{...{
|
|
||||||
type: 'file',
|
|
||||||
id: 'popup_screenshot',
|
|
||||||
onChange: async (e) => {
|
|
||||||
const popup_screenshot_base64 = await toBase64(e.target.files[0]);
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_screenshot_base64,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popup_action">Czy coś klikn*ł*m w informacjach o RODO?</label>
|
|
||||||
<select
|
|
||||||
id="popup_action"
|
|
||||||
value={config.popup_type}
|
|
||||||
onChange={(e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_action: e.target.value as EmailTemplate3Config['popup_action'],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<option value="ignored">Nic nie klin*ł*m</option>
|
|
||||||
<option value="accepted">Kliknięte „{config.popup_accept_all_text}”</option>
|
|
||||||
<option value="closed">Zamkn*ł*m okienko (np. przyciskiem "X")</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{config.popup_action === 'closed' ? (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popup_closed_how">Jak okienko zostało zamknięte? Poprzez</label>
|
|
||||||
<input
|
|
||||||
id="popup_closed_how"
|
|
||||||
type="text"
|
|
||||||
placeholder="kliknięcie przycisku „X”"
|
|
||||||
value={config.popup_closed_how}
|
|
||||||
style={{ width: '300px' }}
|
|
||||||
onChange={(e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_closed_how: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
{config.popup_type !== 'none' ? (
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="popup_mentions_passive_consent"
|
|
||||||
checked={config.popup_mentions_passive_consent}
|
|
||||||
onChange={(e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_mentions_passive_consent: e.target.checked,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label htmlFor="popup_mentions_passive_consent">
|
|
||||||
okienko wspomina o pasywnej zgodzie (np. „korzystając ze strony wyrażasz
|
|
||||||
zgodę”)
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
{config.popup_mentions_passive_consent ? (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popup_passive_consent_text">
|
|
||||||
Jak okienko próbuje wmówić Ci, że wyrażasz zgodę? Przeklej z treści okienka:
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="popup_passive_consent_text"
|
|
||||||
placeholder="Korzystając ze strony wyrażasz zgodę"
|
|
||||||
value={config.popup_passive_consent_text}
|
|
||||||
onChange={(e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_passive_consent_text: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
@import '../sidebar/fonts.scss';
|
|
||||||
@import '../sidebar/colors.scss';
|
|
||||||
|
|
||||||
.mail-wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 0.75fr minmax(20rem, 60rem);
|
|
||||||
height: calc(100vh - 5rem);
|
|
||||||
background-color: $ultra-light-grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mail-content-wrapper {
|
|
||||||
background-color: $ultra-light-grey;
|
|
||||||
height: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mail-controls {
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mail-content {
|
|
||||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 2rem 1.75rem;
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { RequestCluster } from '../request-cluster';
|
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
|
||||||
import { getDate } from '../util';
|
|
||||||
import EmailTemplate3Controls from './email-template-3-controls';
|
|
||||||
import './email-template-3.scss';
|
|
||||||
|
|
||||||
declare var PLUGIN_NAME: string;
|
|
||||||
declare var PLUGIN_URL: string;
|
|
||||||
|
|
||||||
export type EmailTemplate3Config = {
|
|
||||||
pronoun: 0 | 1 | 2 | 3; // masc, fem, neutral, plural
|
|
||||||
policy_readable: 'null' | 'yes' | 'entirely_obscured_by_popup' | 'cant_find';
|
|
||||||
hosts_settings: Record<
|
|
||||||
string,
|
|
||||||
{
|
|
||||||
presence: 'null' | 'not_mentioned' | 'mentioned_in_popup' | 'mentioned_in_policy';
|
|
||||||
legal_basis_type: 'null' | 'consent' | 'legitimate_interest' | 'not_mentioned';
|
|
||||||
consent_problems?:
|
|
||||||
| 'claims_consent_but_sends_before_consent'
|
|
||||||
| 'claims_consent_but_there_was_no_easy_refuse'
|
|
||||||
| 'none'
|
|
||||||
| 'null';
|
|
||||||
|
|
||||||
legitimate_interest_activity_specified: 'null' | 'no' | 'precise' | 'vague';
|
|
||||||
legitimate_interest_activity_description: string;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
popup_type: 'none' | 'passive_cookie_banner' | 'consent';
|
|
||||||
popup_action: 'ignored' | 'accepted' | 'closed';
|
|
||||||
popup_closed_how: string;
|
|
||||||
popup_screenshot_base64: string | null;
|
|
||||||
popup_accept_all_text: string;
|
|
||||||
popup_mentions_passive_consent: boolean;
|
|
||||||
popup_passive_consent_text: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function EmailTemplate3({
|
|
||||||
entries,
|
|
||||||
clusters,
|
|
||||||
}: {
|
|
||||||
entries: StolenDataEntry[];
|
|
||||||
clusters: Record<string, RequestCluster>;
|
|
||||||
version: number;
|
|
||||||
}): JSX.Element {
|
|
||||||
const all_host_ids = Array.from(new Set(entries.map((entry) => entry.request.shorthost)));
|
|
||||||
|
|
||||||
const [config, setConfig] = React.useState<EmailTemplate3Config>({
|
|
||||||
pronoun: Math.round(Math.random()) as 0 | 1,
|
|
||||||
policy_readable: 'null',
|
|
||||||
hosts_settings: Object.fromEntries(
|
|
||||||
all_host_ids.map((cluster_id) => [
|
|
||||||
cluster_id,
|
|
||||||
{
|
|
||||||
presence: 'null',
|
|
||||||
legal_basis_presence: 'null',
|
|
||||||
legal_basis_type: 'null',
|
|
||||||
consent_problems: 'null',
|
|
||||||
legitimate_interest_activity_specified: 'null',
|
|
||||||
legitimate_interest_activity_description: '',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
),
|
|
||||||
popup_type: 'none',
|
|
||||||
popup_action: 'ignored',
|
|
||||||
popup_screenshot_base64: null,
|
|
||||||
popup_accept_all_text: 'Zaakceptuj wszystkie',
|
|
||||||
popup_mentions_passive_consent: false,
|
|
||||||
popup_passive_consent_text: '',
|
|
||||||
popup_closed_how: 'kliknięcie przycisku „X”',
|
|
||||||
});
|
|
||||||
|
|
||||||
const visited_url = entries[0].request.originalURL;
|
|
||||||
|
|
||||||
const p = config.pronoun;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mail-wrapper">
|
|
||||||
<div className="mail-controls">
|
|
||||||
<EmailTemplate3Controls {...{ config, setConfig, hosts: all_host_ids }} />
|
|
||||||
</div>
|
|
||||||
<div className="mail-content-wrapper">
|
|
||||||
<article className="mail-content">
|
|
||||||
<p>
|
|
||||||
Dzień dobry, w dniu {getDate()}{' '}
|
|
||||||
{['odwiedziłem', 'odwiedziłam', 'odwiedziłom', 'odwiedziliśmy'][p]} stronę{' '}
|
|
||||||
{visited_url} i {['zbadałem', 'zbadałam', 'zbadałom', 'zbadaliśmy'][p]} ją
|
|
||||||
za pomocą wtyczki <a href={PLUGIN_URL}>{PLUGIN_NAME}</a> w celu zbadania,
|
|
||||||
jakie informacje o {['mnie', 'mnie', 'mnie', 'nas'][p]} wysyła ta strona do
|
|
||||||
podmiotów trzecich.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{['Moją', 'Moją', 'Moją', 'Naszą'][p]} szczególną uwagę przykuło:
|
|
||||||
WYFILTROWANE WZGLĘDEM TEGO, CZY DANEGO PODMIOTU NIE MA W POLITYCE
|
|
||||||
PRYWATNOŚCI LUB POWIADOMIENIU O COOKIESACH{' '}
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
- wysyłanie mojego identyfikatora internetowego [z Cookie] (value)
|
|
||||||
oraz części mojej historii przeglądania do właściciela domeny
|
|
||||||
(domain);
|
|
||||||
</li>
|
|
||||||
<li> - (...).</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Dane te zostały wysłane zanim{' '}
|
|
||||||
{['miałem', 'miałam', 'miałom', 'mieliśmy'][p]} szansę przeczytać Państwa
|
|
||||||
politykę prywatności i w jakikolwiek czynny i jednoznaczny sposób wyrazić
|
|
||||||
zgodę na takie przetwarzanie moich danych osobowych.
|
|
||||||
</p>
|
|
||||||
{!['yes', 'null'].includes(config.policy_readable) ? (
|
|
||||||
<p>
|
|
||||||
{['Chciałem', 'Chciałam', 'Chciałom', 'Chcieliśmy'][p]} przeczytać
|
|
||||||
Państwa politykę prywatności przed akceptacją, ale{' '}
|
|
||||||
{config.policy_readable == 'cant_find' ? (
|
|
||||||
<>nie mogę znaleźć jej nigdzie na Państwa stronie.</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}{' '}
|
|
||||||
{config.policy_readable == 'entirely_obscured_by_popup' ? (
|
|
||||||
<>jest ona przesłonięta przez okienko o RODO.</>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
Dane zostały udostępnione podmiotom, o których nie{' '}
|
|
||||||
{['znalazłem', 'znalazłam', 'znalazłom', 'znaleźliśmy'][p]} informacji ani w
|
|
||||||
Państwa polityce prywatności, ani w żadnym wyskakującym okienku na Państwa
|
|
||||||
stronie. Z tego powodu zwracam{p == 3 ? 'y' : ''} się do Państwa z pytaniem:
|
|
||||||
jakie były podstawy prawne takiego ujawnienia{' '}
|
|
||||||
{['moich', 'moich', 'moich', 'naszych'][p]} danych osobowych wyżej
|
|
||||||
wymienionym podmiotom? Uprzejmie{' '}
|
|
||||||
{['proszę', 'proszę', 'proszę', 'prosimy'][p]} o wskazanie podstawy prawnej
|
|
||||||
dla każdego z tych podmiotów z osobna.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { RequestCluster } from '../request-cluster';
|
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
|
||||||
import EmailTemplate3 from './email-template-3';
|
|
||||||
|
|
||||||
export default function EmailTemplate({
|
|
||||||
entries,
|
|
||||||
clusters,
|
|
||||||
version,
|
|
||||||
}: {
|
|
||||||
entries: StolenDataEntry[];
|
|
||||||
clusters: Record<string, RequestCluster>;
|
|
||||||
version: number;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<EmailTemplate3 {...{ entries, clusters, version }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
314
report-window/generate-survey-questions.ts
Normal file
314
report-window/generate-survey-questions.ts
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
function generateHostPage(
|
||||||
|
host: string,
|
||||||
|
index: number,
|
||||||
|
all_hosts: string[]
|
||||||
|
): { title: string; elements: any[] } {
|
||||||
|
function f(name: string, h = host) {
|
||||||
|
return `${h.replace(/\./g, '_')}|${name}`;
|
||||||
|
}
|
||||||
|
const previous_host: string | null = index > 0 ? all_hosts[index - 1] : null;
|
||||||
|
function defaultValue(name: string) {
|
||||||
|
if (!previous_host) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return { defaultValueExpression: `{${f(name, previous_host)}}` };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: host,
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: f('present'),
|
||||||
|
isRequired: true,
|
||||||
|
title: `Cel ujawnienia danych właścicielowi domeny ${host}`,
|
||||||
|
...defaultValue('present'),
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'not_mentioned',
|
||||||
|
text: 'nie jest podany nigdzie na stronie',
|
||||||
|
visibleIf: "{policy_readable} = 'yes' ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'not_before_making_a_choice',
|
||||||
|
text: 'nie jest podany w żadnym miejscu na stronie, do którego można się dostać bez podejmowania wyboru dotyczącego przetwarzania danych osobowych',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'mentioned_in_policy',
|
||||||
|
text: 'jest podany w polityce prywatności',
|
||||||
|
visibleIf: "{policy_readable} = 'yes' ",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: 'mentioned_in_popup',
|
||||||
|
text: 'jest podany w okienku RODO',
|
||||||
|
visibleIf: "{popup_type} != 'none' ",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: f('legal_basis_type'),
|
||||||
|
...defaultValue('legal_basis_type'),
|
||||||
|
isRequired: true,
|
||||||
|
title: `Podstawa prawna dla tego konkretnego celu`,
|
||||||
|
visibleIf: `{${f('present')}} notempty and {${f(
|
||||||
|
'present'
|
||||||
|
)}} != "not_mentioned" and {${f('present')}} != "not_before_making_a_choice"`,
|
||||||
|
choices: [
|
||||||
|
{ value: 'consent', text: 'to zgoda.' },
|
||||||
|
{
|
||||||
|
value: 'legitimate_interest',
|
||||||
|
text: 'to uzasadniony interes.',
|
||||||
|
},
|
||||||
|
{ value: 'not_mentioned', text: 'nie jest wskazana nigdzie na stronie.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: f('consent_problems'),
|
||||||
|
...defaultValue('consent_problems'),
|
||||||
|
isRequired: true,
|
||||||
|
title: `Jak ma się ta podstawa prawna do stanu faktycznego?`,
|
||||||
|
visibleIf: `{${f('legal_basis_type')}} = "consent"`,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'claims_consent_but_sends_before_consent',
|
||||||
|
text: `Strona wysłała {moje} dane do ${host} zanim {wyraziłem} na to zgodę`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'claims_consent_but_there_was_no_easy_refuse',
|
||||||
|
text: '{Kliknąłem} przycisk od wyrażania zgody, ale w okienku o zgodę nie było natychmiastowo dostępnego przycisku do niewyrażenia zgody jednym kliknięciem',
|
||||||
|
},
|
||||||
|
{ value: 'none', text: 'żadne z powyższych.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'dropdown',
|
||||||
|
name: f('legitimate_interest_activity_specified'),
|
||||||
|
...defaultValue('legitimate_interest_activity_specified'),
|
||||||
|
isRequired: true,
|
||||||
|
title: /* HTML */ `Czy administrator strony opisał szczegółowo, na czym polega
|
||||||
|
uzasadniony interes w kontekście tego celu?`,
|
||||||
|
visibleIf: `{${f('legal_basis_type')}} = "legitimate_interest"`,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'precise',
|
||||||
|
text: /* HTML */ `Tak, wskazuje jasno na bieżące działania lub korzyści
|
||||||
|
wynikające z takiego przetwarzania danych.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'vague',
|
||||||
|
text: `Wskazuje tylko ogólnie, jak np. „marketing” czy „statystyki”.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'no',
|
||||||
|
text: `Nie. Nie wiadomo, na czym ten uzasadniony interes polega.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
title: `Jak administrator opisał to, na czym polega uzasadniony interes w kontekście ${host}?`,
|
||||||
|
name: f('legitimate_interest_description'),
|
||||||
|
visibleIf: `{${f('legitimate_interest_activity_specified')}} = 'vague'`,
|
||||||
|
placeholder: 'marketing',
|
||||||
|
defaultValueExpression:
|
||||||
|
index == 0
|
||||||
|
? 'marketing'
|
||||||
|
: `{${f('legitimate_interest_description', previous_host)}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'dropdown',
|
||||||
|
title: `Czy domena ${host} należy do podmiotu spoza Europy (np. Google, Facebook)?`,
|
||||||
|
name: f('outside_eu'),
|
||||||
|
...defaultValue('outside_eu'),
|
||||||
|
visibleIf: `{${f('legitimate_interest_activity_specified')}} = "precise" or {${f(
|
||||||
|
'consent_problems'
|
||||||
|
)}} = "none"`,
|
||||||
|
choices: [
|
||||||
|
{ value: 'yes', text: 'Tak' },
|
||||||
|
{ value: 'no', text: 'Nie' },
|
||||||
|
{ value: 'not_sure', text: 'Nie wiem' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function generateSurveyQuestions(hosts: string[]) {
|
||||||
|
return {
|
||||||
|
showQuestionNumbers: 'off',
|
||||||
|
showProgressBar: 'top',
|
||||||
|
clearInvisibleValues: 'onHidden',
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
title: 'Zaimki',
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: 'zaimek',
|
||||||
|
title: 'Forma czasownika:',
|
||||||
|
isRequired: true,
|
||||||
|
choices: [
|
||||||
|
{ value: 0, text: 'Wysłałem' },
|
||||||
|
{ value: 1, text: 'Wysłałam' },
|
||||||
|
{ value: 2, text: 'Wysłałom' },
|
||||||
|
{ value: 3, text: 'Wysłaliśmy' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Obowiązek informacyjny i machanizm pozyskiwania zgody',
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
title: 'Jaką formę informacji o przetwarzaniu danych osobowych stosuje ta strona?',
|
||||||
|
name: 'popup_type',
|
||||||
|
isRequired: true,
|
||||||
|
choices: [
|
||||||
|
{ value: 'none', text: 'Brak informacji' },
|
||||||
|
{
|
||||||
|
value: 'passive_popup',
|
||||||
|
text: /* HTML */ `Okienko o cookiesach, bez możliwości podjęcia
|
||||||
|
żadnego wyboru (np. tylko opcja „zamknij”)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'some_choice',
|
||||||
|
text: 'Okienko o cookiesach, z możliwością podjęcia wyboru',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'checkbox',
|
||||||
|
title: /* HTML */ `Istnieje możliwość, że okienko z informacjami i wyborami
|
||||||
|
dotyczącymi przetwarzania {Twoich} danych osobowych ukazało się dawno temu w
|
||||||
|
trakcie {twojej} wcześniejszej wizyty i wtedy je {odkliknąłeś}. {Otwórz} tę
|
||||||
|
samą stronę w Trybie Prywatnym (Incognito). Co {widzisz}?`,
|
||||||
|
visibleIf: "{popup_type} = 'none'",
|
||||||
|
name: 'is_incognito_different',
|
||||||
|
isRequired: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'incognito_is_the_same',
|
||||||
|
text: 'W Trybie prywatnym {widzę} to samo, co {widziałem} w normalnym trybie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'html',
|
||||||
|
visibleIf: '{is_incognito_different} != "no" and {popup_type} = "none"',
|
||||||
|
html: /* HTML */ `Jeżeli w trybie incognito widzisz więcej okienek z
|
||||||
|
informacjami o przetwarzaniu danych osobowych, wykonaj analizę w
|
||||||
|
normalnym trybie ponownie - ale najpierw usuń pliki cookies tej strony.
|
||||||
|
<a
|
||||||
|
href="https://support.mozilla.org/pl/kb/usuwanie-ciasteczek-i-danych-stron-firefox?redirectslug=usuwanie-ciasteczek&redirectlocale=pl"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Zobacz, jak to zrobić
|
||||||
|
</a>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: 'mentions_passive_consent',
|
||||||
|
isRequired: true,
|
||||||
|
visibleIf: '{popup_type} = "passive_popup"',
|
||||||
|
title: 'Czy treść okienka wskazuje na zgodę wyrażoną pasywnie, np. „Korzystając z naszej strony wyrażasz zgodę” lub „Brak zmiany ustawień przeglądarki oznacza zgodę”?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'yes',
|
||||||
|
text: 'Tak',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'no',
|
||||||
|
text: 'Nie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'passive_consent_description',
|
||||||
|
isRequired: true,
|
||||||
|
visibleIf: '{mentions_passive_consent} = "yes"',
|
||||||
|
title: 'Jakimi słowami administrator opisuje to pasywne wyrażenie zgody? Zacytuj wprost',
|
||||||
|
defaultValue: 'Korzystając ze strony wyrażasz zgodę',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: 'cookie_wall',
|
||||||
|
isRequired: true,
|
||||||
|
visibleIf: '{popup_type} = "passive_popup"',
|
||||||
|
title: 'Czy treść strony jest wygodnie czytelna bez odkliknięcia tego okienka o RODO?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'no', // wiem, że tu jest "no", a odpowiedź brzmi "tak" - ale nazwa pytania dotyczy obecności cookie walla
|
||||||
|
text: 'Tak, jest czytelna',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'yes',
|
||||||
|
text: 'Nie. Jest zupełnie niewidoczna albo jest przesłonięta w stopniu uniemożliwiającym lub znacznie utrudniającym czytanie treści strony.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: 'rejection_is_hard',
|
||||||
|
isRequired: true,
|
||||||
|
visibleIf: '{popup_type} = "some_choice"',
|
||||||
|
title: 'Czy wyrażenie zgody na wszystkie cele jest dokładnie tak samo łatwe, jak odmowa zgody na wszystkie cele?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'no', // wiem, że tu jest "no", a odpowiedź brzmi "tak" - ale nazwa pytania dotyczy braku równowagi
|
||||||
|
text: 'Tak. Opcja odmowy zgody na wszystkie cele jest równie widoczna i łatwo dostępna, co opcja wyrażenia zgody.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'yes',
|
||||||
|
text: 'Nie. Muszę wykonać więcej czynności aby odmówić wszystkich zgód, albo opcja niewyrażenia zgody jest mało widoczna.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
name: 'administrator_identity_available_before_choice',
|
||||||
|
isRequired: true,
|
||||||
|
visibleIf: '{popup_type} = "some_choice"',
|
||||||
|
title: 'Czy przed podjęciem wyboru dot. {Twoich} danych masz możliwość poznać tożsamość administratora strony?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
value: 'yes',
|
||||||
|
text: 'Tak.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'no',
|
||||||
|
text: 'Nie.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Obowiązek informacyjny, polityka prywatności',
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'radiogroup',
|
||||||
|
title: 'Czy polityka prywatności jest dostępna i czytelna?',
|
||||||
|
name: 'policy_readable',
|
||||||
|
isRequired: true,
|
||||||
|
choices: [
|
||||||
|
{ value: 'yes', text: 'dostępna i czytelna' },
|
||||||
|
{
|
||||||
|
value: 'entirely_obscured_by_popup',
|
||||||
|
text: 'dostępna, ale nieczytelna. Zasłania ją całkowicie lub prawie całkowicie popup o RODO lub nie można się do niej doklikać bez podjęcia wyboru w okienku',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'cant_find',
|
||||||
|
text: `Niedostępna. {Szukałem}, ale nie {znalazłem} jej na stronie`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...hosts.map(generateHostPage),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
0
report-window/generate-survey.ts
Normal file
0
report-window/generate-survey.ts
Normal file
|
@ -1,65 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { setHostSetting } from './email-host-settings';
|
|
||||||
import { EmailTemplate3Config } from './email-template-3';
|
|
||||||
|
|
||||||
export default function LegitimateInteresProblems({
|
|
||||||
settings,
|
|
||||||
host_id,
|
|
||||||
pronoun,
|
|
||||||
setConfig,
|
|
||||||
}: {
|
|
||||||
host_id: string;
|
|
||||||
settings: EmailTemplate3Config['hosts_settings'][string];
|
|
||||||
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
|
||||||
}) {
|
|
||||||
if (settings.legal_basis_type !== 'legitimate_interest') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const [description, setDescription] = React.useState('marketing');
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
Czy administrator strony opisał szczegółowo, na czym polega uzasadniony interes w
|
|
||||||
kontekście tego celu?
|
|
||||||
{/* {hostSettingsDropdown({
|
|
||||||
settings,
|
|
||||||
host_id,
|
|
||||||
setConfig,
|
|
||||||
field: 'legitimate_interest_activity_specified' as const,
|
|
||||||
value: settings.legitimate_interest_activity_specified,
|
|
||||||
options: {
|
|
||||||
precise: [
|
|
||||||
'Tak, wskazuje jasno na bieżące działania lub korzyści wynikające z takiego przetwarzania danych.',
|
|
||||||
],
|
|
||||||
vague: ['Wskazuje tylko ogólnie, jak np. „marketing” czy „statystyki”.'],
|
|
||||||
no: ['Nie. Nie wiadomo, na czym ten uzasadniony interes polega.'],
|
|
||||||
},
|
|
||||||
})} */}
|
|
||||||
</div>
|
|
||||||
{settings.legitimate_interest_activity_specified === 'vague' ? (
|
|
||||||
<div>
|
|
||||||
Jak administrator opisał to, na czym polega uzasadniony interes w kontekście{' '}
|
|
||||||
{host_id}?{' '}
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={description}
|
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={(e) =>
|
|
||||||
setHostSetting(
|
|
||||||
setConfig,
|
|
||||||
[host_id, 'legitimate_interest_activity_description'],
|
|
||||||
description
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
zatwierdź
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
83
report-window/parse-answers.ts
Normal file
83
report-window/parse-answers.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import RawAnswers, { BasicRawAnswers, HostRawAnswers } from './raw-answers';
|
||||||
|
|
||||||
|
export type RecordValue<T> = T extends Record<any, infer R> ? R : any;
|
||||||
|
|
||||||
|
export type ParsedHostAnswers =
|
||||||
|
| {
|
||||||
|
present: 'not_mentioned' | 'not_before_making_a_choice';
|
||||||
|
}
|
||||||
|
| ({
|
||||||
|
present: 'mentioned_in_policy' | 'mentioned_in_popup';
|
||||||
|
} & (
|
||||||
|
| ({
|
||||||
|
legal_basis_type: 'consent';
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
consent_problems:
|
||||||
|
| 'claims_consent_but_sends_before_consent'
|
||||||
|
| 'claims_consent_but_there_was_no_easy_refuse';
|
||||||
|
}
|
||||||
|
| { consent_problems: 'none'; outside_eu: 'yes' | 'no' | 'not_sure' }
|
||||||
|
))
|
||||||
|
| ({
|
||||||
|
legal_basis_type: 'legitimate_interest';
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
legitimate_interest_activity_specified: 'no';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
legitimate_interest_activity_specified: 'precise';
|
||||||
|
outside_eu: 'yes' | 'no' | 'not_sure';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
legitimate_interest_activity_specified: 'vague';
|
||||||
|
legitimate_interest_description: string;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
| {
|
||||||
|
legal_basis_type: 'not_mentioned';
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
export type ParsedAnswers = BasicRawAnswers & { hosts: Record<string, ParsedHostAnswers> };
|
||||||
|
|
||||||
|
function parseHostAnswers(
|
||||||
|
raw_answers: Record<keyof HostRawAnswers, string>
|
||||||
|
): Record<string, ParsedHostAnswers> {
|
||||||
|
const result: Record<string, Record<string, string>> = {};
|
||||||
|
for (const [key, value] of Object.entries(raw_answers)) {
|
||||||
|
const [masked_host, attr] = key.split('|');
|
||||||
|
const host = masked_host.replace(/_/g, '.');
|
||||||
|
if (!result[host]) {
|
||||||
|
result[host] = {} as ParsedHostAnswers;
|
||||||
|
}
|
||||||
|
result[host][attr] = value;
|
||||||
|
}
|
||||||
|
return result as Record<string, ParsedHostAnswers>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseAnswers({
|
||||||
|
zaimek,
|
||||||
|
is_incognito_different,
|
||||||
|
policy_readable,
|
||||||
|
popup_type,
|
||||||
|
cookie_wall,
|
||||||
|
passive_consent_description,
|
||||||
|
mentions_passive_consent,
|
||||||
|
rejection_is_hard,
|
||||||
|
administrator_identity_available_before_choice,
|
||||||
|
...rest
|
||||||
|
}: RawAnswers): ParsedAnswers {
|
||||||
|
return {
|
||||||
|
zaimek,
|
||||||
|
is_incognito_different,
|
||||||
|
policy_readable,
|
||||||
|
popup_type,
|
||||||
|
cookie_wall,
|
||||||
|
passive_consent_description,
|
||||||
|
mentions_passive_consent,
|
||||||
|
rejection_is_hard,
|
||||||
|
administrator_identity_available_before_choice,
|
||||||
|
hosts: parseHostAnswers(rest),
|
||||||
|
} as RawAnswers;
|
||||||
|
}
|
19
report-window/questions.tsx
Normal file
19
report-window/questions.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import * as Survey from 'survey-react';
|
||||||
|
import RawAnswers from './raw-answers';
|
||||||
|
import useSurvey from './use-survey';
|
||||||
|
|
||||||
|
export default function Questions({
|
||||||
|
hosts,
|
||||||
|
onComplete,
|
||||||
|
}: {
|
||||||
|
hosts: string[];
|
||||||
|
onComplete: (data: RawAnswers) => void;
|
||||||
|
}) {
|
||||||
|
const survey = useSurvey(hosts, {
|
||||||
|
onComplete: (sender) => onComplete(sender.data),
|
||||||
|
});
|
||||||
|
if (!survey) {
|
||||||
|
return <div>Wczytywanie....</div>;
|
||||||
|
}
|
||||||
|
return <Survey.Survey model={survey} />;
|
||||||
|
}
|
57
report-window/raw-answers.ts
Normal file
57
report-window/raw-answers.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
export type HostRawAnswers = {
|
||||||
|
[key: `${string}|present`]:
|
||||||
|
| 'not_mentioned'
|
||||||
|
| 'not_before_making_a_choice'
|
||||||
|
| 'mentioned_in_policy'
|
||||||
|
| 'mentioned_in_popup';
|
||||||
|
[key: `${string}|legal_basis_type`]: 'consent' | 'legitimate_interest' | 'not_mentioned';
|
||||||
|
[key: `${string}|consent`]:
|
||||||
|
| 'claims_consent_but_sends_before_consent'
|
||||||
|
| 'claims_consent_but_there_was_no_easy_refuse'
|
||||||
|
| 'none';
|
||||||
|
[key: `${string}|legitimate_interest_activity_specified`]: 'precise' | 'vague' | 'no';
|
||||||
|
[key: `${string}|legitimate_interest_description`]: string;
|
||||||
|
[key: `${string}|outside_eu`]: 'yes' | 'no' | 'not_sure';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BasicRawAnswers = {
|
||||||
|
zaimek: 0 | 1 | 2 | 3;
|
||||||
|
is_incognito_different: [] | ['incognito_is_the_same'];
|
||||||
|
policy_readable: 'yes' | 'vague' | 'cant_find';
|
||||||
|
} & (
|
||||||
|
| ({
|
||||||
|
popup_type: 'passive_popup';
|
||||||
|
cookie_wall: 'yes' | 'no';
|
||||||
|
rejection_is_hard: undefined;
|
||||||
|
administrator_identity_available_before_choice: undefined;
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
mentions_passive_consent?: 'yes';
|
||||||
|
passive_consent_description: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
mentions_passive_consent?: 'no';
|
||||||
|
passive_consent_description: undefined;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
| {
|
||||||
|
popup_type: 'some_choice';
|
||||||
|
rejection_is_hard: 'yes' | 'no';
|
||||||
|
cookie_wall: undefined;
|
||||||
|
passive_consent_description: undefined;
|
||||||
|
mentions_passive_consent: undefined;
|
||||||
|
administrator_identity_available_before_choice: 'yes' | 'no';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
popup_type: 'none';
|
||||||
|
cookie_wall: undefined;
|
||||||
|
passive_consent_description: undefined;
|
||||||
|
mentions_passive_consent: undefined;
|
||||||
|
rejection_is_hard: undefined;
|
||||||
|
administrator_identity_available_before_choice: undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type RawAnswers = BasicRawAnswers & HostRawAnswers;
|
||||||
|
|
||||||
|
export default RawAnswers;
|
|
@ -1,40 +1,53 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { getMemory } from '../memory';
|
import { getMemory } from '../memory';
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
import { useEmitter } from '../util';
|
||||||
import { reduceConcat, useEmitter } from '../util';
|
|
||||||
import EmailTemplate from './email-template';
|
|
||||||
|
|
||||||
import './report-window.scss';
|
import './report-window.scss';
|
||||||
|
import Questions from './questions';
|
||||||
|
import EmailContent from './email-content';
|
||||||
|
import { parseAnswers, ParsedAnswers } from './parse-answers';
|
||||||
|
|
||||||
function Report() {
|
function Report() {
|
||||||
try {
|
try {
|
||||||
const origin = new URL(document.location.toString()).searchParams.get('origin');
|
const origin = new URL(document.location.toString()).searchParams.get('origin');
|
||||||
const [counter, setCounter] = useEmitter(getMemory());
|
const [counter] = useEmitter(getMemory());
|
||||||
|
const [answers, setAnswers] = React.useState<ParsedAnswers>(null);
|
||||||
|
const [mode, setMode] = React.useState('survey');
|
||||||
const clusters = getMemory().getClustersForOrigin(origin);
|
const clusters = getMemory().getClustersForOrigin(origin);
|
||||||
const [entries, setEntries] = React.useState<StolenDataEntry[]>([]);
|
/* const [entries, setEntries] = React.useState<StolenDataEntry[]>([]); */
|
||||||
React.useEffect(() => {
|
/* React.useEffect(() => {
|
||||||
setEntries(
|
* setEntries(
|
||||||
Object.values(clusters)
|
* Object.values(clusters)
|
||||||
.map((cluster) => {
|
* .map((cluster) => {
|
||||||
cluster.calculateRepresentativeStolenData();
|
* cluster.calculateRepresentativeStolenData();
|
||||||
return cluster.representativeStolenData;
|
* return cluster.representativeStolenData;
|
||||||
})
|
* })
|
||||||
.reduce(reduceConcat, [])
|
* .reduce(reduceConcat, [])
|
||||||
.filter((entry) => entry.isMarked)
|
* .filter((entry) => entry.isMarked)
|
||||||
);
|
* );
|
||||||
}, []);
|
* }, []); */
|
||||||
if (entries.length == 0) {
|
/* if (entries.length == 0) {
|
||||||
return <>Wczytywanie...</>;
|
* return <>Wczytywanie...</>;
|
||||||
}
|
* } */
|
||||||
const result = (
|
const result = (
|
||||||
<div {...{ 'data-version': counter }}>
|
<div {...{ 'data-version': counter }}>
|
||||||
<nav>
|
<nav>
|
||||||
<img src="../assets/icon-addon.svg" width={48} height={48}></img>{' '}
|
<img src="../assets/icon-addon.svg" width={48} height={48}></img>{' '}
|
||||||
<h1>Rentgen - Generuj treść maila dla {origin}</h1>
|
<h1>Rentgen - Generuj treść maila dla {origin}</h1>
|
||||||
</nav>
|
</nav>
|
||||||
|
{mode === 'survey' ? (
|
||||||
<EmailTemplate {...{ entries, clusters, version: counter }} />
|
<Questions
|
||||||
|
hosts={Object.keys(clusters)}
|
||||||
|
onComplete={(answers) => {
|
||||||
|
setAnswers(parseAnswers(answers));
|
||||||
|
setMode('preview');
|
||||||
|
}}
|
||||||
|
></Questions>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
{mode === 'preview' ? <EmailContent {...{ answers }} /> : ''}
|
||||||
{/* <HARConverter {...{ entries }} /> */}
|
{/* <HARConverter {...{ entries }} /> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
34
report-window/use-survey.ts
Normal file
34
report-window/use-survey.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as Survey from 'survey-react';
|
||||||
|
import generateSurveyQuestions from './generate-survey-questions';
|
||||||
|
import RawAnswers from './raw-answers';
|
||||||
|
import verbs from './verbs';
|
||||||
|
|
||||||
|
export default function useSurvey(
|
||||||
|
hosts: string[],
|
||||||
|
{ onComplete }: { onComplete: (sender: { data: RawAnswers }) => void }
|
||||||
|
): Survey.ReactSurveyModel {
|
||||||
|
const [survey, setSurvey] = React.useState<Survey.Model>(null);
|
||||||
|
React.useEffect(() => {
|
||||||
|
const model = generateSurveyQuestions(hosts);
|
||||||
|
const survey = new Survey.Model(model);
|
||||||
|
survey.onProcessTextValue.add(function (
|
||||||
|
sender: Survey.SurveyModel,
|
||||||
|
options: { name: string; value?: string }
|
||||||
|
) {
|
||||||
|
if (verbs[options.name.toLowerCase()]) {
|
||||||
|
options.value = verbs[options.name.toLowerCase()][sender.valuesHash.zaimek];
|
||||||
|
if (options.name[0] == options.name[0].toUpperCase()) {
|
||||||
|
options.value = [
|
||||||
|
options.value[0].toUpperCase(),
|
||||||
|
...options.value.slice(1),
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
survey.onComplete.add(onComplete);
|
||||||
|
setSurvey(survey);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return survey;
|
||||||
|
}
|
|
@ -157,7 +157,7 @@ export default function StolenDataCluster({
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="table-header" colSpan={4}>
|
<th className="table-header" colSpan={4}>
|
||||||
Znalezione ustawienia:
|
Wysłane dane:
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user