=== Klassifieds for WooCommerce — older releases ===

The latest releases are listed in readme.txt.

== Changelog ==
= 0.9.207 =

* Changed: the dashboard and the settings screen have a new look, in a light and a dark scheme. Surfaces, lines and text take their colours from one palette of ten names, purple for light and mist for dark. The state colours of published, draft, detached and trashed products are the same as before.
* Changed: the old lightness variables (`--color`, `--bg-color`, `--bg-lighter-N`, `--bg-darker-N`, `--bg-color-tr`, `--bg-color-dec`) are no longer defined. Klassifieds Pro 0.9.176 and older draw their product list with them; update Pro to 0.9.177.
* Changed: counts and rows on the dashboard show a focus ring, the product search box rings as a whole, buttons show a pointer cursor, and the state counts have a lighter, softly glowing edge in the dark scheme.
* The "PRO" badge also shows next to Featured and Quarantined on the dashboard.
* For developers: the stylesheet is built from `admin/css/style-admin-klassifieds-input.css` and the files in `admin/css/parts/`. Tailwind reads only `admin/`, `includes/` and `src/`.

= 0.9.206 =

* Fixed: an account install sometimes finished with 0 records although the account has ads, and the screen went back to the onboarding without a message. A job could read the account at the same moment as the import, and the import took the empty result for an empty account. The import is the only reader now, and the jobs wait until the index of the ads is stored.
* Changed: an import that reads no ads reads again, up to five times a minute apart, and only then removes the account ID, with a message on the onboarding screen. The terms of use stay agreed to (filter `klassifieds_keep_consent`).
* Fixed: a cleanup that interrupted an import could finish while the import was still creating products, which stayed published without an account. The cleanup waits for the import's jobs.
* A failed request to kleinanzeigen.de is written to the debug log (`WP_DEBUG_LOG`) with its reason.

= 0.9.205 =

* Fixed: on a site without an account, the dashboard's styles reached the whole admin page: the WordPress menu floated over the content and the footer scripts printed as text. The styles apply inside the dashboard again, as in 0.9.203. 0.9.204 has this error; update.

= 0.9.204 =

* Changed: notices, WordPress's update bar among them, appear at the top of the Klassifieds page instead of above its header. The dashboard has a new logo and tighter spacing.

= 0.9.203 =

* Changed: the "Upgrade now" box, the "Sync now" button and the upgrade list of the dashboard look right in the light and the dark mode.

= 0.9.202 =

* Fixed: the dashboard ended in a fatal error on a site without Klassifieds Pro, or with a Pro older than 0.9.175, because it asked Pro's product list for the search box. Without it, the search box is left out. 0.9.200 and 0.9.201 have this error; update.
* Fixed: a PHP warning about an undefined variable when the dashboard loaded.

= 0.9.201 =

* Fixed: the dashboard's heading showed layout placeholder words, such as "text-6xl" and "text-white", next to its tagline. It reads "Unleash your Classifieds" again.

= 0.9.200 =

* New: the dashboard has a new layout, in a light and a dark mode. A button in the page header switches between them, and the browser remembers the choice.
* Changed: the notice that offers the background service opens when a job is overdue, after 30 seconds while an account is being installed and after 60 otherwise, and closes by itself once the jobs have run.
* Changed: the dashboard events are named `klassifieds:dashboard` and `klassifieds:dashboard:ready`. This release needs Klassifieds Pro 0.9.175.

= 0.9.199 =

* Fixed: the admin scripts of 0.9.198 were the readable development build, four to six times larger than needed. They are minified again, as in 0.9.197. Nothing else changed.

= 0.9.198 =

* New: when scheduled jobs ran late because nobody visited the site, the dashboard offers the background service in a snackbar with a switch. Closing it keeps it closed for the rest of the browser session. The message and the switch's name are translated into German.
* Fixed: the cache of a fetched ad page no longer fills the options table. A page is cached only after a real fetch, for ten minutes instead of an hour, and is removed once the images are stored; expired pages are deleted every five minutes.
* The account's display name is read once and kept.

= 0.9.197 =

* Fixed: the "delete images" button on the product edit screen did nothing without Klassifieds Pro, because the library it needs to read the form was only loaded by Pro. The plugin loads it itself again.
* For developers: the ajax script is the class `KlassifiedsAjax`, registered as `window.KlassifiedsAjaxClass`, so Klassifieds Pro can extend it. The inline edit, `poll` and `ping` functions moved to Pro, which is the only plugin that answers them. The script no longer refers to Pro's websocket or list classes.

= 0.9.196 =

* The admin scripts are built from source: each one is a module in `src/admin/`, compiled by `npm run build` into `build/admin/`. The page loads the same scripts under the same names and they do what they did.
* Fixed: after importing images on a product without Klassifieds Pro, the message that says how many images came in never appeared; the script asked for a class only Pro has.
* Fixed: clearing the dashboard's scheduler stopped only every other timer.
* The script that adds `notice()` and `revert()` to jQuery now loads after the script that hands it its settings. On the product and term screens it ran before.
* For developers: `npm run test:js` runs the tests of the admin scripts (Vitest). The AJAX request functions read the nonce when they are called, not when the script starts.

= 0.9.195 =

* Cookies go through one class, `KlassifiedsCookie`. It reads the last cookie on the page, which the old reader missed, and a missing orientation cookie no longer stops the dashboard's scripts.
* The server-side bulk edit marker is gone: `KlassifiedsAjax.bulkedit()` and the `_ajax_klassifieds_bulkedit` action. Klassifieds Pro guards the list in the browser.
* For developers: `Klassifieds\Utils\Utils::parse_remote_url()` moved to `klassifieds()->fn()->parse_remote_url()`. Klassifieds Pro 0.9.171 calls it there.

= 0.9.194 =

* The dashboard's rainbow border turns again while it loads in the background, instead of turning the whole header with it. Its animation was named `spin`, the name WooCommerce's admin styles give a rotation, and whichever stylesheet loaded last won; it is `klassifieds-rainbow-spin` now.
* Products and label terms without an image show WooCommerce's placeholder (Klassifieds Pro's own, with Pro) through the new `klassifieds_placeholder_image` filter. The ad icon and the installer mark follow the new artwork.
* For developers: `Klassifieds_Product::placeholder_image()` is gone; use `klassifieds()->fn()->get_placeholder_image_src()`. The location icons' classes are `klassifieds` and `custom`, set by Klassifieds Pro 0.9.170.

= 0.9.193 =

* The installer shows its percentage again. 0.9.192 renamed the element it is written into, and the installer's script kept looking for the old name, so the number stayed empty while an account was being installed.
* For developers: the two percentages are written by id, `#svg-installer-value` and `#svg-bulk-value` (Klassifieds Pro 0.9.169 writes the bulk edit's there). `tests/svg-styles.php` pins both ids.

= 0.9.192 =

* An ad's page is read once. Its text, its location and its images come from one reader for the mobile and the desktop page of an ad, which keeps each page it fetched for an hour, where every one of them used to fetch the page again.
* A product keeps its description when its ad's page cannot be read, instead of taking "Not found" or nothing in its place.
* Bulk actions in Klassifieds Pro's product list show how far they have come, as a percentage over the list.
* For developers: every AJAX handler answers through `Klassifieds_Ajax::json_success()` or `json_error()`, so every reply carries `success`, and a failure a `message` instead of `data.error`. `Klassifieds_Dom_Document` (`mobile()`, `desktop()`) replaces `read_dom_document()`, `get_ad_locality()` and `get_ad_images()`; `get_ad_content()` stays, deprecated, for Klassifieds Pro 0.9.167. `create_product_images()` takes the page as `array( 'device' => …, 'html' => … )`. Update Klassifieds Pro to 0.9.168 afterwards.

Older releases are listed in changelog.txt, which ships with the plugin.

= 0.9.191 =

* German is back on a German site. The translation the plugin ships was complete, but nothing told WordPress where it lies: WordPress looks in wp-content/languages and in the folders a plugin registers itself, never in a plugin's Domain Path on its own. The registration went missing in 0.9.167 with the old text domain and was never put back, so from 0.9.168 on every screen stayed English — and wordpress.org carries no approved German for this plugin yet, so no language pack stepped in either. The admin screens and the React parts of them are German again.
* For developers: the bundled translations are registered with `load_plugin_textdomain()` on `after_setup_theme`, and `wp_set_script_translations()` is given the path to the JSON. A language pack from wordpress.org still wins over the bundled copy, as it should. `tests/translation-de.php` pins both.

= 0.9.190 =

* The admin page header credits WebPremiere. 0.9.189 announced the new author name but was packaged before this last place followed, so the published release still read the old one.
* Stale docblocks naming the former author are gone from seven files. Nothing else changes: no new behaviour, no new setting, no database work.

= 0.9.189 =

* New artwork: the plugin's mark, banner and icons are redrawn, and the admin menu icon, the page header logo and the loading animation follow it. The screenshots in the plugin directory are new as well.
* The dashboard's card for products that come from an ad now reads "Kleinanzeigen", the platform's name, where it read "kleinanzeigen.de" before.
* The plugin is published under the author name "WebPremiere". The German translation covers this release's new and changed strings.

= 0.9.187 =

* Prices: a product whose ad has no price keeps an empty price instead of 0, and the price is set on the product itself before its data store saves it. The lookup table's price difference can be adjusted through the new `klassifieds_has_price_diff` filter.

= 0.9.186 =

* In affiliate mode, Klassifieds products count as purchasable, with or without a price; up to 0.9.185 they did not. The button keeps linking to kleinanzeigen.de and reads "Buy at" or "Visit at" by the price. In normal mode a product without a price shows no button, as before.

= 0.9.185 =

* In affiliate mode, the button of a product without a price reads "Visit at kleinanzeigen.de" instead of "Buy at kleinanzeigen.de". Products with a price keep "Buy at kleinanzeigen.de".
* For developers: the unused function `klassifieds_add_to_cart_text()` is gone. The `klassifieds_affiliate_button_text` filter receives either text.

= 0.9.184 =

* Products the plugin creates for new ads get their description, price, keyword categories and labels, and brands again. Since 0.9.160 the ad page was read with two arguments swapped, so every product the create job made came out without them; a manual import was not affected. The ad page is read as it was in 0.9.141 again, and the class `Klassifieds_Vip_Document` is gone. To fill in the products created since then, run "Rebuild product data" on the Tools tab once.
* The dashboard keeps updating when you decide to stay on a page that asked before you left it (Klassifieds Pro asks while a bulk edit runs).
* For developers: the e-mail tab's helpers for Klassifieds Pro (`statusReport`, `parseCaptureSize`, `randomProductData`, `screenshot`) and the `SCREENSHOT_API` value are no longer in `admin/js/ajax.js`; Klassifieds Pro 0.9.166 carries its own. With Pro 0.9.165 the e-mail tab's two buttons do nothing, so update Pro to 0.9.166.

= 0.9.183 =

* Security: posts by users without the `unfiltered_html` capability no longer keep `<script>` and `<style>` elements. The plugin's filter on WordPress's allowed post HTML let both through, on every page of the site; it now adds only an SVG with its path and two attributes.
* Security: the product list's actions — importing and deleting images, featuring, connecting, detaching, deleting, restoring and changing the status — check that you may edit, delete or publish that very product, not only that you may use the plugin, and no longer reach posts of other types.
* The readme says exactly what the optional trigger service receives, keeps and deletes, and for how long. The privacy policy and terms it links describe the service as well.
* The readme's changelog keeps the latest releases; the older ones are in `changelog.txt`, which ships with the plugin.
* For developers: `Klassifieds_Exception` extends `Exception` without declaring `Throwable` itself, and the admin scripts fit Prettier's line width again. Klassifieds Pro needs no update.

= 0.9.182 =

* The admin screens' markup, styles, data attributes and JavaScript events carry the plugin's prefix: `klassifieds-` instead of `ka-` in class names, ids and `data-` attributes, and `klassifieds:` instead of `ka:` in the events. Update Klassifieds Pro to 0.9.164 at the same time: until both are updated, its product list's actions and search notice don't react.
* The log folder is `wp-content/uploads/klassifieds-for-woocommerce/logs/`. An existing `ka-logs/` folder is left as it is.
* For developers: the admin body classes are `klassifieds-free` and `klassifieds-pro` (`Enums\Constant::FREE`, `::PRO`), the location icon of an ad from kleinanzeigen.de has the class `kleinanzeigen`, and its image is `admin/images/icon_kleinanzeigen.svg`.

= 0.9.181 =

* Installing and updating finishes, or tries again. The plugin records its version only once its tables and settings are in place, and checks that its tables exist after creating them; if a step fails, the next request runs them all again. Every other request reads a single setting.
* The update steps for installs of 0.9.173 and older are gone: the data move of 0.9.172 and the shop_manager repair. A site still on one of those versions updates to 0.9.180 first.
* Uninstalling no longer looks for data under names from before 0.9.172.
* Needs Klassifieds Pro 0.9.162. Update Pro first: this version ends Pro 0.9.161 and older in a fatal error.
* For developers: every name carries the plugin's prefix. The product accessors are `get_klassifieds_*()` and `set_klassifieds_*()` only; `get_klassifieds_product()`, `is_klassifieds_pro()` and `is_klassifieds_free()` replace their short-named versions; the REST API's `ka_price` field is `klassifieds_price`; error codes and schedule names are `klassifieds_*`. Values both plugins read are enums (`Schedules`, `Connector`, the new `Cache`), and the dashboard's top header is filled through the `klassifieds_dashboard_topheader` action.

= 0.9.180 =

* The General tab says its two longest settings in plain words. "Images" is now "Product images", with the box "Show images from kleinanzeigen.de instead of copying them" and a short note on what a visitor's browser sends kleinanzeigen.de when it is ticked. The trigger service is now the "Auxiliary service", with the box "Working in a timely manner"; its description says what it does and that it is run by the plugin's author and receives your site address, the plugin version and the edition once a day.
* The dashboard's Published and Deactivated cards call the products that come from an ad "kleinanzeigen.de" instead of "Klassifieds".
* Fixed: after importing several images, German admins saw a literal "{count}" in the message. The German plural named a placeholder the message never filled.
* The readme describes the housekeeping — with "Cleanup regularly" on, a product whose ad has been gone for more than 30 days is deleted — and what uninstalling removes and keeps, `KLASSIFIEDS_REMOVE_ALL_DATA` included.
* For developers: the image-import message's placeholders are `{count}` and `{total}` instead of `{count1}` and `{count2}`. Klassifieds Pro needs no update.

= 0.9.179 =

* A product whose ad comes back is published again, as it was before the ad went away. On very busy accounts it could stay a draft instead: if the plugin found its ad missing a second time before it came back, that second time overwrote the status the product was to return to. Products this already happened to stay drafts; publish them by hand.
* Background jobs no longer run twice at the same time. A job now takes its lock in one step and shares it with every schedule that runs the same job, and a job that finds the lock taken leaves all of that job's work alone.
* For developers: the job lock is a row `klassifieds_job_lock_{task}` in the options table instead of the `klassifieds_job_active_*` transients. `Klassifieds_Account_Installer::delete_active_job_options()` and `delete_active_job_option()` are gone. Klassifieds Pro needs no update.

= 0.9.178 =

* Switching on "Keep background work running on time" registers your site with the trigger service right away, instead of at the next daily run. Switching it off still withdraws the registration right away, and a site that never switched it on sends nothing.
* For developers: every settings page registers its hooks in a public `hooks()`, which `Klassifieds_Settings_Page` calls on `klassifieds_register_settings`. `Klassifieds_Connector::ttl()` returns the trigger service's response body. Klassifieds Pro 0.9.160 and older do not work with this version; update Pro to 0.9.161 first.

= 0.9.177 =

* The plugin reads your whole account. Until now it read only the first page of your ads, about 25, so a larger account never had more of them turned into products. It now goes through the account 100 ads per page, up to the 10,000 kleinanzeigen.de lists for one account. The rest are picked up from your next sync on.
* A read whose number of ads does not match your account's own count, because an ad came or went while it paged or a page failed, no longer replaces the stored list of ads. It only adds the ads that list lacks, so no product is taken out of your shop on its word.
* For developers: `klassifieds_index_limit` defaults to `KLASSIFIEDS_MAX_CAPACITY` (10,000) instead of `null`, and a filter answering `null` now sets no limit at all instead of one page. `klassifieds_page_crawl_size` defaults to 100. `Klassifieds_Data_Cache::is_complete()` is removed.
* For developers: the filters `klassifieds_task_items` and `klassifieds_pre_create_ad_product` are removed, and with them `Klassifieds\Enums\Exception::DEFERRED`. Nothing limits which ads become products any more.

= 0.9.176 =

* The plugin runs no shell commands any more. The server load it samples comes from PHP's own `sys_getloadavg()`.
* Debug messages from the class loader and about a missing template are written only when `WP_DEBUG_LOG` is on.
* The helper that decodes base64-encoded JSON sanitizes every value it returns.
* The update from 0.9.171 or older renames old shortcodes with one prepared statement per tag.
* Removed an unused function that could make a file world-writable.

= 0.9.175 =

* Security: the product list's "save" request accepts only a product's title, slug, excerpt, content, status and menu order, each sanitized, and a status only if WordPress knows it. It used to pass any post field from the request on to WordPress, the post ID included, so a user allowed to manage the plugin could have changed a different post. It now also checks that the user may edit that product, and may delete it before a trashed product is deleted.

= 0.9.174 =

* The plugin's data under its old names is removed on the first request after the update: the tables `ka_meta_lookup`, `ka_quarantine` and `ka_deleted_posts`, the `_ka_*` meta of products and images, and the old colour of labels. 0.9.173 copied all of it to the new names. A site updating from 0.9.171 or older copies and removes it in one go, and removes nothing if the copy fails.
* After this update, do not go back to 0.9.171 or older: those versions would no longer recognise your products as ads and would import them again. Going back to 0.9.173 is fine.
* Using Klassifieds Pro? It needs 0.9.154 or later, as since 0.9.172.

= 0.9.173 =

* Fixed: 0.9.172 stopped every page of the site with a fatal error, "Call to undefined function Klassifieds\klassifieds()". The plugin declared its main function only after loading the class that calls it. The error came before the plugin changed anything, so the 0.9.172 update never started: this version carries it out on its first request, copying tables, meta, label colours and shortcodes to their new names.
* Everything 0.9.172 says applies. Using Klassifieds Pro? Update it to 0.9.154 or later first.

= 0.9.172 =

* Every name the plugin adds to WordPress now carries its own prefix, as WordPress.org asks: its database tables, the meta keys on products and images, the colour of a label, its shortcodes, its cron schedules, its script and style handles, the REST namespace, a cookie and two transients. Many of them used to start with `ka`, which is too short to be unique.
* Your data comes along. On the first request after the update, the plugin copies its tables, the meta of products and images and the colours of labels to the new names, and renames `[ka-timestamp]` and `[ka-classifieds-total]` to `[klassifieds-timestamp]` and `[klassifieds-total]` in posts, pages and widgets. Until the copy has finished, no import or sync job runs. The old tables and meta stay untouched for now, so going back to 0.9.171 still works; a later version removes them.
* Using Klassifieds Pro? Update it to 0.9.154 first. Pro 0.9.153 and older stop with a fatal error against this version.
* Using one of the shortcodes in a theme template, through `do_shortcode()`? Rename it there; only content and widgets are updated for you.
* For developers: `KA()` is now `klassifieds()`. The product classes `WC_Product_Klassified` and `WC_Product_Variable_Klassified` are now `Klassifieds_Product_Simple` and `Klassifieds_Product_Variable`. The REST namespace `ka/v1` is now `klassifieds/v1`, with no alias. Script and style handles are `klassifieds-*` instead of `ka-*`, and the constant `WC_TERMS` is `KLASSIFIEDS_WC_TERMS`. Query the tables through `$wpdb->klassifieds_meta_lookup`, `$wpdb->klassifieds_quarantine` and `$wpdb->klassifieds_deleted_posts`, and read the meta through the `KLASSIFIEDS_META__*` constants.

= 0.9.171 =

* The plugin carries no ad capacity and enforces no licence any more. The code Klassifieds Pro used to hold an install to its licensed number of ads is gone: the helpers that picked the ads a licence covers, a second check after the plugin's own filters, the capacity notice on the settings screen and the dot on the dashboard's ad count. Klassifieds Pro 0.9.153 does all of it itself. What stays are three filters, `klassifieds_index_limit`, `klassifieds_task_items` and `klassifieds_pre_create_ad_product`, and each passes on what its callbacks answer.
* Using Klassifieds Pro? Update it to 0.9.153 first. Pro 0.9.152 and older stop with a fatal error against this version.
* A limit set through `klassifieds_index_limit` is kept exactly. The account index used to hold up to one page of ads more than the limit asked for.
* For developers: `Klassifieds\Enums\Exception::CAPACITY_REACHED` is now `Exception::DEFERRED`, with the same value. A refusal from `klassifieds_pre_create_ad_product` carrying it still makes the import job skip the ad silently.

= 0.9.170 =

* The admin scripts and stylesheets that ship pass WordPress's JavaScript and CSS linters (`wp-scripts lint-js` and `lint-style`). Their configuration ships as `eslint.config.cjs` and `stylelint.config.cjs`. The admin screens look the same.
* Fixed on the way, found by the linters: saving a product from the list threw on an undefined variable; deleting a product's images never noticed a missing post ID; a failed save of the terms of use threw in its error handler instead of showing the error; and the onboarding screen could fail on a page without the plugin's AJAX script.
* The browser console stays quiet: the heartbeat, job, screen and version diagnostics print only with `WP_DEBUG` on. Failures are still reported there as errors.
* Saving the General settings while the account installer is running is refused with "Cleanup in progress…" only while the previous account really is being cleaned up, and no longer while inactive ads are being removed.
* Code that never ran is gone: three demo components nothing loaded, one of them a copied snippet under CC BY-SA, and a style rule that matched no element.

= 0.9.169 =

* The plugin ships the sources of its compiled parts: `src/` for the onboarding screen and the two Tailwind inputs for the admin stylesheet, together with `package.json` and its lock file. The new "Source code and building" section says how to rebuild them. `composer.json` carries the plugin's name and description and no longer maps a namespace no class uses.
* Whether a product still needs its images is asked per mode. Where images are stored, the thumbnail has to be a stored file; with "Link images from kleinanzeigen.de" ticked, a linked or a stored image will do, so images already stored stay — their files go when "Cleanup regularly" on the Tools tab removes their product. In either mode an image that is neither is fetched again. The setting's description says so.
* Four yes/no settings are read the same way whether they were saved as text or as a number: the terms-of-use confirmation, publishing new products directly, the inactive-ad cleanup, and whether an administrator or shop manager has opted in to a notification.
* Klassifieds Pro: 0.9.152 stops discarding stored images on every sync, which kept the dashboard's "missing images" count changing and downloaded every image again. It works with this version and with 0.9.168, so the order of the updates does not matter.

= 0.9.168 =

* Security: the public REST routes under `ka/v1/products` serve published products only. The list also returned drafts and trashed products, and a single product was served whatever its status — both without logging in. A draft or a trashed product now answers like a missing one, to anyone, an administrator included. Changing and deleting a product still ask what the user may do, and still reach drafts.
* Ad images are stored in your media library, as WebP, and your shop serves them itself. Until now they were linked from kleinanzeigen.de, so every visitor's browser fetched them there and sent kleinanzeigen.de the visitor's IP address, user agent and your shop's address — which the readme said never happens. After updating, a background job stores the linked images one product at a time; until a product's turn comes, its images still load from kleinanzeigen.de. A site that would rather keep the images off its disk can tick "Link images from kleinanzeigen.de instead of storing them" on the General tab, which says what that means for visitors. Downloads ask for WebP: without that, the image host answers with a PNG about thirty times larger.
* The German translation keeps the names. The plugin stays "Klassifieds for WooCommerce" instead of becoming "Kleinanzeigen für WooCommerce", and "Klassifieds" no longer turns into "Kleinanzeigen" anywhere. Where a text means the platform, it names kleinanzeigen.de, in English as well — the terms-of-use confirmation among them. Every text is translated, those added in 0.9.167 included, and two that said something other than the English are fixed: the refusal to show the sync status said you were allowed, and the thank-you line had lost its link.
* The label colour uses WordPress's own colour picker. The bundled wp-color-picker-alpha 3.0.1, an outdated copy of a third-party library, is gone, and with it a transparency slider no label used.
* The plugin no longer changes WordPress's cron lock for the whole site: it set `WP_CRON_LOCK_TIMEOUT` to 30 seconds, which applied to every plugin's scheduled events. When it asks for its background work to start, WordPress's own `spawn_cron()` sends the request — only when something is due, respecting the cron lock, and verifying SSL as the site's settings say, where the plugin used to switch verification off. A site with `DISABLE_WP_CRON` gets no such request.
* The admin screens' SVG images carry no `<style>` elements any more. The logo and installer animations are styled by a stylesheet loaded on the plugin's own screens, the two SVG files by attributes. Nothing looks different.
* The admin screens' requests are answered through `wp_send_json()`. The plugin's own error handler used to run on every `wp_die()`, which would have put an error after every such answer; it now answers real errors only.
* The General tab lists Scheduling last, and the installer's mode label is set smaller.
* Klassifieds Pro: update it to 0.9.151 before this version. Pro's debug statistics and the inline editor's term count read the plugin's answers as text; 0.9.151 reads them either way.

= 0.9.167 =

* Security: the plugin no longer logs anyone in by itself. It hooked into WordPress's authentication for every request to the site and signed in the user whose ID and encrypted application password stood in the request's URL, and its REST routes under `ka/v1` accepted nothing else — not even a logged-in administrator. Authentication is now WordPress's own, a login cookie with its REST nonce or an application password in the Authorization header, and the routes only ask what that user may do. A `redirect_url` parameter that forwarded the response to any address is gone with it. The product update route now also reads a JSON body.
* Security: every admin action asks whether you may do it before anything else. Removing or syncing the account, rebuilding the lookup table or the taxonomies, and saving the settings checked only that the request came from the plugin's own form.
* Security: the label colour and the brand fields are saved only from their own form, by someone allowed to edit that term, and only those fields. The check meant for this looked for a form field that was never sent, so it never ran, and any posted value was stored as term meta, for a term of any taxonomy. The colour takes hex or rgb()/rgba(), the brand images take attachment IDs, and saving hangs on the plugin's own two taxonomies only. A new label also keeps the colour picked for it instead of getting the default.
* Security: the settings import checks the upload itself — PHP's own error code, that the file is this request's upload, its extension, its size on disk and its type — and refuses the whole file when one entry does not fit the section it is imported into. A labels export can no longer be read into categories, and a label's action is limited to the choices the settings screen offers. A successful import now says so.
* Every setting has a sanitizer of its own: the account ID, each checkbox (stored as "1" or ""), the keyword lists. Two buttons on the Tools tab and two fields of the import form were registered as settings, so saving wrote them into the options table; they no longer are.
* Removed a logged-out AJAX action that handed out nonces nothing could use.
* Klassifieds Pro: update it together with this version. The "Publish now" link in Pro's new-product e-mail relied on the login removed here; the matching Pro release replaces it with a signed link, and the `[ka-rest-message]` shortcode of the page it opens now comes with Pro.

= 0.9.166 =

* Fixed: the debug log was never written. With `WP_DEBUG_LOG` on, the plugin wrote to a `logs` folder under uploads that nothing ever created, so every line was lost. It now writes to `wp-content/uploads/klassifieds-for-woocommerce/ka-logs/`, which is created on first use with its own `index.html` and `.htaccess`. The SQL log, written when `SAVEQUERIES` is on, no longer ends in a fatal error when its file cannot be opened.
* Removed the admin notices code the plugin had taken over from WooCommerce. Its notices had the same names as WooCommerce's, and so did the user meta that remembers a dismissed notice, so dismissing a notice in either plugin hid the other's. The code that displayed them loaded a file that does not exist, and none of its notices was ever shown. The plugin now stores no user meta at all.
* Removed `Utils::_log()`, which nothing called and which replaced the day's log file instead of adding to it.

= 0.9.165 =

* Fixed: deleting the plugin could end in a fatal error. `uninstall.php` loaded a file that does not exist, and tried to load the whole plugin at a point where it is already deactivated and WooCommerce may be gone. Uninstall now runs from its own class, `Klassifieds_Uninstaller`, which loads nothing of the plugin.
* Fixed: uninstall could delete other plugins' data. It removed every option and user meta key matching `%klassifieds_%` — a leading wildcard, and an underscore that SQL reads as any character — and it deleted orphaned term relationships, terms and term meta across the whole site. It now removes only names that start with `klassifieds_`, matched literally, and deletes terms only by the IDs of the plugin's own taxonomies. Klassifieds Pro's options, its licence among them, are left to Pro's own uninstall.
* Uninstall now removes what it used to leave behind: the plugin's scheduled events and its folder under uploads, on every site of a network. Products of the plugin's own product types become WooCommerce simple and variable products, so they keep working afterwards. Shop content — products, their Klassifieds data, labels, and the "specials" and "rent" attributes — stays, unless `KLASSIFIEDS_REMOVE_ALL_DATA` is set to `true` in `wp-config.php`.
* Activation no longer changes WooCommerce's shop manager role. Earlier versions set 26 capabilities WooCommerce gives shop managers — editing and publishing posts and pages, managing categories, moderating comments, export and import among them — to false, so activating the plugin quietly took those rights away. On the first request after updating, each site gets back every one of them that is still denied, once; a capability you have changed yourself since is left as it is. Uninstall does the same.

= 0.9.164 =

* Seven of the eight bundled font families are gone. Gibson, Good Times, Good Times Bad Times, Adobe Handwriting Ernie and Venganza were licensed through Adobe Fonts, which does not permit self-hosting; Google Sans and Google Sans Text are Google's own brand faces and are not licensed to anyone else at all. Five of the seven were referenced by nothing, and Roboto — the one that could have stayed — only ever appeared as a fallback *name* in the mail and front-end stylesheets, which needs no bundled copy. The two that were really in use are replaced in kind, each under the SIL Open Font License with its licence beside it: the installer's mode label takes Orbitron, which also renders the weight that rule asks for where the old bundle had none and the browser was synthesising one; and the Get-PRO panel takes Herr Von Muellerhoff, a calligraphic script chosen against the original for stroke weight and slant, sized so the line keeps the width and the line box the panel was drawn around. The package sheds 26 files and 1.11 MB.
* The plugin no longer decides the typeface of the whole admin. Tailwind's preflight sets `font-family` on `html` rather than on the plugin's own markup, so on a Klassifieds screen this stylesheet was choosing the font for the admin menu, the toolbar and every notice WordPress renders. It uses the stack wp-admin declares for itself now, so the plugin's pages stop re-setting the admin's typography.

= 0.9.163 =

* Fixed: the settings import read its file with `unserialize()`. A file uploaded through the import form was handed to PHP's object deserializer, which builds objects out of whatever the file describes and runs their `__wakeup()` and `__destruct()` while doing it — and with WooCommerce loaded that is a wide surface. What the plugin did with the result afterwards made no difference: the work happens inside the call. Exports are JSON now and imports are read with `json_decode()`, which cannot construct an object at all. Settings files written by an earlier version are no longer accepted — export again from the site they came from.
* Removed the settings framework's `value_type` argument and the `serialize()` behind it. A field declaring `serialized` had its stored value serialized into the rendered input's `value` attribute, submitted back as that string and stored raw — so `get_option()` unserialized a string the browser had sent, every time it was read. No field ever declared it and the save path never knew it existed. Neither `serialize()` nor `unserialize()` appears anywhere in the plugin now.
* Import and export ask whether the user may act, not only where the request came from. Both had a nonce and no capability check, and `render_settings()` runs on `admin_init` for anyone who reaches wp-admin. Both now require the plugin's own capability — the one every AJAX handler was put behind at 0.9.161, and which this pair was missed out of.
* The import now checks the file it was actually handed: `is_uploaded_file()` before anything reads it, the size measured from the file on disk instead of the reported field, and the decoded contents required to be an array before they are passed on. A comment claimed the `is_uploaded_file()` check was made further down; it was made nowhere.
* Fixed: the exported file was run through `wp_kses_post()`, which strips markup out of the payload. Because PHP serialization carries the byte length of every string, any mapping containing an angle bracket exported as a file that could not be read back in.
* Fixed: the export sent its headers after the body, so on an installation without an active output buffer the `Content-Disposition` was ignored and the file rendered in the browser instead of downloading.
* Fixed: importing keyword categories read `description` without checking that it was there, and put it and the term name into `wp_insert_term()` unnarrowed. Entries that are not arrays are skipped now, and both fields are sanitized first.
* Fixed: the import branch was gated by the *export* filter, so an extension narrowing `klassifieds_export_settings_{tab}` switched importing off as well. Importing has its own `klassifieds_import_settings_{tab}` now.
* Fixed: an installation reported itself at capacity when it has none. The dashboard's capacity endpoint compared the ads managed against a capacity of `null`, and PHP reads `null` as zero in that comparison, so the answer was "reached" everywhere. Nothing was visibly capped — the notice and the snackbar are held back by the empty message that arrives with it — but the `ka::capacity-reached` cookie was written on every poll, and a single message filter would have turned that into a limit users could see.
* The capacity no longer registers a veto that cannot veto. Creating an ad asks the capacity again immediately after `klassifieds_pre_create_ad_product` has run, on the same object, so the callback on that filter could only repeat the answer it was about to give. The filter itself is untouched and still open to extensions.
* Removed the machinery for an ad allowance this edition does not have: the grandfathered floor with its stored option, and two licence-cache stubs that only threw. Every installation had been writing an option named `capacity_floor` that nothing ever read back.
* Licence and capacity wording throughout. A comment announcing "Ads the free edition manages" had outlived the constant it described and reattached itself to the quarantine lifetime. The refusal message said "This edition manages %d ads", where no edition here manages a number at all. Two docblocks spoke of a "licensed capacity" in code that runs in every edition.
* The account notice names the account type it actually means. "This software only supports Klassifieds Pro Accounts" meant the commercial seller account on kleinanzeigen.de, not the paid add-on that happens to share the name; it now says so, and says that ads from a private account cannot be imported. The greyed-out checkbox beside it is gone — it was permanently ticked, could not be changed, and no code read it.
* A German translation file for a text domain the plugin has not used since 0.9.135 is no longer packaged. Nothing loaded it, 281 of its 300 entries are in the file that is loaded, and none of the remainder is a string the plugin still has.
* Fixed: the stable tag named 0.9.161 while the package was 0.9.162.

= 0.9.162 =

* Every link in the plugin points at the plugin site rather than the company root, which is under construction: the Author URI in the header, five docblock links, and the trigger service's disclosure in this readme.
* The trigger service's terms and privacy policy are linked properly. The terms link went to a page carrying an Impressum and no terms at all; both now point at the real documents, in English.

= 0.9.161 =

* The free edition no longer manages a fixed number of ads. It was 50, enforced through the crawl limit, a task trim and a veto on creation — which is trialware under the WordPress.org guidelines, whatever the number. `capacity()` answers null, every gate stands down, and the licence wording is gone from this package. Klassifieds Pro enforces the capacity its licence was sold with, through the same helpers.
* The plugin no longer defines `DISABLE_WP_CRON`. It was set for the whole site, which stopped scheduled events for every other plugin installed alongside it — and it is what made the external trigger service load-bearing rather than optional. WordPress's own scheduler runs the jobs now.
* The trigger service is opt-in and off by default. Nothing is sent unless it is switched on under Klassifieds → Settings; activation no longer registers, and switching it off withdraws the registration. It sends the site address, the plugin version and the edition, once a day, and the plugin works without it.
* Fixed: the REST endpoints for updating and deleting a product accepted any authenticated user, with no capability check and no check that the product was theirs. Both ask `user_can()` now, and their filters can only narrow that answer.
* Fixed: the REST endpoints answered for any product that had *ever* been imported, because `_ka_internal_id` stays on a product for good. Membership is `_ka_active` now — what the install manages today.
* Every AJAX handler is behind the plugin's own capability. A nonce proves a request came from one of our screens; on a shop, every customer can obtain one.
* Fixed: `update_term_meta()` was writing both key and value straight from the request.
* Fixed: a job could run twice at once. The guard compared a transient against the string `'1'`, but an object cache hands the value back as an integer, so on every install with Redis or Memcached the guard reported no active job while one was running.
* Nothing is written inside the plugin folder any more. Logs and the fetch cache live under `wp-content/uploads/`, created with an index.html and a .htaccess. The plugin folder is replaced on every update and is read-only on many hosts.
* Output escaping and input sanitizing across the admin, the includes and the front end: 112 findings to none.
* `defined( 'ABSPATH' ) || exit;` in the eighteen files that lacked it.
* Ten of the thirteen inline `<script>` and `<style>` blocks are enqueued files now. Two of them shipped a copy of the same function for every row of a list. The three that remain are inside inline SVG, where they belong to the image.
* The admin menu sits at position 58, after the WooCommerce group, instead of 10 — the slot Media occupies.
* jetpack-autoloader 3.1.3 → 6.0.0, jetpack-constants 2.0.5 → 4.0.0.
* Fixed: an unused fallback in the onboarding logo referenced a remote image. It could never render, and only shipped the address.
* The readme describes what is imported and how, and documents both external services with their conditions and links.

= 0.9.160 =

* A local development configuration file is no longer packaged. It was read by the development environment only and had no part in the plugin at runtime.

= 0.9.159 =

* The "capacity reached" signal carries a message, not just a yes or no. It travels as an object now — the answer and the text that goes with it — so the snackbar can say *why* the limit was hit instead of only that it was. New `ajax_capacity` action, answered by `Klassifieds_Dashbord::get_capacity()`.
* Renamed throughout, from "max reached" to "capacity reached": the `ka:capacity-reached` and `ka:capacity-reached:change` events, the `ka::capacity-reached` cookie and the `.capacity-reached` class. The old names described a ceiling; what is actually being reported is a capacity, and the two are not the same thing.
* The shop summary tolerates missing values instead of assuming they are there, so a summary rendered before its counts have arrived shows zeroes rather than notices.

= 0.9.158 =

* The dot that opens the capacity message on the shop summary is legible on both grounds: it takes a border in the dark logo colour and an outline in the light one, so the ring stays visible whichever the header is set to. It is 15 pixels across and was previously lost against one of the two.

= 0.9.157 =

* The header logo is no longer one fixed colour scheme. Its fills come from two custom properties, `--logo-dark-color` and `--logo-light-color`, and the two admin screens set them the other way round: the dashboard keeps the violet mark on a light ground, the settings screen inverts it.
* `--vendor-light-color` is a faint violet (`#f6eeff`) rather than pure white, so the light half of the pair belongs to the same palette as the dark half.
* The dashboard's mode indicator follows the logo pair instead of `--mode-bg-color`, so it inverts with the screen it sits on.
* Its own version, because the plugin looks different while behaving identically — two packages that differ only in appearance must not share a version number.

= 0.9.156 =

* The admin header carries the new Klassifieds logo. It takes its colour from the `--vendor-dark-color` custom property instead of a hard-coded red, and brings its own animation with it.
* That property is violet (`#7300ff`) now, so the change is not limited to the logo: `--mode-bg-color` is derived from it and colours the dashboard's mode indicator too.
* Its own version, because the plugin looks different while behaving identically — two packages that differ only in appearance must not share a version number.

= 0.9.155 =

* `plugin-check.json` is no longer packaged into the plugin zip. It is a wp-env configuration for the plugin-check tool, pointing at localhost zips, and the only thing that reads it — the `wp plugin-check` scripts in `package.json` — is already excluded, so nothing shipped referred to it. No runtime change.

= 0.9.154 =

* The standalone tests are no longer packaged into the plugin zip (`/tests/ export-ignore`). They stub WordPress and run anywhere PHP does, which is the point of them — but they belong to development, not to a customer's server. No runtime change; only the contents of the zip differ, which is why this gets its own version rather than rebuilding 0.9.153 with different contents under the same number.

= 0.9.153 =

Note: the free edition still had an ad capacity when this shipped. It was removed
in 0.9.161, so the machinery described below bounds Klassifieds Pro today — the
free edition has no capacity for it to enforce.

* The capacity is now enforced where its numbers are *used*, not only through the filters that answer them. `klassifieds_task_items`, `klassifieds_pre_create_ad_product` and `klassifieds_index_limit` are filters, so a callback registered at a later priority had the last word on all three and could raise an install past what its licence covers with one `add_filter()` in a theme. Each consumer now asks the capacity again after the filter has run and combines the two answers with an operator that can only narrow: `min()` for the crawl limit, a re-filter for a task's items, and — a permission having no `min()` — asking again for the create veto. Narrowing an install from a filter still works exactly as before; only widening stops.
* New `Klassifieds_Capacity::set_authority()` / `bound()`: one non-filter way to ask who owns the capacity on this install. `capacity()` stands down under Pro and answers `null` there, which is the one answer a bound must never guess. First writer wins, so the claim cannot be taken back. Klassifieds Pro 0.9.145 claims it.
* New `Klassifieds_Capacity::confine_task_items()`, the single definition of the narrowing rule, applied by `Klassifieds_Task::items()`. `gate_task_items()` keeps answering the filter — that is what bounds an install whose task class predates the call — and keeps `invalid-ad`'s widening, which stays a filter because losing it costs a retirement cycle and not a slot.

= 0.9.152 =

Note: as above — the capacity these entries refine is Klassifieds Pro's now. The
free edition's own was removed in 0.9.161.

* The account index is never larger than the capacity. The limit was the capacity plus a page of reserve, and because pages arrive whole the crawl then stopped a page past that — a 120-ad capacity held 300 ads. The limit is now the capacity exactly and the fetched pages are trimmed to it.
* Consequence worth knowing: `entitled_ids()` drops quarantined ads before slicing, so without the reserve an ad that cannot be fetched costs a slot for good — five unfetchable ads in a 120-ad window mean 115 products.
* Capacity retirement no longer requires a complete index. It is the overage now, not membership, and a number a truncated index cannot inflate; requiring completeness would have switched retirement off permanently, since a capped index is truncated by design on any account larger than the capacity.

= 0.9.151 =

* The account modal collects the terms of use itself. Entering an account ID could never succeed while an agreement was unticked — the installer refuses the ID until `terms_of_use_confirmed` is `1` — but the modal showed the field anyway and let the user walk into a refusal that explains itself nowhere. The field is now disabled until the agreements hold, and the agreements are on the same screen instead of behind a link to the settings.
* Fixed: saving with an unticked agreement took the whole request down. The agreements are registered as strings but default to `false`, so `/wp/v2/settings` reports an unset one as `null` and then refuses that same `null` on the way back (`rest_invalid_stored_value`). The values are normalised to `"1"` or `""` before they are sent.
* Fixed: a listener in the account modal was added on every render and never removed.
* The terms-of-use view can be left on any save, not only on the one that completes it.
* German translations updated, including the script translations for the modal.

= 0.9.142 =

* Ad pages are now read through a list of candidate selectors instead of a single element id, so imports keep working when kleinanzeigen changes the page layout.
* Fixed: no images were imported when the gallery markup changed, because the fallback selector was never reached.
* Fixed: a changed address layout aborted the import with an uncaught error.
* Fixed: reading the account ID from an ad URL failed with a fatal error.
* Fixed: ads whose description could not be read stored "Not found" as the product description. The stored description is kept now and the ad is retried later.
* Fixed: umlauts could be garbled when reading ad pages.
* Ad page parsing moved into its own class, `Klassifieds_Vip_Document`.

== Upgrade Notice ==
= 0.9.207 =
New look for the dashboard and settings, light and dark. Klassifieds Pro 0.9.176 and older lose the colours of their product list with it: update Pro to 0.9.177.

= 0.9.206 =
Fixes installs that finished with 0 records, and a cleanup that left products behind. An empty read is tried again five times before the account ID is removed.

= 0.9.205 =
Fixes a broken admin layout on the dashboard of a site without an account in 0.9.204. Update.

= 0.9.204 =
Notices move to the top of the Klassifieds page; new dashboard logo and spacing. No other change.

= 0.9.203 =
Styling of the dashboard's "Upgrade now" box and "Sync now" button. No other change.

= 0.9.202 =
Fixes a fatal error on the dashboard without Klassifieds Pro, or with a Pro older than 0.9.175, in 0.9.200 and 0.9.201. Update.

= 0.9.201 =
Removes placeholder words from the dashboard's heading, which 0.9.200 showed. No other change.

= 0.9.200 =
New dashboard layout with a light and a dark mode. Update Klassifieds Pro to 0.9.175 as well: with an older Pro the list of ads stays empty.

= 0.9.199 =
The admin scripts are minified again; 0.9.198 shipped them unminified. No other change.

= 0.9.198 =
The dashboard offers the background service when scheduled jobs ran late, and cached ad pages no longer pile up in the options table.

= 0.9.197 =
The delete-images button works without Klassifieds Pro. Update Klassifieds Pro to 0.9.173 first: this release moves functions it needs into Pro.

= 0.9.196 =
The admin scripts are built from source and behave as before, with two small fixes. Update Klassifieds Pro to 0.9.172 as well.

= 0.9.195 =
Cookies are read more reliably, and the server-side bulk edit marker is gone. Update Klassifieds Pro to 0.9.171 as well.

= 0.9.194 =
The dashboard's rainbow border turns again instead of the whole header, and products without an image show WooCommerce's placeholder. Update Klassifieds Pro to 0.9.170 as well.

= 0.9.193 =
The installer shows its percentage again, which 0.9.192 left empty. Update Klassifieds Pro to 0.9.169 as well.

= 0.9.192 =
Ad pages are read once per product instead of once per field, and a product keeps its description when its ad cannot be read. Update this plugin first, then Klassifieds Pro to 0.9.168, which needs it.

= 0.9.191 =
The bundled German translation is loaded again; from 0.9.168 to 0.9.190 the plugin stayed English on a German site. Klassifieds Pro needs no update.

= 0.9.190 =
Finishes the author rename that 0.9.189 announced: the admin page header now credits WebPremiere. No behaviour changes. Klassifieds Pro needs no update.

= 0.9.189 =
New artwork throughout, and the dashboard's card for ad products now reads "Kleinanzeigen". The plugin's author name is now WebPremiere. Klassifieds Pro needs no update.

= 0.9.187 =
Products without a price keep an empty price instead of 0. Klassifieds Pro needs no update.

= 0.9.186 =
Affiliate mode: Klassifieds products count as purchasable. Klassifieds Pro needs no update.

= 0.9.185 =
In affiliate mode, products without a price offer to visit the ad instead of buying it. Klassifieds Pro needs no update.

= 0.9.184 =
Products created for new ads get their price, categories, labels and brands again. Run "Rebuild product data" on the Tools tab once to fill in the ones made since 0.9.160. Using Klassifieds Pro? Update it to 0.9.166.

= 0.9.183 =
Security fixes: posts no longer keep script from contributors, and the product list's actions check your rights on each product. Recommended for every installation.

= 0.9.182 =
Update Klassifieds Pro to 0.9.164 at the same time.

= 0.9.181 =
Update Klassifieds Pro to 0.9.162 first. A site still on 0.9.173 or older updates to 0.9.180 first.

= 0.9.180 =
Plainer wording on the General tab, and a fix for the image-import message in German.

= 0.9.179 =
Products whose ad comes back no longer stay drafts on busy accounts. Publish any that already did by hand.

= 0.9.178 =
Switching on the trigger service registers your site right away. Update Klassifieds Pro to 0.9.161 first.

= 0.9.177 =
Reads your whole account instead of only its first page of ads.

= 0.9.176 =
Maintenance: no shell commands, and debug output only with WP_DEBUG_LOG.

= 0.9.175 =
Security hardening: the product list's save request accepts only a few sanitized fields.

= 0.9.174 =
Removes the plugin's data under its old names, which 0.9.173 copied. Afterwards,
do not go back to 0.9.171 or older: it would import your ads again.

= 0.9.173 =
Fixes a fatal error on every page in 0.9.172, and carries out its update. Using
Klassifieds Pro? Update it to 0.9.154 or later first.

= 0.9.172 =
Gives the plugin's tables, meta keys and shortcodes its own prefix and copies your
data over on the first request. Using Klassifieds Pro? Update it to 0.9.154 first;
older versions stop with a fatal error.

= 0.9.171 =
Removes the capacity and licence code Klassifieds Pro relied on. Using Klassifieds
Pro? Update it to 0.9.153 first; older versions stop with a fatal error.

= 0.9.170 =
Fixes errors in the admin screens that WordPress's linters found, and keeps the
browser console quiet unless WP_DEBUG is on.

= 0.9.169 =
Ships the sources of its compiled files. Using Klassifieds Pro? Update it to
0.9.152, which stops stored images from being discarded on every sync.

= 0.9.168 =
Stores ad images in your media library instead of linking them from
kleinanzeigen.de, and keeps drafts out of the public REST API. Using Klassifieds
Pro? Update it to 0.9.151 first.

= 0.9.167 =
Security fixes: no more login from URL parameters, and every admin action, setting
and import is checked. Using Klassifieds Pro? Update it together with this version.

= 0.9.166 =
Fixes debug logging, which never wrote a line, and removes admin notice code
taken over from WooCommerce that mixed its dismissed notices with WooCommerce's.

= 0.9.165 =
Fixes uninstall, which could fail with a fatal error and delete other plugins'
data, and gives shop managers back the rights earlier versions removed from
their role.

= 0.9.164 =
Appearance and packaging. The admin screens leave WordPress's own typeface alone
instead of replacing it, and the bundled fonts that could not be redistributed are
gone. Nothing behaves differently.

= 0.9.163 =
Security fix: the settings import no longer reads its file with unserialize(),
and importing and exporting settings now check that the user may do so. Note the
one breaking change — settings files exported by an earlier version are no longer
accepted, so export again from the site they came from. Also fixes an
installation reporting itself at capacity when it has none. Recommended for every
installation.

= 0.9.157 =
Appearance only: the header logo takes its colours from a pair of custom
properties that the dashboard and the settings screen set in opposite order, so
the two screens carry the mark inverted from each other. Nothing behaves
differently.

= 0.9.156 =
Appearance only: the admin header carries the new logo and takes its colour from
a custom property, which is violet now. Nothing behaves differently.

= 0.9.155 =
Packaging only: a wp-env configuration file no longer travels in the plugin zip.
Nothing on your site behaves differently.

= 0.9.154 =
Packaging only: the standalone developer tests no longer travel in the plugin
zip. Nothing on your site behaves differently.

= 0.9.153 =
The capacity is now enforced where its numbers are used, so a filter registered
at a later priority can still narrow an installation but no longer widen it past
what its licence covers. (The free edition's own capacity was removed in 0.9.161;
what this bounds today is Klassifieds Pro.) Recommended for every installation.
