PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 1.51.3
Advanced Ads – Ad Manager & AdSense v1.51.3
2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / classes / ad.php
advanced-ads / classes Last commit date
Advanced_Ads_Modal.php 2 years ago EDD_SL_Plugin_Updater.php 2 years ago ad-ajax.php 2 years ago ad-debug.php 2 years ago ad-expiration.php 3 years ago ad-health-notices.php 2 years ago ad-model.php 2 years ago ad-select.php 3 years ago ad.php 2 years ago ad_ajax_callbacks.php 2 years ago ad_group.php 2 years ago ad_placements.php 2 years ago ad_type_abstract.php 2 years ago ad_type_content.php 2 years ago ad_type_dummy.php 2 years ago ad_type_group.php 2 years ago ad_type_image.php 2 years ago ad_type_plain.php 2 years ago checks.php 2 years ago class-translation-promo.php 2 years ago compatibility.php 2 years ago display-conditions.php 2 years ago filesystem.php 2 years ago frontend_checks.php 2 years ago in-content-injector.php 2 years ago inline-css.php 2 years ago plugin.php 2 years ago upgrades.php 2 years ago utils.php 3 years ago visitor-conditions.php 2 years ago widget.php 2 years ago
ad.php
1071 lines
1 <?php
2 /**
3 * Advanced Ads Ad.
4 *
5 * @package Advanced_Ads_Ad
6 * @author Thomas Maier <support@wpadvancedads.com>
7 * @license GPL-2.0+
8 * @link https://wpadvancedads.com
9 * @copyright 2013-2020 Thomas Maier, Advanced Ads GmbH
10 */
11
12 use AdvancedAds\Entities;
13 use AdvancedAds\Utilities\WordPress;
14
15 /**
16 * An ad object
17 *
18 * @package Advanced_Ads_Ad
19 * @author Thomas Maier <support@wpadvancedads.com>
20 * @deprecated since version 1.5.3 (May 6th 2015)
21 * might still be needed if some old add-ons are running somewhere
22 */
23 if ( ! class_exists( 'Advads_Ad', false ) ) {
24 class Advads_Ad extends Advanced_Ads_Ad {
25
26 }
27 }
28
29 /**
30 * An ad object
31 *
32 * @package Advanced_Ads_Ad
33 * @author Thomas Maier <support@wpadvancedads.com>
34 */
35 class Advanced_Ads_Ad {
36
37 /**
38 * Id of the post type for this ad
39 *
40 * @var int $id
41 */
42 public $id = 0;
43
44 /**
45 * True, if this is an Advanced Ads Ad post type
46 *
47 * @var bool $is_ad
48 */
49 public $is_ad = false;
50
51 /**
52 * Ad type
53 *
54 * @var string $type ad type.
55 */
56 public $type = 'content';
57
58 /**
59 * Notes about the ad usage
60 *
61 * @var string $description
62 */
63 public $description = '';
64
65 /**
66 * Ad width
67 *
68 * @var int $width width of the ad.
69 */
70 public $width = 0;
71
72 /**
73 * Target url
74 *
75 * @var string $url ad URL parameter.
76 */
77 public $url = '';
78
79 /**
80 * Ad height
81 *
82 * @var int $height height of the ad.
83 */
84 public $height = 0;
85
86 /**
87 * Object of current ad type
88 *
89 * @var Advanced_Ads_Ad_Type_Abstract $type_obj object of the current ad type.
90 */
91 protected $type_obj;
92
93 /**
94 * Content of the ad
95 *
96 * Only needed for ad types using the post content field
97 *
98 * @var string $content content of the ad.
99 */
100 public $content = '';
101
102 /**
103 * Conditions of the ad display
104 *
105 * @var array $conditions display and visitor conditions.
106 */
107 public $conditions = [];
108
109 /**
110 * Status of the ad (e.g. publish, pending)
111 *
112 * @var string $status status of the ad.
113 */
114 public $status = '';
115
116 /**
117 * Array with meta field options aka parameters
118 *
119 * @var array $options ad options.
120 */
121 protected $options;
122
123 /**
124 * Name of the meta field to save options to
125 *
126 * @var string $options_meta_field under which post meta key the ad options are stored.
127 */
128 public static $options_meta_field = 'advanced_ads_ad_options';
129
130 /**
131 * Additional arguments set when ad is loaded, overwrites or extends options
132 *
133 * @var array $args
134 */
135 public $args = [];
136
137 /**
138 * Multidimensional array contains information about the wrapper
139 * Each possible html attribute is an array with possible multiple elements
140 *
141 * @var array $wrapper options of the ad wrapper.
142 */
143 public $wrapper = [];
144
145 /**
146 * Will the ad be tracked?
147 *
148 * @var mixed $global_output
149 */
150 public $global_output;
151
152 /**
153 * Title of the ad
154 *
155 * @var string $title
156 */
157 public $title = '';
158
159 /**
160 * Displayed above the ad.
161 *
162 * @var string $label ad label.
163 */
164 protected $label = '';
165
166 /**
167 * Inline CSS object, one instance per ad.
168 *
169 * @var Advanced_Ads_Inline_Css
170 */
171 private $inline_css;
172 /**
173 * Timestamp if ad has an expiration date.
174 *
175 * @var int
176 */
177 public $expiry_date = 0;
178
179 /**
180 * The ad expiration object.
181 *
182 * @var Advanced_Ads_Ad_Expiration
183 */
184 private $ad_expiration;
185
186 /**
187 * The saved output options.
188 *
189 * @var array
190 */
191 public $output;
192
193 /**
194 * Whether the current ad is in a head placement.
195 *
196 * @var bool
197 */
198 public $is_head_placement;
199
200 /**
201 * Advanced_Ads_Ad constructor.
202 *
203 * @param int $id Ad ID.
204 * @param iterable $args Additional arguments.
205 */
206 public function __construct( int $id, iterable $args = [] ) {
207 if ( empty( $id ) ) {
208 return;
209 }
210
211 $this->id = $id;
212 $this->args = is_array( $args ) ? $args : [];
213
214 $post_data = get_post( $id );
215 if ( $post_data === null || $post_data->post_type !== Entities::POST_TYPE_AD ) {
216 return;
217 }
218
219 $this->is_ad = true;
220 $this->type = $this->options( 'type' );
221 $this->title = $post_data->post_title;
222 $this->type_obj = Advanced_Ads::get_instance()->ad_types[ $this->type ] ?? new Advanced_Ads_Ad_Type_Abstract();
223
224 // filter the positioning options.
225 new Advanced_Ads_Ad_Positioning( $this );
226
227 $this->url = $this->get_url();
228 $this->width = absint( $this->options( 'width' ) );
229 $this->height = absint( $this->options( 'height' ) );
230 $this->conditions = $this->options( 'conditions' );
231 $this->description = $this->options( 'description' );
232 $this->output = $this->options( 'output' );
233 $this->status = $post_data->post_status;
234 $this->expiry_date = (int) $this->options( 'expiry_date' );
235 $this->is_head_placement = isset( $this->args['placement_type'] ) && 'header' === $this->args['placement_type'];
236 $this->args['is_top_level'] = ! isset( $this->args['is_top_level'] );
237
238 // load content based on ad type.
239 $this->content = $this->type_obj->load_content( $post_data );
240
241 if ( ! $this->is_head_placement ) {
242 $this->maybe_create_label();
243 $this->wrapper = $this->load_wrapper_options();
244
245 // set wrapper conditions.
246 $this->wrapper = apply_filters( 'advanced-ads-set-wrapper', $this->wrapper, $this );
247 // add unique wrapper id.
248 if ( is_array( $this->wrapper )
249 && [] !== $this->wrapper
250 && ! isset( $this->wrapper['id'] ) ) {
251 // create unique id if not yet given.
252 $this->wrapper['id'] = $this->create_wrapper_id();
253 }
254 }
255
256 $this->ad_expiration = new Advanced_Ads_Ad_Expiration( $this );
257
258 // dynamically add sanitize filters for condition types.
259 $condition_types = array_unique( array_column( Advanced_Ads::get_instance()->get_model()->get_ad_conditions(), 'type' ) );
260 foreach ( $condition_types as $condition_type ) {
261 $method_name = 'sanitize_condition_' . $condition_type;
262 if ( method_exists( $this, $method_name ) ) {
263 add_filter( 'advanced-ads-sanitize-condition-' . $condition_type, [ $this, $method_name ], 10, 1 );
264 } elseif ( function_exists( 'advads_sanitize_condition_' . $condition_type ) ) {
265 // check for public function to sanitize this.
266 add_filter( 'advanced-ads-sanitize-condition-' . $condition_type, 'advads_sanitize_condition_' . $condition_type, 10, 1 );
267 }
268 }
269
270 // whether the ad will be tracked.
271 $this->global_output = ! isset( $this->args['global_output'] ) || (bool) $this->args['global_output'];
272
273 // Run constructor to check early if ajax cache busting already created inline css.
274 $this->inline_css = new Advanced_Ads_Inline_Css();
275 }
276
277 /**
278 * Get options from meta field and return specific field
279 *
280 * @param string $field post meta key to be returned. Can be passed as array keys separated with `.`, i.e. 'parent.child' to retrieve multidimensional array values.
281 * @param array $default default options.
282 *
283 * @return mixed meta field content
284 */
285 public function options( $field = '', $default = null ) {
286 // retrieve options, if not given yet
287 if ( is_null( $this->options ) ) {
288 // may return false.
289 $meta = get_post_meta( $this->id, self::$options_meta_field, true );
290 if ( $meta && is_array( $meta ) ) {
291 // merge meta with arguments given on ad load.
292 $this->options = Advanced_Ads_Utils::merge_deep_array( [ $meta, $this->args ] );
293 } else {
294 // load arguments given on ad load.
295 $this->options = $this->args;
296 }
297
298 if ( isset( $this->options['change-ad'] ) ) {
299 // some options was provided by the user.
300 $this->options = Advanced_Ads_Utils::merge_deep_array(
301 [
302 $this->options,
303 $this->options['change-ad'],
304 ]
305 );
306 }
307 }
308
309 // return all options if no field given.
310 if ( empty( $field ) ) {
311 return $this->options;
312 }
313
314 $field = preg_replace( '/\s/', '', $field );
315 $value = $this->options;
316 foreach ( explode( '.', $field ) as $key ) {
317 if ( ! isset( $value[ $key ] ) ) {
318 $value = $default;
319 break;
320 }
321 $value = $value[ $key ];
322 }
323
324 if ( is_null( $value ) ) {
325 $value = $default;
326 }
327
328 /**
329 * Filter the option value retrieved for $field.
330 * `$field` parameter makes dynamic hook portion.
331 *
332 * @var mixed $value The option value (may be set to default).
333 * @var Advanced_Ads_Ad $this The current Advanced_Ads_Ad instance.
334 */
335 return apply_filters( "advanced-ads-ad-option-{$field}", $value, $this );
336 }
337
338 /**
339 * Set an option of the ad
340 *
341 * @param string $option name of the option.
342 * @param mixed $value value of the option.
343 *
344 * @since 1.1.0
345 */
346 public function set_option( $option = '', $value = '' ) {
347 if ( '' === $option ) {
348 return;
349 }
350
351 // get current options.
352 $options = $this->options();
353
354 // set options.
355 $options[ $option ] = $value;
356
357 // save options.
358 $this->options = $options;
359
360 }
361
362
363 /**
364 * Return ad content for frontend output
365 *
366 * @param array $output_options output options.
367 *
368 * @return string $output ad output
369 * @since 1.0.0
370 */
371 public function output( $output_options = [] ) {
372 if ( ! $this->is_ad ) {
373 return '';
374 }
375
376 $this->global_output = isset( $output_options['global_output'] ) ? $output_options['global_output'] : $this->global_output;
377 $output_options['global_output'] = $this->global_output;
378
379 // switch between normal and debug mode.
380 // check if debug output should only be displayed to admins.
381 $user_can_manage_ads = WordPress::user_can( 'advanced_ads_manage_options' );
382 if ( $this->options( 'output.debugmode' )
383 && ( $user_can_manage_ads || ( ! $user_can_manage_ads && ! defined( 'ADVANCED_ADS_AD_DEBUG_FOR_ADMIN_ONLY' ) ) ) ) {
384 $debug = new Advanced_Ads_Ad_Debug();
385
386 return $debug->prepare_debug_output( $this );
387 } else {
388 $output = $this->prepare_frontend_output();
389 }
390
391 // add the ad to the global output array.
392 $advads = Advanced_Ads::get_instance();
393 if ( $output_options['global_output'] ) {
394 $new_ad = [
395 'type' => 'ad',
396 'id' => $this->id,
397 'title' => $this->title,
398 'output' => $output,
399 ];
400 // if ( method_exists( 'Advanced_Ads_Tracking_Plugin' , 'check_ad_tracking_enabled' ) ) {
401 // if ( class_exists( 'Advanced_Ads_Tracking_Plugin', false ) ) {
402 if ( defined( 'AAT_VERSION' ) && - 1 < version_compare( AAT_VERSION, '1.4.2' ) ) {
403
404 $new_ad['tracking_enabled'] = Advanced_Ads_Tracking_Plugin::get_instance()->check_ad_tracking_enabled( $this );
405
406 $tracking_options = Advanced_Ads_Tracking_Plugin::get_instance()->options();
407 if ( isset( $tracking_options['method'] ) && 'frontend' === $tracking_options['method'] && isset( $this->output['placement_id'] ) ) {
408 $new_ad['placement_id'] = $this->output['placement_id'];
409 }
410 }
411
412 $advads->current_ads[] = $new_ad;
413 }
414
415 // action when output is created.
416 do_action( 'advanced-ads-output', $this, $output, $output_options );
417
418 return apply_filters( 'advanced-ads-output-final', $output, $this, $output_options );
419 }
420
421 /**
422 * Check if the ad can be displayed in frontend due to its own conditions
423 *
424 * @param array $check_options check options.
425 *
426 * @return bool $can_display true if can be displayed in frontend
427 * @since 1.0.0
428 */
429 public function can_display( $check_options = [] ) {
430 $check_options = wp_parse_args(
431 $check_options,
432 [
433 'passive_cache_busting' => false,
434 'ignore_debugmode' => false,
435 ]
436 );
437
438 // prevent ad to show up through wp_head, if this is not a header placement.
439 if ( doing_action( 'wp_head' ) && isset( $this->options['placement_type'] ) && 'header' !== $this->options['placement_type']
440 && ! Advanced_Ads_Compatibility::can_inject_during_wp_head() ) {
441 return false;
442 }
443
444 // Check If the current ad is requested using a shortcode placed in the content of the current ad.
445 if ( isset( $this->options['shortcode_ad_id'] ) && (int) $this->options['shortcode_ad_id'] === $this->id ) {
446 return false;
447 }
448
449 // force ad display if debug mode is enabled.
450 if ( isset( $this->output['debugmode'] ) && ! $check_options['ignore_debugmode'] ) {
451 return true;
452 }
453
454 if ( ! $check_options['passive_cache_busting'] ) {
455 // don’t display ads that are not published or private for users not logged in.
456 if ( 'publish' !== $this->status && ! ( 'private' === $this->status && is_user_logged_in() ) ) {
457 return false;
458 }
459
460 if ( ! $this->can_display_by_visitor() ) {
461 return false;
462 }
463 } elseif ( 'publish' !== $this->status ) {
464 return false;
465 }
466
467 if ( $this->ad_expiration->is_ad_expired() ) {
468 return false;
469 }
470
471 // add own conditions to flag output as possible or not.
472 return apply_filters( 'advanced-ads-can-display', true, $this, $check_options );
473 }
474
475 /**
476 * Check visitor conditions
477 *
478 * @return bool $can_display true if can be displayed in frontend based on visitor settings
479 * @since 1.1.0
480 */
481 public function can_display_by_visitor() {
482 if ( ! empty( $this->options['wp_the_query']['is_feed'] ) ) {
483 return true;
484 }
485
486 $visitor_conditions = $this->options( 'visitors', [] );
487 if ( empty( $visitor_conditions ) ) {
488 return true;
489 }
490
491 $last_result = false;
492 $length = count( $visitor_conditions );
493
494 for ( $i = 0; $i < $length; ++ $i ) {
495 $_condition = current( $visitor_conditions );
496 // ignore OR if last result was true.
497 if ( $last_result && isset( $_condition['connector'] ) && 'or' === $_condition['connector'] ) {
498 next( $visitor_conditions );
499 continue;
500 }
501 $result = Advanced_Ads_Visitor_Conditions::frontend_check( $_condition, $this );
502 $last_result = $result;
503 if ( ! $result ) {
504 // return false only, if the next condition doesn’t have an OR operator.
505 $next = next( $visitor_conditions );
506 if ( ! isset( $next['connector'] ) || 'or' !== $next['connector'] ) {
507 return false;
508 }
509 } else {
510 next( $visitor_conditions );
511 }
512 }
513
514 // check mobile condition.
515 if ( isset( $visitor_conditions['mobile'] ) ) {
516 switch ( $visitor_conditions['mobile'] ) {
517 case 'only':
518 if ( ! wp_is_mobile() ) {
519 return false;
520 }
521 break;
522 case 'no':
523 if ( wp_is_mobile() ) {
524 return false;
525 }
526 break;
527 }
528 }
529
530 return true;
531 }
532
533 /**
534 * Check expiry date
535 *
536 * @return bool $can_display true if can be displayed in frontend based on expiry date
537 * @since 1.3.15
538 * @deprecated 1.31.0 This is an internal method and should not have been public.
539 */
540 public function can_display_by_expiry_date() {
541 return $this->ad_expiration->is_ad_expired();
542 }
543
544 /**
545 * Save an ad to the database
546 * takes values from the current state
547 */
548 public function save() {
549 global $wpdb;
550
551 // remove slashes from content.
552 $this->content = $this->prepare_content_to_save();
553
554 $where = [ 'ID' => $this->id ];
555 $wpdb->update( $wpdb->posts, [ 'post_content' => $this->content ], $where );
556
557 // clean post from object cache.
558 clean_post_cache( $this->id );
559
560 // sanitize conditions
561 // see sanitize_conditions function for example on using this filter.
562 $conditions = self::sanitize_conditions_on_save( $this->conditions );
563
564 // save other options to post meta field.
565 $options = $this->options();
566
567 $options['type'] = $this->type;
568 $options['url'] = $this->url;
569 // Inform the tracking add-on about the new url.
570 unset( $options['tracking']['link'] );
571 $options['width'] = $this->width;
572 $options['height'] = $this->height;
573 $options['conditions'] = $conditions;
574 $options['expiry_date'] = $this->expiry_date;
575 $options['description'] = $this->description;
576
577 // save the plugin version, with every ad save.
578 $options['last_save_version'] = ADVADS_VERSION;
579
580 // sanitize container ID option.
581 $options['output']['wrapper-id'] = isset( $options['output']['wrapper-id'] ) ? sanitize_key( $options['output']['wrapper-id'] ) : '';
582
583 // sanitize options before saving
584 $options = $this->prepare_options_to_save( $options );
585
586 // filter to manipulate options or add more to be saved.
587 $options = apply_filters( 'advanced-ads-save-options', $options, $this );
588
589 update_post_meta( $this->id, self::$options_meta_field, $options );
590 }
591
592 /**
593 * Save ad options.
594 * Meant to be used from the outside of an ad.
595 *
596 * @param int $ad_id post ID of the ad.
597 * @param array $options ad options.
598 */
599 public static function save_ad_options( $ad_id, array $options ) {
600
601 // don’t allow to clear options by accident.
602 if ( [] === $options ) {
603 return;
604 }
605
606 update_post_meta( $ad_id, self::$options_meta_field, $options );
607 }
608
609 /**
610 * Native filter for content field before being saved
611 *
612 * @return string $content ad content
613 */
614 public function prepare_content_to_save() {
615
616 $content = $this->content;
617
618 // load ad type specific parameter filter
619 // @todo this is just a hotfix for type_obj not set, yet the cause is still unknown. Likely when the ad is first saved
620 if ( is_object( $this->type_obj ) ) {
621 $content = $this->type_obj->sanitize_content( $content );
622 }
623 // apply a custom filter by ad type.
624 $content = apply_filters( 'advanced-ads-pre-ad-save-' . $this->type, $content );
625
626 return $content;
627 }
628
629 /**
630 * Sanitize ad options before being saved
631 * allows some ad types to sanitize certain values
632 *
633 * @param array $options ad options.
634 * @return array sanitized options.
635 */
636 public function prepare_options_to_save( $options ) {
637
638 // load ad type specific sanitize function.
639 // we need to load the ad type object if not set (e.g., when the ad is saved for the first time)
640 if ( ! is_object( $this->type_obj ) || ! $this->type_obj->ID ) {
641 $types = Advanced_Ads::get_instance()->ad_types;
642 if ( isset( $types[ $this->type ] ) ) {
643 $this->type_obj = $types[ $this->type ];
644 }
645 }
646
647 $options = $this->type_obj->sanitize_options( $options );
648
649 return $options;
650 }
651
652 /**
653 * Prepare ads output
654 *
655 * @return string.
656 */
657 public function prepare_frontend_output() {
658 $options = $this->options();
659
660 if ( isset( $options['change-ad']['content'] ) ) {
661 // output was provided by the user.
662 $output = $options['change-ad']['content'];
663 } else {
664 // load ad type specific content filter.
665 $output = $this->type_obj->prepare_output( $this );
666 }
667
668 // don’t deliver anything, if main ad content is empty.
669 if ( empty( $output ) ) {
670 return '';
671 }
672
673 if ( ! $this->is_head_placement ) {
674 // filter to manipulate the output before the wrapper is added
675 $output = apply_filters( 'advanced-ads-output-inside-wrapper', $output, $this );
676
677 // build wrapper around the ad.
678 $output = $this->add_wrapper( $output );
679
680 // add a clearfix, if set.
681 if (
682 ( ! empty( $this->args['is_top_level'] ) && ! empty( $this->args['placement_clearfix'] ) )
683 || $this->options( 'output.clearfix' )
684 ) {
685 $output .= '<br style="clear: both; display: block; float: none;"/>';
686 }
687 }
688
689 // apply a custom filter by ad type.
690 $output = apply_filters( 'advanced-ads-ad-output', $output, $this );
691
692 return $output;
693 }
694
695 /**
696 * Sanitize ad display conditions when saving the ad
697 *
698 * @param array $conditions conditions array send via the dashboard form for an ad.
699 *
700 * @return array with sanitized conditions
701 * @since 1.0.0
702 */
703 public function sanitize_conditions_on_save( $conditions = [] ) {
704
705 global $advanced_ads_ad_conditions;
706
707 if ( ! is_array( $conditions ) || [] === $conditions ) {
708 return [];
709 }
710
711 foreach ( $conditions as $_key => $_condition ) {
712 if ( 'postids' === $_key ) {
713 // sanitize single post conditions
714 if ( empty( $_condition['ids'] ) ) { // remove, if empty.
715 $_condition['include'] = [];
716 $_condition['exclude'] = [];
717 } elseif ( isset( $_condition['method'] ) ) {
718 switch ( $_condition['method'] ) {
719 case 'include':
720 $_condition['include'] = $_condition['ids'];
721 $_condition['exclude'] = [];
722 break;
723 case 'exclude':
724 $_condition['include'] = [];
725 $_condition['exclude'] = $_condition['ids'];
726 break;
727 }
728 }
729 } else {
730 if ( ! is_array( $_condition ) ) {
731 $_condition = trim( $_condition );
732 }
733 if ( $_condition == '' ) {
734 $conditions[ $_key ] = $_condition;
735 continue;
736 }
737 }
738 $type = ! empty( $advanced_ads_ad_conditions[ $_key ]['type'] ) ? $advanced_ads_ad_conditions[ $_key ]['type'] : 0;
739 if ( empty( $type ) ) {
740 continue;
741 }
742
743 // dynamically apply filters for each condition used.
744 $conditions[ $_key ] = apply_filters( 'advanced-ads-sanitize-condition-' . $type, $_condition );
745 }
746
747 return $conditions;
748 }
749
750 /**
751 * Sanitize id input field(s) for pattern /1,2,3,4/
752 *
753 * @param mixed $cond input string/array.
754 *
755 * @return array/string $cond sanitized string/array
756 */
757 public static function sanitize_condition_idfield( $cond = '' ) {
758 // strip anything that is not comma or number.
759
760 if ( is_array( $cond ) ) {
761 foreach ( $cond as $_key => $_cond ) {
762 $cond[ $_key ] = preg_replace( '#[^0-9,]#', '', $_cond );
763 }
764 } else {
765 $cond = preg_replace( '#[^0-9,]#', '', $cond );
766 }
767
768 return $cond;
769 }
770
771 /**
772 * Sanitize radio input field
773 *
774 * @param string $string input string.
775 *
776 * @return string $string sanitized string.
777 */
778 public static function sanitize_condition_radio( $string = '' ) {
779 // only allow 0, 1 and empty.
780 return preg_replace( '#[^01]#', '', $string );
781 }
782
783 /**
784 * Sanitize comma seperated text input field
785 *
786 * @param mixed $cond input string/array.
787 *
788 * @return array/string $cond sanitized string/array.
789 */
790 public static function sanitize_condition_textvalues( $cond = '' ) {
791 // strip anything that is not comma, alphanumeric, minus and underscore.
792 if ( is_array( $cond ) ) {
793 foreach ( $cond as $_key => $_cond ) {
794 $cond[ $_key ] = preg_replace( '#[^0-9,A-Za-z-_]#', '', $_cond );
795 }
796 } else {
797 $cond = preg_replace( '#[^0-9,A-Za-z-_]#', '', $cond );
798 }
799
800 return $cond;
801 }
802
803 /**
804 * Load wrapper options set with the ad
805 *
806 * @return array $wrapper options array ready to be use in add_wrapper() function.
807 * @since 1.3
808 */
809 protected function load_wrapper_options() {
810 $wrapper = [];
811
812 $position = $this->options( 'output.position', '' );
813 $use_placement_pos = false;
814
815 if ( $this->args['is_top_level'] ) {
816 if ( isset( $this->output['class'] ) && is_array( $this->output['class'] ) ) {
817 $wrapper['class'] = $this->output['class'];
818 }
819 if ( ! empty( $this->args['placement_position'] ) ) {
820 // If not group, Set placement position instead of ad position.
821 $use_placement_pos = true;
822 $position = $this->args['placement_position'];
823 }
824 }
825
826 switch ( $position ) {
827 case 'left':
828 case 'left_float':
829 case 'left_nofloat':
830 $wrapper['style']['float'] = 'left';
831 break;
832 case 'right':
833 case 'right_float':
834 case 'right_nofloat':
835 $wrapper['style']['float'] = 'right';
836 break;
837 case 'center':
838 case 'center_nofloat':
839 case 'center_float':
840 $wrapper['style']['margin-left'] = 'auto';
841 $wrapper['style']['margin-right'] = 'auto';
842
843 if (
844 ( ! $this->width || empty( $this->output['add_wrapper_sizes'] ) )
845 || $use_placement_pos
846 ) {
847 $wrapper['style']['text-align'] = 'center';
848 }
849
850 // add css rule after wrapper to center the ad.
851 break;
852 case 'clearfix':
853 $wrapper['style']['clear'] = 'both';
854 break;
855 }
856
857 // add manual classes.
858 if ( isset( $this->output['wrapper-class'] ) && '' !== $this->output['wrapper-class'] ) {
859 $classes = explode( ' ', $this->output['wrapper-class'] );
860
861 foreach ( $classes as $_class ) {
862 $wrapper['class'][] = sanitize_text_field( $_class );
863 }
864 }
865
866 if ( ! empty( $this->output['margin']['top'] ) ) {
867 $wrapper['style']['margin-top'] = (int) $this->output['margin']['top'] . 'px';
868 }
869 if ( empty( $wrapper['style']['margin-right'] ) && ! empty( $this->output['margin']['right'] ) ) {
870 $wrapper['style']['margin-right'] = (int) $this->output['margin']['right'] . 'px';
871 }
872 if ( ! empty( $this->output['margin']['bottom'] ) ) {
873 $wrapper['style']['margin-bottom'] = (int) $this->output['margin']['bottom'] . 'px';
874 }
875 if ( empty( $wrapper['style']['margin-left'] ) && ! empty( $this->output['margin']['left'] ) ) {
876 $wrapper['style']['margin-left'] = (int) $this->output['margin']['left'] . 'px';
877 }
878
879 if ( ! empty( $this->output['add_wrapper_sizes'] ) ) {
880 if ( ! empty( $this->width ) ) {
881 $wrapper['style']['width'] = $this->width . 'px';
882 }
883 if ( ! empty( $this->height ) ) {
884 $wrapper['style']['height'] = $this->height . 'px';
885 }
886 }
887
888 if ( ! empty( $this->output['clearfix_before'] ) ) {
889 $wrapper['style']['clear'] = 'both';
890 }
891
892 return $wrapper;
893 }
894
895 /**
896 * Add a wrapper arount the ad content if wrapper information are given
897 *
898 * @param string $ad_content content of the ad.
899 *
900 * @return string $wrapper ad within the wrapper
901 * @since 1.1.4
902 */
903 protected function add_wrapper( $ad_content = '' ) {
904 $wrapper_options = apply_filters( 'advanced-ads-output-wrapper-options', $this->wrapper, $this );
905
906 if ( $this->label && ! empty( $wrapper_options['style']['height'] ) ) {
907 // Create another wrapper so that the label does not reduce the height of the ad wrapper.
908 $height = [ 'style' => [ 'height' => $wrapper_options['style']['height'] ] ];
909 unset( $wrapper_options['style']['height'] );
910 $ad_content = '<div' . Advanced_Ads_Utils::build_html_attributes( $height ) . '>' . $ad_content . '</div>';
911 }
912
913 // Adds inline css to the wrapper.
914 if ( ! empty( $this->options['inline-css'] ) && $this->args['is_top_level'] ) {
915 $wrapper_options = $this->inline_css->add_css( $wrapper_options, $this->options['inline-css'], $this->global_output );
916 }
917
918 if (
919 ! defined( 'ADVANCED_ADS_DISABLE_EDIT_BAR' )
920 // Add edit button for users with the appropriate rights.
921 && WordPress::user_can( 'advanced_ads_edit_ads' )
922 // We need a wrapper. Check if at least the placement wrapper exists.
923 && ! empty( $this->args['placement_type'] )
924 ) {
925 ob_start();
926 include ADVADS_ABSPATH . 'public/views/ad-edit-bar.php';
927 $ad_content = trim( ob_get_clean() ) . $ad_content;
928 // Include the tooltip title from get_tooltip_title() in the 'title' attribute.
929 $this->output['wrapper_attrs']['data-title'][] = $this->get_tooltip_title();
930 }
931
932 // ad Health Tool add class and attribute in to ads and group
933 if ( WordPress::user_can('advanced_ads_edit_ads') ) {
934
935 $has_group_info = isset($this->args['group_info']);
936 $frontend_prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
937
938 if ( ! $has_group_info ) {
939 // Add the 'highlight-wrapper' class to the ad wrapper
940 $wrapper_options['class'][] = $frontend_prefix . 'highlight-wrapper';
941 }
942
943 }
944
945 if ('' === ($this->output['wrapper-id'] ?? '')
946 && ( [] === $wrapper_options || ! is_array($wrapper_options) )) {
947 return $this->label . $ad_content;
948 }
949
950 // create unique id if not yet given.
951 if ( empty( $wrapper_options['id'] ) ) {
952 $wrapper_options['id'] = $this->create_wrapper_id();
953 $this->wrapper['id'] = $wrapper_options['id'];
954 }
955
956 $wrapper_element = ! empty( $this->args['inline_wrapper_element'] ) ? 'span' : 'div';
957
958 // build the box
959 $wrapper = '<' . $wrapper_element . Advanced_Ads_Utils::build_html_attributes( array_merge(
960 $wrapper_options,
961 isset( $this->output['wrapper_attrs'] ) ? $this->output['wrapper_attrs'] : []
962 ) ) . '>';
963 $wrapper .= $this->label;
964 $wrapper .= apply_filters( 'advanced-ads-output-wrapper-before-content', '', $this );
965 $wrapper .= $ad_content;
966 $wrapper .= apply_filters( 'advanced-ads-output-wrapper-after-content', '', $this );
967 $wrapper .= '</' . $wrapper_element . '>';
968
969 return $wrapper;
970 }
971
972 /**
973 * Create a random wrapper id
974 *
975 * @return string $id random id string
976 * @since 1.1.4
977 */
978 private function create_wrapper_id() {
979
980 if ( isset( $this->output['wrapper-id'] ) ) {
981 $id = sanitize_key( $this->output['wrapper-id'] );
982 if ( '' !== $id ) {
983 return $id;
984 }
985 }
986
987 $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
988
989 return $prefix . mt_rand();
990 }
991
992 /**
993 * Create an "Advertisement" label if conditions are met.
994 */
995 public function maybe_create_label() {
996 $placement_state = isset( $this->args['ad_label'] ) ? $this->args['ad_label'] : 'default';
997
998 $label = Advanced_Ads::get_instance()->get_label( $placement_state );
999
1000 if ( $this->args['is_top_level'] && $label ) {
1001 $this->label = $label;
1002 }
1003 }
1004
1005 /**
1006 * Get the ad url.
1007 *
1008 * @return string
1009 */
1010 private function get_url() {
1011 $this->url = $this->options( 'url' );
1012
1013 // If the tracking add-on is not active.
1014 if ( ! defined( 'AAT_VERSION' ) ) {
1015 global $pagenow;
1016 // If this is not the ad edit page.
1017 if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow && ! empty( $this->url ) ) {
1018 // Remove placeholders.
1019 $this->url = str_replace(
1020 [
1021 '[POST_ID]',
1022 '[POST_SLUG]',
1023 '[CAT_SLUG]',
1024 '[AD_ID]',
1025 ],
1026 '',
1027 $this->url
1028 );
1029 }
1030 }
1031
1032 return $this->url;
1033 }
1034
1035 /**
1036 * Generate the tooltip title for a placement with associated ads.
1037 *
1038 * @return string Tooltip title containing placement and ads name.
1039 */
1040 private function get_tooltip_title() {
1041 $ads = [];
1042
1043 // Check if a group ID is provided in the arguments.
1044 if ( isset( $this->args['group_info']['id'] ) ) {
1045 // Create an instance of Advanced_Ads_Group using the provided group ID.
1046 $group = new Advanced_Ads_Group( $this->args['group_info']['id'] );
1047 // Get all ads within the group and extract their post titles.
1048 $ads = wp_list_pluck( $group->get_all_ads(), 'post_title' );
1049 } else {
1050
1051 // If no group ID is provided, get ads directly from the Advanced_Ads model.
1052 $ads = wp_list_pluck(
1053 Advanced_Ads::get_instance()->get_model()->get_ads(
1054 [
1055 'post__in' => [ $this->args['id'] ]
1056 ]
1057 ),
1058 'post_title'
1059 );
1060 }
1061
1062 // Construct and format the tooltip title using the placement ID and ad titles.
1063 return sprintf(
1064 // translators: %1$s is a placement name, %2$s is the ads name.
1065 __( 'Placement name: %1$s; Ads: %2$s', 'advanced-ads' ),
1066 esc_attr( $this->args['output']['placement_id'] ?? '' ),
1067 esc_attr( $ads ? implode( ',', $ads ) : '' )
1068 );
1069 }
1070 }
1071