The cookie sits in the corner
A client’s WordPress site needed a cookie consent banner before the plugin’s license key had arrived. The banner itself doesn’t check the key, so I configured Complianz on the QA database by hand, in phpMyAdmin, one statement at a time. At the end, the full-width bar across the bottom of every page was gone. In its place is a small cookie button in the bottom left that opens a sheet when you want it.
The setup is live on QA, and the license-only pieces are waiting for the key. Everything else, from the wizard answers to the policy links and the banner’s look, is in place.
This post covers each statement that ran, what it was for, and how to read phpMyAdmin’s row counts, including the two where 0 rows was a real failure and the one where 0 rows was exactly right. It also covers turning the bar into a corner button, and the few screens in the plugin you shouldn’t press Save on.
Reading the results
Two things show up in phpMyAdmin that are easy to misread.
Warning #1287 isn’t a failure. On this MariaDB version, any INSERT … ON DUPLICATE KEY UPDATE … VALUES() prints a yellow #1287 saying VALUES() is deprecated. The row is still saved.
The row count is the result. “0 rows inserted” or “0 rows affected” means the statement matched nothing. On every statement here but one, that’s the failure. On the safe-mode check it’s the answer I wanted.
| Statement | Expected | What 0 rows means |
|---|---|---|
| Wizard answers | 1 inserted or updated | Nothing written |
| Wizard completed flag | 1 inserted or updated | Nothing written |
| Enable default banner | 1 affected | No default banner row |
| Link a policy page by slug | 1 inserted | No published page with that slug |
| Splice into the serialized option | 1 affected | The value no longer starts the way the statement expects |
| Safe-mode check | 0 affected | Correct: safe mode was never on |
Write the wizard answers
Complianz keeps its wizard answers in one option, cmplz_options, as a serialized PHP array. One insert stored a US-only setup with 19 keys:
INSERT INTO wp_options (option_name, option_value, autoload)
VALUES ('cmplz_options', 'a:19:{s:11:"use_cdb_api";s:3:"yes"; … }', 'yes')
ON DUPLICATE KEY UPDATE option_value = VALUES(option_value), autoload = 'yes';
What the keys say, in plain terms:
- Everyone gets the US banner.
use_countryis false andregionsisus, so there’s no geolocation lookup.other_region_behaviourisnone. - California is included.
us_statesstorescal=1, which gives the “Do Not Sell My Personal Information” form a state to attach to. - Consent records and data requests are on, and
respect_dnthonors Do Not Track. - The site’s own policy pages are used.
cookie-statementandprivacy-statementarecustom, andimpressumisnone, so Complianz doesn’t generate a competing privacy statement. - Statistics go through the existing tag manager.
compile_statisticsisgtg, withgoogle-tag-managerandconsent-modeon, andconfiguration_by_complianzisno. Complianz prints the Consent Mode defaults onwp_headat priority 0. The theme prints its tag manager snippet at priority 1. Complianz must not add a second container. - Ads, but not personalized ones.
uses_ad_cookiesisyesanduses_ad_cookies_personalizedisno, which keeps the IAB framework (TCF) off.
Mark the wizard complete, and turn the banner on
The banner stays hidden until Complianz thinks the wizard has run once. That’s its own option, separate from the answers:
INSERT INTO wp_options (option_name, option_value, autoload)
VALUES ('cmplz_wizard_completed_once', '1', 'yes')
ON DUPLICATE KEY UPDATE option_value = '1', autoload = 'yes';
Then the default banner row has to be enabled:
UPDATE wp_cmplz_cookiebanners
SET position = 'bottom', disable_cookiebanner = 0
WHERE `default` = 1;
It still didn’t show. All three were stored, and the page looked the same. The site runs a cache layer, Redis, a caching plugin or both, and the old options were cached. After a purge, the bar appeared. Purge after every change in this post.
Link the policy pages, and the slug that wasn’t published
The cookie policy link isn’t in cmplz_options. It’s its own row, cmplz_cookie-statement_custom_page, holding a page ID. I looked the page up by slug:
INSERT INTO wp_options (option_name, option_value, autoload)
SELECT 'cmplz_cookie-statement_custom_page', ID, 'yes'
FROM wp_posts
WHERE post_type = 'page' AND post_name = 'cookie-settings' AND post_status = 'publish'
LIMIT 1;
0 rows inserted, the first time. The Cookie Settings page existed as a draft, and the statement asks for publish, the same rule WordPress’s get_page_by_path follows. There was nothing to select. The #1287 warning showed up on that run too, but it wasn’t the reason.
After publishing the page, I deleted the old option rows and ran the insert again: 1 row inserted. That ID is the one the banner links to.
The page content matters too. It has to be a Shortcode block in the post content, because custom fields don’t run shortcodes. And the shortcode needs the region:
[cmplz-document type="cookie-statement" region="us"]
Without region="us" it falls back to the EU and prints “Region EU not activated for cookie-statement.”
Two more pages were made in the admin, not in SQL: Your Privacy Choices with [cmplz-dnsmpi-request], which becomes a footer link once published, and Privacy Request with [cmplz-data-request], which isn’t linked from the footer. The privacy policy already existed as page 3.
The option that wasn’t there
I tried to copy the privacy page ID from WordPress’s own setting into Complianz’s:
INSERT INTO wp_options (option_name, option_value, autoload)
SELECT 'cmplz_privacy-statement_custom_page', option_value, 'yes'
FROM wp_options
WHERE option_name = 'page_for_privacy_policy' AND option_value NOT IN ('', '0')
LIMIT 1;
0 rows. There was no option by that name. WordPress stores Settings → Privacy as wp_page_for_privacy_policy, with the prefix. So I read the pages instead, found the published privacy-policy page, ID 3, and inserted cmplz_privacy-statement_custom_page from its slug, like the cookie page. 1 row. The Privacy Statement link at the bottom of the cookie policy then opened the right page.
A second insert in that batch wrote page_for_privacy_policy too, the same unprefixed name. It’s harmless, but nothing reads it. Check the real option name before you write one.
Splicing into a serialized option
The US cookie document prints the company address, but the wizard hides the company fields while the documents are set to custom. So the address went into cmplz_options directly.
Serialized PHP counts everything. The array starts with its number of keys, a:19:{, and every string with its length in bytes, s:15:"address_company". Add a key and both have to change, or PHP can’t read the option at all and the plugin loses every answer.
So the statement only runs if the value still starts exactly as expected, then bumps the count to 20 and puts the new key in front of the old first one. The address here is a placeholder. Note the byte count: three lines of 18, 9 and 21 characters plus two newlines is 50.
UPDATE wp_options
SET option_value = REPLACE(
option_value,
'a:19:{s:11:"use_cdb_api"',
CONCAT(
'a:20:{s:15:"address_company";s:50:"',
'100 Example Street', CHAR(10),
'Suite 200', CHAR(10),
'Springfield, ST 00000',
'";s:11:"use_cdb_api"'
)
)
WHERE option_name = 'cmplz_options'
AND option_value LIKE 'a:19:{s:11:"use_cdb_api"%';
1 row affected. 0 would mean the value had already changed, and the guard did its job.
The Finish screen still listed five unanswered questions and two required radios. A question can show a default and still count as unanswered when its key was never stored. The same guarded replace took the array from 20 keys to 27: no disclaimer, the company country, no social media embeds, no Amazon consent signal (it’s for UK and EEA ads), the banner and the script blocker both on, and five purposes for collecting personal data, without selling data to third parties. After a purge, the questions were gone.
Organisation name, email and phone stayed empty. I didn’t invent them, and the phone field is locked until there’s a license.
Two things not to touch
Safe mode was never on. I thought it was stuck, because the Finish screen shows a box titled Using Safe Mode. That’s help text beside the blocker radio. Safe mode is a checkbox under Tools → Support, the inverse of the script blocker, and writing the blocker’s answer in SQL doesn’t run the plugin’s own mapping. A guarded replace from safe_mode 1 to 0 matched 0 rows, which is the right answer: it was never stored as on.
Don’t save the Visitors screen. California is stored, which is why the opt-out form renders. But that screen’s California checkbox draws empty. Pressing Save there would write the empty box back and remove California. I left it unsaved.
Waiting on the license
Four things are blocked until the key arrives:
- The cookie scan, the one remaining setup step.
- The cookiedatabase.org sync. It’s switched on, and the wizard says it needs the domain authenticated first.
- The phone field, and with it the rest of the company details.
- TCF, which is off on purpose, since ads aren’t personalized.
From a bar to a button
Complianz already prints both a banner and a small “manage consent” button. The theme had been forcing the banner open and hiding the button. Swapping that around took a small script and a stylesheet partial. The styles are compiled into the critical CSS, so the plugin’s default card never flashes before the full stylesheet loads.
The banner, as a visitor meets it
US site, opt-out consent. Not to scale.
message closed
max(40vh, 500px) tall
Opt-out changes what “consent” means. On a US banner, cmplz_has_consent() is already true before anyone clicks. So the theme hides the button only when a category cookie is explicitly allow. Reject writes deny, and the button stays. An empty cookie means no choice yet. On load, if the visitor hasn’t accepted, the theme marks the banner dismissed without recording a denial, so the sheet doesn’t pop open on its own.
Colors come from the plugin, not over it. The theme had been painting over the banner with !important, so the plugin’s color pickers did nothing. That’s gone. The colors now come from the banner record: dark background, light text, the accent color for Accept, and a mid-tone for the second button. Complianz writes them to a generated CSS file when the banner is saved.
Complianz also hides the Deny label and its color picker on opt-out banners. A small filter in the theme shows both fields and labels the button Reject. The opt-out stylesheet still hides that button, so the theme only forces it to display. It doesn’t set its colors.
This one had to be a plugin save, not SQL. The color file is generated when the banner is saved in the admin. A raw update of the banner row doesn’t regenerate it. So QA still shows the plugin defaults, the label Deny and the default gray and purple, until the same save runs there. The theme’s compiled CSS reaches QA through the build. The banner’s color file is generated on the server and was never in git.
What I learned
Looking back, four lessons stand out.
Read the row count, not the warning. The yellow #1287 was on almost every insert and meant nothing. The two real failures were quiet “0 rows” results.
Look up by published slug, and publish first. A draft page is invisible to the same lookup WordPress itself uses.
Guard every edit to a serialized value. Match the exact prefix you expect, and change the key count and byte lengths together. A wrong count breaks every answer in the option, not just the new one.
Some settings only exist after a save. Generated files, mappings between options and the plugin’s own side effects don’t happen when you write the row yourself.
What’s next?
When the license arrives: the cookie scan, the cookiedatabase.org sync, the company details, and the banner save on QA so the colors match.
The site went from a bar that covered the bottom of every page to a cookie that sits in the corner until someone wants it.