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