PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.5
Advanced Ads – Ad Manager & AdSense v2.0.5
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
ad-health-notices.php 1 year ago checks.php 1 year ago display-conditions.php 1 year ago filesystem.php 2 years ago frontend_checks.php 1 year ago in-content-injector.php 1 year ago inline-css.php 1 year ago utils.php 1 year ago visitor-conditions.php 1 year ago
frontend_checks.php
1260 lines
1 <?php
2 // phpcs:ignoreFile
3
4 use AdvancedAds\Abstracts\Ad;
5 use AdvancedAds\Utilities\Data;
6 use AdvancedAds\Utilities\Validation;
7 use AdvancedAds\Utilities\Conditional;
8
9 /**
10 * Class Advanced_Ads_Frontend_Checks
11 *
12 * Handle Ad Health and other notifications and checks in the frontend.
13 */
14 class Advanced_Ads_Frontend_Checks {
15 /**
16 * True if 'the_content' was invoked, false otherwise.
17 *
18 * @var bool
19 */
20 private $did_the_content = false;
21 private $has_many_the_content = false;
22
23 /**
24 * Plugin options.
25 *
26 * @var array
27 */
28 private $options = [];
29
30 /**
31 * Constructor.
32 */
33 public function __construct() {
34 // Wait until other plugins (for example Elementor) have disabled admin bar using `show_admin_bar` filter.
35 add_action( 'template_redirect', [ $this, 'init' ], 11 );
36
37 if ( wp_doing_ajax() ) {
38 add_filter( 'advanced-ads-ad-output', [ $this, 'after_ad_output' ], 10, 2 );
39 }
40
41 // get plugin options.
42 $this->options = Advanced_Ads::get_instance()->options();
43 }
44
45 /**
46 * Ad Health init.
47 */
48 public function init() {
49 if ( ! is_admin()
50 && is_admin_bar_showing()
51 && Conditional::user_can( 'advanced_ads_edit_ads' )
52 && Advanced_Ads_Ad_Health_Notices::notices_enabled()
53 ) {
54 add_action( 'admin_bar_menu', [ $this, 'add_admin_bar_menu' ], 1000 );
55 add_filter( 'the_content', [ $this, 'set_did_the_content' ] );
56 add_action( 'wp_footer', [ $this, 'footer_checks' ], -101 );
57 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
58 add_filter( 'advanced-ads-ad-select-args', [ $this, 'ad_select_args_callback' ] );
59 add_filter( 'advanced-ads-ad-output', [ $this, 'after_ad_output' ], 10, 2 );
60 }
61
62 if ( Advanced_Ads_Ad_Health_Notices::notices_enabled() ) {
63 add_action( 'body_class', [ $this, 'body_class' ] );
64 }
65
66 if ( ( $this->has_adblocker_placements() || $this->has_adblocker_visitor_condition() )
67 && ! wp_advads()->registry->is_script( 'find-adblocker', 'enqueued' ) ) {
68 wp_advads()->registry->enqueue_script( 'find-adblocker' );
69 }
70 }
71
72 /**
73 * Notify ads loaded with AJAX.
74 *
75 * @param array $args ad arguments.
76 * @return array $args
77 */
78 public function ad_select_args_callback( $args ) {
79 $args['frontend-check'] = true;
80 return $args;
81 }
82
83 /**
84 * Enqueue scripts
85 * needs to add ajaxurl in case no other plugin is doing that
86 */
87 public function enqueue_scripts() {
88 if ( advads_is_amp() ) {
89 return;
90 }
91
92 // we don’t have our own script, so we attach this information to jquery.
93 wp_localize_script( 'jquery', 'advads_frontend_checks', [ 'ajax_url' => admin_url( 'admin-ajax.php' ) ] );
94 }
95
96 /**
97 * List current ad situation on the page in the admin-bar.
98 *
99 * @param object $wp_admin_bar WP_Admin_Bar.
100 */
101 public function add_admin_bar_menu( $wp_admin_bar ) {
102 global $wp_the_query, $post, $wp_scripts;
103
104 $options = Advanced_Ads::get_instance()->options();
105
106 // load AdSense related options.
107 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
108
109 // common data used in nodes[].
110 $health_parent_class = 'advanced_ads_ad_health';
111 $health_node_common_data = [
112 'parent' => $health_parent_class,
113 'meta' => [
114 'class' => 'advanced_ads_ad_health_warning',
115 'target' => '_blank',
116 ],
117 ];
118
119 // check if AdSense loads Auto Ads ads
120 // Hidden, will be shown using js.
121 if ( ! isset( $adsense_options['violation-warnings-disable'] ) ) {
122 $nodes[] = [
123 'type' => 2,
124 'data' => [
125 'parent' => $health_parent_class,
126 'id' => 'advanced_ads_autoads_displayed',
127 'title' => __( 'Random AdSense ads', 'advanced-ads' ),
128 'href' => 'https://wpadvancedads.com/adsense-in-random-positions-auto-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=frontend-autoads-ads',
129 'meta' => [
130 'class' => 'hidden',
131 'target' => '_blank',
132 ],
133 ],
134 ];
135 }
136
137 // check if current user was identified as a bot.
138 if ( Conditional::is_ua_bot() ) {
139 $nodes[] = [
140 'type' => 1,
141 'data' => array_merge(
142 $health_node_common_data,
143 [
144 'id' => 'advanced_ads_user_is_bot',
145 'title' => __( 'You look like a bot', 'advanced-ads' ),
146 'href' => 'https://wpadvancedads.com/manual/ad-health/#look-like-bot',
147 ]
148 ),
149 ];
150 }
151
152 // check if an ad blocker is enabled
153 // Hidden, will be shown using js.
154 $nodes[] = [
155 'type' => 2,
156 'data' => [
157 'parent' => $health_parent_class,
158 'id' => 'advanced_ads_ad_health_adblocker_enabled',
159 'title' => __( 'Ad blocker enabled', 'advanced-ads' ),
160 'meta' => [
161 'class' => 'hidden advanced_ads_ad_health_warning',
162 'target' => '_blank',
163 ],
164 ],
165 ];
166
167 if ( $wp_the_query->is_singular() ) {
168 if ( $this->has_the_content_placements() ) {
169 $nodes[] = [
170 'type' => 2,
171 'data' => [
172 'parent' => $health_parent_class,
173 'id' => 'advanced_ads_ad_health_the_content_not_invoked',
174 'title' => __( '<em>the_content</em> filter does not exist', 'advanced-ads' ),
175 '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',
176 'meta' => [
177 'class' => 'hidden advanced_ads_ad_health_warning',
178 'target' => '_blank',
179 ],
180 ],
181 ];
182 }
183
184 if ( ! empty( $post->ID ) ) {
185 $ad_settings = get_post_meta( $post->ID, '_advads_ad_settings', true );
186
187 if ( ! empty( $ad_settings['disable_the_content'] ) ) {
188 $nodes[] = [
189 'type' => 1,
190 'data' => array_merge(
191 $health_node_common_data,
192 [
193 'id' => 'advanced_ads_ad_health_disabled_in_content',
194 'title' => __( 'Ads are disabled in the content of this page', 'advanced-ads' ),
195 'href' => get_edit_post_link( $post->ID ) . '#advads-ad-settings',
196 ]
197 ),
198 ];
199 }
200 } else {
201 $nodes[] = [
202 'type' => 1,
203 'data' => array_merge(
204 $health_node_common_data,
205 [
206 'id' => 'advanced_ads_ad_health_post_zero',
207 'title' => __( 'the current post ID is 0 ', 'advanced-ads' ),
208 'href' => 'https://wpadvancedads.com/manual/ad-health/#post-id-0',
209 ]
210 ),
211 ];
212
213 }
214 }
215
216 $disabled_reason = wp_advads()->frontend->get_disabled_reason();
217 $disabled_id = wp_advads()->frontend->get_disabled_id();
218 $settings_page = admin_url( 'admin.php?page=advanced-ads-settings' );
219
220 if ( 'page' === $disabled_reason && $disabled_id ) {
221 $nodes[] = [
222 'type' => 1,
223 'data' => array_merge(
224 $health_node_common_data,
225 [
226 'id' => 'advanced_ads_ad_health_disabled_on_page',
227 'title' => __( 'Ads are disabled on this page', 'advanced-ads' ),
228 'href' => get_edit_post_link( $disabled_id ) . '#advads-ad-settings',
229 ]
230 ),
231 ];
232 } elseif ( 'all' === $disabled_reason ) {
233 $nodes[] = [
234 'type' => 1,
235 'data' => array_merge(
236 $health_node_common_data,
237 [
238 'id' => 'advanced_ads_ad_health_no_all',
239 'title' => __( 'Ads are disabled on all pages', 'advanced-ads' ),
240 'href' => $settings_page,
241 ]
242 ),
243 ];
244 } elseif ( '404' === $disabled_reason ) {
245 $nodes[] = [
246 'type' => 1,
247 'data' => array_merge(
248 $health_node_common_data,
249 [
250 'id' => 'advanced_ads_ad_health_no_404',
251 'title' => __( 'Ads are disabled on 404 pages', 'advanced-ads' ),
252 'href' => $settings_page,
253 ]
254 ),
255 ];
256 } elseif ( 'archive' === $disabled_reason ) {
257 $nodes[] = [
258 'type' => 1,
259 'data' => array_merge(
260 $health_node_common_data,
261 [
262 'id' => 'advanced_ads_ad_health_no_archive',
263 'title' => __( 'Ads are disabled on non singular pages', 'advanced-ads' ),
264 'href' => $settings_page,
265 ]
266 ),
267 ];
268 } elseif ( 'user-role' === $disabled_reason ) {
269 global $wp_roles;
270 $role_names = [];
271
272 if ( isset( $this->options['hide-for-user-role'] ) ) {
273 $option_roles = array_flip( $this->options['hide-for-user-role'] );
274 $role_names = array_intersect_key( $wp_roles->get_names(), $option_roles );
275 }
276
277 $title = __( 'Ads are disabled for the user role(s)', 'advanced-ads' ) . ': ' . implode( ', ', $role_names );
278
279 $nodes[] = [
280 'type' => 1,
281 'data' => array_merge(
282 $health_node_common_data,
283 [
284 'id' => 'advanced_ads_ad_health_no_user_role',
285 'title' => $title,
286 'href' => $settings_page,
287 ]
288 ),
289 ];
290 } elseif ( 'ip-address' === $disabled_reason ) {
291 $user_ip = get_user_ip_address();
292 $nodes[] = [
293 'type' => 1,
294 'data' => array_merge(
295 $health_node_common_data,
296 [
297 'id' => 'advanced_ads_ad_health_disabled_ip_address',
298 'title' => __( 'Ads are disabled for this IP address', 'advanced-ads' ) . ': ' . $user_ip,
299 'href' => $settings_page,
300 ]
301 ),
302 ];
303 }
304
305 $nodes[] = [
306 'type' => 2,
307 'data' => [
308 'parent' => $health_parent_class,
309 'id' => 'advanced_ads_ad_health_has_http',
310 'title' => sprintf(
311 '%s %s',
312 __( 'Your website is using HTTPS, but the ad code contains HTTP and might not work.', 'advanced-ads' ),
313 /* translators: em tags */
314 sprintf( __( 'Ad IDs: %s', 'advanced-ads' ), '<em></em>' )
315 ),
316 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-https-ads#https-ads',
317 'meta' => [
318 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_has_http',
319 'target' => '_blank',
320 ],
321 ],
322 ];
323
324 $nodes[] = [
325 'type' => 2,
326 'data' => [
327 'parent' => $health_parent_class,
328 'id' => 'advanced_ads_ad_health_incorrect_head',
329 'title' => sprintf( __( 'Visible ads should not use the Header placement: %s', 'advanced-ads' ), '<i></i>' ),
330 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-visible-ad-in-header#header-ads',
331 'meta' => [
332 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_incorrect_head',
333 'target' => '_blank',
334 ],
335 ],
336 ];
337
338 // warn if an AdSense ad seems to be hidden.
339 if ( ! isset( $adsense_options['violation-warnings-disable'] ) ) {
340 $nodes[] = [
341 'type' => 2,
342 'data' => [
343 'parent' => $health_parent_class,
344 'id' => 'advanced_ads_ad_health_hidden_adsense',
345 'title' => sprintf(
346 '%s: %s. %s',
347 __( 'AdSense violation', 'advanced-ads' ),
348 __( 'Ad is hidden', 'advanced-ads' ),
349 /* translators: em tags */
350 sprintf( __( 'IDs: %s', 'advanced-ads' ), '<em></em>' )
351 ),
352 'href' => 'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adhealth-frontend-adsense-hidden#adsense-hidden',
353 'meta' => [
354 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_hidden_adsense',
355 'target' => '_blank',
356 ],
357 ],
358 ];
359 }
360
361 $nodes[] = [
362 'type' => 2,
363 'data' => [
364 'parent' => $health_parent_class,
365 'id' => 'advanced_ads_ad_health_floated_responsive_adsense',
366 /* translators: em tags */
367 'title' => sprintf( __( 'The following responsive AdSense ads are not showing up: %s', 'advanced-ads' ), '<em></em>' ),
368 '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',
369 'meta' => [
370 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_floated_responsive_adsense',
371 'target' => '_blank',
372 ],
373 ],
374 ];
375
376 // warn if consent was not given.
377 $privacy = Advanced_Ads_Privacy::get_instance();
378 if ( 'not_needed' !== $privacy->get_state() ) {
379 $nodes[] = [
380 'type' => 2,
381 'data' => [
382 'parent' => $health_parent_class,
383 'id' => 'advanced_ads_ad_health_consent_missing',
384 'title' => __( 'Consent not given', 'advanced-ads' ),
385 'href' => admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ),
386 'meta' => [
387 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_consent_missing',
388 'target' => '_blank',
389 ],
390 ],
391 ];
392 }
393
394 $privacy_options = $privacy->options();
395 if ( empty( $privacy_options['enabled'] ) || 'iab_tcf_20' !== $privacy_options['consent-method'] ) {
396 $nodes[] = [
397 'type' => 2,
398 'data' => [
399 'parent' => $health_parent_class,
400 'id' => 'advanced_ads_ad_health_privacy_disabled',
401 'title' => __( 'Enable TCF integration', 'advanced-ads' ),
402 'href' => admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ),
403 'meta' => [
404 'class' => 'hidden advanced_ads_ad_health_warning advanced_ads_ad_health_privacy_disabled',
405 'target' => '_blank',
406 ],
407 ],
408 ];
409 }
410
411 $nodes[] = [
412 'type' => 3,
413 'data' => [
414 'parent' => $health_parent_class,
415 'id' => 'advanced_ads_ad_health_gam_debug',
416 'title' => __( 'Debug Google Ad Manager', 'advanced-ads' ),
417 'href' => esc_url( add_query_arg( 'google_force_console', '1' ) ),
418 'meta' => [
419 'class' => 'hidden advanced_ads_ad_health_gam_debug_link',
420 ],
421 ],
422 ];
423
424 // link to highlight ads and jump from one ad to the next.
425 $nodes[] = [
426 'type' => 3,
427 'amp' => false,
428 'data' => [
429 'parent' => $health_parent_class,
430 'id' => 'advanced_ads_ad_health_highlight_ads',
431 'title' => sprintf(
432 '<span class="link">%s</span> %s',
433 __( 'highlight ads', 'advanced-ads' ),
434 '<span class="arrows">
435 <i class = "dashicons dashicons-arrow-up-alt previous"></i>
436 <i class = "dashicons dashicons-arrow-down-alt next"></i>
437 </span>'
438 ),
439 'meta' => [
440 'class' => 'advanced_ads_ad_health_highlight_ads',
441 ],
442 ],
443 ];
444
445 /**
446 * Add new node.
447 *
448 * @param array $node An array that contains:
449 * 'type' => 1 - warning, 2 - hidden warning that will be shown using JS, 3 - info message
450 * 'data': @see WP_Admin_Bar->add_node
451 * @param object $wp_admin_bar
452 */
453 $nodes = apply_filters( 'advanced-ads-ad-health-nodes', $nodes );
454
455 usort( $nodes, [ $this, 'sort_nodes' ] );
456
457 // load number of already detected notices.
458 $notices = Advanced_Ads_Ad_Health_Notices::get_number_of_notices();
459
460 if ( ! advads_is_amp() ) {
461 $warnings = 0; // Will be updated using JS.
462 } else {
463 $warnings = $this->count_visible_warnings( $nodes, [ 1 ] );
464 }
465
466 $issues = $warnings;
467
468 $this->add_header_nodes( $wp_admin_bar, $issues, $notices );
469
470 foreach ( $nodes as $node ) {
471 if ( isset( $node['data'] ) ) {
472 $wp_admin_bar->add_node( $node['data'] );
473 }
474 }
475
476 $this->add_footer_nodes( $wp_admin_bar, $issues );
477 }
478
479
480 /**
481 * Add classes to the `body` tag.
482 *
483 * @param string[] $classes Array of existing class names.
484 * @return string[] $classes Array of existing and new class names.
485 */
486 public function body_class( $classes ) {
487 global $post;
488
489 $aa_classes = [
490 'aa-prefix-' . wp_advads()->get_frontend_prefix(),
491 ];
492
493 $disabled_reason = wp_advads()->frontend->get_disabled_reason();
494 if ( $disabled_reason ) {
495 $aa_classes[] = 'aa-disabled-' . esc_attr( $disabled_reason );
496 }
497
498 if ( ! empty( $post->ID ) ) {
499 $ad_settings = get_post_meta( $post->ID, '_advads_ad_settings', true );
500 if ( ! empty( $ad_settings['disable_the_content'] ) ) {
501 $aa_classes[] = 'aa-disabled-content';
502 }
503 }
504
505 // hide-ads-from-bots option is enabled.
506 if ( ! empty( $this->options['block-bots'] ) ) {
507 $aa_classes[] = 'aa-disabled-bots';
508 }
509
510 $aa_classes = apply_filters( 'advanced-ads-body-classes', $aa_classes );
511
512 if ( ! is_array( $classes ) ) {
513 $classes = [];
514 }
515 if ( ! is_array( $aa_classes ) ) {
516 $aa_classes = [];
517 }
518
519 return array_merge( $classes, $aa_classes );
520 }
521
522
523
524
525 /**
526 * Count visible notices and warnings.
527 *
528 * @param array $nodes Nodes to add.
529 * @param array $types Warning types.
530 */
531 private function count_visible_warnings( $nodes, $types = [] ) {
532 $warnings = 0;
533 foreach ( $nodes as $node ) {
534 if ( ! isset( $node['type'] ) || ! isset( $node['data'] ) ) {
535 continue;
536 }
537 if ( in_array( $node['type'], $types ) ) {
538 ++$warnings;
539 }
540 }
541 return $warnings;
542 }
543
544 /**
545 * Add header nodes.
546 *
547 * @param object $wp_admin_bar WP_Admin_Bar object.
548 * @param int $issues Number of all issues.
549 * @param int $notices Number of notices.
550 */
551 private function add_header_nodes( $wp_admin_bar, $issues, $notices ) {
552 $wp_admin_bar->add_node(
553 [
554 'id' => 'advanced_ads_ad_health',
555 'title' => __( 'Ad Health', 'advanced-ads' ) . '&nbsp;<span class="advanced-ads-issue-counter">' . $issues . '</span>',
556 'parent' => false,
557 'href' => admin_url( 'admin.php?page=advanced-ads' ),
558 'meta' => [
559 'class' => $issues ? 'advads-adminbar-is-warnings' : '',
560 ],
561 ]
562 );
563
564 // show that there are backend notices.
565 if ( $notices ) {
566 $wp_admin_bar->add_node(
567 [
568 'parent' => 'advanced_ads_ad_health',
569 'id' => 'advanced_ads_ad_health_more',
570 /* translators: number of notices */
571 'title' => sprintf( __( 'Show %d more notifications', 'advanced-ads' ), absint( $notices ) ),
572 'href' => admin_url( 'admin.php?page=advanced-ads' ),
573 ]
574 );
575 }
576 }
577
578 /**
579 * Add footer nodes.
580 *
581 * @param obj $wp_admin_bar WP_Admin_Bar object.
582 * @param int $issues Number of all issues.
583 */
584 private function add_footer_nodes( $wp_admin_bar, $issues ) {
585 if ( ! $issues ) {
586 $wp_admin_bar->add_node(
587 [
588 'parent' => 'advanced_ads_ad_health',
589 'id' => 'advanced_ads_ad_health_fine',
590 'title' => __( 'Everything is fine', 'advanced-ads' ),
591 'href' => false,
592 'meta' => [
593 'target' => '_blank',
594 ],
595 ]
596 );
597 }
598
599 $wp_admin_bar->add_node(
600 [
601 'parent' => 'advanced_ads_ad_health',
602 'id' => 'advanced_ads_ad_health_support',
603 'title' => __( 'Get help', 'advanced-ads' ),
604 'href' => Data::support_url( '?utm_source=advanced-ads&utm_medium=link&utm_campaign=health-support' ),
605 'meta' => [
606 'target' => '_blank',
607 ],
608 ]
609 );
610 }
611
612 /**
613 * Filter out nodes intended to AMP pages only.
614 *
615 * @param array $nodes Nodes to add.
616 * @return array $nodes Nodes to add.
617 */
618 private function filter_nodes( $nodes ) {
619 return $nodes;
620 }
621
622 /**
623 * Sort nodes.
624 *
625 * @param array $a The first node to compare.
626 * @param array $b The second node to compare.
627 * @return int Returns -1 if $a is less than $b, 1 if $a is greater than $b, and 0 if they are equal.
628 */
629 public function sort_nodes( $a, $b ) {
630 if ( ! isset( $a['type'] ) || ! isset( $b['type'] ) ) {
631 return 0;
632 }
633 if ( $a['type'] === $b['type'] ) {
634 return 0;
635 }
636 return ( $a['type'] < $b['type'] ) ? -1 : 1;
637 }
638
639 /**
640 * Set variable to 'true' when 'the_content' filter is invoked.
641 *
642 * @param string $content The content to be filtered.
643 * @return string $content The filtered content.
644 */
645 public function set_did_the_content( $content ) {
646 if ( ! $this->did_the_content ) {
647 $this->did_the_content = true;
648 }
649
650 if ( Advanced_Ads::get_instance()->has_many_the_content() ) {
651 $this->has_many_the_content = true;
652 }
653 return $content;
654 }
655
656 /**
657 * Check conditions and display warning.
658 * Conditions:
659 * AdBlocker enabled,
660 * jQuery is included in header
661 * AdSense Quick Start ads are running
662 */
663 public function footer_checks() {
664 ob_start();
665 ?><!-- Advanced Ads: <?php esc_html_e( 'the following code is used for automatic error detection and only visible to admins', 'advanced-ads' ); ?>-->
666 <style>#wp-admin-bar-advanced_ads_ad_health .hidden { display: none; }
667 #wp-admin-bar-advanced_ads_ad_health-default a:after { content: "\25BA"; margin-left: .5em; font-size: smaller; }
668 #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; }
669 #wp-admin-bar-advanced_ads_ad_health-default .advanced_ads_ad_health_highlight_ads div:hover { color: #00b9eb; cursor: pointer; }
670 #wpadminbar .advanced-ads-issue-counter { background-color: #d54e21; display: none; padding: 1px 7px 1px 6px!important; border-radius: 50%; color: #fff; }
671 #wpadminbar .advads-adminbar-is-warnings .advanced-ads-issue-counter { display: inline; }
672 .advanced-ads-highlight-ads { outline:4px solid #0474A2 !important; }
673 #wp-admin-bar-advanced_ads_ad_health .advanced_ads_ad_health_highlight_ads .arrows {display: none;}
674 #wp-admin-bar-advanced_ads_ad_health .arrows .dashicons {font-family: 'dashicons';}
675 #wp-admin-bar-advanced_ads_ad_health.hover .advanced_ads_ad_health_highlight_ads.active .arrows {display: inline-block;}
676 </style>
677 <?php
678 // phpcs:ignore
679 echo ob_get_clean();
680
681 if ( advads_is_amp() ) {
682 return;
683 }
684
685 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
686 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.EnqueuedResources.NonEnqueuedScript
687 ob_start();
688 ?>
689 <script type="text/javascript" src="<?php echo ADVADS_BASE_URL . 'admin/assets/js/advertisement.js'; ?>"></script>
690 <script>
691 var advanced_ads_frontend_checks = {
692 showCount: function() {
693 try {
694 // Count only warnings that have the 'advanced_ads_ad_health_warning' class.
695 var warning_count = document.querySelectorAll( '.advanced_ads_ad_health_warning:not(.hidden)' ).length;
696 var fine_item = document.getElementById( 'wp-admin-bar-advanced_ads_ad_health_fine' );
697 } catch ( e ) { return; }
698
699 var header = document.querySelector( '#wp-admin-bar-advanced_ads_ad_health > a' );
700 if ( warning_count ) {
701 if ( fine_item ) {
702 // Hide 'fine' item.
703 fine_item.className += ' hidden';
704 }
705
706 if ( header ) {
707 header.innerHTML = header.innerHTML.replace(/<span class="advanced-ads-issue-counter">\d*<\/span>/, '') + '<span class="advanced-ads-issue-counter">' + warning_count + '</span>';
708 // add class
709 header.className += ' advads-adminbar-is-warnings';
710 }
711 } else {
712 // Show 'fine' item.
713 if ( fine_item ) {
714 fine_item.classList.remove('hidden');
715 }
716
717 // Remove counter.
718 if ( header ) {
719 header.innerHTML = header.innerHTML.replace(/<span class="advanced-ads-issue-counter">\d*<\/span>/, '');
720 header.classList.remove('advads-adminbar-is-warnings');
721 }
722 }
723 },
724
725 array_unique: function( array ) {
726 var r= [];
727 for ( var i = 0; i < array.length; i++ ) {
728 if ( r.indexOf( array[ i ] ) === -1 ) {
729 r.push( array[ i ] );
730 }
731 }
732 return r;
733 },
734
735 /**
736 * Add item to Ad Health node.
737 *
738 * @param string selector Selector of the node.
739 * @param string/array item item(s) to add.
740 */
741 add_item_to_node: function( selector, item ) {
742 if ( typeof item === 'string' ) {
743 item = item.split();
744 }
745 var selector = document.querySelector( selector );
746 if ( selector ) {
747 selector.className = selector.className.replace( 'hidden', '' );
748 selector.innerHTML = selector.innerHTML.replace( /(<i>)(.*?)(<\/i>)/, function( match, p1, p2, p3 ) {
749 p2 = ( p2 ) ? p2.split( ', ' ) : [];
750 p2 = p2.concat( item );
751 p2 = advanced_ads_frontend_checks.array_unique( p2 );
752 return p1 + p2.join( ', ' ) + p3;
753 } );
754 advanced_ads_frontend_checks.showCount();
755 }
756 },
757
758 /**
759 * Add item to Ad Health notices in the backend
760 *
761 * @param key of the notice
762 * @param attr
763 * @returns {undefined}
764 */
765 add_item_to_notices: function( key, attr = '' ) {
766 var cookie = advads.get_cookie( 'advanced_ads_ad_health_notices' );
767 if ( cookie ){
768 advads_cookie_notices = JSON.parse( cookie );
769 } else {
770 advads_cookie_notices = new Array();
771 }
772 // stop if notice was added less than 1 hour ago
773 if ( 0 <= advads_cookie_notices.indexOf( key ) ){
774 return;
775 }
776 var query = {
777 action: 'advads-ad-health-notice-push',
778 key: key,
779 attr: attr,
780 nonce: '<?php echo wp_create_nonce('advanced-ads-ad-health-ajax-nonce'); ?>'
781 };
782 // send query
783 // update notices and cookie
784 jQuery.post( advads_frontend_checks.ajax_url, query, function (r) {
785 advads_cookie_notices.push( key );
786 var notices_str = JSON.stringify( advads_cookie_notices );
787 advads.set_cookie_sec( 'advanced_ads_ad_health_notices', notices_str, 3600 ); // 1 hour
788 });
789 },
790
791 /**
792 * Search for hidden AdSense.
793 *
794 * @param string context Context for search.
795 */
796 advads_highlight_hidden_adsense: function( context ) {
797 if ( ! context ) {
798 context = 'html'
799 }
800 if ( window.jQuery ) {
801 var responsive_zero_width = [];
802 jQuery( 'ins.adsbygoogle', context ).each( function() {
803 // Zero width, perhaps because a parent container is floated
804 if ( jQuery( this ).attr( 'data-ad-format' ) && 0 === jQuery( this ).width() ) {
805 responsive_zero_width.push( this.dataset.adSlot );
806 }
807 });
808 if ( responsive_zero_width.length ) {
809 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_floated_responsive_adsense', responsive_zero_width );
810 }
811 }
812 }
813 };
814
815 (function(d, w) {
816 // highlight link as global
817 var highlightLink = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_highlight_ads' );
818 var adWrappers;
819 // update ad count in health tool admin bar
820 updateAdsCount(d);
821 var addEvent = function( obj, type, fn ) {
822 if ( obj.addEventListener )
823 obj.addEventListener( type, fn, false );
824 else if ( obj.attachEvent )
825 obj.attachEvent( 'on' + type, function() { return fn.call( obj, window.event ); } );
826 };
827
828 function getAdWrappers() {
829 return document.querySelectorAll(".<?php echo wp_advads()->get_frontend_prefix(); ?>highlight-wrapper, .google-auto-placed");
830 }
831
832 // highlight ads that use Advanced Ads placements or AdSense Auto ads
833 function highlightAds() {
834 /**
835 * Selectors:
836 * Placement container: ".<?php echo wp_advads()->get_frontend_prefix(); ?>highlight-wrapper, .google-auto-placed"
837 * AdSense Auto ads: 'google-auto-placed'
838 */
839 try {
840 <?php //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped ?>
841 adWrappers = getAdWrappers();
842 <?php //phpcs:enable ?>
843 } catch ( e ) { return; }
844 for ( i = 0; i < adWrappers.length; i++ ) {
845 // Check highlighted ads active
846 adWrappers[i].classList.toggle('advanced-ads-highlight-ads');
847 // show title only when highlight ads active.
848 if ( adWrappers[i].classList.contains('advanced-ads-highlight-ads') ) {
849 adWrappers[i].title = adWrappers[i].getAttribute('data-title');
850 } else {
851 adWrappers[i].title = '';
852 }
853 }
854
855 // add or remove active class from highlight link
856 highlightLink.classList.toggle('active');
857 }
858
859 function scrollToHighlightedAd() {
860 try {
861 // If no ad wrappers are found, exit the function
862 if (adWrappers.length === 0) return;
863
864 // Initialize or update the index of the currently highlighted ad
865 if (typeof window.current_highlighted_ad === "undefined") {
866 window.current_highlighted_ad = 0;
867 } else if (this.classList.contains('next') && adWrappers.length - 1 > window.current_highlighted_ad) {
868 window.current_highlighted_ad++;
869 } else if (this.classList.contains('previous') && window.current_highlighted_ad > 0) {
870 window.current_highlighted_ad--;
871 }
872
873 // Get the offsetTop of the currently highlighted ad's wrapper
874 const scrollDiv = document.getElementById(adWrappers[window.current_highlighted_ad]?.id)?.offsetTop;
875
876 // If scrollDiv is defined, scroll to the ad wrapper's position
877 if (scrollDiv !== undefined) {
878 window.scrollTo({ top: scrollDiv, behavior: 'smooth' });
879 }
880 } catch (e) {
881 // Handle any errors that might occur
882 }
883 }
884
885 advanced_ads_ready( function() {
886 var adblock_item = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_adblocker_enabled' );
887
888 // handle click on the highlightAds link
889 var link = highlightLink.querySelector('.link');
890 addEvent( link, 'click', highlightAds );
891
892 // arrows click handler
893 var arrows = highlightLink.querySelector('.arrows').querySelectorAll('.dashicons');
894 for ( let i = 0; i < arrows.length; i++ ) {
895 arrows[i].addEventListener("click", scrollToHighlightedAd);
896 }
897
898 if ( adblock_item && typeof advanced_ads_adblocker_test === 'undefined' ) {
899 // show hidden item
900 adblock_item.className = adblock_item.className.replace( /hidden/, '' );
901 }
902
903 <?php if ( ! $this->did_the_content ) : ?>
904 var the_content_item = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_the_content_not_invoked' );
905 if ( the_content_item ) {
906 the_content_item.className = the_content_item.className.replace( /hidden/, '' );
907 }
908 <?php endif; ?>
909
910 advanced_ads_frontend_checks.showCount();
911 });
912
913 <?php if ( ! isset( $adsense_options['violation-warnings-disable'] ) ) : ?>
914 // show warning if AdSense ad is hidden
915 // show hint if AdSense Auto ads are enabled
916 setTimeout( function(){
917 advanced_ads_ready( advanced_ads_frontend_checks.advads_highlight_hidden_adsense );
918 }, 2000 );
919
920 // highlight AdSense Auto Ads ads 3 seconds after site loaded
921 setTimeout( function(){
922 advanced_ads_ready( advads_highlight_adsense_autoads );
923 }, 3000 );
924 function advads_highlight_adsense_autoads(){
925 if ( ! window.jQuery ) {
926 window.console && window.console.log( 'Advanced Ads: jQuery not found. Some Ad Health warnings will not be displayed.' );
927 return;
928 }
929 var autoads_ads = document.querySelectorAll('.google-auto-placed');
930 // show Auto Ads warning in Ad Health bar if relevant
931 if ( autoads_ads.length ){
932 var advads_autoads_link = document.querySelector( '#wp-admin-bar-advanced_ads_autoads_displayed.hidden' );
933 if ( advads_autoads_link ) {
934 advads_autoads_link.className = advads_autoads_link.className.replace( 'hidden', '' );
935 }
936 advanced_ads_frontend_checks.showCount();
937 }
938 }
939 <?php
940 endif;
941 /**
942 * Code to check if current user gave consent to show ads
943 */
944 $privacy = Advanced_Ads_Privacy::get_instance();
945 if ( 'not_needed' !== $privacy->get_state() ) :
946 ?>
947 document.addEventListener('advanced_ads_privacy', function (event) {
948 var advads_consent_link = document.querySelector('#wp-admin-bar-advanced_ads_ad_health_consent_missing');
949
950 if (!advads_consent_link) {
951 return;
952 }
953
954 if (event.detail.state !== 'accepted' && event.detail.state !== 'not_needed') {
955 advads_consent_link.classList.remove('hidden');
956 } else {
957 advads_consent_link.classList.add('hidden');
958 }
959
960 advanced_ads_frontend_checks.showCount();
961 });
962 <?php
963 endif;
964 $privacy_options = $privacy->options();
965 if (
966 ( empty( $privacy_options['enabled'] ) || 'iab_tcf_20' !== $privacy_options['consent-method'] )
967 && (bool) apply_filters( 'advanced-ads-ad-health-show-tcf-notice', true )
968 ) :
969 ?>
970 var count = 0,
971 tcfapiInterval = setInterval(function () {
972 if (++count === 181) {
973 clearInterval(tcfapiInterval);
974 }
975 if (typeof window.__tcfapi === 'undefined') {
976 return;
977 }
978 clearInterval(tcfapiInterval);
979
980 var advadsPrivacyLink = document.querySelector('#wp-admin-bar-advanced_ads_ad_health_privacy_disabled');
981
982 if (!advadsPrivacyLink) {
983 return;
984 }
985
986 advadsPrivacyLink.classList.remove('hidden');
987
988 advanced_ads_frontend_checks.showCount();
989 }, 333);
990 <?php endif; ?>
991 /**
992 * show Google Ad Manager debug link in Ad Health
993 *
994 * look for container with ID starting with `div-gpt-ad-`
995 * or `gpt-ad-` as used by our own Google Ad Manager integration
996 * we don’t look for the gpt header script because that is also used by other services that are based on Google Publisher Tags
997 */
998 function advadsGamShowDebugLink(){
999 var advadsGamDebugLink = document.querySelector( '.advanced_ads_ad_health_gam_debug_link.hidden' );
1000
1001 if ( ! advadsGamDebugLink ){
1002 return;
1003 }
1004
1005 // Check for the `googletag` variable created in the page header or directly in the body alongside the ad slot definition.
1006 if ( typeof window.googletag !== 'undefined' ) {
1007 advadsGamDebugLink.className = advadsGamDebugLink.className.replace( 'hidden', '' );
1008 }
1009 }
1010 // look for Google Ad Manager tags with a delay of 2 seconds
1011 setTimeout( function(){
1012 advanced_ads_ready( advadsGamShowDebugLink );
1013 }, 2000 );
1014
1015 // Function to count visible ads with unique group IDs
1016 function getAdsCount(){
1017 // Get all elements with the specified class name
1018 const adWrappers = getAdWrappers();
1019 // Initialize a count for visible ads
1020 let ads_count = 0;
1021 // Loop through each ad wrapper element
1022 for ( let i = 0; i < adWrappers.length; i++ ) {
1023 // Check if the group ID is either null or not included in the array of seen group IDs.
1024 if ( adWrappers[i].offsetHeight > 0 ) {
1025 // Increment the ad count and add the group ID to the list.
1026 ads_count++;
1027 }
1028 }
1029 // Return the total count of eligible ads
1030 return ads_count;
1031 }
1032
1033 function updateAdsCount(d){
1034 var highlightLink = d.getElementById( 'wp-admin-bar-advanced_ads_ad_health_highlight_ads' );
1035 // update ad count in health tool admin bar
1036 highlightLink.querySelector('.link').innerHTML += ' (<span class="highlighted_ads_count">' + getAdsCount() + '</span>) ';
1037
1038 // If any ads load by ajax its update count after ajax load
1039 var origOpen = XMLHttpRequest.prototype.open;
1040 XMLHttpRequest.prototype.open = function() {
1041 this.addEventListener('load', function() {
1042 if ( this.status === 200 ) {
1043 highlightLink.querySelector('.highlighted_ads_count').innerHTML = getAdsCount();
1044 }
1045 });
1046 origOpen.apply(this, arguments);
1047 };
1048 }
1049 })(document, window);
1050 </script>
1051 <?php
1052 // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.EnqueuedResources.NonEnqueuedScript
1053 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1054 echo Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
1055 }
1056
1057 /**
1058 * Inject JS after ad content.
1059 *
1060 * @param string $content Ad content.
1061 * @param Ad $ad Ad instance.
1062 *
1063 * @return string $content ad content
1064 */
1065 public function after_ad_output( $content, Ad $ad ) {
1066 if ( null === $ad->get_prop( 'frontend-check' ) ) {
1067 return $content;
1068 }
1069
1070 if ( advads_is_amp() ) {
1071 return $content;
1072 }
1073
1074 if ( Validation::is_ad_https( $ad ) ) {
1075 ob_start();
1076 ?>
1077 <script>advanced_ads_ready( function() {
1078 var ad_id = '<?php echo esc_html( $ad->get_id() ); ?>';
1079 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_has_http', ad_id );
1080 advanced_ads_frontend_checks.add_item_to_notices( 'ad_has_http', { append_key: ad_id, ad_id: ad_id } );
1081 });</script>
1082 <?php
1083 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
1084 }
1085
1086 if ( ! Advanced_Ads_Frontend_Checks::can_use_head_placement( $content, $ad ) ) {
1087 ob_start();
1088 ?>
1089 <script>advanced_ads_ready( function() {
1090 var ad_id = '<?php echo esc_html( $ad->get_id() ); ?>';
1091 advanced_ads_frontend_checks.add_item_to_node( '.advanced_ads_ad_health_incorrect_head', ad_id );
1092 advanced_ads_frontend_checks.add_item_to_notices( 'ad_with_output_in_head', { append_key: ad_id, ad_id: ad_id } );
1093 });</script>
1094 <?php
1095 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
1096 }
1097
1098 $adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
1099 if (
1100 $ad->is_type( 'adsense' ) &&
1101 ! empty( $ad->get_prop( 'cache_busting_elementid' ) ) &&
1102 ! isset( $adsense_options['violation-warnings-disable'] )
1103 ) {
1104 ob_start();
1105 ?>
1106 <script>advanced_ads_ready( function() {
1107 var ad_id = '<?php echo esc_html( $ad->get_id() ); ?>';
1108 var wrapper = '#<?php echo esc_html( $ad->get_prop( 'cache_busting_elementid' ) ); ?>';
1109 advanced_ads_frontend_checks.advads_highlight_hidden_adsense( wrapper );
1110 });</script>
1111 <?php
1112 $content .= Advanced_Ads_Utils::get_inline_asset( ob_get_clean() );
1113 }
1114
1115 return $content;
1116 }
1117
1118
1119 /**
1120 * Check if the 'Header Code' placement can be used to delived the ad.
1121 *
1122 * @param string $content Ad content.
1123 * @param Ad $ad Ad.
1124 *
1125 * @return bool
1126 */
1127 public static function can_use_head_placement( $content, Ad $ad ) {
1128
1129 if ( ! $ad->is_head_placement() ) {
1130 return true;
1131 }
1132
1133 // strip linebreaks, because, a line break after a comment is identified as a text node.
1134 $content = preg_replace( "/\r|\n/", '', $content );
1135
1136 if ( ! $dom = self::get_ad_dom( $content ) ) {
1137 return true;
1138 }
1139
1140 $body = $dom->getElementsByTagName( 'body' )->item( 0 );
1141
1142 // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase, WordPress.PHP.StrictInArray.MissingTrueStrict
1143 $count = $body->childNodes->length;
1144 for ( $i = 0; $i < $count; $i++ ) {
1145 $node = $body->childNodes->item( $i );
1146
1147 if ( XML_TEXT_NODE === $node->nodeType ) {
1148 return false;
1149 }
1150
1151 if ( XML_ELEMENT_NODE === $node->nodeType
1152 && ! in_array( $node->nodeName, [ 'meta', 'link', 'title', 'style', 'script', 'noscript', 'base' ] ) ) {
1153 return false;
1154 }
1155 }
1156 // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase, WordPress.PHP.StrictInArray.MissingTrueStrict
1157 return true;
1158 }
1159
1160 /**
1161 * Convert ad content to a DOMDocument.
1162 *
1163 * @param string $content The ad content.
1164 * @return DOMDocument|false
1165 */
1166 private static function get_ad_dom( $content ) {
1167 if ( ! extension_loaded( 'dom' ) ) {
1168 return false;
1169 }
1170 $libxml_previous_state = libxml_use_internal_errors( true );
1171 $dom = new DOMDocument();
1172 $result = $dom->loadHTML( '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $content . '</body></html>' );
1173
1174 libxml_clear_errors();
1175 libxml_use_internal_errors( $libxml_previous_state );
1176
1177 if ( ! $result ) {
1178 return false;
1179 }
1180
1181 return $dom;
1182 }
1183
1184 /**
1185 * Check if at least one placement uses `the_content`.
1186 *
1187 * @return bool True/False.
1188 */
1189 private function has_the_content_placements() {
1190 $placements = wp_advads_get_placements();
1191
1192 // Find a placement that depends on 'the_content' filter.
1193 foreach ( $placements as $placement ) {
1194 if ( $placement->get_type_object()->get_options()['uses_the_content'] ) {
1195 return true;
1196 }
1197 }
1198
1199 return false;
1200 }
1201
1202 /**
1203 * Check if atleast one placement uses `adblocker item`.
1204 *
1205 * @return bool True/False.
1206 */
1207 private function has_adblocker_placements() {
1208 $placements = wp_advads_get_placements();
1209 foreach ( $placements as $placement ) {
1210 if ( ! empty( $placement->get_prop( 'item_adblocker' ) ) ) {
1211 return true;
1212 }
1213 }
1214
1215 return false;
1216 }
1217
1218 /**
1219 * Check if atleast one ad uses adblocker visitor condition.
1220 *
1221 * @return bool True/False.
1222 */
1223 public function has_adblocker_visitor_condition() {
1224 $placements = wp_advads_get_placements();
1225
1226 foreach ( $placements as $placement ) {
1227 $item = $placement->get_item();
1228 if ( empty( $item ) ) {
1229 continue;
1230 }
1231
1232 $ads = [];
1233
1234 if ( 'ad' === $placement->get_item_type() ) {
1235 $ads[] = $placement->get_item_object();
1236 } elseif ( 'group' === $placement->get_item_type() ) {
1237 $item_object = $placement->get_item_object();
1238 $ads = array_merge(
1239 $ads,
1240 $item_object ? $item_object->get_ads() : []
1241 );
1242 }
1243
1244 foreach ( $ads as $ad ) {
1245 if ( ! $ad ) {
1246 continue;
1247 }
1248 $options = $ad->get_visitor_conditions() ?? [];
1249 foreach ( $options as $option ) {
1250 if ( 'adblocker' === $option['type'] ) {
1251 return true;
1252 }
1253 }
1254 }
1255 }
1256
1257 return false;
1258 }
1259 }
1260