> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# Materialien Einwilligung zur Datenverarbeitung

> Vollständiges Beispiel für den Einwilligungs-Layer: Abschnitt AgreementCookies in der shop.config, HTML-Layer auf Bootstrap-Basis, Link zum erneuten Öffnen und ergänzendes CSS.

Diese Seite enthält ein vollständiges Beispiel für den Einwilligungs-Layer: den passenden Abschnitt in der `shop.config`, den HTML-Quellcode des Layers, einen Link zum erneuten Öffnen und das ergänzende CSS. Das Designbeispiel berücksichtigt das EuGH-Urteil vom 1. Oktober 2019, nach dem eine vorangekreuzte Checkbox keine wirksame Einwilligung ist.

Layer und Konfiguration können Sie im Mustershop [v8s-demoshop.websale.biz](https://v8s-demoshop.websale.biz/) ausprobieren.

Wegweiser: [Einwilligung zur Datenverarbeitung (Cookies)](https://dokumentation.websale.de/v8s/einstellungen/einwilligung-zur-datenverarbeitung-cookies.md) · Referenz: shop.config, Abschnitt AgreementCookies

## Die vier Bausteine

Der Layer besteht aus vier Teilen, die zusammengehören. Lassen Sie einen davon weg, funktioniert das Ganze nicht.

| Baustein | Liegt in | Aufgabe |
| - | - | - |
| Abschnitt `AgreementCookies` | `shop.config` | legt fest, welche Dienste es gibt und wie sie gruppiert sind |
| HTML-Layer | Template | zeigt die Checkboxen an und speichert die Auswahl |
| Link zum erneuten Öffnen | Datenschutzseite oder Fußzeile | erlaubt dem Besucher, seine Einwilligung zu widerrufen |
| Ergänzendes CSS | Stylesheet des Shops | positioniert den Layer am unteren Bildschirmrand |

> [!NOTE]
>
> Das Beispiel setzt Bootstrap 4.1.3 und jQuery 3.4.1 voraus. Außerdem braucht es das WEBSALE-Modul `ws-module-agreementcookies`, das die beiden Funktionen `ws_agreementcookies_set` und `ws_agreement_updatecheckboxes` mitbringt. Dieses Modul setzt als einziges WS-Modul nur jQuery voraus, nicht die Basis-Komponente.
>
> Das Pfeil-Symbol am Aufklapper stammt aus Font Awesome. Verwenden Sie dort ein anderes Symbol, wenn Ihr Shop Font Awesome nicht einbindet.

Referenz: [WEBSALE JavaScript-Bibliothek](https://dokumentation.websale.de/v8s/materialien/websale-javascript-bibliothek.md)

## Baustein 1: Der Abschnitt AgreementCookies

Das Beispiel definiert zehn Dienste und fasst sie in drei Gruppen zusammen: Komfortfunktionen, Shop-Optimierungen und Social Media mit Marketing. Alle zehn stehen auf `OptIn` mit der Vorbelegung `unchecked`, keine Checkbox ist also vorangekreuzt.

> [accordion] shop.config, Abschnitt AgreementCookies
>
> ```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ###################################################################################
> ###             Einwilligung zur Datenverarbeitung                              ###
> ###################################################################################
>
> <AgreementCookies>
>     Allow                   = yes
>
>     <+Entry>
>         ID                  = cookiebasket
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Warenkorb für nächsten Besuch speichern
>         Descr               =
>         ConsentGroupID      = comfort
>         +WEBSALEService     = CookieBasket
>     </+Entry>
>
>     <+Entry>
>         ID                  = welcomecookie
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Persönliche Begrüßung
>         Descr               =
>         ConsentGroupID      = comfort
>         +WEBSALEService     = WelcomeCookie
>     </+Entry>
>
>     <+Entry>
>         ID                  = googleMaps
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Anfahrtsplanung mit Google Maps
>         Descr               =
>         ConsentGroupID      = comfort
>     </+Entry>
>
>     <+Entry>
>         ID                  = youtube
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Produktvideos über YouTube
>         Descr               =
>         ConsentGroupID      = comfort
>     </+Entry>
>
>     <+Entry>
>         ID                  = google
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Google Analytics
>         Descr               =
>         ConsentGroupID      = optimize
>     </+Entry>
>
>     <+Entry>
>         ID                  = econda
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Econda
>         Descr               =
>         ConsentGroupID      = optimize
>     </+Entry>
>
>     <+Entry>
>         ID                  = googleTagManager
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Google Tag Manager
>         Descr               =
>         ConsentGroupID      = marketing
>     </+Entry>
>
>     <+Entry>
>         ID                  = instagram
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Instagram
>         Descr               =
>         ConsentGroupID      = marketing
>     </+Entry>
>
>     <+Entry>
>         ID                  = pinterest
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Pinterest
>         Descr               =
>         ConsentGroupID      = marketing
>     </+Entry>
>
>     <+Entry>
>         ID                  = twitter
>         Type                = OptIn
>         Preselection        = unchecked
>         Name                = Twitter
>         Descr               =
>         ConsentGroupID      = marketing
>     </+Entry>
>
>     <+ConsentGroupInfo>
>         ConsentGroupID      = comfort
>         Name                = Komfortfunktionen
>         Descr               = Wir möchten die Bedienung dieses Shops für Sie möglichst komfortabel gestalten.
>     </+ConsentGroupInfo>
>
>     <+ConsentGroupInfo>
>         ConsentGroupID      = optimize
>         Name                = Shop-Optimierungen
>         Descr               = Um den Shop zu optimieren möchten wir Dienste nutzen die Daten erheben und aggregieren und uns diese als statistische Übersicht zur Verfügung stellen.
>     </+ConsentGroupInfo>
>
>     <+ConsentGroupInfo>
>         ConsentGroupID      = marketing
>         Name                = Social Media / Marketing Funktionen
>         Descr               = Zu Werbezwecken dürfen Nutzungsdaten erhoben und mit statistischen Methoden zu einem Nutzerprofil zusammengefasst werden. Dies wird Interessengruppen zugeordnet, an die webseitenübergreifend Werbung ausgespielt wird.
>     </+ConsentGroupInfo>
> </AgreementCookies>
> ```

Zwei Dinge an diesem Beispiel sind leicht zu übersehen:

Die beiden ersten Einträge tragen ein `+WEBSALEService`, die übrigen acht nicht. Mit `+WEBSALEService` sagen Sie dem Shop, dass hinter dem Eintrag eine eingebaute WEBSALE-Funktion steckt. Der Shop schaltet diese Funktion dann selbst ab, wenn der Besucher nicht zustimmt. Die acht Einträge ohne diese Zeile sind reine Erklärungen gegenüber dem Besucher, das Abschalten der fremden Dienste müssen Sie im Template selbst erledigen.

Die Reihenfolge der Gruppen im Layer kommt aus der Reihenfolge der `<+ConsentGroupInfo>`-Blöcke, nicht aus der Reihenfolge der Dienste. Gruppen, für die kein `<+ConsentGroupInfo>` existiert, landen am Ende.

## Baustein 2: Der HTML-Layer

Der Layer zeigt im eingeklappten Zustand nur die Erklärung, eine Checkbox für alles und die Speichern-Schaltfläche. Erst der Aufklapper "Details" blendet die Gruppen mit ihren einzelnen Diensten ein.

> [accordion] HTML-Layer auf Basis von Bootstrap 4.1.3
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> <body>
> ...
> <div id="agreementPrivacy" class="py-3 px-sm-3"{!AC-AgreementNotMadeOrNeedsUpdate} style="display: none;"{/!AC-AgreementNotMadeOrNeedsUpdate}>
>    <div class="container-fluid">
>       <div class="row">
>          <div class="col">
>             <p class="lead mb-1">Einwilligung zur Datenverarbeitung</p>
>             <p class="small">Wir möchten Dienste von Drittanbietern nutzen, um den Shop und unsere Dienste zu verbessern
>                und optimal zu gestalten (Komfortfunktionen, Shop-Optimierung). Weiter wollen wir unsere Produkte bewerben (Social Media/Marketing).<br>
>                Dafür können Sie hier Ihre Einwilligung erteilen und jederzeit widerrufen. Weitere Informationen dazu finden Sie in unserer
>                <a href="~WS-LoadTpl(tpl_datenschutz.htm,Datenschutz)~" class="text-body"><u>Datenschutzerklärung</u></a>.</p>
>          </div>
>          <div class="col-12 mt-2">
>             <div class="row">
>                <div class="col-12 col-sm-6">
>                   <input type="checkbox" value="" onclick="ws_agreement_updatecheckboxes();" class="~AC-ConsentAll_CheckboxClass~" ~AC-ConsentAll_CheckboxIsChecked~>
>                   <b>Alle Dienste erlauben</b>
>                </div>
>                <div class="col-12 col-sm-6">
>                   <input type="button" onclick="ws_agreementcookies_set(); $('#agreementPrivacy').hide(); $('#agreementPrivacy').removeClass('isFixed');
>                      $('#cookieDetails').addClass('d-none'); $('#cookieDetailToggler').removeClass('open');" value="Einstellungen speichern" class="btn btn-block btn-success">
>                </div>
>             </div>
>             <div class="cursorPointer clearfix mt-2">
>                <span id="cookieDetailToggler" onclick="$('#cookieDetails').toggleClass('d-none'); $('#agreementPrivacy').toggleClass('isFixed'); $(this).toggleClass('open');">
>                   Details<i class="fa fa-angle-down ml-1" aria-hidden="true"></i>
>                </span>
>             </div>
>          </div>
>       </div>
>       <div id="cookieDetails" class="d-none">
>          ~DC-DynamicLoop1_set($AC-ConsentGroupList$)~
>          {@DC-DynamicLoop1}
>             {AC-ConsentGroup($DC-DynamicLoop1_data$)}
>                <div class="mb-3">
>                   <div class="mt-3 p-3 border border-secondary">
>                      <div class="font-weight-bold mb-1">
>                         <input type="checkbox" value="" onclick="ws_agreement_updatecheckboxes();" class="~AC-ConsentGroup_CheckboxClass~" ~AC-ConsentGroup_CheckboxIsChecked~>
>                         <b>Alle ~AC-ConsentGroup_Name~ erlauben</b>
>                      </div>
>                      <div class="small">~AC-ConsentGroup_Descr~</div>
>                      {AC-Entry}
>                         <div class="row mt-2 ml-2">
>                            {@AC-Entry($DC-DynamicLoop1_data$)}
>                               <div class="col-6 mb-2">
>                                  <input type="checkbox" value="" onclick="ws_agreement_updatecheckboxes();" class="~AC-Entry_CheckboxClass~" ~AC-Entry_CheckboxIsChecked~>
>                                  ~AC-Entry_Name~
>                               </div>
>                            {/@AC-Entry($DC-DynamicLoop1_data$)}
>                         </div>
>                      {/AC-Entry}
>                   </div>
>                </div>
>             {/AC-ConsentGroup($DC-DynamicLoop1_data$)}
>          {/@DC-DynamicLoop1}
>       </div>
>    </div>
> </div>
> ...
> </body>
> ```

### Wie der Layer aufgebaut ist

Die äußere Hülle entscheidet, ob der Layer überhaupt sichtbar ist:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<div id="agreementPrivacy" class="py-3 px-sm-3"{!AC-AgreementNotMadeOrNeedsUpdate} style="display: none;"{/!AC-AgreementNotMadeOrNeedsUpdate}>
```

Der Layer steht immer im Dokument. Nur wenn keine Einwilligung vorliegt oder ein neu hinzugekommener Dienst noch keine Entscheidung hat, fehlt das `display: none` und der Layer wird sichtbar. Deshalb genügt für den Link weiter unten ein einfaches `show()`.

Die Gruppenschleife läuft nicht über ein eigenes Schleifen-Tag, sondern über die dynamische Schleife:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
~DC-DynamicLoop1_set($AC-ConsentGroupList$)~
{@DC-DynamicLoop1}
   {AC-ConsentGroup($DC-DynamicLoop1_data$)}
      ...
   {/AC-ConsentGroup($DC-DynamicLoop1_data$)}
{/@DC-DynamicLoop1}
```

`~AC-ConsentGroupList~` liefert alle Gruppenkennungen als kommagetrennte Liste in der Reihenfolge aus der `shop.config`. `DC-DynamicLoop1` zerlegt diese Liste, und `AC-ConsentGroup` schaltet je Durchlauf auf die passende Gruppe um. Innerhalb davon läuft `{@AC-Entry(…)}` über die Dienste genau dieser Gruppe.

Die drei Ebenen von Checkboxen sind gleich aufgebaut. Jede bekommt ihre Klassen aus einem `_CheckboxClass`-Tag und ihren Haken aus einem `_CheckboxIsChecked`-Tag, und jede ruft beim Klick `ws_agreement_updatecheckboxes()` auf. Diese Funktion hält die drei Ebenen synchron: Setzt der Besucher den Haken bei einer Gruppe, bekommen alle Dienste darin ebenfalls einen Haken.

| Ebene | Klassen-Tag | Haken-Tag |
| - | - | - |
| alle Dienste | `AC-ConsentAll_CheckboxClass` | `AC-ConsentAll_CheckboxIsChecked` |
| eine Gruppe | `AC-ConsentGroup_CheckboxClass` | `AC-ConsentGroup_CheckboxIsChecked` |
| ein Dienst | `AC-Entry_CheckboxClass` | `AC-Entry_CheckboxIsChecked` |

> [!WARNING]
>
> Die Checkboxen tragen kein `name` und kein `value`. Gespeichert wird allein über die Klassen, die aus den `_CheckboxClass`-Tags kommen, und zwar erst beim Aufruf von `ws_agreementcookies_set()`. Entfernen Sie diese Klassen nicht und ergänzen Sie eigene nur zusätzlich.

## Baustein 3: Link zum erneuten Öffnen

Der Besucher muss seine Einwilligung jederzeit widerrufen können. Setzen Sie dafür beispielsweise auf der Datenschutzseite einen Link, der den Layer wieder einblendet.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<body>
   ...
   <div class="btn btn-primary mb-5" onclick="$('#agreementPrivacy').show();">Einwilligung zur Datenverarbeitung ändern</div>
   ...
</body>
```

Damit das funktioniert, muss der Layer aus Baustein 2 auf derselben Seite stehen. Binden Sie ihn deshalb über ein Include in alle Templates ein, nicht nur in die Startseite.

## Baustein 4: Individuelles CSS

Diese Anweisungen kommen zusätzlich zu den Standard-Klassen von Bootstrap. Sie heften den Layer an den unteren Bildschirmrand und beschränken ihn ab der Bootstrap-Breakpoint-Breite auf 500 Pixel.

```css theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
/* Zusätzlich zu den Standard-CSS-Anweisungen von Bootstrap v4.1.3 */

#agreementPrivacy {
    position: fixed;
    background: #dde5ee;
    z-index: 9999999;
    right: 0;
    bottom: 0;
    left: 0;
    box-shadow: 0 0 10px rgba(0,0,0,.1);
}

@media (min-width:576px) {
   #agreementPrivacy {
      right: auto;
      width: 500px;
   }
}

#agreementPrivacy.isFixed {
   height: 100%;
   overflow-y: auto;
}

#cookieDetailToggler.open > i.fa {
   transform: rotate(180deg);
}
```

Die Klasse `isFixed` setzt der Aufklapper selbst. Sie sorgt dafür, dass der aufgeklappte Layer den ganzen Bildschirm nutzt und bei vielen Diensten scrollbar wird. `#cookieDetailToggler.open > i.fa` dreht den Pfeil am Aufklapper um.

> [!NOTE]
>
> Die Klasse `cursorPointer` aus dem HTML-Beispiel ist in diesem CSS nicht enthalten. Ergänzen Sie sie selbst, sonst sieht der Besucher am Aufklapper keinen Mauszeiger:
>
> ```css theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> .cursorPointer { cursor: pointer; }
> ```

Der Layer bekommt `z-index: 9999999`. Prüfen Sie, dass kein anderes Element Ihres Shops darüber liegt, besonders klebende Kopfzeilen und Warenkorb-Vorschauen.

## Was das Beispiel nicht zeigt

Das Beispiel nutzt nur einen Teil der Möglichkeiten des Abschnitts. Die ausführliche Beschreibung steht auf der Referenzseite zur `shop.config`.

### Weitere Werte für Allow und Type

| Parameter | Wert | Bedeutung |
| - | - | - |
| `Allow` | `test` | das Verfahren ist nur im Testbetrieb des Shops aktiv, im Livebetrieb bleibt es aus |
| `Type` | `OptOutChecked` | ein gesetzter Haken bedeutet, dass der Besucher widerspricht |
| `Type` | `OptOutUnchecked` | ein fehlender Haken bedeutet, dass der Besucher widerspricht |

Bei beiden Opt-Out-Varianten gilt die Zustimmung als erteilt, solange der Besucher nichts entscheidet. Bei `OptIn` ist es umgekehrt.

> [!WARNING]
>
> Der Name des Cookies, in dem die Entscheidung steht, enthält den Wert von `Type`. Ändern Sie `Type` bei einem bestehenden Dienst, sucht der Shop ab dann ein anders benanntes Cookie, findet es nicht, und alle bisher erteilten Einwilligungen für diesen Dienst gelten als nicht vorhanden. Der Layer öffnet sich dann bei allen Besuchern erneut.
>
> Dasselbe gilt für eine Änderung an `ID`.

### Weitere WEBSALE-Dienste

Neben `CookieBasket` und `WelcomeCookie` aus dem Beispiel kennt der Shop vier weitere eingebaute Dienste, die er bei fehlender Zustimmung selbst abschaltet.

| Wert für `+WEBSALEService` | Schaltet ab |
| - | - |
| `CookieBasket` | das Speichern des Warenkorbs für den nächsten Besuch |
| `WelcomeCookie` | die persönliche Begrüßung |
| `RefererCookie` | das Merken der Herkunft des Besuchers |
| `AffiliateProgram` | die Affiliate-Programme |
| `DesignCookie` | das Design-Cookie, in dem der Shop Einstellungen zur Darstellung merkt |
| `TellAFriendCookie` | das Cookie der Weiterempfehlungsfunktion |

Ein Eintrag darf mehrere `+WEBSALEService`-Zeilen tragen, wenn eine Checkbox mehrere eingebaute Dienste abdecken soll.

### Weitere Einträge je Dienst

| Eintrag | Zweck |
| - | - |
| `Descr` | ein erklärender Text zum Dienst, abrufbar über das zugehörige AC-Tag |
| `ExternalServiceID` | eine Kennung für die Zuordnung zu einem externen Consent-Dienst |
| `iOSApp-AcceptViaTrackingFramework-List` | Liste für die Zustimmung über das Tracking-Framework der iOS-App |

### Zwei häufige Fehler in der Konfiguration

> [!WARNING]
>
> Eine `ConsentGroupID` darf nur Buchstaben, Ziffern, Bindestrich und Unterstrich enthalten. Bei jedem anderen Zeichen und bei einer leeren Angabe protokolliert der Shop einen kritischen Fehler und steckt den Dienst in eine Ersatzgruppe. Diese Ersatzgruppe hat keinen Namen und keine Beschreibung und fällt im Layer sofort auf.
>
> Ein `<+ConsentGroupInfo>`-Block, dessen `ConsentGroupID` von keinem Dienst verwendet wird, wird übergangen und protokolliert. Prüfen Sie nach jeder Umbenennung beide Stellen.

Die Vorbelegung aus `Preselection` greift nur, solange für den Dienst noch keine Entscheidung vorliegt. Sobald der Besucher einmal gespeichert hat, zählt seine Entscheidung, und eine spätere Änderung an `Preselection` ändert daran nichts.


## Related topics

- [Einwilligung zur Datenverarbeitung (Cookies)](https://dokumentation.websale.de/v8s/einstellungen/einwilligung-zur-datenverarbeitung-cookies.md)
- [Verwendete Cookies](https://dokumentation.websale.de/v8s/referenz/verwendete-cookies.md)
- [Materialien Tracking-Schnittstelle](https://dokumentation.websale.de/v8s/materialien/materialien-tracking-schnittstelle.md)
- [WEBSALE JavaScript-Bibliothek](https://dokumentation.websale.de/v8s/materialien/websale-javascript-bibliothek.md)
- [AC- (AgreementCookies)](https://dokumentation.websale.de/v8s/referenz/tags/ac-agreementcookies.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.