Compare commits
2 Commits
a306061042
...
894391aaa3
Author | SHA1 | Date | |
---|---|---|---|
|
894391aaa3 | ||
|
2c5e4f1005 |
@ -1,5 +1,5 @@
|
|||||||
trailingComma: "es5"
|
trailingComma: "es5"
|
||||||
tabWidth: 4
|
tabWidth: 4
|
||||||
printWidth: 80
|
printWidth: 100
|
||||||
semi: true
|
semi: true
|
||||||
singleQuote: true
|
singleQuote: true
|
||||||
|
@ -54,6 +54,10 @@ esbuild
|
|||||||
outdir: './lib',
|
outdir: './lib',
|
||||||
loader: { '.woff': 'file', '.woff2': 'file' },
|
loader: { '.woff': 'file', '.woff2': 'file' },
|
||||||
plugins: [scss(), skipReactImports],
|
plugins: [scss(), skipReactImports],
|
||||||
|
define: {
|
||||||
|
PLUGIN_NAME: '"Rentgen"',
|
||||||
|
PLUGIN_URL: '"https://git.internet-czas-dzialac.pl/icd/rentgen"',
|
||||||
|
},
|
||||||
external: ['react', 'react-dom'],
|
external: ['react', 'react-dom'],
|
||||||
watch,
|
watch,
|
||||||
})
|
})
|
||||||
|
43
report-window/consent-problems.tsx
Normal file
43
report-window/consent-problems.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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: /* HTML */ `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: /* HTML */ `${[
|
||||||
|
'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>
|
||||||
|
);
|
||||||
|
}
|
108
report-window/email-host-settings.tsx
Normal file
108
report-window/email-host-settings.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
{Object.entries(config.hosts_settings).map(([host_id, settings]) => (
|
||||||
|
<div key={host_id}>
|
||||||
|
<h5>
|
||||||
|
{host_id}, {hostNeedsQuestions(settings).toString()}
|
||||||
|
</h5>
|
||||||
|
<p>
|
||||||
|
Cele przetwarzania danych przez właściciela domeny {host_id}{' '}
|
||||||
|
{hostSettingsDropdown({
|
||||||
|
host_id,
|
||||||
|
setConfig,
|
||||||
|
settings,
|
||||||
|
field: 'presence',
|
||||||
|
value: settings.presence,
|
||||||
|
options: {
|
||||||
|
not_mentioned: ['nie są nigdzie na stronie opisane'],
|
||||||
|
mentioned_in_policy: [
|
||||||
|
'są opisane w polityce prywatności',
|
||||||
|
config.policy_readable !== 'yes',
|
||||||
|
],
|
||||||
|
mentioned_in_popup: ['są opisane w okienku RODO'],
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
{!['not_mentioned', 'null'].includes(settings.presence) ? (
|
||||||
|
<p>
|
||||||
|
Wskazana przez administratora podstawa prawna dla{' '}
|
||||||
|
<strong> tego konkretnego celu</strong>{' '}
|
||||||
|
{hostSettingsDropdown({
|
||||||
|
host_id,
|
||||||
|
setConfig,
|
||||||
|
settings,
|
||||||
|
field: 'legal_basis_type' as const,
|
||||||
|
value: settings.legal_basis_type,
|
||||||
|
options: {
|
||||||
|
consent: ['to zgoda.'],
|
||||||
|
legitimate_interest: ['to uzasadniony interes.'],
|
||||||
|
not_mentioned: ['nie jest wskazana nigdzie na stronie.'],
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
{!['not_mentioned', 'null'].includes(settings.legal_basis_type) ? (
|
||||||
|
<div>
|
||||||
|
{ConsentProblems({ settings, host_id, pronoun: p, setConfig })}
|
||||||
|
{LegitimateInteresProblems({
|
||||||
|
settings,
|
||||||
|
host_id,
|
||||||
|
setConfig,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,232 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { RequestCluster } from '../request-cluster';
|
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
|
||||||
import { getDate, toBase64 } from '../util';
|
|
||||||
import DomainSummary from './domain-summary';
|
|
||||||
|
|
||||||
type PopupState = 'not_clicked' | 'clicked_but_no_reject_all';
|
|
||||||
|
|
||||||
export default function EmailTemplate1({
|
|
||||||
entries,
|
|
||||||
clusters,
|
|
||||||
}: {
|
|
||||||
entries: StolenDataEntry[];
|
|
||||||
clusters: Record<string, RequestCluster>;
|
|
||||||
version: number;
|
|
||||||
}): JSX.Element {
|
|
||||||
const [popupState, setPopupState] =
|
|
||||||
React.useState<PopupState>('not_clicked');
|
|
||||||
const [acceptAllName, setAcceptAllName] = React.useState<string>(
|
|
||||||
'Zaakceptuj wszystkie'
|
|
||||||
);
|
|
||||||
const [popupScreenshotBase64, setPopupScreenshotBase64] =
|
|
||||||
React.useState<string>(null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popupState">Status okienka o rodo:</label>
|
|
||||||
<select
|
|
||||||
id="popupState"
|
|
||||||
value={popupState}
|
|
||||||
onChange={(e) => setPopupState(e.target.value as PopupState)}
|
|
||||||
>
|
|
||||||
<option value="not_clicked">Nic nie kliknięte</option>
|
|
||||||
<option value="clicked_but_no_reject_all">
|
|
||||||
Kliknięte "akceptuj wszystkie", ale nie było opcji "Odrzuć
|
|
||||||
wszystkie"
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
{popupState === 'clicked_but_no_reject_all' ? (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="acceptAllName">
|
|
||||||
Tekst na przycisku do zatwierdzania wszystkich zgód:
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
{...{
|
|
||||||
type: 'text',
|
|
||||||
value: acceptAllName,
|
|
||||||
onChange: (e) =>
|
|
||||||
setAcceptAllName(e.target.value),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="popup-screenshot">
|
|
||||||
Zrzut ekranu z tego, jak wyglądał popup przed
|
|
||||||
kliknięciem „{acceptAllName}”:
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
{...{
|
|
||||||
type: 'file',
|
|
||||||
id: 'popup-screenshot',
|
|
||||||
onChange: async (e) => {
|
|
||||||
setPopupScreenshotBase64(
|
|
||||||
await toBase64(e.target.files[0])
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<p>
|
|
||||||
Dzień dobry, w dniu {getDate()} odwiedziłem stronę{' '}
|
|
||||||
{entries[0].request.originalURL}. Strona ta wysłała moje dane
|
|
||||||
osobowe do podmiotów trzecich - bez mojej zgody.{' '}
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
{Object.values(clusters)
|
|
||||||
.filter((cluster) => cluster.hasMarks())
|
|
||||||
.map((cluster) => (
|
|
||||||
<DomainSummary cluster={cluster} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
{' '}
|
|
||||||
Dane te zostały wysłane przez Państwa stronę - a mówiąc
|
|
||||||
dokładniej, przez zamieszczone przez Państwa na tej stronie
|
|
||||||
skrypty.
|
|
||||||
</p>
|
|
||||||
{popupState === 'not_clicked' ? (
|
|
||||||
<p>
|
|
||||||
Nastąpiło to, zanim zdążyłem w ogóle przeczytać treść
|
|
||||||
wyskakującego okienka ze zgodami i zanim miałem szansę
|
|
||||||
wyrazić sprzeciw takiemu przetwarzaniu danych osobowych.
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
{popupState === 'clicked_but_no_reject_all' ? (
|
|
||||||
<p>
|
|
||||||
O ile po wejściu na stronę wcisnąłem w wyskakującym okienku
|
|
||||||
przycisk „{acceptAllName}”, 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 Art. 4. pkt 11{' '}
|
|
||||||
<em>
|
|
||||||
rozporządzenia Parlamentu Europejskiego i Rady (UE)
|
|
||||||
2016/679 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
|
|
||||||
</em>
|
|
||||||
.{<img {...{ src: popupScreenshotBase64 }} />}
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
<p>
|
|
||||||
Udokumentowałem to na zrzutach ekranu z mojej przeglądarki
|
|
||||||
internetowej, które to zrzuty przesyłam w załączeniu.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Wiem, że nie wszystkie rodzaje przetwarzania danych wymagają
|
|
||||||
zgody użytkownika. W kontekście stron internetowych z
|
|
||||||
wymienionych w Art. 6. pkt 1. RODO mogą mieć zastosowanie albo
|
|
||||||
„zgoda” (Art. 6. pkt 1. lit. a)), albo niezbędność tego
|
|
||||||
przetwarzania do wykonania umowy (Art. 6. pkt 1. lit. b)), albo
|
|
||||||
uzasadniony interes (Art. 6. pkt 1. lit. f)). Wiem też, że każda
|
|
||||||
z tych podstaw prawnych ma moc dopiero po spełnieniu określonych
|
|
||||||
warunków.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nie widzę ważnej podstawy prawnej legalizującej procesy
|
|
||||||
przetwarzania moich danych osobowych, jakie wymieniłem powyżej
|
|
||||||
(na pewno nie jest to przetwarzanie konieczne do wyświetlenia
|
|
||||||
strony z technicznego punktu widzenia). Jeżeli takie przesłanki
|
|
||||||
legalizujące jednak występują, proszę o ich wskazanie,
|
|
||||||
<strong>
|
|
||||||
{' '}
|
|
||||||
dla każdego z celów i podmiotów z <em>osobna</em>
|
|
||||||
</strong>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Jeżeli wskazaną przez Państwa przesłanką legalizującą dany
|
|
||||||
element procesu przetwarzania danych osobowych przez Państwa
|
|
||||||
stronę jest Art 6. pkt 1 lit. a) RODO (zgoda), na mocy Art. 7
|
|
||||||
pkt 1 RODO proszę o wykazanie, że udzieliłem Państwu zgodę na
|
|
||||||
takie przetwarzanie moich danych osobowych zanim to
|
|
||||||
przetwarzanie nastąpiło, oraz że ta zgoda jest ważna w świetle
|
|
||||||
RODO (odnosząc się w szczególności do art. 7 ust. 3 RODO). Z
|
|
||||||
góry zaznaczam, że „ustawienia przeglądarki” nie stanowią ważnej
|
|
||||||
w świetle RODO zgody.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Jeżeli wskazaną przez Państwa przesłanką legalizującą dany
|
|
||||||
element procesu przetwarzania danych osobowych przez Państwa
|
|
||||||
stronę jest Art 6. pkt 1 lit. b) RODO (niezbędność takiego
|
|
||||||
przetwarzania do wykonania umowy), proszę o wskazanie, w jaki
|
|
||||||
sposób ta konieczność zachodzi, oraz co sprawia, że Państwa
|
|
||||||
zdaniem nie można wykonać umowy związanej z wyświetleniem
|
|
||||||
Państwa strony bez przekazywania identyfikatora internetowego z
|
|
||||||
plików Cookies lub historii przeglądania w nagłówku Referer do
|
|
||||||
wskazanych podmiotów trzecich.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Jeżeli wskazaną przez Państwa przesłanką legalizującą dany
|
|
||||||
element procesu przetwarzania danych osobowych przez Państwa
|
|
||||||
stronę jest Art 6. pkt 1 lit. f) RODO (uzasadniony interes),
|
|
||||||
proszę o wskazanie, jaki to jest{' '}
|
|
||||||
<strong>konkretny interes</strong> (prosze o bardziej dokładny
|
|
||||||
opis niż np. tylko "marketing"), oraz o wynik testu równowagi
|
|
||||||
pomiędzy Państwa interesem a moimi podstawowymi wolnościami i
|
|
||||||
prawami - ze wskazaniem tego, co sprawia, że w Państwa ocenie
|
|
||||||
Państwa uzasadniony interes przeważa moje prawa i interesy w
|
|
||||||
kontekście wspomnianych powyżej procesów przetwarzania danych.
|
|
||||||
Proszę też pamiętać, że aby w ramach danego celu przetwarzania
|
|
||||||
powołać się na prawnie uzasadniony interes, powinni mi Państo
|
|
||||||
umożliwić wyrażenie sprzeciwu wobec przetwarzania moich danych w
|
|
||||||
tym celu <em>przed</em> rozpoczęciem przetwarzania - 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 Europejskiej Rady Ochrony Danych dotyczące
|
|
||||||
targetowania użytkowników mediów społecznościowych
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Niniejszym zwracam się także z żądaniem ujawnienia tożsamości
|
|
||||||
podmiotów, które są właścicielami wyżej wymienionych domen, abym
|
|
||||||
mógł zapoznać się z ich politykami prywatności i zwrócić się do
|
|
||||||
tych podmiotów o usunięcie z ich baz wysłanych przez Państwa
|
|
||||||
stronę moich danych.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Proszę też o wysłanie kopii danych zebranych na mój temat i
|
|
||||||
wysłanych do wyżej wymienionych podmiotów.
|
|
||||||
</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 oficjalne wytyczne EROD
|
|
||||||
dotyczące zgody w kontekście RODO:
|
|
||||||
https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_pl.pdf
|
|
||||||
). 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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,16 +1,68 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { toBase64 } from '../util';
|
import { toBase64 } from '../util';
|
||||||
import { EmailTemplate2Config } from './email-template-2';
|
import ConsentProblems from './consent-problems';
|
||||||
|
import emailHostSettings from './email-host-settings';
|
||||||
|
import { EmailTemplate3Config } from './email-template-3';
|
||||||
|
|
||||||
export default function EmailTemplate2Controls({
|
export default function EmailTemplate3Controls({
|
||||||
config,
|
config,
|
||||||
setConfig,
|
setConfig,
|
||||||
}: {
|
}: {
|
||||||
config: EmailTemplate2Config;
|
config: EmailTemplate3Config;
|
||||||
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate2Config>>;
|
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
|
const p = config.pronoun;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="pronoun">Forma czasownika:</label>
|
||||||
|
<select
|
||||||
|
id="pronoun"
|
||||||
|
value={config.pronoun}
|
||||||
|
onChange={(e) =>
|
||||||
|
setConfig((v) => ({
|
||||||
|
...v,
|
||||||
|
pronoun: parseInt(e.target.value) as EmailTemplate3Config['pronoun'],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="0">Wysłałem</option>
|
||||||
|
<option value="1">Wysłałam</option>
|
||||||
|
<option value="2">Wysłałom</option>
|
||||||
|
<option value="3">Wysłaliśmy</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="policy_readable">
|
||||||
|
Czy polityka prywatności jest dostępna i czytelna?
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="policy_readable"
|
||||||
|
value={config.policy_readable}
|
||||||
|
onChange={(e) =>
|
||||||
|
setConfig((v) => ({
|
||||||
|
...v,
|
||||||
|
policy_readable: e.target
|
||||||
|
.value as EmailTemplate3Config['policy_readable'],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="null" disabled>
|
||||||
|
wybierz opcję
|
||||||
|
</option>
|
||||||
|
<option value="yes">dostępna i czytelna</option>
|
||||||
|
<option value="entirely_obscured_by_popup">
|
||||||
|
dostępna, ale nieczytelna. Zasłania ją popup o RODO
|
||||||
|
</option>
|
||||||
|
<option value="cant_find">
|
||||||
|
Niedostępna. {['Szukałem', 'Szukałam', 'Szukałom', 'Szukaliśmy'][p]}, ale
|
||||||
|
nie {['znalazłem', 'znalazłam', 'znalazłom', 'znaleźliśmy'][p]} jej na
|
||||||
|
stronie
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{emailHostSettings(config, setConfig)}
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="poup_type">Typ okienka o RODO: </label>
|
<label htmlFor="poup_type">Typ okienka o RODO: </label>
|
||||||
<select
|
<select
|
||||||
@ -19,8 +71,7 @@ export default function EmailTemplate2Controls({
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setConfig((v) => ({
|
setConfig((v) => ({
|
||||||
...v,
|
...v,
|
||||||
popup_type: e.target
|
popup_type: e.target.value as EmailTemplate3Config['popup_type'],
|
||||||
.value as EmailTemplate2Config['popup_type'],
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -33,17 +84,13 @@ export default function EmailTemplate2Controls({
|
|||||||
</div>
|
</div>
|
||||||
{config.popup_type !== 'none' ? (
|
{config.popup_type !== 'none' ? (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="popup_screenshot">
|
<label htmlFor="popup_screenshot">Zrzut ekranu okienka o RODO:</label>
|
||||||
Zrzut ekranu okienka o RODO:
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
{...{
|
{...{
|
||||||
type: 'file',
|
type: 'file',
|
||||||
id: 'popup_screenshot',
|
id: 'popup_screenshot',
|
||||||
onChange: async (e) => {
|
onChange: async (e) => {
|
||||||
const popup_screenshot_base64 = await toBase64(
|
const popup_screenshot_base64 = await toBase64(e.target.files[0]);
|
||||||
e.target.files[0]
|
|
||||||
);
|
|
||||||
setConfig((v) => ({
|
setConfig((v) => ({
|
||||||
...v,
|
...v,
|
||||||
popup_screenshot_base64,
|
popup_screenshot_base64,
|
||||||
@ -55,55 +102,26 @@ export default function EmailTemplate2Controls({
|
|||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
)}
|
)}
|
||||||
{config.popup_type === 'consent' ? (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="acceptAllName">
|
|
||||||
Tekst na przycisku do zatwierdzania wszystkich zgód:
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
{...{
|
|
||||||
type: 'text',
|
|
||||||
value: config.popup_accept_all_text,
|
|
||||||
onChange: (e) =>
|
|
||||||
setConfig((v) => ({
|
|
||||||
...v,
|
|
||||||
popup_accept_all_text: e.target.value,
|
|
||||||
})),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="popup_action">
|
<label htmlFor="popup_action">Czy coś klikn*ł*m w informacjach o RODO?</label>
|
||||||
Czy coś klikn*ł*m w informacjach o RODO?
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
id="popup_action"
|
id="popup_action"
|
||||||
value={config.popup_type}
|
value={config.popup_type}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setConfig((v) => ({
|
setConfig((v) => ({
|
||||||
...v,
|
...v,
|
||||||
popup_action: e.target
|
popup_action: e.target.value as EmailTemplate3Config['popup_action'],
|
||||||
.value as EmailTemplate2Config['popup_action'],
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<option value="ignored">Nic nie klin*ł*m</option>
|
<option value="ignored">Nic nie klin*ł*m</option>
|
||||||
<option value="accepted">
|
<option value="accepted">Kliknięte „{config.popup_accept_all_text}”</option>
|
||||||
Kliknięte „{config.popup_accept_all_text}”
|
<option value="closed">Zamkn*ł*m okienko (np. przyciskiem "X")</option>
|
||||||
</option>
|
|
||||||
<option value="closed">
|
|
||||||
Zamkn*ł*m okienko (np. przyciskiem "X")
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{config.popup_action === 'closed' ? (
|
{config.popup_action === 'closed' ? (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="popup_closed_how">
|
<label htmlFor="popup_closed_how">Jak okienko zostało zamknięte? Poprzez</label>
|
||||||
Jak okienko zostało zamknięte? Poprzez
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
id="popup_closed_how"
|
id="popup_closed_how"
|
||||||
type="text"
|
type="text"
|
||||||
@ -130,14 +148,13 @@ export default function EmailTemplate2Controls({
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setConfig((v) => ({
|
setConfig((v) => ({
|
||||||
...v,
|
...v,
|
||||||
popup_mentions_passive_consent:
|
popup_mentions_passive_consent: e.target.checked,
|
||||||
e.target.checked,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="popup_mentions_passive_consent">
|
<label htmlFor="popup_mentions_passive_consent">
|
||||||
okienko wspomina o pasywnej zgodzie (np. „korzystając ze
|
okienko wspomina o pasywnej zgodzie (np. „korzystając ze strony wyrażasz
|
||||||
strony wyrażasz zgodę”)
|
zgodę”)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -146,8 +163,7 @@ export default function EmailTemplate2Controls({
|
|||||||
{config.popup_mentions_passive_consent ? (
|
{config.popup_mentions_passive_consent ? (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="popup_passive_consent_text">
|
<label htmlFor="popup_passive_consent_text">
|
||||||
Jak okienko próbuje wmówić Ci, że wyrażasz zgodę?
|
Jak okienko próbuje wmówić Ci, że wyrażasz zgodę? Przeklej z treści okienka:
|
||||||
Przeklej z treści okienka:
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="popup_passive_consent_text"
|
id="popup_passive_consent_text"
|
147
report-window/email-template-3.tsx
Normal file
147
report-window/email-template-3.tsx
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
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 style={{ display: 'flex', flexFlow: 'row wrap', margin: '-1rem' }}>
|
||||||
|
<div style={{ flexBasis: '50rem', margin: '1rem' }}>
|
||||||
|
<EmailTemplate3Controls {...{ config, setConfig }} />
|
||||||
|
</div>
|
||||||
|
<article
|
||||||
|
style={{
|
||||||
|
boxShadow: 'rgba(0, 0, 0, 0.2) 5px 3px 8px',
|
||||||
|
padding: '4rem 3rem',
|
||||||
|
margin: '1rem',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
color: 'hsl(240, 5.7%, 15.8%)',
|
||||||
|
flexBasis: '50rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
|
||||||
import { RequestCluster } from '../request-cluster';
|
import { RequestCluster } from '../request-cluster';
|
||||||
import { StolenDataEntry } from '../stolen-data-entry';
|
import { StolenDataEntry } from '../stolen-data-entry';
|
||||||
import EmailTemplate1 from './email-template-1';
|
import EmailTemplate3 from './email-template-3';
|
||||||
import EmailTemplate2 from './email-template-2';
|
|
||||||
|
|
||||||
export default function EmailTemplate({
|
export default function EmailTemplate({
|
||||||
entries,
|
entries,
|
||||||
@ -13,21 +11,9 @@ export default function EmailTemplate({
|
|||||||
clusters: Record<string, RequestCluster>;
|
clusters: Record<string, RequestCluster>;
|
||||||
version: number;
|
version: number;
|
||||||
}) {
|
}) {
|
||||||
const [templateVersion, setTemplateVersion] = React.useState('2');
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<select
|
<EmailTemplate3 {...{ entries, clusters, version }} />
|
||||||
value={templateVersion}
|
|
||||||
onChange={(e) => setTemplateVersion(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="1">wersja 1</option>
|
|
||||||
<option value="2">wersja 2</option>
|
|
||||||
</select>
|
|
||||||
{templateVersion === '1' ? (
|
|
||||||
<EmailTemplate1 {...{ entries, clusters, version }} />
|
|
||||||
) : (
|
|
||||||
<EmailTemplate2 {...{ entries, clusters, version }} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
32
report-window/host-needs-questions.ts
Normal file
32
report-window/host-needs-questions.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { EmailTemplate3Config } from './email-template-3';
|
||||||
|
|
||||||
|
export function hostNeedsQuestions({
|
||||||
|
presence,
|
||||||
|
legal_basis_type,
|
||||||
|
consent_problems,
|
||||||
|
legitimate_interest_activity_description,
|
||||||
|
legitimate_interest_activity_specified,
|
||||||
|
}: EmailTemplate3Config['hosts_settings'][string]) {
|
||||||
|
if (presence == 'not_mentioned') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (legal_basis_type == 'not_mentioned') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (legal_basis_type == 'consent' && consent_problems !== 'null') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
legitimate_interest_activity_specified !== 'null' &&
|
||||||
|
legitimate_interest_activity_specified !== 'vague'
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
legal_basis_type == 'legitimate_interest' &&
|
||||||
|
legitimate_interest_activity_description != ''
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
46
report-window/host-setting-dropdown.tsx
Normal file
46
report-window/host-setting-dropdown.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { setHostSetting } from './email-host-settings';
|
||||||
|
import { EmailTemplate3Config } from './email-template-3';
|
||||||
|
|
||||||
|
export default function hostSettingsDropdown<
|
||||||
|
Field extends keyof EmailTemplate3Config['hosts_settings'][string]
|
||||||
|
>({
|
||||||
|
host_id,
|
||||||
|
setConfig,
|
||||||
|
value,
|
||||||
|
field,
|
||||||
|
options,
|
||||||
|
}: {
|
||||||
|
host_id: string;
|
||||||
|
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
||||||
|
settings: EmailTemplate3Config['hosts_settings'][string];
|
||||||
|
field: Field;
|
||||||
|
value: string;
|
||||||
|
options: Record<
|
||||||
|
Exclude<EmailTemplate3Config['hosts_settings'][string][Field], 'null'>,
|
||||||
|
[string, boolean?]
|
||||||
|
>;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={(e) =>
|
||||||
|
setHostSetting(
|
||||||
|
setConfig,
|
||||||
|
[host_id, field],
|
||||||
|
e.target.value as EmailTemplate3Config['hosts_settings'][string][typeof field]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="null" disabled>
|
||||||
|
wybierz opcję
|
||||||
|
</option>
|
||||||
|
{Object.entries(options).map(
|
||||||
|
([value, [display, disabled]]: [string, [string, boolean]]) => (
|
||||||
|
<option value={value} disabled={disabled}>
|
||||||
|
{display}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
47
report-window/host-settings-radio.tsx
Normal file
47
report-window/host-settings-radio.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { normalizeForClassname } from '../util';
|
||||||
|
import { EmailTemplate3Config } from './email-template-3';
|
||||||
|
|
||||||
|
export default function hostSettingsRadio<
|
||||||
|
Field extends keyof EmailTemplate3Config['hosts_settings'][string]
|
||||||
|
>({
|
||||||
|
options,
|
||||||
|
host_id,
|
||||||
|
field,
|
||||||
|
setConfig,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
host_id: string;
|
||||||
|
setConfig: React.Dispatch<React.SetStateAction<EmailTemplate3Config>>;
|
||||||
|
field: Field;
|
||||||
|
options: Record<Exclude<EmailTemplate3Config['hosts_settings'][string][Field], 'null'>, string>;
|
||||||
|
value: EmailTemplate3Config['hosts_settings'][string][Field];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{Object.entries(options).map(([option_value, display]) => (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name={normalizeForClassname(host_id + '_consent_problems')}
|
||||||
|
value={option_value}
|
||||||
|
checked={value == option_value}
|
||||||
|
id={normalizeForClassname(option_value + host_id)}
|
||||||
|
onChange={(e) => {
|
||||||
|
setConfig((v) => ({
|
||||||
|
...v,
|
||||||
|
hosts_settings: {
|
||||||
|
...v.hosts_settings,
|
||||||
|
[host_id]: {
|
||||||
|
...v.hosts_settings[host_id],
|
||||||
|
[field]: e.target.value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={normalizeForClassname(option_value + host_id)}>{display}</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
66
report-window/legitimate-interest-problems.tsx
Normal file
66
report-window/legitimate-interest-problems.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { setHostSetting } from './email-host-settings';
|
||||||
|
import { EmailTemplate3Config } from './email-template-3';
|
||||||
|
import hostSettingsDropdown from './host-setting-dropdown';
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
@ -9,13 +9,15 @@
|
|||||||
<body>
|
<body>
|
||||||
<div
|
<div
|
||||||
id="app"
|
id="app"
|
||||||
style="max-width: 50rem; margin: 0 auto; padding: 1rem 0rem 2rem;"
|
style=" margin: 0 auto; padding: 1rem 0rem 2rem;"
|
||||||
></div>
|
></div>
|
||||||
<style>
|
<style>
|
||||||
tr:hover {
|
tr:hover {
|
||||||
background-color: hsla(0, 0%, 0%, 0.1);
|
background-color: hsla(0, 0%, 0%, 0.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script src="/node_modules/react/umd/react.development.js"></script>
|
||||||
|
<script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||||
<script src="/lib/report-window/report-window.js"></script>
|
<script src="/lib/report-window/report-window.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -80,44 +80,39 @@ function DataPreview({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Report() {
|
function Report() {
|
||||||
console.time('getOrigin');
|
try {
|
||||||
const origin = new URL(document.location.toString()).searchParams.get(
|
const origin = new URL(document.location.toString()).searchParams.get(
|
||||||
'origin'
|
'origin'
|
||||||
);
|
|
||||||
console.timeEnd('getOrigin');
|
|
||||||
console.time('useMemory');
|
|
||||||
const [counter, setCounter] = useEmitter(getMemory());
|
|
||||||
console.timeEnd('useMemory');
|
|
||||||
console.time('getClustersForOrigin');
|
|
||||||
const clusters = getMemory().getClustersForOrigin(origin);
|
|
||||||
console.timeEnd('getClustersForOrigin');
|
|
||||||
const [entries, setEntries] = React.useState<StolenDataEntry[]>([]);
|
|
||||||
console.time('useEffect report-window');
|
|
||||||
React.useEffect(() => {
|
|
||||||
setEntries(
|
|
||||||
Object.values(clusters)
|
|
||||||
.map((cluster) => {
|
|
||||||
cluster.calculateRepresentativeStolenData();
|
|
||||||
return cluster.representativeStolenData;
|
|
||||||
})
|
|
||||||
.reduce(reduceConcat, [])
|
|
||||||
.filter((entry) => entry.isMarked)
|
|
||||||
);
|
);
|
||||||
}, []);
|
const [counter, setCounter] = useEmitter(getMemory());
|
||||||
console.timeEnd('useEffect report-window');
|
const clusters = getMemory().getClustersForOrigin(origin);
|
||||||
if (entries.length == 0) {
|
const [entries, setEntries] = React.useState<StolenDataEntry[]>([]);
|
||||||
return <>Wczytywanie...</>;
|
React.useEffect(() => {
|
||||||
|
setEntries(
|
||||||
|
Object.values(clusters)
|
||||||
|
.map((cluster) => {
|
||||||
|
cluster.calculateRepresentativeStolenData();
|
||||||
|
return cluster.representativeStolenData;
|
||||||
|
})
|
||||||
|
.reduce(reduceConcat, [])
|
||||||
|
.filter((entry) => entry.isMarked)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
if (entries.length == 0) {
|
||||||
|
return <>Wczytywanie...</>;
|
||||||
|
}
|
||||||
|
const result = (
|
||||||
|
<div {...{ 'data-version': counter }}>
|
||||||
|
<h1>Generuj treść maila dla {origin}</h1>
|
||||||
|
<EmailTemplate {...{ entries, clusters, version: counter }} />
|
||||||
|
{/* <HARConverter {...{ entries }} /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return <div>ERRO! {JSON.stringify(e)}</div>;
|
||||||
}
|
}
|
||||||
console.time('rendering template');
|
|
||||||
const result = (
|
|
||||||
<div {...{ 'data-version': counter }}>
|
|
||||||
<h1>Generuj treść maila dla {origin}</h1>
|
|
||||||
<EmailTemplate {...{ entries, clusters, version: counter }} />
|
|
||||||
<HARConverter {...{ entries }} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
console.timeEnd('rendering template');
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(<Report />, document.getElementById('app'));
|
ReactDOM.render(<Report />, document.getElementById('app'));
|
||||||
|
26
util.ts
26
util.ts
@ -87,17 +87,13 @@ export function parseToObject(str: unknown): Record<string | symbol, unknown> {
|
|||||||
result = JSON.parse(str);
|
result = JSON.parse(str);
|
||||||
} else if (typeof str == 'object') {
|
} else if (typeof str == 'object') {
|
||||||
result = str as Record<string | symbol, unknown>;
|
result = str as Record<string | symbol, unknown>;
|
||||||
original_string =
|
original_string = (result[Symbol.for('originalString')] as string) || JSON.stringify(str);
|
||||||
(result[Symbol.for('originalString')] as string) ||
|
|
||||||
JSON.stringify(str);
|
|
||||||
}
|
}
|
||||||
result[Symbol.for('originalString')] = original_string;
|
result[Symbol.for('originalString')] = original_string;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isJSONObject(
|
export function isJSONObject(str: unknown): str is Record<string, unknown> | string | number {
|
||||||
str: unknown
|
|
||||||
): str is Record<string, unknown> | string | number {
|
|
||||||
try {
|
try {
|
||||||
const firstChar = JSON.stringify(parseToObject(str))[0];
|
const firstChar = JSON.stringify(parseToObject(str))[0];
|
||||||
return ['{', '['].includes(firstChar);
|
return ['{', '['].includes(firstChar);
|
||||||
@ -137,9 +133,10 @@ export function reduceConcat<T>(a: T[], b: T[]): T[] {
|
|||||||
|
|
||||||
export function getDate() {
|
export function getDate() {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
return `${d.getFullYear()}-${(d.getMonth() + 1)
|
return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d
|
||||||
|
.getDate()
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
|
.padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toBase64(file: File): Promise<string> {
|
export function toBase64(file: File): Promise<string> {
|
||||||
@ -193,8 +190,7 @@ export function isBase64JSON(s: unknown): s is string {
|
|||||||
|
|
||||||
export function flattenObject(
|
export function flattenObject(
|
||||||
obj: unknown,
|
obj: unknown,
|
||||||
parser: (to_parse: unknown) => string | Record<string, unknown> = (id) =>
|
parser: (to_parse: unknown) => string | Record<string, unknown> = (id) => id.toString(),
|
||||||
id.toString(),
|
|
||||||
key = '',
|
key = '',
|
||||||
ret = [] as [string, string][],
|
ret = [] as [string, string][],
|
||||||
parsed = false
|
parsed = false
|
||||||
@ -224,8 +220,7 @@ export function flattenObject(
|
|||||||
|
|
||||||
export function flattenObjectEntries(
|
export function flattenObjectEntries(
|
||||||
entries: [string, unknown][],
|
entries: [string, unknown][],
|
||||||
parser: (to_parse: unknown) => string | Record<string, unknown> = (id) =>
|
parser: (to_parse: unknown) => string | Record<string, unknown> = (id) => id.toString()
|
||||||
id.toString()
|
|
||||||
): [string, string][] {
|
): [string, string][] {
|
||||||
return flattenObject(Object.fromEntries(entries), parser);
|
return flattenObject(Object.fromEntries(entries), parser);
|
||||||
}
|
}
|
||||||
@ -236,8 +231,7 @@ export function maskString(
|
|||||||
max_chars_total: number
|
max_chars_total: number
|
||||||
): string {
|
): string {
|
||||||
const amount_of_chars_to_cut =
|
const amount_of_chars_to_cut =
|
||||||
str.length -
|
str.length - Math.min(str.length * max_fraction_remaining, max_chars_total);
|
||||||
Math.min(str.length * max_fraction_remaining, max_chars_total);
|
|
||||||
if (amount_of_chars_to_cut == 0) {
|
if (amount_of_chars_to_cut == 0) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@ -255,3 +249,7 @@ export function safeDecodeURIComponent(s: string) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeForClassname(string: string) {
|
||||||
|
return string.replace(/[^a-z0-9]/gi, '-');
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user