{"id":322632,"date":"2026-07-30T12:25:18","date_gmt":"2026-07-30T12:25:18","guid":{"rendered":"https:\/\/de.wordpress.org\/plugins\/adengine-free-ad-inserter-manager\/"},"modified":"2026-08-23T19:22:44","modified_gmt":"2026-08-23T19:22:44","slug":"hafenstudios-ads","status":"publish","type":"plugin","link":"https:\/\/ar.wordpress.org\/plugins\/hafenstudios-ads\/","author":23511618,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.4.0","stable_tag":"2.4.0","tested":"7.1","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Hafenstudios Ads \u2013 Ad Manager, AdSense & Banner Ads","header_author":"hafenstudios","header_description":"A privacy-aware ad placement manager for WordPress: automatic placements, shortcodes, widgets, targeting, consent handling, lazy loading and lightweight stats.","assets_banners_color":"42ad9e","last_updated":"2026-08-23 19:22:44","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/hafenstudios.com\/","header_author_uri":"https:\/\/hafenstudios.com","rating":5,"author_block_rating":0,"active_installs":100,"downloads":1863,"num_ratings":14,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.8.4":{"tag":"1.8.4","author":"hafenstudios","date":"2026-07-30 12:24:57"},"1.8.5":{"tag":"1.8.5","author":"hafenstudios","date":"2026-07-30 14:37:59"},"1.8.6":{"tag":"1.8.6","author":"hafenstudios","date":"2026-07-31 12:40:10"},"1.8.7":{"tag":"1.8.7","author":"hafenstudios","date":"2026-07-31 13:33:05"},"1.9.0":{"tag":"1.9.0","author":"hafenstudios","date":"2026-08-01 10:10:34"},"2.0.0":{"tag":"2.0.0","author":"hafenstudios","date":"2026-08-06 05:50:49"},"2.1.0":{"tag":"2.1.0","author":"hafenstudios","date":"2026-08-13 07:10:51"},"2.1.1":{"tag":"2.1.1","author":"hafenstudios","date":"2026-08-13 10:41:16"},"2.2.0":{"tag":"2.2.0","author":"hafenstudios","date":"2026-08-13 18:21:11"},"2.3.0":{"tag":"2.3.0","author":"hafenstudios","date":"2026-08-17 00:59:04"},"2.3.1":{"tag":"2.3.1","author":"hafenstudios","date":"2026-08-18 11:24:24"},"2.4.0":{"tag":"2.4.0","author":"hafenstudios","date":"2026-08-23 19:22:44"}},"upgrade_notice":{"2.4.0":"<p>Six new templates, ads.txt, space reservation against layout shift, non-personalised AdSense on declined consent, AdSense fallback, page-type and role targeting, three new content positions, sticky sidebar ads, a diagnosis view, CSV export and an importer for Ad Inserter and Advanced Ads. Existing blocks render unchanged.<\/p>","2.3.2":"<p>Media library picker, real date pickers, scheduling and design fields inside the template assistant, and a non-destructive way from template to custom HTML. Nothing changes for stored ads.<\/p>","2.3.1":"<p>Fixes the consent banner flashing on every page view for visitors who already decided. Nothing changes for visitors who have not decided yet: the banner still shows by default, deliberately, because hiding it by script would take the choice away from anyone using an ad blocker.<\/p>","2.3.0":"<p>Measurement clean-up: overlay impressions and form clicks were miscounted and are now honest, so expect lower impression and click numbers after the update. That is the fix, not a delivery problem. Split tests count from the day they start, consent banners move to their own System section, Consent Mode v2 gets its missing default, and Brevo can run real double opt-in.<\/p>","2.2.0":"<p>New Split-Tests page: the variants of one rotation group side by side with impressions, clicks, CTR and who is ahead. Newsletter signups no longer reveal whether an address is already on your list: a known address gets the same confirmation as a new one, and no provider message reaches your visitors any more. Since a confirmation no longer proves that a contact was created, watch the new warning on the ad block overview, it names the last signup that did not go through.<\/p>","2.1.1":"<p>Ad block numbers are never reused, so a new block can no longer inherit a deleted one&#039;s statistics or take over its shortcode. Your totals drop slightly: a period used to sum up one day more than its label promised. Nothing was lost. Tested with WordPress 7.1.<\/p>","2.1.0":"<p>Cache-proof ad rotation in automatic placements, impressions counted on actual visibility (numbers drop, CTR rises - intentional), overlay targeting, new AdSense formats, and Kit (ConvertKit) as the fifth newsletter provider.<\/p>","2.0.0":"<p>Five new free templates, &quot;show only in categories\/tags&quot; targeting, cache-proof statistics and a consent-aware AdSense loader. The opt-in and tracking endpoints no longer break on page-cached sites.<\/p>","1.9.0":"<p>Adds MailerLite, Brevo and Mailchimp as newsletter providers next to SendFox. An existing SendFox connection keeps working; nothing needs to be re-entered.<\/p>","1.8.7":"<p>Uninstalling no longer deletes your data unless you switch on &quot;Delete all data on uninstall&quot; in the settings. Adds a weekly summary above the ad block list and complete Spanish, French and Dutch translations.<\/p>","1.8.4":"<p>Renaming only: the plugin is now called &quot;Hafenstudios Ad Placement Manager&quot;. Your ad blocks, settings and statistics stay exactly as they are.<\/p>","1.8.2":"<p>The legacy <code>[sai_ad]<\/code> and <code>[sai_ad_group]<\/code> shortcodes from AdEngine were removed. If you still have them in your content, switch to <code>[adjet_ad]<\/code> and <code>[adjet_ad_group]<\/code>.<\/p>","1.8.1":"<p>Maintenance release: valid declared URLs and all JavaScript\/CSS moved into properly enqueued files. No action needed.<\/p>","1.8.0":"<p>Adds a built-in cookie-consent banner (Google Consent Mode v2) and a native SendFox newsletter integration. Both optional; the plugin stays fully functional without them.<\/p>","1.7.0":"<p>Big update since 1.6.0: template gallery, guided assistant with optional AI copy suggestions (bring your own Anthropic key, stored encrypted), a Google AdSense assistant, site-wide overlays and first-run onboarding. Free stays fully functional.<\/p>","1.6.0":"<p>Adds the manage_adjet capability, a REST API and a statistics chart. No action required.<\/p>","1.5.0":"<p>Rebrand with a refreshed admin interface. Existing ad blocks, settings and stats are migrated automatically.<\/p>"},"ratings":{"1":0,"2":0,"3":0,"4":0,"5":14},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3628546,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3628546,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3650300,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3650300,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":{"adjet\/ad":{"name":"adjet\/ad","title":"Adjet Ad"}},"tagged_versions":["1.8.4","1.8.5","1.8.6","1.8.7","1.9.0","2.0.0","2.1.0","2.1.1","2.2.0","2.3.0","2.3.1","2.4.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3650300,"resolution":"1","location":"assets","locale":"","width":2560,"height":1600},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3650300,"resolution":"2","location":"assets","locale":"","width":2560,"height":1600},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3650300,"resolution":"3","location":"assets","locale":"","width":2560,"height":1600},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3650300,"resolution":"4","location":"assets","locale":"","width":2560,"height":1600},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3650300,"resolution":"5","location":"assets","locale":"","width":2560,"height":1600},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3650300,"resolution":"6","location":"assets","locale":"","width":2560,"height":1600}},"screenshots":{"1":"The ad blocks overview with impressions, clicks and CTR per block.","2":"The split test view: variants side by side, the data progress gauge, and a mark on the one that is ahead.","3":"The template gallery: pick a purpose, fill in the fields, no HTML needed.","4":"The cookie consent banner assistant with selectable cookie images.","5":"The AdSense assistant: Publisher-ID, slot and format, nothing else.","6":"Local statistics with the 30-day chart, rendered server-side without any external service."}},"plugin_section":[],"plugin_tags":[2533,321,148,368,4084],"plugin_category":[35,43],"plugin_contributors":[273845],"plugin_business_model":[],"class_list":["post-322632","plugin","type-plugin","status-publish","hentry","plugin_tags-ad-manager","plugin_tags-ads","plugin_tags-adsense","plugin_tags-advertising","plugin_tags-banner-ads","plugin_category-advertising","plugin_category-customization","plugin_contributors-hafenstudios","plugin_committers-hafenstudios"],"banners":{"banner":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/banner-772x250.png?rev=3650300","banner_2x":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/banner-1544x500.png?rev=3650300","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/icon-128x128.png?rev=3628546","icon_2x":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/icon-256x256.png?rev=3628546","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-1.png?rev=3650300","caption":"The ad blocks overview with impressions, clicks and CTR per block."},{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-2.png?rev=3650300","caption":"The split test view: variants side by side, the data progress gauge, and a mark on the one that is ahead."},{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-3.png?rev=3650300","caption":"The template gallery: pick a purpose, fill in the fields, no HTML needed."},{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-4.png?rev=3650300","caption":"The cookie consent banner assistant with selectable cookie images."},{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-5.png?rev=3650300","caption":"The AdSense assistant: Publisher-ID, slot and format, nothing else."},{"src":"https:\/\/ps.w.org\/hafenstudios-ads\/assets\/screenshot-6.png?rev=3650300","caption":"Local statistics with the 30-day chart, rendered server-side without any external service."}],"raw_content":"<!--section=description-->\n<p>Hafenstudios Ads is an ad manager for WordPress: place banner ads, AdSense units and any ad code exactly where you want them, rotate them, target who sees them, and keep an eye on impressions, clicks and CTR \u2014 without sending any analytics to an external server.<\/p>\n\n<p>Create multiple ad blocks, insert them automatically before or after the content, after paragraph N or by shortcode, widget and Gutenberg block. Weighted ad rotation, split testing, device and post-type targeting, campaign scheduling and consent gating are built in.<\/p>\n\n<h4>Highlights<\/h4>\n\n<ul>\n<li>Split test two ad variants against each other: every variant with impressions, clicks and CTR, a data progress gauge, and a mark on the one that is ahead.<\/li>\n<li>Weighted ad rotation, cache-proof: blocks sharing a group name rotate in automatic placements and via <code>[adjet_ad_group name=\"header\"]<\/code>; the winner is picked in the visitor's browser, so page caches cannot freeze the choice.<\/li>\n<li>AdSense Wizard: Publisher-ID + Slot-ID are enough for responsive AdSense units, including in-article and multiplex formats.<\/li>\n<li>Nothing leaves your server: no external calls, no phoning home, statistics stay in your own database.<\/li>\n<li>Template gallery with 27 free templates: banners, native cards, testimonial, book\/course card, seasonal deals, events, sponsor logo wall, notification bars, popups and a cookie consent banner with selectable cookie images.<\/li>\n<li>Automatic placements before content, after content, after paragraph N, in the middle of the content, before the last paragraph, after heading N or after the first image.<\/li>\n<li>Manual placement via shortcode <code>[adjet_ad id=\"1\"]<\/code>, widget or Gutenberg block.<\/li>\n<li>Clean, focused admin interface with brand bar, weekly summary and clear navigation tabs.<\/li>\n<li>Device targeting for desktop, tablet and mobile.<\/li>\n<li>Post type targeting plus exclusions for posts, categories, tags and custom terms.<\/li>\n<li>Optional \"Advertisement\" label with editable text.<\/li>\n<li>Lazy loading via IntersectionObserver.<\/li>\n<li>Consent-cookie gating for common consent tools.<\/li>\n<li>Optional anonymous local impression and click statistics.<\/li>\n<li>Campaign scheduling with optional start and end time.<\/li>\n<li>One-click duplicate and pause\/activate actions for faster workflow.<\/li>\n<li>Code editor and admin-side live preview that does not execute scripts.<\/li>\n<li>AdSense Wizard: Publisher-ID + Slot-ID are enough for responsive AdSense units.<\/li>\n<li>Optional AdSense Auto Ads script loader with clear settings.<\/li>\n<li>30-day statistics chart (impressions and clicks) rendered inline, no external libraries.<\/li>\n<li>Weekly summary above the ad block list: impressions, clicks and CTR since Monday, compared with the same weekday span of the previous week.<\/li>\n<li>Least-privilege capability <code>manage_adjet<\/code> with an optional editor-access toggle.<\/li>\n<li>Premium templates and features unlock with Pro.<\/li>\n<li>REST API (<code>adjet\/v1<\/code>) for ad blocks, stats and settings via Application Passwords.<\/li>\n<li>Guided assistant with optional AI copy suggestions (bring your own Anthropic Claude key).<\/li>\n<li>Built-in cookie-consent banner template with Google Consent Mode v2 support.<\/li>\n<li>Native newsletter opt-in for SendFox, MailerLite, Brevo, Mailchimp and Kit (ConvertKit), bring your own token, with double opt-in.<\/li>\n<li>First-run onboarding that routes you to AdSense, a template or your own HTML.<\/li>\n<li>JSON import and export with file-size\/type checks.<\/li>\n<li>Uninstall routine that keeps your data unless you explicitly ask for it to be deleted.<\/li>\n<\/ul>\n\n<h4>Privacy<\/h4>\n\n<p>The plugin does not contact hafenstudios servers from the WordPress admin area or frontend. Optional statistics are stored locally in your own WordPress database. Third-party ad code pasted into an ad block may contact external services; configure your consent solution accordingly.<\/p>\n\n<h4>External services<\/h4>\n\n<p>The plugin works fully without any external service. Every integration below is optional and only ever runs when you explicitly enable it:<\/p>\n\n<ul>\n<li>Google AdSense (frontend): if you configure an AdSense ad unit or enable Auto Ads, the frontend loads Google's <code>adsbygoogle.js<\/code> from <code>pagead2.googlesyndication.com<\/code> to display ads. Terms: https:\/\/www.google.com\/adsense\/localized-terms \u2014 Privacy: https:\/\/policies.google.com\/privacy<\/li>\n<li>Anthropic Claude (admin only, bring-your-own-key): the optional AI buttons send data to the Anthropic API (<code>api.anthropic.com<\/code>), each only after you enter your own Anthropic API key in the settings and click that button; no key, no request, never automatic. What each button sends: the \"AI suggestions\" button in the ad assistant sends the template's field labels, your site name\/description and any brief you type to generate draft ad copy; the \"Suggest alt text\" button sends the selected ad image (from your media library as encoded image data, otherwise as its URL) to describe it; the \"Let the AI read the numbers\" button on the statistics page sends aggregated daily totals, weekly totals and per-block impression\/click counts. No visitor data, IP addresses or e-mail addresses are ever sent. The key is stored encrypted and used server-side only. Terms: https:\/\/www.anthropic.com\/legal\/commercial-terms \u2014 Privacy: https:\/\/www.anthropic.com\/legal\/privacy<\/li>\n<li>SendFox (bring-your-own-token): if you connect SendFox in the settings and use the \"Newsletter (SendFox)\" template, visitor email addresses submitted through that form are sent to the SendFox API (<code>api.sendfox.com<\/code>) to be added to your list. This happens only when you have connected a SendFox access token. The token is stored encrypted and used server-side only. Terms: https:\/\/sendfox.com\/tos \u2014 Privacy: https:\/\/sendfox.com\/privacy<\/li>\n<li>MailerLite (bring-your-own-token): if you connect MailerLite in the settings and use the \"Newsletter (MailerLite)\" template, visitor email addresses submitted through that form are sent to the MailerLite API (<code>connect.mailerlite.com<\/code>) to be added to your group. This happens only when you have connected a MailerLite API token. The token is stored encrypted and used server-side only. Terms: https:\/\/www.mailerlite.com\/legal\/terms-of-service \u2014 Privacy: https:\/\/www.mailerlite.com\/legal\/privacy-policy<\/li>\n<li>Brevo (bring-your-own-token): if you connect Brevo in the settings and use the \"Newsletter (Brevo)\" template, visitor email addresses submitted through that form are sent to the Brevo API (<code>api.brevo.com<\/code>). With a double opt-in template and redirect URL configured in the settings, the signup uses Brevo's double opt-in endpoint (<code>\/contacts\/doubleOptinConfirmation<\/code>) and Brevo sends the confirmation mail; without them the contact is created directly and no confirmation mail is sent, which the form's success message states honestly. This happens only when you have connected a Brevo API key. The key is stored encrypted and used server-side only. Terms: https:\/\/www.brevo.com\/legal\/termsofuse\/ \u2014 Privacy: https:\/\/www.brevo.com\/legal\/privacypolicy\/<\/li>\n<li>Mailchimp (bring-your-own-token): if you connect Mailchimp in the settings and use the \"Newsletter (Mailchimp)\" template, visitor email addresses submitted through that form are sent to the Mailchimp API (<code>&lt;dc&gt;.api.mailchimp.com<\/code>, the data center encoded in your API key) to be added to your audience with status \"pending\", which triggers Mailchimp's double opt-in. This happens only when you have connected a Mailchimp API key. The key is stored encrypted and used server-side only. Terms: https:\/\/mailchimp.com\/legal\/terms\/ \u2014 Privacy: https:\/\/www.intuit.com\/privacy\/statement\/<\/li>\n<li>Kit, formerly ConvertKit (bring-your-own-token): if you connect Kit in the settings and use the \"Newsletter (Kit)\" template, visitor email addresses submitted through that form are sent to the Kit API (<code>api.kit.com<\/code>) to create or update the subscriber and add them to your chosen Kit form; if the form has double opt-in enabled, Kit sends its confirmation email. This happens only when you have connected a Kit v4 API key. The key is stored encrypted and used server-side only. Terms: https:\/\/kit.com\/terms \u2014 Privacy: https:\/\/kit.com\/privacy<\/li>\n<\/ul>\n\n<h4>Security<\/h4>\n\n<p>The plugin uses capability checks, nonces for form actions, sanitization on save and escaping on output. Raw ad code is only stored unchanged for users with the <code>unfiltered_html<\/code> capability.<\/p>\n\n<h4>Localization<\/h4>\n\n<ul>\n<li>Source language: English.<\/li>\n<li>Included translations: German, Spanish, French and Dutch \u2014 complete and shipped with the plugin.<\/li>\n<\/ul>\n\n<h4>Support<\/h4>\n\n<p>This plugin is developed and maintained by hafenstudios. For direct support you can reach us by e-mail at moin@hafenstudios.com or by phone at +49 174 3935796. You are also welcome to use the plugin support forum on WordPress.org.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the ZIP via Plugins &gt; Add New &gt; Upload Plugin.<\/li>\n<li>Activate the plugin.<\/li>\n<li>Open the new \"Hafenstudios Ads\" menu and create your first ad block.<\/li>\n<li>Optional: configure the ad label, consent-cookie settings and local statistics.<\/li>\n<\/ol>\n\n<p>Upgrading from \"AdEngine \u2013 Free Ad Inserter &amp; Manager\"? Just install this plugin \u2014 your ad blocks, settings and statistics are migrated automatically on first load.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20work%20with%20adsense%3F\"><h3>Does this plugin work with AdSense?<\/h3><\/dt>\n<dd><p>Yes. You can either paste original AdSense code as custom code or use the AdSense Wizard with your Publisher-ID and Slot-ID. The plugin then generates a responsive ad unit and loads the AdSense script on the frontend when needed.<\/p><\/dd>\n<dt id=\"can%20i%20use%20it%20manually%3F\"><h3>Can I use it manually?<\/h3><\/dt>\n<dd><p>Yes. Use the shortcode <code>[adjet_ad id=\"1\"]<\/code>, the shortcode <code>[adjet_ad_group name=\"header\"]<\/code>, the included widget or the Gutenberg block.<\/p><\/dd>\n<dt id=\"does%20it%20replace%20a%20consent%20tool%3F\"><h3>Does it replace a consent tool?<\/h3><\/dt>\n<dd><p>No. The plugin can wait for a configured consent cookie, but it does not provide legal advice or a cookie banner.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20track%20users%20externally%3F\"><h3>Does this plugin track users externally?<\/h3><\/dt>\n<dd><p>No. The optional statistics feature stores anonymous aggregated counts locally in your WordPress database.<\/p><\/dd>\n<dt id=\"does%20the%20ai%20feature%20send%20data%20anywhere%3F\"><h3>Does the AI feature send data anywhere?<\/h3><\/dt>\n<dd><p>Only when you use it. The AI is the one deliberate exception to \"no external calls\": it runs only after you store your own Anthropic API key and click a button - one click, one request, never automatic. Depending on the button, the request carries the template's field labels, your site name and description, the brief you type, the selected ad image (alt-text suggestion) or aggregated statistics totals (statistics summary). It never sends visitor data, IP addresses, e-mail addresses or page content, and your key is stored encrypted and never leaves your server.<\/p><\/dd>\n<dt id=\"can%20editors%20manage%20ads%20without%20being%20administrators%3F\"><h3>Can editors manage ads without being administrators?<\/h3><\/dt>\n<dd><p>Yes. Enable \"Editor access\" in Settings to grant the <code>manage_adjet<\/code> capability to the editor role. Administrators always have access.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20my%20data%20when%20i%20uninstall%20the%20plugin%3F\"><h3>What happens to my data when I uninstall the plugin?<\/h3><\/dt>\n<dd><p>Nothing is deleted unless you ask for it. The setting \"Delete all data on uninstall\" is off by default, so removing the plugin leaves your ad blocks, settings and the statistics table in place and a later reinstall picks them up again. Switch the setting on before uninstalling if you want the options, the statistics table and the <code>manage_adjet<\/code> capability removed for good.<\/p><\/dd>\n<dt id=\"is%20there%20a%20rest%20api%3F\"><h3>Is there a REST API?<\/h3><\/dt>\n<dd><p>Yes. The <code>adjet\/v1<\/code> namespace exposes ad blocks (<code>\/blocks<\/code>, CRUD), statistics (<code>\/stats<\/code>) and settings (<code>\/settings<\/code>). Authenticate with an Application Password; access requires the <code>manage_adjet<\/code> capability.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.4.0<\/h4>\n\n<ul>\n<li>Six new templates, all free: a testimonial ad, an in-feed native card for post lists, a book\/course card with star rating and price note, a seasonal deal banner (Black Friday, Christmas, Easter, Summer) with a copy-to-clipboard code and optional countdown, an event announcement that hides itself after the start, and a sponsor logo wall. All use the assistant fields, the media picker and the date picker; empty optional images, codes and timers are hidden automatically.<\/li>\n<li>New: ads.txt. Paste your ads.txt lines into the settings and the plugin serves them at \/ads.txt (a physical file in the web root always wins). A suggested line is built from your AdSense Publisher ID. AdSense warns in your account when ads.txt is missing; now it is a text field.<\/li>\n<li>New: reserve space against layout shift. Every block can reserve a minimum height before the ad loads, and the AdSense assistant suggests the usual heights per format. Lazy loading no longer costs you Core Web Vitals.<\/li>\n<li>New: ads without consent, your call. An AdSense setting serves non-personalised ads while consent is missing or declined (Google's NPA flag, Consent Mode v2 stays as it is). Off by default; the setting text says what it means in the EU.<\/li>\n<li>New: fallback for empty AdSense slots. Pick another block that appears when Google leaves the slot unfilled; the swap happens in the browser when AdSense reports it.<\/li>\n<li>New: load ad scripts on first interaction (scroll, tap, key press, at the latest after 5 seconds). Off by default.<\/li>\n<li>New: page-type targeting for overlays, manual placements and in-feed ads: front page, blog index, archives, search, 404, author pages. Empty means everywhere, as before.<\/li>\n<li>New: visibility by login state and role. \"Everyone\", \"logged-out visitors only\" or \"logged-in users only\", plus a list of roles to hide the block for. The old \"logged-out only\" switch carries over.<\/li>\n<li>New: three content positions, middle of the content, before the last paragraph, and after heading N.<\/li>\n<li>New: sticky option for manual placements (widget, shortcode, block): the ad keeps its position while the visitor scrolls, with an adjustable top offset.<\/li>\n<li>New: \"Why isn't the ad showing here?\" Open any page of your site with ?adjet_debug=1 while logged in, and wherever a block is not shown you (and only you) see a note with the first reason: post type, page type, schedule, word count, consent, device and so on. Nothing of this reaches visitors or page caches.<\/li>\n<li>New: statistics export as CSV (semicolon-separated, Excel-friendly) for the selected period.<\/li>\n<li>New: import from Ad Inserter and Advanced Ads on the Tools page. Blocks, names and placements are carried over; rules without a counterpart are listed after the import; everything arrives paused; a second import creates no duplicates.<\/li>\n<\/ul>\n\n<h4>2.3.2<\/h4>\n\n<ul>\n<li>New: pick images from the media library. The image field of the image banner template gets a \"Choose from media library\" button with a thumbnail and a remove link; the URL field stays for external images, and the library's alt text fills the alt field while it is empty. Suggested by the WordPress 103 video review.<\/li>\n<li>New: real date pickers. The countdown's target date is a date-and-time field instead of a text box with a format hint, and scheduling (start and end) is now available inside the template assistant under \"Advanced\", with the site's timezone spelled out; the end can no longer be set before the start. Stored values keep their format, existing ads are unaffected.<\/li>\n<li>New: more design without leaving the template. \"Advanced\" in the assistant now holds alignment, maximum width, margin and an optional \"Custom CSS\" field whose selectors are limited to that one block automatically, media queries included, so nothing else on the site changes. The same fields sit in the full editor.<\/li>\n<li>Changed: the way from a template to your own HTML. The old \"Edit the HTML yourself\" link dropped the template and its fields for good. It is now a visible card that opens a copy of the ad in the HTML editor (or the expert editor for AdSense); the template ad stays exactly as it is and the copy starts paused. The assistant's header says so up front.<\/li>\n<li>Changed: \"Left\" and \"Right\" alignment now also move a template's centred root element to that side; before, it only changed the text alignment inside.<\/li>\n<li>Fixed: the \"Recommended\" badge on the AdSense Wizard card was hard-coded as German \"Empfohlen\" inside the stylesheet and therefore showed in German on every site. It is now translatable markup, translated in all seven bundled languages.<\/li>\n<li>Housekeeping: the changelog had grown past the 5,000 words WordPress.org displays. Entries up to 2.1.1 moved to changelog.txt inside the plugin folder.<\/li>\n<\/ul>\n\n<h4>2.3.1<\/h4>\n\n<ul>\n<li>Fixed: the cookie consent banner flashed on every page view, even for visitors who had decided long ago. The banner markup ships with every page on purpose - a page cache serves the same HTML to everybody, so the server cannot know the cookie - and frontend.js removes it in the browser. The starting state was the wrong way round, though: the banner carries an inline display:flex and sits far ahead of the script that takes it away (measured on a live shop: banner at 69 % of the document, frontend.js at 94 %, no defer, some 100 KB of HTML and several hundred KB of scripts in between). Adjet now prints a tiny inline snippet at the very top of the head that hides the banner right away when a decision is already stored. The banner itself stays visible by default on purpose: hiding it in the template and revealing it by script would look shorter, but frontend.js sits under plugins\/hafenstudios-ads\/ and ad blockers reliably block that path - the banner would never appear for exactly those visitors who care most about privacy, leaving them no way to consent at all.<\/li>\n<li>Fixed: the flash also hit installations that do not use the consent template. A banner can just as well run as a plain HTML block - copied from the template once, maintained by hand since, or several versions running against each other in a split test. Adjet now recognises banner markup in block code too, so those installations get the fix as well.<\/li>\n<\/ul>\n\n<h4>2.3.0<\/h4>\n\n<ul>\n<li>Fixed, and this one changes your numbers: impressions are no longer booked for overlays nobody saw. The visibility check used to fall back to the block's invisible zero-height wrapper whenever overlay code starts with a style tag. In 2.1.x that booked thousands of impressions for popups that never opened; since 2.2.0 it booked none at all, even when they did open, which is why overlay blocks could show more clicks than impressions. The observer now finds the fixed overlay element itself and never counts a target without a visible area. Expect overlay impression counts to drop where they were phantom counts and to come back where they had vanished; from now on they mean \"the overlay was actually on screen\".<\/li>\n<li>Fixed: clicking into an opt-in form's e-mail field, correcting a typo or closing a popup no longer counts as an ad click. Only links, buttons and submit controls count, and at most one click per block and page view. Opt-in blocks used to reach a \"CTR\" above 100 percent this way; expect click counts to drop and the rate to become meaningful.<\/li>\n<li>Fixed: a split test now knows when it started. Every rotation group gets a start date the day it is formed, and the split test page counts from that day, never earlier, so numbers a block collected before it became a variant no longer distort the comparison. Tests that already existed are stamped with today's date the first time the page is opened, and the page states the date it counts from.<\/li>\n<li>Safety net for rotation groups: an undecided group can no longer book an impression on any code path; only the variant the browser picked counts.<\/li>\n<li>New on the split test page: a data gauge. The needle shows how much data the test has collected, measured by the weakest running variant against the 100-impression threshold. The confidence zones stay greyed out with a lock; whether the lead is solid is what Adjet Pro works out, and the free line keeps refusing to claim significance it cannot check.<\/li>\n<li>Changed: consent banners moved out of the ad statistics into their own \"System\" section on the ad list and the statistics page, with the columns \"Decisions\" and \"Decision rate\". A banner is answered, not clicked: its 27-percent \"CTR\" was a consent quote wearing the wrong label, and as the only site-wide block it dominated every total. The weekly summary and the top block now count real ads only. Free counts accepting and declining as the same decision; the actual consent rate remains part of Pro's consent statistics.<\/li>\n<li>New: an honest warning on overlay blocks that can never appear. Custom overlay code that starts hidden and carries none of the free triggers used to run invisibly for weeks without a word. The block list and the editor now say so, with the two attributes (data-adjet-popup, data-adjet-exit) that fix it. Popup and exit templates keep their trigger attribute on the root element, even through \"edit the HTML yourself\".<\/li>\n<li>New: a frequency cap for overlays, stored per day in the visitor's browser. A popup or exit overlay shows at most once per day, and a closed overlay, bars included, stays away for the rest of the day. The close button now also works inside overlays built from custom code.<\/li>\n<li>New: two cards in the moments system. When two or more active blocks share a placement without a rotation group, one click lets them compete, which is all it takes to start a split test. And when the evaluated test has collected enough data, a card names the leading variant, the gap, and what the Pro autopilot would do now.<\/li>\n<li>Fixed for Google Consent Mode v2: while the built-in consent banner is active, the consent DEFAULT (denied) is now printed early in the head, before any gtag.js, and an already stored decision is replayed on every page view. Until now the only signal was the update pushed by the banner's buttons: GA4 ran unrestricted from page load, Google's Tag Assistant reported the update as arriving before a default, and a visitor's decision lasted exactly one page view.<\/li>\n<li>Brevo signups can now use Brevo's real double opt-in: store the ID of a Brevo double opt-in template plus a redirect URL in the settings and signups run through Brevo's DOI flow, confirmation mail included. Without those fields the contact is created directly, exactly as before, but the form now answers \"You are signed up.\" instead of promising a confirmation mail that never existed. Both paths keep the 2.2.0 rule that the answer never reveals whether an address is already on your list.<\/li>\n<\/ul>\n\n<h4>2.2.0<\/h4>\n\n<ul>\n<li>New: a \"Split-Tests\" page that reads one of your rotation groups as an A\/B test. Ad blocks sharing a group name have been rotating and counting per block for a while, but nowhere could you see the variants side by side. The page lists every variant of the group with its status, weight, impressions, clicks and CTR, and marks the one that is ahead. Nothing new is measured, nothing is sent anywhere, and no API key of any kind is involved.<\/li>\n<li>\"Ahead right now\" appears only once two running variants have at least 100 impressions each in the selected period and their rates actually differ. One variant over the threshold while the other has three impressions names nobody, and neither does a tie. The size of the lead is printed underneath in percentage points, because a lead of one single click looks exactly like a lead of five points otherwise. Whether that lead will hold is a question of significance, which is what Pro answers, together with the uplift, taking the winner over with one click and an AI suggestion for the next variant.<\/li>\n<li>A group counts as a test when its blocks share a group name AND a placement. That is how the rotation works: in automatic placements only blocks with the same placement take turns, everything else appears on the page at the same time. Two blocks of one group in different placements are therefore not competing, and putting them side by side as variants would compare positions rather than variants. The shortcode <code>[adjet_ad_group name=\"header\"]<\/code> is unaffected, it rotates the whole group, and those blocks all carry the placement \"manual\".<\/li>\n<li>You can run as many rotation groups as you like, as always. One of them is evaluated on the page, and you pick which one with a single click. Without a pick it is the test you created first, which cannot move under you when a group is renamed.<\/li>\n<li>Rotation groups that contain a cookie banner are shown but not judged: the banner's buttons set a cookie instead of counting as a click, so its CTR would report a perfectly good banner as the loser. The page says so instead of pretending otherwise.<\/li>\n<li>Paused variants stay in their group with their numbers, so pausing one does not delete half the test from view. They cannot be \"ahead right now\", though: a variant that is not being served is not running against anything, and its numbers stop while the others keep counting.<\/li>\n<li>Changed, please read this one: an address that is already on your newsletter list now gets exactly the same confirmation as a brand new one. Until now the form printed the answer of your newsletter provider word for word, and that answer says out loud whether an address is already a member. Anyone could have typed addresses into your form one after another and read off who is on your list. The form no longer tells the two cases apart at all, which is also all that a provider with double opt-in reveals to the outside. What that costs you: a visitor who mistypes and hits someone else's address still reads \"please confirm your signup\". The message now means \"we handed this over to your provider\", not \"this address is new\".<\/li>\n<li>Fixed: the signup form no longer shows a single word written by your newsletter provider. Whatever SendFox, MailerLite, Brevo, Mailchimp or Kit answers stays on your server. Visitors read your own sentence, in the language of your site, instead of an English developer message such as \"is already a list member. Use PUT to insert or update list members.\"<\/li>\n<li>Fixed: connection problems no longer put technical detail in front of visitors. A failed lookup used to show the cURL error and the host name of your provider inside the form.<\/li>\n<li>Changed: the same neutral answer covers every rejection your provider sends back about a submission, not only the two status codes Mailchimp happens to use for a duplicate. A rule that neutralises 400 and 422 alone re-opens the same hole the day a provider answers \"already a member\" with 409 instead. The deliberate price: a genuinely broken setup, for example a required merge field your form does not send or a list deleted at the provider, now also looks like a success from the front. That is why every one of those cases is written to the log described below and shown as a warning on the ad block overview. Throttling by your provider (429), a provider outage and a rejected token stay errors, because none of them depend on the address that was entered.<\/li>\n<li>Fixed: the status code of the signup endpoint now says what actually happened. Everything the provider answers about a submission is a normal 200. There are three error states and they mean what they say: 503 the provider answered but cannot deliver right now, 502 the request never reached the provider at all, and 500 something in your own setup is wrong (no token, no list picked, token rejected) and will not fix itself by waiting. If you monitor 502 responses, expect them to nearly disappear.<\/li>\n<li>New: the ad block overview shows the last signup that did not go through, with the date, the provider, the status code and the provider's own words. That is where the detail went that visitors no longer see, so a broken connection cannot stay hidden behind a friendly confirmation. E-mail addresses in that message are masked. The duplicate case is deliberately not listed there: it is not a failure, it is the most frequent thing that happens to a public form, and it would push the rare configuration error out of that single slot.<\/li>\n<li>Fixed: with Kit, an address that was already known could end the signup halfway. Kit is told about the subscriber first and only then about the form, and only that second step sends the confirmation mail. A known address stopped the first step, so the visitor got an error and no mail. Both steps now run, and a real problem in that first step is written to the log instead of disappearing. Kit's own wording arrives there too: Kit reports its reasons in a field the plugin did not read, so every Kit problem used to be logged as a bare status code.<\/li>\n<li>Fixed for Brevo: a signup with an address Brevo already had would quietly add that person to the list you configured, including someone who had unsubscribed from exactly that list, and Brevo sends no confirmation mail at this step. The plugin no longer updates existing contacts. New contacts are created as before, and a known address is answered by Brevo with an error that counts as a success towards the visitor, so nothing changes for them.<\/li>\n<li>Fixed: the honeypot answer. A submission caught by the hidden anti-bot field was answered with a slightly different body than a real signup, which made the trap detectable in a single request. Both answers are now byte for byte identical.<\/li>\n<li>Fixed: the three fallback texts of the signup form (\"Thank you!\", \"Something went wrong.\", \"Connection failed. Please try again later.\") were built into the JavaScript file and could never be translated. They now come from the server, translated like every other text.<\/li>\n<\/ul>\n\n<p>Older entries (2.1.1 and earlier) live in changelog.txt inside the plugin folder.<\/p>","raw_excerpt":"The ad manager that stays on your server: banner ads, AdSense, rotation, split testing, targeting, consent gating and local stats.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/322632","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=322632"}],"author":[{"embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/hafenstudios"}],"wp:attachment":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=322632"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=322632"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=322632"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=322632"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=322632"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=322632"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}