PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / wordads / class-wordads.php
jetpack / modules / wordads Last commit date
css 7 months ago js 7 months ago php 2 weeks ago class-wordads.php 2 months ago
class-wordads.php
1040 lines
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