PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 1.39.2
Advanced Ads – Ad Manager & AdSense v1.39.2
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 3 years ago EDD_SL_Plugin_Updater.php 4 years ago ad-ajax.php 3 years ago ad-debug.php 3 years ago ad-expiration.php 3 years ago ad-health-notices.php 3 years ago ad-model.php 3 years ago ad-select.php 3 years ago ad.php 3 years ago ad_ajax_callbacks.php 3 years ago ad_group.php 3 years ago ad_placements.php 3 years ago ad_type_abstract.php 3 years ago ad_type_content.php 3 years ago ad_type_dummy.php 3 years ago ad_type_group.php 3 years ago ad_type_image.php 3 years ago ad_type_plain.php 3 years ago checks.php 3 years ago compatibility.php 3 years ago display-conditions.php 3 years ago filesystem.php 3 years ago frontend-notices.php 3 years ago frontend_checks.php 3 years ago in-content-injector.php 3 years ago inline-css.php 3 years ago plugin.php 3 years ago upgrades.php 6 years ago utils.php 3 years ago visitor-conditions.php 3 years ago widget.php 3 years ago
ad.php
1048 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 /**
13 * An ad object
14 *
15 * @package Advanced_Ads_Ad
16 * @author Thomas Maier <support@wpadvancedads.com>
17 * @deprecated since version 1.5.3 (May 6th 2015)
18 * might still be needed if some old add-ons are running somewhere
19 */
20 if ( ! class_exists( 'Advads_Ad', false ) ) {
21 class Advads_Ad extends Advanced_Ads_Ad {
22
23 }
24 }
25
26 /**
27 * An ad object
28 *
29 * @package Advanced_Ads_Ad
30 * @author Thomas Maier <support@wpadvancedads.com>
31 */
32 class Advanced_Ads_Ad {
33
34 /**
35 * Id of the post type for this ad
36 *
37 * @var int $id
38 */
39 public $id = 0;
40
41 /**
42 * True, if this is an Advanced Ads Ad post type
43 *
44 * @var bool $is_ad
45 */
46 public $is_ad = false;
47
48 /**
49 * Ad type
50 *
51 * @var string $type ad type.
52 */
53 public $type = 'content';
54
55 /**
56 * Notes about the ad usage
57 *
58 * @var string $description
59 */
60 public $description = '';
61
62 /**
63 * Ad width
64 *
65 * @var int $width width of the ad.
66 */
67 public $width = 0;
68
69 /**
70 * Target url
71 *
72 * @var string $url ad URL parameter.
73 */
74 public $url = '';
75
76 /**
77 * Ad height
78 *
79 * @var int $height height of the ad.
80 */
81 public $height = 0;
82
83 /**
84 * Object of current ad type
85 *
86 * @var Advanced_Ads_Ad_Type_Abstract $type_obj object of the current ad type.
87 */
88 protected $type_obj;
89
90 /**
91 * Content of the ad
92 *
93 * Only needed for ad types using the post content field
94 *
95 * @var string $content content of the ad.
96 */
97 public $content = '';
98
99 /**
100 * Conditions of the ad display
101 *
102 * @var array $conditions display and visitor conditions.
103 */
104 public $conditions = [];
105
106 /**
107 * Status of the ad (e.g. publish, pending)
108 *
109 * @var string $status status of the ad.
110 */
111 public $status = '';
112
113 /**
114 * Array with meta field options aka parameters
115 *
116 * @var array $options ad options.
117 */
118 protected $options;
119
120 /**
121 * Name of the meta field to save options to
122 *
123 * @var string $options_meta_field under which post meta key the ad options are stored.
124 */
125 public static $options_meta_field = 'advanced_ads_ad_options';
126
127 /**
128 * Additional arguments set when ad is loaded, overwrites or extends options
129 *
130 * @var array $args
131 */
132 public $args = [];
133
134 /**
135 * Multidimensional array contains information about the wrapper
136 * Each possible html attribute is an array with possible multiple elements
137 *
138 * @var array $wrapper options of the ad wrapper.
139 */
140 public $wrapper = [];
141
142 /**
143 * Will the ad be tracked?
144 *
145 * @var mixed $global_output
146 */
147 public $global_output;
148
149 /**
150 * Title of the ad
151 *
152 * @var string $title
153 */
154 public $title = '';
155
156 /**
157 * Displayed above the ad.
158 *
159 * @var string $label ad label.
160 */
161 protected $label = '';
162
163 /**
164 * Inline CSS object, one instance per ad.
165 *
166 * @var Advanced_Ads_Inline_Css
167 */
168 private $inline_css;
169 /**
170 * Timestamp if ad has an expiration date.
171 *
172 * @var int
173 */
174 public $expiry_date = 0;
175
176 /**
177 * The ad expiration object.
178 *
179 * @var Advanced_Ads_Ad_Expiration
180 */
181 private $ad_expiration;
182
183 /**
184 * The saved output options.
185 *
186 * @var array
187 */
188 public $output;
189
190 /**
191 * Whether the current ad is in a head placement.
192 *
193 * @var bool
194 */
195 public $is_head_placement;
196
197 /**
198 * Init ad object
199 *
200 * @param int $id id of the ad.
201 * @param array $args additional arguments.
202 */
203 public function __construct( $id, $args = [] ) {
204 $this->id = (int) $id;
205 $this->args = is_array( $args ) ? $args : [];
206
207 // whether the ad will be tracked.
208 $this->global_output = isset( $this->args['global_output'] ) ? (bool) $this->args['global_output'] : true;
209
210 // Run constructor to check early if ajax cache busting already created inline css.
211 $this->inline_css = new Advanced_Ads_Inline_Css();
212
213 if ( ! empty( $this->id ) ) {
214 $this->load( $this->id );
215 }
216
217 // dynamically add sanitize filters for condition types.
218 $_types = [];
219 // -TODO use model
220 $advanced_ads_ad_conditions = Advanced_Ads::get_ad_conditions();
221 foreach ( $advanced_ads_ad_conditions as $_condition ) {
222 // add unique.
223 $_types[ $_condition['type'] ] = false;
224 }
225 // iterate types.
226 foreach ( array_keys( $_types ) as $_type ) {
227 // -TODO might be faster to use __call() method or isset()-test class method array
228 $method_name = 'sanitize_condition_' . $_type;
229 if ( method_exists( $this, $method_name ) ) {
230 add_filter( 'advanced-ads-sanitize-condition-' . $_type, [ $this, $method_name ], 10, 1 );
231 } elseif ( function_exists( 'advads_sanitize_condition_' . $_type ) ) {
232 // check for public function to sanitize this.
233 add_filter( 'advanced-ads-sanitize-condition-' . $_type, 'advads_sanitize_condition_' . $_type, 10, 1 );
234 }
235 }
236 }
237
238 /**
239 * Load an ad object by id based on its ad type
240 *
241 * @param int $id ad id.
242 *
243 * @return bool false if ad could not be loaded.
244 */
245 private function load( $id = 0 ) {
246
247 $_data = get_post( $id );
248 if ( null === $_data ) {
249 return false;
250 }
251
252 // return, if not an ad.
253 if ( Advanced_Ads::POST_TYPE_SLUG !== $_data->post_type ) {
254 return false;
255 } else {
256 $this->is_ad = true;
257 }
258
259 $this->type = $this->options( 'type' );
260 $this->title = $_data->post_title;
261 /* load ad type object */
262 $types = Advanced_Ads::get_instance()->ad_types;
263 if ( isset( $types[ $this->type ] ) ) {
264 $this->type_obj = $types[ $this->type ];
265 } else {
266 $this->type_obj = new Advanced_Ads_Ad_Type_Abstract();
267 }
268
269 // filter the positioning options.
270 new Advanced_Ads_Ad_Positioning( $this );
271
272 $this->url = $this->get_url();
273 $this->width = absint( $this->options( 'width' ) );
274 $this->height = absint( $this->options( 'height' ) );
275 $this->conditions = $this->options( 'conditions' );
276 $this->description = $this->options( 'description' );
277 $this->output = $this->options( 'output' );
278 $this->status = $_data->post_status;
279 $this->expiry_date = (int) $this->options( 'expiry_date' );
280 $this->is_head_placement = isset( $this->args['placement_type'] ) && 'header' === $this->args['placement_type'];
281 $this->args['is_top_level'] = ! isset( $this->args['is_top_level'] );
282
283 // load content based on ad type.
284 $this->content = $this->type_obj->load_content( $_data );
285
286 if ( ! $this->is_head_placement ) {
287 $this->maybe_create_label();
288 $this->wrapper = $this->load_wrapper_options();
289
290 // set wrapper conditions.
291 $this->wrapper = apply_filters( 'advanced-ads-set-wrapper', $this->wrapper, $this );
292 // add unique wrapper id.
293 if ( is_array( $this->wrapper )
294 && [] !== $this->wrapper
295 && ! isset( $this->wrapper['id'] ) ) {
296 // create unique id if not yet given.
297 $this->wrapper['id'] = $this->create_wrapper_id();
298 }
299 }
300
301 $this->ad_expiration = new Advanced_Ads_Ad_Expiration( $this );
302 }
303
304 /**
305 * Get options from meta field and return specific field
306 *
307 * @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.
308 * @param array $default default options.
309 *
310 * @return mixed meta field content
311 */
312 public function options( $field = '', $default = null ) {
313 // retrieve options, if not given yet
314 if ( is_null( $this->options ) ) {
315 // may return false.
316 $meta = get_post_meta( $this->id, self::$options_meta_field, true );
317 if ( $meta && is_array( $meta ) ) {
318 // merge meta with arguments given on ad load.
319 $this->options = Advanced_Ads_Utils::merge_deep_array( [ $meta, $this->args ] );
320 } else {
321 // load arguments given on ad load.
322 $this->options = $this->args;
323 }
324
325 if ( isset( $this->options['change-ad'] ) ) {
326 // some options was provided by the user.
327 $this->options = Advanced_Ads_Utils::merge_deep_array(
328 [
329 $this->options,
330 $this->options['change-ad'],
331 ]
332 );
333 }
334 }
335
336 // return all options if no field given.
337 if ( empty( $field ) ) {
338 return $this->options;
339 }
340
341 $field = preg_replace( '/\s/', '', $field );
342 $value = $this->options;
343 foreach ( explode( '.', $field ) as $key ) {
344 if ( ! isset( $value[ $key ] ) ) {
345 $value = $default;
346 break;
347 }
348 $value = $value[ $key ];
349 }
350
351 if ( is_null( $value ) ) {
352 $value = $default;
353 }
354
355 /**
356 * Filter the option value retrieved for $field.
357 * `$field` parameter makes dynamic hook portion.
358 *
359 * @var mixed $value The option value (may be set to default).
360 * @var Advanced_Ads_Ad $this The current Advanced_Ads_Ad instance.
361 */
362 return apply_filters( "advanced-ads-ad-option-{$field}", $value, $this );
363 }
364
365 /**
366 * Set an option of the ad
367 *
368 * @param string $option name of the option.
369 * @param mixed $value value of the option.
370 *
371 * @since 1.1.0
372 */
373 public function set_option( $option = '', $value = '' ) {
374 if ( '' === $option ) {
375 return;
376 }
377
378 // get current options.
379 $options = $this->options();
380
381 // set options.
382 $options[ $option ] = $value;
383
384 // save options.
385 $this->options = $options;
386
387 }
388
389
390 /**
391 * Return ad content for frontend output
392 *
393 * @param array $output_options output options.
394 *
395 * @return string $output ad output
396 * @since 1.0.0
397 */
398 public function output( $output_options = [] ) {
399 if ( ! $this->is_ad ) {
400 return '';
401 }
402
403 $this->global_output = isset( $output_options['global_output'] ) ? $output_options['global_output'] : $this->global_output;
404 $output_options['global_output'] = $this->global_output;
405
406 // switch between normal and debug mode.
407 // check if debug output should only be displayed to admins.
408 $user_can_manage_ads = current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_manage_options' ) );
409 if ( $this->options( 'output.debugmode' )
410 && ( $user_can_manage_ads || ( ! $user_can_manage_ads && ! defined( 'ADVANCED_ADS_AD_DEBUG_FOR_ADMIN_ONLY' ) ) ) ) {
411 $debug = new Advanced_Ads_Ad_Debug();
412
413 return $debug->prepare_debug_output( $this );
414 } else {
415 $output = $this->prepare_frontend_output();
416 }
417
418 // add the ad to the global output array.
419 $advads = Advanced_Ads::get_instance();
420 if ( $output_options['global_output'] ) {
421 $new_ad = [
422 'type' => 'ad',
423 'id' => $this->id,
424 'title' => $this->title,
425 'output' => $output,
426 ];
427 // if ( method_exists( 'Advanced_Ads_Tracking_Plugin' , 'check_ad_tracking_enabled' ) ) {
428 // if ( class_exists( 'Advanced_Ads_Tracking_Plugin', false ) ) {
429 if ( defined( 'AAT_VERSION' ) && - 1 < version_compare( AAT_VERSION, '1.4.2' ) ) {
430
431 $new_ad['tracking_enabled'] = Advanced_Ads_Tracking_Plugin::get_instance()->check_ad_tracking_enabled( $this );
432
433 $tracking_options = Advanced_Ads_Tracking_Plugin::get_instance()->options();
434 if ( isset( $tracking_options['method'] ) && 'frontend' === $tracking_options['method'] && isset( $this->output['placement_id'] ) ) {
435 $new_ad['placement_id'] = $this->output['placement_id'];
436 }
437 }
438
439 $advads->current_ads[] = $new_ad;
440 }
441
442 // action when output is created.
443 do_action( 'advanced-ads-output', $this, $output, $output_options );
444
445 return apply_filters( 'advanced-ads-output-final', $output, $this, $output_options );
446 }
447
448 /**
449 * Check if the ad can be displayed in frontend due to its own conditions
450 *
451 * @param array $check_options check options.
452 *
453 * @return bool $can_display true if can be displayed in frontend
454 * @since 1.0.0
455 */
456 public function can_display( $check_options = [] ) {
457 $check_options = wp_parse_args(
458 $check_options,
459 [
460 'passive_cache_busting' => false,
461 'ignore_debugmode' => false,
462 ]
463 );
464
465 // prevent ad to show up through wp_head, if this is not a header placement.
466 if ( doing_action( 'wp_head' ) && isset( $this->options['placement_type'] ) && 'header' !== $this->options['placement_type']
467 && ! Advanced_Ads_Compatibility::can_inject_during_wp_head() ) {
468 return false;
469 }
470
471 // Check If the current ad is requested using a shortcode placed in the content of the current ad.
472 if ( isset( $this->options['shortcode_ad_id'] ) && (int) $this->options['shortcode_ad_id'] === $this->id ) {
473 return false;
474 }
475
476 // force ad display if debug mode is enabled.
477 if ( isset( $this->output['debugmode'] ) && ! $check_options['ignore_debugmode'] ) {
478 return true;
479 }
480
481 if ( ! $check_options['passive_cache_busting'] ) {
482 // don’t display ads that are not published or private for users not logged in.
483 if ( 'publish' !== $this->status && ! ( 'private' === $this->status && is_user_logged_in() ) ) {
484 return false;
485 }
486
487 if ( ! $this->can_display_by_visitor() ) {
488 return false;
489 }
490 } elseif ( 'publish' !== $this->status ) {
491 return false;
492 }
493
494 if ( $this->ad_expiration->is_ad_expired() ) {
495 return false;
496 }
497
498 // add own conditions to flag output as possible or not.
499 return apply_filters( 'advanced-ads-can-display', true, $this, $check_options );
500 }
501
502 /**
503 * Check visitor conditions
504 *
505 * @return bool $can_display true if can be displayed in frontend based on visitor settings
506 * @since 1.1.0
507 */
508 public function can_display_by_visitor() {
509 if ( ! empty( $this->options['wp_the_query']['is_feed'] ) ) {
510 return true;
511 }
512
513 $visitor_conditions = $this->options( 'visitors', [] );
514 if ( empty( $visitor_conditions ) ) {
515 return true;
516 }
517
518 $last_result = false;
519 $length = count( $visitor_conditions );
520
521 for ( $i = 0; $i < $length; ++ $i ) {
522 $_condition = current( $visitor_conditions );
523 // ignore OR if last result was true.
524 if ( $last_result && isset( $_condition['connector'] ) && 'or' === $_condition['connector'] ) {
525 next( $visitor_conditions );
526 continue;
527 }
528 $result = Advanced_Ads_Visitor_Conditions::frontend_check( $_condition, $this );
529 $last_result = $result;
530 if ( ! $result ) {
531 // return false only, if the next condition doesn’t have an OR operator.
532 $next = next( $visitor_conditions );
533 if ( ! isset( $next['connector'] ) || 'or' !== $next['connector'] ) {
534 return false;
535 }
536 } else {
537 next( $visitor_conditions );
538 }
539 }
540
541 // check mobile condition.
542 if ( isset( $visitor_conditions['mobile'] ) ) {
543 switch ( $visitor_conditions['mobile'] ) {
544 case 'only':
545 if ( ! wp_is_mobile() ) {
546 return false;
547 }
548 break;
549 case 'no':
550 if ( wp_is_mobile() ) {
551 return false;
552 }
553 break;
554 }
555 }
556
557 return true;
558 }
559
560 /**
561 * Check expiry date
562 *
563 * @return bool $can_display true if can be displayed in frontend based on expiry date
564 * @since 1.3.15
565 * @deprecated 1.31.0 This is an internal method and should not have been public.
566 */
567 public function can_display_by_expiry_date() {
568 return $this->ad_expiration->is_ad_expired();
569 }
570
571 /**
572 * Save an ad to the database
573 * takes values from the current state
574 */
575 public function save() {
576 global $wpdb;
577
578 // remove slashes from content.
579 $this->content = $this->prepare_content_to_save();
580
581 $where = [ 'ID' => $this->id ];
582 $wpdb->update( $wpdb->posts, [ 'post_content' => $this->content ], $where );
583
584 // clean post from object cache.
585 clean_post_cache( $this->id );
586
587 // sanitize conditions
588 // see sanitize_conditions function for example on using this filter.
589 $conditions = self::sanitize_conditions_on_save( $this->conditions );
590
591 // save other options to post meta field.
592 $options = $this->options();
593
594 $options['type'] = $this->type;
595 $options['url'] = $this->url;
596 // Inform the tracking add-on about the new url.
597 unset( $options['tracking']['link'] );
598 $options['width'] = $this->width;
599 $options['height'] = $this->height;
600 $options['conditions'] = $conditions;
601 $options['expiry_date'] = $this->expiry_date;
602 $options['description'] = $this->description;
603
604 // save the plugin version, with every ad save.
605 $options['last_save_version'] = ADVADS_VERSION;
606
607 // sanitize container ID option.
608 $options['output']['wrapper-id'] = isset( $options['output']['wrapper-id'] ) ? sanitize_key( $options['output']['wrapper-id'] ) : '';
609
610 // sanitize options before saving
611 $options = $this->prepare_options_to_save( $options );
612
613 // filter to manipulate options or add more to be saved.
614 $options = apply_filters( 'advanced-ads-save-options', $options, $this );
615
616 update_post_meta( $this->id, self::$options_meta_field, $options );
617 }
618
619 /**
620 * Save ad options.
621 * Meant to be used from the outside of an ad.
622 *
623 * @param int $ad_id post ID of the ad.
624 * @param array $options ad options.
625 */
626 public static function save_ad_options( $ad_id, array $options ) {
627
628 // don’t allow to clear options by accident.
629 if ( [] === $options ) {
630 return;
631 }
632
633 update_post_meta( $ad_id, self::$options_meta_field, $options );
634 }
635
636 /**
637 * Native filter for content field before being saved
638 *
639 * @return string $content ad content
640 */
641 public function prepare_content_to_save() {
642
643 $content = $this->content;
644
645 // load ad type specific parameter filter
646 // @todo this is just a hotfix for type_obj not set, yet the cause is still unknown. Likely when the ad is first saved
647 if ( is_object( $this->type_obj ) ) {
648 $content = $this->type_obj->sanitize_content( $content );
649 }
650 // apply a custom filter by ad type.
651 $content = apply_filters( 'advanced-ads-pre-ad-save-' . $this->type, $content );
652
653 return $content;
654 }
655
656 /**
657 * Sanitize ad options before being saved
658 * allows some ad types to sanitize certain values
659 *
660 * @param array $options ad options.
661 * @return array sanitized options.
662 */
663 public function prepare_options_to_save( $options ) {
664
665 // load ad type specific sanitize function.
666 // we need to load the ad type object if not set (e.g., when the ad is saved for the first time)
667 if ( ! is_object( $this->type_obj ) || ! $this->type_obj->ID ) {
668 $types = Advanced_Ads::get_instance()->ad_types;
669 if ( isset( $types[ $this->type ] ) ) {
670 $this->type_obj = $types[ $this->type ];
671 }
672 }
673
674 $options = $this->type_obj->sanitize_options( $options );
675
676 return $options;
677 }
678
679 /**
680 * Prepare ads output
681 *
682 * @return string.
683 */
684 public function prepare_frontend_output() {
685 $options = $this->options();
686
687 if ( isset( $options['change-ad']['content'] ) ) {
688 // output was provided by the user.
689 $output = $options['change-ad']['content'];
690 } else {
691 // load ad type specific content filter.
692 $output = $this->type_obj->prepare_output( $this );
693 }
694
695 // don’t deliver anything, if main ad content is empty.
696 if ( empty( $output ) ) {
697 return '';
698 }
699
700 if ( ! $this->is_head_placement ) {
701 // filter to manipulate the output before the wrapper is added
702 $output = apply_filters( 'advanced-ads-output-inside-wrapper', $output, $this );
703
704 // build wrapper around the ad.
705 $output = $this->add_wrapper( $output );
706
707 // add a clearfix, if set.
708 if (
709 ( ! empty( $this->args['is_top_level'] ) && ! empty( $this->args['placement_clearfix'] ) )
710 || $this->options( 'output.clearfix' )
711 ) {
712 $output .= '<br style="clear: both; display: block; float: none;"/>';
713 }
714 }
715
716 // apply a custom filter by ad type.
717 $output = apply_filters( 'advanced-ads-ad-output', $output, $this );
718
719 return $output;
720 }
721
722 /**
723 * Sanitize ad display conditions when saving the ad
724 *
725 * @param array $conditions conditions array send via the dashboard form for an ad.
726 *
727 * @return array with sanitized conditions
728 * @since 1.0.0
729 */
730 public function sanitize_conditions_on_save( $conditions = [] ) {
731
732 global $advanced_ads_ad_conditions;
733
734 if ( ! is_array( $conditions ) || [] === $conditions ) {
735 return [];
736 }
737
738 foreach ( $conditions as $_key => $_condition ) {
739 if ( 'postids' === $_key ) {
740 // sanitize single post conditions
741 if ( empty( $_condition['ids'] ) ) { // remove, if empty.
742 $_condition['include'] = [];
743 $_condition['exclude'] = [];
744 } elseif ( isset( $_condition['method'] ) ) {
745 switch ( $_condition['method'] ) {
746 case 'include':
747 $_condition['include'] = $_condition['ids'];
748 $_condition['exclude'] = [];
749 break;
750 case 'exclude':
751 $_condition['include'] = [];
752 $_condition['exclude'] = $_condition['ids'];
753 break;
754 }
755 }
756 } else {
757 if ( ! is_array( $_condition ) ) {
758 $_condition = trim( $_condition );
759 }
760 if ( $_condition == '' ) {
761 $conditions[ $_key ] = $_condition;
762 continue;
763 }
764 }
765 $type = ! empty( $advanced_ads_ad_conditions[ $_key ]['type'] ) ? $advanced_ads_ad_conditions[ $_key ]['type'] : 0;
766 if ( empty( $type ) ) {
767 continue;
768 }
769
770 // dynamically apply filters for each condition used.
771 $conditions[ $_key ] = apply_filters( 'advanced-ads-sanitize-condition-' . $type, $_condition );
772 }
773
774 return $conditions;
775 }
776
777 /**
778 * Sanitize id input field(s) for pattern /1,2,3,4/
779 *
780 * @param mixed $cond input string/array.
781 *
782 * @return array/string $cond sanitized string/array
783 */
784 public static function sanitize_condition_idfield( $cond = '' ) {
785 // strip anything that is not comma or number.
786
787 if ( is_array( $cond ) ) {
788 foreach ( $cond as $_key => $_cond ) {
789 $cond[ $_key ] = preg_replace( '#[^0-9,]#', '', $_cond );
790 }
791 } else {
792 $cond = preg_replace( '#[^0-9,]#', '', $cond );
793 }
794
795 return $cond;
796 }
797
798 /**
799 * Sanitize radio input field
800 *
801 * @param string $string input string.
802 *
803 * @return string $string sanitized string.
804 */
805 public static function sanitize_condition_radio( $string = '' ) {
806 // only allow 0, 1 and empty.
807 return preg_replace( '#[^01]#', '', $string );
808 }
809
810 /**
811 * Sanitize comma seperated text input field
812 *
813 * @param mixed $cond input string/array.
814 *
815 * @return array/string $cond sanitized string/array.
816 */
817 public static function sanitize_condition_textvalues( $cond = '' ) {
818 // strip anything that is not comma, alphanumeric, minus and underscore.
819 if ( is_array( $cond ) ) {
820 foreach ( $cond as $_key => $_cond ) {
821 $cond[ $_key ] = preg_replace( '#[^0-9,A-Za-z-_]#', '', $_cond );
822 }
823 } else {
824 $cond = preg_replace( '#[^0-9,A-Za-z-_]#', '', $cond );
825 }
826
827 return $cond;
828 }
829
830 /**
831 * Load wrapper options set with the ad
832 *
833 * @return array $wrapper options array ready to be use in add_wrapper() function.
834 * @since 1.3
835 */
836 protected function load_wrapper_options() {
837 $wrapper = [];
838
839 $position = $this->options( 'output.position', '' );
840 $use_placement_pos = false;
841
842 if ( $this->args['is_top_level'] ) {
843 if ( isset( $this->output['class'] ) && is_array( $this->output['class'] ) ) {
844 $wrapper['class'] = $this->output['class'];
845 }
846 if ( ! empty( $this->args['placement_position'] ) ) {
847 // If not group, Set placement position instead of ad position.
848 $use_placement_pos = true;
849 $position = $this->args['placement_position'];
850 }
851 }
852
853 switch ( $position ) {
854 case 'left':
855 case 'left_float':
856 case 'left_nofloat':
857 $wrapper['style']['float'] = 'left';
858 break;
859 case 'right':
860 case 'right_float':
861 case 'right_nofloat':
862 $wrapper['style']['float'] = 'right';
863 break;
864 case 'center':
865 case 'center_nofloat':
866 case 'center_float':
867 $wrapper['style']['margin-left'] = 'auto';
868 $wrapper['style']['margin-right'] = 'auto';
869
870 if (
871 ( ! $this->width || empty( $this->output['add_wrapper_sizes'] ) )
872 || $use_placement_pos
873 ) {
874 $wrapper['style']['text-align'] = 'center';
875 }
876
877 // add css rule after wrapper to center the ad.
878 break;
879 case 'clearfix':
880 $wrapper['style']['clear'] = 'both';
881 break;
882 }
883
884 // add manual classes.
885 if ( isset( $this->output['wrapper-class'] ) && '' !== $this->output['wrapper-class'] ) {
886 $classes = explode( ' ', $this->output['wrapper-class'] );
887
888 foreach ( $classes as $_class ) {
889 $wrapper['class'][] = sanitize_text_field( $_class );
890 }
891 }
892
893 if ( ! empty( $this->output['margin']['top'] ) ) {
894 $wrapper['style']['margin-top'] = (int) $this->output['margin']['top'] . 'px';
895 }
896 if ( empty( $wrapper['style']['margin-right'] ) && ! empty( $this->output['margin']['right'] ) ) {
897 $wrapper['style']['margin-right'] = (int) $this->output['margin']['right'] . 'px';
898 }
899 if ( ! empty( $this->output['margin']['bottom'] ) ) {
900 $wrapper['style']['margin-bottom'] = (int) $this->output['margin']['bottom'] . 'px';
901 }
902 if ( empty( $wrapper['style']['margin-left'] ) && ! empty( $this->output['margin']['left'] ) ) {
903 $wrapper['style']['margin-left'] = (int) $this->output['margin']['left'] . 'px';
904 }
905
906 if ( ! empty( $this->output['add_wrapper_sizes'] ) ) {
907 if ( ! empty( $this->width ) ) {
908 $wrapper['style']['width'] = $this->width . 'px';
909 }
910 if ( ! empty( $this->height ) ) {
911 $wrapper['style']['height'] = $this->height . 'px';
912 }
913 }
914
915 if ( ! empty( $this->output['clearfix_before'] ) ) {
916 $wrapper['style']['clear'] = 'both';
917 }
918
919 return $wrapper;
920 }
921
922 /**
923 * Add a wrapper arount the ad content if wrapper information are given
924 *
925 * @param string $ad_content content of the ad.
926 *
927 * @return string $wrapper ad within the wrapper
928 * @since 1.1.4
929 */
930 protected function add_wrapper( $ad_content = '' ) {
931 $wrapper_options = apply_filters( 'advanced-ads-output-wrapper-options', $this->wrapper, $this );
932
933 if ( $this->label && ! empty( $wrapper_options['style']['height'] ) ) {
934 // Create another wrapper so that the label does not reduce the height of the ad wrapper.
935 $height = [ 'style' => [ 'height' => $wrapper_options['style']['height'] ] ];
936 unset( $wrapper_options['style']['height'] );
937 $ad_content = '<div' . Advanced_Ads_Utils::build_html_attributes( $height ) . '>' . $ad_content . '</div>';
938 }
939
940 // Adds inline css to the wrapper.
941 if ( ! empty( $this->options['inline-css'] ) && $this->args['is_top_level'] ) {
942 $wrapper_options = $this->inline_css->add_css( $wrapper_options, $this->options['inline-css'], $this->global_output );
943 }
944
945 if (
946 ! defined( 'ADVANCED_ADS_DISABLE_EDIT_BAR' )
947 // Add edit button for users with the appropriate rights.
948 && current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_edit_ads' ) )
949 // We need a wrapper. Check if at least the placement wrapper exists.
950 && ! empty( $this->args['placement_type'] )
951 ) {
952 ob_start();
953 include ADVADS_BASE_PATH . 'public/views/ad-edit-bar.php';
954 $ad_content = trim( ob_get_clean() ) . $ad_content;
955 }
956
957 if ( ( ! isset( $this->output['wrapper-id'] ) || '' === $this->output['wrapper-id'] )
958 && [] === $wrapper_options || ! is_array( $wrapper_options ) ) {
959 return $this->label . $ad_content;
960 }
961
962 // create unique id if not yet given.
963 if ( empty( $wrapper_options['id'] ) ) {
964 $wrapper_options['id'] = $this->create_wrapper_id();
965 $this->wrapper['id'] = $wrapper_options['id'];
966 }
967
968 $wrapper_element = ! empty( $this->args['inline_wrapper_element'] ) ? 'span' : 'div';
969
970 // build the box
971 $wrapper = '<' . $wrapper_element . Advanced_Ads_Utils::build_html_attributes( array_merge(
972 $wrapper_options,
973 isset( $this->output['wrapper_attrs'] ) ? $this->output['wrapper_attrs'] : []
974 ) ) . '>';
975 $wrapper .= $this->label;
976 $wrapper .= apply_filters( 'advanced-ads-output-wrapper-before-content', '', $this );
977 $wrapper .= $ad_content;
978 $wrapper .= apply_filters( 'advanced-ads-output-wrapper-after-content', '', $this );
979 $wrapper .= '</' . $wrapper_element . '>';
980
981 return $wrapper;
982 }
983
984 /**
985 * Create a random wrapper id
986 *
987 * @return string $id random id string
988 * @since 1.1.4
989 */
990 private function create_wrapper_id() {
991
992 if ( isset( $this->output['wrapper-id'] ) ) {
993 $id = sanitize_key( $this->output['wrapper-id'] );
994 if ( '' !== $id ) {
995 return $id;
996 }
997 }
998
999 $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix();
1000
1001 return $prefix . mt_rand();
1002 }
1003
1004 /**
1005 * Create an "Advertisement" label if conditions are met.
1006 */
1007 public function maybe_create_label() {
1008 $placement_state = isset( $this->args['ad_label'] ) ? $this->args['ad_label'] : 'default';
1009
1010 $label = Advanced_Ads::get_instance()->get_label( $placement_state );
1011
1012 if ( $this->args['is_top_level'] && $label ) {
1013 $this->label = $label;
1014 }
1015 }
1016
1017 /**
1018 * Get the ad url.
1019 *
1020 * @return string
1021 */
1022 private function get_url() {
1023 $this->url = $this->options( 'url' );
1024
1025 // If the tracking add-on is not active.
1026 if ( ! defined( 'AAT_VERSION' ) ) {
1027 global $pagenow;
1028 // If this is not the ad edit page.
1029 if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
1030 // Remove placeholders.
1031 $this->url = str_replace(
1032 [
1033 '[POST_ID]',
1034 '[POST_SLUG]',
1035 '[CAT_SLUG]',
1036 '[AD_ID]',
1037 ],
1038 '',
1039 $this->url
1040 );
1041 }
1042 }
1043
1044 return $this->url;
1045 }
1046
1047 }
1048