PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / modules / wordads / class-wordads.php

class-wordads.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at modules/wordads/class-wordads.php

1,040 lines 28.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main WordAds file.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Status\Host;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit( 0 );
12 }
13
14 define( 'WORDADS_ROOT', __DIR__ );
15 define( 'WORDADS_BASENAME', plugin_basename( __FILE__ ) );
16 define( 'WORDADS_FILE_PATH', WORDADS_ROOT . '/' . basename( __FILE__ ) );
17 define( 'WORDADS_URL', plugins_url( '/', __FILE__ ) );
18 define( 'WORDADS_API_TEST_ID', '26942' );
19 define( 'WORDADS_API_TEST_ID2', '114160' );
20
21 require_once WORDADS_ROOT . '/php/class-wordads-sidebar-widget.php';
22 require_once WORDADS_ROOT . '/php/class-wordads-api.php';
23 require_once WORDADS_ROOT . '/php/class-wordads-cron.php';
24 require_once WORDADS_ROOT . '/php/class-wordads-california-privacy.php';
25 require_once WORDADS_ROOT . '/php/class-wordads-ccpa-do-not-sell-link-widget.php';
26 require_once WORDADS_ROOT . '/php/class-wordads-consent-management-provider.php';
27 require_once WORDADS_ROOT . '/php/class-wordads-formats.php';
28 require_once WORDADS_ROOT . '/php/class-wordads-smart.php';
29 require_once WORDADS_ROOT . '/php/class-wordads-shortcode.php';
30
31 /**
32 * Primary WordAds class.
33 */
34 class WordAds {
35
36 /**
37 * Ads parameters.
38 *
39 * @var WordAds_Params
40 */
41 public $params = null;
42
43 /**
44 * Ads.
45 *
46 * @var array
47 */
48 public $ads = array();
49
50 /**
51 * Array of supported ad types.
52 *
53 * @var array
54 */
55 public static $ad_tag_ids = array(
56 'mrec' => array(
57 'tag' => '300x250_mediumrectangle',
58 'height' => '250',
59 'width' => '300',
60 ),
61 'leaderboard' => array(
62 'tag' => '728x90_leaderboard',
63 'height' => '90',
64 'width' => '728',
65 ),
66 'mobile_leaderboard' => array(
67 'tag' => '320x50_mobileleaderboard',
68 'height' => '50',
69 'width' => '320',
70 ),
71 'wideskyscraper' => array(
72 'tag' => '160x600_wideskyscraper',
73 'height' => '600',
74 'width' => '160',
75 ),
76 );
77
78 /**
79 * Mapping array of location slugs to placement ids
80 *
81 * @var array
82 */
83 public static $ad_location_ids = array(
84 'top' => 110,
85 'belowpost' => 120,
86 'belowpost2' => 130,
87 'sidebar' => 140,
88 'widget' => 150,
89 'gutenberg' => 200,
90 'inline' => 310,
91 'inline-plugin' => 320,
92 );
93
94 /**
95 * Mapping array of form factor slugs to form factor ids
96 *
97 * @var array
98 */
99 public static $form_factor_ids = array(
100 'square' => '001', // 250x250
101 'leaderboard' => '002', // 728x90
102 'skyscraper' => '003', // 120x600
103 );
104
105 /**
106 * Counter to enable unique, sequential section IDs for all amp-ad units
107 *
108 * @var int
109 */
110 public static $amp_section_id = 1;
111
112 /**
113 * Solo unit CSS string.
114 *
115 * @var string
116 */
117 public static $solo_unit_css = 'float:left;margin-right:5px;margin-top:0;';
118
119 /**
120 * Checks for AMP support and returns true iff active & AMP request
121 *
122 * @return boolean True if supported AMP request
123 *
124 * @since 7.5.0
125 */
126 public static function is_amp() {
127 return class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
128 }
129
130 /**
131 * Increment the AMP section ID and return the value
132 *
133 * @return int
134 */
135 public static function get_amp_section_id() {
136 return self::$amp_section_id++;
137 }
138
139 /**
140 * Convenience function for grabbing options from params->options
141 *
142 * @param string $option the option to grab.
143 * @param mixed $default (optional).
144 * @return mixed option or $default if not set
145 *
146 * @since 4.5.0
147 */
148 public function option( $option, $default = false ) {
149 if ( ! isset( $this->params->options[ $option ] ) ) {
150 return $default;
151 }
152
153 return $this->params->options[ $option ];
154 }
155
156 /**
157 * Returns the ad tag property array for supported ad types.
158 *
159 * @return array array with ad tags
160 *
161 * @since 7.1.0
162 */
163 public function get_ad_tags() {
164 return self::$ad_tag_ids;
165 }
166
167 /**
168 * Returns the solo css for unit
169 *
170 * @return string the special css for solo units
171 *
172 * @since 7.1.0
173 */
174 public function get_solo_unit_css() {
175 return self::$solo_unit_css;
176 }
177
178 /**
179 * Instantiate the plugin
180 *
181 * @since 4.5.0
182 */
183 public function __construct() {
184 add_action( 'wp', array( $this, 'init' ) );
185 add_action( 'rest_api_init', array( $this, 'init' ) );
186 add_action( 'widgets_init', array( $this, 'widget_callback' ) );
187
188 if ( is_admin() ) {
189 WordAds_California_Privacy::init_ajax_actions();
190 WordAds_Consent_Management_Provider::init_ajax_actions();
191 }
192 }
193
194 /**
195 * Code to run on WordPress 'init' hook
196 *
197 * @since 4.5.0
198 */
199 public function init() {
200 require_once WORDADS_ROOT . '/php/class-wordads-params.php';
201 $this->params = new WordAds_Params();
202
203 if ( $this->should_bail() || self::is_infinite_scroll() ) {
204 return;
205 }
206
207 if ( is_admin() ) {
208 require_once WORDADS_ROOT . '/php/class-wordads-admin.php';
209 return;
210 }
211
212 $this->insert_adcode();
213
214 // Include California Privacy Act related features if enabled.
215 if ( $this->params->options['wordads_ccpa_enabled'] ) {
216 WordAds_California_Privacy::init();
217 }
218
219 // Initialize CMP if enabled.
220 if ( isset( $this->params->options['wordads_cmp_enabled'] ) && $this->params->options['wordads_cmp_enabled'] ) {
221 WordAds_Consent_Management_Provider::init();
222 }
223
224 // Initialize [wordads] shortcode.
225 WordAds_Shortcode::init();
226
227 // Initialize Smart.
228 WordAds_Smart::instance()->init( $this->params );
229
230 if ( ( isset( $_SERVER['REQUEST_URI'] ) && '/ads.txt' === $_SERVER['REQUEST_URI'] )
231 || ( site_url( 'ads.txt', 'relative' ) === $_SERVER['REQUEST_URI'] ) ) {
232
233 $ads_txt_transient = get_transient( 'wordads_ads_txt' );
234
235 if ( false === ( $ads_txt_transient ) ) {
236 $wordads_ads_txt = WordAds_API::get_wordads_ads_txt();
237 $ads_txt_transient = is_wp_error( $wordads_ads_txt ) ? '' : $wordads_ads_txt;
238 set_transient( 'wordads_ads_txt', $ads_txt_transient, DAY_IN_SECONDS );
239 }
240
241 /**
242 * Provide plugins a way of modifying the contents of the automatically-generated ads.txt file.
243 *
244 * @module wordads
245 *
246 * @since 6.1.0
247 *
248 * @param string WordAds_API::get_wordads_ads_txt() The contents of the ads.txt file.
249 */
250 $ads_txt_content = apply_filters( 'wordads_ads_txt', $ads_txt_transient );
251
252 status_header( 200 );
253 header( 'Content-Type: text/plain; charset=utf-8' );
254 echo esc_html( $ads_txt_content );
255 die( 0 );
256 }
257 }
258
259 /**
260 * Check for Jetpack's The_Neverending_Home_Page and use got_infinity
261 *
262 * @return boolean true if load came from infinite scroll
263 *
264 * @since 4.5.0
265 */
266 public static function is_infinite_scroll() {
267 return class_exists( 'The_Neverending_Home_Page' ) && The_Neverending_Home_Page::got_infinity();
268 }
269
270 /**
271 * Add the actions/filters to insert the ads. Checks for mobile or desktop.
272 *
273 * @since 4.5.0
274 */
275 private function insert_adcode() {
276 add_filter( 'wp_resource_hints', array( $this, 'resource_hints' ), 10, 2 );
277
278 // These 'wp_head' actions add the IPONWEB JavaScript snippets to the page.
279 // We need to remove these calls whenever migration to Aditude is complete.
280 add_action( 'wp_head', array( $this, 'insert_head_meta' ), 20 );
281
282 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
283 add_filter( 'wordads_ads_txt', array( $this, 'insert_custom_adstxt' ) );
284
285 /**
286 * Filters enabling ads in `the_content` filter
287 *
288 * @see https://jetpack.com/support/ads/
289 *
290 * @module wordads
291 *
292 * @since 5.8.0
293 *
294 * @param bool True to disable ads in `the_content`
295 */
296 if ( ! apply_filters( 'wordads_content_disable', false ) ) {
297 add_filter( 'the_content', array( $this, 'insert_ad' ) );
298 }
299
300 /**
301 * Filters enabling ads in `the_excerpt` filter
302 *
303 * @see https://jetpack.com/support/ads/
304 *
305 * @module wordads
306 *
307 * @since 5.8.0
308 *
309 * @param bool True to disable ads in `the_excerpt`
310 */
311 if ( ! apply_filters( 'wordads_excerpt_disable', false ) ) {
312 add_filter( 'the_excerpt', array( $this, 'insert_ad' ) );
313 }
314
315 if ( $this->option( 'enable_header_ad', true ) ) {
316 if ( self::is_amp() ) {
317 add_filter( 'the_content', array( $this, 'insert_header_ad_amp' ) );
318 } else {
319 add_action( 'wordads_header_ad', array( $this, 'insert_header_ad' ), 100 );
320 add_action( 'wp_footer', array( $this, 'insert_header_ad' ), 100 );
321 }
322 }
323 }
324
325 /**
326 * Register desktop scripts and styles
327 *
328 * @since 4.5.0
329 */
330 public function enqueue_scripts() {
331 // Ads are never shown on 404 pages.
332 if ( is_404() ) {
333 return;
334 }
335
336 wp_enqueue_style(
337 'wordads',
338 WORDADS_URL . 'css/style.css',
339 array(),
340 '2015-12-18'
341 );
342 }
343
344 /**
345 * Add the IPW resource hints
346 *
347 * @since 7.9
348 *
349 * @param array $hints Domains for hinting.
350 * @param string $relation_type Resource type.
351 *
352 * @return array Domains for hinting.
353 */
354 public function resource_hints( $hints, $relation_type ) {
355 if ( 'dns-prefetch' === $relation_type && ! is_404() ) {
356 $hints[] = '//s.pubmine.com';
357 $hints[] = '//x.bidswitch.net';
358 $hints[] = '//static.criteo.net';
359 $hints[] = '//ib.adnxs.com';
360 $hints[] = '//aax.amazon-adsystem.com';
361 $hints[] = '//bidder.criteo.com';
362 $hints[] = '//cas.criteo.com';
363 $hints[] = '//gum.criteo.com';
364 $hints[] = '//ads.pubmatic.com';
365 $hints[] = '//gads.pubmatic.com';
366 $hints[] = '//tpc.googlesyndication.com';
367 $hints[] = '//ad.doubleclick.net';
368 $hints[] = '//googleads.g.doubleclick.net';
369 $hints[] = '//www.googletagservices.com';
370 $hints[] = '//cdn.switchadhub.com';
371 $hints[] = '//delivery.g.switchadhub.com';
372 $hints[] = '//delivery.swid.switchadhub.com';
373 }
374
375 return $hints;
376 }
377
378 /**
379 * IPONWEB metadata used by the various scripts
380 */
381 public function insert_head_meta() {
382 if ( self::is_amp() || is_404() ) {
383 return;
384 }
385 $hosting_type = ( new Host() )->is_woa_site() ? 1 : 2; // 1 = WPCOM, 2 = Jetpack.
386 $pagetype = (int) $this->params->get_page_type_ipw();
387 $data_tags = ( $this->params->cloudflare ) ? ' data-cfasync="false"' : '';
388 $site_id = $this->params->blog_id;
389 $consent = (int) isset( $_COOKIE['personalized-ads-consent'] );
390 $is_logged_in = is_user_logged_in() ? '1' : '0';
391
392 $disabled_slot_formats = apply_filters( 'wordads_disabled_slot_formats', array() );
393
394 if ( apply_filters( 'wordads_iponweb_bottom_sticky_ad_disable', false ) ) {
395 $disabled_slot_formats[] = 'MTS';
396 }
397
398 if ( apply_filters( 'wordads_iponweb_sidebar_sticky_right_ad_disable', false ) ) {
399 $disabled_slot_formats[] = 'DPR';
400 }
401
402 $config = array(
403 'pt' => $pagetype,
404 'ht' => $hosting_type,
405 'tn' => get_stylesheet(),
406 'uloggedin' => $is_logged_in,
407 'amp' => false,
408 'siteid' => $site_id,
409 'consent' => $consent,
410 'ad' => array(
411 'label' => array(
412 'text' => __( 'Advertisements', 'jetpack' ),
413 ),
414 'reportAd' => array(
415 'text' => __( 'Report this ad', 'jetpack' ),
416 ),
417 'privacySettings' => array(
418 'text' => __( 'Privacy', 'jetpack' ),
419 'onClick' => 'js:function() { window.__tcfapi && window.__tcfapi(\'showUi\'); }',
420 ),
421 ),
422 'disabled_slot_formats' => $disabled_slot_formats,
423 );
424 $js_config = WordAds_Array_Utils::array_to_js_object( $config );
425 ?>
426 <script<?php echo esc_attr( $data_tags ); ?> type="text/javascript">
427 var __ATA_PP = <?php echo $js_config; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
428 var __ATA = __ATA || {};
429 __ATA.cmd = __ATA.cmd || [];
430 __ATA.criteo = __ATA.criteo || {};
431 __ATA.criteo.cmd = __ATA.criteo.cmd || [];
432 </script>
433 <?php
434
435 $section_id = $this->params->blog_id . 5;
436
437 // Get below post tag.
438 $tag_belowpost = $this->get_fallback_ad_snippet( $section_id, 'square', 'belowpost', '', '{{unique_id}}' );
439
440 // Remove linebreaks and sanitize.
441 $tag_belowpost = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_belowpost ) );
442
443 // Get an inline tag with a macro as id handled on JS side to use as a fallback.
444 $tag_inline = $this->get_fallback_ad_snippet( $section_id, 'square', 'inline', '', '{{unique_id}}' );
445
446 // Remove linebreaks and sanitize.
447 $tag_inline = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_inline ) );
448
449 // Get top tag.
450 $tag_top = $this->get_fallback_ad_snippet( $section_id, 'leaderboard', 'top', '', '{{unique_id}}' );
451
452 // Remove linebreaks and sanitize.
453 $tag_top = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_top ) );
454
455 // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
456 echo <<<HTML
457 <script type="text/javascript">
458 window.sas_fallback = window.sas_fallback || [];
459 window.sas_fallback.push(
460 { tag: "$tag_inline", type: 'inline' },
461 { tag: "$tag_belowpost", type: 'belowpost' },
462 { tag: "$tag_top", type: 'top' }
463 );
464 </script>
465 HTML;
466 }
467
468 /**
469 * Insert the ad onto the page
470 *
471 * @since 4.5.0
472 *
473 * @param string $content HTML content.
474 */
475 public function insert_ad( $content ) {
476 // Don't insert ads in feeds, or for anything but the main display. (This is required for compatibility with the Publicize module).
477 if ( is_feed() || ! is_main_query() || ! in_the_loop() ) {
478 return $content;
479 }
480 /**
481 * Allow third-party tools to disable the display of in post ads.
482 *
483 * @module wordads
484 *
485 * @since 4.5.0
486 *
487 * @param bool true Should the in post unit be disabled. Default to false.
488 */
489 $disable = apply_filters( 'wordads_inpost_disable', false );
490 if ( ! $this->params->should_show() || $disable ) {
491 return $content;
492 }
493
494 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
495 return $content . $this->get_ad( 'belowpost', $ad_type );
496 }
497
498 /**
499 * Insert an inline ad into a post content
500 * Used for rendering the `wordads` shortcode.
501 *
502 * @since 6.1.0
503 *
504 * @param string $content HTML content.
505 */
506 public function insert_inline_ad( $content ) {
507 // Ad JS won't work in XML feeds.
508 if ( is_feed() ) {
509 return $content;
510 }
511 /**
512 * Allow third-party tools to disable the display of in post ads.
513 *
514 * @module wordads
515 *
516 * @since 4.5.0
517 *
518 * @param bool true Should the in post unit be disabled. Default to false.
519 */
520 $disable = apply_filters( 'wordads_inpost_disable', false );
521 if ( $disable ) {
522 return $content;
523 }
524
525 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
526 $location = 'shortcode';
527
528 // Not house ad and WATL enabled.
529 // phpcs:disable WordPress.Security.NonceVerification.Recommended
530 if ( 'house' !== $ad_type && ( isset( $_GET['wordads-logging'] ) && isset( $_GET[ $location ] ) && 'true' === $_GET[ $location ] ) ) {
531 return $content . $this->get_watl_ad_html_tag( $location );
532 }
533
534 return $content . $this->get_ad( 'inline', $ad_type );
535 }
536
537 /**
538 * Inserts ad into header
539 *
540 * @since 4.5.0
541 */
542 public function insert_header_ad() {
543 /**
544 * Allow third-party tools to disable the display of header ads.
545 *
546 * @module wordads
547 *
548 * @since 4.5.0
549 *
550 * @param bool true Should the header unit be disabled. Default to false.
551 */
552 if ( apply_filters( 'wordads_header_disable', false ) ) {
553 return;
554 }
555
556 // Prevent multiple manual ads.
557 if ( 2 <= did_action( 'wordads_header_ad' ) ) {
558 return;
559 }
560
561 // Prevent placing an automatic ad if a manual ad has already been placed.
562 if ( doing_action( 'wp_footer' ) && did_action( 'wordads_header_ad' ) ) {
563 return;
564 }
565
566 // Prevent placing a manual ad if an automatic ad has already been placed.
567 if ( doing_action( 'wordads_header_ad' ) && did_action( 'wp_footer' ) ) {
568 return;
569 }
570
571 // Default ad placement to just after the <body> tag.
572 $selector = 'body > :first-child';
573
574 // Special theme cases.
575 switch ( get_stylesheet() ) {
576 case 'twentyseventeen':
577 $selector = '#content';
578 break;
579 case 'twentyfifteen':
580 $selector = '#main';
581 break;
582 case 'twentyfourteen':
583 $selector = 'article';
584 break;
585 }
586
587 // Don't relocate if the ad is being placed manually.
588 if ( doing_action( 'wordads_header_ad' ) ) {
589 $selector = '';
590 }
591
592 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '2';
593 $form_factor = $this->params->mobile_device ? 'square' : 'leaderboard';
594 echo $this->get_dynamic_ad_snippet( $section_id, $form_factor, 'top', $selector ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
595 }
596
597 /**
598 * Header unit for AMP
599 *
600 * @param string $content Content of the page.
601 *
602 * @since 7.5.0
603 */
604 public function insert_header_ad_amp( $content ) {
605
606 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
607 if ( 'house' === $ad_type ) {
608 return $content;
609 }
610 return $this->get_ad( 'top_amp', $ad_type ) . $content;
611 }
612
613 /**
614 * Filter the latest ads.txt to include custom user entries. Strips any tags or whitespace.
615 *
616 * @param string $adstxt The ads.txt being filtered.
617 * @return string Filtered ads.txt with custom entries, if applicable.
618 *
619 * @since 6.5.0
620 */
621 public function insert_custom_adstxt( $adstxt ) {
622 if ( ! $this->option( 'wordads_custom_adstxt_enabled' ) ) {
623 return $adstxt;
624 }
625
626 $custom_adstxt = trim( wp_strip_all_tags( $this->option( 'wordads_custom_adstxt' ) ) );
627 if ( $custom_adstxt ) {
628 $adstxt .= "\n\n#Jetpack - User Custom Entries\n";
629 $adstxt .= $custom_adstxt . "\n";
630 }
631
632 return $adstxt;
633 }
634
635 /**
636 * Get the ad for the spot and type.
637 *
638 * @param string $spot top, side, inline, or belowpost.
639 * @param string $type iponweb or adsense.
640 */
641 public function get_ad( $spot, $type = 'iponweb' ) {
642 $snippet = '';
643 if ( 'iponweb' === $type ) {
644 $section_id = WORDADS_API_TEST_ID;
645
646 if ( 'top' === $spot ) {
647 // mrec for mobile, leaderboard for desktop.
648 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '2';
649 $form_factor = $this->params->mobile_device ? 'square' : 'leaderboard';
650 $snippet = $this->get_dynamic_ad_snippet( $section_id, $form_factor, $spot );
651 } elseif ( 'belowpost' === $spot ) {
652 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '1';
653 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
654 } elseif ( 'inline' === $spot ) {
655 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '5';
656 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
657 } elseif ( 'top_amp' === $spot ) {
658 // Ad unit which can safely be inserted below title, above content in a variety of themes.
659 $width = 300;
660 $height = 250;
661 $snippet = $this->get_ad_div( $spot, $this->get_amp_snippet( $height, $width ) );
662 }
663 } elseif ( 'house' === $type ) {
664 $leaderboard = 'top' === $spot && ! $this->params->mobile_device;
665 $snippet = $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
666 if ( 'belowpost' === $spot && $this->option( 'wordads_second_belowpost', true ) ) {
667 $snippet .= $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
668 }
669 }
670
671 return $snippet;
672 }
673
674 /**
675 * Returns the AMP snippet to be inserted
676 *
677 * @param int $height Height.
678 * @param int $width Width.
679 * @return string
680 *
681 * @since 8.7
682 */
683 public function get_amp_snippet( $height, $width ) {
684 $height = esc_attr( $height + 15 ); // this will ensure enough padding for "Report this ad".
685 $width = esc_attr( $width );
686 $amp_section_id = esc_attr( self::get_amp_section_id() );
687 $site_id = esc_attr( $this->params->blog_id );
688 return <<<HTML
689 <amp-ad width="$width" height="$height"
690 type="pubmine"
691 data-siteid="$site_id"
692 data-section="$amp_section_id">
693 </amp-ad>
694 HTML;
695 }
696
697 /**
698 * Compatibility function -- main functionality replaced with get_dynamic_ad_snippet
699 *
700 * @param int $section_id Ad section.
701 * @param int $height Ad height.
702 * @param int $width Ad width.
703 * @param string $location Location.
704 * @param string $css CSS.
705 *
706 * @return string
707 *
708 * @since 5.7
709 */
710 public function get_ad_snippet( $section_id, $height, $width, $location = '', $css = '' ) {
711 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
712 return $this->get_amp_snippet( $height, $width );
713 }
714
715 $this->ads[] = array(
716 'location' => $location,
717 'width' => $width,
718 'height' => $height,
719 );
720
721 if ( 'gutenberg' === $location ) {
722 $ad_number = count( $this->ads ) . '-' . uniqid();
723 $data_tags = $this->params->cloudflare ? ' data-cfasync="false"' : '';
724 $css = esc_attr( $css );
725
726 $loc_id = 100;
727 if ( ! empty( self::$ad_location_ids[ $location ] ) ) {
728 $loc_id = self::$ad_location_ids[ $location ];
729 }
730
731 $loc_id = esc_js( $loc_id );
732
733 // Determine supported Gutenberg format by width and height.
734 $format = WordAds_Formats::get_format_slug( (int) $width, (int) $height );
735 if ( $format === '' ) {
736 return ''; // When format is not supported, ignore placement.
737 }
738
739 // For a while return elements for both IPONWEB (__ATA) and Aditude (tudeMappings).
740 // We will remove using IPONWEB after partnership termination on June 30, 2025.
741 return <<<HTML
742 <div style="padding-bottom:15px;width:{$width}px;height:{$height}px;$css">
743 <div id="atatags-{$ad_number}">
744 <script$data_tags type="text/javascript">
745 window.getAdSnippetCallback = function () {
746 if ( true === ( window.isWatlV1 ?? false ) ) {
747 // Use IPONWEB scripts.
748 window.__ATA = window.__ATA || {};
749 window.__ATA.cmd = __ATA.cmd || [];
750 window.__ATA.cmd.push(function() {
751 window.__ATA.initSlot('atatags-{$ad_number}', {
752 collapseEmpty: 'before',
753 sectionId: '{$section_id}',
754 location: {$loc_id},
755 width: {$width},
756 height: {$height}
757 });
758 });
759 } else {
760 // Use Aditude scripts.
761 window.tudeMappings = window.tudeMappings || [];
762 window.tudeMappings.push( {
763 divId: 'atatags-{$ad_number}',
764 format: '{$format}',
765 width: {$width},
766 height: {$height},
767 } );
768 }
769 }
770
771 if ( document.readyState === 'loading' ) {
772 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
773 } else {
774 window.getAdSnippetCallback();
775 }
776 </script>
777 </div>
778 </div>
779 HTML;
780 }
781
782 $form_factor = 'square';
783 if ( 250 > $width ) {
784 $form_factor = 'skyscraper';
785 } elseif ( 300 < $width ) {
786 $form_factor = 'leaderboard';
787 }
788
789 return $this->get_dynamic_ad_snippet( $section_id, $form_factor, $location );
790 }
791
792 /**
793 * Returns the dynamic snippet to be inserted into the ad unit
794 *
795 * @param int $section_id section_id.
796 * @param string $form_factor form_factor.
797 * @param string $location location.
798 * @param string $relocate location to be moved after the fact for themes without required hook.
799 * @param string | null $id A unique string ID or placeholder.
800 *
801 * @return string
802 *
803 * @since 8.7
804 */
805 public function get_dynamic_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
806 $is_location_enabled = in_array( $location, array( 'top', 'belowpost', 'inline' ), true );
807
808 if ( $is_location_enabled ) {
809 return self::get_watl_ad_html_tag( $location );
810 }
811
812 return $this->get_fallback_ad_snippet( $section_id, $form_factor, $location, $relocate, $id );
813 }
814
815 /**
816 * Returns the fallback dynamic snippet to be inserted into the ad unit
817 *
818 * @param int $section_id section_id.
819 * @param string $form_factor form_factor.
820 * @param string $location location.
821 * @param string $relocate location to be moved after the fact for themes without required hook.
822 * @param string | null $id A unique string ID or placeholder.
823 *
824 * @return string
825 *
826 * @since 8.7
827 */
828 public function get_fallback_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
829 $div_id = 'atatags-' . $section_id . '-' . ( $id ?? uniqid() );
830 $div_id = esc_attr( $div_id );
831
832 // Default form factor.
833 $form_factor_id = self::$form_factor_ids['square'];
834 if ( isset( self::$form_factor_ids[ $form_factor ] ) ) {
835 $form_factor_id = self::$form_factor_ids[ $form_factor ];
836 }
837
838 $loc_id = 100;
839 if ( isset( self::$ad_location_ids[ $location ] ) ) {
840 $loc_id = self::$ad_location_ids[ $location ];
841 }
842
843 $form_factor_id = esc_js( $form_factor_id );
844 $advertisements_text = esc_js( __( 'Advertisements', 'jetpack' ) );
845 $report_ad_text = esc_js( __( 'Report this ad', 'jetpack' ) );
846 $privacy_settings_text = esc_js( __( 'Privacy settings', 'jetpack' ) );
847
848 $relocate_script = '';
849 if ( ! empty( $relocate ) ) {
850 $selector = wp_json_encode( $relocate, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
851 $relocate_script = <<<JS
852 <script type="text/javascript">
853 var adNode = document.getElementById( '{$div_id}' );
854 var selector = {$selector};
855 var relocateNode = document.querySelector( selector );
856 relocateNode.parentNode.insertBefore( adNode, relocateNode );
857 </script>
858 JS;
859 }
860
861 return <<<HTML
862 <div id="{$div_id}"></div>
863 {$relocate_script}
864 <script>
865 __ATA.cmd.push(function() {
866 __ATA.initDynamicSlot({
867 id: '{$div_id}',
868 location: {$loc_id},
869 formFactor: '{$form_factor_id}',
870 label: {
871 text: '{$advertisements_text}',
872 },
873 creative: {
874 reportAd: {
875 text: '{$report_ad_text}',
876 },
877 privacySettings: {
878 text: '{$privacy_settings_text}',
879 onClick: function() { window.__tcfapi && window.__tcfapi('showUi'); },
880 }
881 }
882 });
883 });
884 </script>
885 HTML;
886 }
887
888 /**
889 * Returns the complete ad div with snippet to be inserted into the page
890 *
891 * @param string $spot top, side, inline, or belowpost.
892 * @param string $snippet The snippet to insert into the div.
893 * @param array $css_classes CSS classes.
894 * @return string The supporting ad unit div.
895 *
896 * @since 7.1
897 */
898 public function get_ad_div( $spot, $snippet, array $css_classes = array() ) {
899 if ( strpos( strtolower( $spot ), 'amp' ) === false && ! 'inline' === $spot ) {
900 return $snippet; // we don't want dynamic ads to be inserted for AMP & Gutenberg.
901 }
902
903 $css_classes[] = 'wpcnt';
904 if ( 'top' === $spot ) {
905 $css_classes[] = 'wpcnt-header';
906 }
907
908 $spot = esc_attr( $spot );
909 $classes = esc_attr( implode( ' ', $css_classes ) );
910 $about = esc_html__( 'Advertisements', 'jetpack' );
911 return <<<HTML
912 <div class="$classes">
913 <div class="wpa">
914 <span class="wpa-about">$about</span>
915 <div class="u $spot">
916 $snippet
917 </div>
918 </div>
919 </div>
920 HTML;
921 }
922
923 /**
924 * Check the reasons to bail before we attempt to insert ads.
925 *
926 * @return true if we should bail (don't insert ads)
927 *
928 * @since 4.5.0
929 */
930 public function should_bail() {
931 return ! $this->option( 'wordads_approved' ) || (bool) $this->option( 'wordads_unsafe' );
932 }
933
934 /**
935 * Returns markup for HTML5 house ad base on unit
936 *
937 * @param string $unit mrec, widesky, or leaderboard.
938 * @return string markup for HTML5 house ad
939 *
940 * @since 4.7.0
941 */
942 public function get_house_ad( $unit = 'mrec' ) {
943
944 switch ( $unit ) {
945 case 'widesky':
946 $width = 160;
947 $height = 600;
948 break;
949 case 'leaderboard':
950 $width = 728;
951 $height = 90;
952 break;
953 case 'mrec':
954 default:
955 $width = 300;
956 $height = 250;
957 break;
958 }
959
960 return <<<HTML
961 <iframe
962 src="https://s0.wp.com/wp-content/blog-plugins/wordads/house/html5/$unit/index.html"
963 width="$width"
964 height="$height"
965 frameborder="0"
966 scrolling="no"
967 marginheight="0"
968 marginwidth="0">
969 </iframe>
970 HTML;
971 }
972
973 /**
974 * Returns the html ad tag used by WordAds Tag Library
975 *
976 * @param string $slot_type e.g belowpost, gutenberg_rectangle.
977 *
978 * @return string
979 *
980 * @since 8.7
981 */
982 public static function get_watl_ad_html_tag( string $slot_type ): string {
983 $uid = uniqid( 'atatags-dynamic-' . $slot_type . '-' );
984
985 return <<<HTML
986 <div style="padding-bottom:15px;" class="wordads-tag" data-slot-type="{$slot_type}">
987 <div id="{$uid}">
988 <script type="text/javascript">
989 window.getAdSnippetCallback = function () {
990 if ( false === ( window.isWatlV1 ?? false ) ) {
991 // Use Aditude scripts.
992 window.tudeMappings = window.tudeMappings || [];
993 window.tudeMappings.push( {
994 divId: '{$uid}',
995 format: '{$slot_type}',
996 } );
997 }
998 }
999
1000 if ( document.readyState === 'loading' ) {
1001 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
1002 } else {
1003 window.getAdSnippetCallback();
1004 }
1005 </script>
1006 </div>
1007 </div>
1008 HTML;
1009 }
1010
1011 /**
1012 * Activation hook actions
1013 *
1014 * @since 4.5.0
1015 */
1016 public static function activate() {
1017 WordAds_API::update_wordads_status_from_api();
1018 }
1019
1020 /**
1021 * Registers the widgets.
1022 */
1023 public function widget_callback() {
1024 register_widget( 'WordAds_Sidebar_Widget' );
1025
1026 $ccpa_enabled = get_option( 'wordads_ccpa_enabled' );
1027
1028 if ( $ccpa_enabled ) {
1029 register_widget( 'WordAds_Ccpa_Do_Not_Sell_Link_Widget' );
1030 }
1031 }
1032 }
1033
1034 add_action( 'jetpack_activate_module_wordads', array( 'WordAds', 'activate' ) );
1035 add_action( 'jetpack_activate_module_wordads', array( 'WordAds_Cron', 'activate' ) );
1036 add_action( 'jetpack_deactivate_module_wordads', array( 'WordAds_Cron', 'deactivate' ) );
1037
1038 global $wordads;
1039 $wordads = new WordAds();
1040