PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 1.47.3
Advanced Ads – Ad Manager & AdSense v1.47.3
2.0.26 2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / classes / frontend_checks.php
advanced-ads / classes Last commit date
Advanced_Ads_Modal.php 2 years ago EDD_SL_Plugin_Updater.php 4 years ago ad-ajax.php 2 years ago ad-debug.php 2 years ago ad-expiration.php 3 years ago ad-health-notices.php 2 years ago ad-model.php 2 years ago ad-select.php 3 years ago ad.php 2 years ago ad_ajax_callbacks.php 2 years ago ad_group.php 2 years ago ad_placements.php 2 years ago ad_type_abstract.php 2 years ago ad_type_content.php 2 years ago ad_type_dummy.php 3 years ago ad_type_group.php 2 years ago ad_type_image.php 2 years ago ad_type_plain.php 2 years ago checks.php 2 years ago class-translation-promo.php 3 years ago compatibility.php 2 years ago display-conditions.php 2 years ago filesystem.php 3 years ago frontend_checks.php 2 years ago in-content-injector.php 2 years ago inline-css.php 2 years ago plugin.php 2 years ago upgrades.php 2 years ago utils.php 3 years ago visitor-conditions.php 2 years ago widget.php 2 years ago
frontend_checks.php
1021 lines
1 <?php
2
3 use AdvancedAds\Utilities\WordPress;
4
5 /**
6 * Class Advanced_Ads_Frontend_Checks
7 *
8 * Handle Ad Health and other notifications and checks in the frontend.
9 */
10 class Advanced_Ads_Frontend_Checks {
11 /**
12 * True if 'the_content' was invoked, false otherwise.
13 *
14 * @var bool
15 */
16 private $did_the_content = false;
17 private $has_many_the_content = false;
18
19 /**
20 * Constructor.
21 */
22 public function __construct() {
23 // Wait until other plugins (for example Elementor) have disabled admin bar using `show_admin_bar` filter.
24 add_action( 'template_redirect', [ $this, 'init' ], 11 );
25
26 if ( wp_doing_ajax() ) {
27 add_filter( 'advanced-ads-ad-output', [ $this, 'after_ad_output' ], 10, 2 );
28 }
29 }
30
31 /**
32 * Ad Health init.
33 */
34 public function init() {
35 if ( ! is_admin()
36 && is_admin_bar_showing()
37 && WordPress::user_can( 'advanced_ads_edit_ads' )
38 && Advanced_Ads_Ad_Health_Notices::notices_enabled()
39 ) {
40 add_action( 'admin_bar_menu', [ $this, 'add_admin_bar_menu' ], 1000 );
41 add_filter( 'the_content', [ $this, 'set_did_the_content' ] );
42 add_action( 'wp_footer', [ $this, 'footer_checks' ], -101 );
43 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
44 add_filter( 'advanced-ads-ad-select-args', [ $this, 'ad_select_args_callback' ] );
45 add_filter( 'advanced-ads-ad-output', [ $this, 'after_ad_output' ], 10, 2 );
46 }
47
48 if ( Advanced_Ads_Ad_Health_Notices::notices_enabled() ) {
49 add_action( 'body_class', [ $this, 'body_class' ] );
50 }
51 }
52
53 /**
54 * Notify ads loaded with AJAX.
55 *
56 * @param array $args ad arguments.
57 * @return array $args
58 */
59 public function ad_select_args_callback( $args ) {
60 $args['frontend-check'] = true;
61 return $args;
62 }
63
64 /**
65 * Enqueue scripts
66 * needs to add ajaxurl in case no other plugin is doing that
67 */
68 public function enqueue_scripts() {
69 if ( advads_is_amp() ) {
70 return;
71 }
72
73 // we don’t have our own script, so we attach this information to jquery
74 wp_localize_script( 'jquery', 'advads_frontend_checks',
75 [ 'ajax_url' => admin_url( 'admin-ajax.php' ) ] );
76 }
77
78 /**
79 * List current ad situation on the page in the admin-bar.
80 *
81 * @param object $wp_admin_bar WP_Admin_Bar.
82 */
83 public function add_admin_bar_menu( $wp_admin_bar ) {
84 global $wp_the_query, $post, $wp_scripts;
85
86 $options = Advanced_Ads_Plugin::get_instance()->options();
87
88 // load AdSense related options.
89 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
90
91 // check if AdSense loads Auto Ads ads
92 // Hidden, will be shown using js.
93 if( ! isset( $adsense_options['violation-warnings-disable'] ) ) {
94 $nodes[] = [ 'type' => 2, 'data' => [
95 'parent' => 'advanced_ads_ad_health',
96 'id' => 'advanced_ads_autoads_displayed',
97 'title' => __( 'Random AdSense ads', 'advanced-ads' ),
98 'href' => 'https://wpadvancedads.com/adsense-in-random-positions-auto-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=frontend-autoads-ads',
99 'meta' => [
100 'class' => 'hidden',
101 'target' => '_blank'
102 ]
103 ] ];
104 }
105
106 // check if current user was identified as a bot.
107 if( Advanced_Ads::get_instance()->is_bot() ) {
108 $nodes[] = [ 'type' => 1, 'data' => [
109 'parent' => 'advanced_ads_ad_health',
110 'id' => 'advanced_ads_user_is_bot',
111 'title' => __( 'You look like a bot', 'advanced-ads' ),
112 'href' => 'https://wpadvancedads.com/manual/ad-health/#look-like-bot',
113 'meta' => [
114 'class' => 'advanced_ads_ad_health_warning',
115 'target' => '_blank'
116 ]
117 ] ];
118 }
119
120 // check if an ad blocker is enabled
121 // Hidden, will be shown using js.
122 $nodes[] = [ 'type' => 2, 'data' => [
123 'parent' => 'advanced_ads_ad_health',
124 'id' => 'advanced_ads_ad_health_adblocker_enabled',
125 'title' => __( 'Ad blocker enabled', 'advanced-ads' ),
126 'meta' => [
127 'class' => 'hidden advanced_ads_ad_health_warning',
128 'target' => '_blank'
129 ]
130 ] ];
131
132 if ( $wp_the_query->is_singular() ) {
133 if ( $this->has_the_content_placements() ) {
134 $nodes[] = [ 'type' => 2, 'data' => [
135 'parent' => 'advanced_ads_ad_health',
136 'id' => 'advanced_ads_ad_health_the_content_not_invoked',
137 'title' => sprintf( __( '<em>%s</em> filter does not exist', 'advanced-ads' ), 'the_content' ),
138 'href' => 'https://wpadvancedads.com/manual/ads-not-showing-up/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-content-filter-missing#the_content-filter-missing',
139 'meta' => [
140 'class' => 'hidden advanced_ads_ad_health_warning',
141 'target' => '_blank'
142 ]
143 ] ];
144 }
145
146 if ( ! empty( $post->ID ) ) {
147 $ad_settings = get_post_meta( $post->ID, '_advads_ad_settings', true );
148
149 if ( ! empty( $ad_settings['disable_the_content'] ) ) {
150 $nodes[] = [ 'type' => 1, 'data' => [
151 'parent' => 'advanced_ads_ad_health',
152 'id' => 'advanced_ads_ad_health_disabled_in_content',
153 'title' => __( 'Ads are disabled in the content of this page', 'advanced-ads' ),
154 'href' => get_edit_post_link( $post->ID ) . '#advads-ad-settings',
155 'meta' => [
156 'class' => 'advanced_ads_ad_health_warning',
157 'target' => '_blank'
158 ]
159 ] ];
160 }
161 } else {
162 $nodes[] = [ 'type' => 1, 'data' => [
163 'parent' => 'advanced_ads_ad_health',
164 'id' => 'advanced_ads_ad_health_post_zero',
165 'title' => __( 'the current post ID is 0 ', 'advanced-ads' ),
166 'href' => 'https://wpadvancedads.com/manual/ad-health/#post-id-0',
167 'meta' => [
168 'class' => 'advanced_ads_ad_health_warning',
169 'target' => '_blank'
170 ]
171 ] ];
172 }
173 }
174
175 $disabled_reason = Advanced_Ads::get_instance()->disabled_reason;
176 $disabled_id = Advanced_Ads::get_instance()->disabled_id;
177
178 if ( 'page' === $disabled_reason && $disabled_id ) {
179 $nodes[] = [
180 'type' => 1,
181 'data' => [
182 'parent' => 'advanced_ads_ad_health',
183 'id' => 'advanced_ads_ad_health_disabled_on_page',
184 'title' => __( 'Ads are disabled on this page', 'advanced-ads' ),
185 'href' => get_edit_post_link( $disabled_id ) . '#advads-ad-settings',
186 'meta' => [
187 'class' => 'advanced_ads_ad_health_warning',
188 'target' => '_blank',
189 ],
190 ],
191 ];
192 }
193
194 if ( 'all' === $disabled_reason ) {
195 $nodes[] = [ 'type' => 1, 'data' => [
196 'parent' => 'advanced_ads_ad_health',
197 'id' => 'advanced_ads_ad_health_no_all',
198 'title' => __( 'Ads are disabled on all pages', 'advanced-ads' ),
199 'href' => admin_url( 'admin.php?page=advanced-ads-settings' ),
200 'meta' => [
201 'class' => 'advanced_ads_ad_health_warning',
202 'target' => '_blank'
203 ]
204 ] ];
205 }
206
207 if ( '404' === $disabled_reason ) {
208 $nodes[] = [
209 'type' => 1,
210 'data' => [
211 'parent' => 'advanced_ads_ad_health',
212 'id' => 'advanced_ads_ad_health_no_404',
213 'title' => __( 'Ads are disabled on 404 pages', 'advanced-ads' ),
214 'href' => admin_url( 'admin.php?page=advanced-ads-settings' ),
215 'meta' => [
216 'class' => 'advanced_ads_ad_health_warning',
217 'target' => '_blank',
218 ],
219 ],
220 ];
221 }
222
223 if ( 'archive' === $disabled_reason ) {
224 $nodes[] = [ 'type' => 1, 'data' => [
225 'parent' => 'advanced_ads_ad_health',
226 'id' => 'advanced_ads_ad_health_no_archive',
227 'title' => __( 'Ads are disabled on non singular pages', 'advanced-ads' ),
228 'href' => admin_url( 'admin.php?page=advanced-ads-settings' ),
229 'meta' => [
230 'class' => 'advanced_ads_ad_health_warning',
231 'target' => '_blank'
232 ]
233 ] ];
234 }
235
236 $nodes[] = [ 'type' => 2, 'data' => [
237 'parent' => 'advanced_ads_ad_health',
238 'id' => 'advanced_ads_ad_health_has_http',
239 'title' => sprintf( '%s %s',
240 __( 'Your website is using HTTPS, but the ad code contains HTTP and might not work.', 'advanced-ads' ),
241 sprintf( __( 'Ad IDs: %s', 'advanced-ads' ), '<i></i>' )
242 ),
243 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-https-ads#https-ads',
244 'meta' => [
245 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_has_http',
246 'target' => '_blank'
247 ]
248 ] ];
249
250 $nodes[] = [ 'type' => 2, 'data' => [
251 'parent' => 'advanced_ads_ad_health',
252 'id' => 'advanced_ads_ad_health_incorrect_head',
253 'title' => sprintf( __( 'Visible ads should not use the Header placement: %s', 'advanced-ads' ), '<i></i>' ),
254 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-visible-ad-in-header#header-ads',
255 'meta' => [
256 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_incorrect_head',
257 'target' => '_blank'
258 ]
259 ] ];
260
261 // warn if an AdSense ad seems to be hidden
262 if( ! isset( $adsense_options['violation-warnings-disable'] ) ) {
263 $nodes[] = [ 'type' => 2, 'data' => [
264 'parent' => 'advanced_ads_ad_health',
265 'id' => 'advanced_ads_ad_health_hidden_adsense',
266 'title' => sprintf( '%s: %s. %s',
267 __( 'AdSense violation', 'advanced-ads' ),
268 __( 'Ad is hidden', 'advanced-ads' ),
269 sprintf( __( 'IDs: %s', 'advanced-ads' ), '<i></i>' )
270 ),
271 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-frontend-adsense-hidden#adsense-hidden',
272 'meta' => [
273 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_hidden_adsense',
274 'target' => '_blank'
275 ]
276 ] ];
277 }
278
279 $nodes[] = [ 'type' => 2, 'data' => [
280 'parent' => 'advanced_ads_ad_health',
281 'id' => 'advanced_ads_ad_health_floated_responsive_adsense',
282 'title' => sprintf( __( 'The following responsive AdSense ads are not showing up: %s', 'advanced-ads' ), '<i></i>' ),
283 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-adsense-responsive-not-showing#The_following_responsive_AdSense_ads_arenot_showing_up',
284 'meta' => [
285 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_floated_responsive_adsense',
286 'target' => '_blank'
287 ]
288 ] ];
289
290 // warn if consent was not given
291 $privacy = Advanced_Ads_Privacy::get_instance();
292 if ( 'not_needed' !== $privacy->get_state() ) {
293 $nodes[] = [ 'type' => 2, 'data' => [
294 'parent' => 'advanced_ads_ad_health',
295 'id' => 'advanced_ads_ad_health_consent_missing',
296 'title' => __( 'Consent not given', 'advanced-ads' ),
297 'href' => admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ),
298 'meta' => [
299 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_consent_missing',
300 'target' => '_blank'
301 ]
302 ] ];
303 }
304
305 $privacy_options = $privacy->options();
306 if ( ( empty( $privacy_options['enabled'] ) || $privacy_options['consent-method'] !== 'iab_tcf_20' ) ) {
307 $nodes[] = [
308 'type' => 2,
309 'data' => [
310 'parent' => 'advanced_ads_ad_health',
311 'id' => 'advanced_ads_ad_health_privacy_disabled',
312 'title' => __( 'Enable TCF integration', 'advanced-ads' ),
313 'href' => admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ),
314 'meta' => [
315 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_privacy_disabled',
316 'target' => '_blank',
317 ],
318 ],
319 ];
320 }
321
322 $nodes[] = [ 'type' => 3, 'data' => [
323 'parent' => 'advanced_ads_ad_health',
324 'id' => 'advanced_ads_ad_health_gam_debug',
325 'title' => __( 'Debug Google Ad Manager', 'advanced-ads' ),
326 'href' => esc_url( add_query_arg( 'google_force_console', '1' ) ),
327 'meta' => [
328 'class' => 'hidden advanced_ads_ad_health_gam_debug_link',
329 ]
330 ] ];
331
332 // link to highlight ads and jump from one ad to the next.
333 $nodes[] = [ 'type' => 3, 'amp' => false, 'data' => [
334 'parent' => 'advanced_ads_ad_health',
335 'id' => 'advanced_ads_ad_health_highlight_ads',
336 'title' => __( 'highlight ads', 'advanced-ads' ),
337 'meta' => [
338 'class' => 'advanced_ads_ad_health_highlight_ads',
339 ],
340 ] ];
341
342 /**
343 * Add new node.
344 *
345 * @param array $node An array that contains:
346 * 'type' => 1 - warning, 2 - hidden warning that will be shown using JS, 3 - info message
347 * 'data': @see WP_Admin_Bar->add_node
348 * @param object $wp_admin_bar
349 */
350 $nodes = apply_filters( 'advanced-ads-ad-health-nodes', $nodes );
351
352 usort( $nodes, [ $this, 'sort_nodes' ] );
353
354 // load number of already detected notices.
355 $notices = Advanced_Ads_Ad_Health_Notices::get_number_of_notices();
356
357 if ( ! advads_is_amp() ) {
358 $warnings = 0; // Will be updated using JS.
359 } else {
360 $warnings = $this->count_visible_warnings( $nodes, [ 1 ] );
361 }
362
363 $issues = $warnings;
364
365 $this->add_header_nodes( $wp_admin_bar, $issues, $notices );
366
367 foreach ( $nodes as $node ) {
368 if ( isset( $node['data'] ) ) {
369 $wp_admin_bar->add_node( $node['data'] );
370 }
371 }
372
373 $this->add_footer_nodes( $wp_admin_bar, $issues );
374 }
375
376
377 /**
378 * Add classes to the `body` tag.
379 *
380 * @param string[] $classes Array of existing class names.
381 * @return string[] $classes Array of existing and new class names.
382 */
383 public function body_class( $classes ) {
384 $aa_classes = [
385 'aa-prefix-' . Advanced_Ads_Plugin::get_instance()->get_frontend_prefix(),
386 ];
387
388 $disabled_reason = Advanced_Ads::get_instance()->disabled_reason;
389 if ( $disabled_reason ) {
390 $aa_classes[] = 'aa-disabled-' . esc_attr( $disabled_reason );
391 }
392
393 global $post;
394 if ( ! empty( $post->ID ) ) {
395 $ad_settings = get_post_meta( $post->ID, '_advads_ad_settings', true );
396 if ( ! empty( $ad_settings['disable_the_content'] ) ) {
397 $aa_classes[] = 'aa-disabled-content';
398 }
399 }
400
401 // hide-ads-from-bots option is enabled
402 $options = Advanced_Ads_Plugin::get_instance()->options();
403 if ( ! empty( $options['block-bots'] ) ) {
404 $aa_classes[] = 'aa-disabled-bots';
405 }
406
407 $aa_classes = apply_filters( 'advanced-ads-body-classes', $aa_classes );
408
409 if ( ! is_array( $classes ) ) {
410 $classes = [];
411 }
412 if ( ! is_array( $aa_classes ) ) {
413 $aa_classes = [];
414 }
415
416 return array_merge( $classes, $aa_classes );
417 }
418
419
420
421
422 /**
423 * Count visible notices and warnings.
424 *
425 * @param array $nodes Nodes to add.
426 * @param array $types Warning types.
427 */
428 private function count_visible_warnings( $nodes, $types = [] ) {
429 $warnings = 0;
430 foreach ( $nodes as $node ) {
431 if ( ! isset( $node['type'] ) || ! isset( $node['data'] ) ) { continue; }
432 if ( in_array( $node['type'], $types ) ) {
433 $warnings++;
434 }
435 }
436 return $warnings;
437 }
438
439 /**
440 * Add header nodes.
441 *
442 * @param object $wp_admin_bar WP_Admin_Bar object.
443 * @param int $issues Number of all issues.
444 * @param int $notices Number of notices.
445 */
446 private function add_header_nodes( $wp_admin_bar, $issues, $notices ) {
447 $wp_admin_bar->add_node( [
448 'id' => 'advanced_ads_ad_health',
449 'title' => __( 'Ad Health', 'advanced-ads' ) . '&nbsp;<span class="advanced-ads-issue-counter">' . $issues . '</span>',
450 'parent' => false,
451 'href' => admin_url( 'admin.php?page=advanced-ads' ),
452 'meta' => [
453 'class' => $issues ? 'advads-adminbar-is-warnings': '',
454 ],
455 ] );
456
457
458 // show that there are backend notices
459 if ( $notices ) {
460 $wp_admin_bar->add_node( [
461 'parent' => 'advanced_ads_ad_health',
462 'id' => 'advanced_ads_ad_health_more',
463 'title' => sprintf(__( 'Show %d more notifications', 'advanced-ads' ), absint( $notices ) ),
464 'href' => admin_url( 'admin.php?page=advanced-ads' ),
465 ] );
466 }
467 }
468
469 /**
470 * Add footer nodes.
471 *
472 * @param obj $wp_admin_bar WP_Admin_Bar object.
473 * @param int $issues Number of all issues.
474 */
475 private function add_footer_nodes( $wp_admin_bar, $issues ) {
476 if ( ! $issues ) {
477 $wp_admin_bar->add_node( [
478 'parent' => 'advanced_ads_ad_health',
479 'id' => 'advanced_ads_ad_health_fine',
480 'title' => __( 'Everything is fine', 'advanced-ads' ),
481 'href' => false,
482 'meta' => [
483 'target' => '_blank',
484 ]
485 ] );
486 }
487
488 $wp_admin_bar->add_node( [
489 'parent' => 'advanced_ads_ad_health',
490 'id' => 'advanced_ads_ad_health_support',
491 'title' => __( 'Get help', 'advanced-ads' ),
492 'href' => Advanced_Ads_Plugin::support_url( '?utm_source=advanced-ads&utm_medium=link&utm_campaign=health-support' ),
493 'meta' => [
494 'target' => '_blank',
495 ]
496 ] );
497 }
498
499 /**
500 * Filter out nodes intended to AMP pages only.
501 *
502 * @param array $nodes Nodes to add.
503 * @return array $nodes Nodes to add.
504 */
505 private function filter_nodes( $nodes ) {
506 return $nodes;
507 }
508
509 /**
510 * Sort nodes.
511 */
512 function sort_nodes( $a, $b ) {
513 if ( ! isset( $a['type'] ) || ! isset( $b['type'] ) ) {
514 return 0;
515 }
516 if ( $a['type'] == $b['type'] ) {
517 return 0;
518 }
519 return ( $a['type'] < $b['type'] ) ? -1 : 1;
520 }
521
522 /**
523 * Set variable to 'true' when 'the_content' filter is invoked.
524 *
525 * @param string $content
526 * @return string $content
527 */
528 public function set_did_the_content( $content ) {
529 if ( ! $this->did_the_content ) {
530 $this->did_the_content = true;
531 }
532
533 if ( Advanced_Ads::get_instance()->has_many_the_content() ) {
534 $this->has_many_the_content = true;
535 }
536 return $content;
537 }
538
539 /**
540 * Check conditions and display warning.
541 * Conditions:
542 * AdBlocker enabled,
543 * jQuery is included in header
544 * AdSense Quick Start ads are running
545 */
546 public function footer_checks() {
547 ob_start();
548 ?><!-- Advanced Ads: <?php esc_html_e( 'the following code is used for automatic error detection and only visible to admins', 'advanced-ads' ); ?>-->
549 <style>#wp-admin-bar-advanced_ads_ad_health .hidden { display: none; }
550 #wp-admin-bar-advanced_ads_ad_health-default a:after { content: "\25BA"; margin-left: .5em; font-size: smaller; }
551 #wp-admin-bar-advanced_ads_ad_health-default .advanced_ads_ad_health_highlight_ads div:before { content: "\f177"; margin-right: .2em; line-height: 1em; padding: 0.2em 0 0; color: inherit; }
552 #wp-admin-bar-advanced_ads_ad_health-default .advanced_ads_ad_health_highlight_ads div:hover { color: #00b9eb; cursor: pointer; }
553 #wpadminbar .advanced-ads-issue-counter { background-color: #d54e21; display: none; padding: 1px 7px 1px 6px!important; border-radius: 50%; color: #fff; }
554 #wpadminbar .advads-adminbar-is-warnings .advanced-ads-issue-counter { display: inline; }
555 .advanced-ads-highlight-ads { outline:4px solid #0474A2 !important; }
556 </style>
557 <?php echo ob_get_clean();
558
559 if ( advads_is_amp() ) {
560 return;
561 }
562
563 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
564 ob_start();
565 ?><script type="text/javascript" src="<?php echo ADVADS_BASE_URL . 'admin/assets/js/advertisement.js' ?>"></script>
566 <script>
567 var advanced_ads_frontend_checks = {
568 showCount: function() {
569 try {
570 // Count only warnings that have the 'advanced_ads_ad_health_warning' class.
571 var warning_count = document.querySelectorAll( '.advanced_ads_ad_health_warning:not(.hidden)' ).length;
572 var fine_item = document.getElementById( 'wp-admin-bar-advanced_ads_ad_health_fine' );
573 } catch ( e ) { return; }
574
575 var header = document.querySelector( '#wp-admin-bar-advanced_ads_ad_health > a' );
576 if ( warning_count ) {
577 if ( fine_item ) {
578 // Hide 'fine' item.
579 fine_item.className += ' hidden';
580 }
581
582 if ( header ) {
583 header.innerHTML = header.innerHTML.replace(/<span class="advanced-ads-issue-counter">\d*<\/span>/, '') + '<span class="advanced-ads-issue-counter">' + warning_count + '</span>';
584 // add class
585 header.className += ' advads-adminbar-is-warnings';
586 }
587 } else {
588 // Show 'fine' item.
589 if ( fine_item ) {
590 fine_item.classList.remove('hidden');
591 }
592
593 // Remove counter.
594 if ( header ) {
595 header.innerHTML = header.innerHTML.replace(/<span class="advanced-ads-issue-counter">\d*<\/span>/, '');
596 header.classList.remove('advads-adminbar-is-warnings');
597 }
598 }
599 },
600
601 array_unique: function( array ) {
602 var r= [];
603 for ( var i = 0; i < array.length; i++ ) {
604 if ( r.indexOf( array[ i ] ) === -1 ) {
605 r.push( array[ i ] );
606 }
607 }
608 return r;
609 },
610
611 /**
612 * Add item to Ad Health node.
613 *
614 * @param string selector Selector of the node.
615 * @param string/array item item(s) to add.
616 */
617 add_item_to_node: function( selector, item ) {
618 if ( typeof item === 'string' ) {
619 item = item.split();
620 }
621 var selector = document.querySelector( selector );
622 if ( selector ) {
623 selector.className = selector.className.replace( 'hidden', '' );
624 selector.innerHTML = selector.innerHTML.replace( /(<i>)(.*?)(<\/i>)/, function( match, p1, p2, p3 ) {
625 p2 = ( p2 ) ? p2.split( ', ' ) : [];
626 p2 = p2.concat( item );
627 p2 = advanced_ads_frontend_checks.array_unique( p2 );
628 return p1 + p2.join( ', ' ) + p3;
629 } );
630 advanced_ads_frontend_checks.showCount();
631 }
632 },
633
634 /**
635 * Add item to Ad Health notices in the backend
636 *
637 * @param key of the notice
638 * @param attr
639 * @returns {undefined}
640 */
641 add_item_to_notices: function( key, attr = '' ) {
642 var cookie = advads.get_cookie( 'advanced_ads_ad_health_notices' );
643 if( cookie ){
644 advads_cookie_notices = JSON.parse( cookie );
645 } else {
646 advads_cookie_notices = new Array();
647 }
648 // stop if notice was added less than 1 hour ago
649 if( 0 <= advads_cookie_notices.indexOf( key ) ){
650 return;
651 }
652 var query = {
653 action: 'advads-ad-health-notice-push',
654 key: key,
655 attr: attr,
656 nonce: '<?php echo wp_create_nonce('advanced-ads-ad-health-ajax-nonce'); ?>'
657 };
658 // send query
659 // update notices and cookie
660 jQuery.post( advads_frontend_checks.ajax_url, query, function (r) {
661 advads_cookie_notices.push( key );
662 var notices_str = JSON.stringify( advads_cookie_notices );
663 advads.set_cookie_sec( 'advanced_ads_ad_health_notices', notices_str, 3600 ); // 1 hour
664 });
665 },
666
667 /**
668 * Search for hidden AdSense.
669 *
670 * @param string context Context for search.
671 */
672 advads_highlight_hidden_adsense: function( context ) {
673 if ( ! context ) {
674 context = 'html'
675 }
676 if ( window.jQuery ) {
677 var responsive_zero_width = [];
678 jQuery( 'ins.adsbygoogle', context ).each( function() {
679 // Zero width, perhaps because a parent container is floated
680 if ( jQuery( this ).attr( 'data-ad-format' ) && 0 === jQuery( this ).width() ) {
681 responsive_zero_width.push( this.dataset.adSlot );
682 }
683 });
684 if ( responsive_zero_width.length ) {
685 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_floated_responsive_adsense', responsive_zero_width );
686 }
687 }
688 }
689 };
690
691 (function(d, w) {
692 // highlight link as global
693 var highlight_link = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_highlight_ads' );
694 // update ad count in health tool admin bar
695 update_ads_count(d);
696 var addEvent = function( obj, type, fn ) {
697 if ( obj.addEventListener )
698 obj.addEventListener( type, fn, false );
699 else if ( obj.attachEvent )
700 obj.attachEvent( 'on' + type, function() { return fn.call( obj, window.event ); } );
701 };
702
703 // highlight ads that use Advanced Ads placements or AdSense Auto ads
704 function highlight_ads() {
705 /**
706 * Selectors:
707 * Placement container: div[id^="<?php echo Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();?>"]
708 * AdSense Auto ads: 'google-auto-placed'
709 */
710 try {
711 var ad_wrappers = document.querySelectorAll('div[id^="<?php echo Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();?>"],.google-auto-placed')
712 } catch ( e ) { return; }
713 for ( i = 0; i < ad_wrappers.length; i++ ) {
714 ad_wrappers[i].title = ad_wrappers[i].className;
715
716 // Check highlighted ads active
717 ad_wrappers[i].classList.toggle('advanced-ads-highlight-ads');
718 }
719
720 // add or remove active class from highlight link
721 highlight_link.classList.toggle('active');
722 }
723
724 advanced_ads_ready( function() {
725 var adblock_item = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_adblocker_enabled' );
726
727
728 // handle click on the highlight_ads link
729 addEvent( highlight_link, 'click', highlight_ads );
730
731 if ( adblock_item && typeof advanced_ads_adblocker_test === 'undefined' ) {
732 // show hidden item
733 adblock_item.className = adblock_item.className.replace( /hidden/, '' );
734 }
735
736 <?php if ( ! $this->did_the_content ) : ?>
737 var the_content_item = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_the_content_not_invoked' );
738 if ( the_content_item ) {
739 the_content_item.className = the_content_item.className.replace( /hidden/, '' );
740 }
741 <?php endif; ?>
742
743 advanced_ads_frontend_checks.showCount();
744 });
745
746 <?php if( ! isset( $adsense_options['violation-warnings-disable'] ) ) : ?>
747 // show warning if AdSense ad is hidden
748 // show hint if AdSense Auto ads are enabled
749 setTimeout( function(){
750 advanced_ads_ready( advanced_ads_frontend_checks.advads_highlight_hidden_adsense );
751 }, 2000 );
752
753 // highlight AdSense Auto Ads ads 3 seconds after site loaded
754 setTimeout( function(){
755 advanced_ads_ready( advads_highlight_adsense_autoads );
756 }, 3000 );
757 function advads_highlight_adsense_autoads(){
758 if ( ! window.jQuery ) {
759 window.console && window.console.log( 'Advanced Ads: jQuery not found. Some Ad Health warnings will not be displayed.' );
760 return;
761 }
762 var autoads_ads = document.querySelectorAll('.google-auto-placed');
763 // show Auto Ads warning in Ad Health bar if relevant
764 if( autoads_ads.length ){
765 var advads_autoads_link = document.querySelector( '#wp-admin-bar-advanced_ads_autoads_displayed.hidden' );
766 if ( advads_autoads_link ) {
767 advads_autoads_link.className = advads_autoads_link.className.replace( 'hidden', '' );
768 }
769 advanced_ads_frontend_checks.showCount();
770 }
771 }
772 <?php endif;
773 /**
774 * Code to check if current user gave consent to show ads
775 */
776 $privacy = Advanced_Ads_Privacy::get_instance();
777 if ( 'not_needed' !== $privacy->get_state() ) :
778 ?>
779 document.addEventListener('advanced_ads_privacy', function (event) {
780 var advads_consent_link = document.querySelector('#wp-admin-bar-advanced_ads_ad_health_consent_missing');
781
782 if (!advads_consent_link) {
783 return;
784 }
785
786 if (event.detail.state !== 'accepted' && event.detail.state !== 'not_needed') {
787 advads_consent_link.classList.remove('hidden');
788 } else {
789 advads_consent_link.classList.add('hidden');
790 }
791
792 advanced_ads_frontend_checks.showCount();
793 });
794 <?php
795 endif;
796 $privacy_options = $privacy->options();
797 if (
798 ( empty( $privacy_options['enabled'] ) || $privacy_options['consent-method'] !== 'iab_tcf_20' )
799 && (bool) apply_filters( 'advanced-ads-ad-health-show-tcf-notice', true )
800 ) :
801 ?>
802 var count = 0,
803 tcfapiInterval = setInterval(function () {
804 if (++count === 600) {
805 clearInterval(tcfapiInterval);
806 }
807 if (typeof window.__tcfapi === 'undefined') {
808 return;
809 }
810 clearInterval(tcfapiInterval);
811
812 var advads_privacy_link = document.querySelector('#wp-admin-bar-advanced_ads_ad_health_privacy_disabled');
813
814 if (!advads_privacy_link) {
815 return;
816 }
817
818 advads_privacy_link.classList.remove('hidden');
819
820 advanced_ads_frontend_checks.showCount();
821 }, 100);
822 <?php endif; ?>
823 /**
824 * show Google Ad Manager debug link in Ad Health
825 *
826 * look for container with ID starting with `div-gpt-ad-`
827 * or `gpt-ad-` as used by our own Google Ad Manager integration
828 * we don’t look for the gpt header script because that is also used by other services that are based on Google Publisher Tags
829 */
830 function advads_gam_show_debug_link(){
831 var advads_gam_debug_link = document.querySelector( '.advanced_ads_ad_health_gam_debug_link.hidden' );
832
833 if( ! advads_gam_debug_link ){
834 return;
835 }
836
837 // initialized by the GAM header tag or inline body tags
838 if ( document.querySelector( '[id^="div-gpt-ad-"],[id^="gpt-ad-"]' ) ) {
839 advads_gam_debug_link.className = advads_gam_debug_link.className.replace( 'hidden', '' );
840 }
841 }
842 // look for Google Ad Manager tags with a delay of 2 seconds
843 setTimeout( function(){
844 advanced_ads_ready( advads_gam_show_debug_link );
845 }, 2000 );
846
847 // Function to count visible ads with unique group IDs
848 function get_ads_count(){
849 // Get all elements with the specified class name
850 const ad_wrappers = document.getElementsByClassName( "<?php echo Advanced_Ads_Plugin::get_instance()->get_frontend_prefix(); ?>highlighted-wrappers" );
851 // Initialize a count for visible ads
852 let ads_count = 0;
853 // Loop through each ad wrapper element
854 for ( let i = 0; i < ad_wrappers.length; i++ ) {
855 // Check if the group ID is either null or not included in the array of seen group IDs,
856 if ( ad_wrappers[i].offsetHeight > 0 ) {
857 // Increment the ad count and add the group ID to the list
858 ads_count++;
859 }
860 }
861 // Return the total count of eligible ads
862 return ads_count;
863 }
864
865 function update_ads_count(d){
866 var highlight_link = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_highlight_ads' );
867 // update ad count in health tool admin bar
868 highlight_link.querySelector('.ab-item').innerHTML += ' (<span class="highlighted_ads_count">' + get_ads_count() + '</span>) ';
869
870 // If any ads load by ajax its update count after ajax load
871 var origOpen = XMLHttpRequest.prototype.open;
872 XMLHttpRequest.prototype.open = function() {
873 this.addEventListener('load', function() {
874 if ( this.status === 200 ) {
875 highlight_link.querySelector('.highlighted_ads_count').innerHTML = get_ads_count();
876 }
877 });
878 origOpen.apply(this, arguments);
879 };
880 }
881 })(document, window);
882 </script>
883 <?php echo Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
884 }
885
886 /**
887 * Inject JS after ad content.
888 *
889 * @param str $content ad content
890 * @param obj $ad Advanced_Ads_Ad
891 * @return str $content ad content
892 */
893 public function after_ad_output( $content, Advanced_Ads_Ad $ad ) {
894 if ( ! isset( $ad->args['frontend-check'] ) ) { return $content; }
895
896 if ( advads_is_amp() ) {
897 return $content;
898 }
899
900 if ( Advanced_Ads_Ad_Debug::is_https_and_http( $ad ) ) {
901 ob_start(); ?>
902 <script>advanced_ads_ready( function() {
903 var ad_id = '<?php echo $ad->id; ?>';
904 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_has_http', ad_id );
905 advanced_ads_frontend_checks.add_item_to_notices( 'ad_has_http', { append_key: ad_id, ad_id: ad_id } );
906 });</script>
907 <?php
908 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
909 }
910
911 if ( ! Advanced_Ads_Frontend_Checks::can_use_head_placement( $content, $ad ) ) {
912 ob_start(); ?>
913 <script>advanced_ads_ready( function() {
914 var ad_id = '<?php echo $ad->id; ?>';
915 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_incorrect_head', ad_id );
916 advanced_ads_frontend_checks.add_item_to_notices( 'ad_with_output_in_head', { append_key: ad_id, ad_id: ad_id } );
917 });</script>
918 <?php
919 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
920 }
921
922 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
923 if ( 'adsense' === $ad->type
924 && ! empty( $ad->args['cache_busting_elementid'] )
925 && ! isset( $adsense_options['violation-warnings-disable'] )
926 ) {
927 ob_start(); ?>
928 <script>advanced_ads_ready( function() {
929 var ad_id = '<?php echo $ad->id; ?>';
930 var wrapper = '#<?php echo $ad->args['cache_busting_elementid']; ?>';
931 advanced_ads_frontend_checks.advads_highlight_hidden_adsense( wrapper );
932 });</script>
933 <?php
934 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
935 }
936
937 return $content;
938 }
939
940
941 /**
942 * Check if the 'Header Code' placement can be used to delived the ad.
943 *
944 * @param string $content Ad content.
945 * @param Advanced_Ads_Ad $ad Advanced_Ads_Ad.
946 * @return bool
947 */
948 public static function can_use_head_placement( $content, Advanced_Ads_Ad $ad ) {
949
950 if ( ! $ad->is_head_placement ) {
951 return true;
952 }
953
954 // strip linebreaks, because, a line break after a comment is identified as a text node.
955 $content = preg_replace( "/\r|\n/", "", $content );
956
957 if ( ! $dom = self::get_ad_dom( $content ) ) {
958 return true;
959 }
960
961 $body = $dom->getElementsByTagName( 'body' )->item( 0 );
962
963 $count = $body->childNodes->length;
964 for ( $i = 0; $i < $count; $i++ ) {
965 $node = $body->childNodes->item( $i );
966
967 if ( XML_TEXT_NODE === $node->nodeType ) {
968 return false;
969 }
970
971 if ( XML_ELEMENT_NODE === $node->nodeType
972 && ! in_array( $node->nodeName, [ 'meta', 'link', 'title', 'style', 'script', 'noscript', 'base' ] ) ) {
973 return false;
974 }
975 }
976 return true;
977 }
978
979 /**
980 * Convert ad content to a DOMDocument.
981 *
982 * @param string $content
983 * @return DOMDocument|false
984 */
985 private static function get_ad_dom( $content ) {
986 if ( ! extension_loaded( 'dom' ) ) {
987 return false;
988 }
989 $libxml_previous_state = libxml_use_internal_errors( true );
990 $dom = new DOMDocument();
991 $result = $dom->loadHTML( '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $content . '</body></html>' );
992
993 libxml_clear_errors();
994 libxml_use_internal_errors( $libxml_previous_state );
995
996 if ( ! $result ) {
997 return false;
998 }
999
1000 return $dom;
1001 }
1002
1003 /**
1004 * Check if at least one placement uses `the_content`.
1005 *
1006 * @return bool True/False.
1007 */
1008 private function has_the_content_placements() {
1009 $placements = Advanced_Ads::get_ad_placements_array();
1010 $placement_types = Advanced_Ads_Placements::get_placement_types();
1011 // Find a placement that depends on 'the_content' filter.
1012 foreach ( $placements as $placement ) {
1013 if ( isset ( $placement['type'] )
1014 && ! empty( $placement_types[ $placement['type'] ]['options']['uses_the_content'] ) ) {
1015 return true;
1016 }
1017 }
1018 return false;
1019 }
1020 }
1021