DT Consent - Fassungsgeschichte ab 0.32.0
=========================================

Die juengsten Fassungen stehen in readme.txt unter "== Changelog ==".
Hierher wandert, was dort nicht mehr hinpasst: WordPress.org schneidet
diesen Abschnitt bei 5.000 Zeichen ab, und ein abgeschnittener Changelog
sieht aus wie einer, der endet.

Diese Datei ist DEUTSCH und bleibt es. Die readme.txt spricht Englisch,
weil sie die Verzeichnisseite ist; dies hier ist ein Archiv, kein
Verkaufstext. 60.000 Zeichen Fassungsgeschichte zu uebersetzen kostet
mehr, als es irgendjemandem nuetzt.

= 1.1.0 =
Diese Fassung stand bis 1.1.3 in der readme.txt und ist am 02.11.2026
hierher gewandert, damit 1.1.4 unter die 5.000-Zeichen-Grenze passt.
Wiedergegeben ist die Fassung von damals, unveraendert.

**One navigation instead of two.** Everything now lives under one menu entry
with one row of tabs. The guided setup used to be a separate menu item; it is
now the second tab, where you look first.

* **The "Analysis" tab is gone, and nothing with it.** It showed the same
  findings as steps 1 and 2 of the guided setup - the same code, twice. What
  it showed *in addition* - origin search, stylesheet findings, what cannot be
  held back, and cleanup - now sits under "Scan", next to the report that
  counts it. One place, not two.
* **Less text.** The explanations on the settings pages were cut back to what
  you need in order to decide.
* Old links keep working: an address with the removed tab lands on the
  overview instead of an empty page.
* For extensions: tabs can be appended through the `dt_consent_admin_tabs`
  filter, and content through `dt_consent_tab_<name>`. Both can only add -
  neither can change or remove a tab of this plugin.

**Fixed, from a review of a live installation:**

* **The walk-through no longer asks you to open a cart and a checkout** when
  there is no shop on this site.
* **The record archive no longer lists the same version twice.** Deactivating
  and reactivating the plugin wrote a second entry under the number that was
  already there.
* The German translation follows the shortened texts. Without this, two thirds
  of the German admin screens would have fallen back to English.
* Internal shorthand removed from screens - the citation next to a defect now
  names the law, not our own acceptance criterion.
* **Services are listed one line each and open on a click.** A service with
  something missing opens by itself and says so in its line; nothing you have
  entered is lost by collapsing a row.
* A service can be marked as setting no cookies, so the reminder about cookie
  names stops appearing where there is nothing to delete.

Older entries of this changelog have moved to `changelog.txt` in the plugin
folder.

= 1.0.17 =
Diese Fassung stand bis 1.1.2 in der readme.txt und ist am 02.11.2026
hierher gewandert, damit 1.1.3 unter die 5.000-Zeichen-Grenze passt.
Wiedergegeben ist die Fassung von damals, unveraendert.

**The check can now be started from outside.** Until now only the button on
the check page ran it. DT-PM Consent Plus 0.9.0 can trigger it through its
bridge, so the site owner's own AI assistant can look for third parties that
have no entry yet — and then enter them.

* **Nothing about the check itself changed.** It still reads only what the
  collector has already recorded: it fetches no page, and a page nobody has
  visited stays unexamined.
* **And it still changes nothing.** No service is added, altered or removed,
  your texts stay as they are, and **no consent becomes invalid** — the
  configuration fingerprint is untouched. A check is a report, not a setting.
* Without the paid edition nothing changes for you: the button is where it
  was, and it does what it did.

Older entries of this changelog have moved to `changelog.txt` in the plugin
folder.

= 1.0.16 =
Diese Fassung stand bis 1.1.1 in der readme.txt und ist am 31.10.2026
hierher gewandert, damit 1.1.2 unter die 5.000-Zeichen-Grenze passt.
Wiedergegeben ist die Fassung von damals, unveraendert.

**Your banner now speaks eight languages by itself.** French, Dutch, Italian,
Spanish, Polish and Romanian join the German and English that were already
there - nothing to switch on. Each cites the GDPR article the way that country
writes it.

* **This covers the texts we ship, not the texts you wrote.** Your own banner
  text stays in the language you typed it in; per-language texts of your own
  need DT-PM Consent Plus 0.8.0.
* "Accept all" and "Reject all" are translated as a pair and checked to stay
  close in length - a short "Accept" next to a long "Reject" is a nudge.
* New filter `dt_consent_eigene_texte` lets a plugin replace *your own* texts
  before the banner is rendered; the locked button labels stay out of reach.
  Without a plugin reaching in, your stored texts come back unchanged.
* **A language switch does not ask your visitors again.** The fingerprint uses
  your stored texts, not the displayed ones. Correct a translation and nobody
  is asked; change the text itself and everyone is, as before.
* The proof archive now records which format an entry was written in. Older
  entries stay readable and nothing is migrated.

= 1.0.15 =
**A new extension point, and it is the weightiest this plugin has.**
`dt_consent_vor_einwilligung_laden` lets another plugin lift the block for a
single script, so that it loads before your visitor has decided. **The default
is always no** - without a plugin reaching in, this plugin blocks exactly as
before. It exists for Google's "advanced consent mode", which DT-PM Consent
Plus 0.7.0 offers as a switch you have to turn on deliberately, with the
liability named. It is asked at both points where a script is blocked: the tag
and the inline snippet beside it.

= 1.0.14 =
**Two fixes in how the consent decision is announced.** Nothing you can see,
and everything if you use a tag manager.

* The decision is announced **before** your blocked scripts are released, not
  after. A released analytics snippet typically carries its configuration call
  right there, and that call used to run first.
* And it is announced on **every** page view, not only the one where your
  visitor clicks. Before this, a returning visitor's page views announced
  nothing at all.
* `dt-consent-change` stays where it was. New alongside it:
  `dt-consent-before-apply`, for anything that has to act before a release.

= 1.0.13 =
**The plugin is now called DT-PM Consent.** The addition "for WordPress" is
gone - the plugin directory does not allow that term in a plugin name. Your
cookie declaration carries the new name, because that entry names us as the
provider.

**Nothing else changed:** not the folder, not the text domain, not a setting,
not the cookie, not your records, and **no consent becomes invalid**. Your own
CSS keeps working - no class and no variable was renamed.

= 1.0.11 =
**The analysis page says what it does not know.** A result with nothing in it
used to look like an all-clear. It now says when the collector has never run,
how many pages have been seen, and marks scripts that only administrators
receive - a visitor never gets them.

**Adopting services says what it does.** An adoption that changes nothing says
so instead of reporting success, and ticked entries that have disappeared from
the recording are named. Adopt several services in one go: every single
adoption asks all your visitors again. Recordings can be deleted; findings
never expire by themselves.

Three service providers now carry their legal entity. And the accent colour
and font can be set by DT-PM Consent Plus - without that edition, nothing
changes.

Older versions, from 1.0.10 downwards, are in `changelog.txt` in the plugin
folder. They are not listed here because WordPress.org truncates this section
at 5,000 characters - and a truncated changelog looks like one that ends.
= 1.0.10 =
Diese Fassung stand bis 1.0.16 in der readme.txt und ist am 25.10.2026
hierher gewandert, damit 1.0.17 unter die 5.000-Zeichen-Grenze passt.
Wiedergegeben ist die gekuerzte Fassung von damals.

= 1.0.10 =
**The banner takes on the colour and the font of the website.** It reads the
link colour of the body text in the content area and uses it as its accent on
both level-one buttons. Nobody has to write CSS for this. Where a theme gives
no usable signal - four of nine measured, Twenty Twenty-Five among them - it
falls back to a checked standard, and the colour is adjusted until it reaches
4.5:1 against its own label rather than discarded.

**And every button of this plugin now keeps the colour it is given**, against
themes that raise the specificity of their own button rules. One consequence
if you style the banner yourself: a plain class no longer overrides it.

= 1.0.9 bis 1.0.0 =
Diese zehn Fassungen standen bis 1.0.16 in der readme.txt und sind am
24.10.2026 hierher gewandert - zusammen mit dem Block darunter, und aus
demselben Grund: WordPress.org schneidet den Changelog-Abschnitt bei 5.000
Zeichen ab. Wiedergegeben ist die GEKUERZTE Fassung von damals; der
vollstaendige Wortlaut steht in der Fassungsgeschichte des Repositoriums.

= 1.0.9 =
**The storage period of Google Analytics reads like a sentence again:** "2
years for _ga and _ga_<property ID>, 24 hours for _gid." The cookie names stay
in on purpose - the authorities' guidance asks for the period per cookie.
Services you have already adopted keep the text you adopted them under;
overwrite the field under "Services" if you want the new wording.

= 1.0.8 =
**The setup now says which service it means.** It used to report "1 service is
missing a mandatory detail" without naming it. An open point now names the
services and the link jumps straight to that row. A long list is shortened
after three names and says how many it left out. Templates that carry a known
caveat now say what the caveat means for you - once adopted, the details stand
in your banner as your own statement.

= 1.0.7 =
**A new name, and nothing else.** The plugin became DT-PM Consent for
WordPress. The folder, the settings, the services you declared and the
consents your visitors gave all stay exactly as they are - nothing to migrate,
and no visitor is asked again. Versions 1.0.5 and 1.0.6 carried the same
change and were never published.

= 1.0.4 =
**The German translation now arrives on older WordPress too.** Before version
6.7, WordPress never looks inside a plugin's own `languages` folder unless the
plugin says where it is - and this plugin had stopped saying so in 0.31.0.
Together with the redirect from 1.0.3 this covers both halves: where the files
are, and which one counts for a language variant. If you run WordPress 6.2 to
6.6 and wondered why the plugin spoke English while the rest of your site did
not, this is the version that fixes it.

= 1.0.3 =
**A German site that says "Sie" now gets the German plugin.** Such a site asks
WordPress for `de_DE_formal`; the plugin shipped `de_DE`, and WordPress does
not fall back from one to the other. A translation now counts for its whole
language - "Deutsch (Sie)", Austrian and Swiss German included. A language the
plugin does not ship stays untranslated and is never served another language's
texts. A translation you place in `wp-content/languages/plugins/` yourself
keeps precedence.

The description now names what "inline snippets" does **not** cover: a script
in your post content that builds a further script while the page runs cannot
be held back, because the address it assembles exists only in the browser.

= 1.0.2 =
**What the guided setup says when it knows nothing.** On a fresh install it
reported "finished" with no service declared and without a single scan.

* A scan that has never run is now its own open point, named first: without a
  scan the list of findings is not empty, it is **unknown**.
* With no service declared, the closing button no longer says "Finish setup" -
  finished is a statement, not a button label.

= 1.0.1 =
* The settings screen carries a header image. Nothing else changed: no new
  behaviour, no new request, no change to what is stored.

= 1.0.0 =
**The first stable version.** Nothing new is added here. The number says that
what the plugin does has been measured rather than intended: it blocks before
consent, loads after it, keeps a decision valid across an update, ships the
same HTML to every visitor, and lets a visitor refuse with one keystroke.

* Escape rejects everything while no decision has been made, and only closes
  the dialog once one exists. The banner says which of the two applies (WCAG
  2.2 AA, 2.1.2).
* The category checkboxes are 24 by 24 pixels. The banner passes axe-core
  without a violation.

= 0.38.0 bis 0.37.0 =
Diese sechs Fassungen standen bis 1.0.16 in der readme.txt und sind am
24.10.2026 hierher gewandert - derselbe Grund wie bei 0.36.0 und 0.33.0:
WordPress.org schneidet den Changelog-Abschnitt bei 5.000 Zeichen ab. Der
englische Wortlaut von damals steht unveraendert darunter.

= 0.38.0 =
**Four documented hooks for developers - and nothing the plugin holds back.**

Other plugins and themes can now add a service to the catalogue, receive every
scan report and every archived configuration, and add a CSS class to the
banner. Nothing changes for anyone who does not use them: the plugin never
asks whether somebody is listening, and never withholds anything if nobody is.
The banner content stays unfilterable on purpose.

= 0.37.4 =
**Saving without changing anything no longer invalidates consents through line
breaks.**

Browsers send the content of a text box with Windows line endings and drop a
line break directly at its start. A multi-line purpose, data transfer, inline
script or banner text that had been stored differently - by adopting a
template, for instance - therefore changed on the first save, even if nobody
touched it. The configuration version went up and every visitor was asked
again. Such text is now kept as stored when it differs only in its line
endings.

= 0.37.3 =
**Saving the service list without changing anything no longer switches off
blocking.**

A service without a category - or with a category that no longer exists - is
held back. Until now, opening the service list and clicking "Save" turned it
into "Necessary": the category selection had no option for its state, and the
browser sends the first option. After that the service loaded before consent,
without any visible change in the form. The selection now offers "No category
assigned" and keeps a deleted category as it is; a new row starts without a
category instead of "Necessary".

Such a service is now listed as an open point in step 4 of the guided setup and
in the check report: it is held back, but your visitors can never release it.

= 0.37.2 =
**"The provider states nothing about this" no longer turns into "yes".**

For eleven catalogue entries the provider says nothing about a transfer to a
third country. As soon as such a service was adopted - and again whenever it
was saved - the banner, the cookie declaration and the evidence export said
"Transfer to a third country: yes". The field has three states, and the
service editor offered a checkbox with two. It is now a selection: yes, no,
or the provider states nothing. A test sets every statement field of every
catalogue entry to "no information", sends it through adoption and saving, and
requires that it still says so in the declaration and the export.

**A storage period like `_ga_<property ID>` keeps its placeholder when saved.**
Until now the part in angle brackets was removed as if it were HTML.

= 0.37.1 =
**The cookie this plugin sets now appears in every cookie declaration.**

Until now `dt_consent` was only listed if you had added DT Consent to your
service list by hand - on a fresh install, nobody had. It now always appears
under "Necessary", taken from the plugin's own catalogue entry. It is not
stored in your service list, so it cannot be deleted by accident, and the
configuration version does not change: no consent is invalidated.

**The safeguard for a third-country transfer now survives adoption.** It was
shown for catalogue entries but lost as soon as a service was adopted or saved
- by all three steps on the way. It has its own field in the service editor
now, and a test sends every catalogue entry through adoption and saving and
compares what arrives, field by field.

= 0.37.0 =
**What a service is for and what it transmits are now two separate lines.**

Fourteen catalogue entries carried a second sentence in their purpose - "the
visitor's IP address is transmitted to Google", "the script is loaded from a
Stripe server". That sentence now has its own line, "Data transfer", directly
below the purpose: in the banner, in the cookie declaration and in the
evidence export. The wording is unchanged. Services you have already saved keep
their text as it is, and nothing about them changes the configuration version.

The reason is the evidence. A purpose is backed by the provider's own
description of its service; a data transfer is a statement about behaviour
and is backed by a document, the code or a measurement. Kept apart, thirteen
of the sixteen original purposes are now backed by the provider. Transfer
sentences without a source are listed as open, by name.

You can fill in the new line for your own services in the service editor.

= 0.36.0 =
Diese Fassung stand bis 0.37.4 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**The bundled catalogue now contains a template for DT Consent itself.**

It names the one cookie the plugin sets, `dt_consent`, what it is for, how long
it is kept, that it goes to no third party - and who provides the plugin: DT PM
GmbH, Rheine. Every statement in it is taken from this plugin's own code and
this readme, and a test checks that they still match.

**Every service in the catalogue now has to state its purpose.** "No
information" is no longer accepted there: a service without a purpose is
worthless in a cookie declaration. Where a purpose is backed by the provider's
own description of the service, the source is named; seven of the older
entries are backed that way, the rest are listed as open.

= 0.35.0 =
Diese Fassung stand bis 0.37.3 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**The banner texts now come with a live preview.**

On the "Banner" tab and in step 3 of the guided setup, the banner appears
below the form exactly as your visitors will see it. Change a text, leave the
field, and the preview follows. Nothing is saved until you click the button -
using the preview does not change the configuration version and does not
invalidate a single consent.

**It is not a look-alike.** The preview is drawn by the same code that draws
the banner on your site. That code used to read its texts straight from the
database, so it could only show what was already saved; it now takes what it
shows as its input, and the site and the preview are its two callers. A test
compares the preview with the published banner character by character, and a
counter-check deliberately makes them differ to prove the test notices.

The preview is an illustration, not a dialog: screen readers are not pulled
into a banner that is not really there, and its buttons cannot be focused.

= 0.34.0 =
Diese Fassung stand bis 0.37.1 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**A foreign font inside an inline style block is now held back.**

Until now the plugin stopped a `<link rel="stylesheet">` to a foreign host and
nothing else. Of the paths CSS can take to a foreign address, that was one of
four. This closes the second one: an `@import` that a theme or plugin puts into
an inline style block via `wp_add_inline_style()`.

That content belongs to WordPress, not to a foreign file - so the statement is
removed and the address is handed to the same mechanism that already holds a
stylesheet back. After consent it loads. The rest of the block stays untouched:
an inline block usually carries layout, and trading a violation for a broken
page is no trade.

**Measured before built.** Five widespread free themes - Twenty Twenty-Four,
Twenty Twenty-Five, Astra, OceanWP, Kadence - were checked on a throwaway
install: not one of them loads a Google font in its default state. They ship
their fonts themselves. A control that loads a font on all four paths confirmed
the apparatus can see each of them, and that the plugin stops exactly one of
the four.

The two remaining paths are named, not guessed: a foreign `@import` inside a
theme's own stylesheet, and a `<style>` block a theme writes straight into the
markup. Both are reported and not blocked - holding back the file that carries
your layout would break the page, and searching the whole output on every
request is a price this plugin does not charge.

= 0.33.5 =
Diese Fassung stand bis 0.34.0 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**Mollie is recognised in a real checkout. It was not.**

The seven payment templates of 0.33.0 were built from what the gateway
extensions can register. A measurement on a real classic checkout - home page,
a product, the cart, the checkout - showed what they actually register, and
Mollie loaded a script none of its patterns matched: it is served from your own
server and carries no provider name. Mollie would have shown up as "not
recognised" with empty fields.

Klarna loaded two scripts; only one matched by name, the other by its host.
Both are now listed by name, because a shop can serve the same script from its
own server.

Amazon Pay, Apple Pay and Google Pay loaded nothing on this path - they need a
merchant account. Their patterns stay, and their evidence now says in plain
words that a measurement produced no occurrence. What is enumerated and not
measured is a possibility, not a finding.

= 0.33.4 =
Diese Fassung stand bis 0.34.0 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**Where addresses carry no page names, the setup says so.**

The finding location is a path, and on WordPress's "plain" permalink setting
every page of a site has the same one. The list then read "seen on: /" for
everything - which looks like a measurement and is a statement about the
address format. It now names the cause instead, at the chooser, and
recommends nothing: changing the addresses of a running shop is not a side
step in a setup.

"Only the home page" stays a valid answer wherever addresses do carry page
names.

= 0.33.3 =
Diese Fassung stand bis 0.34.0 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**The setup now says on which pages it saw a script.**

Whether a service may run without consent depends on where its script loads -
the setup asked that and could not answer it. Every finding now carries the
pages it was seen on, right next to the chooser. It says "seen on" and never
"loads only on": the list comes from the pages you opened yourself, so another
one can load it too.

The first step asks for the walk that fills the list - home page, a product,
the cart, the checkout, in that order. On a real shop one of three PayPal
handles loaded only when the cart came first.

= 0.33.2 =
Diese Fassung stand bis 0.34.0 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**The reason for an open category now stands where the category is chosen.**

Seven payment templates deliberately leave the category open, because it
depends on where a script loads - in the checkout of a customer who chose that
method, or on every page of the shop. The template said so in its note, and the
note sat twenty lines above the chooser. In a run on 11 September it was not
read, and "Necessary" was picked: the one category that runs without consent.

The sentence now stands directly at the chooser, and only where the template
really leaves the category open.

**And the last step checks it.** A service filed under "Necessary" whose
template leaves the category open is listed as an open point. It is not
forbidden - it can be right - but it is a decision the template deliberately
did not make for you, and it should be seen. A service whose template does say
"necessary" is never listed.

= 0.33.1 =
Diese Fassung stand bis 0.34.0 in der readme.txt und ist von dort
hierher gewandert - derselbe Grund wie bei 0.33.0. Der englische
Wortlaut von damals steht unveraendert darunter.

**Four things the first dry run of the guided setup turned up.**

**The first step shows, it does not ask.** It rendered eighteen input fields -
tick boxes, a name field, a category chooser - that stood in no form at all.
Whatever you typed there was gone without a word when you clicked "Next". The
first step now has a plain list; the second one asks.

**It no longer claims that findings are assigned when none are.** "62 are
already assigned" stood on a fresh install with not a single service declared;
those 62 need no decision, which is a different thing. The sentence says so.

**Privacy policy and legal notice can be created from the guided setup.** On a
fresh WordPress the privacy page is an unpublished draft and a legal notice
does not exist at all - the setup asked for two links to pages that were not
there. It now creates and links them, **as drafts on purpose**: an empty
published privacy policy looks like a duty fulfilled and is worse than none.
There are three states now - missing, draft, published - and a link to a draft
counts as not done, because a visitor gets an error page.

**And the first step offers one way, not three.** The scan reminder no longer
appears inside the guided setup, and the collector sits below the result as an
offer for a missing cookie instead of beside it as a third route.

Also: PayPal is recognised in a real checkout. The detection patterns came from
the extension's source; a measurement on an actual shop showed that the two
handles it really loads were not among them.

= 0.33.0 =
Diese Fassung stand bis 0.33.2 in der readme.txt und ist von dort hierher
gewandert - derselbe Grund wie bei 0.32.0. Der englische Wortlaut von damals
steht unveraendert darunter.

**Guided setup: four steps from activation to a banner that names what your
site actually runs.**

Until now the plugin opened with six tabs and nothing said which one comes
first; setting it up took most of a working day. The guided setup asks four
questions instead — what runs on my site, what is each of them, what does the
banner say, is it right. You can stop at any point and pick up where you left
off, skip it entirely, and open it again later: it stays in the menu.

**The fourth step does not congratulate you.** It names what is still open — a
finding without a service, a missing privacy link, a service without a storage
period — and links to where each is settled.

**It is not a second way of saving.** Every form in the guided setup posts to
the same handler as the form on the matching tab. The same change made either
way produces the same fingerprint and the same archive entry, and finishing the
setup changes no setting and invalidates no consent.

**Seven payment services have joined the bundled catalogue**: PayPal, Klarna,
Apple Pay, Google Pay, Mollie, Stripe and Amazon Pay. Payment was the one
category missing entirely, and it is the first thing a shop finds. The
detection rules — script handles and hosts — were read out of the source of the
gateway extensions themselves, with file and line. What the providers do not
publish stays "no information": not one of the seven names its cookies or their
storage periods, and nothing is invented here. None of them preselects a
category either — whether a payment script may run without consent depends on
where it loads, and that decision belongs to you.

= 0.32.0 =
Diese Fassung stand bis 0.33.1 in der readme.txt und ist von dort hierher
gewandert - derselbe Grund wie bei 0.31.4 und 0.31.5. Der englische Wortlaut
von damals steht unveraendert darunter.

**The setup helper now says what is loaded from inside stylesheets.**

An independent review measured this against 0.31.5: an
`@import url('https://fonts.googleapis.com/…')` in a stylesheet keeps loading
although the admin area lists the service as blocked. Measured afterwards —
four of five CSS routes go through. The plugin stops a stylesheet by its
address, but it does not read what a stylesheet does inside.

That is still not stopped. But the admin area now says so: under "Stylesheets
that load from elsewhere" you find the file, the external server and the route
— `@import`, or font, or image. An operator who knows can act; one who is
shown "is blocked" cannot.

Read for this are the stylesheets enqueued through WordPress, once per file
and only when a logged-in administrator visits the site. No visitor pays
processing time for it.

**The limit is now stated in this file**, above with the others: a `<style>`
block a theme writes straight into the markup passes no WordPress interface
and is therefore not found either.

Also new is the second documented extension point: the filter
`dt_consent_installation_key` determines what counts as "the same
installation". **You need it when a site changes its address without anything
changing for the visitor** — moving from the staging to the live domain,
switching from `www` to the bare domain — and whoever sets it must know that
changing this value alone invalidates EVERY consent already given: every
visitor is asked again on their next visit. That is why the change belongs at
the beginning of a move and not after it.

Left alone, everything stays as before. For the most common suspected case,
multilingual sites with a language prefix in the path, it is explicitly not
needed: measured against Polylang 3.6.5 with two languages in subdirectories,
`/` and `/en/` return the same value.

= 0.31.5 =
Diese Fassung stand bis 0.33.0 in der readme.txt und ist von dort hierher
gewandert - derselbe Grund wie bei 0.31.4. Der englische Wortlaut von damals
steht unveraendert darunter.

**The bundled service descriptions are now available in English as well.**

Purpose, storage period and notes for the 16 prepared services were German
only in the source. They now follow the language of your website. The German
wording has been carried over unchanged.

**No reference to a national provision any more.** Where "Section 25 TDDDG"
used to stand, nothing stands now: storing information on a terminal device is
regulated separately in every country, and a default text naming the German
provision is wrong in the other 26 member states. The reference to Art. 6(1)(a)
GDPR remains — that applies directly throughout the Union.

**New: a field for your own legal reference.** Empty on delivery, found in the
banner tab. What you enter there appears below the consent text. Changing it
does not raise the configuration version — nobody is asked again because a
section number was corrected.

**Every entry in the evidence archive now names its language and the plugin
version.** The archive still records the texts as displayed; without the
language it would not be apparent in which one they were shown to the visitor.

= 0.31.4 =
Diese Fassung stand bis 0.33.0 in der readme.txt und ist von dort hierher
gewandert - der Abschnitt dort bleibt unter 5.000 Zeichen. Der englische
Wortlaut von damals steht unveraendert darunter.

**Addresses with umlauts and East Asian full stops are matched more
reliably.**

Three Unicode characters that a browser reads as a dot were previously
recognised only when the PHP extension "intl" was available on the server.
Without it, a script went through that was stopped with it. The plugin now
recognises these characters itself.

For addresses with umlauts (`münchen.de`, say) the extension is still needed
when your entry and the page write the address differently — once readable,
once as `xn--…`. If the extension is missing, the plugin now says so on the
affected service instead of silently blocking less.

**Category names no longer freeze.** As with the banner texts in 0.31.2, they
were fixed in the language of the day on the first save. From now on only what
differs from the default is stored.

**The evidence archive no longer stores display text.** The reason for an
entry is held as an identifier and translated only when displayed and
exported — an archive kept in two languages otherwise has to be read in two
languages. Existing entries stay unchanged: evidence that someone alters after
the fact is not evidence.

= 0.31.3 =
**Der Nachweis-Export ist lesbar - in jeder Sprache, die das Plugin spricht.**

In der Kopfzeile der CSV-Datei standen bisher die internen Bezeichner
(„dienst_zweck", „banner_ueberschrift"), und die Spalte „Drittland" enthielt
fest „ja" oder „nein". Beides ist jetzt übersetzt. Die Reihenfolge und die
Auswahl der Spalten ändern sich nicht - eine ältere und eine neue
Ausfertigung bleiben Spalte für Spalte vergleichbar.

Der Inhalt der Datei ist unverändert: Gespeichert werden weiterhin
Bezeichner und Werte, übersetzt wird erst beim Ausgeben.

= 0.31.2 =
**Ein Speichern ohne Änderung fragt die Besucher nicht mehr erneut.**

Wer im Backend auf „Speichern" klickte, ohne ein Feld anzufassen, entwertete
damit alle bereits erteilten Einwilligungen: Das Banner erschien bei jedem
Besucher erneut, und die Seite meldete dazu, die Konfiguration habe sich
inhaltlich geändert. Ursache war das Formular, das mit den Vorgabetexten
vorbelegt ist — abgeschickt wurden sie als eigene Eingabe des Betreibers.

Gespeichert wird ab jetzt nur noch, was von der Vorgabe abweicht.

**Der Bannertext folgt wieder der Sprache der Seite.** Solange die Vorgaben als
eigene Werte gespeichert waren, blieben sie in der Sprache stehen, in der
gespeichert wurde — die Übersetzung war wirkungslos. Ein selbst
geschriebener Text bleibt davon unberührt und wird nicht übersetzt.

**Beim Update wird einmalig aufgeräumt.** Gespeicherte Werte, die nur die
Vorgabe wiederholen, werden entfernt. Das ändert den Fingerabdruck der
Konfiguration ein einziges Mal — Ihre Besucher werden danach ein letztes Mal
gefragt. Danach nicht mehr, außer bei einer echten Änderung.

= 0.30.3 =
**Der Nachweis-Export ist auf PHP 8.4 und neuer vorbereitet.**

Ab PHP 8.4 gibt die Funktion, mit der die CSV-Datei geschrieben wird, eine
Warnung aus. Sie wäre in der heruntergeladenen Datei gelandet — also
ausgerechnet in dem Dokument, das einem Prüfer vorgelegt wird.

Behoben ist dabei zugleich ein zweiter, älterer Fehler: Ein Wert mit einem
Rückstrich unmittelbar vor einem Anführungszeichen kam aus der Datei anders
heraus, als er hineingegangen war. Betroffen war jede Angabe mit dieser
Zeichenfolge, etwa ein Dienstname oder eine Zweckangabe. Die Datei gibt jetzt
in jedem Fall genau das wieder, was gespeichert ist.

Das Archiv selbst war nie betroffen, nur seine Ausgabe als Datei. Eine bereits
heruntergeladene Ausfertigung lässt sich jederzeit neu erzeugen.

= 0.30.2 =
Die letzten drei Hinweise des Prüfwerkzeugs betrafen eine Ausnahme, die im
Text an der falschen Stelle endete. Damit steht die Sicherheitsprüfung auf
null und wird ab sofort erzwungen: Ein Rückfall lässt den Prüflauf
fehlschlagen, statt unbemerkt zu bleiben.

= 0.30.1 =
Nachbesserung zu 0.30.0. Drei Meldungen des Prüfwerkzeugs waren noch offen,
zwei davon durch die Korrektur selbst entstanden: Ein erklärender Kommentar
hatte sich zwischen den Übersetzerhinweis und die Textzeile geschoben, auf
die er sich bezieht. Damit sind alle Meldungen am Code abgearbeitet; offen
bleibt die Verpackung.

= 0.30.0 =
**Was der Prüfer sieht, stimmt.**

Der erste Lauf von Plugin Check und PHPCS mit dem WordPress-Standard hat
dreiundzwanzig Meldungen ergeben. Behoben ist alles, was am Code liegt:

* Acht Zählwerte in Meldungen der Verwaltung gehen jetzt sichtbar durch eine
  Ganzzahl-Umwandlung. Sie waren nie gefährlich - der Wert stand längst als
  Zahl fest -, aber ein Prüfer sieht das der Zeile nicht an.
* Zwei Meldungen bekommen den Zahlenplatzhalter auch in der Einzahl. Sprachen
  mit mehreren Pluralformen benutzen die Einzahlform auch für 21 und 101;
  ohne Platzhalter stünde dort "Ein Dienst", wo 21 gemeint sind.
* Drei Meldungen sind sachlich falsch und tragen jetzt eine Begründung im
  Code: der inerte Platzhalter, der ein angemeldetes Skript ersetzt und
  deshalb gerade nicht angemeldet werden darf; der Speicherpuffer des
  CSV-Exports, der kein Dateisystemzugriff ist; und die Liste der fremden
  Server, die das Plugin blockiert - ein Werkzeug, das fremde Adressen
  benennt, wird für eines gehalten, das von ihnen lädt.

= 0.29.0 =
**Das Banner kann übersetzt werden, ohne dass die Einwilligung verfällt.**

Bis 0.28.0 waren die beiden wichtigsten Schaltflächen des Banners
unveränderlich deutsch beschriftet - auf einer niederländischen oder
französischen Website standen dort deutsche Wörter, und der Betreiber konnte
nichts dagegen tun. Die Sperre gegen umbenannte Ablehnung soll ihn ja gerade
daran hindern. Sie bleibt und bedeutet jetzt das Richtige: nicht "die Wörter
sind deutsch", sondern "Sie wählen die Wörter nicht". Welche es in einer
Sprache sind, entscheidet die Übersetzung des Plugins.

Sämtliche Texte an den Besucher und die Namen der Standardkategorien gehen
jetzt durch die Übersetzung. Übersetzungsdateien liegen noch keine bei; ohne
sie erscheint alles wie bisher.

**Der Vorgabetext nennt keine deutsche Vorschrift mehr.** § 25 Abs. 1 TDDDG
ist deutsches Recht - jeder EU-Staat hat Artikel 5 Abs. 3 der
ePrivacy-Richtlinie eigenständig umgesetzt. Die DSGVO bleibt genannt: Sie ist
eine Verordnung und gilt in der gesamten EU und im EWR unmittelbar. Wer die
Vorschrift seines Landes nennen möchte, trägt sie im Backend ein; das Feld
sagt, wie sie in Deutschland heißt.

**Wichtig für bestehende Installationen:** Der Fingerabdruck der
Konfiguration ging bisher über die Kategorienamen. Da die jetzt übersetzbar
sind, hätte jede Sprache einen anderen Fingerabdruck ergeben - ein Besucher
wäre bei jedem Sprachwechsel erneut gefragt worden. Der Fingerabdruck beruht
deshalb nur noch auf dem, was den Umfang der Einwilligung ausmacht. **Diese
Umstellung entwertet einmalig alle zuvor erteilten Einwilligungen.**

= 0.28.0 =
**Das Backend sagt jetzt, wann Aufhalten die zweitbeste Loesung ist.**

Bei Diensten, die man auch selbst ausliefern kann - allen voran **Google
Fonts** - steht in der Dienstzeile ein Hinweis darauf, samt ausklappbarer
Kurzanleitung in vier Schritten. Der Hinweis ist bewusst *kein* Mangel: Die
Blockade greift ja. Er sagt nur, dass es einen Weg gibt, der ohne Einwilligung
auskommt und ohne den Sprung im Layout, den jede aufgehaltene Schrift macht.

Neu ist ausserdem der erste dokumentierte Erweiterungspunkt des Plugins: Der
Filter `dt_consent_howto_url` traegt die Adresse einer ausfuehrlichen
Anleitung nach. Solange dort nichts hinterlegt ist, zeigt das Backend nur die
mitgelieferte Kurzanleitung - ein Link auf eine Seite, die es noch nicht gibt,
faellt niemandem auf, bis ein Kunde darauf klickt.

Automatisches Herunterladen und lokales Einbinden fremder Dateien macht das
Plugin ausdruecklich **nicht**. Das waere ein Eingriff in fremde Theme-Dateien
mit einem Weg zurueck, den niemand kennt; und die Richtlinien von
WordPress.org verbieten Netzverkehr ohne Zutun des Betreibers.

= 0.27.0 =
**Stylesheets und Bilder werden jetzt ebenfalls aufgehalten.**

Bis 0.26.0 hing das Plugin an fuenf Haken - und an keinem davon kam ein
`<link rel="stylesheet">` oder ein `<img>` vorbei. Betroffen war vor allem
**Google Fonts**, der in Deutschland meistabgemahnte Fall: Der Dienst stand in
der Liste, im Banner und in der Cookie-Erklaerung, und die Schrift lud bei
jedem Aufruf ohne Einwilligung. 0.26.0 hat das ehrlich gemeldet; hier ist es
behoben.

Neu aufgehalten werden:

* Stylesheets, die ein Theme oder Plugin ueber `wp_enqueue_style()` einbindet
* `<link href>` und `<img src>` im Beitragstext - damit faellt auch das
  Zaehlpixel, das als Bild von einem Fremdhost kommt
* `srcset` eines aufgehaltenen Bildes; ein Bild ohne `src`, aber mit `srcset`
  laedt genauso

**Entschieden wird ueber den HOST, nicht ueber das Handle.** Genau diese
Verwechslung war die Ursache: Die Handles der Google-Fonts-Signatur landeten
im Feld fuer Skript-Handles und sahen nach einem gueltigen Weg aus - es waren
aber Style-Handles, die der Skript-Haken nie zu sehen bekommt.

*Was das sichtbar macht.* Bis zur Einwilligung erscheint die Seite in der
Ersatzschrift, danach springt das Layout. Das ist der Preis, und er ist
ehrlicher als eine Schrift, die ohne Einwilligung laedt. Der bessere Weg
steht unveraendert in der Erkennungsdatenbank: **die Schriften lokal
ausliefern** - dann braucht es dafuer gar keine Einwilligung.

*Was weiterhin nicht erfasst ist:* `@import` in einem `<style>`-Block. Das
verlangte, CSS zu lesen, und eine halbe CSS-Erkennung waere genau die Sorte
Streichliste, die dieses Projekt schon zweimal eingeholt hat. Ebenso ein
Inline-Schnipsel, das zur Laufzeit selbst ein Skript baut - das ist ohne
Ausfuehrung nicht zu beurteilen.

**Und unveraendert die Grenze, die das ganze Plugin traegt:** Aufgehalten wird
nur, was einem eingerichteten Dienst gehoert. Ein fremdes Stylesheet oder Bild
bleibt unangetastet - alles andere loeschte jedem Shop die Produktbilder.

= 0.26.0 =
**Zwei Befunde aus dem Browsernachweis auf einer echten Installation.**

*Ein Dienst, den das Plugin nicht aufhalten kann, sagt das jetzt.* Google
Fonts wird als Stylesheet eingebunden - und an keinem der fuenf Haken des
Plugins kommt ein `<link rel="stylesheet">` vorbei. Der uebernommene Dienst
sah trotzdem aus wie jeder andere: in der Liste, im Banner, in der
Cookie-Erklaerung, **und die Maengelpruefung bestaetigte ihn als in
Ordnung**. Die Schrift lud bei jedem Aufruf ohne Einwilligung.

Solche Dienste werden jetzt als nicht aufhaltbar gemeldet, mit dem Rat, der
ohnehin der bessere ist: die Schriften lokal ausliefern - dann braucht es
dafuer gar keine Einwilligung. **Die Blockade von Stylesheets ist damit nicht
gebaut, sondern die falsche Zusicherung abgestellt.** Eine offene Luecke, die
benannt ist, ist besser als ein Haken, der nicht haelt.

*Nach dem Widerruf wird beim naechsten Laden nachgeraeumt.* Bisher wurde
ausschliesslich im Moment einer Entscheidung geloescht. Ein Tracking-Cookie,
das das noch laufende Drittanbieter-Skript in der Luecke zwischen Loeschen und
Neuladen zurueckschreibt, blieb danach dauerhaft liegen - gemessen an
`_ga_<Property-ID>`, dessen Zeitstempel nach dem Loeschzeitpunkt lag. Bei
jedem Seitenaufruf werden jetzt die Cookies der nicht erteilten Kategorien
entfernt. Pflichtkategorien bleiben unberuehrt.

= 0.25.0 =
**Ein Host-Eintrag mit Stern blockiert jetzt auch wirklich.**

`*.youtube.com` ist die Schreibweise, die man fuer "samt Subdomains" am
ehesten waehlt - und sie blockierte gar nichts. Weder war
"www.youtube.com" gleich "*.youtube.com", noch traf das Suffix
".*.youtube.com" je zu. Der Eintrag stand im Backend, die Uebersicht meldete
"blockt etwas", und das iframe lud trotzdem.

Das ist derselbe Fehler wie beim fuehrenden Punkt, den 0.15.2 behoben hat -
nur mit einem anderen Zeichen. Der Stern wird jetzt abgeraeumt: Aus
`*.youtube.com` wird `youtube.com`, und Subdomains sind ohnehin
eingeschlossen. Gilt fuer iframe-Hosts und Skript-Hosts gleichermassen.

Ein einzelner `*` bleibt wirkungslos. "Blockiere alles" waere keine
Kurzschreibweise, sondern eine Fussangel - sie legte die Website lahm. Der
betroffene Dienst wird stattdessen als einer gemeldet, der nichts aufhaelt.

= 0.24.0 =
**Der Widerruf erreicht jetzt auch Cookies mit eigenem Pfad, und ein
Inline-Snippet verschwindet nicht mehr, wenn sein Text `<script` enthaelt.**

*Der Widerruf.* Geloescht wurde bisher nur unter `path=/`. Ein Cookie, das mit
einem eigenen Pfad gesetzt wurde - `path=/shop`, wie es Shop- und
Kassen-Skripte tun -, blieb davon unberuehrt: Der Browser fuehrt (Name,
Domain, Pfad) als eigenen Eintrag, und ein Loeschversuch auf "/" legt nur
einen zweiten, sofort abgelaufenen Eintrag daneben. Der Widerruf hielt damit
nicht, was Art. 7 Abs. 3 DSGVO verlangt. Jetzt wird zusaetzlich unter jedem
Pfad der aufgerufenen Seite geloescht.

**Was auch damit nicht geht:** Ein Cookie mit `HttpOnly` ist fuer JavaScript
unsichtbar und unloeschbar - das ist der Zweck des Merkmals. Serverseitig
loeschen kaeme nicht in Frage, weil der Server das Einwilligungs-Cookie
nirgends liest; genau das haelt die Cache-Invariante. Tracking-Dienste setzen
ihre Cookies fast immer per JavaScript und damit ohne HttpOnly - die Ausnahme
wird hier benannt statt verschwiegen.

*Die Inline-Snippets.* `type="text/plain"` macht ein Inline-Skript inert -
ausser sein Text enthaelt `<script` oder `</script`. Dann verweigert der
WordPress-Kern (ab 7.0) die Textzuweisung, und `wp_get_inline_script_tag()`
gibt eine leere Zeichenkette zurueck: **Das Tag wird nicht inert, es wird nie
ausgegeben.** Betroffen sind reale Muster - `document.write`-Loader und
JSON-LD-Vorlagen. Der Dienst laedt nach der Einwilligung, findet seine
Einrichtung nicht vor und sendet nichts, ohne dass sich etwas meldet.

Solche Snippets tragen ihren Inhalt jetzt base64-kodiert in einem Attribut,
so wie es das Plugin fuer eigene Snippets schon immer getan hat. Harmlose
Snippets gehen unveraendert den bisherigen Weg.

Und der Pruefaufbau bildet den Kern jetzt vollstaendig nach: Er reicht dem
Filter den Inhalt mit und stellt die Weigerung des Kerns nach. Ohne das waere
kein Nachweis moeglich gewesen - dieselbe Sorte Luecke, an der schon der
Befund aus dem ersten Einbau haing.

= 0.23.0 =
**Ein Skript, das jemand direkt in einen Beitrag geklebt hat, wird jetzt auch
aufgehalten.**

*Die Luecke.* Die Blockade arbeitete bisher ausschliesslich handle-basiert -
sie sieht, was ein Plugin oder Theme ueber `wp_enqueue_script()` anmeldet. Ein
Analytics- oder Pixel-Snippet, das ein Redakteur in einen Custom-HTML-Block
einfuegt, hat kein Handle und stand damit vollstaendig ausserhalb: Der
Drittanbieter-Request ging vor jeder Einwilligung ab, waehrend das Backend
"blockiert" meldete. Das ist die haeufigste Einbauart ueberhaupt.

*Was jetzt passiert.* Ein Dienst hat ein neues Feld "Skript-Hosts". Ein
`<script src>` im Beitragstext, dessen Host darauf passt, wird genauso inert
gemacht wie ein angemeldetes Skript und nach der Einwilligung freigegeben. Die
Erkennungsdatenbank fuellt das Feld beim Uebernehmen selbst - sie kannte die
Hosts schon, gab sie aber bisher nicht weiter.

*Was ausdruecklich NICHT passiert.* Ein Skript, dessen Host keinem
eingerichteten Dienst gehoert, bleibt unangetastet. Alles Fremde zu blockieren
waere die schaerfere Regel und die schlechtere: Sie traefe
Kartenbibliotheken, Schriftarten, Formular-Einbettungen und Zahlungsdienste,
die eine Seite zum Funktionieren braucht. **Die Reichweitengrenze bleibt
damit bestehen: Was in keiner Diensteliste steht, wird nicht aufgehalten.**
Der Sammler findet solche Skripte und meldet sie.

Ebenfalls nicht erfasst: ein Inline-`<script>` ohne `src` im Beitragstext. Ob
es laedt, ist ohne Ausfuehrung nicht zu beurteilen.

*Und eine Korrektur, die alle kuenftigen Erweiterungen betrifft.* Der
Konfigurationshash, aus dem der Fingerabdruck im Besucher-Cookie entsteht,
ging bisher ueber die Dienste einschliesslich leerer Felder. **Jedes neue
Dienstfeld haette damit beim ersten Speichern nach dem Update saemtliche
erteilten Einwilligungen entwertet** - fuer ein Feld, das niemand ausgefuellt
hat. Leere Werte gehen jetzt nicht mehr in den Hash ein; ein gefuelltes Feld
aendert ihn weiterhin.

= 0.22.0 =
**Ein Dienst ohne Kategorie wird nicht mehr stillschweigend durchgelassen.**
Gefunden von einer unabhaengigen Pruefung der Verwaltungsoberflaeche.

*Der Fehler.* Ein Dienst, dem die Kategorie fehlt, wurde vom Plugin wie
"technisch notwendig" behandelt: Sein Skript lief, sein iframe lud, er stand in
keinem Banner, in keiner Cookie-Erklaerung, und seine Cookies wurden beim
Widerruf nicht geloescht. Die Verwaltung meldete ihn dabei durchgehend als
erledigt - Uebersicht ohne Maengel, Einrichtungshilfe ohne offene Zeile,
Pruefbericht "alles zugeordnet".

Das traf nicht nur Tippfehler: **Google Tag Manager, reCAPTCHA und Cloudflare
werden absichtlich ohne Kategorie ausgeliefert**, weil die Zweckentscheidung
beim Betreiber liegen muss. Wer sie uebernahm, ohne die Kategorie zu waehlen,
bekam eine wirkungslose Attrappe - und beim Tag Manager haengt daran praktisch
alles Weitere.

*Was jetzt passiert.* Eine fehlende oder unbekannte Kategorie ist ein
Konfigurationsfehler und wird als solcher behandelt: Der Dienst wird
aufgehalten, in der Dienstliste als mangelhaft gemeldet, und die
Einrichtungshilfe uebernimmt ihn gar nicht erst - die Zeile bleibt offen stehen
und sagt, was fehlt. Bei der Cookie-Zuordnung gilt jetzt ausserdem die im
Formular gewaehlte Kategorie; sie wurde dort bisher ignoriert.

*Und die Meldung stimmt wieder.* Das Umschalten von "Einwilligung auch auf
Subdomains" entwertet jede gespeicherte Einwilligung - der Geltungsbereich
steckt im Fingerabdruck. Die Seite meldete danach woertlich "bestehende
Einwilligungen bleiben gueltig", und im Nachweisarchiv stand nichts davon. Der
Vorgang bekommt jetzt eine eigene Konfigurationsversion und einen eigenen
Archiveintrag.

= 0.21.0 =
**Eingebettete Inhalte gehen nicht mehr dauerhaft verloren, und fremde
Markierungen im Beitragstext wirken nicht mehr.** Beides gefunden von einer
unabhaengigen Pruefung, die gegen einen echten Browser gemessen hat.

*Ein blockierter Inhalt kehrt zurueck.* Ein iframe, das seinen Inhalt im
`srcdoc` traegt statt ueber eine Adresse, wurde blockiert - und war danach
weg. Das `srcdoc` fiel der Positivliste zum Opfer, und was gespeichert wurde,
war eine leere Adresse. Der Besucher bekam einen Platzhalter mit einer
Schaltflaeche, die nichts tat, und danach eine leere Flaeche ohne Erklaerung.
**Auch wer ausdruecklich zustimmte, sah den Inhalt nie wieder.** Der Inhalt
wird jetzt gesichert und bei der Freigabe zeichengetreu zurueckgegeben. Eine
Schaltflaeche erscheint nur noch, wenn sie etwas bewirken kann.

Damit ist auch ein stiller Inhaltstausch behoben: Trug ein iframe eine eigene
Adresse UND ein einwilligungspflichtiges `srcdoc`, erschien nach der Freigabe
die Adresse statt des `srcdoc`.

*Markierungen aus dem Beitragstext wirken nicht mehr.* Das Plugin markiert
blockierte Elemente mit `data-dtc-`-Attributen. Standen solche Attribute von
Hand im Beitragstext, hat das Frontend ihnen geglaubt - **und beim Klick auf
"Alle ablehnen" den Drittanbieter geladen**, weil die notwendige Kategorie
auch bei einer Ablehnung freigegeben ist. Der Filter raeumt solche Attribute
jetzt ab, und das Frontend gibt grundsaetzlich nichts mehr ueber eine
Kategorie frei, die keine Einwilligung verlangt.

*Weiterhin offen und ausdruecklich benannt:* Ein `srcdoc`, das seine Inhalte
ueber `link`, `srcset`, `@import` oder `meta refresh` laedt, wird noch nicht
erkannt. Und die Notbremse - der Rueckweg ohne den WordPress-Kernleser - kennt
`srcdoc` nicht; ein darueber laufendes iframe verliert seinen Inhalt weiterhin
ersatzlos.

= 0.20.0 =
**Beigestellte Inline-Schnipsel gehen nicht mehr verloren.** Gefunden beim
ersten Einbau in einer echten Installation.

WordPress liefert zu einem Skript oft drei Teile: ein Vorskript, das Skript
selbst und ein Nachskript. Bei Google Analytics steht der Einrichtungsaufruf
`gtag('config', ...)` praktisch immer im Nachskript. Bis 0.19.0 hat das Plugin
beim Blockieren alle drei Teile durch einen Platzhalter ersetzt - und damit
Vor- und Nachskript nicht zurueckgestellt, sondern **verworfen**.

**Die Blockade war davon nicht betroffen** - vor der Einwilligung lud
weiterhin nichts. Der Schaden lag auf der anderen Seite: Nach der Einwilligung
lud der Dienst, fand seinen eigenen Einrichtungsaufruf aber nicht mehr vor.
Google Analytics war damit geladen, aber nicht eingerichtet, und hat **nichts
gemessen, ohne dass sich etwas gemeldet haette**.

Jetzt wird nur noch das Skript-Tag selbst unschaedlich gemacht; Vorskript,
Nachskript und Uebersetzungen bleiben stehen und werden nach der Einwilligung
mit freigegeben - in der Reihenfolge Vorskript, Skript, Nachskript.

**Wenn Sie Analytics oder ein Tag-Management einsetzen, pruefen Sie bitte nach
diesem Update, ob wieder Zahlen ankommen.** Es kann sein, dass seit dem
Einsatz des Plugins keine kamen.

Neu ist ausserdem ein Nachweis, der die FREIGABE prueft - bisher haben alle
Nachweise nur den blockierten Zustand gemessen. Deshalb ist der Fehler so
lange unentdeckt geblieben.

= 0.19.0 =
**Die iframe-Erkennung ist neu gebaut.** Sieben Wege, auf denen eine
Einbettung bisher an der Blockierung vorbeikam, sind zu.

**Voraussetzung ist jetzt WordPress 6.2** (bisher 6.0). Das Plugin benutzt den
HTML-Leser des WordPress-Kerns, den es seit dieser Fassung gibt. Bisher las es
Einbettungen mit einem Textmuster - und der Browser liest HTML nun einmal
anders als ein Textmuster. Gemessen an 22 Faellen gegen einen echten Browser:

* Textmuster (bis 0.18.1): **7 Einbettungen ungeblockt**
* HTML-Leser des Kerns:    **0**

Betroffen waren unter anderem: `<IFRAME>` in Grossbuchstaben, `src` ohne
Anfuehrungszeichen, ein `>` im Titelattribut, ein auskommentierter alter
Einbettungscode, HTML-Sonderzeichen in der Adresse - und `srcdoc`, das das
Plugin bis dahin gar nicht kannte.

**Nach diesem Update wird mehr blockiert als vorher. Bitte pruefen Sie beide
Richtungen:** ob Einbettungen jetzt blockiert werden, die vorher durchliefen -
und ob noch alles laedt, was laden soll.

Zwei Aenderungen, die Ihnen auffallen koennen:

* **Der Platzhalter entsteht jetzt im Browser.** Der Server macht die
  Einbettung nur noch unschaedlich; Text und Schaltflaeche baut das
  Plugin-Skript. Das schliesst eine ganze Fehlerklasse: Der Server gibt kein
  Attribut der urspruenglichen Einbettung mehr aus und kann deshalb auch
  keines mehr durchlassen. **Ohne JavaScript sehen Besucher eine leere
  Flaeche statt eines Hinweistextes** - ohne JavaScript koennen sie ohnehin
  nicht einwilligen.
* **Eine Einbettung mit `srcdoc`, in der ein Skript steht, wird blockiert** -
  auch wenn kein Drittanbieter erkennbar ist. Ein Skript kann beim Ausfuehren
  alles nachladen; das Plugin kann das nicht beurteilen und laesst es im
  Zweifel nicht zu. Ein `srcdoc` ohne aeussere Bezuege bleibt unberuehrt.

Der Text des Platzhalters ist neu unter den Bannertexten einstellbar.

= 0.18.1 =
Reine Wartung, keine Verhaltensaenderung: eine ueberfluessige Typpruefung
entfernt, die die statische Pruefung beanstandet hat.

= 0.18.0 =
**Zwei Befunde eines fuenften unabhaengigen Pruefdurchgangs - beide Folgen der
Korrekturen aus 0.16.0 und 0.17.0.** Beide gemessen.

* **Zweimaliges Umschalten der Subdomain-Geltung machte einen Widerruf
  rueckgaengig.** Ein Cookie auf der uebergeordneten Domain wird beim
  Abschalten zwar nicht mehr beachtet, aber auch nicht geloescht - loeschen
  wuerde die Entscheidung der Nachbar-Website mittreffen. Es wurde deshalb
  wieder gueltig, sobald die Einstellung zurueckkam: mit dem alten, breiteren
  Umfang, obwohl der Besucher in der Zwischenzeit widerrufen hatte. Jede
  Aenderung an dieser Einstellung erhoeht jetzt eine Zahl, die **nur steigt** -
  eine Einstellung kann zurueckkehren, diese Zahl nicht.
  **Folge: Jedes Umschalten der Subdomain-Geltung entwertet die gespeicherten
  Einwilligungen.** Das war bis 0.17.0 bereits so, nur unbeabsichtigt und ohne
  dass es irgendwo stand.
* **Ein veralteter oder fremder Eintrag hielt den Widerruf auf.** Seit 0.17.0
  loescht der erste Widerruf die vorgefundenen Cookies - aber nur, wenn gar
  kein Einwilligungs-Cookie lesbar war. Ein **einziger** lesbarer Eintrag
  genuegte, um das abzuschalten: ein alter Eintrag des Besuchers nach einer
  Aenderung Ihrer Dienstliste, ein Eintrag aus einer aelteren Plugin-Fassung,
  oder einer von einer Nachbar-Website auf gemeinsamer Domain. Massgeblich ist
  jetzt, ob ein **eigener gueltiger** Zustand vorliegt.

**Berichtigung zu 0.17.0:** Der dortige Satz *„Beruecksichtigt werden jetzt alle
lesbaren Eintraege zusammen"* traf zwar auf die Ermittlung zu, nicht aber auf
das Ergebnis - siehe den zweiten Punkt oben. Erst mit 0.18.0 stimmt er.

= 0.17.0 =
**Fuenf Befunde aus zwei weiteren unabhaengigen Pruefdurchgaengen** - drei davon
Fehler, die erst mit 0.16.0 entstanden sind. Alle fuenf sind gemessen worden.

* **Die Installationskennung konnte sich unter Last aendern.** Sie wurde bei
  Bedarf in der Datenbank angelegt - und zwei gleichzeitige Seitenaufrufe
  konnten dabei verschiedene Werte erzeugen. Im schlimmsten Fall (Datenbank
  nicht schreibbar) bekam jeder einzelne Seitenaufruf eine neue Kennung, und
  **keine Einwilligung hielt je** - ohne dass irgendetwas darauf hingewiesen
  haette. Die Kennung wird jetzt aus der Adresse Ihrer Website berechnet statt
  gespeichert. **Folge: Ein Domainumzug entwertet die gespeicherten
  Einwilligungen; ein Wechsel von http auf https nicht.**
* **Nach dem Abschalten der Subdomain-Geltung galt die alte Einwilligung
  weiter.** Das Cookie auf der uebergeordneten Domain blieb liegen und gewann
  gegen jede spaetere Entscheidung - ein einziger Klick im Backend machte den
  Widerruf fuer alle bisherigen Zustimmer dauerhaft unwirksam. Der gespeicherte
  Zustand traegt jetzt seinen Geltungsbereich mit sich.
* **Ein fremdes Cookie konnte den Widerruf komplett aufhalten.** Lag auf einer
  gemeinsamen uebergeordneten Domain ein Einwilligungs-Cookie einer anderen
  Website, wurde beim Widerruf gar nichts geloescht und die Seite nicht neu
  geladen. Beruecksichtigt werden jetzt alle lesbaren Eintraege zusammen.
* **Ein gescheitertes Speichern blieb unbemerkt.** Nimmt der Browser das
  Cookie nicht an - Cookie-Limit, Datenschutz-Erweiterung -, schloss sich das
  Banner trotzdem und die Dienste liefen an, obwohl nichts gespeichert war.
  **Jetzt wird nach jedem Speichern nachgesehen; scheitert es, wird nichts
  freigeschaltet und der Besucher erfaehrt es.**
* **Der erste Widerruf loeschte nichts.** Wird das Plugin auf einer laufenden
  Website installiert, tragen die Besucher bereits Cookies. Beim ersten "Alle
  ablehnen" blieben sie alle stehen, weil noch keine frueher erteilte
  Einwilligung bekannt war. Jetzt gilt: Was der Besucher nicht ausgewaehlt hat,
  wird geloescht.

**Bestehende Einwilligungen verfallen mit diesem Update einmalig.** Ihre
Besucher werden einmal erneut gefragt.

= 0.16.0 =
**Fuenf Befunde zum gespeicherten Consent-Zustand.** Sie stammen aus einem
unabhaengigen Pruefdurchgang, alle fuenf sind gemessen worden.

* **Zwei Installationen hatten denselben Pruefwert.** Er wurde bisher allein
  aus der Konfiguration gebildet - und zwei frisch installierte Instanzen sind
  gleich konfiguriert. Teilten sie sich das Cookie ueber eine gemeinsame
  uebergeordnete Domain, galt eine auf der einen erteilte Einwilligung auf der
  anderen als erteilt, obwohl deren Banner nie zu sehen war. Der Pruefwert
  enthaelt jetzt eine Kennung, die diese Installation von jeder anderen
  unterscheidet. **Folge: Bestehende Einwilligungen verfallen mit diesem
  Update einmalig, Ihre Besucher werden einmal erneut gefragt.**
* **Jede Entscheidung loeschte die Entscheidung des Nachbarn.** Vor dem
  Speichern wurde bis zur uebergeordneten Domain aufgeraeumt - auch bei
  ausgeschalteter Subdomain-Geltung. Auf geteilten Domains bekamen dadurch die
  Besucher der Nachbarseite das Banner erneut. Geloescht wird jetzt nur noch
  dort, wo das eigene Cookie liegen kann.
* **Nach einer Konfigurationsaenderung loeschte "Alle ablehnen" nichts.** Der
  frueher erteilte Umfang war nicht mehr lesbar, also stand keine Kategorie als
  widerrufen fest, also blieb jedes Tracking-Cookie stehen - ausgerechnet in
  dem Moment, in dem der Besucher das Gegenteil erwartet.
* **Die Selbstpruefung nach dem Schreiben war zu grob.** Sie verglich nur den
  Zeitstempel; ein fremdes Cookie mit demselben konnte einen fehlgeschlagenen
  Schreibvorgang verdecken. Verglichen wird jetzt der vollstaendige Wert.
* **Der Zeitstempel war sekundengenau** und damit ein
  Wiedererkennungsmerkmal, das bei geteilter Domain an jede Subdomain
  mitging. Er wird jetzt auf den Tag abgerundet. Alle Besucher, die am selben
  Tag dasselbe entscheiden, haben damit ein identisches Cookie.

Neu unter "Haeufige Fragen": zwei benannte Grenzen zu geteilten Domains, die
sich technisch nicht schliessen lassen und deshalb dort stehen, statt
verschwiegen zu werden.

= 0.15.3 =
**Fuenf Wege, auf denen ein eingebettetes iframe am Blocking vorbeikam.** Alle
fuenf sind gemessen worden, alle waren von Anfang an da. In jedem Fall blieb
das iframe unveraendert stehen, lud beim Seitenaufbau und uebertrug die
IP-Adresse des Besuchers an den Anbieter - ohne Einwilligung.

* **"data-src" gewann gegen "src".** Steht ein data-src vor dem echten src,
  wurde dessen Adresse geprueft. Lazy-Loading-Plugins erzeugen genau dieses
  Muster.
* **Der Platzhalter trug die Fremdadresse weiter.** Auch wenn richtig
  blockiert wurde, ueberlebte ein data-src im Platzhalter - ein Lazy-Loader
  konnte daraus wieder ein ladendes src machen. Der Platzhalter enthaelt jetzt
  keine data-Attribute des Originals mehr.
* **Ein iframe ohne Schlusstag wurde nicht erfasst.** Ungueltiges HTML, das
  jeder Browser klaglos laedt.
* **Ein ">" in einem Attributwert** (etwa `title="5 > 3"`) beendete die Suche,
  bevor das src erreicht war.
* **Backslash-Adressen** las das Plugin anders als der Browser.
  `https:/\www.youtube.com/...` ergab in PHP gar keinen Host, im Browser den
  echten. Adressen werden jetzt gelesen, wie ein Browser sie liest.

**Wer das Plugin einsetzt, sollte nach diesem Update pruefen, ob Einbettungen
jetzt blockiert werden, die vorher durchliefen** - und ob umgekehrt alles noch
laedt, was laden soll.

= 0.15.2 =
**Zwei Fehler in der Korrektur von 0.15.1.** Ein zweiter, unabhaengiger
Pruefdurchgang hat sie gefunden - beide in derselben Funktion, beide gemessen.

**Ein "@" im Pfad oder in der Abfrage wurde als Anmeldeangabe gelesen.** Wer
".../maps/@52.5,13.4" eintrug, bekam kein Blocking. Und ein Eintrag wie
"...?a=b@com" schrumpfte auf "com" zusammen und traf damit **jeden** .com-Host -
auch im Sammler und im Pruefbericht, deren Anbieternamen ins Nachweisarchiv
gehen. Die Reihenfolge stimmt jetzt: erst der Pfad, dann die Anmeldedaten.

**Unicode-Punkte am Ende eines Hostnamens blieben stehen.** "www.youtube.com。"
(U+3002 und zwei weitere Varianten) wurde nicht als "www.youtube.com" erkannt
und deshalb nicht blockiert - der Browser laedt so ein iframe klaglos. Ursache:
Die Punkte werden erst bei der Umwandlung nach ASCII zu einem gewoehnlichen
Punkt, und bis dahin war schon abgeschnitten worden.

Beide Faelle sind jetzt als Tests verankert, zusammen mit einer Gegenprobe,
dass die Erkennung dadurch nicht loechrig wird.

= 0.15.1 =
Drei Korrekturen aus einer unabhaengigen Sicherheitspruefung. **Die erste
betrifft das Kernversprechen des Plugins und wiegt schwer.**

**Ein Host mit fuehrendem Punkt wurde nicht blockiert.** Wer bei einem Dienst
".youtube.com" eintrug - die uebliche Schreibweise fuer "samt Subdomains" -,
bekam gar kein Blocking: Das iframe lud beim Seitenaufbau, die IP-Adresse des
Besuchers ging ohne Einwilligung an den Anbieter, und das Backend meldete
trotzdem, der Dienst werde aufgehalten. Dasselbe galt fuer vollstaendige
Adressen ("https://www.youtube.com/embed/..."), fuer Angaben mit Pfad und mit
Portnummer.

**Wer das Plugin einsetzt, sollte nach diesem Update seine Dienste-Liste
durchsehen** - und danach mit den Entwicklerwerkzeugen des Browsers pruefen,
dass vor der Einwilligung wirklich kein Request an einen Drittanbieter abgeht.

**Das Cookie-Muster "*" loeschte jedes Cookie.** Wer es eintrug, um "alle
Cookies dieses Dienstes" zu meinen, loeschte beim Widerruf auch das
Consent-Cookie selbst - die Entscheidung wurde dann nie gespeichert, das Banner
erschien bei jedem Seitenaufruf - sowie die WordPress-Anmeldung des Betreibers.
Solche Muster werden jetzt gar nicht erst gespeichert, und das Consent-Cookie
sowie die Anmelde-Cookies von WordPress sind vom Loeschen ausgenommen.

**Zwei gleichnamige Cookies, und das falsche gewann.** Seit 0.15.0 kann das
Consent-Cookie auf einer uebergeordneten Domain liegen. Lag daneben eines aus
einer anderen Installation, verwarf das Frontend die eigene, gueltige
Einwilligung, statt weiterzusuchen. Jetzt wird weitergesucht; angenommen wird
weiterhin nur ein Cookie mit passendem Fingerabdruck.

= 0.15.0 =
**Die Einwilligung kann jetzt auf Subdomains gelten.** Bisher galt sie nur auf
genau dem Hostnamen, auf dem sie erteilt wurde - auch example.de und
www.example.de waren getrennt.

Neu im Tab "Banner": "Geltung auf Subdomains". Standardmaessig **aus**. Legt
WordPress selbst eine Cookie-Domain fest - etwa in einem Multisite-Netzwerk
mit Subdomains -, gilt sie ohne diese Einstellung.

Das Plugin leitet die Domain **nicht** selbst her. Aus "shop.example.co.uk"
waeren die letzten zwei Bestandteile "co.uk", und darauf laesst kein Browser
ein Cookie zu - es wuerde dann gar keines gesetzt und die Entscheidung nie
gespeichert. Deshalb: ein Vorschlag im Formular, den Sie pruefen. Lehnt der
Browser die Domain trotzdem ab, merkt das Plugin es und speichert wie bisher
nur fuer diesen Host. Eine falsche Angabe kostet die geteilte Einwilligung,
nie die Einwilligung selbst.

**Alle bisher erteilten Einwilligungen werden mit diesem Update ungueltig; das
Banner erscheint bei allen Besuchern einmal erneut.** Grund: Im Cookie steht
jetzt ein Fingerabdruck der Konfiguration statt einer fortlaufenden Nummer.
Eine Nummer ist nur innerhalb einer Installation eindeutig - wird das Cookie
ueber Subdomains geteilt, koennte eine zweite WordPress-Installation bei
derselben Nummer stehen und die Einwilligung mit ganz anderen Diensten
einloesen. Der Fingerabdruck schliesst das aus.

Die lesbare Versionsnummer bleibt, wo sie gebraucht wird: in der Uebersicht, im
Nachweisarchiv und in der ersten Spalte der Exportdatei. Das Archivformat
aendert sich nicht.

= 0.14.1 =
Keine neue Funktion. Erster Lauf der statischen Pruefung (PHPStan) ueber den
gesamten Quelltext; sieben Befunde, alle abgearbeitet, keine Baseline.

Der einzige mit Aussenwirkung: Das Nachweisarchiv wurde mit dem Autoload-Wert
'no' gespeichert. Diese Zeichenkette ist seit WordPress 6.7 ausdruecklich
veraltet; der Parameter erwartet einen Wahrheitswert. Jetzt false. Am
Verhalten aendert sich nichts - die Aenderung nimmt eine kuenftige
Deprecation-Meldung vorweg.

Die uebrigen sechs: eine falsche Typangabe in einem Docblock, zwei nie
erreichte Ausgaben in Vorlagen, eine ueberfluessige isset-Abfrage, eine
fehlende Umwandlung nach Zeichenkette und ein Dokumentationsfehler in
WordPress selbst, der als begruendete Ausnahme eingetragen ist.

= 0.14.0 =
Drei Ergaenzungen aus Auftrag FAB-010, alle drei aus demselben Grund: Das
Plugin hatte die Daten bereits, gab sie aber nicht heraus - oder pruefte
nicht, ob sie vollstaendig sind.

* **Cookie-Erklaerung fuer die Datenschutzerklaerung.** Neu sind der Shortcode
  `[dt_consent_cookies]` und ein Block "Cookie-Erklaerung" fuer den
  Blockeditor. Sie geben die Dienste nach Kategorie gruppiert aus, je Dienst
  Anbieter, Zweck, Speicherdauer, Drittland und Cookienamen - einschliesslich
  der notwendigen Kategorie, ueber die zwar nicht eingewilligt, aber
  informiert werden muss (Art. 13 DSGVO).
* Die Erklaerung zieht ihre Angaben aus **derselben Quelle wie das Banner**.
  Sie hat keine eigenen Texte und keinen zweiten Pflegeort - Banner und
  Datenschutzerklaerung koennen damit nicht auseinanderlaufen. Der Block wird
  serverseitig gerendert; eine im Beitrag eingefrorene Kopie waere genau das
  Auseinanderlaufen, das hier verhindert werden soll.
* Fehlt zu einem Dienst eine Angabe, entfaellt die Zeile. Es erscheint kein
  Platzhalter - eine erfundene Angabe stuende in der Datenschutzerklaerung
  oeffentlich.
* **Nachweisarchiv als CSV herunterladen.** Eine Zeile je Dienst und
  Konfigurationsstand, mit Zeitpunkt, Anlass, Konfigurations-Hash und
  Bannertexten. Die Datei bescheinigt nichts - sie haelt fest, was wann
  eingestellt war.
* Was in die Datei geht, steht als abschliessende Liste fest, nicht als
  Filter, der Unerwuenschtes entfernt. Ein Feld, das im Archiv steht, aber
  nicht auf der Liste, erreicht die Datei nicht. Geprueft wird das an einem
  untergeschobenen Feld.
* Zellen, die mit =, +, - oder @ beginnen, werden entschaerft. Excel und
  LibreOffice lesen solche Werte als Formel; die Datei wird von einem Pruefer
  geoeffnet, und die Werte darin hat der Betreiber eingegeben.
* **Zwei neue Warnungen im Backend.** Ein Dienst ohne Cookienamen ist NICHT
  WIDERRUFBAR - der Widerruf loescht Cookies anhand genau dieser Namen. Ein
  Dienst ohne Skript-Handle, iframe-Host und Inline-Skript haelt nichts auf.
  Beides sah bisher aus wie ein vollstaendiger Eintrag.
* Beide Warnungen gelten nur fuer einwilligungspflichtige Kategorien und
  zaehlen auf der Uebersichtsseite als offener Punkt mit - sonst meldet sie
  "nichts offen", waehrend ein Dienst nicht widerrufbar ist.

= 0.13.3 =
* Das Banner ist auf Ebene 1 deutlich kompakter. Bisher stand jeder Dienst als
  eigener aufklappbarer Block; bei zwoelf Diensten wurde daraus eine Wand, und
  die Ablehnen-Schaltflaeche rutschte nach unten. Genau das beschreibt die
  Orientierungshilfe der Datenschutzkonferenz in Rn. 58 und 60 als Mangel.
* Neu: Je Kategorie stehen Name und Anbieter aller Dienste in EINER Zeile
  ("Dienste: Google Analytics 4 (Google Ireland Limited), ..."). Zweck,
  Speicherdauer, Drittland und Cookienamen klappen mit einem Griff auf - fuer
  die ganze Kategorie statt je Dienst.
* **Die Ebene bleibt dieselbe.** Es gibt weiterhin keinen zweiten Dialog: Alle
  Angaben stehen im ausgelieferten HTML auf der Ebene, auf der die
  Einwilligung eingeholt wird (Rn. 38). Wer stattdessen ein schmales Banner
  zeigt und die Angaben erst hinter "Anpassen" nachliefert, erfuellt das
  nicht.
* Die Angabe "wer greift zu" (Rn. 37) ist weiterhin OHNE jede Interaktion
  sichtbar - nur eben in einer Zeile statt in zwoelf Bloecken.
* Der Test dazu prueft das jetzt auch wirklich: Er hat bisher toContainText
  benutzt, das auch verborgenen Text sieht, und waere gruen geblieben, wenn
  der Anbieter hinter eine Klappe gewandert waere.

= 0.13.2 =
* Das Dienste-Formular ist in zwei beschriftete Gruppen geteilt: "Was im
  Banner steht" (Name, Kategorie, Anbieter, Speicherdauer, Zweck, Drittland)
  und "Was blockiert und beim Widerruf geloescht wird" (Cookienamen,
  Skript-Handles, iframe-Hosts, Inline-Skript).
* Bisher standen zehn Felder gleichrangig nebeneinander. Ob ein Feld im Banner
  landet oder die Technik steuert, ob es leer bleiben darf oder nicht, war der
  Anordnung nicht anzusehen.
* Die zweite Gruppe startet zugeklappt, solange nichts darin steht - mit dem
  Vermerk "noch nichts eingetragen: Dieser Dienst wird nur genannt, nicht
  aufgehalten". Sobald etwas darin steht, ist sie offen.
* Entfernt wurde KEIN Feld. Jedes wird verbraucht: vier sind Pflichtangaben
  nach der Orientierungshilfe der Datenschutzkonferenz (Rn. 37), vier sind
  Voraussetzung fuer gefordertes Verhalten - Blockieren nach Paragraph 25
  Abs. 1 TDDDG und Loeschung beim Widerruf.
* Unter den Cookienamen steht jetzt, wozu sie gebraucht werden: Ohne sie kann
  der Widerruf die Cookies nicht loeschen. Das ist die schwerere der beiden
  Luecken und war im Backend bisher nicht zu sehen.

= 0.13.1 =
* Die Pruefung auf fehlende Pflichtangaben verlangt bei NOTWENDIGEN Diensten
  keinen Anbieter und keine Speicherdauer mehr. Sie verlangt damit weniger,
  nicht mehr.
* Der Grund: Anbieter, Zweck, Funktionsdauer und Drittzugriff stammen aus der
  Orientierungshilfe der Datenschutzkonferenz (OH Digitale Dienste V1.2,
  Rn. 37/38) und beziehen sich auf die Ebene, auf der eine EINWILLIGUNG
  eingeholt wird. Fuer Dienste nach Paragraph 25 Abs. 2 Nr. 2 TDDDG wird
  keine eingeholt - dort greift die Anforderung nicht.
* Der Zweck bleibt auch bei notwendigen Diensten gefordert: Sie stehen im
  Banner mit aufgefuehrt und sind dort nicht abwaehlbar; ohne Zweckangabe
  erfaehrt der Leser nicht, warum. Die Informationspflicht aus Art. 13 DSGVO
  haengt ausserdem nicht an der Einwilligung.
* Der Befund dahinter: Ein Anwender hatte das Cookie eines anderen
  Consent-Werkzeugs als notwendigen Dienst eingetragen und bekam eine
  Warnung, die er nicht aufloesen konnte, weil sie nichts forderte, was
  jemand fordert. Das ist kein harmloser Ueberschuss - wer lernt, dass die
  Warnungen dieses Plugins nicht stimmen, liest auch die richtigen nicht
  mehr.

= 0.13.0 =
* Die Pruefung beruecksichtigt jetzt die Herkunftssuche. Bisher wusste der
  Bericht, dass ein Cookie neu aufgetaucht ist - aber nicht, aus welcher
  Erweiterung es stammt, obwohl die Suche das laengst ermittelt hatte.
* Neu aufgetauchte Cookies stehen im Bericht mit ihrer Herkunft: "neu:
  pwb_marke - stammt aus Perfect WooCommerce Brands". Der haeufigste Grund
  fuer ein neues Cookie ist eine neu installierte oder aktualisierte
  Erweiterung; aus einer Beobachtung wird damit eine Erklaerung.
* Ist die Herkunft nicht bekannt, steht das da - "Herkunft noch nicht
  ermittelt". Die Aussage "in keiner installierten Erweiterung gefunden"
  faellt weiterhin nur nach einem vollstaendigen Suchlauf.
* Neuer Abschnitt "Erweiterung nicht mehr installiert": Cookies, deren
  ermittelter Fundort auf der Installation nicht mehr existiert. Es ist die
  einzige Stelle, an der das Plugin von sich aus sagen kann, dass eine
  Konfiguration veraltet ist - ein Dienst, dessen Erweiterung deinstalliert
  wurde, blockiert weiter etwas, das gar nicht mehr geladen wird.
* Entfernt wird auch dort nichts von selbst, und der Hinweis nennt den
  Vorbehalt: Die Sammlung wird ergaenzt und nie neu bewertet, ein alter Fund
  bleibt also stehen.
* Ein-Datei-Plugins (hello.php direkt in plugins/) werden dabei nicht
  faelschlich als entfernt gemeldet. Laesst sich die Ordnerliste nicht lesen,
  wird gar nichts gemeldet - eine Warnung aus fehlenden Daten waere schlimmer
  als keine.

= 0.12.2 =
* Die Herkunftssuche zeigt jetzt ihr ERGEBNIS. Bisher stand dort nur die
  Buchhaltung des Laufs ("14.933 von 30.948 Dateien durchsucht") - die
  gefundenen Fundorte wurden ausschliesslich in der Zuordnungstabelle der
  offenen Cookies ausgegeben, und die gibt es nicht, sobald alles zugeordnet
  ist. Der Block meldete also, WIE VIEL er gelesen hat, und verschwieg, WAS
  er gefunden hat.
* Neu ist eine Tabelle "Cookie / Stammt aus" mit allen erfassten Cookies. Sie
  hat drei ehrliche Zustaende: gefunden (mit Name der Erweiterung), gesucht
  aber nicht gefunden, und noch nicht gesucht - Letzteres fuer Cookies, die
  seit dem Lauf dazugekommen sind.
* Zahlen erscheinen im Sichtbaren nur noch dann, wenn sie die Bedeutung des
  Ergebnisses aendern: wenn zu einem Namen kein Fundort steht. Nur dann
  entscheidet die Abdeckung, ob "nicht gefunden" heisst "kommt nicht vor"
  oder "es wurde noch nicht ueberall nachgesehen".
* Alles Uebrige - gelesene Dateien, uebersprungene Ordner, Groessengrenze,
  Mindestlaenge des Namens sowie die genaue Fundstelle je Cookie - steht
  jetzt unter "Technische Angaben".
* Waehrend eines laufenden Suchvorgangs werden keine technischen Angaben mehr
  angezeigt: Die gespeicherten Zahlen stammen dort noch vom vorigen Lauf.
* Das gespeicherte Ergebnis bleibt sichtbar, auch wenn die Sammlung geleert
  wurde.

= 0.12.1 =
* Die Herkunftssuche ist wieder auffindbar. Sie stand innerhalb des
  Abschnitts "Nicht zugeordnete Cookies" und verschwand deshalb genau dann,
  wenn jedes Cookie einem Dienst zugeordnet war - also im Normalzustand einer
  fertig eingerichteten Website. Sie steht jetzt dauerhaft oben auf der
  Analyse-Seite, direkt neben dem Sammler.
* Das war ein Entwurfsfehler, kein Programmierfehler: Das Werkzeug hing an
  der Aufgabe ("noch nicht zugeordnet") statt am Gegenstand ("Cookie"). Die
  Frage "aus welcher Erweiterung stammt das?" bleibt aber berechtigt, gerade
  um eine bestehende Zuordnung zu ueberpruefen.
* Die Suche beruecksichtigt deshalb jetzt ALLE erfassten Cookies, nicht nur
  die ohne Dienst.
* Zwei Tests halten den Fall fest: einer in PHP fuer die Lage "alles
  zugeordnet", einer gegen echtes WordPress fuer die Frage, ob die
  Ueberschrift auf der Seite sichtbar ist.

= 0.12.0 =
* Die Herkunftssuche durchsucht jetzt auch `vendor/`. Das war der eigentliche
  Fehler: WooCommerce liefert den Action Scheduler und die Jetpack-Pakete
  dort aus, Zahlungs-Plugins ihre eigenen Module - es wurde also gerade dort
  nicht gesucht, wo bei den grossen Erweiterungen der Code liegt.
* Neu: eine Bestandsaufnahme vor der Suche. Sie zaehlt, wie viele Dateien es
  ueberhaupt gibt, und nennt getrennt, was uebersprungen wird und warum.
  Vorher stand dort eine Zahl ohne Bezugsgroesse - "3000 durchsucht"
  beantwortet nicht die einzige Frage, die zaehlt: von wie vielen?
* Die Suche laeuft in Etappen mit Fortschritt statt in einem Durchgang. Jede
  Etappe ist ein Klick, ein Serveraufruf und hoechstens 10 Sekunden -
  gemessen gegen das Zeitlimit des Servers, nie mehr als dessen Haelfte.
  Damit ist auch eine Installation mit 80.000 Dateien vollstaendig
  durchsuchbar, ohne dass ein Aufruf ins Zeitlimit laeuft.
* Kein Cron, keine Hintergrundschleife, ein Aufruf zur Zeit. Wer den Tab
  schliesst, haelt an; beim naechsten Oeffnen laesst sich fortsetzen oder
  verwerfen.
* Die Begrenzung nach Dateizahl ist ersatzlos entfallen. Messung vom
  29.08.2026: derselbe Suchvorgang schafft ueber gebuendelte JS-Dateien 86
  Dateien pro Sekunde und ueber typische PHP-Dateien 52.000. Eine feste
  Dateigrenze ist damit auf der einen Installation bedeutungslos und auf der
  anderen ein Serverabbruch.
* "Kommt in keiner installierten Erweiterung vor" steht nur noch nach einem
  VOLLSTAENDIGEN Lauf. Endet die Suche frueher, weil alle Namen gefunden
  sind, sagt sie das - und behauptet nichts ueber Dateien, die niemand
  gelesen hat.

= 0.11.1 =
* BEHOBEN, und der Fehler war schwerwiegend: Nach dem Ein- oder Ausschalten
  des Sammlers, nach dem Uebernehmen von Diensten, nach dem Zuordnen von
  Cookies, nach der Herkunftssuche und nach dem Loeschen landete man auf einer
  LEEREN Seite. Grund: Beim Umbau auf Tabs hat eine Sammelersetzung in sechs
  Weiterleitungen den Parameter "page" nicht ergaenzt, sondern ersetzt. Ohne
  ihn weiss admin.php nicht, was es anzeigen soll.
* Alle Weiterleitungen laufen jetzt ueber eine einzige Funktion, die Seite und
  Tab zusammen setzt. Ein Test verbietet ab sofort, dass eine Adminseite ihre
  Navigationsadresse selbst zusammenbaut.
* Der Testnachbau von add_query_arg() gab bisher die Adresse unveraendert
  zurueck. Damit war jede Pruefung einer Weiterleitung wertlos - genau
  deshalb ist der Fehler oben durchgerutscht. Er baut die Parameter jetzt
  wirklich an.

= 0.11.0 =
* Der Tab "Analyse" ist nach ZUSTAND gegliedert statt nach "Schritt 1/2/3":
  "Zu entscheiden (n)", "Kann das Plugin nicht aufhalten (n)", "Erledigt (n)".
  Skript, fremder Server und Cookie stehen nebeneinander im selben Block -
  das ist die Sicht des Betreibers. Ob ein Fund aus der Skriptregistrierung
  oder aus dem Browser stammt, ist unsere Sicht.
* "Schritt 3: Der Sammler" war falsch beschriftet. Er ist kein spaeterer
  Schritt, sondern die Voraussetzung fuer die Haelfte der Funde - und stand
  mitten auf der Seite, unter den Ergebnissen, die er selbst erzeugt. Jetzt
  ist er eine Statuszeile ganz oben: laeuft / laeuft nicht, wie viele Seiten
  aufgezeichnet, ein Knopf.
* Die langen Erklaerungen stehen hinter Aufklappern statt vor den Daten. Beim
  roten Block waren es rund vierhundert Woerter Begruendung ueber sechs Zeilen
  Tabelle. Jetzt kommt die Tabelle zuerst.
* Die beiden Loeschknoepfe stehen zusammen unter "Aufraeumen" statt an zwei
  Stellen der Seite.
* BEHOBEN: Anmeldecookies wie wp-settings-1 blieben in einer bereits
  gespeicherten Sammlung stehen. Der Filter dafuer gibt es seit 0.4.1, aber er
  lief nur beim Eingang - und die Sammlung wird ergaenzt, nie neu bewertet.
  Er greift jetzt auch bei der Anzeige. Wer betroffen war, muss nichts
  loeschen; die Eintraege sind nach dem Update weg.

= 0.10.1 =
* Behebt acht Fehler im CI-Testlauf, die KEINE Programmfehler waren: Der
  Notbehelf-Testlaeufer kannte mehr WordPress-Funktionen als der massgebliche
  PHPUnit-Lauf. 150 Tests gruen hier, acht "Call to undefined function" dort.
* Die Nachbauten der WordPress-Funktionen stehen jetzt in EINER Datei, die
  beide Laeufer einlesen (tests/wp-function-stubs.php). Vorher hatte jeder
  seinen eigenen Satz.
* Ursache war zusaetzlich eine Falle im alten Aufbau: Ein einziges
  function_exists() umschloss ALLE Nachbauten. Existierte die eine gepruefte
  Funktion bereits, wurde keine der uebrigen definiert - obwohl sie fehlten.
  Jetzt ist jede einzeln abgesichert.
* Neu: EnvironmentTest prueft die Testumgebung, bevor sie den Code prueft.
  Fehlt eine WordPress-Funktion, gibt es EINEN Fehlschlag mit ihrem Namen
  statt eines Stapelabbilds mitten im Rendern einer Seite.

= 0.10.0 =
* Das Backend hat jetzt EINEN Menuepunkt mit einer Tab-Leiste statt drei
  Menuepunkten. Reihenfolge nach Arbeitsablauf: Uebersicht, Analyse, Pruefung,
  Dienste, Banner, Nachweis. Vorne steht, was man oft tut, hinten, was man
  einmal einstellt.
* Neu: Tab "Uebersicht". Er beantwortet die Frage, die man beim Oeffnen des
  Plugins wirklich hat - steht das jetzt, oder fehlt noch etwas? Jeder offene
  Punkt endet in einem Knopf, der zur zustaendigen Stelle fuehrt.
  Ausdruecklich KEIN gruener Haken fuer Rechtskonformitaet: Geprueft wird, ob
  die Angaben vollstaendig sind, nicht ob sie stimmen.
* Die Hauptseite stapelte bisher Dienste, Bannertexte, Einwilligungsdauer und
  Nachweisarchiv untereinander. Wer die Texte aendern wollte, scrollte an
  allen Diensten vorbei. Das ist jetzt getrennt.
* In der Fundliste steht nicht mehr die Skript-Kennung als graues Kaestchen,
  sondern lesbarer Text: "Google Analytics 4", "Wird geladen von
  js.stripe.com", "Aus dem Plugin woocommerce". Kennung und Adresse liegen
  darunter unter "Technische Angaben" - fuer Entwickler und Pruefer, nicht im
  Weg des Betreibers.
* Sicherheitsnetz beim Umbau: Dienste und Bannertexte lagen bisher in EINEM
  Formular. Auf zwei Tabs verteilt haette das Speichern eines Bannertextes die
  gesamte Dienste-Liste geloescht. Jedes Formular meldet deshalb an, welche
  Abschnitte es enthaelt; was nicht angemeldet ist, bleibt unangetastet. Drei
  Tests pruefen genau das.

= 0.9.0 =
* Neu: die Seite "Pruefung". Sie vergleicht, was Ihre Website laedt, mit dem,
  was in Ihrer Dienste-Liste steht - und mit dem Stand der letzten Pruefung.
  Damit faellt auf, wenn ein Plugin-Update einen neuen Drittdienst mitbringt.
* Die Pruefung aendert NICHTS. Insbesondere loescht "diesmal nicht gesehen"
  keinen Dienst: Ein Fund fehlt auch dann, wenn Sie die Kasse einfach nicht
  aufgerufen haben. Eine Automatik wuerde eine vollstaendige Konfiguration
  still ausduennen.
* Die erste Pruefung meldet keine Aenderung, sondern haelt den Zustand fest.
  Alles als "neu" auszuweisen waere bequem und falsch - Sie saehen vierzig
  rote Zeilen fuer etwas, das Sie gerade selbst eingerichtet haben.
* Der Bericht nennt neben den Aenderungen, was OHNEHIN offen ist. "Unveraendert"
  und "in Ordnung" sind nicht dasselbe.
* Erinnerung nach 30 Tagen - ausschliesslich auf den Seiten dieses Plugins und
  wegklickbar.

= 0.8.1 =
* Das Meta Pixel ist doch aufgenommen - 16 Eintraege. In 0.8.0 stand es noch
  auf der Ausschlussliste, weil Metas Cookie-Richtlinie maschinell nicht
  abrufbar und damit NICHT GEPRUEFT war. Ein Mensch hat sie durchgesehen und
  dort keine Speicherdauer gefunden. Damit wurde aus "nicht geprueft" ein
  Pruefergebnis, und der Eintrag erfuellt die Regel.
* Die Speicherdauer nennt fuer _fbc 90 Tage (von Meta belegt) und fuer _fbp
  ausdruecklich KEINE Zahl. Die im Netz kursierenden "90 Tage" fuer _fbp
  stammen nicht von Meta. Stattdessen steht dort, wie Sie die tatsaechliche
  Ablaufzeit an Ihrer eigenen Website ablesen. Ein Test schlaegt fehl, sobald
  jemand die Zahl gut gemeint nachtraegt.
* _fbp und _fbc setzt das Pixel auf IHRER Domain. Der Sammler findet sie
  deshalb, und die Einrichtungshilfe kann sie ab jetzt zuordnen - bisher
  standen sie als nichtssagender Name da.

= 0.8.0 =
* Erkennungsdatenbank von 10 auf 15 Eintraege: Google Maps, Vimeo, Trusted
  Shops (Trustbadge und Widgets), Polylang und WPML/WooCommerce Multilingual
  kommen dazu. Jeder Eintrag mit Belegquelle und Pruefdatum.
* Vier weitere Dienste wurden NICHT aufgenommen: Meta Pixel, Klarna,
  Elementor, Mailchimp. Die Gruende stehen im Quelltext. Beim Meta Pixel ist
  der Grund ausdruecklich "nicht geprueft" und nicht "nicht vorhanden" - die
  Cookie-Richtlinie von Meta war maschinell nicht abrufbar. Diese beiden
  Aussagen werden hier auseinandergehalten.
* Die Aufnahmeregel ist geschaerft: Eine fehlende Speicherdauer schliesst
  einen Eintrag nicht mehr aus, solange die Cookienamen belegt sind und das
  Fehlen als solches benannt wird ("Der Anbieter veroeffentlicht dazu nichts,
  geprueft am ..."). Eine Zahl wird weiterhin NIE geraten. Fehlen die
  Cookienamen, gibt es keinen Eintrag - ohne Namen kann das Plugin nichts
  zuordnen.
* Google Maps ist bewusst OHNE google.com als eingebetteten Server
  eingetragen. Auf google.com liegt auch reCAPTCHA; wer die Karte ueber den
  Server blockiert, blockiert das Pruefbild an der Kasse mit. Der Eintrag
  sagt das und nennt den praktikablen Weg (Vorschaubild statt Karte, bis die
  Einwilligung vorliegt).

= 0.7.0 =
* Erkennungsdatenbank von 5 auf 10 Eintraege: YouTube, Google Ads/DoubleClick,
  Google reCAPTCHA, Matomo (selbst betrieben) und Cloudflare kommen dazu.
  Jeder Eintrag mit Belegquelle und Pruefdatum.
* Vier Dienste wurden BEWUSST NICHT aufgenommen, weil die Anbieter ihre
  Cookienamen oder Speicherdauern nicht veroeffentlichen: PayPal, Stripe,
  Hotjar, Google Pay. Die Begruendung steht im Quelltext - eine geratene
  Speicherdauer landet ueber das Banner im Nachweisarchiv.

= 0.6.1 =
* Die Kennung eines Dienstes ging beim Speichern der Dienste-Seite verloren
  und wurde aus dem Namen neu gebildet. Aus "woocommerce-auftragszuordnung"
  wurde "woocommerce-auftragszuordnungsourcebuster". Folge: Die
  Konfigurationsversion stieg, obwohl sich inhaltlich nichts geaendert hatte -
  alle erteilten Einwilligungen waren ohne Grund ungueltig und das Banner
  erschien bei allen Besuchern erneut.

= 0.6.0 =
* Cookies, die zusammengehoeren, stehen in EINER Zeile. Die
  Auftragszuordnung von WooCommerce setzt sieben Cookies - das waren sieben
  Entscheidungen fuer einen einzigen Dienst. Jetzt ist es eine.
* Neu: Herkunftssuche. Auf Knopfdruck durchsucht das Plugin die installierten
  Erweiterungen nach dem Cookienamen und sagt, in welcher er vorkommt. Aus
  einem nichtssagenden Namen wird damit ein Plugin, das der Betreiber selbst
  installiert hat - und die Frage "was ist das?" wird zu "brauche ich das?".
* Die Kategorien werden dort erklaert, wo sie gewaehlt werden. Besonders
  "Notwendig": Wer das waehlt, nimmt dem Besucher die Wahl.

= 0.5.1 =
* Die Cookie-Zuordnung verlangte ZWEI Bedienschritte: ein Haekchen in einer
  eigenen Spalte und eine Auswahl in der letzten. Wer nur das Ziel waehlte -
  also genau das tat, worum die Seite bittet - drueckte auf den Knopf und es
  passierte nichts. Das Haekchen ist weg: Die Auswahl eines Ziels ist die
  Absicht.
* Nach dem Zuordnen gab es keinerlei Rueckmeldung. Ein erfolgreicher Lauf sah
  aus wie ein wirkungsloser. Jetzt meldet die Seite, was zugeordnet wurde, was
  offen blieb, und was fehlte.

= 0.5.0 =
* Gefundene Cookies lassen sich jetzt direkt zuordnen: ankreuzen, Ziel
  waehlen, ein Knopf - genau wie bei den Skripten. Vorher wurden sie nur
  angezeigt, mit dem Hinweis, der Betreiber moege sie "unter Dienste
  zuordnen". Das war die Arbeit, die eine Einrichtungshilfe abnehmen soll, als
  Merkzettel zurueckgegeben.
* Die Erkennungsdatenbank findet den Dienst jetzt auch anhand eines
  Cookienamens. Aus einem nackten "sbjs_current" wird ein vollstaendiger
  Eintrag mit Anbieter, Zweck, Speicherdauer und Beleg.
* Ein Cookie laesst sich auch an einen bestehenden Dienst anhaengen, ohne
  dessen Angaben anzufassen.
* Google Analytics kennt jetzt das Muster _ga_* und erkennt damit das Cookie
  mit der Property-Kennung im Namen.

= 0.4.2 =
* Der Warnhinweis zu Skripten, die das Blocking nicht erreicht, war fuer
  Nicht-Entwickler unverstaendlich. Er erklaert jetzt in einfachen Worten,
  warum das Plugin dort nichts ausrichtet, und nennt drei konkrete Wege aus
  dem Problem - der erste ist bei Schriftarten der richtige und dauert eine
  Viertelstunde.

= 0.4.1 =
* Der Sammler zeichnete Cookienamen auf, zeigte sie aber nirgends an. Auf
  einem Shop, dessen Drittdienste abgeschaltet sind, war damit die gesamte
  Ausbeute unsichtbar - er meldete "nichts gefunden", obwohl er etwas
  gefunden hatte. Gesetzte Cookies stehen jetzt in zwei Listen: deklariert
  und nicht deklariert.
* Die Anmeldecookies des Administrators werden gar nicht erst gespeichert -
  ein Besucher bekommt sie nie zu sehen. Das Sitzungscookie von WooCommerce
  (wp_woocommerce_session_) ist davon ausdrücklich ausgenommen.
* Cookies vom eigenen Server werden damit endlich sichtbar. Die
  Auftragszuordnung von WooCommerce kontaktiert keinen fremden Server und war
  vorher in keiner Liste zu sehen.

= 0.4.0 =
* Der Sammler (Einrichtungshilfe Stufe 2). Er schaut im Browser nach, welche
  fremden Server die Seiten tatsaechlich kontaktieren, und findet damit auch,
  was an WordPress vorbeigeht: Schriftarten direkt im Design, Skripte aus
  einem Tag-Manager, Zahlungsdienste auf der Kassenseite.
* Er laeuft nur fuer angemeldete Administratoren und speichert nur Hostnamen,
  Cookienamen und den Seitenpfad - keine Cookieinhalte, keine
  Adressparameter, keine Seiteninhalte.
* Jeder Fund wird danach sortiert, ob das Blocking ihn ueberhaupt erreichen
  kann. Was es nicht erreicht, wird als solches gekennzeichnet und laesst sich
  bewusst NICHT als Dienst uebernehmen - mit der Handlungsanweisung, wie es zu
  loesen ist.
* Sammler abschaltbar, Sammlung vollstaendig loeschbar.

= 0.3.2 =
* Lizenz auf GPLv2 or later umgestellt. Vorher stand "proprietary" im
  Plugin-Kopf - damit waere das Plugin nach Richtlinie 1 nicht ins
  WordPress-Verzeichnis gekommen. Der Lizenztext liegt als LICENSE bei.

= 0.3.1 =
* Die Aufraeumhilfe aus 0.3.0 griff bei genau den Daten nicht, fuer die sie
  gebaut war. Sie verglich Kennung UND Namen - die fehlerhafte
  Einrichtungshilfe hatte die Kennungen aber eindeutig gemacht
  (woocommerce-warenkorb, -2, -3 ...). Massgeblich ist jetzt, was der Besucher
  sieht: gleicher Name in derselben Kategorie.
* Gleicher Name in VERSCHIEDENEN Kategorien wird nicht zusammengefuehrt,
  sondern als Widerspruch gemeldet - welche Kategorie gilt, entscheidet ein
  Mensch.
* Die Einrichtungshilfe benutzt dieselbe Regel wie die Aufraeumhilfe.

= 0.3.0 =
* Die Dienstangaben im Banner sind aufklappbar. Sichtbar bleiben Name und
  Anbieter; Zweck, Speicherdauer, Drittland und Cookienamen sind einen
  Tastendruck entfernt - auf derselben Ebene, vor jeder Entscheidung
  erreichbar. Bei einem Shop mit fuenfzehn Diensten war die ausgeschriebene
  Fassung eine Textwand, die niemand liest und die die Ablehnung nach unten
  drueckt.
* Die Cookienamen stehen jetzt im Banner. Bisher standen sie nirgends.
* Die Fokusfalle kennt das Aufklappelement. Ohne diese Ergaenzung waere der
  Fokus beim Tabben hinter das Banner gewandert.
* Aufraeumhilfe: Ein Knopf auf der Einstellungsseite fuehrt Dienste zusammen,
  die dieselbe Kennung und denselben Namen tragen - fuer Installationen, die
  mit den Doppeleintraegen aus 0.2.1 eingerichtet wurden.

= 0.2.2 =
* Mehrere Kennungen desselben Dienstes ergaben mehrere identische Einträge -
  WooCommerce gibt seinen Warenkorb über fünf Kennungen aus, und im Banner
  stand danach fünfmal derselbe Absatz. Sie werden jetzt zu einem Eintrag
  zusammengeführt.
* Beim Zusammenführen bleiben selbst gepflegte Formulierungen zu Zweck und
  Speicherdauer erhalten.

= 0.2.1 =
* Die Einrichtungshilfe nahm alle Skripte auf, die WordPress KENNT, statt der,
  die eine Seite LÄDT. WordPress registriert bei jedem Aufruf den kompletten
  Kernbestand einschließlich Adminbereich - in einem WooCommerce-Shop kamen so
  rund 250 belanglose Zeilen zusammen. Aufgenommen wird jetzt nur noch, was
  tatsächlich ausgegeben wurde.
* Die Liste ist zweigeteilt: oben, was von einem fremden Server kommt oder als
  bekannter Dienst erkannt wurde; darunter eingeklappt alles Weitere vom
  eigenen Server.
* Das Plugin schlägt sich nicht mehr selbst als Dienst vor.
* Eine Aufnahme aus 0.2.0 wird beim nächsten Frontend-Aufruf einmalig
  verworfen.

= 0.2.0 =
* Einrichtungshilfe: Das Plugin zeigt die geladenen Skripte an und schlägt für
  bekannte Dienste einen fertigen Eintrag mit Anbieter, Zweck, Speicherdauer
  und Drittlandangabe vor. Übernommen wird nur, was ausdrücklich bestätigt
  wird.
* Erkennungsdatenbank mit Belegquelle und Prüfdatum je Eintrag.
* Nicht erkannte Skripte werden als unbekannt gekennzeichnet und mit leeren
  Feldern vorgelegt - es wird nichts geraten.

= 0.1.1 =
* Zwei Blocking-Lücken aus der unabhängigen Sicherheitsprüfung geschlossen:
  iframes mit Zeilenumbruch im Tag wurden nicht erfasst, und ein Hostname mit
  abschließendem Punkt umging den Abgleich.

= 0.1.0 =
* Erste Fassung. Blocking, Banner, Widerruf, Konfigurationsversionierung und
  Nachweisarchiv.
