PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.2.1
Jetpack – WP Security, Backup, Speed, & Growth v15.2.1
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 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 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 1 year ago js 1 year ago php 1 year ago class-wordads.php 9 months ago
class-wordads.php
1061 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 http_response_code( 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 // TODO: Remove this function after June 30th, 2025.
283 add_action( 'wp_head', array( $this, 'insert_head_iponweb' ), 30 );
284
285 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
286 add_filter( 'wordads_ads_txt', array( $this, 'insert_custom_adstxt' ) );
287
288 /**
289 * Filters enabling ads in `the_content` filter
290 *
291 * @see https://jetpack.com/support/ads/
292 *
293 * @module wordads
294 *
295 * @since 5.8.0
296 *
297 * @param bool True to disable ads in `the_content`
298 */
299 if ( ! apply_filters( 'wordads_content_disable', false ) ) {
300 add_filter( 'the_content', array( $this, 'insert_ad' ) );
301 }
302
303 /**
304 * Filters enabling ads in `the_excerpt` filter
305 *
306 * @see https://jetpack.com/support/ads/
307 *
308 * @module wordads
309 *
310 * @since 5.8.0
311 *
312 * @param bool True to disable ads in `the_excerpt`
313 */
314 if ( ! apply_filters( 'wordads_excerpt_disable', false ) ) {
315 add_filter( 'the_excerpt', array( $this, 'insert_ad' ) );
316 }
317
318 if ( $this->option( 'enable_header_ad', true ) ) {
319 if ( self::is_amp() ) {
320 add_filter( 'the_content', array( $this, 'insert_header_ad_amp' ) );
321 } else {
322 add_action( 'wordads_header_ad', array( $this, 'insert_header_ad' ), 100 );
323 add_action( 'wp_footer', array( $this, 'insert_header_ad' ), 100 );
324 }
325 }
326 }
327
328 /**
329 * Register desktop scripts and styles
330 *
331 * @since 4.5.0
332 */
333 public function enqueue_scripts() {
334 wp_enqueue_style(
335 'wordads',
336 WORDADS_URL . 'css/style.css',
337 array(),
338 '2015-12-18'
339 );
340 }
341
342 /**
343 * Add the IPW resource hints
344 *
345 * @since 7.9
346 *
347 * @param array $hints Domains for hinting.
348 * @param string $relation_type Resource type.
349 *
350 * @return array Domains for hinting.
351 */
352 public function resource_hints( $hints, $relation_type ) {
353 if ( 'dns-prefetch' === $relation_type ) {
354 $hints[] = '//s.pubmine.com';
355 $hints[] = '//x.bidswitch.net';
356 $hints[] = '//static.criteo.net';
357 $hints[] = '//ib.adnxs.com';
358 $hints[] = '//aax.amazon-adsystem.com';
359 $hints[] = '//bidder.criteo.com';
360 $hints[] = '//cas.criteo.com';
361 $hints[] = '//gum.criteo.com';
362 $hints[] = '//ads.pubmatic.com';
363 $hints[] = '//gads.pubmatic.com';
364 $hints[] = '//tpc.googlesyndication.com';
365 $hints[] = '//ad.doubleclick.net';
366 $hints[] = '//googleads.g.doubleclick.net';
367 $hints[] = '//www.googletagservices.com';
368 $hints[] = '//cdn.switchadhub.com';
369 $hints[] = '//delivery.g.switchadhub.com';
370 $hints[] = '//delivery.swid.switchadhub.com';
371 }
372
373 return $hints;
374 }
375
376 /**
377 * IPONWEB metadata used by the various scripts
378 */
379 public function insert_head_meta() {
380 if ( self::is_amp() ) {
381 return;
382 }
383 $hosting_type = ( new Host() )->is_woa_site() ? 1 : 2; // 1 = WPCOM, 2 = Jetpack.
384 $pagetype = (int) $this->params->get_page_type_ipw();
385 $data_tags = ( $this->params->cloudflare ) ? ' data-cfasync="false"' : '';
386 $site_id = $this->params->blog_id;
387 $consent = (int) isset( $_COOKIE['personalized-ads-consent'] );
388 $is_logged_in = is_user_logged_in() ? '1' : '0';
389
390 $disabled_slot_formats = apply_filters( 'wordads_disabled_slot_formats', array() );
391
392 if ( apply_filters( 'wordads_iponweb_bottom_sticky_ad_disable', false ) ) {
393 $disabled_slot_formats[] = 'MTS';
394 }
395
396 if ( apply_filters( 'wordads_iponweb_sidebar_sticky_right_ad_disable', false ) ) {
397 $disabled_slot_formats[] = 'DPR';
398 }
399
400 $config = array(
401 'pt' => $pagetype,
402 'ht' => $hosting_type,
403 'tn' => get_stylesheet(),
404 'uloggedin' => $is_logged_in,
405 'amp' => false,
406 'siteid' => $site_id,
407 'consent' => $consent,
408 'ad' => array(
409 'label' => array(
410 'text' => __( 'Advertisements', 'jetpack' ),
411 ),
412 'reportAd' => array(
413 'text' => __( 'Report this ad', 'jetpack' ),
414 ),
415 'privacySettings' => array(
416 'text' => __( 'Privacy', 'jetpack' ),
417 'onClick' => 'js:function() { window.__tcfapi && window.__tcfapi(\'showUi\'); }',
418 ),
419 ),
420 'disabled_slot_formats' => $disabled_slot_formats,
421 );
422 $js_config = WordAds_Array_Utils::array_to_js_object( $config );
423 ?>
424 <script<?php echo esc_attr( $data_tags ); ?> type="text/javascript">
425 var __ATA_PP = <?php echo $js_config; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
426 var __ATA = __ATA || {};
427 __ATA.cmd = __ATA.cmd || [];
428 __ATA.criteo = __ATA.criteo || {};
429 __ATA.criteo.cmd = __ATA.criteo.cmd || [];
430 </script>
431 <?php
432
433 $section_id = $this->params->blog_id . 5;
434
435 // Get below post tag.
436 $tag_belowpost = $this->get_fallback_ad_snippet( $section_id, 'square', 'belowpost', '', '{{unique_id}}' );
437
438 // Remove linebreaks and sanitize.
439 $tag_belowpost = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_belowpost ) );
440
441 // Get an inline tag with a macro as id handled on JS side to use as a fallback.
442 $tag_inline = $this->get_fallback_ad_snippet( $section_id, 'square', 'inline', '', '{{unique_id}}' );
443
444 // Remove linebreaks and sanitize.
445 $tag_inline = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_inline ) );
446
447 // Get top tag.
448 $tag_top = $this->get_fallback_ad_snippet( $section_id, 'leaderboard', 'top', '', '{{unique_id}}' );
449
450 // Remove linebreaks and sanitize.
451 $tag_top = esc_js( str_replace( array( "\n", "\t", "\r" ), '', $tag_top ) );
452
453 // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
454 echo <<<HTML
455 <script type="text/javascript">
456 window.sas_fallback = window.sas_fallback || [];
457 window.sas_fallback.push(
458 { tag: "$tag_inline", type: 'inline' },
459 { tag: "$tag_belowpost", type: 'belowpost' },
460 { tag: "$tag_top", type: 'top' }
461 );
462 </script>
463 HTML;
464 }
465
466 /**
467 * IPONWEB scripts in <head>
468 *
469 * @since 4.5.0
470 */
471 public function insert_head_iponweb() {
472 if ( self::is_amp() ) {
473 return;
474 }
475
476 $data_tags = ( $this->params->cloudflare ) ? ' data-cfasync="false"' : '';
477 ?>
478 <script<?php echo esc_attr( $data_tags ); ?> type="text/javascript">
479 function loadIPONWEB() { // TODO: Remove this after June 30th, 2025
480 (function(){var g=Date.now||function(){return+new Date};function h(a,b){a:{for(var c=a.length,d="string"==typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:"string"==typeof a?a.charAt(b):a[b]};function k(a,b,c){c=null!=c?"="+encodeURIComponent(String(c)):"";if(b+=c){c=a.indexOf("#");0>c&&(c=a.length);var d=a.indexOf("?");if(0>d||d>c){d=c;var e=""}else e=a.substring(d+1,c);a=[a.substr(0,d),e,a.substr(c)];c=a[1];a[1]=b?c?c+"&"+b:b:c;a=a[0]+(a[1]?"?"+a[1]:"")+a[2]}return a};var l=0;function m(a,b){var c=document.createElement("script");c.src=a;c.onload=function(){b&&b(void 0)};c.onerror=function(){b&&b("error")};a=document.getElementsByTagName("head");var d;a&&0!==a.length?d=a[0]:d=document.documentElement;d.appendChild(c)}function n(a){var b=void 0===b?document.cookie:b;return(b=h(b.split("; "),function(c){return-1!=c.indexOf(a+"=")}))?b.split("=")[1]:""}function p(a){return"string"==typeof a&&0<a.length}
481 function r(a,b,c){b=void 0===b?"":b;c=void 0===c?".":c;var d=[];Object.keys(a).forEach(function(e){var f=a[e],q=typeof f;"object"==q&&null!=f||"function"==q?d.push(r(f,b+e+c)):null!==f&&void 0!==f&&(e=encodeURIComponent(b+e),d.push(e+"="+encodeURIComponent(f)))});return d.filter(p).join("&")}function t(a,b){a||((window.__ATA||{}).config=b.c,m(b.url))}var u=Math.floor(1E13*Math.random()),v=window.__ATA||{};window.__ATA=v;window.__ATA.cmd=v.cmd||[];v.rid=u;v.createdAt=g();var w=window.__ATA||{},x="s.pubmine.com";
482 w&&w.serverDomain&&(x=w.serverDomain);var y="//"+x+"/conf",z=window.top===window,A=window.__ATA_PP&&window.__ATA_PP.gdpr_applies,B="boolean"===typeof A?Number(A):null,C=window.__ATA_PP||null,D=z?document.referrer?document.referrer:null:null,E=z?window.location.href:document.referrer?document.referrer:null,F,G=n("__ATA_tuuid");F=G?G:null;var H=window.innerWidth+"x"+window.innerHeight,I=n("usprivacy"),J=r({gdpr:B,pp:C,rid:u,src:D,ref:E,tuuid:F,vp:H,us_privacy:I?I:null},"",".");
483 (function(a){var b=void 0===b?"cb":b;l++;var c="callback__"+g().toString(36)+"_"+l.toString(36);a=k(a,b,c);window[c]=function(d){t(void 0,d)};m(a,function(d){d&&t(d)})})(y+"?"+J);}).call(this);
484 }
485 </script>
486 <?php
487 }
488
489 /**
490 * Insert the ad onto the page
491 *
492 * @since 4.5.0
493 *
494 * @param string $content HTML content.
495 */
496 public function insert_ad( $content ) {
497 // Don't insert ads in feeds, or for anything but the main display. (This is required for compatibility with the Publicize module).
498 if ( is_feed() || ! is_main_query() || ! in_the_loop() ) {
499 return $content;
500 }
501 /**
502 * Allow third-party tools to disable the display of in post ads.
503 *
504 * @module wordads
505 *
506 * @since 4.5.0
507 *
508 * @param bool true Should the in post unit be disabled. Default to false.
509 */
510 $disable = apply_filters( 'wordads_inpost_disable', false );
511 if ( ! $this->params->should_show() || $disable ) {
512 return $content;
513 }
514
515 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
516 return $content . $this->get_ad( 'belowpost', $ad_type );
517 }
518
519 /**
520 * Insert an inline ad into a post content
521 * Used for rendering the `wordads` shortcode.
522 *
523 * @since 6.1.0
524 *
525 * @param string $content HTML content.
526 */
527 public function insert_inline_ad( $content ) {
528 // Ad JS won't work in XML feeds.
529 if ( is_feed() ) {
530 return $content;
531 }
532 /**
533 * Allow third-party tools to disable the display of in post ads.
534 *
535 * @module wordads
536 *
537 * @since 4.5.0
538 *
539 * @param bool true Should the in post unit be disabled. Default to false.
540 */
541 $disable = apply_filters( 'wordads_inpost_disable', false );
542 if ( $disable ) {
543 return $content;
544 }
545
546 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
547 $location = 'shortcode';
548
549 // Not house ad and WATL enabled.
550 // phpcs:disable WordPress.Security.NonceVerification.Recommended
551 if ( 'house' !== $ad_type && ( isset( $_GET['wordads-logging'] ) && isset( $_GET[ $location ] ) && 'true' === $_GET[ $location ] ) ) {
552 return $content . $this->get_watl_ad_html_tag( $location );
553 }
554
555 return $content . $this->get_ad( 'inline', $ad_type );
556 }
557
558 /**
559 * Inserts ad into header
560 *
561 * @since 4.5.0
562 */
563 public function insert_header_ad() {
564 /**
565 * Allow third-party tools to disable the display of header ads.
566 *
567 * @module wordads
568 *
569 * @since 4.5.0
570 *
571 * @param bool true Should the header unit be disabled. Default to false.
572 */
573 if ( apply_filters( 'wordads_header_disable', false ) ) {
574 return;
575 }
576
577 // Prevent multiple manual ads.
578 if ( 2 <= did_action( 'wordads_header_ad' ) ) {
579 return;
580 }
581
582 // Prevent placing an automatic ad if a manual ad has already been placed.
583 if ( doing_action( 'wp_footer' ) && did_action( 'wordads_header_ad' ) ) {
584 return;
585 }
586
587 // Prevent placing a manual ad if an automatic ad has already been placed.
588 if ( doing_action( 'wordads_header_ad' ) && did_action( 'wp_footer' ) ) {
589 return;
590 }
591
592 // Default ad placement to just after the <body> tag.
593 $selector = 'body > :first-child';
594
595 // Special theme cases.
596 switch ( get_stylesheet() ) {
597 case 'twentyseventeen':
598 $selector = '#content';
599 break;
600 case 'twentyfifteen':
601 $selector = '#main';
602 break;
603 case 'twentyfourteen':
604 $selector = 'article';
605 break;
606 }
607
608 // Don't relocate if the ad is being placed manually.
609 if ( doing_action( 'wordads_header_ad' ) ) {
610 $selector = '';
611 }
612
613 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '2';
614 $form_factor = $this->params->mobile_device ? 'square' : 'leaderboard';
615 echo $this->get_dynamic_ad_snippet( $section_id, $form_factor, 'top', $selector ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
616 }
617
618 /**
619 * Header unit for AMP
620 *
621 * @param string $content Content of the page.
622 *
623 * @since 7.5.0
624 */
625 public function insert_header_ad_amp( $content ) {
626
627 $ad_type = $this->option( 'wordads_house' ) ? 'house' : 'iponweb';
628 if ( 'house' === $ad_type ) {
629 return $content;
630 }
631 return $this->get_ad( 'top_amp', $ad_type ) . $content;
632 }
633
634 /**
635 * Filter the latest ads.txt to include custom user entries. Strips any tags or whitespace.
636 *
637 * @param string $adstxt The ads.txt being filtered.
638 * @return string Filtered ads.txt with custom entries, if applicable.
639 *
640 * @since 6.5.0
641 */
642 public function insert_custom_adstxt( $adstxt ) {
643 if ( ! $this->option( 'wordads_custom_adstxt_enabled' ) ) {
644 return $adstxt;
645 }
646
647 $custom_adstxt = trim( wp_strip_all_tags( $this->option( 'wordads_custom_adstxt' ) ) );
648 if ( $custom_adstxt ) {
649 $adstxt .= "\n\n#Jetpack - User Custom Entries\n";
650 $adstxt .= $custom_adstxt . "\n";
651 }
652
653 return $adstxt;
654 }
655
656 /**
657 * Get the ad for the spot and type.
658 *
659 * @param string $spot top, side, inline, or belowpost.
660 * @param string $type iponweb or adsense.
661 */
662 public function get_ad( $spot, $type = 'iponweb' ) {
663 $snippet = '';
664 if ( 'iponweb' === $type ) {
665 $section_id = WORDADS_API_TEST_ID;
666
667 if ( 'top' === $spot ) {
668 // mrec for mobile, leaderboard for desktop.
669 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '2';
670 $form_factor = $this->params->mobile_device ? 'square' : 'leaderboard';
671 $snippet = $this->get_dynamic_ad_snippet( $section_id, $form_factor, $spot );
672 } elseif ( 'belowpost' === $spot ) {
673 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '1';
674 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
675 } elseif ( 'inline' === $spot ) {
676 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '5';
677 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
678 } elseif ( 'top_amp' === $spot ) {
679 // Ad unit which can safely be inserted below title, above content in a variety of themes.
680 $width = 300;
681 $height = 250;
682 $snippet = $this->get_ad_div( $spot, $this->get_amp_snippet( $height, $width ) );
683 }
684 } elseif ( 'house' === $type ) {
685 $leaderboard = 'top' === $spot && ! $this->params->mobile_device;
686 $snippet = $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
687 if ( 'belowpost' === $spot && $this->option( 'wordads_second_belowpost', true ) ) {
688 $snippet .= $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
689 }
690 }
691
692 return $snippet;
693 }
694
695 /**
696 * Returns the AMP snippet to be inserted
697 *
698 * @param int $height Height.
699 * @param int $width Width.
700 * @return string
701 *
702 * @since 8.7
703 */
704 public function get_amp_snippet( $height, $width ) {
705 $height = esc_attr( $height + 15 ); // this will ensure enough padding for "Report this ad".
706 $width = esc_attr( $width );
707 $amp_section_id = esc_attr( self::get_amp_section_id() );
708 $site_id = esc_attr( $this->params->blog_id );
709 return <<<HTML
710 <amp-ad width="$width" height="$height"
711 type="pubmine"
712 data-siteid="$site_id"
713 data-section="$amp_section_id">
714 </amp-ad>
715 HTML;
716 }
717
718 /**
719 * Compatibility function -- main functionality replaced with get_dynamic_ad_snippet
720 *
721 * @param int $section_id Ad section.
722 * @param int $height Ad height.
723 * @param int $width Ad width.
724 * @param string $location Location.
725 * @param string $css CSS.
726 *
727 * @return string
728 *
729 * @since 5.7
730 */
731 public function get_ad_snippet( $section_id, $height, $width, $location = '', $css = '' ) {
732 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
733 return $this->get_amp_snippet( $height, $width );
734 }
735
736 $this->ads[] = array(
737 'location' => $location,
738 'width' => $width,
739 'height' => $height,
740 );
741
742 if ( 'gutenberg' === $location ) {
743 $ad_number = count( $this->ads ) . '-' . uniqid();
744 $data_tags = $this->params->cloudflare ? ' data-cfasync="false"' : '';
745 $css = esc_attr( $css );
746
747 $loc_id = 100;
748 if ( ! empty( self::$ad_location_ids[ $location ] ) ) {
749 $loc_id = self::$ad_location_ids[ $location ];
750 }
751
752 $loc_id = esc_js( $loc_id );
753
754 // Determine supported Gutenberg format by width and height.
755 $format = WordAds_Formats::get_format_slug( (int) $width, (int) $height );
756 if ( $format === '' ) {
757 return ''; // When format is not supported, ignore placement.
758 }
759
760 // For a while return elements for both IPONWEB (__ATA) and Aditude (tudeMappings).
761 // We will remove using IPONWEB after partnership termination on June 30, 2025.
762 return <<<HTML
763 <div style="padding-bottom:15px;width:{$width}px;height:{$height}px;$css">
764 <div id="atatags-{$ad_number}">
765 <script$data_tags type="text/javascript">
766 window.getAdSnippetCallback = function () {
767 if ( true === ( window.isWatlV1 ?? false ) ) {
768 // Use IPONWEB scripts.
769 window.__ATA = window.__ATA || {};
770 window.__ATA.cmd = __ATA.cmd || [];
771 window.__ATA.cmd.push(function() {
772 window.__ATA.initSlot('atatags-{$ad_number}', {
773 collapseEmpty: 'before',
774 sectionId: '{$section_id}',
775 location: {$loc_id},
776 width: {$width},
777 height: {$height}
778 });
779 });
780 } else {
781 // Use Aditude scripts.
782 window.tudeMappings = window.tudeMappings || [];
783 window.tudeMappings.push( {
784 divId: 'atatags-{$ad_number}',
785 format: '{$format}',
786 width: {$width},
787 height: {$height},
788 } );
789 }
790 }
791
792 if ( document.readyState === 'loading' ) {
793 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
794 } else {
795 window.getAdSnippetCallback();
796 }
797 </script>
798 </div>
799 </div>
800 HTML;
801 }
802
803 $form_factor = 'square';
804 if ( 250 > $width ) {
805 $form_factor = 'skyscraper';
806 } elseif ( 300 < $width ) {
807 $form_factor = 'leaderboard';
808 }
809
810 return $this->get_dynamic_ad_snippet( $section_id, $form_factor, $location );
811 }
812
813 /**
814 * Returns the dynamic snippet to be inserted into the ad unit
815 *
816 * @param int $section_id section_id.
817 * @param string $form_factor form_factor.
818 * @param string $location location.
819 * @param string $relocate location to be moved after the fact for themes without required hook.
820 * @param string | null $id A unique string ID or placeholder.
821 *
822 * @return string
823 *
824 * @since 8.7
825 */
826 public function get_dynamic_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
827 $is_location_enabled = in_array( $location, array( 'top', 'belowpost', 'inline' ), true );
828
829 if ( $is_location_enabled ) {
830 return self::get_watl_ad_html_tag( $location );
831 }
832
833 return $this->get_fallback_ad_snippet( $section_id, $form_factor, $location, $relocate, $id );
834 }
835
836 /**
837 * Returns the fallback dynamic snippet to be inserted into the ad unit
838 *
839 * @param int $section_id section_id.
840 * @param string $form_factor form_factor.
841 * @param string $location location.
842 * @param string $relocate location to be moved after the fact for themes without required hook.
843 * @param string | null $id A unique string ID or placeholder.
844 *
845 * @return string
846 *
847 * @since 8.7
848 */
849 public function get_fallback_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
850 $div_id = 'atatags-' . $section_id . '-' . ( $id ?? uniqid() );
851 $div_id = esc_attr( $div_id );
852
853 // Default form factor.
854 $form_factor_id = self::$form_factor_ids['square'];
855 if ( isset( self::$form_factor_ids[ $form_factor ] ) ) {
856 $form_factor_id = self::$form_factor_ids[ $form_factor ];
857 }
858
859 $loc_id = 100;
860 if ( isset( self::$ad_location_ids[ $location ] ) ) {
861 $loc_id = self::$ad_location_ids[ $location ];
862 }
863
864 $form_factor_id = esc_js( $form_factor_id );
865 $advertisements_text = esc_js( __( 'Advertisements', 'jetpack' ) );
866 $report_ad_text = esc_js( __( 'Report this ad', 'jetpack' ) );
867 $privacy_settings_text = esc_js( __( 'Privacy settings', 'jetpack' ) );
868
869 $relocate_script = '';
870 if ( ! empty( $relocate ) ) {
871 $selector = wp_json_encode( $relocate );
872 $relocate_script = <<<JS
873 <script type="text/javascript">
874 var adNode = document.getElementById( '{$div_id}' );
875 var selector = {$selector};
876 var relocateNode = document.querySelector( selector );
877 relocateNode.parentNode.insertBefore( adNode, relocateNode );
878 </script>
879 JS;
880 }
881
882 return <<<HTML
883 <div id="{$div_id}"></div>
884 {$relocate_script}
885 <script>
886 __ATA.cmd.push(function() {
887 __ATA.initDynamicSlot({
888 id: '{$div_id}',
889 location: {$loc_id},
890 formFactor: '{$form_factor_id}',
891 label: {
892 text: '{$advertisements_text}',
893 },
894 creative: {
895 reportAd: {
896 text: '{$report_ad_text}',
897 },
898 privacySettings: {
899 text: '{$privacy_settings_text}',
900 onClick: function() { window.__tcfapi && window.__tcfapi('showUi'); },
901 }
902 }
903 });
904 });
905 </script>
906 HTML;
907 }
908
909 /**
910 * Returns the complete ad div with snippet to be inserted into the page
911 *
912 * @param string $spot top, side, inline, or belowpost.
913 * @param string $snippet The snippet to insert into the div.
914 * @param array $css_classes CSS classes.
915 * @return string The supporting ad unit div.
916 *
917 * @since 7.1
918 */
919 public function get_ad_div( $spot, $snippet, array $css_classes = array() ) {
920 if ( strpos( strtolower( $spot ), 'amp' ) === false && ! 'inline' === $spot ) {
921 return $snippet; // we don't want dynamic ads to be inserted for AMP & Gutenberg.
922 }
923
924 $css_classes[] = 'wpcnt';
925 if ( 'top' === $spot ) {
926 $css_classes[] = 'wpcnt-header';
927 }
928
929 $spot = esc_attr( $spot );
930 $classes = esc_attr( implode( ' ', $css_classes ) );
931 $about = esc_html__( 'Advertisements', 'jetpack' );
932 return <<<HTML
933 <div class="$classes">
934 <div class="wpa">
935 <span class="wpa-about">$about</span>
936 <div class="u $spot">
937 $snippet
938 </div>
939 </div>
940 </div>
941 HTML;
942 }
943
944 /**
945 * Check the reasons to bail before we attempt to insert ads.
946 *
947 * @return true if we should bail (don't insert ads)
948 *
949 * @since 4.5.0
950 */
951 public function should_bail() {
952 return ! $this->option( 'wordads_approved' ) || (bool) $this->option( 'wordads_unsafe' );
953 }
954
955 /**
956 * Returns markup for HTML5 house ad base on unit
957 *
958 * @param string $unit mrec, widesky, or leaderboard.
959 * @return string markup for HTML5 house ad
960 *
961 * @since 4.7.0
962 */
963 public function get_house_ad( $unit = 'mrec' ) {
964
965 switch ( $unit ) {
966 case 'widesky':
967 $width = 160;
968 $height = 600;
969 break;
970 case 'leaderboard':
971 $width = 728;
972 $height = 90;
973 break;
974 case 'mrec':
975 default:
976 $width = 300;
977 $height = 250;
978 break;
979 }
980
981 return <<<HTML
982 <iframe
983 src="https://s0.wp.com/wp-content/blog-plugins/wordads/house/html5/$unit/index.html"
984 width="$width"
985 height="$height"
986 frameborder="0"
987 scrolling="no"
988 marginheight="0"
989 marginwidth="0">
990 </iframe>
991 HTML;
992 }
993
994 /**
995 * Returns the html ad tag used by WordAds Tag Library
996 *
997 * @param string $slot_type e.g belowpost, gutenberg_rectangle.
998 *
999 * @return string
1000 *
1001 * @since 8.7
1002 */
1003 public static function get_watl_ad_html_tag( string $slot_type ): string {
1004 $uid = uniqid( 'atatags-dynamic-' . $slot_type . '-' );
1005
1006 return <<<HTML
1007 <div style="padding-bottom:15px;" class="wordads-tag" data-slot-type="{$slot_type}">
1008 <div id="{$uid}">
1009 <script type="text/javascript">
1010 window.getAdSnippetCallback = function () {
1011 if ( false === ( window.isWatlV1 ?? false ) ) {
1012 // Use Aditude scripts.
1013 window.tudeMappings = window.tudeMappings || [];
1014 window.tudeMappings.push( {
1015 divId: '{$uid}',
1016 format: '{$slot_type}',
1017 } );
1018 }
1019 }
1020
1021 if ( document.readyState === 'loading' ) {
1022 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
1023 } else {
1024 window.getAdSnippetCallback();
1025 }
1026 </script>
1027 </div>
1028 </div>
1029 HTML;
1030 }
1031
1032 /**
1033 * Activation hook actions
1034 *
1035 * @since 4.5.0
1036 */
1037 public static function activate() {
1038 WordAds_API::update_wordads_status_from_api();
1039 }
1040
1041 /**
1042 * Registers the widgets.
1043 */
1044 public function widget_callback() {
1045 register_widget( 'WordAds_Sidebar_Widget' );
1046
1047 $ccpa_enabled = get_option( 'wordads_ccpa_enabled' );
1048
1049 if ( $ccpa_enabled ) {
1050 register_widget( 'WordAds_Ccpa_Do_Not_Sell_Link_Widget' );
1051 }
1052 }
1053 }
1054
1055 add_action( 'jetpack_activate_module_wordads', array( 'WordAds', 'activate' ) );
1056 add_action( 'jetpack_activate_module_wordads', array( 'WordAds_Cron', 'activate' ) );
1057 add_action( 'jetpack_deactivate_module_wordads', array( 'WordAds_Cron', 'deactivate' ) );
1058
1059 global $wordads;
1060 $wordads = new WordAds();
1061