PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.0.3
Jetpack – WP Security, Backup, Speed, & Growth v15.0.3
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 1 year ago
class-wordads.php
1066 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 $snippet = '';
667
668 if ( 'top' === $spot ) {
669 // mrec for mobile, leaderboard for desktop.
670 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '2';
671 $form_factor = $this->params->mobile_device ? 'square' : 'leaderboard';
672 $snippet = $this->get_dynamic_ad_snippet( $section_id, $form_factor, $spot );
673 } elseif ( 'belowpost' === $spot ) {
674 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '1';
675 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
676 } elseif ( 'inline' === $spot ) {
677 $section_id = 0 === $this->params->blog_id ? WORDADS_API_TEST_ID : $this->params->blog_id . '5';
678 $snippet = $this->get_dynamic_ad_snippet( $section_id, 'square', $spot );
679 } elseif ( 'top_amp' === $spot ) {
680 // Ad unit which can safely be inserted below title, above content in a variety of themes.
681 $width = 300;
682 $height = 250;
683 $snippet = $this->get_ad_div( $spot, $this->get_amp_snippet( $height, $width ) );
684 }
685 } elseif ( 'house' === $type ) {
686 $leaderboard = 'top' === $spot && ! $this->params->mobile_device;
687 $snippet = $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
688 if ( 'belowpost' === $spot && $this->option( 'wordads_second_belowpost', true ) ) {
689 $snippet .= $this->get_house_ad( $leaderboard ? 'leaderboard' : 'mrec' );
690 }
691 }
692
693 return $snippet;
694 }
695
696 /**
697 * Returns the AMP snippet to be inserted
698 *
699 * @param int $height Height.
700 * @param int $width Width.
701 * @return string
702 *
703 * @since 8.7
704 */
705 public function get_amp_snippet( $height, $width ) {
706 $height = esc_attr( $height + 15 ); // this will ensure enough padding for "Report this ad".
707 $width = esc_attr( $width );
708 $amp_section_id = esc_attr( self::get_amp_section_id() );
709 $site_id = esc_attr( $this->params->blog_id );
710 return <<<HTML
711 <amp-ad width="$width" height="$height"
712 type="pubmine"
713 data-siteid="$site_id"
714 data-section="$amp_section_id">
715 </amp-ad>
716 HTML;
717 }
718
719 /**
720 * Compatibility function -- main functionality replaced with get_dynamic_ad_snippet
721 *
722 * @param int $section_id Ad section.
723 * @param int $height Ad height.
724 * @param int $width Ad width.
725 * @param string $location Location.
726 * @param string $css CSS.
727 *
728 * @return string
729 *
730 * @since 5.7
731 */
732 public function get_ad_snippet( $section_id, $height, $width, $location = '', $css = '' ) {
733 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
734 return $this->get_amp_snippet( $height, $width );
735 }
736
737 $this->ads[] = array(
738 'location' => $location,
739 'width' => $width,
740 'height' => $height,
741 );
742
743 if ( 'gutenberg' === $location ) {
744 $ad_number = count( $this->ads ) . '-' . uniqid();
745 $data_tags = $this->params->cloudflare ? ' data-cfasync="false"' : '';
746 $css = esc_attr( $css );
747
748 $loc_id = 100;
749 if ( ! empty( self::$ad_location_ids[ $location ] ) ) {
750 $loc_id = self::$ad_location_ids[ $location ];
751 }
752
753 $loc_id = esc_js( $loc_id );
754
755 // Determine supported Gutenberg format by width and height.
756 $format = WordAds_Formats::get_format_slug( (int) $width, (int) $height );
757 if ( $format === '' ) {
758 return ''; // When format is not supported, ignore placement.
759 }
760
761 // For a while return elements for both IPONWEB (__ATA) and Aditude (tudeMappings).
762 // We will remove using IPONWEB after partnership termination on June 30, 2025.
763 return <<<HTML
764 <div style="padding-bottom:15px;width:{$width}px;height:{$height}px;$css">
765 <div id="atatags-{$ad_number}">
766 <script$data_tags type="text/javascript">
767 window.getAdSnippetCallback = function () {
768 if ( true === ( window.isWatlV1 ?? false ) ) {
769 // Use IPONWEB scripts.
770 window.__ATA = window.__ATA || {};
771 window.__ATA.cmd = __ATA.cmd || [];
772 window.__ATA.cmd.push(function() {
773 window.__ATA.initSlot('atatags-{$ad_number}', {
774 collapseEmpty: 'before',
775 sectionId: '{$section_id}',
776 location: {$loc_id},
777 width: {$width},
778 height: {$height}
779 });
780 });
781 } else {
782 // Use Aditude scripts.
783 window.tudeMappings = window.tudeMappings || [];
784 window.tudeMappings.push( {
785 divId: 'atatags-{$ad_number}',
786 format: '{$format}',
787 width: {$width},
788 height: {$height},
789 } );
790 }
791 }
792
793 if ( document.readyState === 'loading' ) {
794 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
795 } else {
796 window.getAdSnippetCallback();
797 }
798 </script>
799 </div>
800 </div>
801 HTML;
802 }
803
804 $form_factor = 'square';
805 if ( 250 > $width ) {
806 $form_factor = 'skyscraper';
807 } elseif ( 300 < $width ) {
808 $form_factor = 'leaderboard';
809 }
810
811 return $this->get_dynamic_ad_snippet( $section_id, $form_factor, $location );
812 }
813
814 /**
815 * Returns the dynamic snippet to be inserted into the ad unit
816 *
817 * @param int $section_id section_id.
818 * @param string $form_factor form_factor.
819 * @param string $location location.
820 * @param string $relocate location to be moved after the fact for themes without required hook.
821 * @param string | null $id A unique string ID or placeholder.
822 *
823 * @return string
824 *
825 * @since 8.7
826 */
827 public function get_dynamic_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
828 $is_location_enabled = in_array( $location, array( 'top', 'belowpost', 'inline' ), true );
829
830 if ( $is_location_enabled ) {
831 return self::get_watl_ad_html_tag( $location );
832 }
833
834 return $this->get_fallback_ad_snippet( $section_id, $form_factor, $location, $relocate, $id );
835 }
836
837 /**
838 * Returns the fallback dynamic snippet to be inserted into the ad unit
839 *
840 * @param int $section_id section_id.
841 * @param string $form_factor form_factor.
842 * @param string $location location.
843 * @param string $relocate location to be moved after the fact for themes without required hook.
844 * @param string | null $id A unique string ID or placeholder.
845 *
846 * @return string
847 *
848 * @since 8.7
849 */
850 public function get_fallback_ad_snippet( $section_id, $form_factor = 'square', $location = '', $relocate = '', $id = null ) {
851 $div_id = 'atatags-' . $section_id . '-' . ( $id ?? uniqid() );
852 $div_id = esc_attr( $div_id );
853
854 // Default form factor.
855 $form_factor_id = self::$form_factor_ids['square'];
856 if ( isset( self::$form_factor_ids[ $form_factor ] ) ) {
857 $form_factor_id = self::$form_factor_ids[ $form_factor ];
858 }
859
860 $loc_id = 100;
861 if ( isset( self::$ad_location_ids[ $location ] ) ) {
862 $loc_id = self::$ad_location_ids[ $location ];
863 }
864
865 $form_factor_id = esc_js( $form_factor_id );
866 $advertisements_text = esc_js( __( 'Advertisements', 'jetpack' ) );
867 $report_ad_text = esc_js( __( 'Report this ad', 'jetpack' ) );
868 $privacy_settings_text = esc_js( __( 'Privacy settings', 'jetpack' ) );
869
870 $relocate_script = '';
871 if ( ! empty( $relocate ) ) {
872 $selector = wp_json_encode( $relocate );
873 $relocate_script = <<<JS
874 <script type="text/javascript">
875 var adNode = document.getElementById( '{$div_id}' );
876 var selector = {$selector};
877 var relocateNode = document.querySelector( selector );
878 relocateNode.parentNode.insertBefore( adNode, relocateNode );
879 </script>
880 JS;
881 }
882
883 return <<<HTML
884 <div id="{$div_id}"></div>
885 {$relocate_script}
886 <script>
887 __ATA.cmd.push(function() {
888 __ATA.initDynamicSlot({
889 id: '{$div_id}',
890 location: {$loc_id},
891 formFactor: '{$form_factor_id}',
892 label: {
893 text: '{$advertisements_text}',
894 },
895 creative: {
896 reportAd: {
897 text: '{$report_ad_text}',
898 },
899 privacySettings: {
900 text: '{$privacy_settings_text}',
901 onClick: function() { window.__tcfapi && window.__tcfapi('showUi'); },
902 }
903 }
904 });
905 });
906 </script>
907 HTML;
908 }
909
910 /**
911 * Returns the complete ad div with snippet to be inserted into the page
912 *
913 * @param string $spot top, side, inline, or belowpost.
914 * @param string $snippet The snippet to insert into the div.
915 * @param array $css_classes CSS classes.
916 * @return string The supporting ad unit div.
917 *
918 * @since 7.1
919 */
920 public function get_ad_div( $spot, $snippet, array $css_classes = array() ) {
921 if ( strpos( strtolower( $spot ), 'amp' ) === false && ! 'inline' === $spot ) {
922 return $snippet; // we don't want dynamic ads to be inserted for AMP & Gutenberg.
923 }
924
925 if ( empty( $css_classes ) ) {
926 $css_classes = array();
927 }
928
929 $css_classes[] = 'wpcnt';
930 if ( 'top' === $spot ) {
931 $css_classes[] = 'wpcnt-header';
932 }
933
934 $spot = esc_attr( $spot );
935 $classes = esc_attr( implode( ' ', $css_classes ) );
936 $about = esc_html__( 'Advertisements', 'jetpack' );
937 return <<<HTML
938 <div class="$classes">
939 <div class="wpa">
940 <span class="wpa-about">$about</span>
941 <div class="u $spot">
942 $snippet
943 </div>
944 </div>
945 </div>
946 HTML;
947 }
948
949 /**
950 * Check the reasons to bail before we attempt to insert ads.
951 *
952 * @return true if we should bail (don't insert ads)
953 *
954 * @since 4.5.0
955 */
956 public function should_bail() {
957 return ! $this->option( 'wordads_approved' ) || (bool) $this->option( 'wordads_unsafe' );
958 }
959
960 /**
961 * Returns markup for HTML5 house ad base on unit
962 *
963 * @param string $unit mrec, widesky, or leaderboard.
964 * @return string markup for HTML5 house ad
965 *
966 * @since 4.7.0
967 */
968 public function get_house_ad( $unit = 'mrec' ) {
969
970 switch ( $unit ) {
971 case 'widesky':
972 $width = 160;
973 $height = 600;
974 break;
975 case 'leaderboard':
976 $width = 728;
977 $height = 90;
978 break;
979 case 'mrec':
980 default:
981 $width = 300;
982 $height = 250;
983 break;
984 }
985
986 return <<<HTML
987 <iframe
988 src="https://s0.wp.com/wp-content/blog-plugins/wordads/house/html5/$unit/index.html"
989 width="$width"
990 height="$height"
991 frameborder="0"
992 scrolling="no"
993 marginheight="0"
994 marginwidth="0">
995 </iframe>
996 HTML;
997 }
998
999 /**
1000 * Returns the html ad tag used by WordAds Tag Library
1001 *
1002 * @param string $slot_type e.g belowpost, gutenberg_rectangle.
1003 *
1004 * @return string
1005 *
1006 * @since 8.7
1007 */
1008 public static function get_watl_ad_html_tag( string $slot_type ): string {
1009 $uid = uniqid( 'atatags-dynamic-' . $slot_type . '-' );
1010
1011 return <<<HTML
1012 <div style="padding-bottom:15px;" class="wordads-tag" data-slot-type="{$slot_type}">
1013 <div id="{$uid}">
1014 <script type="text/javascript">
1015 window.getAdSnippetCallback = function () {
1016 if ( false === ( window.isWatlV1 ?? false ) ) {
1017 // Use Aditude scripts.
1018 window.tudeMappings = window.tudeMappings || [];
1019 window.tudeMappings.push( {
1020 divId: '{$uid}',
1021 format: '{$slot_type}',
1022 } );
1023 }
1024 }
1025
1026 if ( document.readyState === 'loading' ) {
1027 document.addEventListener( 'DOMContentLoaded', window.getAdSnippetCallback );
1028 } else {
1029 window.getAdSnippetCallback();
1030 }
1031 </script>
1032 </div>
1033 </div>
1034 HTML;
1035 }
1036
1037 /**
1038 * Activation hook actions
1039 *
1040 * @since 4.5.0
1041 */
1042 public static function activate() {
1043 WordAds_API::update_wordads_status_from_api();
1044 }
1045
1046 /**
1047 * Registers the widgets.
1048 */
1049 public function widget_callback() {
1050 register_widget( 'WordAds_Sidebar_Widget' );
1051
1052 $ccpa_enabled = get_option( 'wordads_ccpa_enabled' );
1053
1054 if ( $ccpa_enabled ) {
1055 register_widget( 'WordAds_Ccpa_Do_Not_Sell_Link_Widget' );
1056 }
1057 }
1058 }
1059
1060 add_action( 'jetpack_activate_module_wordads', array( 'WordAds', 'activate' ) );
1061 add_action( 'jetpack_activate_module_wordads', array( 'WordAds_Cron', 'activate' ) );
1062 add_action( 'jetpack_deactivate_module_wordads', array( 'WordAds_Cron', 'deactivate' ) );
1063
1064 global $wordads;
1065 $wordads = new WordAds();
1066