From 8e72759a7ea0ae637fb80bf1bb574e5326b721ef Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Mon, 7 Feb 2022 21:11:25 +0100 Subject: [PATCH] Create complex types for the survey response to prepare for template writing --- report-window/consent-problems.tsx | 40 -- report-window/data-preview.tsx | 69 --- report-window/domain-summary.tsx | 42 -- report-window/email-content.tsx | 10 + report-window/email-host-settings.scss | 2 - report-window/email-host-settings.tsx | 67 --- report-window/email-template-2.tsx | 486 ------------------ report-window/email-template-3-controls.tsx | 407 --------------- report-window/email-template-3.scss | 27 - report-window/email-template-3.tsx | 144 ------ report-window/email-template.tsx | 19 - report-window/generate-survey-questions.ts | 314 +++++++++++ report-window/generate-survey.ts | 0 .../legitimate-interest-problems.tsx | 65 --- report-window/parse-answers.ts | 83 +++ report-window/questions.tsx | 19 + report-window/raw-answers.ts | 57 ++ report-window/report-window.tsx | 55 +- report-window/use-survey.ts | 34 ++ sidebar/stolen-data-cluster.tsx | 2 +- 20 files changed, 552 insertions(+), 1390 deletions(-) delete mode 100644 report-window/consent-problems.tsx delete mode 100644 report-window/data-preview.tsx delete mode 100644 report-window/domain-summary.tsx create mode 100644 report-window/email-content.tsx delete mode 100644 report-window/email-host-settings.scss delete mode 100644 report-window/email-host-settings.tsx delete mode 100644 report-window/email-template-2.tsx delete mode 100644 report-window/email-template-3-controls.tsx delete mode 100644 report-window/email-template-3.scss delete mode 100644 report-window/email-template-3.tsx delete mode 100644 report-window/email-template.tsx create mode 100644 report-window/generate-survey-questions.ts create mode 100644 report-window/generate-survey.ts delete mode 100644 report-window/legitimate-interest-problems.tsx create mode 100644 report-window/parse-answers.ts create mode 100644 report-window/questions.tsx create mode 100644 report-window/raw-answers.ts create mode 100644 report-window/use-survey.ts diff --git a/report-window/consent-problems.tsx b/report-window/consent-problems.tsx deleted file mode 100644 index 4bdf37c..0000000 --- a/report-window/consent-problems.tsx +++ /dev/null @@ -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>; - * settings: EmailTemplate3Config['hosts_settings'][string]; - * pronoun: 0 | 1 | 2 | 3; - * }) { - * if (settings.legal_basis_type !== 'consent') { - * return ''; - * } - * const p = pronoun; - * return ( - *
- * {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.', - * }, - * })} - *
- * ); - * } */ diff --git a/report-window/data-preview.tsx b/report-window/data-preview.tsx deleted file mode 100644 index 3d84bfe..0000000 --- a/report-window/data-preview.tsx +++ /dev/null @@ -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 ( - - - - - - - - - - - {entries.map((entry) => ( - - - - - - - ))} - -
Adres docelowyŹródło danychTreść danychKlasyfikacja
{entry.request.shorthost} - {entry.source}:{entry.name} - - {entry.getValuePreview()} - {/* always gonna have - one key, because unwrapEntry is called above */} - - -
- ); -} diff --git a/report-window/domain-summary.tsx b/report-window/domain-summary.tsx deleted file mode 100644 index 0d0446a..0000000 --- a/report-window/domain-summary.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { RequestCluster } from '../request-cluster'; -import { Classifications, Sources } from '../stolen-data-entry'; - -const emailClassifications: Record = { - id: 'mój identyfikator internetowy', - history: 'część mojej historii przeglądania', - location: 'informacje na temat mojej lokalizacji geograficznej', -}; - -const emailSources: Record = { - 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 ( -
  • - Właścicielowi domeny {cluster.id} zostały - ujawnione:{' '} -
      - {cluster.representativeStolenData - .filter((entry) => entry.isMarked) - .map((entry) => ( -
    • - {emailClassifications[entry.classification]}{' '} - {emailSources[entry.source]} (nazwa:{' '} - {entry.name}, wartość:{' '} - {entry.getValuePreview()}) -
    • - ))} -
    -
  • - ); -} diff --git a/report-window/email-content.tsx b/report-window/email-content.tsx new file mode 100644 index 0000000..1c62798 --- /dev/null +++ b/report-window/email-content.tsx @@ -0,0 +1,10 @@ +import { ParsedAnswers } from './parse-answers'; + +export default function EmailContent({ answers }: { answers: ParsedAnswers }) { + return ( +
    +

    Email template

    +
    {JSON.stringify(answers, null, 3)}
    +
    + ); +} diff --git a/report-window/email-host-settings.scss b/report-window/email-host-settings.scss deleted file mode 100644 index 61d7e62..0000000 --- a/report-window/email-host-settings.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '../sidebar/fonts.scss'; -@import '../sidebar/colors.scss'; \ No newline at end of file diff --git a/report-window/email-host-settings.tsx b/report-window/email-host-settings.tsx deleted file mode 100644 index 971d2b2..0000000 --- a/report-window/email-host-settings.tsx +++ /dev/null @@ -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>, - [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> -) { - if (config.policy_readable == 'null') { - return ''; - } - const p = config.pronoun; - return ( - - ); -} diff --git a/report-window/email-template-2.tsx b/report-window/email-template-2.tsx deleted file mode 100644 index 0a88e5a..0000000 --- a/report-window/email-template-2.tsx +++ /dev/null @@ -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 ( - - {invisbleCharacter.repeat(6)} - ({children}) - {invisbleCharacter.repeat(6)} - - ); -} - -function Base64Image({ base64 }: { base64: string }) { - return ; -} - -export default function EmailTemplate2({ - entries, - clusters, -}: { - entries: StolenDataEntry[]; - clusters: Record; - version: number; -}): JSX.Element { - const [config, setConfig] = React.useState({ - 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 ( - <> - -
    -

    - Dzień dobry, w dniu {getDate()} odwiedziłem stronę{' '} - {visited_url}. -

    - {config.popup_type === 'none' ? ( -

    - 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 -

    - ) : config.popup_type == 'passive_cookie_banner' ? ( - <> -

    - Na stronie była widoczna informacja o plikach - Cookie.{' '} -

    -

    - -

    - - ) : ( - <> -

    - Ukazało mi się okienko z informacjami i pytaniami - dotyczącymi sposobów, w jaki strona przetwarza moje - dane osobowe.{' '} -

    -

    - -

    -

    - {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}”.` - : ''} -

    - - )} -

    - 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: -

    -
      - {Object.values(clusters) - .filter((cluster) => cluster.hasMarks()) - .map((cluster) => ( - - ))} -
    - {config.popup_action === 'ignored' ? ( -

    - Dane te zostały wysłane, zanim kliknąłem cokolwiek na - tej stronie. -

    - ) : config.popup_action === 'accepted' ? ( -

    - Dane te zostały wysłane po tym, jak kliknąłem przycisk „ - {config.popup_accept_all_text}” -

    - ) : ( - '' - )} -

    - W załączeniu przesyłam część zrzutów ekranu dokumentujących - fakt wysłania tych danych przez Państwa stronę.{' '} -

    -

    Podstawa prawna

    -

    - 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ć ujawniane 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 ujawniania moich - danych osobowych (pochodzących m.in. z Cookies) podmiotom - trzecim. -

    -

    - 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: -

    -
      -
    1. - „Zgoda” — osoba, której dane dotyczą wyraziła - zgodę na przetwarzanie swoich danych osobowych w jednym - lub większej liczbie określonych celów ( - Art. 6 pkt 1 lit. a)). -
    2. -
    3. - „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 ( - Art. 6 pkt 1 lit. b));{' '} -
    4. -
    5. - „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 (Art. 6 pkt 1 lit. f) - ); -
    6. -
    -

    - 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.{' '} - - oficjalnych wytycznych EROD dotyczących zgody na - mocy rozporządzenia 2016/679 - - ) - - ) : ( - '' - )} - . -

    -

    - 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.{' '} - - Wytyczne 8/2020 EROD dotyczące targetowania użytkowników - mediów społecznościowych - - , par. 49);.{' '} -

    -

    - 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:{' '} -

    -
      -
    1. - Administrator danych lub podmiot trzeci, któremu dane są - ujawniane musi{' '} - - faktycznie realizować dany konkretny uzasadniony - interes - {' '} - ( - - Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion - ID, C-40/17, ECLI:EU:C:2019:629 - - , pkt 95.) -
    2. -
    3. - Takie przetwarzanie danych jest{' '} - konieczne dla potrzeb wynikających z - danego uzasadnionego interesu ( - - Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion - ID, C-40/17, ECLI:EU:C:2019:629 - - , pkt 95.) -
    4. -
    5. - Wybrany uzasadniony interes musi mieć pierwszeństwo nad - prawami i wolnościami osoby, której dotyczą przetwarzane - dane ( - - Wyrok TSUE z dnia 29 lipca 2019 r. w sprawie Fashion - ID, C-40/17, ECLI:EU:C:2019:629 - - , pkt 95.) -
    6. -
    7. - Osoby, których dane dotyczą, powinny mieć możliwość - wyrażenia sprzeciwu wobec przetwarzania ich danych do - celów związanych z targetowaniem{' '} - przed rozpoczęciem przetwarzania (zob.{' '} - - Wytyczne 8/2020 EROD dotyczące targetowania - użytkowników mediów społecznościowych - - , par. 54); -
    8. -
    - {config.popup_action !== 'accepted' ? ( -

    - 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. -

    - ) : ( - '' - )} -

    - 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,{' '} - dla każdego z wymienionych podmiotów z osobna. - (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: -

    -
    -

    - W dniu {getDate()} strona {visited_url}: -

    -
      - {Object.values(clusters) - .filter((cluster) => cluster.hasMarks()) - .map((cluster) => ( -
    • - ujawniła pańskie dane w zakresie{' '} - - - {' '} - firmie{' '} - nazwa firmy, - która jest właścicielem domeny{' '} - {cluster.id} i swoją - politykę prywatności publikuje pod adresem{' '} - - adres URL polityki prywatności tej firmy - - . Podstawą prawną takiego przetwarzania - danych przez naszą stronę jest uzasadniony - interes:{' '} - - 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 - {' '} - realizowany przez{' '} - - kogo? jaki podmiot podejmuje wspomniane - działania lub jest beneficjentem - wspomnianych korzyści? - - . Ujawnienie{' '} - {' '} - temu podmiotowi przez naszą stronę było - konieczne dla potrzeb wynikających z tego - interesu, ponieważ - - uzasadnienie konieczności - - .
      -
    • - ))} -
    -
    -

    - 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. ) -

    -

    - 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ą{' '} - - notorycznie niezgodne z RODO i pozbawione szacunku dla - Użytkowników - - . -

    -

    - 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 - - . 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{' '} - - Referrer-Policy{' '} - - . -

    -
    - - ); -} diff --git a/report-window/email-template-3-controls.tsx b/report-window/email-template-3-controls.tsx deleted file mode 100644 index 7e5c2cf..0000000 --- a/report-window/email-template-3-controls.tsx +++ /dev/null @@ -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(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. - - Zobacz, jak to zrobić - `, - }, - { - 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
    Wczytywanie...
    ; - } - - return ; -} - -export function _EmailTemplate3Controls({ - config, - setConfig, -}: { - config: EmailTemplate3Config; - setConfig: React.Dispatch>; -}): JSX.Element { - return ( -
    - {emailHostSettings(config, setConfig)} -
    - - -
    - {config.popup_type !== 'none' ? ( -
    - - { - const popup_screenshot_base64 = await toBase64(e.target.files[0]); - setConfig((v) => ({ - ...v, - popup_screenshot_base64, - })); - }, - }} - /> -
    - ) : ( - '' - )} -
    - - -
    - {config.popup_action === 'closed' ? ( -
    - - - setConfig((v) => ({ - ...v, - popup_closed_how: e.target.value, - })) - } - /> -
    - ) : ( - '' - )} - {config.popup_type !== 'none' ? ( -
    - - setConfig((v) => ({ - ...v, - popup_mentions_passive_consent: e.target.checked, - })) - } - /> - -
    - ) : ( - '' - )} - {config.popup_mentions_passive_consent ? ( -
    - - - setConfig((v) => ({ - ...v, - popup_passive_consent_text: e.target.value, - })) - } - /> -
    - ) : ( - '' - )} -
    - ); -} diff --git a/report-window/email-template-3.scss b/report-window/email-template-3.scss deleted file mode 100644 index e31b8f5..0000000 --- a/report-window/email-template-3.scss +++ /dev/null @@ -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; -} diff --git a/report-window/email-template-3.tsx b/report-window/email-template-3.tsx deleted file mode 100644 index 052c905..0000000 --- a/report-window/email-template-3.tsx +++ /dev/null @@ -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; - version: number; -}): JSX.Element { - const all_host_ids = Array.from(new Set(entries.map((entry) => entry.request.shorthost))); - - const [config, setConfig] = React.useState({ - 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 ( -
    -
    - -
    -
    -
    -

    - 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 {PLUGIN_NAME} w celu zbadania, - jakie informacje o {['mnie', 'mnie', 'mnie', 'nas'][p]} wysyła ta strona do - podmiotów trzecich. -

    -

    - {['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{' '} -

      -
    • - - wysyłanie mojego identyfikatora internetowego [z Cookie] (value) - oraz części mojej historii przeglądania do właściciela domeny - (domain); -
    • -
    • - (...).
    • -
    -

    -

    - 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. -

    - {!['yes', 'null'].includes(config.policy_readable) ? ( -

    - {['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. - ) : ( - '' - )} -

    - ) : ( - '' - )} -

    - 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. -

    -
    -
    -
    - ); -} diff --git a/report-window/email-template.tsx b/report-window/email-template.tsx deleted file mode 100644 index ac8f656..0000000 --- a/report-window/email-template.tsx +++ /dev/null @@ -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; - version: number; -}) { - return ( -
    - -
    - ); -} diff --git a/report-window/generate-survey-questions.ts b/report-window/generate-survey-questions.ts new file mode 100644 index 0000000..7c40618 --- /dev/null +++ b/report-window/generate-survey-questions.ts @@ -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. + + Zobacz, jak to zrobić + `, + }, + { + 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), + ], + }; +} diff --git a/report-window/generate-survey.ts b/report-window/generate-survey.ts new file mode 100644 index 0000000..e69de29 diff --git a/report-window/legitimate-interest-problems.tsx b/report-window/legitimate-interest-problems.tsx deleted file mode 100644 index 8ba5040..0000000 --- a/report-window/legitimate-interest-problems.tsx +++ /dev/null @@ -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>; -}) { - if (settings.legal_basis_type !== 'legitimate_interest') { - return ''; - } - const [description, setDescription] = React.useState('marketing'); - return ( -
    -
    - 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.'], - }, - })} */} -
    - {settings.legitimate_interest_activity_specified === 'vague' ? ( -
    - Jak administrator opisał to, na czym polega uzasadniony interes w kontekście{' '} - {host_id}?{' '} - setDescription(e.target.value)} - /> - -
    - ) : ( - '' - )} -
    - ); -} diff --git a/report-window/parse-answers.ts b/report-window/parse-answers.ts new file mode 100644 index 0000000..c0a114c --- /dev/null +++ b/report-window/parse-answers.ts @@ -0,0 +1,83 @@ +import RawAnswers, { BasicRawAnswers, HostRawAnswers } from './raw-answers'; + +export type RecordValue = T extends Record ? 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 }; + +function parseHostAnswers( + raw_answers: Record +): Record { + const result: Record> = {}; + 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; +} + +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; +} diff --git a/report-window/questions.tsx b/report-window/questions.tsx new file mode 100644 index 0000000..3029a51 --- /dev/null +++ b/report-window/questions.tsx @@ -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
    Wczytywanie....
    ; + } + return ; +} diff --git a/report-window/raw-answers.ts b/report-window/raw-answers.ts new file mode 100644 index 0000000..26246bc --- /dev/null +++ b/report-window/raw-answers.ts @@ -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; diff --git a/report-window/report-window.tsx b/report-window/report-window.tsx index a12124d..692112d 100644 --- a/report-window/report-window.tsx +++ b/report-window/report-window.tsx @@ -1,40 +1,53 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { getMemory } from '../memory'; -import { StolenDataEntry } from '../stolen-data-entry'; -import { reduceConcat, useEmitter } from '../util'; -import EmailTemplate from './email-template'; +import { useEmitter } from '../util'; import './report-window.scss'; +import Questions from './questions'; +import EmailContent from './email-content'; +import { parseAnswers, ParsedAnswers } from './parse-answers'; function Report() { try { const origin = new URL(document.location.toString()).searchParams.get('origin'); - const [counter, setCounter] = useEmitter(getMemory()); + const [counter] = useEmitter(getMemory()); + const [answers, setAnswers] = React.useState(null); + const [mode, setMode] = React.useState('survey'); const clusters = getMemory().getClustersForOrigin(origin); - const [entries, setEntries] = React.useState([]); - 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 [entries, setEntries] = React.useState([]); */ + /* 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 = (
    - - + {mode === 'survey' ? ( + { + setAnswers(parseAnswers(answers)); + setMode('preview'); + }} + > + ) : ( + '' + )} + {mode === 'preview' ? : ''} {/* */}
    ); diff --git a/report-window/use-survey.ts b/report-window/use-survey.ts new file mode 100644 index 0000000..84a588f --- /dev/null +++ b/report-window/use-survey.ts @@ -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(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; +} diff --git a/sidebar/stolen-data-cluster.tsx b/sidebar/stolen-data-cluster.tsx index 46a87b5..70d072f 100644 --- a/sidebar/stolen-data-cluster.tsx +++ b/sidebar/stolen-data-cluster.tsx @@ -157,7 +157,7 @@ export default function StolenDataCluster({ - Znalezione ustawienia: + Wysłane dane: