Advanced Ads - Version 1.20.0-rc.1

Version Description

Download this release

Release Info

Developer advancedads
Plugin Icon 128x128 Advanced Ads
Version 1.20.0-rc.1
Comparing to
See all releases

Code changes from version 1.19.1 to 1.20.0-rc.1

Files changed (43) hide show
  1. admin/assets/css/admin.css +47 -7
  2. admin/class-advanced-ads-admin.php +13 -37
  3. admin/includes/class-ad-groups-list.php +3 -3
  4. admin/includes/class-ad-type.php +5 -3
  5. admin/includes/class-list-filters.php +1 -1
  6. admin/views/ad-list-details-column.php +5 -0
  7. admin/views/ad-list-timing-column.php +2 -2
  8. admin/views/ad-submitbox-meta.php +1 -2
  9. admin/views/settings/general/advanced-js.php +2 -2
  10. admin/views/settings/general/link-target.php +1 -1
  11. advanced-ads.php +2 -2
  12. classes/ad-ajax.php +68 -28
  13. classes/ad.php +16 -5
  14. classes/ad_group.php +4 -3
  15. classes/ad_type_image.php +1 -1
  16. classes/compatibility.php +16 -0
  17. classes/frontend_checks.php +17 -10
  18. classes/plugin.php +17 -11
  19. classes/utils.php +52 -0
  20. languages/advanced-ads.pot +138 -99
  21. lib/composer/ClassLoader.php +1 -1
  22. modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php +3 -0
  23. modules/ads-txt/admin/views/setting-additional-content.php +27 -1
  24. modules/ads-txt/includes/class-advanced-ads-ads-txt-strategy.php +8 -4
  25. modules/ads-txt/includes/class-advanced-ads-ads-txt-utils.php +50 -0
  26. modules/ads-txt/public/class-advanced-ads-ads-txt-public.php +13 -3
  27. modules/gadsense/includes/class-ad-type-adsense.php +8 -5
  28. modules/gadsense/public/public.php +36 -24
  29. modules/gadsense/public/templates/page-level.php +27 -31
  30. modules/privacy/admin/admin.php +111 -94
  31. modules/privacy/admin/assets/js/privacy.js +15 -0
  32. modules/privacy/admin/views/setting-ad-ignore-consent.php +22 -7
  33. modules/privacy/admin/views/setting-consent-method.php +0 -18
  34. modules/privacy/admin/views/setting-enable.php +0 -7
  35. modules/privacy/admin/views/setting-general.php +102 -0
  36. modules/privacy/classes/class-privacy.php +205 -0
  37. modules/privacy/classes/plugin.php +0 -168
  38. modules/privacy/config.php +4 -7
  39. modules/privacy/main.php +6 -4
  40. public/assets/js/advanced.js +1 -1
  41. public/assets/js/advanced.orig.js +163 -47
  42. public/class-advanced-ads.php +45 -3
  43. readme.txt +18 -3
admin/assets/css/admin.css CHANGED
@@ -42,8 +42,9 @@
42
  /**
43
  * AD OVERVIEW PAGE
44
  */
45
- .ad_details img { float: left; margin-right: 10px; }
46
- .ad_details img + p { float: left; }
 
47
  .post-type-advanced_ads .subsubsub .draft a { background-color: #FCE7C0; }
48
  .post-type-advanced_ads .subsubsub .pending a { background-color: #FFDEDE; }
49
  .post-type-advanced_ads .wp-list-table .status-draft { background-color: #FCE7C0; }
@@ -174,8 +175,8 @@ select + .advads-conditions-single { padding-left: 10px }
174
 
175
  /* option lists */
176
  .advads-option-list { overflow: hidden; }
177
- .advads-option-list > .label { display: block; float: left; width: 10em; padding: 10px; font-weight: bold; text-transform: capitalize; color: #444; text-align: left; font-size: 100%; }
178
- .advads-option-list > .label + div { display: block; float: left; padding: 10px; max-width: calc(100% - (10em + 40px)); }
179
  .advads-option-list > .label + div.hidden { display: none; }
180
  .advads-option-list > hr { clear: both; float: none; display: block; }
181
  .advads-option-list div[style*="display: none"] + hr { display: none; }
@@ -200,7 +201,7 @@ select + .advads-conditions-single { padding-left: 10px }
200
  .advads-timestamp p { margin: 8px 0 6px; }
201
  .advads-timestamp input { text-align: center; }
202
  .advads-timestamp select { font-size: 12px; }
203
- .advads-timestamp .advads-jj, .advads-timestamp .advads-timestamp .advads-hh, .advads-timestamp .advads-mn { width: 2em; }
204
  .advads-timestamp .advads-aa, .advads-timestamp .advads-jj, .advads-timestamp .advads-hh, .advads-timestamp .advads-mn { padding: 6px 1px; font-size: 12px; line-height: 1.16666666; }
205
  .advads-timestamp .advads-aa { width: 3.4em; }
206
 
@@ -397,7 +398,7 @@ tr:hover .on-hover { display: block; }
397
  #advads-tabs .nav-tab-active { background: #fff; border-bottom: 0; margin-top: 1px; }
398
  .advads-tab { display: none; padding: 5px 15px; overflow: hidden; background-color: #fff; border: 1px solid #ccc; border-top: none; }
399
  .advads-tab input[type="checkbox"] { margin-right: 8px; }
400
- .advads-tab .form-table th, .advads-tab, .advads-tab .form-table td { padding-bottom: 30px; vertical-align: top; }
401
  .advads-tab.active { display: block; }
402
  .advads-license-activate-active { color: #46b450; }
403
  .advads-license-activate-error { color: #dc3232; }
@@ -581,4 +582,43 @@ tr.advads-clickable-row:hover{
581
 
582
  /* Set max-width for wide select elements */
583
  .advads-conditions-select-wrap { display: inline-block; max-width: 100%; }
584
- .advads-conditions-select-wrap select { width: 100%; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  /**
43
  * AD OVERVIEW PAGE
44
  */
45
+ .ad_details img { float: left; margin-right: 10px; width: calc(50% - 10px); }
46
+ .ad_details img ~ p { float: left; width: 50%; }
47
+ .advads-ad-size { white-space: nowrap; }
48
  .post-type-advanced_ads .subsubsub .draft a { background-color: #FCE7C0; }
49
  .post-type-advanced_ads .subsubsub .pending a { background-color: #FFDEDE; }
50
  .post-type-advanced_ads .wp-list-table .status-draft { background-color: #FCE7C0; }
175
 
176
  /* option lists */
177
  .advads-option-list { overflow: hidden; }
178
+ .advads-option-list > .label { display: block; padding: 10px; font-weight: bold; text-transform: capitalize; color: #444; text-align: left; font-size: 100%; }
179
+ .advads-option-list > .label + div { display: block; padding: 10px; }
180
  .advads-option-list > .label + div.hidden { display: none; }
181
  .advads-option-list > hr { clear: both; float: none; display: block; }
182
  .advads-option-list div[style*="display: none"] + hr { display: none; }
201
  .advads-timestamp p { margin: 8px 0 6px; }
202
  .advads-timestamp input { text-align: center; }
203
  .advads-timestamp select { font-size: 12px; }
204
+ .advads-timestamp .advads-jj, .advads-timestamp .advads-hh, .advads-timestamp .advads-mn { width: 2em; }
205
  .advads-timestamp .advads-aa, .advads-timestamp .advads-jj, .advads-timestamp .advads-hh, .advads-timestamp .advads-mn { padding: 6px 1px; font-size: 12px; line-height: 1.16666666; }
206
  .advads-timestamp .advads-aa { width: 3.4em; }
207
 
398
  #advads-tabs .nav-tab-active { background: #fff; border-bottom: 0; margin-top: 1px; }
399
  .advads-tab { display: none; padding: 5px 15px; overflow: hidden; background-color: #fff; border: 1px solid #ccc; border-top: none; }
400
  .advads-tab input[type="checkbox"] { margin-right: 8px; }
401
+ .advads-tab .form-table th, .advads-tab, .advads-tab .form-table td { padding-top: 15px; padding-bottom: 30px; vertical-align: top; }
402
  .advads-tab.active { display: block; }
403
  .advads-license-activate-active { color: #46b450; }
404
  .advads-license-activate-error { color: #dc3232; }
582
 
583
  /* Set max-width for wide select elements */
584
  .advads-conditions-select-wrap { display: inline-block; max-width: 100%; }
585
+ .advads-conditions-select-wrap select { width: 100%; }
586
+
587
+ /* table layout, mobile by default */
588
+ .advads-option-table { width:100%; }
589
+ .advads-option-table thead { display: none; }
590
+ .advads-option-table tbody td { display: block; }
591
+ .advads-option-table tbody td:before { /* display a label that is in the "data-th" attribute of the td tag */
592
+ content: attr( data-th );
593
+ display: inline-block;
594
+ width: 32%;
595
+ }
596
+
597
+ /**
598
+ * RESPONSIVE BEHAVIOR
599
+ * mobile is default
600
+ * we actually define behavior for larger screens here
601
+ */
602
+ @media screen and (min-width: 600px) {
603
+ /* show ad options below the label instead of next to them */
604
+ .advads-option-list > .label { float: left; width: 10em; }
605
+ .advads-option-list > .label + div { float: left; max-width: calc(100% - (10em + 40px)); }
606
+
607
+ /* tables */
608
+ .advads-option-table { width:100%; }
609
+ .advads-option-table thead { display: table-header-group; }
610
+ .advads-option-table tbody td { display: table-cell; }
611
+ .advads-option-table tbody td:before { display: none; }
612
+ }
613
+
614
+
615
+ /**
616
+ MODULE ACTIVATION
617
+ */
618
+ .advads-sub-settings {
619
+ display: none;
620
+ }
621
+
622
+ input.advads-has-sub-settings:checked ~ .advads-sub-settings {
623
+ display: block;
624
+ }
admin/class-advanced-ads-admin.php CHANGED
@@ -308,53 +308,29 @@ class Advanced_Ads_Admin {
308
  }
309
 
310
  /**
311
- * Get DateTimeZone object for the WP installation
 
 
 
 
 
312
  */
313
  public static function get_wp_timezone() {
314
- $_time_zone = get_option( 'timezone_string' );
315
- $time_zone = new DateTimeZone( 'UTC' );
316
- if ( $_time_zone ) {
317
- $time_zone = new DateTimeZone( $_time_zone );
318
- } else {
319
- $gmt_offset = floatval( get_option( 'gmt_offset' ) );
320
- $sign = ( 0 > $gmt_offset ) ? '-' : '+';
321
- $int = floor( abs( $gmt_offset ) );
322
- $frac = abs( $gmt_offset ) - $int;
323
-
324
- $gmt = '';
325
- if ( $gmt_offset ) {
326
- $gmt .= $sign . zeroise( $int, 2 ) . ':' . zeroise( 60 * $frac, 2 );
327
- $time_zone = date_create( '2017-10-01T12:00:00' . $gmt )->getTimezone();
328
- }
329
- }
330
-
331
- return $time_zone;
332
  }
333
 
334
  /**
335
- * Get literal expression of timezone
336
  *
337
  * @param DateTimeZone $date_time_zone the DateTimeZone object to get literal value from.
338
  *
339
  * @return string time zone.
 
 
 
340
  */
341
- public static function timezone_get_name( $date_time_zone ) {
342
- if ( $date_time_zone instanceof DateTimeZone ) {
343
- $time_zone = timezone_name_get( $date_time_zone );
344
- if ( 'UTC' === $time_zone ) {
345
- return 'UTC+0';
346
- }
347
- if ( false === strpos( $time_zone, '/' ) ) {
348
- $time_zone = 'UTC' . $time_zone;
349
- } else {
350
- // translators: time zone name.
351
- $time_zone = sprintf( __( 'time of %s', 'advanced-ads' ), $time_zone );
352
- }
353
-
354
- return $time_zone;
355
- }
356
-
357
- return 'UTC+0';
358
  }
359
 
360
  /**
308
  }
309
 
310
  /**
311
+ * Get DateTimeZone object for the WP installation
312
+ *
313
+ * @return DateTimeZone object set in WP settings.
314
+ * @see Advanced_Ads_Utils::get_wp_timezone()
315
+ *
316
+ * @deprecated This is also used outside of admin as well as other plugins.
317
  */
318
  public static function get_wp_timezone() {
319
+ return Advanced_Ads_Utils::get_wp_timezone();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  }
321
 
322
  /**
323
+ * Get literal expression of timezone.
324
  *
325
  * @param DateTimeZone $date_time_zone the DateTimeZone object to get literal value from.
326
  *
327
  * @return string time zone.
328
+ * @see Advanced_Ads_Utils::get_timezone_name()
329
+ *
330
+ * @deprecated This is also used outside of admin as well as other plugins.
331
  */
332
+ public static function timezone_get_name( DateTimeZone $date_time_zone ) {
333
+ return Advanced_Ads_Utils::get_timezone_name();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
336
  /**
admin/includes/class-ad-groups-list.php CHANGED
@@ -189,16 +189,16 @@ class Advanced_Ads_Groups_List {
189
 
190
 
191
  if ( $tz_option ) {
192
- $expiry_date->setTimezone( Advanced_Ads_Admin::get_wp_timezone() );
193
  } else {
194
- $tz_name = Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_wp_timezone() );
195
  $tz_offset = substr( $tz_name, 3 );
196
  $off_time = date_create( '2017-09-21 T10:44:02' . $tz_offset );
197
  $offset_in_sec = date_offset_get( $off_time );
198
  $expiry_date = date_create( '@' . ( $expiry + $offset_in_sec ) );
199
  }
200
 
201
- $tz = ' ( ' . Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_wp_timezone() ) . ' )';
202
 
203
  if ( $expiry > time() ) {
204
  // translators: %s is a date.
189
 
190
 
191
  if ( $tz_option ) {
192
+ $expiry_date->setTimezone( Advanced_Ads_Utils::get_wp_timezone() );
193
  } else {
194
+ $tz_name = Advanced_Ads_Utils::get_timezone_name();
195
  $tz_offset = substr( $tz_name, 3 );
196
  $off_time = date_create( '2017-09-21 T10:44:02' . $tz_offset );
197
  $offset_in_sec = date_offset_get( $off_time );
198
  $expiry_date = date_create( '@' . ( $expiry + $offset_in_sec ) );
199
  }
200
 
201
+ $tz = ' ( ' . Advanced_Ads_Utils::get_timezone_name() . ' )';
202
 
203
  if ( $expiry > time() ) {
204
  // translators: %s is a date.
admin/includes/class-ad-type.php CHANGED
@@ -325,6 +325,8 @@ class Advanced_Ads_Admin_Ad_Type {
325
 
326
  $size = apply_filters( 'advanced-ads-list-ad-size', $size, $ad );
327
 
 
 
328
  include ADVADS_BASE_PATH . 'admin/views/ad-list-details-column.php';
329
  }
330
  }
@@ -603,7 +605,7 @@ class Advanced_Ads_Admin_Ad_Type {
603
  if ( ! $valid_date ) {
604
  $ad->expiry_date = 0;
605
  } else {
606
- $gm_date = date_create( $expiration_date, Advanced_Ads_Admin::get_wp_timezone() );
607
  $gm_date->setTimezone( new DateTimeZone( 'UTC' ) );
608
  $gm_date = $gm_date->format( 'Y-m-d-H-i' );
609
  list( $year, $month, $day, $hour, $minute ) = explode( '-', $gm_date );
@@ -781,9 +783,9 @@ class Advanced_Ads_Admin_Ad_Type {
781
  $exp_time = clone $utc_time;
782
 
783
  if ( $tz_option ) {
784
- $exp_time->setTimezone( Advanced_Ads_Admin::get_wp_timezone() );
785
  } else {
786
- $tz_name = Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_wp_timezone() );
787
  $tz_offset = substr( $tz_name, 3 );
788
  $off_time = date_create( $utc_time->format( 'Y-m-d\TH:i:s' ) . $tz_offset );
789
  $offset_in_sec = date_offset_get( $off_time );
325
 
326
  $size = apply_filters( 'advanced-ads-list-ad-size', $size, $ad );
327
 
328
+ $privacy_overriden = ! empty( Advanced_Ads_Privacy::get_instance()->options()['enabled'] ) && ! empty( $ad->options()['privacy']['ignore-consent'] );
329
+
330
  include ADVADS_BASE_PATH . 'admin/views/ad-list-details-column.php';
331
  }
332
  }
605
  if ( ! $valid_date ) {
606
  $ad->expiry_date = 0;
607
  } else {
608
+ $gm_date = date_create( $expiration_date, Advanced_Ads_Utils::get_wp_timezone() );
609
  $gm_date->setTimezone( new DateTimeZone( 'UTC' ) );
610
  $gm_date = $gm_date->format( 'Y-m-d-H-i' );
611
  list( $year, $month, $day, $hour, $minute ) = explode( '-', $gm_date );
783
  $exp_time = clone $utc_time;
784
 
785
  if ( $tz_option ) {
786
+ $exp_time->setTimezone( Advanced_Ads_Utils::get_wp_timezone() );
787
  } else {
788
+ $tz_name = Advanced_Ads_Utils::get_timezone_name();
789
  $tz_offset = substr( $tz_name, 3 );
790
  $off_time = date_create( $utc_time->format( 'Y-m-d\TH:i:s' ) . $tz_offset );
791
  $offset_in_sec = date_offset_get( $off_time );
admin/includes/class-list-filters.php CHANGED
@@ -466,7 +466,7 @@ class Advanced_Ads_Ad_List_Filters {
466
  foreach ( $the_list as $post ) {
467
  if ( isset( $this->all_ads_options[ $post->ID ] ) ) {
468
  $option = $this->all_ads_options[ $post->ID ];
469
- if ( $request['adtype'] === $option['type'] ) {
470
  $new_posts[] = $post;
471
  }
472
  }
466
  foreach ( $the_list as $post ) {
467
  if ( isset( $this->all_ads_options[ $post->ID ] ) ) {
468
  $option = $this->all_ads_options[ $post->ID ];
469
+ if ( isset( $option['type'] ) && $request['adtype'] === $option['type'] ) {
470
  $new_posts[] = $post;
471
  }
472
  }
admin/views/ad-list-details-column.php CHANGED
@@ -14,6 +14,11 @@
14
  <p class="advads-ad-size"><?php echo esc_attr( $size ); ?></p>
15
  <?php
16
  endif;
 
 
 
 
 
17
  do_action( 'advanced-ads-ad-list-details-column-after', $ad );
18
  ?>
19
  </div>
14
  <p class="advads-ad-size"><?php echo esc_attr( $size ); ?></p>
15
  <?php
16
  endif;
17
+ ?>
18
+ <?php if ( $privacy_overriden ) : ?>
19
+ <p><?php esc_html_e( 'Consent disabled', 'advanced-ads' ); ?></p>
20
+ <?php endif; ?>
21
+ <?php
22
  do_action( 'advanced-ads-ad-list-details-column-after', $ad );
23
  ?>
24
  </div>
admin/views/ad-list-timing-column.php CHANGED
@@ -37,12 +37,12 @@
37
 
38
  if ( $tz_option ) {
39
 
40
- $expiry_date->setTimezone( Advanced_Ads_Admin::get_wp_timezone() );
41
  $expiry_date_string = $expiry_date->format( $expiry_date_format );
42
 
43
  } else {
44
 
45
- $tz_name = Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_wp_timezone() );
46
  $tz_offset = substr( $tz_name, 3 );
47
  $off_time = date_create( $expiry_date->format( 'Y-m-d\TH:i:s' ) . $tz_offset );
48
  $offset_in_sec = date_offset_get( $off_time );
37
 
38
  if ( $tz_option ) {
39
 
40
+ $expiry_date->setTimezone( Advanced_Ads_Utils::get_wp_timezone() );
41
  $expiry_date_string = $expiry_date->format( $expiry_date_format );
42
 
43
  } else {
44
 
45
+ $tz_name = Advanced_Ads_Utils::get_timezone_name();
46
  $tz_offset = substr( $tz_name, 3 );
47
  $off_time = date_create( $expiry_date->format( 'Y-m-d\TH:i:s' ) . $tz_offset );
48
  $offset_in_sec = date_offset_get( $off_time );
admin/views/ad-submitbox-meta.php CHANGED
@@ -4,7 +4,6 @@
4
  *
5
  * @var int $curr_month current month index;
6
  */
7
- $timezone_name = Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_wp_timezone() );
8
  ?><div id="advanced-ads-expiry-date" class="misc-pub-section curtime misc-pub-curtime">
9
  <label onclick="advads_toggle_box('#advanced-ads-expiry-date-enable', '#advanced-ads-expiry-date .inner')">
10
  <input type="checkbox" id="advanced-ads-expiry-date-enable" name="advanced_ad[expiry_date][enabled]" value="1" <?php checked( $enabled, 1 ); ?>/><?php esc_html_e( 'Set expiry date', 'advanced-ads' ); ?>
@@ -46,6 +45,6 @@ $timezone_name = Advanced_Ads_Admin::timezone_get_name( Advanced_Ads_Admin::get_
46
  // phpcs:enable
47
  ?>
48
  </fieldset>
49
- (<?php echo esc_html( $timezone_name ); ?>)
50
  </div>
51
  </div>
4
  *
5
  * @var int $curr_month current month index;
6
  */
 
7
  ?><div id="advanced-ads-expiry-date" class="misc-pub-section curtime misc-pub-curtime">
8
  <label onclick="advads_toggle_box('#advanced-ads-expiry-date-enable', '#advanced-ads-expiry-date .inner')">
9
  <input type="checkbox" id="advanced-ads-expiry-date-enable" name="advanced_ad[expiry_date][enabled]" value="1" <?php checked( $enabled, 1 ); ?>/><?php esc_html_e( 'Set expiry date', 'advanced-ads' ); ?>
45
  // phpcs:enable
46
  ?>
47
  </fieldset>
48
+ (<?php echo esc_html( Advanced_Ads_Utils::get_timezone_name() ); ?>)
49
  </div>
50
  </div>
admin/views/settings/general/advanced-js.php CHANGED
@@ -14,8 +14,8 @@ printf(
14
  __( 'Enable advanced JavaScript functions (<a href="%s" target="_blank">here</a>). Some features and add-ons might override this setting if they need features from this file.', 'advanced-ads' ),
15
  array(
16
  'a' => array(
17
- 'href' => array(),
18
- 'taret' => array(),
19
  ),
20
  )
21
  ),
14
  __( 'Enable advanced JavaScript functions (<a href="%s" target="_blank">here</a>). Some features and add-ons might override this setting if they need features from this file.', 'advanced-ads' ),
15
  array(
16
  'a' => array(
17
+ 'href' => array(),
18
+ 'target' => array(),
19
  ),
20
  )
21
  ),
admin/views/settings/general/link-target.php CHANGED
@@ -3,4 +3,4 @@
3
  checked( 1, $target );
4
  ?>
5
  />
6
- <p class="description"><?php echo wp_kses( __( 'Open programatically created links in a new window (use <code>target="_blank"</code>)', 'advanced-ads' ), array( 'code' => array() ) ); ?></p>
3
  checked( 1, $target );
4
  ?>
5
  />
6
+ <p class="description"><?php echo wp_kses( __( 'Open programmatically created links in a new window (use <code>target="_blank"</code>)', 'advanced-ads' ), array( 'code' => array() ) ); ?></p>
advanced-ads.php CHANGED
@@ -12,7 +12,7 @@
12
  * Plugin Name: Advanced Ads
13
  * Plugin URI: https://wpadvancedads.com
14
  * Description: Manage and optimize your ads in WordPress
15
- * Version: 1.19.1
16
  * Author: Thomas Maier, Advanced Ads GmbH
17
  * Author URI: https://wpadvancedads.com
18
  * Text Domain: advanced-ads
@@ -39,7 +39,7 @@ define( 'ADVADS_BASE_DIR', dirname( ADVADS_BASE ) ); // directory of the plugin
39
  // general and global slug, e.g. to store options in WP.
40
  define( 'ADVADS_SLUG', 'advanced-ads' );
41
  define( 'ADVADS_URL', 'https://wpadvancedads.com/' );
42
- define( 'ADVADS_VERSION', '1.19.1' );
43
 
44
  // Autoloading, modules and functions.
45
 
12
  * Plugin Name: Advanced Ads
13
  * Plugin URI: https://wpadvancedads.com
14
  * Description: Manage and optimize your ads in WordPress
15
+ * Version: 1.20.0-rc.1
16
  * Author: Thomas Maier, Advanced Ads GmbH
17
  * Author URI: https://wpadvancedads.com
18
  * Text Domain: advanced-ads
39
  // general and global slug, e.g. to store options in WP.
40
  define( 'ADVADS_SLUG', 'advanced-ads' );
41
  define( 'ADVADS_URL', 'https://wpadvancedads.com/' );
42
+ define( 'ADVADS_VERSION', '1.20.0-rc.1' );
43
 
44
  // Autoloading, modules and functions.
45
 
classes/ad-ajax.php CHANGED
@@ -108,44 +108,47 @@ class Advanced_Ads_Ajax {
108
  $arguments = stripslashes( $arguments );
109
  $arguments = json_decode( $arguments, true );
110
  }
 
111
  if ( ! empty( $request['elementId'] ) ) {
112
  $arguments['cache_busting_elementid'] = $request['elementId'];
113
  }
114
 
115
- $response = array();
116
- if ( isset( $methods[ $method ] ) && isset( $id ) ) {
117
- $advads = Advanced_Ads::get_instance();
118
- $l = count( $advads->current_ads );
119
-
120
- // Build content.
121
- $content = $selector->get_ad_by_method( $id, $method, $arguments );
122
- $ad_ids = array_slice( $advads->current_ads, $l ); // Ads loaded by this request.
123
-
124
- $r = array(
125
- 'status' => 'success',
126
- 'item' => $content,
127
- 'id' => $id,
128
- 'method' => $method,
129
- 'ads' => $ad_ids,
130
- 'blog_id' => get_current_blog_id(),
131
- );
132
-
133
- return apply_filters(
134
- 'advanced-ads-cache-busting-item',
135
- $r,
136
- array(
137
- 'id' => $id,
138
- 'method' => $method,
139
- 'args' => $arguments,
140
- )
141
- );
142
- } else {
143
  // Report error.
144
  return array(
145
  'status' => 'error',
146
  'message' => 'No valid ID or METHOD found.',
147
  );
148
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
 
151
  /**
@@ -196,4 +199,41 @@ class Advanced_Ads_Ajax {
196
 
197
  die();
198
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
108
  $arguments = stripslashes( $arguments );
109
  $arguments = json_decode( $arguments, true );
110
  }
111
+
112
  if ( ! empty( $request['elementId'] ) ) {
113
  $arguments['cache_busting_elementid'] = $request['elementId'];
114
  }
115
 
116
+ if ( ! array_key_exists( $method, $methods ) || empty( $id ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  // Report error.
118
  return array(
119
  'status' => 'error',
120
  'message' => 'No valid ID or METHOD found.',
121
  );
122
  }
123
+
124
+ add_filter( 'advanced-ads-can-display', array( $this, 'can_display_by_consent' ), 10, 2 );
125
+ $content = $selector->get_ad_by_method( $id, $method, $arguments );
126
+
127
+ if ( empty( $content ) ) {
128
+ return array(
129
+ 'status' => 'error',
130
+ 'message' => 'No displayable ad found for privacy settings.',
131
+ );
132
+ }
133
+
134
+ $response = array(
135
+ 'status' => 'success',
136
+ 'item' => $content,
137
+ 'id' => $id,
138
+ 'method' => $method,
139
+ 'ads' => Advanced_Ads::get_instance()->current_ads,
140
+ 'blog_id' => get_current_blog_id(),
141
+ );
142
+
143
+ return apply_filters(
144
+ 'advanced-ads-cache-busting-item',
145
+ $response,
146
+ array(
147
+ 'id' => $id,
148
+ 'method' => $method,
149
+ 'args' => $arguments,
150
+ )
151
+ );
152
  }
153
 
154
  /**
199
 
200
  die();
201
  }
202
+
203
+ /**
204
+ * Check if AJAX ad can be displayed, with consent information sent in request.
205
+ *
206
+ * @param bool $can_display Whether this ad can be displayed.
207
+ * @param Advanced_Ads_Ad $ad The ad object.
208
+ *
209
+ * @return bool
210
+ */
211
+ public function can_display_by_consent( $can_display, $ad ) {
212
+ // already false, honor this.
213
+ if ( ! $can_display ) {
214
+ return $can_display;
215
+ }
216
+
217
+ // If consent is overridden for the ad.
218
+ if ( ! empty( $ad->options()['privacy']['ignore-consent'] ) ) {
219
+ return true;
220
+ }
221
+
222
+ // if privacy module is not active, we can display.
223
+ $privacy_options = Advanced_Ads_Privacy::get_instance()->options();
224
+ if ( ! isset( $privacy_options['enabled'] ) ) {
225
+ return true;
226
+ }
227
+
228
+ $consent_state = sanitize_text_field( $_REQUEST['consent'] );
229
+ if ( $consent_state === 'not_needed' ) {
230
+ return true;
231
+ }
232
+ // Don't display ad if tcf is used and the consent state is anything different than accepted.
233
+ if ( $privacy_options['consent-method'] === 'iab_tcf_20' && $consent_state !== 'accepted' ) {
234
+ return in_array( $ad->type, array( 'image', 'dummy' ), true );
235
+ }
236
+
237
+ return true;
238
+ }
239
  }
classes/ad.php CHANGED
@@ -681,10 +681,6 @@ class Advanced_Ads_Ad {
681
  // filter to manipulate the output before the wrapper is added
682
  $output = apply_filters( 'advanced-ads-output-inside-wrapper', $output, $this );
683
 
684
- if ( $this->label ) {
685
- $output = $this->label . $output;
686
- }
687
-
688
  // build wrapper around the ad.
689
  $output = $this->add_wrapper( $output );
690
 
@@ -914,6 +910,18 @@ class Advanced_Ads_Ad {
914
  $this->wrapper['id'] = $wrapper_options['id'];
915
  }
916
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  // add edit button for users with the appropriate rights.
918
  if ( ! defined( 'ADVANCED_ADS_DISABLE_EDIT_BAR' ) && current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_edit_ads' ) ) ) {
919
  ob_start();
@@ -921,6 +929,8 @@ class Advanced_Ads_Ad {
921
  $ad_content = ob_get_clean() . $ad_content;
922
  }
923
 
 
 
924
  // build the box
925
  $wrapper = '<div' . Advanced_Ads_Utils::build_html_attributes( $wrapper_options ) . '>';
926
  $wrapper .= apply_filters( 'advanced-ads-output-wrapper-before-content', '', $this );
@@ -958,7 +968,8 @@ class Advanced_Ads_Ad {
958
  $placement_state = isset( $this->args['ad_label'] ) ? $this->args['ad_label'] : 'default';
959
 
960
  $label = Advanced_Ads::get_instance()->get_label( $placement_state );
961
- if ( 'group' !== $this->type && $label ) {
 
962
  $this->label = $label;
963
  }
964
  }
681
  // filter to manipulate the output before the wrapper is added
682
  $output = apply_filters( 'advanced-ads-output-inside-wrapper', $output, $this );
683
 
 
 
 
 
684
  // build wrapper around the ad.
685
  $output = $this->add_wrapper( $output );
686
 
910
  $this->wrapper['id'] = $wrapper_options['id'];
911
  }
912
 
913
+ if ( $this->label && ! empty( $wrapper_options['style']['height'] ) ) {
914
+ // Create another wrapper so that the label does not reduce the height of the ad wrapper.
915
+ $height = array( 'style' => array( 'height' => $wrapper_options['style']['height'] ) );
916
+ unset( $wrapper_options['style']['height'] );
917
+ $ad_content = $this->label
918
+ . '<div' . Advanced_Ads_Utils::build_html_attributes( $height ) . '>'
919
+ . $ad_content
920
+ . '</div>';
921
+ } else {
922
+ $ad_content = $this->label . $ad_content;
923
+ }
924
+
925
  // add edit button for users with the appropriate rights.
926
  if ( ! defined( 'ADVANCED_ADS_DISABLE_EDIT_BAR' ) && current_user_can( Advanced_Ads_Plugin::user_cap( 'advanced_ads_edit_ads' ) ) ) {
927
  ob_start();
929
  $ad_content = ob_get_clean() . $ad_content;
930
  }
931
 
932
+
933
+
934
  // build the box
935
  $wrapper = '<div' . Advanced_Ads_Utils::build_html_attributes( $wrapper_options ) . '>';
936
  $wrapper .= apply_filters( 'advanced-ads-output-wrapper-before-content', '', $this );
968
  $placement_state = isset( $this->args['ad_label'] ) ? $this->args['ad_label'] : 'default';
969
 
970
  $label = Advanced_Ads::get_instance()->get_label( $placement_state );
971
+
972
+ if ( $this->args['is_top_level'] && $label ) {
973
  $this->label = $label;
974
  }
975
  }
classes/ad_group.php CHANGED
@@ -600,12 +600,13 @@ class Advanced_Ads_Group {
600
  private function create_wrapper() {
601
  $this->wrapper = array();
602
 
603
- // Add label.
604
- $placement_state = isset( $this->ad_args['ad_label'] ) ? $this->ad_args['ad_label'] : 'default';
605
- $this->label = Advanced_Ads::get_instance()->get_label( $placement_state );
606
 
607
 
608
  if ( $this->ad_args['is_top_level'] ) {
 
 
 
 
609
  // Add placement class.
610
  if ( isset( $this->ad_args['output']['class'] ) && is_array( $this->ad_args['output']['class'] ) ) {
611
  $this->wrapper['class'] = $this->ad_args['output']['class'];
600
  private function create_wrapper() {
601
  $this->wrapper = array();
602
 
 
 
 
603
 
604
 
605
  if ( $this->ad_args['is_top_level'] ) {
606
+ // Add label.
607
+ $placement_state = isset( $this->ad_args['ad_label'] ) ? $this->ad_args['ad_label'] : 'default';
608
+ $this->label = Advanced_Ads::get_instance()->get_label( $placement_state );
609
+
610
  // Add placement class.
611
  if ( isset( $this->ad_args['output']['class'] ) && is_array( $this->ad_args['output']['class'] ) ) {
612
  $this->wrapper['class'] = $this->ad_args['output']['class'];
classes/ad_type_image.php CHANGED
@@ -146,7 +146,7 @@ class Advanced_Ads_Ad_Type_Image extends Advanced_Ads_Ad_Type_Abstract {
146
  $style = '' !== $style ? 'style="' . $style . '"' : '';
147
 
148
  $more_attributes = apply_filters( 'advanced-ads-ad-image-tag-attributes', $more_attributes );
149
- $more_attributes .= $hwstring . ' ' . $style;
150
  $img = sprintf( '<img src="%s" alt="%s" %s />', esc_url( $src ), esc_attr( $alt ), $more_attributes );
151
 
152
  // Add 'loading' attribute if applicable, available from WP 5.5.
146
  $style = '' !== $style ? 'style="' . $style . '"' : '';
147
 
148
  $more_attributes = apply_filters( 'advanced-ads-ad-image-tag-attributes', $more_attributes );
149
+ $more_attributes .= ' ' . $hwstring . ' ' . $style;
150
  $img = sprintf( '<img src="%s" alt="%s" %s />', esc_url( $src ), esc_attr( $alt ), $more_attributes );
151
 
152
  // Add 'loading' attribute if applicable, available from WP 5.5.
classes/compatibility.php CHANGED
@@ -29,6 +29,8 @@ class Advanced_Ads_Compatibility {
29
  add_filter( 'wpseo_sitemap_entry', array( $this, 'wordpress_seo_noindex_ad_attachments' ), 10, 3 );
30
  // Add shortcode for MailPoet.
31
  add_filter( 'mailpoet_newsletter_shortcode', array( $this, 'mailpoet_ad_shortcode' ), 10, 5 );
 
 
32
  }
33
 
34
  /**
@@ -196,4 +198,18 @@ class Advanced_Ads_Compatibility {
196
  }
197
  return false;
198
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
29
  add_filter( 'wpseo_sitemap_entry', array( $this, 'wordpress_seo_noindex_ad_attachments' ), 10, 3 );
30
  // Add shortcode for MailPoet.
31
  add_filter( 'mailpoet_newsletter_shortcode', array( $this, 'mailpoet_ad_shortcode' ), 10, 5 );
32
+
33
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_dequeue_scripts_and_styles' ), 100 );
34
  }
35
 
36
  /**
198
  }
199
  return false;
200
  }
201
+
202
+ /**
203
+ * Dequeue scripts and styles to prevent layout issues.
204
+ */
205
+ public function admin_dequeue_scripts_and_styles() {
206
+ if ( ! Advanced_Ads_Admin::screen_belongs_to_advanced_ads() ) {
207
+ return;
208
+ }
209
+
210
+ // Dequeue the css file enqueued by the JNews theme.
211
+ if ( defined( 'JNEWS_THEME_URL' ) ) {
212
+ wp_dequeue_style( 'jnews-admin' );
213
+ }
214
+ }
215
  }
classes/frontend_checks.php CHANGED
@@ -872,17 +872,24 @@ class Advanced_Ads_Frontend_Checks {
872
  * Code to check if current user gave consent to show ads
873
  */
874
  $privacy_state = Advanced_Ads_Privacy::get_instance()->get_state();
875
- if( 'not_needed' !== $privacy_state ) :
876
- ?>advanced_ads_ready( function() {
877
- var state = ( advads.privacy ) ? advads.privacy.get_state() : "";
878
- var advads_consent_link = document.querySelector( '#wp-admin-bar-advanced_ads_ad_health_consent_missing.hidden' );
879
- if ( 'unknown' === state && advads_consent_link ) {
880
- advads_consent_link.className = advads_consent_link.className.replace( 'hidden', '' );
881
- }
882
 
883
- advanced_ads_frontend_checks.showCount();
884
- });
885
- <?php endif; ?>
 
 
 
 
 
 
 
 
 
 
886
  /**
887
  * show Google Ad Manager debug link in Ad Health
888
  *
872
  * Code to check if current user gave consent to show ads
873
  */
874
  $privacy_state = Advanced_Ads_Privacy::get_instance()->get_state();
875
+ if ( 'not_needed' !== $privacy_state ) :
876
+ ?>
877
+ document.addEventListener('advanced_ads_privacy', function (event) {
878
+ var advads_consent_link = document.querySelector('#wp-admin-bar-advanced_ads_ad_health_consent_missing');
 
 
 
879
 
880
+ if (!advads_consent_link) {
881
+ return;
882
+ }
883
+
884
+ if (event.detail.state !== 'accepted' && event.detail.state !== 'not_needed') {
885
+ advads_consent_link.classList.remove('hidden');
886
+ } else {
887
+ advads_consent_link.classList.add('hidden');
888
+ }
889
+
890
+ advanced_ads_frontend_checks.showCount();
891
+ });
892
+ <?php endif; ?>
893
  /**
894
  * show Google Ad Manager debug link in Ad Health
895
  *
classes/plugin.php CHANGED
@@ -160,15 +160,28 @@ class Advanced_Ads_Plugin {
160
  return;
161
  }
162
  // wp_enqueue_script( $this->get_plugin_slug() . '-plugin-script', plugins_url('assets/js/public.js', __FILE__), array('jquery'), ADVADS_VERSION);
163
- $options = $this->options();
164
- $activated_js = apply_filters( 'advanced-ads-activate-advanced-js', isset( $options['advanced-js'] ) );
165
 
166
  if ( $activated_js || ! empty( $_COOKIE['advads_frontend_picker'] ) ) {
167
- wp_enqueue_script( $this->get_plugin_slug() . '-advanced-js', ADVADS_BASE_URL . 'public/assets/js/advanced.js', array( 'jquery' ), ADVADS_VERSION, false );
 
 
 
 
 
 
 
 
 
 
 
 
168
  $data = array(
169
  'blog_id' => get_current_blog_id(),
 
170
  );
171
- wp_localize_script( $this->get_plugin_slug() . '-advanced-js', 'advanced_ads_data', $data );
 
172
  }
173
  }
174
 
@@ -207,13 +220,6 @@ class Advanced_Ads_Plugin {
207
  advanced_ads_ready=function(){var fns=[],listener,doc=typeof document==="object"&&document,hack=doc&&doc.documentElement.doScroll,domContentLoaded="DOMContentLoaded",loaded=doc&&(hack?/^loaded|^c/:/^loaded|^i|^c/).test(doc.readyState);if(!loaded&&doc){listener=function(){doc.removeEventListener(domContentLoaded,listener);window.removeEventListener("load",listener);loaded=1;while(listener=fns.shift())listener()};doc.addEventListener(domContentLoaded,listener);window.addEventListener("load",listener)}return function(fn){loaded?setTimeout(fn,0):fns.push(fn)}}();
208
  <?php
209
  }
210
-
211
- // Output privacy options.
212
- $privacy_options = Advanced_Ads_Privacy::get_instance()->options();
213
- if ( ! empty( $privacy_options['enabled'] ) ) {
214
- printf( '(advads_options = window.advads_options || {} )["privacy"] = %s;', wp_json_encode( $privacy_options ) );
215
- }
216
-
217
  ?>
218
  </script>
219
  <?php
160
  return;
161
  }
162
  // wp_enqueue_script( $this->get_plugin_slug() . '-plugin-script', plugins_url('assets/js/public.js', __FILE__), array('jquery'), ADVADS_VERSION);
163
+ $activated_js = apply_filters( 'advanced-ads-activate-advanced-js', isset( $this->options()['advanced-js'] ) );
 
164
 
165
  if ( $activated_js || ! empty( $_COOKIE['advads_frontend_picker'] ) ) {
166
+ wp_enqueue_script(
167
+ $this->get_plugin_slug() . '-advanced-js',
168
+ sprintf( '%spublic/assets/js/advanced%s.js', ADVADS_BASE_URL, defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '.orig' : '' ),
169
+ array( 'jquery' ),
170
+ ADVADS_VERSION,
171
+ false
172
+ );
173
+
174
+ $privacy = Advanced_Ads_Privacy::get_instance();
175
+ $privacy_options = $privacy->options();
176
+ $privacy_options['enabled'] = ! empty( $privacy_options['enabled'] );
177
+ $privacy_options['state'] = $privacy->get_state();
178
+
179
  $data = array(
180
  'blog_id' => get_current_blog_id(),
181
+ 'privacy' => $privacy_options,
182
  );
183
+
184
+ wp_localize_script( $this->get_plugin_slug() . '-advanced-js', 'advads_options', $data );
185
  }
186
  }
187
 
220
  advanced_ads_ready=function(){var fns=[],listener,doc=typeof document==="object"&&document,hack=doc&&doc.documentElement.doScroll,domContentLoaded="DOMContentLoaded",loaded=doc&&(hack?/^loaded|^c/:/^loaded|^i|^c/).test(doc.readyState);if(!loaded&&doc){listener=function(){doc.removeEventListener(domContentLoaded,listener);window.removeEventListener("load",listener);loaded=1;while(listener=fns.shift())listener()};doc.addEventListener(domContentLoaded,listener);window.addEventListener("load",listener)}return function(fn){loaded?setTimeout(fn,0):fns.push(fn)}}();
221
  <?php
222
  }
 
 
 
 
 
 
 
223
  ?>
224
  </script>
225
  <?php
classes/utils.php CHANGED
@@ -222,5 +222,57 @@ class Advanced_Ads_Utils {
222
  }
223
  return false;
224
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  }
226
 
222
  }
223
  return false;
224
  }
225
+
226
+ /**
227
+ * Get DateTimeZone object for the WP installation
228
+ *
229
+ * @return DateTimeZone DateTimeZone object.
230
+ */
231
+ public static function get_wp_timezone() {
232
+ static $date_time_zone;
233
+ if ( ! is_null( $date_time_zone ) ) {
234
+ return $date_time_zone;
235
+ }
236
+
237
+ // wp_timezone() is available since WordPress 5.3.0.
238
+ if ( function_exists( 'wp_timezone' ) ) {
239
+ $date_time_zone = wp_timezone();
240
+
241
+ return $date_time_zone;
242
+ }
243
+
244
+ $time_zone = get_option( 'timezone_string' );
245
+ // no timezone string but gmt offset.
246
+ if ( empty( $time_zone ) ) {
247
+ $time_zone = get_option( 'gmt_offset' );
248
+ // gmt + x but not prefixed with a "+".
249
+ if ( preg_match( '/^\d/', $time_zone ) ) {
250
+ $time_zone = '+' . $time_zone;
251
+ }
252
+ }
253
+
254
+ $date_time_zone = new DateTimeZone( $time_zone );
255
+
256
+ return $date_time_zone;
257
+ }
258
+
259
+ /**
260
+ * Get literal expression of timezone.
261
+ *
262
+ * @return string Human readable timezone name.
263
+ */
264
+ public static function get_timezone_name() {
265
+ $time_zone = self::get_wp_timezone()->getName();
266
+ if ( $time_zone === 'UTC' ) {
267
+ return 'UTC+0';
268
+ }
269
+
270
+ if ( strpos( $time_zone, '+' ) === 0 || strpos( $time_zone, '-' ) === 0 ) {
271
+ return 'UTC' . $time_zone;
272
+ }
273
+
274
+ // translators: time zone name.
275
+ return sprintf( __( 'time of %s', 'advanced-ads' ), $time_zone );
276
+ }
277
  }
278
 
languages/advanced-ads.pot CHANGED
@@ -2,14 +2,14 @@
2
  # This file is distributed under the same license as the Advanced Ads plugin.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Advanced Ads 1.19.1\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/advanced-ads/\n"
7
  "Last-Translator: Thomas Maier <post@webzunft.de>\n"
8
  "Language-Team: webgilde <support@wpadvancedads.com>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "POT-Creation-Date: 2020-08-12T08:03:35+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
  "X-Generator: WP-CLI 2.4.0\n"
15
  "X-Domain: advanced-ads\n"
@@ -85,26 +85,21 @@ msgstr ""
85
  msgid "Hide inactive ads"
86
  msgstr ""
87
 
88
- #. translators: time zone name.
89
- #: admin/class-advanced-ads-admin.php:351
90
- msgid "time of %s"
91
- msgstr ""
92
-
93
- #: admin/class-advanced-ads-admin.php:422
94
  #: admin/includes/class-menu.php:156
95
  #: admin/includes/class-menu.php:159
96
  #: admin/views/settings.php:29
97
  msgid "Support"
98
  msgstr ""
99
 
100
- #: admin/class-advanced-ads-admin.php:426
101
  #: admin/includes/class-overview-widgets.php:71
102
  msgid "Add-Ons"
103
  msgstr ""
104
 
105
  #. translators: %s is the URL to add a new review, https://wordpress.org/support/plugin/advanced-ads/reviews/#new-post
106
  #. translators: %s is a URL.
107
- #: admin/class-advanced-ads-admin.php:697
108
  #: admin/includes/class-overview-widgets.php:194
109
  msgid "Thank the developer with a &#9733;&#9733;&#9733;&#9733;&#9733; review on <a href=\"%s\" target=\"_blank\">wordpress.org</a>"
110
  msgstr ""
@@ -317,7 +312,7 @@ msgstr ""
317
  #: modules/import-export/classes/import.php:146
318
  #: modules/import-export/classes/import.php:186
319
  #: modules/import-export/classes/import.php:564
320
- #: public/class-advanced-ads.php:743
321
  msgid "Edit"
322
  msgstr ""
323
 
@@ -359,83 +354,83 @@ msgid "Ad Shortcode"
359
  msgstr ""
360
 
361
  #. translators: %s is the number of ads.
362
- #: admin/includes/class-ad-type.php:440
363
  msgid "%s ad updated."
364
  msgid_plural "%s ads updated."
365
  msgstr[0] ""
366
  msgstr[1] ""
367
 
368
  #. translators: %s is the number of ads.
369
- #: admin/includes/class-ad-type.php:442
370
  msgid "%s ad not updated, somebody is editing it."
371
  msgid_plural "%s ads not updated, somebody is editing them."
372
  msgstr[0] ""
373
  msgstr[1] ""
374
 
375
  #. translators: %s is the number of ads.
376
- #: admin/includes/class-ad-type.php:444
377
  msgid "%s ad permanently deleted."
378
  msgid_plural "%s ads permanently deleted."
379
  msgstr[0] ""
380
  msgstr[1] ""
381
 
382
  #. translators: %s is the number of ads.
383
- #: admin/includes/class-ad-type.php:446
384
  msgid "%s ad moved to the Trash."
385
  msgid_plural "%s ads moved to the Trash."
386
  msgstr[0] ""
387
  msgstr[1] ""
388
 
389
  #. translators: %s is the number of ads.
390
- #: admin/includes/class-ad-type.php:448
391
  msgid "%s ad restored from the Trash."
392
  msgid_plural "%s ads restored from the Trash."
393
  msgstr[0] ""
394
  msgstr[1] ""
395
 
396
  #. Translators: %s is the time the ad was first saved.
397
- #: admin/includes/class-ad-type.php:658
398
  msgid "Ad created on %s"
399
  msgstr ""
400
 
401
- #: admin/includes/class-ad-type.php:846
402
- #: admin/includes/class-ad-type.php:847
403
  msgid "Ad updated."
404
  msgstr ""
405
 
406
  #. translators: %s: date and time of the revision
407
- #: admin/includes/class-ad-type.php:848
408
  msgid "Ad restored to revision from %s"
409
  msgstr ""
410
 
411
- #: admin/includes/class-ad-type.php:849
412
- #: admin/includes/class-ad-type.php:850
413
  msgid "Ad saved."
414
  msgstr ""
415
 
416
- #: admin/includes/class-ad-type.php:851
417
  msgid "Ad submitted."
418
  msgstr ""
419
 
420
  #. translators: %1$s is a date.
421
- #: admin/includes/class-ad-type.php:854
422
  msgid "Ad scheduled for: <strong>%1$s</strong>."
423
  msgstr ""
424
 
425
  #. translators: Publish box date format, see http://php.net/date.
426
- #: admin/includes/class-ad-type.php:856
427
  msgid "M j, Y @ G:i"
428
  msgstr ""
429
 
430
- #: admin/includes/class-ad-type.php:858
431
  msgid "Ad draft updated."
432
  msgstr ""
433
 
434
- #: admin/includes/class-ad-type.php:914
435
  msgid "You don’t have access to ads. Please deactivate and re-enable Advanced Ads again to fix this."
436
  msgstr ""
437
 
438
- #: admin/includes/class-ad-type.php:915
439
  #: classes/frontend_checks.php:434
440
  msgid "Get help"
441
  msgstr ""
@@ -554,19 +549,19 @@ msgstr ""
554
  #: classes/widget.php:129
555
  #: modules/gutenberg/includes/class-gutenberg.php:79
556
  #: modules/import-export/views/page.php:23
557
- #: public/class-advanced-ads.php:739
558
  msgid "Ads"
559
  msgstr ""
560
 
561
  #: admin/includes/class-menu.php:112
562
- #: public/class-advanced-ads.php:742
563
  msgid "Add New Ad"
564
  msgstr ""
565
 
566
  #: admin/includes/class-menu.php:113
567
  #: admin/views/ad-group-list-ads.php:36
568
- #: public/class-advanced-ads.php:741
569
- #: public/class-advanced-ads.php:745
570
  msgid "New Ad"
571
  msgstr ""
572
 
@@ -659,7 +654,7 @@ msgstr ""
659
  #: admin/views/ad-output-metabox.php:61
660
  #: admin/views/settings/general/custom-label.php:9
661
  #: modules/ads-txt/admin/views/setting-create.php:11
662
- #: modules/privacy/admin/views/setting-enable.php:2
663
  msgid "Manual"
664
  msgstr ""
665
 
@@ -1087,7 +1082,7 @@ msgid "Cancel"
1087
  msgstr ""
1088
 
1089
  #: admin/views/ad-conditions-string-operators.php:15
1090
- #: modules/privacy/admin/views/setting-consent-method.php:10
1091
  msgid "contains"
1092
  msgstr ""
1093
 
@@ -1163,7 +1158,7 @@ msgstr ""
1163
  #: classes/ad-debug.php:118
1164
  #: classes/ad-debug.php:167
1165
  #: classes/ad-debug.php:169
1166
- #: public/class-advanced-ads.php:740
1167
  msgid "Ad"
1168
  msgstr ""
1169
 
@@ -1180,7 +1175,7 @@ msgstr ""
1180
  #: admin/views/placements.php:66
1181
  #: modules/gadsense/admin/views/external-ads-list.php:45
1182
  #: modules/gadsense/admin/views/external-ads-list.php:55
1183
- #: modules/privacy/admin/views/setting-consent-method.php:9
1184
  msgid "Name"
1185
  msgstr ""
1186
 
@@ -1329,6 +1324,10 @@ msgstr ""
1329
  msgid "Internal description or your own notes about this ad."
1330
  msgstr ""
1331
 
 
 
 
 
1332
  #: admin/views/ad-list-filters.php:18
1333
  msgid "all ad types"
1334
  msgstr ""
@@ -1474,38 +1473,38 @@ msgstr ""
1474
  msgid "reserve this space"
1475
  msgstr ""
1476
 
1477
- #: admin/views/ad-submitbox-meta.php:10
1478
  msgid "Set expiry date"
1479
  msgstr ""
1480
 
1481
- #: admin/views/ad-submitbox-meta.php:15
1482
  msgid "Month"
1483
  msgstr ""
1484
 
1485
  #. translators: %1$s is the month number, %2$s is the month shortname.
1486
- #: admin/views/ad-submitbox-meta.php:21
1487
  msgctxt "1: month number (01, 02, etc.), 2: month abbreviation"
1488
  msgid "%1$s-%2$s"
1489
  msgstr ""
1490
 
1491
- #: admin/views/ad-submitbox-meta.php:28
1492
  msgid "Day"
1493
  msgstr ""
1494
 
1495
- #: admin/views/ad-submitbox-meta.php:29
1496
  msgid "Year"
1497
  msgstr ""
1498
 
1499
- #: admin/views/ad-submitbox-meta.php:30
1500
  msgid "Hour"
1501
  msgstr ""
1502
 
1503
- #: admin/views/ad-submitbox-meta.php:31
1504
  msgid "Minute"
1505
  msgstr ""
1506
 
1507
  #. translators: %1$s month, %2$s day, %3$s year, %4$s hour, %5$s minute.
1508
- #: admin/views/ad-submitbox-meta.php:39
1509
  msgctxt "order of expiry date fields 1: month, 2: day, 3: year, 4: hour, 5: minute"
1510
  msgid "%1$s %2$s, %3$s @ %4$s %5$s"
1511
  msgstr ""
@@ -1628,7 +1627,7 @@ msgid "Visitor conditions limit the number of users who can see your ad. There i
1628
  msgstr ""
1629
 
1630
  #: admin/views/conditions/visitor-conditions-form-top.php:9
1631
- #: modules/privacy/admin/views/setting-enable.php:4
1632
  msgid "It seems that a caching plugin is activated."
1633
  msgstr ""
1634
 
@@ -2252,7 +2251,7 @@ msgid "Choose the roles a user must have in order to not see any ads."
2252
  msgstr ""
2253
 
2254
  #: admin/views/settings/general/link-target.php:6
2255
- msgid "Open programatically created links in a new window (use <code>target=\"_blank\"</code>)"
2256
  msgstr ""
2257
 
2258
  #: admin/views/settings/general/uninstall-delete-data.php:3
@@ -2485,7 +2484,7 @@ msgid "main query"
2485
  msgstr ""
2486
 
2487
  #: classes/ad-debug.php:121
2488
- #: public/class-advanced-ads.php:704
2489
  msgctxt "ad group singular name"
2490
  msgid "Ad Group"
2491
  msgstr ""
@@ -3098,6 +3097,11 @@ msgstr ""
3098
  msgid "Closing the message"
3099
  msgstr ""
3100
 
 
 
 
 
 
3101
  #: classes/visitor-conditions.php:40
3102
  msgid "device"
3103
  msgstr ""
@@ -3204,7 +3208,7 @@ msgid "Ad blocker fix"
3204
  msgstr ""
3205
 
3206
  #: modules/ad-blocker/admin/admin.php:150
3207
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:362
3208
  msgid "Unable to connect to the filesystem. Please confirm your credentials."
3209
  msgstr ""
3210
 
@@ -3291,60 +3295,69 @@ msgstr ""
3291
  msgid "Want to know how many of your visitors are using an ad blocker? Enter your Google Analytics property ID above to count them."
3292
  msgstr ""
3293
 
3294
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:176
3295
  msgid "The ads.txt file cannot be placed because the URL contains a subdirectory. You need to make the file available at %s"
3296
  msgstr ""
3297
 
3298
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:187
3299
  msgid "The file is available on %s."
3300
  msgstr ""
3301
 
3302
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:191
3303
  msgid "The file was not created."
3304
  msgstr ""
3305
 
3306
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:198
3307
  msgid "Import & Replace"
3308
  msgstr ""
3309
 
3310
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:200
3311
  msgid "Move the content of the existing ads.txt file into Advanced Ads and remove it."
3312
  msgstr ""
3313
 
3314
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:207
3315
- #: modules/ads-txt/admin/views/setting-additional-content.php:4
3316
  msgid "An error occured: %s."
3317
  msgstr ""
3318
 
3319
  #. translators: %s the line that may need to be added manually
3320
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:220
3321
  msgid "If your site is located on a subdomain, you need to add the following line to the ads.txt file of the root domain: %s"
3322
  msgstr ""
3323
 
3324
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:335
3325
  msgid "The ads.txt is now managed with Advanced Ads."
3326
  msgstr ""
3327
 
3328
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:380
3329
  msgid "Not the main blog"
3330
  msgstr ""
3331
 
3332
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:409
3333
  msgid "Could not delete the existing ads.txt file"
3334
  msgstr ""
3335
 
3336
- #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:412
3337
  msgid "Could not find the existing ads.txt file"
3338
  msgstr ""
3339
 
3340
- #: modules/ads-txt/admin/views/setting-additional-content.php:2
 
 
 
 
 
3341
  msgid "Additional records to add to the file, one record per line. AdSense is added automatically."
3342
  msgstr ""
3343
 
3344
- #: modules/ads-txt/admin/views/setting-additional-content.php:5
3345
  msgid "Check for problems"
3346
  msgstr ""
3347
 
 
 
 
 
3348
  #: modules/ads-txt/admin/views/setting-create.php:19
3349
  msgid "Generate a single ads.txt file for all sites in the multisite network."
3350
  msgstr ""
@@ -3979,7 +3992,7 @@ msgid "When you click the button below Advanced Ads will create an XML file for
3979
  msgstr ""
3980
 
3981
  #: modules/import-export/views/page.php:24
3982
- #: public/class-advanced-ads.php:713
3983
  msgid "Groups"
3984
  msgstr ""
3985
 
@@ -4011,129 +4024,155 @@ msgstr ""
4011
  msgid "Start import"
4012
  msgstr ""
4013
 
4014
- #: modules/privacy/admin/admin.php:63
4015
  msgid "Privacy"
4016
  msgstr ""
4017
 
4018
- #: modules/privacy/admin/admin.php:84
4019
  msgid "Enable Privacy module"
4020
  msgstr ""
4021
 
4022
- #: modules/privacy/admin/admin.php:87
4023
- msgid "Consent method"
 
 
 
 
 
 
 
 
4024
  msgstr ""
4025
 
4026
- #: modules/privacy/admin/views/setting-ad-ignore-consent.php:2
4027
  msgid "privacy"
4028
  msgstr ""
4029
 
4030
- #: modules/privacy/admin/views/setting-ad-ignore-consent.php:6
4031
- msgid "Ignore <a href=\"%s\">general Privacy settings</a> and display the ad even without consent."
 
4032
  msgstr ""
4033
 
4034
- #: modules/privacy/admin/views/setting-consent-method.php:12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4035
  msgid "Value"
4036
  msgstr ""
4037
 
4038
- #: modules/privacy/admin/views/setting-consent-method.php:18
4039
  msgid "Show non-personalized AdSense ads until consent is given."
4040
  msgstr ""
4041
 
4042
- #: modules/privacy/admin/views/setting-enable.php:5
4043
  msgid "Your users’ consent might get cached and show ads to users who didn’t give their consent yet. "
4044
  msgstr ""
4045
 
4046
- #: modules/privacy/admin/views/setting-enable.php:6
4047
- msgid "Cache-busting in <a href=\"%s\" target=\"_blank\">Advanced Ads Pro</a> solves that."
4048
  msgstr ""
4049
 
4050
- #: modules/privacy/classes/plugin.php:149
4051
- msgid "Show all ads even without consent."
4052
  msgstr ""
4053
 
4054
- #: modules/privacy/classes/plugin.php:150
4055
- msgid "Cookie"
 
4056
  msgstr ""
4057
 
4058
- #: public/class-advanced-ads.php:369
4059
  msgid "Advanced Ads Error following:"
4060
  msgstr ""
4061
 
4062
  #. translators: %s is an error message generated by the plugin.
4063
- #: public/class-advanced-ads.php:373
4064
  msgid "Advanced Ads Error: %s"
4065
  msgstr ""
4066
 
4067
- #: public/class-advanced-ads.php:703
4068
  msgctxt "ad group general name"
4069
  msgid "Ad Groups & Rotations"
4070
  msgstr ""
4071
 
4072
- #: public/class-advanced-ads.php:705
4073
  msgid "Search Ad Groups"
4074
  msgstr ""
4075
 
4076
- #: public/class-advanced-ads.php:706
4077
  msgid "All Ad Groups"
4078
  msgstr ""
4079
 
4080
- #: public/class-advanced-ads.php:707
4081
  msgid "Parent Ad Groups"
4082
  msgstr ""
4083
 
4084
- #: public/class-advanced-ads.php:708
4085
  msgid "Parent Ad Groups:"
4086
  msgstr ""
4087
 
4088
- #: public/class-advanced-ads.php:709
4089
  msgid "Edit Ad Group"
4090
  msgstr ""
4091
 
4092
- #: public/class-advanced-ads.php:710
4093
  msgid "Update Ad Group"
4094
  msgstr ""
4095
 
4096
- #: public/class-advanced-ads.php:711
4097
  msgid "Add New Ad Group"
4098
  msgstr ""
4099
 
4100
- #: public/class-advanced-ads.php:712
4101
  msgid "New Ad Groups Name"
4102
  msgstr ""
4103
 
4104
- #: public/class-advanced-ads.php:714
4105
  msgid "No Ad Group found"
4106
  msgstr ""
4107
 
4108
- #: public/class-advanced-ads.php:744
4109
  msgid "Edit Ad"
4110
  msgstr ""
4111
 
4112
- #: public/class-advanced-ads.php:746
4113
  msgid "View"
4114
  msgstr ""
4115
 
4116
- #: public/class-advanced-ads.php:747
4117
  msgid "View the Ad"
4118
  msgstr ""
4119
 
4120
- #: public/class-advanced-ads.php:748
4121
  msgid "Search Ads"
4122
  msgstr ""
4123
 
4124
- #: public/class-advanced-ads.php:749
4125
  msgid "No Ads found"
4126
  msgstr ""
4127
 
4128
- #: public/class-advanced-ads.php:750
4129
  msgid "No Ads found in Trash"
4130
  msgstr ""
4131
 
4132
- #: public/class-advanced-ads.php:751
4133
  msgid "Parent Ad"
4134
  msgstr ""
4135
 
4136
- #: public/class-advanced-ads.php:890
4137
  msgctxt "label above ads"
4138
  msgid "Advertisements"
4139
  msgstr ""
2
  # This file is distributed under the same license as the Advanced Ads plugin.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Advanced Ads 1.20.0-rc.1\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/advanced-ads/\n"
7
  "Last-Translator: Thomas Maier <post@webzunft.de>\n"
8
  "Language-Team: webgilde <support@wpadvancedads.com>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
+ "POT-Creation-Date: 2020-09-11T11:33:02+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
  "X-Generator: WP-CLI 2.4.0\n"
15
  "X-Domain: advanced-ads\n"
85
  msgid "Hide inactive ads"
86
  msgstr ""
87
 
88
+ #: admin/class-advanced-ads-admin.php:398
 
 
 
 
 
89
  #: admin/includes/class-menu.php:156
90
  #: admin/includes/class-menu.php:159
91
  #: admin/views/settings.php:29
92
  msgid "Support"
93
  msgstr ""
94
 
95
+ #: admin/class-advanced-ads-admin.php:402
96
  #: admin/includes/class-overview-widgets.php:71
97
  msgid "Add-Ons"
98
  msgstr ""
99
 
100
  #. translators: %s is the URL to add a new review, https://wordpress.org/support/plugin/advanced-ads/reviews/#new-post
101
  #. translators: %s is a URL.
102
+ #: admin/class-advanced-ads-admin.php:673
103
  #: admin/includes/class-overview-widgets.php:194
104
  msgid "Thank the developer with a &#9733;&#9733;&#9733;&#9733;&#9733; review on <a href=\"%s\" target=\"_blank\">wordpress.org</a>"
105
  msgstr ""
312
  #: modules/import-export/classes/import.php:146
313
  #: modules/import-export/classes/import.php:186
314
  #: modules/import-export/classes/import.php:564
315
+ #: public/class-advanced-ads.php:755
316
  msgid "Edit"
317
  msgstr ""
318
 
354
  msgstr ""
355
 
356
  #. translators: %s is the number of ads.
357
+ #: admin/includes/class-ad-type.php:442
358
  msgid "%s ad updated."
359
  msgid_plural "%s ads updated."
360
  msgstr[0] ""
361
  msgstr[1] ""
362
 
363
  #. translators: %s is the number of ads.
364
+ #: admin/includes/class-ad-type.php:444
365
  msgid "%s ad not updated, somebody is editing it."
366
  msgid_plural "%s ads not updated, somebody is editing them."
367
  msgstr[0] ""
368
  msgstr[1] ""
369
 
370
  #. translators: %s is the number of ads.
371
+ #: admin/includes/class-ad-type.php:446
372
  msgid "%s ad permanently deleted."
373
  msgid_plural "%s ads permanently deleted."
374
  msgstr[0] ""
375
  msgstr[1] ""
376
 
377
  #. translators: %s is the number of ads.
378
+ #: admin/includes/class-ad-type.php:448
379
  msgid "%s ad moved to the Trash."
380
  msgid_plural "%s ads moved to the Trash."
381
  msgstr[0] ""
382
  msgstr[1] ""
383
 
384
  #. translators: %s is the number of ads.
385
+ #: admin/includes/class-ad-type.php:450
386
  msgid "%s ad restored from the Trash."
387
  msgid_plural "%s ads restored from the Trash."
388
  msgstr[0] ""
389
  msgstr[1] ""
390
 
391
  #. Translators: %s is the time the ad was first saved.
392
+ #: admin/includes/class-ad-type.php:660
393
  msgid "Ad created on %s"
394
  msgstr ""
395
 
396
+ #: admin/includes/class-ad-type.php:848
397
+ #: admin/includes/class-ad-type.php:849
398
  msgid "Ad updated."
399
  msgstr ""
400
 
401
  #. translators: %s: date and time of the revision
402
+ #: admin/includes/class-ad-type.php:850
403
  msgid "Ad restored to revision from %s"
404
  msgstr ""
405
 
406
+ #: admin/includes/class-ad-type.php:851
407
+ #: admin/includes/class-ad-type.php:852
408
  msgid "Ad saved."
409
  msgstr ""
410
 
411
+ #: admin/includes/class-ad-type.php:853
412
  msgid "Ad submitted."
413
  msgstr ""
414
 
415
  #. translators: %1$s is a date.
416
+ #: admin/includes/class-ad-type.php:856
417
  msgid "Ad scheduled for: <strong>%1$s</strong>."
418
  msgstr ""
419
 
420
  #. translators: Publish box date format, see http://php.net/date.
421
+ #: admin/includes/class-ad-type.php:858
422
  msgid "M j, Y @ G:i"
423
  msgstr ""
424
 
425
+ #: admin/includes/class-ad-type.php:860
426
  msgid "Ad draft updated."
427
  msgstr ""
428
 
429
+ #: admin/includes/class-ad-type.php:916
430
  msgid "You don’t have access to ads. Please deactivate and re-enable Advanced Ads again to fix this."
431
  msgstr ""
432
 
433
+ #: admin/includes/class-ad-type.php:917
434
  #: classes/frontend_checks.php:434
435
  msgid "Get help"
436
  msgstr ""
549
  #: classes/widget.php:129
550
  #: modules/gutenberg/includes/class-gutenberg.php:79
551
  #: modules/import-export/views/page.php:23
552
+ #: public/class-advanced-ads.php:751
553
  msgid "Ads"
554
  msgstr ""
555
 
556
  #: admin/includes/class-menu.php:112
557
+ #: public/class-advanced-ads.php:754
558
  msgid "Add New Ad"
559
  msgstr ""
560
 
561
  #: admin/includes/class-menu.php:113
562
  #: admin/views/ad-group-list-ads.php:36
563
+ #: public/class-advanced-ads.php:753
564
+ #: public/class-advanced-ads.php:757
565
  msgid "New Ad"
566
  msgstr ""
567
 
654
  #: admin/views/ad-output-metabox.php:61
655
  #: admin/views/settings/general/custom-label.php:9
656
  #: modules/ads-txt/admin/views/setting-create.php:11
657
+ #: modules/privacy/admin/views/setting-general.php:34
658
  msgid "Manual"
659
  msgstr ""
660
 
1082
  msgstr ""
1083
 
1084
  #: admin/views/ad-conditions-string-operators.php:15
1085
+ #: modules/privacy/admin/views/setting-general.php:47
1086
  msgid "contains"
1087
  msgstr ""
1088
 
1158
  #: classes/ad-debug.php:118
1159
  #: classes/ad-debug.php:167
1160
  #: classes/ad-debug.php:169
1161
+ #: public/class-advanced-ads.php:752
1162
  msgid "Ad"
1163
  msgstr ""
1164
 
1175
  #: admin/views/placements.php:66
1176
  #: modules/gadsense/admin/views/external-ads-list.php:45
1177
  #: modules/gadsense/admin/views/external-ads-list.php:55
1178
+ #: modules/privacy/admin/views/setting-general.php:44
1179
  msgid "Name"
1180
  msgstr ""
1181
 
1324
  msgid "Internal description or your own notes about this ad."
1325
  msgstr ""
1326
 
1327
+ #: admin/views/ad-list-details-column.php:19
1328
+ msgid "Consent disabled"
1329
+ msgstr ""
1330
+
1331
  #: admin/views/ad-list-filters.php:18
1332
  msgid "all ad types"
1333
  msgstr ""
1473
  msgid "reserve this space"
1474
  msgstr ""
1475
 
1476
+ #: admin/views/ad-submitbox-meta.php:9
1477
  msgid "Set expiry date"
1478
  msgstr ""
1479
 
1480
+ #: admin/views/ad-submitbox-meta.php:14
1481
  msgid "Month"
1482
  msgstr ""
1483
 
1484
  #. translators: %1$s is the month number, %2$s is the month shortname.
1485
+ #: admin/views/ad-submitbox-meta.php:20
1486
  msgctxt "1: month number (01, 02, etc.), 2: month abbreviation"
1487
  msgid "%1$s-%2$s"
1488
  msgstr ""
1489
 
1490
+ #: admin/views/ad-submitbox-meta.php:27
1491
  msgid "Day"
1492
  msgstr ""
1493
 
1494
+ #: admin/views/ad-submitbox-meta.php:28
1495
  msgid "Year"
1496
  msgstr ""
1497
 
1498
+ #: admin/views/ad-submitbox-meta.php:29
1499
  msgid "Hour"
1500
  msgstr ""
1501
 
1502
+ #: admin/views/ad-submitbox-meta.php:30
1503
  msgid "Minute"
1504
  msgstr ""
1505
 
1506
  #. translators: %1$s month, %2$s day, %3$s year, %4$s hour, %5$s minute.
1507
+ #: admin/views/ad-submitbox-meta.php:38
1508
  msgctxt "order of expiry date fields 1: month, 2: day, 3: year, 4: hour, 5: minute"
1509
  msgid "%1$s %2$s, %3$s @ %4$s %5$s"
1510
  msgstr ""
1627
  msgstr ""
1628
 
1629
  #: admin/views/conditions/visitor-conditions-form-top.php:9
1630
+ #: modules/privacy/admin/views/setting-general.php:59
1631
  msgid "It seems that a caching plugin is activated."
1632
  msgstr ""
1633
 
2251
  msgstr ""
2252
 
2253
  #: admin/views/settings/general/link-target.php:6
2254
+ msgid "Open programmatically created links in a new window (use <code>target=\"_blank\"</code>)"
2255
  msgstr ""
2256
 
2257
  #: admin/views/settings/general/uninstall-delete-data.php:3
2484
  msgstr ""
2485
 
2486
  #: classes/ad-debug.php:121
2487
+ #: public/class-advanced-ads.php:716
2488
  msgctxt "ad group singular name"
2489
  msgid "Ad Group"
2490
  msgstr ""
3097
  msgid "Closing the message"
3098
  msgstr ""
3099
 
3100
+ #. translators: time zone name.
3101
+ #: classes/utils.php:275
3102
+ msgid "time of %s"
3103
+ msgstr ""
3104
+
3105
  #: classes/visitor-conditions.php:40
3106
  msgid "device"
3107
  msgstr ""
3208
  msgstr ""
3209
 
3210
  #: modules/ad-blocker/admin/admin.php:150
3211
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:365
3212
  msgid "Unable to connect to the filesystem. Please confirm your credentials."
3213
  msgstr ""
3214
 
3295
  msgid "Want to know how many of your visitors are using an ad blocker? Enter your Google Analytics property ID above to count them."
3296
  msgstr ""
3297
 
3298
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:179
3299
  msgid "The ads.txt file cannot be placed because the URL contains a subdirectory. You need to make the file available at %s"
3300
  msgstr ""
3301
 
3302
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:190
3303
  msgid "The file is available on %s."
3304
  msgstr ""
3305
 
3306
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:194
3307
  msgid "The file was not created."
3308
  msgstr ""
3309
 
3310
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:201
3311
  msgid "Import & Replace"
3312
  msgstr ""
3313
 
3314
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:203
3315
  msgid "Move the content of the existing ads.txt file into Advanced Ads and remove it."
3316
  msgstr ""
3317
 
3318
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:210
3319
+ #: modules/ads-txt/admin/views/setting-additional-content.php:29
3320
  msgid "An error occured: %s."
3321
  msgstr ""
3322
 
3323
  #. translators: %s the line that may need to be added manually
3324
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:223
3325
  msgid "If your site is located on a subdomain, you need to add the following line to the ads.txt file of the root domain: %s"
3326
  msgstr ""
3327
 
3328
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:338
3329
  msgid "The ads.txt is now managed with Advanced Ads."
3330
  msgstr ""
3331
 
3332
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:383
3333
  msgid "Not the main blog"
3334
  msgstr ""
3335
 
3336
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:412
3337
  msgid "Could not delete the existing ads.txt file"
3338
  msgstr ""
3339
 
3340
+ #: modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php:415
3341
  msgid "Could not find the existing ads.txt file"
3342
  msgstr ""
3343
 
3344
+ #. translators: %s: The adsense line added automically by Advanced Ads.
3345
+ #: modules/ads-txt/admin/views/setting-additional-content.php:8
3346
+ msgid "The following line will be added automatically because you connected your AdSense account with Advanced Ads: %s"
3347
+ msgstr ""
3348
+
3349
+ #: modules/ads-txt/admin/views/setting-additional-content.php:22
3350
  msgid "Additional records to add to the file, one record per line. AdSense is added automatically."
3351
  msgstr ""
3352
 
3353
+ #: modules/ads-txt/admin/views/setting-additional-content.php:30
3354
  msgid "Check for problems"
3355
  msgstr ""
3356
 
3357
+ #: modules/ads-txt/admin/views/setting-additional-content.php:31
3358
+ msgid "Preview"
3359
+ msgstr ""
3360
+
3361
  #: modules/ads-txt/admin/views/setting-create.php:19
3362
  msgid "Generate a single ads.txt file for all sites in the multisite network."
3363
  msgstr ""
3992
  msgstr ""
3993
 
3994
  #: modules/import-export/views/page.php:24
3995
+ #: public/class-advanced-ads.php:725
3996
  msgid "Groups"
3997
  msgstr ""
3998
 
4024
  msgid "Start import"
4025
  msgstr ""
4026
 
4027
+ #: modules/privacy/admin/admin.php:54
4028
  msgid "Privacy"
4029
  msgstr ""
4030
 
4031
+ #: modules/privacy/admin/admin.php:75
4032
  msgid "Enable Privacy module"
4033
  msgstr ""
4034
 
4035
+ #: modules/privacy/admin/admin.php:105
4036
+ msgid "Show all ads even without consent"
4037
+ msgstr ""
4038
+
4039
+ #: modules/privacy/admin/admin.php:108
4040
+ msgid "Cookie"
4041
+ msgstr ""
4042
+
4043
+ #: modules/privacy/admin/admin.php:112
4044
+ msgid "TCF v2.0 integration (Quantcast, euconsent-v2, ...)"
4045
  msgstr ""
4046
 
4047
+ #: modules/privacy/admin/views/setting-ad-ignore-consent.php:10
4048
  msgid "privacy"
4049
  msgstr ""
4050
 
4051
+ #. Translators: 1: a tag with link to general privacy settings, 2: closing a tag
4052
+ #: modules/privacy/admin/views/setting-ad-ignore-consent.php:17
4053
+ msgid "Ignore %1$sgeneral Privacy settings%2$s and display the ad even without consent."
4054
  msgstr ""
4055
 
4056
+ #: modules/privacy/admin/views/setting-general.php:16
4057
+ msgid "Show ads only to users who give their permission to cookies and ads."
4058
+ msgstr ""
4059
+
4060
+ #: modules/privacy/admin/views/setting-general.php:20
4061
+ msgid "Consent method"
4062
+ msgstr ""
4063
+
4064
+ #: modules/privacy/admin/views/setting-general.php:43
4065
+ msgid "Cookie name"
4066
+ msgstr ""
4067
+
4068
+ #: modules/privacy/admin/views/setting-general.php:48
4069
+ msgid "value"
4070
+ msgstr ""
4071
+
4072
+ #: modules/privacy/admin/views/setting-general.php:49
4073
  msgid "Value"
4074
  msgstr ""
4075
 
4076
+ #: modules/privacy/admin/views/setting-general.php:53
4077
  msgid "Show non-personalized AdSense ads until consent is given."
4078
  msgstr ""
4079
 
4080
+ #: modules/privacy/admin/views/setting-general.php:62
4081
  msgid "Your users’ consent might get cached and show ads to users who didn’t give their consent yet. "
4082
  msgstr ""
4083
 
4084
+ #: modules/privacy/admin/views/setting-general.php:65
4085
+ msgid "Cache-busting in Advanced Ads Pro solves that."
4086
  msgstr ""
4087
 
4088
+ #: modules/privacy/admin/views/setting-general.php:74
4089
+ msgid "Ads are loaded after the user gives their consent and reloads the page."
4090
  msgstr ""
4091
 
4092
+ #. Translators: 1: opening link tag with link to Advanced Ads Pro 2: closing link tag
4093
+ #: modules/privacy/admin/views/setting-general.php:78
4094
+ msgid "Install %1$sAdvanced Ads Pro%2$s to reload the ads instantly without an additional page request."
4095
  msgstr ""
4096
 
4097
+ #: public/class-advanced-ads.php:381
4098
  msgid "Advanced Ads Error following:"
4099
  msgstr ""
4100
 
4101
  #. translators: %s is an error message generated by the plugin.
4102
+ #: public/class-advanced-ads.php:385
4103
  msgid "Advanced Ads Error: %s"
4104
  msgstr ""
4105
 
4106
+ #: public/class-advanced-ads.php:715
4107
  msgctxt "ad group general name"
4108
  msgid "Ad Groups & Rotations"
4109
  msgstr ""
4110
 
4111
+ #: public/class-advanced-ads.php:717
4112
  msgid "Search Ad Groups"
4113
  msgstr ""
4114
 
4115
+ #: public/class-advanced-ads.php:718
4116
  msgid "All Ad Groups"
4117
  msgstr ""
4118
 
4119
+ #: public/class-advanced-ads.php:719
4120
  msgid "Parent Ad Groups"
4121
  msgstr ""
4122
 
4123
+ #: public/class-advanced-ads.php:720
4124
  msgid "Parent Ad Groups:"
4125
  msgstr ""
4126
 
4127
+ #: public/class-advanced-ads.php:721
4128
  msgid "Edit Ad Group"
4129
  msgstr ""
4130
 
4131
+ #: public/class-advanced-ads.php:722
4132
  msgid "Update Ad Group"
4133
  msgstr ""
4134
 
4135
+ #: public/class-advanced-ads.php:723
4136
  msgid "Add New Ad Group"
4137
  msgstr ""
4138
 
4139
+ #: public/class-advanced-ads.php:724
4140
  msgid "New Ad Groups Name"
4141
  msgstr ""
4142
 
4143
+ #: public/class-advanced-ads.php:726
4144
  msgid "No Ad Group found"
4145
  msgstr ""
4146
 
4147
+ #: public/class-advanced-ads.php:756
4148
  msgid "Edit Ad"
4149
  msgstr ""
4150
 
4151
+ #: public/class-advanced-ads.php:758
4152
  msgid "View"
4153
  msgstr ""
4154
 
4155
+ #: public/class-advanced-ads.php:759
4156
  msgid "View the Ad"
4157
  msgstr ""
4158
 
4159
+ #: public/class-advanced-ads.php:760
4160
  msgid "Search Ads"
4161
  msgstr ""
4162
 
4163
+ #: public/class-advanced-ads.php:761
4164
  msgid "No Ads found"
4165
  msgstr ""
4166
 
4167
+ #: public/class-advanced-ads.php:762
4168
  msgid "No Ads found in Trash"
4169
  msgstr ""
4170
 
4171
+ #: public/class-advanced-ads.php:763
4172
  msgid "Parent Ad"
4173
  msgstr ""
4174
 
4175
+ #: public/class-advanced-ads.php:932
4176
  msgctxt "label above ads"
4177
  msgid "Advertisements"
4178
  msgstr ""
lib/composer/ClassLoader.php CHANGED
@@ -60,7 +60,7 @@ class ClassLoader
60
  public function getPrefixes()
61
  {
62
  if (!empty($this->prefixesPsr0)) {
63
- return call_user_func_array('array_merge', $this->prefixesPsr0);
64
  }
65
 
66
  return array();
60
  public function getPrefixes()
61
  {
62
  if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
64
  }
65
 
66
  return array();
modules/ads-txt/admin/class-advanced-ads-ads-txt-admin.php CHANGED
@@ -140,6 +140,9 @@ class Advanced_Ads_Ads_Txt_Admin {
140
  $content = $this->strategy->get_additional_content();
141
  $notices = $this->get_notices();
142
  $notices = $this->get_notices_markup( $notices );
 
 
 
143
  include dirname( __FILE__ ) . '/views/setting-additional-content.php';
144
  }
145
 
140
  $content = $this->strategy->get_additional_content();
141
  $notices = $this->get_notices();
142
  $notices = $this->get_notices_markup( $notices );
143
+
144
+ $link = home_url( '/' ) . 'ads.txt';
145
+ $adsense_line = $this->get_adsense_blog_data();
146
  include dirname( __FILE__ ) . '/views/setting-additional-content.php';
147
  }
148
 
modules/ads-txt/admin/views/setting-additional-content.php CHANGED
@@ -1,5 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <textarea cols="50" rows="5" id="advads-ads-txt-additional-content" name="advads-ads-txt-additional-content"><?php echo esc_textarea( $content ); ?></textarea>
2
  <p class="description"><?php esc_html_e( 'Additional records to add to the file, one record per line. AdSense is added automatically.', 'advanced-ads' ); ?></p>
3
- <div id="advads-ads-txt-notice-wrapper"><?php echo $notices; ?></div>
 
 
 
 
 
4
  <p class="advads-error-message hidden" id="advads-ads-txt-notice-error"><?php esc_html_e( 'An error occured: %s.', 'advanced-ads' ); ?></p>
5
  <button class="button advads-ads-txt-action" type="button" id="advads-ads-txt-notice-refresh"><?php esc_html_e( 'Check for problems', 'advanced-ads' ); ?></button>
 
1
+ <?php if ( $adsense_line ) : ?>
2
+ <p>
3
+
4
+ <?php
5
+ echo wp_kses(
6
+ sprintf(
7
+ /* translators: %s: The adsense line added automically by Advanced Ads. */
8
+ __( 'The following line will be added automatically because you connected your AdSense account with Advanced Ads: %s', 'advanced-ads' ),
9
+ '<br><code>' . $adsense_line . '</code>'
10
+ ),
11
+ array(
12
+ 'br' => array(),
13
+ 'code' => array(),
14
+ )
15
+ );
16
+ ?>
17
+ </p>
18
+ <?php endif; ?>
19
+
20
+ <br />
21
  <textarea cols="50" rows="5" id="advads-ads-txt-additional-content" name="advads-ads-txt-additional-content"><?php echo esc_textarea( $content ); ?></textarea>
22
  <p class="description"><?php esc_html_e( 'Additional records to add to the file, one record per line. AdSense is added automatically.', 'advanced-ads' ); ?></p>
23
+ <div id="advads-ads-txt-notice-wrapper">
24
+ <?php
25
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
26
+ echo $notices;
27
+ ?>
28
+ </div>
29
  <p class="advads-error-message hidden" id="advads-ads-txt-notice-error"><?php esc_html_e( 'An error occured: %s.', 'advanced-ads' ); ?></p>
30
  <button class="button advads-ads-txt-action" type="button" id="advads-ads-txt-notice-refresh"><?php esc_html_e( 'Check for problems', 'advanced-ads' ); ?></button>
31
+ <a href="<?php echo esc_url( $link ); ?>" class="button" target="_blank"><?php esc_html_e( 'Preview', 'advanced-ads' ); ?></button>
modules/ads-txt/includes/class-advanced-ads-ads-txt-strategy.php CHANGED
@@ -55,10 +55,6 @@ class Advanced_Ads_Ads_Txt_Strategy {
55
  public function toggle( $is_enabled, $all_network, $additional_content ) {
56
  $prev = $this->get_options();
57
 
58
- $additional_content = explode( "\n", $additional_content );
59
- $additional_content = array_filter( array_map( 'trim', $additional_content ) );
60
- $additional_content = implode( "\n", $additional_content );
61
-
62
  $this->options['enabled'] = $is_enabled;
63
  $this->options['all_network'] = $all_network;
64
  $this->options['custom'] = $additional_content;
@@ -148,6 +144,14 @@ class Advanced_Ads_Ads_Txt_Strategy {
148
  return true;
149
  }
150
 
 
 
 
 
 
 
 
 
151
  if ( is_multisite() ) {
152
  update_site_meta(
153
  get_current_blog_id(),
55
  public function toggle( $is_enabled, $all_network, $additional_content ) {
56
  $prev = $this->get_options();
57
 
 
 
 
 
58
  $this->options['enabled'] = $is_enabled;
59
  $this->options['all_network'] = $all_network;
60
  $this->options['custom'] = $additional_content;
144
  return true;
145
  }
146
 
147
+ $blog_id = get_current_blog_id();
148
+ $tmp_options = Advanced_Ads_Ads_Txt_Utils::remove_duplicate_lines(
149
+ array(
150
+ $blog_id => $this->options,
151
+ )
152
+ );
153
+ $this->options = $tmp_options[ $blog_id ];
154
+
155
  if ( is_multisite() ) {
156
  update_site_meta(
157
  get_current_blog_id(),
modules/ads-txt/includes/class-advanced-ads-ads-txt-utils.php CHANGED
@@ -135,4 +135,54 @@ class Advanced_Ads_Ads_Txt_Utils {
135
  }
136
  return false;
137
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
135
  }
136
  return false;
137
  }
138
+
139
+ /**
140
+ * Remove duplicate lines.
141
+ *
142
+ * @param array $blog_data Array of arrays of blog options, keyed by by blog IDs.
143
+ * @param array $options {
144
+ * Options.
145
+ *
146
+ * @type string $to_comments Whether to convert duplicate records to comments.
147
+ * }
148
+ * @return array $blog_data Array of arrays of blog options, keyed by by blog IDs.
149
+ */
150
+ public static function remove_duplicate_lines( $blog_data, $options = array() ) {
151
+ $to_comments = ! empty( $options['to_comments'] );
152
+
153
+ $added_records = array();
154
+ foreach ( $blog_data as $blog_id => &$blog_options ) {
155
+ foreach ( $blog_options['networks'] as $id => $data ) {
156
+ // Convert to comments or remove duplicate records that are not comments.
157
+ if ( ! empty( $data['rec'] ) && '#' !== substr( $data['rec'], 0, 1 ) && in_array( $data['rec'], $added_records, true ) ) {
158
+ if ( $to_comments ) {
159
+ $blog_options['networks'][ $id ]['rec'] = '# ' . $blog_options['networks'][ $id ]['rec'];
160
+ } else {
161
+ unset( $blog_options['networks'][ $id ] );
162
+ }
163
+ continue;
164
+ }
165
+ $added_records[] = $data['rec'];
166
+ }
167
+
168
+ $blog_options['custom'] = explode( "\n", $blog_options['custom'] );
169
+ $blog_options['custom'] = array_map( 'trim', $blog_options['custom'] );
170
+
171
+ foreach ( $blog_options['custom'] as $id => $rec ) {
172
+ // Convert to comments or remove duplicate records that are not comments.
173
+ if ( ! empty( $rec ) && '#' !== substr( $rec, 0, 1 ) && in_array( $rec, $added_records, true ) ) {
174
+ if ( $to_comments ) {
175
+ $blog_options['custom'][ $id ] = '# ' . $blog_options['custom'][ $id ];
176
+ } else {
177
+ unset( $blog_options['custom'][ $id ] );
178
+ }
179
+ continue;
180
+ }
181
+ $added_records[] = $rec;
182
+ }
183
+ $blog_options['custom'] = implode( "\n", $blog_options['custom'] );
184
+
185
+ }
186
+ return $blog_data;
187
+ }
188
  }
modules/ads-txt/public/class-advanced-ads-ads-txt-public.php CHANGED
@@ -70,6 +70,7 @@ class Advanced_Ads_Ads_Txt_Public {
70
  /**
71
  * Prepare content of several blogs for output.
72
  *
 
73
  * @return string
74
  */
75
  public function prepare_multisite( $domain = null ) {
@@ -120,13 +121,21 @@ class Advanced_Ads_Ads_Txt_Public {
120
  join( ',', array_map( 'intval', $not_refferals ) )
121
  ) );
122
 
 
123
  foreach ( $results as $result ) {
124
  $blog_id = $result->blog_id;
125
 
126
  $options = maybe_unserialize( $result->meta_value );
127
  $options = $this->strategy->load_default_options( $options );
128
- $content = $this->strategy->parse_content( $options );
129
 
 
 
 
 
 
 
 
 
130
  if ( $content ) {
131
  $content = "# blog_id: $blog_id\n" . $content;
132
  }
@@ -139,10 +148,11 @@ class Advanced_Ads_Ads_Txt_Public {
139
  }
140
 
141
  $content = apply_filters( 'advanced-ads-ads-txt-content', $content, $blog_id );
142
- $o .= $content;
143
  }
144
- }
145
 
 
 
146
  return $o;
147
  }
148
 
70
  /**
71
  * Prepare content of several blogs for output.
72
  *
73
+ * @param string $domain Domain name.
74
  * @return string
75
  */
76
  public function prepare_multisite( $domain = null ) {
121
  join( ',', array_map( 'intval', $not_refferals ) )
122
  ) );
123
 
124
+ $blog_data = array();
125
  foreach ( $results as $result ) {
126
  $blog_id = $result->blog_id;
127
 
128
  $options = maybe_unserialize( $result->meta_value );
129
  $options = $this->strategy->load_default_options( $options );
 
130
 
131
+ $blog_data[ $blog_id ] = $options;
132
+ }
133
+
134
+ $blog_data = Advanced_Ads_Ads_Txt_Utils::remove_duplicate_lines( $blog_data, array( 'to_comments' => true ) );
135
+
136
+ foreach ( $blog_data as $blog_id => $blog_lines ) {
137
+
138
+ $content = $this->strategy->parse_content( $blog_lines );
139
  if ( $content ) {
140
  $content = "# blog_id: $blog_id\n" . $content;
141
  }
148
  }
149
 
150
  $content = apply_filters( 'advanced-ads-ads-txt-content', $content, $blog_id );
151
+ $o .= $content . "\n";
152
  }
 
153
 
154
+
155
+ }
156
  return $o;
157
  }
158
 
modules/gadsense/includes/class-ad-type-adsense.php CHANGED
@@ -149,18 +149,21 @@ class Advanced_Ads_Ad_Type_Adsense extends Advanced_Ads_Ad_Type_Abstract {
149
  /**
150
  * Sanitize content field on save
151
  *
152
- * @param string $content ad content
153
- *
154
  * @return string $content sanitized ad content
155
  * @since 1.0.0
156
  */
157
  public function sanitize_content( $content = '' ) {
158
  $content = wp_unslash( $content );
159
- $ad_unit = json_decode( $content );
 
 
 
160
  // remove this slotId from unsupported_ads
161
  $mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
162
- if ( array_key_exists( $ad_unit->slotId, $mapi_options['unsupported_units'] ) ) {
163
- unset( $mapi_options['unsupported_units'][ $ad_unit->slotId ] );
164
  update_option( Advanced_Ads_AdSense_MAPI::OPTNAME, $mapi_options );
165
  }
166
 
149
  /**
150
  * Sanitize content field on save
151
  *
152
+ * @param string $content ad content.
153
+ *
154
  * @return string $content sanitized ad content
155
  * @since 1.0.0
156
  */
157
  public function sanitize_content( $content = '' ) {
158
  $content = wp_unslash( $content );
159
+ $ad_unit = json_decode( $content, true );
160
+ if ( empty( $ad_unit ) ) {
161
+ $ad_unit = array();
162
+ }
163
  // remove this slotId from unsupported_ads
164
  $mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
165
+ if ( array_key_exists( 'slotId', $ad_unit ) && array_key_exists( $ad_unit['slotId'], $mapi_options['unsupported_units'] ) ) {
166
+ unset( $mapi_options['unsupported_units'][ $ad_unit['slotId'] ] );
167
  update_option( Advanced_Ads_AdSense_MAPI::OPTNAME, $mapi_options );
168
  }
169
 
modules/gadsense/public/public.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php
2
 
 
 
 
3
  class Advanced_Ads_AdSense_Public {
4
 
5
  private $data; // options
@@ -11,17 +14,23 @@ class Advanced_Ads_AdSense_Public {
11
  add_action( 'wp_head', array( $this, 'inject_header' ), 20 );
12
  }
13
 
 
 
 
 
 
14
  public static function get_instance() {
15
- if ( null == self::$instance ) {
16
- self::$instance = new self;
17
  }
 
18
  return self::$instance;
19
  }
20
 
21
  /**
22
  * Print data in the head tag on the front end.
23
  */
24
- public function inject_header(){
25
  $options = $this->data->get_options();
26
 
27
  // Inject CSS to make AdSense background transparent.
@@ -29,49 +38,52 @@ class Advanced_Ads_AdSense_Public {
29
  echo '<style>ins.adsbygoogle { background-color: transparent; padding: 0; }</style>';
30
  }
31
 
32
- if ( defined( 'ADVADS_ADS_DISABLED' ) ) {
33
- return;
34
- }
35
- if ( advads_is_amp() ) {
36
  return;
37
  }
38
 
39
- $privacy_options = Advanced_Ads_Privacy::get_instance()->options();
40
- $privacy_enabled = ! empty( $privacy_options['enabled'] ) && 'not_needed' !== Advanced_Ads_Privacy::get_instance()->get_state();
41
- $npa_enabled = ! empty( $privacy_options['show-non-personalized-adsense'] );
 
42
 
43
- // Show non-personalized Adsense ads if consent was not given.
44
- // If non-personalized ads are enabled.
45
  if ( $privacy_enabled && $npa_enabled ) {
46
  echo '<script>';
47
  // If the page is not from a cache.
48
- if ( Advanced_Ads_Privacy::get_instance()->get_state() === 'unknown' ) {
49
  echo '(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1;';
50
  }
51
-
52
- // If the page is from a cache.
53
- // Wait until 'advads.privacy' is available. Execute before cache-busting.
54
  echo '( window.advanced_ads_ready || jQuery( document ).ready ).call( null, function() {
55
  var state = ( advads.privacy ) ? advads.privacy.get_state() : "";
56
  var use_npa = ( state === "unknown" ) ? 1 : 0;
57
  (adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=use_npa;
58
- } )</script>';
 
59
  }
60
 
61
  if ( ! apply_filters( 'advanced-ads-can-display-ads-in-header', true ) ) {
62
  return;
63
  }
64
 
65
- /**
66
- * Inject page-level header code
67
- *
68
- * @since 1.6.9
69
- */
70
  $pub_id = trim( $this->data->get_adsense_id() );
71
 
72
  if ( $pub_id && isset( $options['page-level-enabled'] ) && $options['page-level-enabled'] ) {
73
- $pub_id = $this->data->get_adsense_id();
74
- $client_id = 'ca-' . $pub_id;
 
 
 
 
 
 
 
 
 
 
 
 
75
  include GADSENSE_BASE_PATH . 'public/templates/page-level.php';
76
  }
77
  }
1
  <?php
2
 
3
+ /**
4
+ * Class Advanced_Ads_AdSense_Public.
5
+ */
6
  class Advanced_Ads_AdSense_Public {
7
 
8
  private $data; // options
14
  add_action( 'wp_head', array( $this, 'inject_header' ), 20 );
15
  }
16
 
17
+ /**
18
+ * Get singleton instance.
19
+ *
20
+ * @return self
21
+ */
22
  public static function get_instance() {
23
+ if ( is_null( self::$instance ) ) {
24
+ self::$instance = new self();
25
  }
26
+
27
  return self::$instance;
28
  }
29
 
30
  /**
31
  * Print data in the head tag on the front end.
32
  */
33
+ public function inject_header() {
34
  $options = $this->data->get_options();
35
 
36
  // Inject CSS to make AdSense background transparent.
38
  echo '<style>ins.adsbygoogle { background-color: transparent; padding: 0; }</style>';
39
  }
40
 
41
+ if ( defined( 'ADVADS_ADS_DISABLED' ) || advads_is_amp() ) {
 
 
 
42
  return;
43
  }
44
 
45
+ $privacy = Advanced_Ads_Privacy::get_instance();
46
+ $privacy_options = $privacy->options();
47
+ $privacy_enabled = $privacy->get_state() !== 'not_needed';
48
+ $npa_enabled = ( isset( $privacy_options['method'] ) && $privacy_options['method'] === 'custom' ) && ! empty( $privacy_options['show-non-personalized-adsense'] );
49
 
50
+ // Show non-personalized Adsense ads if non-personalized ads are enabled and consent was not given.
 
51
  if ( $privacy_enabled && $npa_enabled ) {
52
  echo '<script>';
53
  // If the page is not from a cache.
54
+ if ( $privacy->get_state() === 'unknown' ) {
55
  echo '(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1;';
56
  }
57
+ // If the page is from a cache, wait until 'advads.privacy' is available. Execute before cache-busting.
 
 
58
  echo '( window.advanced_ads_ready || jQuery( document ).ready ).call( null, function() {
59
  var state = ( advads.privacy ) ? advads.privacy.get_state() : "";
60
  var use_npa = ( state === "unknown" ) ? 1 : 0;
61
  (adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=use_npa;
62
+ } )';
63
+ echo '</script>';
64
  }
65
 
66
  if ( ! apply_filters( 'advanced-ads-can-display-ads-in-header', true ) ) {
67
  return;
68
  }
69
 
 
 
 
 
 
70
  $pub_id = trim( $this->data->get_adsense_id() );
71
 
72
  if ( $pub_id && isset( $options['page-level-enabled'] ) && $options['page-level-enabled'] ) {
73
+ $pub_id = $this->data->get_adsense_id();
74
+ $client_id = 'ca-' . $pub_id;
75
+ $top_anchor = isset( $options['top-anchor-ad'] ) && $options['top-anchor-ad'];
76
+ $top_anchor_code = sprintf(
77
+ '(adsbygoogle = window.adsbygoogle || []).push({
78
+ google_ad_client: "%s",
79
+ enable_page_level_ads: true,
80
+ overlays: {bottom: true}
81
+ });',
82
+ esc_attr( $client_id )
83
+ );
84
+ $script_src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
85
+
86
+ // inject page-level header code.
87
  include GADSENSE_BASE_PATH . 'public/templates/page-level.php';
88
  }
89
  }
modules/gadsense/public/templates/page-level.php CHANGED
@@ -2,46 +2,44 @@
2
  /**
3
  * Output auto ads enabled code in head
4
  *
5
- * @var bool $privacy_enabled
6
- * @var bool $npa_enabled
7
- * @var string $client_id
8
- * @var array $options
 
 
9
  */
10
 
11
- $top_anchor = isset( $options['top-anchor-ad'] ) && $options['top-anchor-ad'];
12
- $top_anchor_code = sprintf(
13
- '(adsbygoogle = window.adsbygoogle || []).push({
14
- google_ad_client: "%s",
15
- enable_page_level_ads: true,
16
- overlays: {bottom: true}
17
- });',
18
- esc_attr( $client_id )
19
- );
20
- $script_src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
21
-
22
  if ( $privacy_enabled ) : ?>
23
  <script>
24
- // Wait until 'advads.privacy' is available.
25
- (window.advanced_ads_ready || jQuery(document).ready).call(null, function () {
26
- var npa_enabled = !!<?php echo $npa_enabled ? 1 : 0; ?>;
27
- if (npa_enabled || (advads.privacy && advads.privacy.get_state() !== 'unknown')) {
 
 
 
 
 
 
 
 
28
  var script = document.createElement('script'),
29
  first = document.getElementsByTagName('script')[0];
30
 
31
  script.async = true;
32
  script.src = '<?php echo esc_url( $script_src ); ?>';
33
  <?php
34
- if ( $top_anchor ) :
35
- // phpcs:disable WordPress.Security.EscapeOutput
36
- echo $top_anchor_code;
37
- // phpcs:enable
38
- else :
39
  ?>
40
- script.dataset.adClient = '<?php echo esc_attr( $client_id ); ?>';
41
- <?php endif; ?>
42
  first.parentNode.insertBefore(script, first);
43
- }
44
- });
45
  </script>
46
  <?php
47
  return;
@@ -53,9 +51,7 @@ if ( $top_anchor ) {
53
  printf(
54
  '<script async src="%s"></script><script>%s</script>',
55
  esc_attr( $script_src ),
56
- // phpcs:disable WordPress.Security.EscapeOutput
57
- $top_anchor_code
58
- // phpcs:enable WordPress.Security.EscapeOutput
59
  );
60
  } else {
61
  printf(
2
  /**
3
  * Output auto ads enabled code in head
4
  *
5
+ * @var bool $privacy_enabled Whether to wait for user consent.
6
+ * @var bool $npa_enabled Whether to show non-personalized ads.
7
+ * @var string $client_id The Google AdSense client ID.
8
+ * @var bool $top_anchor AdSense anchor ad on top of pages.
9
+ * @var string $top_anchor_code The code for top anchor ads.
10
+ * @var string $script_src AdSense script url.
11
  */
12
 
 
 
 
 
 
 
 
 
 
 
 
13
  if ( $privacy_enabled ) : ?>
14
  <script>
15
+ (function () {
16
+ var scriptDone = false;
17
+ document.addEventListener('advanced_ads_privacy', function (event) {
18
+ if (
19
+ (event.detail.state !== 'accepted' && event.detail.state !== 'not_needed' && !advads.privacy.is_adsense_npa_enabled())
20
+ || scriptDone
21
+ ) {
22
+ return;
23
+ }
24
+ // google adsense script can only be added once.
25
+ scriptDone = true;
26
+
27
  var script = document.createElement('script'),
28
  first = document.getElementsByTagName('script')[0];
29
 
30
  script.async = true;
31
  script.src = '<?php echo esc_url( $script_src ); ?>';
32
  <?php
33
+ if ( $top_anchor ) {
34
+ echo esc_attr( $top_anchor_code );
35
+ } else {
36
+ printf( 'script.dataset.adClient = "%s";', esc_attr( $client_id ) );
37
+ }
38
  ?>
39
+
 
40
  first.parentNode.insertBefore(script, first);
41
+ });
42
+ })();
43
  </script>
44
  <?php
45
  return;
51
  printf(
52
  '<script async src="%s"></script><script>%s</script>',
53
  esc_attr( $script_src ),
54
+ esc_attr( $top_anchor_code )
 
 
55
  );
56
  } else {
57
  printf(
modules/privacy/admin/admin.php CHANGED
@@ -1,179 +1,196 @@
1
  <?php
2
 
3
- class Advanced_Ads_Privacy_Admin
4
- {
 
 
5
  /**
6
  * Singleton instance of the plugin
7
  *
8
- * @var Advanced_Ads_Privacy_Admin
9
  */
10
  protected static $instance;
11
 
12
- /**
13
- * Module options
14
- *
15
- * @var array (if loaded)
16
- */
17
- protected $options;
18
-
19
  /**
20
  * Initialize the module
21
- *
22
  */
23
  private function __construct() {
24
-
25
  // add module settings to Advanced Ads settings page
26
- add_action( 'advanced-ads-settings-init', array( $this, 'settings_init' ), 20, 1 );
27
- add_filter('advanced-ads-setting-tabs', array($this, 'setting_tabs'), 20 );
28
-
29
  // additional ad options
30
- add_action('advanced-ads-ad-params-after', array($this, 'render_ad_options'), 20, 2);
31
  add_filter( 'advanced-ads-save-options', array( $this, 'save_ad_options' ), 10, 2 );
32
  }
33
 
34
  /**
35
  * Return an instance of Advanced_Ads_Privacy_Admin
36
  *
37
- * @return Advanced_Ads_Privacy_Admin
38
  */
39
  public static function get_instance() {
40
  // If the single instance hasn't been set, set it now.
41
- if (null === self::$instance)
42
- {
43
- self::$instance = new self;
44
  }
45
 
46
  return self::$instance;
47
  }
48
-
49
  /**
50
- * Add tracking settings tab
 
 
51
  *
 
52
  * @since 1.8.30
53
- * @param arr $tabs existing setting tabs
54
- * @return arr $tabs setting tabs with AdSense tab attached
55
  */
56
- public function setting_tabs(array $tabs) {
57
-
58
- $tabs['privacy'] = array(
59
- // TODO abstract string
60
- 'page' => ADVADS_PRIVACY_SLUG . '-settings',
61
- 'group' => ADVADS_PRIVACY_SLUG,
62
- 'tabid' => 'privacy',
63
- 'title' => __( 'Privacy', 'advanced-ads' )
64
- );
65
-
66
- return $tabs;
67
  }
68
-
69
  /**
70
  * Add settings to settings page
71
- *
72
- * @param string $hook settings page hook
73
  */
74
- public function settings_init($hook) {
75
-
76
  register_setting( ADVADS_PRIVACY_SLUG, Advanced_Ads_Privacy::OPTION_KEY, array( $this, 'sanitize_settings' ) );
77
 
78
- // add new section
79
  add_settings_section(
80
- ADVADS_PRIVACY_SLUG . '_settings_section', '', array($this, 'render_settings_section'), ADVADS_PRIVACY_SLUG . '-settings'
81
- );
82
-
83
- add_settings_field(
84
- 'enable-privacy-module', __('Enable Privacy module', 'advanced-ads'), array($this, 'render_settings_enable_module'), ADVADS_PRIVACY_SLUG . '-settings', ADVADS_PRIVACY_SLUG . '_settings_section'
85
  );
 
86
  add_settings_field(
87
- 'consent-method', __('Consent method', 'advanced-ads'), array($this, 'render_settings_consent_method'), ADVADS_PRIVACY_SLUG . '-settings', ADVADS_PRIVACY_SLUG . '_settings_section'
 
 
 
 
 
88
  );
89
  }
90
 
91
  /**
92
  * Sanitize settings.
93
  *
94
- * @param array $options Privacy options.
95
- * @return array $options Privacy options.
 
96
  */
97
  public function sanitize_settings( $options ) {
98
- $options['custom-cookie-name'] = isset( $options['custom-cookie-name'] ) ? trim( $options['custom-cookie-name'] ) : '';
99
  $options['custom-cookie-value'] = isset( $options['custom-cookie-value'] ) ? trim( $options['custom-cookie-value'] ) : '';
 
100
  return $options;
101
  }
102
-
103
-
104
- /**
105
- * Render settings section
106
- */
107
- public function render_settings_section() {
108
 
109
- }
110
-
111
  /**
112
  * Render enable module setting
113
  */
114
  public function render_settings_enable_module() {
115
- $options = Advanced_Ads_Privacy::get_instance()->options();
116
- $module_enabled = isset( $options['enabled']) ? $options['enabled'] : false;
117
- require ADVADS_BASE_PATH . 'modules/privacy/admin/views/setting-enable.php';
118
- }
119
-
120
- /**
121
- * Render setting to choose the cookie method to hide ads
122
- */
123
- public function render_settings_consent_method() {
124
- $options = Advanced_Ads_Privacy::get_instance()->options();
125
- $methods = Advanced_Ads_Privacy::get_instance()->get_consent_methods();
126
- $current_method = isset( $options['consent-method']) ? $options['consent-method'] : '0';
127
- $custom_cookie_name = isset( $options['custom-cookie-name'] ) ? $options['custom-cookie-name'] : '';
128
- $custom_cookie_value = isset( $options['custom-cookie-value'] ) ? $options['custom-cookie-value'] : '';
129
- $show_non_personalized_adsense = isset( $options['show-non-personalized-adsense']) ? 1 : false;
130
- require ADVADS_BASE_PATH . 'modules/privacy/admin/views/setting-consent-method.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
-
133
  /**
134
  * Add options to ad edit page
135
  *
136
- * @param obj $ad ad object
137
- * @param arr $types ad types
138
  */
139
- public function render_ad_options( $ad, $types ) {
140
-
141
- if (!isset($ad->id) || empty($ad->id)) {
142
  return;
143
  }
144
 
145
- $ad = new Advanced_Ads_Ad($ad->id);
146
- $ad_options = $ad->options();
147
- $ad_privacy_options = isset( $ad_options['privacy'] ) ? $ad_options['privacy'] : array();
148
- $ignore_consent = isset( $ad_privacy_options['ignore-consent']) ? true : false;
149
-
150
  $privacy_options = Advanced_Ads_Privacy::get_instance()->options();
151
- $module_enabled = ! empty( $privacy_options['enabled'] );
 
 
 
152
 
153
- // If the module is not enabled and the option wasn't checked before.
154
- if ( ! $module_enabled && ! $ignore_consent ) {
 
 
 
155
  return;
156
  }
157
 
158
- include ADVADS_BASE_PATH . 'modules/privacy/admin/views/setting-ad-ignore-consent.php';
159
 
 
160
  }
161
 
162
  /**
163
  * Save ad options.
164
  *
165
- * @param arr $options
166
- * @param obj $ad Advanced_Ads_Ad
167
- * @return arr $options
168
  */
169
- public function save_ad_options( $options = array(), Advanced_Ads_Ad $ad ) {
 
170
  if ( isset( $_POST['advanced_ad']['privacy'] ) ) {
171
  $options['privacy'] = $_POST['advanced_ad']['privacy'];
172
  } else {
173
  unset( $options['privacy'] );
174
  }
 
175
 
176
  return $options;
177
  }
178
-
179
  }
1
  <?php
2
 
3
+ /**
4
+ * Admin class for privacy settings.
5
+ */
6
+ class Advanced_Ads_Privacy_Admin {
7
  /**
8
  * Singleton instance of the plugin
9
  *
10
+ * @var Advanced_Ads_Privacy_Admin
11
  */
12
  protected static $instance;
13
 
 
 
 
 
 
 
 
14
  /**
15
  * Initialize the module
 
16
  */
17
  private function __construct() {
 
18
  // add module settings to Advanced Ads settings page
19
+ add_action( 'advanced-ads-settings-init', array( $this, 'settings_init' ), 20 );
20
+ add_filter( 'advanced-ads-setting-tabs', array( $this, 'setting_tabs' ), 20 );
21
+
22
  // additional ad options
23
+ add_action( 'advanced-ads-ad-params-after', array( $this, 'render_ad_options' ), 20 );
24
  add_filter( 'advanced-ads-save-options', array( $this, 'save_ad_options' ), 10, 2 );
25
  }
26
 
27
  /**
28
  * Return an instance of Advanced_Ads_Privacy_Admin
29
  *
30
+ * @return Advanced_Ads_Privacy_Admin
31
  */
32
  public static function get_instance() {
33
  // If the single instance hasn't been set, set it now.
34
+ if ( is_null( self::$instance ) ) {
35
+ self::$instance = new self();
 
36
  }
37
 
38
  return self::$instance;
39
  }
40
+
41
  /**
42
+ * Add tracking settings tab.
43
+ *
44
+ * @param array $tabs existing setting tabs.
45
  *
46
+ * @return array $tabs setting tabs with AdSense tab attached
47
  * @since 1.8.30
 
 
48
  */
49
+ public function setting_tabs( array $tabs ) {
50
+ $tabs['privacy'] = array(
51
+ 'page' => ADVADS_PRIVACY_SLUG . '-settings',
52
+ 'group' => ADVADS_PRIVACY_SLUG,
53
+ 'tabid' => 'privacy',
54
+ 'title' => __( 'Privacy', 'advanced-ads' ),
55
+ );
56
+
57
+ return $tabs;
 
 
58
  }
59
+
60
  /**
61
  * Add settings to settings page
 
 
62
  */
63
+ public function settings_init() {
 
64
  register_setting( ADVADS_PRIVACY_SLUG, Advanced_Ads_Privacy::OPTION_KEY, array( $this, 'sanitize_settings' ) );
65
 
 
66
  add_settings_section(
67
+ ADVADS_PRIVACY_SLUG . '_settings_section',
68
+ '',
69
+ '__return_empty_string',
70
+ ADVADS_PRIVACY_SLUG . '-settings'
 
71
  );
72
+
73
  add_settings_field(
74
+ 'enable-privacy-module',
75
+ __( 'Enable Privacy module', 'advanced-ads' ),
76
+ array( $this, 'render_settings_enable_module' ),
77
+ ADVADS_PRIVACY_SLUG . '-settings',
78
+ ADVADS_PRIVACY_SLUG . '_settings_section',
79
+ array( 'label_for' => Advanced_Ads_Privacy::OPTION_KEY . '_enabled' )
80
  );
81
  }
82
 
83
  /**
84
  * Sanitize settings.
85
  *
86
+ * @param array $options Privacy options.
87
+ *
88
+ * @return array
89
  */
90
  public function sanitize_settings( $options ) {
91
+ $options['custom-cookie-name'] = isset( $options['custom-cookie-name'] ) ? trim( $options['custom-cookie-name'] ) : '';
92
  $options['custom-cookie-value'] = isset( $options['custom-cookie-value'] ) ? trim( $options['custom-cookie-value'] ) : '';
93
+
94
  return $options;
95
  }
 
 
 
 
 
 
96
 
 
 
97
  /**
98
  * Render enable module setting
99
  */
100
  public function render_settings_enable_module() {
101
+ $options = Advanced_Ads_Privacy::get_instance()->options();
102
+ $module_enabled = isset( $options['enabled'] );
103
+ $methods = array(
104
+ '' => array(
105
+ 'label' => __( 'Show all ads even without consent', 'advanced-ads' ),
106
+ ),
107
+ 'custom' => array(
108
+ 'label' => __( 'Cookie', 'advanced-ads' ),
109
+ 'manual_url' => ADVADS_URL . 'manual/ad-cookie-consent/#utm_source=advanced-ads&utm_medium=link&utm_campaign=privacy-tab',
110
+ ),
111
+ 'iab_tcf_20' => array(
112
+ 'label' => __( 'TCF v2.0 integration (Quantcast, euconsent-v2, ...)', 'advanced-ads' ),
113
+ 'manual_url' => ADVADS_URL . 'manual/tcf-consent-wordpress/#utm_source=advanced-ads&utm_medium=link&utm_campaign=privacy-tab',
114
+ ),
115
+ );
116
+ $current_method = isset( $options['consent-method'] ) ? $options['consent-method'] : '';
117
+ $custom_cookie_name = isset( $options['custom-cookie-name'] ) ? $options['custom-cookie-name'] : '';
118
+ $custom_cookie_value = isset( $options['custom-cookie-value'] ) ? $options['custom-cookie-value'] : '';
119
+ $show_non_personalized_adsense = isset( $options['show-non-personalized-adsense'] );
120
+ $link_default_attrs = array(
121
+ 'href' => esc_url( ADVADS_URL . 'add-ons/advanced-ads-pro/#utm_source=advanced-ads&utm_medium=link&utm_campaign=privacy-cache' ),
122
+ 'target' => '_blank',
123
+ );
124
+ $pro_link_attrs = apply_filters( 'advanced-ads-privacy-custom-link-attributes', $link_default_attrs );
125
+ if ( ! array_key_exists( 'href', $pro_link_attrs ) ) {
126
+ $pro_link_attrs = wp_parse_args( $pro_link_attrs, $link_default_attrs );
127
+ }
128
+ $opening_link_to_pro = sprintf(
129
+ '<a %s>',
130
+ implode(
131
+ ' ',
132
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- attrs get escaped below.
133
+ array_map(
134
+ function( $key, $value ) {
135
+ return sprintf( '%s="%s"', $key, esc_attr( $value ) );
136
+ },
137
+ array_keys( $pro_link_attrs ),
138
+ $pro_link_attrs
139
+ )
140
+ )
141
+ );
142
+
143
+ wp_enqueue_script( Advanced_Ads_Privacy::OPTION_KEY, ADVADS_PRIVACY_BASE_URL . 'admin/assets/js/privacy.js', array( 'jquery' ), '1.19.1', true );
144
+ wp_localize_script( Advanced_Ads_Privacy::OPTION_KEY, 'advads_privacy', array( 'option_key' => Advanced_Ads_Privacy::OPTION_KEY ) );
145
+
146
+ require ADVADS_PRIVACY_BASE_PATH . 'admin/views/setting-general.php';
147
  }
148
+
149
  /**
150
  * Add options to ad edit page
151
  *
152
+ * @param Advanced_Ads_Ad $ad Ad object.
 
153
  */
154
+ public function render_ad_options( Advanced_Ads_Ad $ad ) {
155
+ if ( empty( $ad->id ) ) {
 
156
  return;
157
  }
158
 
 
 
 
 
 
159
  $privacy_options = Advanced_Ads_Privacy::get_instance()->options();
160
+ // module is not enabled.
161
+ if ( ! isset( $privacy_options['enabled'] ) ) {
162
+ return;
163
+ }
164
 
165
+ // Don't add override option if the ad is adsense and tcf is enabled, or if the ad is image or dummy.
166
+ if (
167
+ ( $ad->type === 'adsense' && $privacy_options['consent-method'] === 'iab_tcf_20' )
168
+ || in_array( $ad->type, array( 'image', 'dummy' ), true )
169
+ ) {
170
  return;
171
  }
172
 
173
+ $ignore_consent = isset( $ad->options()['privacy']['ignore-consent'] );
174
 
175
+ include ADVADS_PRIVACY_BASE_PATH . 'admin/views/setting-ad-ignore-consent.php';
176
  }
177
 
178
  /**
179
  * Save ad options.
180
  *
181
+ * @param array $options Current options, default empty.
182
+ *
183
+ * @return array
184
  */
185
+ public function save_ad_options( $options = array() ) {
186
+ // phpcs:disable WordPress.Security.NonceVerification.Missing
187
  if ( isset( $_POST['advanced_ad']['privacy'] ) ) {
188
  $options['privacy'] = $_POST['advanced_ad']['privacy'];
189
  } else {
190
  unset( $options['privacy'] );
191
  }
192
+ // phpcs:enable
193
 
194
  return $options;
195
  }
 
196
  }
modules/privacy/admin/assets/js/privacy.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;(function ($) {
2
+ var $cookieName = $('[name="' + window.advads_privacy.option_key + '[custom-cookie-name]'),
3
+ $method = $('[name="' + window.advads_privacy.option_key + '[consent-method]"]:checked');
4
+
5
+ // set required if radios change.
6
+ $('[name="' + window.advads_privacy.option_key + '[consent-method]"]').on('change', function () {
7
+ $method = $('[name="' + window.advads_privacy.option_key + '[consent-method]"]:checked');
8
+ $cookieName.prop('required', $method.val() === 'custom');
9
+ });
10
+
11
+ // if enabled status changes, set required.
12
+ $('[name="' + window.advads_privacy.option_key + '[enabled]"]').on('change', function () {
13
+ $cookieName.prop('required', ($(this).is(':checked') ? $method.val() === 'custom' : false));
14
+ });
15
+ })(jQuery);
modules/privacy/admin/views/setting-ad-ignore-consent.php CHANGED
@@ -1,10 +1,25 @@
 
 
 
 
 
 
 
 
1
  <div class="advads-option-list">
2
- <span class="label"><?php _e( 'privacy', 'advanced-ads' ); ?></span>
3
- <div id="advanced-ads-ad-parameters-privacy">
4
- <label>
5
- <input name="advanced_ad[privacy][ignore-consent]" type="checkbox" value="1" <?php checked( $ignore_consent, true ); ?>/>
6
- <?php printf( __( 'Ignore <a href="%s">general Privacy settings</a> and display the ad even without consent.', 'advanced-ads' ), esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ) ) ); ?>
7
- </label>
8
- </div>
 
 
 
 
 
 
 
9
  </div>
10
  <hr/>
1
+ <?php
2
+ /**
3
+ * Single ad section for overriding privacy settings.
4
+ * Not used if privacy not activated or method is 'iab_tcf_20' and ad type 'adsense'.
5
+ *
6
+ * @var bool $ignore_consent Whether to override privacy setting for this ad.
7
+ */
8
+ ?>
9
  <div class="advads-option-list">
10
+ <span class="label"><?php esc_html_e( 'privacy', 'advanced-ads' ); ?></span>
11
+ <div id="advanced-ads-ad-parameters-privacy">
12
+ <label>
13
+ <input name="advanced_ad[privacy][ignore-consent]" type="checkbox" <?php checked( $ignore_consent ); ?>/>
14
+ <?php
15
+ printf(
16
+ /* Translators: 1: a tag with link to general privacy settings, 2: closing a tag */
17
+ esc_html__( 'Ignore %1$sgeneral Privacy settings%2$s and display the ad even without consent.', 'advanced-ads' ),
18
+ '<a onclick="event.stopPropagation();" href="' . esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#privacy' ) ) . '">',
19
+ '</a>'
20
+ );
21
+ ?>
22
+ </label>
23
+ </div>
24
  </div>
25
  <hr/>
modules/privacy/admin/views/setting-consent-method.php DELETED
@@ -1,18 +0,0 @@
1
- <?php
2
- //dynamically show activated cookie consent plugins here
3
- ?>
4
- <ul>
5
- <?php foreach ( $methods as $method => $description ): ?>
6
- <li><label><input type="radio" name="<?php echo Advanced_Ads_Privacy::OPTION_KEY; ?>[consent-method]" value="<?php echo $method; ?>" <?php checked( $method , $current_method ); ?> /><?php echo $description ?></label>
7
- <?php if ( $method === 'custom' ): ?>
8
- <input type="text" name="<?php echo Advanced_Ads_Privacy::OPTION_KEY; ?>[custom-cookie-name]" value="<?php esc_attr_e( $custom_cookie_name );
9
- ?>" placeholder="<?php _e( 'Name', 'advanced-ads' ); ?>"/>
10
- <label><?php _e( 'contains', 'advanced-ads' ) ?> <input type="text" name="<?php
11
- echo Advanced_Ads_Privacy::OPTION_KEY; ?>[custom-cookie-value]" value="<?php esc_attr_e( $custom_cookie_value );
12
- ?>" placeholder="<?php _e( 'Value', 'advanced-ads' ); ?>"/> </label>
13
- <?php endif; ?>
14
- </li>
15
- <?php endforeach; ?>
16
- </ul>
17
-
18
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label><input type="checkbox" name="<?php echo Advanced_Ads_Privacy::OPTION_KEY; ?>[show-non-personalized-adsense]" value="1" <?php checked( $show_non_personalized_adsense, 1 ); ?> /><?php _e( 'Show non-personalized AdSense ads until consent is given.', 'advanced-ads' ); ?></label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/privacy/admin/views/setting-enable.php DELETED
@@ -1,7 +0,0 @@
1
- <input name="<?php echo Advanced_Ads_Privacy::OPTION_KEY; ?>[enabled]" type="checkbox" value="1" <?php checked( $module_enabled, 1 ); ?>/>&nbsp;
2
- <a href="<?php echo ADVADS_URL . 'manual/ad-cookie-consent/#utm_source=advanced-ads&utm_medium=link&utm_campaign=privacy-tab'; ?>" target="_blank"><?php _e( 'Manual', 'advanced-ads' ); ?></a>
3
- <?php if( Advanced_Ads_Checks::cache() && ! defined('AAP_VERSION') ) :
4
- ?><p><span class="advads-error-message"><?php _e( 'It seems that a caching plugin is activated.', 'advanced-ads' ); ?></span>&nbsp;<?php
5
- _e( 'Your users’ consent might get cached and show ads to users who didn’t give their consent yet. ', 'advanced-ads' );
6
- ?> <?php printf( __( 'Cache-busting in <a href="%s" target="_blank">Advanced Ads Pro</a> solves that.', 'advanced-ads' ), ADVADS_URL . 'add-ons/advanced-ads-pro/#utm_source=advanced-ads&utm_medium=link&utm_campaign=privacy-cache' ); ?></p><?php
7
- endif;
 
 
 
 
 
 
 
modules/privacy/admin/views/setting-general.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Enable Privacy Module.
4
+ *
5
+ * @var bool $module_enabled Whether the privacy module is enabled.
6
+ * @var array $methods Available privacy methods.
7
+ * @var string $current_method Currently chosen method.
8
+ * @var string $custom_cookie_name Name of custom cookie, if this setting is chosen.
9
+ * @var string $custom_cookie_value (Partial) Value of custom cookie, if this setting is chosen.
10
+ * @var bool $show_non_personalized_adsense Whether to show non-personalized ads until custom cookie consent is given.
11
+ * @var string $opening_link_to_pro Opening link tag for link to Pro (either upsell or settings).
12
+ */
13
+ ?>
14
+ <input name="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>[enabled]" id="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>_enabled" type="checkbox" <?php checked( $module_enabled ); ?> class="advads-has-sub-settings"/>
15
+ <label for="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>_enabled">
16
+ <?php esc_html_e( 'Show ads only to users who give their permission to cookies and ads.', 'advanced-ads' ); ?>
17
+ </label>
18
+
19
+ <div class="advads-sub-settings">
20
+ <h4><?php esc_html_e( 'Consent method', 'advanced-ads' ); ?></h4>
21
+ <ul>
22
+ <?php
23
+ foreach ( $methods as $method => $options ) :
24
+ $checked = checked( $method, $current_method, false );
25
+ ?>
26
+ <li>
27
+ <label>
28
+ <input type="radio" name="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>[consent-method]" value="<?php echo esc_attr( $method ); ?>" <?php echo esc_attr( $checked ); ?> />
29
+ <?php
30
+ echo esc_html( $options['label'] );
31
+ if ( ! empty( $options['manual_url'] ) ) :
32
+ ?>
33
+ &ndash; <a href="<?php echo esc_url( $options['manual_url'] ); ?>" target="_blank">
34
+ <?php esc_html_e( 'Manual', 'advanced-ads' ); ?>
35
+ </a>
36
+ <?php endif; ?>
37
+ </label>
38
+
39
+ <?php if ( $method === 'custom' ) : ?>
40
+
41
+ <div style="margin: 10px 24px;">
42
+ <label>
43
+ <?php esc_html_e( 'Cookie name', 'advanced-ads' ); ?>
44
+ <input type="text" name="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>[custom-cookie-name]" value="<?php echo esc_attr( $custom_cookie_name ); ?>" placeholder="<?php esc_attr_e( 'Name', 'advanced-ads' ); ?>" <?php echo $method === $current_method ? 'required' : ''; ?>/>
45
+ </label>
46
+ <label>
47
+ <?php esc_html_e( 'contains', 'advanced-ads' ); ?>
48
+ <?php esc_html_e( 'value', 'advanced-ads' ); ?>
49
+ <input type="text" name="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>[custom-cookie-value]" value="<?php echo esc_attr( $custom_cookie_value ); ?>" placeholder="<?php esc_attr_e( 'Value', 'advanced-ads' ); ?>"/>
50
+ </label>
51
+ <label style="display: block; margin-top: 5px; margin-bottom: 7px;">
52
+ <input type="checkbox" name="<?php echo esc_attr( Advanced_Ads_Privacy::OPTION_KEY ); ?>[show-non-personalized-adsense]" <?php checked( $show_non_personalized_adsense ); ?> />
53
+ <?php esc_html_e( 'Show non-personalized AdSense ads until consent is given.', 'advanced-ads' ); ?>
54
+ </label>
55
+ </div>
56
+
57
+ <?php if ( apply_filters( 'advanced-ads-privacy-custom-show-warning', ! empty( $checked ) && Advanced_Ads_Checks::cache() ) ) : ?>
58
+ <p class="description" style="margin: 5px 0 10px 23px;">
59
+ <span class="advads-error-message"><?php esc_html_e( 'It seems that a caching plugin is activated.', 'advanced-ads' ); ?></span>
60
+ <br>
61
+ <?php
62
+ esc_html_e( 'Your users’ consent might get cached and show ads to users who didn’t give their consent yet. ', 'advanced-ads' );
63
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- attributes already escaped.
64
+ echo $opening_link_to_pro;
65
+ esc_html_e( 'Cache-busting in Advanced Ads Pro solves that.', 'advanced-ads' );
66
+ echo '</a>';
67
+ ?>
68
+ </p>
69
+ <?php endif; ?>
70
+ <?php elseif ( $method === 'iab_tcf_20' ) : ?>
71
+ <?php if ( apply_filters( 'advanced-ads-privacy-tcf-show-warning', ! empty( $checked ) ) ) : ?>
72
+ <p class="description" style="margin: 5px 0 10px 23px;">
73
+ <?php
74
+ esc_html_e( 'Ads are loaded after the user gives their consent and reloads the page.', 'advanced-ads' );
75
+ echo ' ';
76
+ printf(
77
+ /* Translators: 1: opening link tag with link to Advanced Ads Pro 2: closing link tag */
78
+ esc_html__( 'Install %1$sAdvanced Ads Pro%2$s to reload the ads instantly without an additional page request.', 'advanced-ads' ),
79
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- attributes already escaped.
80
+ $opening_link_to_pro,
81
+ '</a>'
82
+ );
83
+ ?>
84
+ </p>
85
+ <?php endif; ?>
86
+ <?php
87
+ if ( method_exists( 'Advanced_Ads_Tracking_Admin', 'get_instance' ) ) :
88
+ $tracking_admin = Advanced_Ads_Tracking_Admin::get_instance();
89
+ if ( $tracking_admin->has_tcf_conflict() ) :
90
+ ?>
91
+ <p class="description advads-error-message">
92
+ <?php echo esc_html( $tracking_admin->get_tcf_conflict_notice() ); ?>
93
+ </p>
94
+ <?php
95
+ endif;
96
+ endif;
97
+ ?>
98
+ <?php endif; ?>
99
+ </li>
100
+ <?php endforeach; ?>
101
+ </ul>
102
+ </div>
modules/privacy/classes/class-privacy.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handles Advanced Ads privacy settings.
5
+ */
6
+ class Advanced_Ads_Privacy {
7
+ /**
8
+ * Singleton instance of the plugin
9
+ *
10
+ * @var self
11
+ */
12
+ protected static $instance;
13
+
14
+ /**
15
+ * Module options
16
+ *
17
+ * @var null|array
18
+ */
19
+ protected $options;
20
+
21
+ /**
22
+ * Option key
23
+ *
24
+ * @const string
25
+ */
26
+ const OPTION_KEY = 'advanced-ads-privacy';
27
+
28
+ /**
29
+ * Initialize the module
30
+ */
31
+ private function __construct() {
32
+ add_filter( 'advanced-ads-can-display', array( $this, 'can_display_by_consent' ), 10, 3 );
33
+
34
+ $this->options();
35
+
36
+ if ( ! empty( $this->options['enabled'] ) ) {
37
+ add_filter( 'advanced-ads-activate-advanced-js', '__return_true' );
38
+
39
+ if ( $this->options['consent-method'] === 'iab_tcf_20' ) {
40
+ add_filter( 'advanced-ads-output-final', array( $this, 'encode_final_ad_output' ), 10, 2 );
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * If this ad is not image or dummy base64_encode the text.
47
+ *
48
+ * @param string $output The output string.
49
+ * @param Advanced_Ads_Ad $ad The ad object.
50
+ *
51
+ * @return string
52
+ */
53
+ public function encode_final_ad_output( $output, Advanced_Ads_Ad $ad ) {
54
+ if (
55
+ advads_is_amp()
56
+ // Never encode image or dummy ads.
57
+ || in_array( $ad->type, array( 'image', 'dummy' ), true )
58
+ // Consent is overridden, and this is not an AdSense ad, don't encode it.
59
+ || ( $ad->type !== 'adsense' && isset( $ad->options()['privacy']['ignore-consent'] ) )
60
+ ) {
61
+ return $output;
62
+ }
63
+
64
+ return sprintf(
65
+ '<script type="text/plain" data-tcf="waiting-for-consent" data-id="%d" data-bid="%s" %s>%s</script>',
66
+ $ad->id,
67
+ get_current_blog_id(),
68
+ ( ! empty( $ad->output['placement_id'] ) ? 'data-placement="' . $ad->output['placement_id'] . '"' : '' ),
69
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- we need to obfuscate the html (and maybe decode it in JS).
70
+ base64_encode( $output )
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Return an instance of Advanced_Ads_Privacy
76
+ *
77
+ * @return self
78
+ */
79
+ public static function get_instance() {
80
+ // If the single instance hasn't been set, set it now.
81
+ if ( null === self::$instance ) {
82
+ self::$instance = new self();
83
+ }
84
+
85
+ return self::$instance;
86
+ }
87
+
88
+ /**
89
+ * Return module options
90
+ *
91
+ * @return array
92
+ */
93
+ public function options() {
94
+ if ( ! isset( $this->options ) ) {
95
+ $this->options = get_option( self::OPTION_KEY, array() );
96
+ if ( isset( $this->options['enabled'] ) && empty( $this->options['consent-method'] ) ) {
97
+ $this->options['enabled'] = false;
98
+ }
99
+ }
100
+
101
+ return $this->options;
102
+ }
103
+
104
+ /**
105
+ * Check if ad can be displayed based on user's consent.
106
+ *
107
+ * @param bool $can_display Whether to display this ad.
108
+ * @param Advanced_Ads_Ad $ad The ad object.
109
+ * @param array $check_options Additional options passed to can_display.
110
+ *
111
+ * @return bool
112
+ */
113
+ public function can_display_by_consent( $can_display, Advanced_Ads_Ad $ad, $check_options ) {
114
+ // already false, honor this.
115
+ if ( ! $can_display ) {
116
+ return $can_display;
117
+ }
118
+
119
+ // passive cache busting enabled.
120
+ if ( $check_options['passive_cache_busting'] ) {
121
+ return true;
122
+ }
123
+
124
+ // privacy module not active, bail early.
125
+ if ( empty( $this->options['enabled'] ) ) {
126
+ return true;
127
+ }
128
+
129
+ // If consent is overriden for the ad.
130
+ if ( ! empty( $ad->options()['privacy']['ignore-consent'] ) ) {
131
+ return true;
132
+ }
133
+
134
+ // If method is iab_tcf_20, always set to true, JS needs to decide whether to display ad or not.
135
+ $consent_method = isset( $this->options['consent-method'] ) ? $this->options['consent-method'] : '';
136
+ if ( $consent_method === 'iab_tcf_20' ) {
137
+ return true;
138
+ }
139
+
140
+ // Either personalized or non-personalized ad will be shown.
141
+ if ( $ad->type === 'adsense' && ! empty( $this->options()['show-non-personalized-adsense'] ) ) {
142
+ return true;
143
+ }
144
+
145
+ return $this->get_state() !== 'unknown';
146
+ }
147
+
148
+ /**
149
+ * Check if consent is not needed or was given by the user.
150
+ *
151
+ * @return string
152
+ * 'not_needed' - consent is not needed.
153
+ * 'accepted' - consent was given.
154
+ * 'unknown' - consent was not given yet.
155
+ */
156
+ public function get_state() {
157
+ static $state;
158
+ if ( is_null( $state ) ) {
159
+ $state = $this->parse_state();
160
+ }
161
+
162
+ return $state;
163
+ }
164
+
165
+ /**
166
+ * Used by get_state() to parse the state of privacy/consent.
167
+ *
168
+ * @return string
169
+ * 'not_needed' - consent is not needed.
170
+ * 'accepted' - consent was given.
171
+ * 'unknown' - consent was not given yet.
172
+ */
173
+ private function parse_state() {
174
+ if ( empty( $this->options['enabled'] ) || advads_is_amp() ) {
175
+ return 'not_needed';
176
+ }
177
+
178
+ $consent_method = isset( $this->options['consent-method'] ) ? $this->options['consent-method'] : '';
179
+ switch ( $consent_method ) {
180
+ case 'custom':
181
+ if ( empty( $this->options['custom-cookie-name'] ) ) {
182
+ return 'not_needed';
183
+ }
184
+
185
+ $name = $this->options['custom-cookie-name'];
186
+ if ( ! isset( $_COOKIE[ $name ] ) ) {
187
+ return 'unknown';
188
+ }
189
+
190
+ $value = isset( $this->options['custom-cookie-value'] ) ? $this->options['custom-cookie-value'] : '';
191
+ if (
192
+ ( $value === '' && $_COOKIE[ $name ] === '' )
193
+ || ( $value !== '' && strpos( $_COOKIE[ $name ], $value ) !== false )
194
+ ) {
195
+ return 'accepted';
196
+ }
197
+
198
+ return 'unknown';
199
+ case 'iab_tcf_20':
200
+ return 'unknown';
201
+ default:
202
+ return isset( $_COOKIE[ $consent_method ] ) ? 'accepted' : 'unknown';
203
+ }
204
+ }
205
+ }
modules/privacy/classes/plugin.php DELETED
@@ -1,168 +0,0 @@
1
- <?php
2
- class Advanced_Ads_Privacy
3
- {
4
- /**
5
- * Singleton instance of the plugin
6
- *
7
- * @var Advanced_Ads_Privacy
8
- */
9
- protected static $instance;
10
-
11
- /**
12
- * Module options
13
- *
14
- * @var array (if loaded)
15
- */
16
- protected $options;
17
-
18
- /**
19
- * Option key
20
- *
21
- * @const array
22
- */
23
- const OPTION_KEY = 'advanced-ads-privacy';
24
-
25
- /**
26
- * Initialize the module
27
- */
28
- private function __construct() {
29
- $options = $this->options();
30
-
31
- add_filter( 'advanced-ads-can-display', array( $this, 'can_display_by_consent' ), 10, 3 );
32
-
33
- if ( ! empty( $options['enabled'] ) ) {
34
- add_filter( 'advanced-ads-activate-advanced-js', '__return_true' );
35
- }
36
- }
37
-
38
- /**
39
- * Return an instance of Advanced_Ads_Privacy
40
- *
41
- * @return Advanced_Ads_Privacy
42
- */
43
- public static function get_instance() {
44
- // If the single instance hasn't been set, set it now.
45
- if ( null === self::$instance )
46
- {
47
- self::$instance = new self;
48
- }
49
-
50
- return self::$instance;
51
- }
52
-
53
- /**
54
- * Return module options
55
- *
56
- * @return array $options
57
- */
58
- public function options() {
59
- if ( ! isset( $this->options ) ) {
60
- $this->options = get_option( self::OPTION_KEY, array() );
61
- }
62
- return $this->options;
63
- }
64
-
65
- /**
66
- * Check if ad can be displayed based on user's consent.
67
- *
68
- * @return bool
69
- */
70
- public function can_display_by_consent( $can_display, Advanced_Ads_Ad $ad, $check_options ) {
71
-
72
- if ( ! $can_display ) {
73
- return $can_display;
74
- }
75
-
76
- if ( $check_options['passive_cache_busting'] ) {
77
- return true;
78
- }
79
-
80
- $ad_options = $ad->options();
81
- // If consent is not needed for the ad.
82
- if ( ! empty( $ad_options['privacy']['ignore-consent'] ) ) {
83
- return true;
84
- }
85
-
86
- $privacy_options = $this->options();
87
- if ( $ad->type === 'adsense' ) {
88
- if ( ! empty( $privacy_options['show-non-personalized-adsense'] ) ) {
89
- // Either personalized or non-personalized ad will be shown.
90
- return true;
91
- }
92
- }
93
-
94
- $state = $this->get_state();
95
- return ( $state === 'accepted' || $state === 'not_needed' );
96
- }
97
-
98
- /**
99
- * Check if consent is not needed or was given by the user.
100
- *
101
- * @return str
102
- * 'not_needed' - consent is not needed.
103
- * 'accepted' - consent was given.
104
- * 'unknown' - consent was not given yet.
105
- */
106
- public function get_state() {
107
- if ( empty ( $this->options['enabled'] ) ) {
108
- return 'not_needed';
109
- }
110
-
111
- if ( advads_is_amp() ) {
112
- return 'not_needed';
113
- }
114
-
115
- $consent_method = isset( $this->options['consent-method'] ) ? $this->options['consent-method'] : 0;
116
- switch ( $consent_method ) {
117
- case '0':
118
- return 'not_needed';
119
- break;
120
- case 'custom':
121
- if ( ! isset( $this->options['custom-cookie-name'] ) || ! isset( $this->options['custom-cookie-value'] ) ) {
122
- return 'not_needed';
123
- }
124
- $name = $this->options['custom-cookie-name'];
125
- $value = $this->options['custom-cookie-value'];
126
- if ( ! isset( $_COOKIE[ $name ] ) ) {
127
- return 'unknown';
128
- }
129
-
130
- if ( ( $value === '' && $_COOKIE[ $name ] === '' )
131
- || ($value !== '' && strpos( $_COOKIE[ $name ], $value ) !== false ) ) {
132
- return 'accepted';
133
- }
134
- return 'unknown';
135
- break;
136
- default:
137
- return isset( $_COOKIE[ $consent_method ] ) ? 'accepted' : 'unknown';
138
- break;
139
- }
140
- }
141
-
142
- /**
143
- * Get consent methods.
144
- *
145
- * @return arr
146
- */
147
- public function get_consent_methods() {
148
- $methods = array(
149
- '0' => __( 'Show all ads even without consent.', 'advanced-ads' ),
150
- 'custom' => __( 'Cookie', 'advanced-ads' ),
151
- );
152
- /*
153
- // https://wordpress.org/plugins/cookie-notice/
154
- if ( class_exists( 'Cookie_Notice' ) ) {
155
- $methods['cookie_notice_accepted'] = 'Cookie Notice by dFactory';
156
- }
157
- // https://wordpress.org/plugins/uk-cookie-consent/
158
- if ( defined( 'CTCC_PLUGIN_URL' ) ) {
159
- $methods['catAccCookies'] = 'Cookie Consent';
160
- }
161
- // https://wordpress.org/plugins/cookie-law-info/
162
- if ( defined( 'CLI_PLUGIN_DEVELOPMENT_MODE' ) ) {
163
- $methods['viewed_cookie_policy'] = 'GDPR Cookie Consent';
164
- }
165
- */
166
- return $methods;
167
- }
168
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/privacy/config.php CHANGED
@@ -1,13 +1,10 @@
1
  <?php
2
 
3
  // module configuration
4
-
5
- $path = dirname( __FILE__ );
6
-
7
  return array(
8
- 'classmap' => array(
9
- 'Advanced_Ads_Privacy' => $path . '/classes/plugin.php',
10
- 'Advanced_Ads_Privacy_Admin' => $path . '/admin/admin.php',
11
  ),
12
  'textdomain' => null,
13
- );
1
  <?php
2
 
3
  // module configuration
 
 
 
4
  return array(
5
+ 'classmap' => array(
6
+ 'Advanced_Ads_Privacy' => __DIR__ . '/classes/class-privacy.php',
7
+ 'Advanced_Ads_Privacy_Admin' => __DIR__ . '/admin/admin.php',
8
  ),
9
  'textdomain' => null,
10
+ );
modules/privacy/main.php CHANGED
@@ -3,16 +3,18 @@ if ( class_exists( 'Advanced_Ads', false ) ) {
3
 
4
  // only load if not already existing (maybe included from another plugin)
5
  if ( defined( 'ADVADS_PRIVACY_SLUG' ) ) {
6
- return ;
7
  }
8
 
9
  // general and global slug, e.g. to store options in WP
10
  define( 'ADVADS_PRIVACY_SLUG', 'advanced-ads-privacy' );
11
-
 
 
12
  Advanced_Ads_Privacy::get_instance();
13
-
14
  if ( is_admin() ) {
15
- Advanced_Ads_Privacy_Admin::get_instance();
16
  }
17
  }
18
 
3
 
4
  // only load if not already existing (maybe included from another plugin)
5
  if ( defined( 'ADVADS_PRIVACY_SLUG' ) ) {
6
+ return;
7
  }
8
 
9
  // general and global slug, e.g. to store options in WP
10
  define( 'ADVADS_PRIVACY_SLUG', 'advanced-ads-privacy' );
11
+ define( 'ADVADS_PRIVACY_BASE_PATH', plugin_dir_path( __FILE__ ) );
12
+ define( 'ADVADS_PRIVACY_BASE_URL', plugins_url( basename( ADVADS_BASE_PATH ) . '/modules/' . basename( ADVADS_PRIVACY_BASE_PATH ) . '/' ) );
13
+
14
  Advanced_Ads_Privacy::get_instance();
15
+
16
  if ( is_admin() ) {
17
+ Advanced_Ads_Privacy_Admin::get_instance();
18
  }
19
  }
20
 
public/assets/js/advanced.js CHANGED
@@ -1 +1 @@
1
- advads={supports_localstorage:function(){"use strict";try{if(!window||window.localStorage===undefined){return false}window.localStorage.setItem("x","x");window.localStorage.removeItem("x");return true}catch(e){return false}},max_per_session:function(name,max){var num=1;if(max===undefined||parseInt(max)===0){max=1}if(this.cookie_exists(name)){if(this.get_cookie(name)>=max){return true}num=num+parseInt(this.get_cookie(name))}this.set_cookie(name,num);return false},count_up:function(name,exdays){var num=1;if(this.cookie_exists(name)){num=num+parseInt(this.get_cookie(name))}this.set_cookie(name,num)},set_cookie_exists:function(name){if(get_cookie(name)){return true}set_cookie(name,"",0);return false},get_cookie:function(name){var i,x,y,ADVcookies=document.cookie.split(";");for(i=0;i<ADVcookies.length;i++){x=ADVcookies[i].substr(0,ADVcookies[i].indexOf("="));y=ADVcookies[i].substr(ADVcookies[i].indexOf("=")+1);x=x.replace(/^\s+|\s+$/g,"");if(x===name){return unescape(y)}}},set_cookie:function(name,value,exdays,path,domain,secure){var expiry=exdays==null?null:exdays*24*60*60;this.set_cookie_sec(name,value,expiry,path,domain,secure)},set_cookie_sec:function(name,value,expiry,path,domain,secure){var exdate=new Date;exdate.setSeconds(exdate.getSeconds()+parseInt(expiry));document.cookie=name+"="+escape(value)+(expiry==null?"":"; expires="+exdate.toUTCString())+(path==null?"; path=/":"; path="+path)+(domain==null?"":"; domain="+domain)+(secure==null?"":"; secure")},cookie_exists:function(name){var c_value=this.get_cookie(name);if(c_value!==null&&c_value!==""&&c_value!==undefined){return true}return false},move:function(element,target,options){var el=jQuery(element);var target_string=target;if(typeof options==="undefined"){options={}}if(typeof options.css==="undefined"){options.css={}}if(typeof options.method==="undefined"){options.method="prependTo"}if(target===""&&typeof options.target!=="undefined"){switch(options.target){case"wrapper":var offset="left";if(typeof options.offset!=="undefined"){offset=options.offset}target=this.find_wrapper(element,offset);break}}if(typeof options.moveintohidden==="undefined"){target=jQuery(target).filter(":visible")}else{target=jQuery(target)}if(target.length>1){console.log("Advanced Ads: element '"+target_string+"' found "+target.length+" times.")}switch(options.method){case"insertBefore":el.insertBefore(target);break;case"insertAfter":el.insertAfter(target);break;case"appendTo":el.appendTo(target);break;case"prependTo":el.prependTo(target);break;default:el.prependTo(target)}},set_parent_relative:function(element,options){var options=typeof options!=="undefined"?options:{};var el=jQuery(element);var parent=el.parent();if(options.use_grandparent){parent=parent.parent()}if(parent.css("position")==="static"||parent.css("position")===""){parent.css("position","relative")}},fix_element:function(element,options){var options=typeof options!=="undefined"?options:{};var el=jQuery(element);if(options.use_grandparent){this.set_parent_relative(el.parent())}else{this.set_parent_relative(el)}if(options.is_invisible){el.show()}var topoffset=parseInt(el.offset().top);var leftoffset=parseInt(el.offset().left);if(options.is_invisible){el.hide()}if("left"===options.offset){var rightoffset=jQuery(window).width()-leftoffset-el.outerWidth();el.css("position","fixed").css("top",topoffset+"px").css("right",rightoffset+"px").css("left","")}else{el.css("position","fixed").css("top",topoffset+"px").css("left",leftoffset+"px").css("right","")}},find_wrapper:function(element,offset){var returnValue;jQuery("body").children().each(function(key,value){if(value.id!==element.substring(1)){var checkedelement=jQuery(value);if(offset==="right"&&checkedelement.offset().left+jQuery(checkedelement).width()<jQuery(window).width()||offset==="left"&&checkedelement.offset().left>0){if(checkedelement.css("position")==="static"||checkedelement.css("position")===""){checkedelement.css("position","relative")}returnValue=value;return false}}});return returnValue},center_fixed_element:function(element){var el=jQuery(element);var left=jQuery(window).width()/2-parseInt(el.css("width"))/2;el.css("left",left+"px")},center_vertically:function(element){var el=jQuery(element);var left=jQuery(window).height()/2-parseInt(el.css("height"))/2;if(el.css("position")!=="fixed"){left-=topoffset=parseInt(el.offset().top)}el.css("top",left+"px")},close:function(element){var wrapper=jQuery(element);wrapper.remove()},wait_for_images:function($el,ready_callback){var loaded_count=0;var srcs=[];$el.find('img[src][src!=""]').each(function(){srcs.push(this.src)});if(srcs.length===0){ready_callback.call($el)}jQuery.each(srcs,function(i,src){var image=new Image;image.src=src;var events="load error";jQuery(image).one(events,function me(event){jQuery(this).off(events,me);loaded_count++;if(loaded_count==srcs.length){ready_callback.call($el[0]);return false}})})},privacy:{get_state:function(){if(!window.advads_options||!window.advads_options.privacy){return"not_needed"}var options=window.advads_options.privacy;if(!options.enabled){return"not_needed"}var method=options["consent-method"]?options["consent-method"]:"0";switch(method){case"0":return"not_needed";break;case"custom":if(options["custom-cookie-value"===undefined]||options["custom-cookie-value"]===undefined){return"not_needed"}var found=advads.get_cookie(options["custom-cookie-name"]);if(typeof found!=="string"){return"unknown"}if(options["custom-cookie-value"]===""&&found===""||options["custom-cookie-value"]!==""&&found.indexOf(options["custom-cookie-value"])!==-1){return"accepted"}return"unknown";break;default:return advads.cookie_exists(method)?"accepted":"unknown"}},is_adsense_npa_enabled:function(){if(!window.advads_options||!window.advads_options.privacy){return true}var options=window.advads_options.privacy;return!!options["show-non-personalized-adsense"]}}};jQuery(document).ready(function(){function is_enabled(){if(!advads.supports_localstorage()||!localStorage.getItem("advads_frontend_picker")){return false}if(window.advanced_ads_data&&advanced_ads_data.blog_id&&localStorage.getItem("advads_frontend_blog_id")&&advanced_ads_data.blog_id!==localStorage.getItem("advads_frontend_blog_id")){return false}if(localStorage.getItem("advads_frontend_starttime")&&parseInt(localStorage.getItem("advads_frontend_starttime"),10)<(new Date).getTime()-45*60*1e3){localStorage.removeItem("advads_frontend_action");localStorage.removeItem("advads_frontend_element");localStorage.removeItem("advads_frontend_picker");localStorage.removeItem("advads_prev_url");localStorage.removeItem("advads_frontend_pathtype");localStorage.removeItem("advads_frontend_boundary");localStorage.removeItem("advads_frontend_blog_id");localStorage.removeItem("advads_frontend_starttime");advads.set_cookie("advads_frontend_picker","",-1);return false}return true}if(is_enabled()){var advads_picker_cur,advads_picker_overlay=jQuery("<div id='advads-picker-overlay'>"),advads_picker_no=[document.body,document.documentElement,document];advads_picker_overlay.css({position:"absolute",border:"solid 2px #428bca",backgroundColor:"rgba(66,139,202,0.5)",boxSizing:"border-box",zIndex:1e6,pointerEvents:"none"}).prependTo("body");if("true"===localStorage.getItem("advads_frontend_boundary")){jQuery("body").css("cursor","not-allowed")}window.advads.is_boundary_reached=function(advads_picker_cur){if("true"!==localStorage.getItem("advads_frontend_boundary")){return false}$advads_picker_cur=jQuery(advads_picker_cur);var $boundary_helpers=jQuery(".advads-frontend-picker-boundary-helper");$boundaries=$boundary_helpers.parent();$boundaries.css("cursor","pointer");return $advads_picker_cur.is($boundaries)||!$advads_picker_cur.closest($boundaries).length};if("xpath"===localStorage.getItem("advads_frontend_pathtype")){var fn="getXPath"}else{var fn="getPath"}jQuery(document).mousemove(function(e){if(e.target===advads_picker_cur){return}if(~advads_picker_no.indexOf(e.target)){advads_picker_cur=null;advads_picker_overlay.hide();return}var target=jQuery(e.target),offset=target.offset(),width=target.outerWidth(),height=target.outerHeight();advads_picker_cur=e.target;var path=jQuery(advads_picker_cur)[fn]();if(!path){return}console.log(path);advads_picker_overlay.css({top:offset.top,left:offset.left,width:width,height:height}).show()});jQuery(document).click(function(e){var path=jQuery(advads_picker_cur)[fn]();if(advads.is_boundary_reached(advads_picker_cur)){return}localStorage.setItem("advads_frontend_element",path);window.location=localStorage.getItem("advads_prev_url")})}});jQuery.fn.extend({getPath:function(path,depth){if(typeof path==="undefined")path="";if(typeof depth==="undefined")depth=0;if(this.is("html")){return"html > "+path}else if(3===depth){return path}var cur=this.get(0).nodeName.toLowerCase();var el_id=this.attr("id"),el_class=this.attr("class");depth=depth+1;if(typeof el_id!=="undefined"&&!/\d/.test(el_id)){cur+="#"+el_id}else if(typeof el_class!=="undefined"){el_class=el_class.split(/[\s\n]+/);el_class=jQuery.grep(el_class,function(element,index){return!/\d/.test(element)});if(el_class.length){cur+="."+el_class.slice(0,2).join(".")}}if(this.siblings(cur).length){cur+=":eq("+this.siblings(cur).addBack().not("#advads-picker-overlay").index(this)+")"}if(path===""){return this.parent().getPath(cur,depth)}else{return this.parent().getPath(cur+" > "+path,depth)}},getXPath:function(path,depth){if(typeof path==="undefined")path="";if(typeof depth==="undefined")depth=0;if(this.is("body")||3===depth){return path}if(advads.is_boundary_reached(this)){return path}var tag=this.get(0).nodeName.toLowerCase();var cur=tag;var el_id=this.attr("id"),el_class=this.attr("class");var classes=[];if(typeof el_id!=="undefined"&&!/\d/.test(el_id)){return cur+'[@id and id="'+el_id+'"]/'+path}else if(typeof el_class!=="undefined"){el_class=el_class.split(/[\s\n]+/);el_class=jQuery.grep(el_class,function(element,index){return!/\d/.test(element)});if(el_class.length){depth=depth+1;var classes=el_class.slice(0,2);var xpath_classes=[];for(var i=0;i<classes.length;i++){xpath_classes.push('(@class and contains(concat(" ", normalize-space(@class), " "), " '+classes[i]+' "))')}cur+="["+xpath_classes.join(" and ")+"]"}}if(classes.length){var $siblings=this.siblings(tag+"."+classes.join("."))}else{var $siblings=this.siblings(tag)}if($siblings.length){var index=$siblings.addBack().not("#advads-picker-overlay").index(this);cur+="["+index+"]"}if(path===""){return this.parent().getXPath(cur,depth)}else{return this.parent().getXPath(cur+"/"+path,depth)}}});
1
+ advads={supports_localstorage:function(){"use strict";try{return window&&void 0!==window.localStorage?(window.localStorage.setItem("x","x"),window.localStorage.removeItem("x"),!0):!1}catch(e){return!1}},max_per_session:function(e,t){var a=1;if(void 0!==t&&0!==parseInt(t)||(t=1),this.cookie_exists(e)){if(this.get_cookie(e)>=t)return!0;a+=parseInt(this.get_cookie(e))}return this.set_cookie(e,a),!1},count_up:function(e,t){var a=1;this.cookie_exists(e)&&(a+=parseInt(this.get_cookie(e))),this.set_cookie(e,a)},set_cookie_exists:function(e){return!!get_cookie(e)||(set_cookie(e,"",0),!1)},get_cookie:function(e){for(var t,a,o=document.cookie.split(";"),n=0;n<o.length;n++)if(t=o[n].substr(0,o[n].indexOf("=")),a=o[n].substr(o[n].indexOf("=")+1),(t=t.replace(/^\s+|\s+$/g,""))===e)return unescape(a)},set_cookie:function(e,t,a,o,n,r){var i=null==a?null:24*a*60*60;this.set_cookie_sec(e,t,i,o,n,r)},set_cookie_sec:function(e,t,a,o,n,r){var i=new Date;i.setSeconds(i.getSeconds()+parseInt(a)),document.cookie=e+"="+escape(t)+(null==a?"":"; expires="+i.toUTCString())+(null==o?"; path=/":"; path="+o)+(null==n?"":"; domain="+n)+(null==r?"":"; secure")},cookie_exists:function(e){var t=this.get_cookie(e);return null!==t&&""!==t&&void 0!==t},move:function(e,t,a){var o=jQuery(e),n=t;if(void 0===a&&(a={}),void 0===a.css&&(a.css={}),void 0===a.method&&(a.method="prependTo"),""===t&&void 0!==a.target)switch(a.target){case"wrapper":var r="left";void 0!==a.offset&&(r=a.offset),t=this.find_wrapper(e,r)}switch(1<(t=void 0===a.moveintohidden?jQuery(t).filter(":visible"):jQuery(t)).length&&console.log("Advanced Ads: element '"+n+"' found "+t.length+" times."),a.method){case"insertBefore":o.insertBefore(t);break;case"insertAfter":o.insertAfter(t);break;case"appendTo":o.appendTo(t);break;case"prependTo":o.prependTo(t);break;default:o.prependTo(t)}},set_parent_relative:function(e,t){var t=void 0!==t?t:{},a=jQuery(e).parent();t.use_grandparent&&(a=a.parent()),"static"!==a.css("position")&&""!==a.css("position")||a.css("position","relative")},fix_element:function(e,t){var t=void 0!==t?t:{},a=jQuery(e);t.use_grandparent?this.set_parent_relative(a.parent()):this.set_parent_relative(a),t.is_invisible&&a.show();var o,n=parseInt(a.offset().top),r=parseInt(a.offset().left);t.is_invisible&&a.hide(),"left"===t.offset?(o=jQuery(window).width()-r-a.outerWidth(),a.css("position","fixed").css("top",n+"px").css("right",o+"px").css("left","")):a.css("position","fixed").css("top",n+"px").css("left",r+"px").css("right","")},find_wrapper:function(o,n){var r;return jQuery("body").children().each(function(e,t){if(t.id!==o.substring(1)){var a=jQuery(t);if("right"===n&&a.offset().left+jQuery(a).width()<jQuery(window).width()||"left"===n&&0<a.offset().left)return"static"!==a.css("position")&&""!==a.css("position")||a.css("position","relative"),r=t,!1}}),r},center_fixed_element:function(e){var t=jQuery(e),a=jQuery(window).width()/2-parseInt(t.css("width"))/2;t.css("left",a+"px")},center_vertically:function(e){var t=jQuery(e),a=jQuery(window).height()/2-parseInt(t.css("height"))/2;"fixed"!==t.css("position")&&(a-=topoffset=parseInt(t.offset().top)),t.css("top",a+"px")},close:function(e){jQuery(e).remove()},wait_for_images:function(n,r){var i=0,s=[];n.find('img[src][src!=""]').each(function(){s.push(this.src)}),0===s.length&&r.call(n),jQuery.each(s,function(e,t){var a=new Image;a.src=t;var o="load error";jQuery(a).one(o,function e(t){if(jQuery(this).off(o,e),++i==s.length)return r.call(n[0]),!1})})},privacy:{state:"unknown",state_executed:!1,get_state:function(){if("unknown"!==window.advads_options.privacy.state)return advads.privacy.state_executed||(advads.privacy.state_executed=!0,advads.privacy.dispatch_event(window.advads_options.privacy.state,!1)),advads.privacy.state;advads.privacy.state_executed=!0;var t=0,a=setInterval(function(){switch(600===t&&clearInterval(a),window.advads_options.privacy["consent-method"]){case"custom":var e=new RegExp(window.advads_options.privacy["custom-cookie-name"]+"=.*?"+window.advads_options.privacy["custom-cookie-value"]+"[^;]*");null!==document.cookie.match(e)&&(clearInterval(a),"accepted"!==advads.privacy.state&&advads.privacy.dispatch_event("accepted",!0));break;case"iab_tcf_20":if(void 0===window.__tcfapi)return;clearInterval(a),window.__tcfapi("addEventListener",2,function(e,t){if(t&&("tcloaded"===e.eventStatus||"useractioncomplete"===e.eventStatus)){var a="useractioncomplete"===e.eventStatus;if(!e.gdprApplies)return void("not_needed"!==advads.privacy.state&&advads.privacy.dispatch_event("not_needed",a));if(e.purpose.consents[1])return void("accepted"!==advads.privacy.state&&advads.privacy.dispatch_event("accepted",a));"rejected"!==advads.privacy.state&&advads.privacy.dispatch_event("rejected",a)}})}t++},100);return advads.privacy.state},is_adsense_npa_enabled:function(){return!window.advads_options||!window.advads_options.privacy||!(!window.advads_options.privacy["show-non-personalized-adsense"]||"custom"!==window.advads_options.privacy["consent-method"])},dispatch_event:function(e,t){var a=advads.privacy.state;advads.privacy.state=e,console.log({state:e,previousState:a,userAction:t}),document.dispatchEvent(new CustomEvent("advanced_ads_privacy",{detail:{state:e,previousState:a,userAction:t}}))},is_ad_decoded:function(e){return null===document.querySelector('script[data-tcf="waiting-for-consent"][data-id="'+e+'"]')},decode_ad:function(e,t){t="boolean"!=typeof t||t;var a=decodeURIComponent(Array.prototype.map.call(atob(e.textContent),function(e){return"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2)}).join(""));if(!t)return a;e.replaceWith(document.createRange().createContextualFragment(a))}}},(window.advanced_ads_ready||jQuery(document).ready).call(null,function(){advads.privacy.get_state()}),document.addEventListener("advanced_ads_privacy",function(e){"accepted"!==e.detail.state&&"not_needed"!==e.detail.state||e.detail.userAction||"loading"===document.readyState||document.querySelectorAll('script[type="text/plain"][data-tcf="waiting-for-consent"]').forEach(advads.privacy.decode_ad)}),jQuery(document).ready(function(){var i,s,d,c;!advads.supports_localstorage()||!localStorage.getItem("advads_frontend_picker")||window.advads_options.blog_id&&localStorage.getItem("advads_frontend_blog_id")&&window.advads_options.blog_id!==localStorage.getItem("advads_frontend_blog_id")||localStorage.getItem("advads_frontend_starttime")&&parseInt(localStorage.getItem("advads_frontend_starttime"),10)<(new Date).getTime()-27e5&&(localStorage.removeItem("advads_frontend_action"),localStorage.removeItem("advads_frontend_element"),localStorage.removeItem("advads_frontend_picker"),localStorage.removeItem("advads_prev_url"),localStorage.removeItem("advads_frontend_pathtype"),localStorage.removeItem("advads_frontend_boundary"),localStorage.removeItem("advads_frontend_blog_id"),localStorage.removeItem("advads_frontend_starttime"),!void advads.set_cookie("advads_frontend_picker","",-1))||(s=jQuery("<div id='advads-picker-overlay'>"),d=[document.body,document.documentElement,document],s.css({position:"absolute",border:"solid 2px #428bca",backgroundColor:"rgba(66,139,202,0.5)",boxSizing:"border-box",zIndex:1e6,pointerEvents:"none"}).prependTo("body"),"true"===localStorage.getItem("advads_frontend_boundary")&&jQuery("body").css("cursor","not-allowed"),window.advads.is_boundary_reached=function(e){if("true"!==localStorage.getItem("advads_frontend_boundary"))return!1;$advads_picker_cur=jQuery(e);var t=jQuery(".advads-frontend-picker-boundary-helper");return $boundaries=t.parent(),$boundaries.css("cursor","pointer"),$advads_picker_cur.is($boundaries)||!$advads_picker_cur.closest($boundaries).length},c="xpath"===localStorage.getItem("advads_frontend_pathtype")?"getXPath":"getPath",jQuery(document).mousemove(function(e){if(e.target!==i){if(~d.indexOf(e.target))return i=null,void s.hide();var t=jQuery(e.target),a=t.offset(),o=t.outerWidth(),n=t.outerHeight();i=e.target;var r=jQuery(i)[c]();r&&(console.log(r),s.css({top:a.top,left:a.left,width:o,height:n}).show())}}),jQuery(document).click(function(e){var t=jQuery(i)[c]();advads.is_boundary_reached(i)||(localStorage.setItem("advads_frontend_element",t),window.location=localStorage.getItem("advads_prev_url"))}))}),jQuery.fn.extend({getPath:function(e,t){if(void 0===e&&(e=""),void 0===t&&(t=0),this.is("html"))return"html > "+e;if(3===t)return e;var a=this.get(0).nodeName.toLowerCase(),o=this.attr("id"),n=this.attr("class");return t+=1,void 0===o||/\d/.test(o)?void 0!==n&&(n=n.split(/[\s\n]+/),(n=jQuery.grep(n,function(e,t){return!/\d/.test(e)})).length&&(a+="."+n.slice(0,2).join("."))):a+="#"+o,this.siblings(a).length&&(a+=":eq("+this.siblings(a).addBack().not("#advads-picker-overlay").index(this)+")"),""===e?this.parent().getPath(a,t):this.parent().getPath(a+" > "+e,t)},getXPath:function(e,t){if(void 0===e&&(e=""),void 0===t&&(t=0),this.is("body")||3===t)return e;if(advads.is_boundary_reached(this))return e;var a,o=this.get(0).nodeName.toLowerCase(),n=o,r=this.attr("id"),i=this.attr("class"),s=[];if(void 0!==r&&!/\d/.test(r))return n+'[@id and id="'+r+'"]/'+e;if(void 0!==i&&(i=i.split(/[\s\n]+/),(i=jQuery.grep(i,function(e,t){return!/\d/.test(e)})).length)){t+=1;for(var s=i.slice(0,2),d=[],c=0;c<s.length;c++)d.push('(@class and contains(concat(" ", normalize-space(@class), " "), " '+s[c]+' "))');n+="["+d.join(" and ")+"]"}return(a=s.length?this.siblings(o+"."+s.join(".")):this.siblings(o)).length&&(n+="["+a.addBack().not("#advads-picker-overlay").index(this)+"]"),""===e?this.parent().getXPath(n,t):this.parent().getXPath(n+"/"+e,t)}});
public/assets/js/advanced.orig.js CHANGED
@@ -16,8 +16,8 @@ advads = {
16
  window.localStorage.setItem("x", "x");
17
  window.localStorage.removeItem("x");
18
  return true;
19
- } catch(e) {
20
- return false;
21
  }
22
  },
23
  /**
@@ -99,7 +99,7 @@ advads = {
99
  * set null to expire cookie in the current session
100
  */
101
  set_cookie: function (name, value, exdays, path, domain, secure) {
102
- // days in seconds
103
  var expiry = ( exdays == null ) ? null : exdays * 24 * 60 * 60;
104
  this.set_cookie_sec( name, value, expiry, path, domain, secure );
105
  },
@@ -148,7 +148,7 @@ advads = {
148
 
149
  var el = jQuery(element);
150
  var target_string = target;
151
-
152
  if( typeof options === 'undefined' ){
153
  options = {};
154
  }
@@ -171,7 +171,7 @@ advads = {
171
  break;
172
  }
173
  }
174
-
175
  // use only visible elements
176
  if( typeof options.moveintohidden === 'undefined' ){
177
  target = jQuery( target ).filter(':visible');
@@ -181,7 +181,7 @@ advads = {
181
 
182
  // print warning in console if the element appears multiple times
183
  if( target.length > 1 ){
184
- console.log( "Advanced Ads: element '" + target_string + "' found " + target.length + " times." );
185
  }
186
 
187
  // switch insert method
@@ -369,60 +369,176 @@ advads = {
369
  },
370
 
371
  privacy: {
 
 
372
  /**
373
  * Get consent state.
 
374
  *
375
- * @return str
376
  * 'not_needed' - consent is not needed.
377
  * 'accepted' - consent was given.
378
  * 'unknown' - consent was not given yet.
379
  */
380
- get_state: function() {
381
- if ( ! window.advads_options || ! window.advads_options.privacy ) {
382
- return 'not_needed';
383
- }
384
-
385
- var options = window.advads_options.privacy;
386
-
387
- if ( ! options.enabled ) {
388
- return 'not_needed';
389
- }
390
-
391
- var method = options['consent-method'] ? options['consent-method'] : '0';
392
-
393
- switch ( method ) {
394
- case '0':
395
- return 'not_needed';
396
- break;
397
- case 'custom':
398
- if ( options['custom-cookie-value' === undefined] || options['custom-cookie-value'] === undefined ) {
399
- return 'not_needed';
400
  }
 
 
401
 
402
- var found = advads.get_cookie( options['custom-cookie-name'] );
403
- if ( typeof found !== 'string' ) {
404
- return 'unknown';
405
- }
406
- if ( ( options['custom-cookie-value'] === '' && found === '' )
407
- || ( options['custom-cookie-value'] !== '' && found.indexOf( options['custom-cookie-value'] ) !== -1 ) ) {
408
- return 'accepted';
409
- }
410
- return 'unknown';
411
- break;
412
- default:
413
- return ( advads.cookie_exists( method ) ) ? 'accepted' : 'unknown';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  }
415
- },
416
- is_adsense_npa_enabled: function() {
417
- if ( ! window.advads_options || ! window.advads_options.privacy ) {
 
 
 
 
 
418
  return true;
419
  }
420
- var options = window.advads_options.privacy;
421
- return !! options['show-non-personalized-adsense'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
  }
423
  }
424
-
425
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  // highlight elements in frontend, if local storage variable is set
427
  jQuery(document).ready(function(){
428
 
@@ -432,9 +548,9 @@ jQuery(document).ready(function(){
432
  }
433
 
434
  // Check if the frontend picker was started on the current blog.
435
- if ( window.advanced_ads_data && advanced_ads_data.blog_id
436
  && localStorage.getItem( 'advads_frontend_blog_id' )
437
- && advanced_ads_data.blog_id !== localStorage.getItem( 'advads_frontend_blog_id' ) ) {
438
  return false;
439
  }
440
 
16
  window.localStorage.setItem("x", "x");
17
  window.localStorage.removeItem("x");
18
  return true;
19
+ } catch(e) {
20
+ return false;
21
  }
22
  },
23
  /**
99
  * set null to expire cookie in the current session
100
  */
101
  set_cookie: function (name, value, exdays, path, domain, secure) {
102
+ // days in seconds
103
  var expiry = ( exdays == null ) ? null : exdays * 24 * 60 * 60;
104
  this.set_cookie_sec( name, value, expiry, path, domain, secure );
105
  },
148
 
149
  var el = jQuery(element);
150
  var target_string = target;
151
+
152
  if( typeof options === 'undefined' ){
153
  options = {};
154
  }
171
  break;
172
  }
173
  }
174
+
175
  // use only visible elements
176
  if( typeof options.moveintohidden === 'undefined' ){
177
  target = jQuery( target ).filter(':visible');
181
 
182
  // print warning in console if the element appears multiple times
183
  if( target.length > 1 ){
184
+ console.log( "Advanced Ads: element '" + target_string + "' found " + target.length + " times." );
185
  }
186
 
187
  // switch insert method
369
  },
370
 
371
  privacy: {
372
+ state: 'unknown',
373
+ state_executed: false,
374
  /**
375
  * Get consent state.
376
+ * IIFE so the events fire only once per event.
377
  *
378
+ * @return string
379
  * 'not_needed' - consent is not needed.
380
  * 'accepted' - consent was given.
381
  * 'unknown' - consent was not given yet.
382
  */
383
+ get_state: (function () {
384
+ return function () {
385
+ // if we already have a state, return that.
386
+ if (window.advads_options.privacy.state !== 'unknown') {
387
+ // make sure this only gets executed once.
388
+ if (!advads.privacy.state_executed) {
389
+ advads.privacy.state_executed = true;
390
+ advads.privacy.dispatch_event(window.advads_options.privacy.state, false);
 
 
 
 
 
 
 
 
 
 
 
 
391
  }
392
+ return advads.privacy.state;
393
+ }
394
 
395
+ // make sure this only gets executed once.
396
+ advads.privacy.state_executed = true;
397
+
398
+ // Run this in an interval (every 0.1s) just in case we are still waiting for consent
399
+ var cnt = 0,
400
+ consentSetInterval = setInterval(function () {
401
+ // Bail if we have not gotten a consent response after 60 seconds.
402
+ if (cnt === 600) {
403
+ clearInterval(consentSetInterval);
404
+ }
405
+ switch (window.advads_options.privacy['consent-method']) {
406
+ case 'custom' :
407
+ // check if custom cookie is set and matches value.
408
+ var regex = new RegExp(window.advads_options.privacy['custom-cookie-name'] + '=.*?' + window.advads_options.privacy['custom-cookie-value'] + '[^;]*');
409
+ if (document.cookie.match(regex) !== null) {
410
+ clearInterval(consentSetInterval);
411
+ if (advads.privacy.state !== 'accepted') {
412
+ advads.privacy.dispatch_event('accepted', true);
413
+ }
414
+ }
415
+ break;
416
+
417
+ case 'iab_tcf_20' :
418
+ // Check if window.__tcfapi has been set.
419
+ if (typeof window.__tcfapi === 'undefined') {
420
+ return;
421
+ }
422
+ clearInterval(consentSetInterval);
423
+
424
+ window.__tcfapi('addEventListener', 2, function (TCData, listenerSuccess) {
425
+ if (!listenerSuccess) {
426
+ return;
427
+ }
428
+ if (TCData.eventStatus === 'tcloaded' || TCData.eventStatus === 'useractioncomplete') {
429
+ var userAction = TCData.eventStatus === 'useractioncomplete';
430
+ if (!TCData.gdprApplies) {
431
+ if (advads.privacy.state !== 'not_needed') {
432
+ advads.privacy.dispatch_event('not_needed', userAction);
433
+ }
434
+ return;
435
+ }
436
+
437
+ if (TCData.purpose.consents[1]) {
438
+ if (advads.privacy.state !== 'accepted') {
439
+ advads.privacy.dispatch_event('accepted', userAction);
440
+ }
441
+ return;
442
+ }
443
+
444
+ // fire another event, in case the user revokes the previous consent.
445
+ if (advads.privacy.state !== 'rejected') {
446
+ advads.privacy.dispatch_event('rejected', userAction);
447
+ }
448
+ }
449
+ }
450
+ );
451
+ break;
452
+ }
453
+
454
+ cnt++;
455
+ }, 100);
456
+
457
+ return advads.privacy.state;
458
  }
459
+ })(),
460
+ /**
461
+ * Check if the privacy_method is custom cookie, and non personalized ads are allowed.
462
+ *
463
+ * @returns {boolean}
464
+ */
465
+ is_adsense_npa_enabled: function () {
466
+ if (!window.advads_options || !window.advads_options.privacy) {
467
  return true;
468
  }
469
+ return !!(window.advads_options.privacy['show-non-personalized-adsense'] && window.advads_options.privacy['consent-method'] === 'custom');
470
+ },
471
+ /**
472
+ * Dispatch a custom event whenever the state changes.
473
+ *
474
+ * @param {String} state The current privacy state.
475
+ * @param {boolean} userAction This is result of action by user.
476
+ */
477
+ dispatch_event: function (state, userAction) {
478
+ var previousState = advads.privacy.state;
479
+ advads.privacy.state = state;
480
+ console.log({
481
+ state: state,
482
+ previousState: previousState,
483
+ userAction: userAction
484
+ });
485
+ document.dispatchEvent(new CustomEvent('advanced_ads_privacy', {
486
+ detail: {
487
+ state: state,
488
+ previousState: previousState,
489
+ userAction: userAction
490
+ }
491
+ }));
492
+ },
493
+ /**
494
+ * Check if ad is decoded.
495
+ *
496
+ * @param {integer} id
497
+ *
498
+ * @returns {boolean}
499
+ */
500
+ is_ad_decoded: function(id) {
501
+ return document.querySelector('script[data-tcf="waiting-for-consent"][data-id="' + id + '"]') === null;
502
+ },
503
+ /**
504
+ * Decode ad content.
505
+ *
506
+ * @param {Element} el
507
+ * @param {boolean} inject
508
+ */
509
+ decode_ad: function (el, inject) {
510
+ // this can also be a number if used in a foreach.
511
+ inject = typeof inject === 'boolean' ? inject : true;
512
+ var string = decodeURIComponent(Array.prototype.map.call(atob(el.textContent), function (c) {
513
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
514
+ }).join(''));
515
+
516
+ if (!inject) {
517
+ return string;
518
+ }
519
+
520
+ el.replaceWith(document.createRange().createContextualFragment(string));
521
  }
522
  }
 
523
  };
524
+
525
+ (window.advanced_ads_ready || jQuery(document).ready).call(null, function () {
526
+ advads.privacy.get_state();
527
+ });
528
+
529
+ document.addEventListener('advanced_ads_privacy', function (event) {
530
+ if (
531
+ (event.detail.state !== 'accepted' && event.detail.state !== 'not_needed')
532
+ || event.detail.userAction
533
+ || document.readyState === 'loading'
534
+ ) {
535
+ return;
536
+ }
537
+
538
+ // Find all scripts waiting for consent and decode them.
539
+ document.querySelectorAll('script[type="text/plain"][data-tcf="waiting-for-consent"]').forEach(advads.privacy.decode_ad);
540
+ });
541
+
542
  // highlight elements in frontend, if local storage variable is set
543
  jQuery(document).ready(function(){
544
 
548
  }
549
 
550
  // Check if the frontend picker was started on the current blog.
551
+ if ( window.advads_options.blog_id
552
  && localStorage.getItem( 'advads_frontend_blog_id' )
553
+ && window.advads_options.blog_id !== localStorage.getItem( 'advads_frontend_blog_id' ) ) {
554
  return false;
555
  }
556
 
public/class-advanced-ads.php CHANGED
@@ -74,6 +74,16 @@ class Advanced_Ads {
74
  */
75
  protected $was_in_the_loop = false;
76
 
 
 
 
 
 
 
 
 
 
 
77
  /**
78
  * List of bots and crawlers to exclude from ad impressions
79
  *
@@ -193,6 +203,8 @@ class Advanced_Ads {
193
 
194
  // check if ads are disabled in secondary queries.
195
  add_action( 'the_post', array( $this, 'set_query_type' ), 10, 2 );
 
 
196
 
197
  }
198
 
@@ -492,7 +504,7 @@ class Advanced_Ads {
492
  if ( ! isset( $options['content-injection-everywhere'] ) || 0 === $options['content-injection-everywhere'] ) {
493
  // check if this is a singular page within the loop or an AMP page.
494
  $is_amp = advads_is_amp();
495
- if ( ( ! is_singular( $public_post_types ) && ! is_feed() ) || ( ! $is_amp && ! in_the_loop() && ! $this->was_in_the_loop ) ) {
496
  return $content; }
497
  } else {
498
  global $wp_query;
@@ -848,7 +860,7 @@ class Advanced_Ads {
848
  * @return bool true while main query is being executed or not in the loop, false otherwise
849
  */
850
  public function is_main_query() {
851
- if ( ! in_the_loop() ) {
852
  // the secondary query check only designed for within post content.
853
  return true;
854
  }
@@ -856,6 +868,36 @@ class Advanced_Ads {
856
  return true === $this->is_main_query;
857
  }
858
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  /**
860
  * Find the calls to `the_content` inside functions hooked to `the_content`.
861
  *
@@ -947,7 +989,7 @@ class Advanced_Ads {
947
  */
948
  public function set_was_in_the_loop( $content ) {
949
  if ( self::get_instance()->has_many_the_content() ) {
950
- $this->was_in_the_loop = $this->was_in_the_loop || in_the_loop();
951
  } else {
952
  // Next top level `the_content`, forget that the loop started.
953
  $this->was_in_the_loop = false;
74
  */
75
  protected $was_in_the_loop = false;
76
 
77
+ /**
78
+ * Signifies Whether the loop has started and the caller is in the loop.
79
+ *
80
+ * We need it because Some the "Divi" theme calls the `loop_start/loop_end` hooks
81
+ * instead of calling `the_post()` to signify that the caller is in the loop.
82
+ *
83
+ * @var bool
84
+ */
85
+ protected $in_the_loop = false;
86
+
87
  /**
88
  * List of bots and crawlers to exclude from ad impressions
89
  *
203
 
204
  // check if ads are disabled in secondary queries.
205
  add_action( 'the_post', array( $this, 'set_query_type' ), 10, 2 );
206
+ add_action( 'loop_start', array( $this, 'set_loop_start' ), 10, 0 );
207
+ add_action( 'loop_end', array( $this, 'set_loop_end' ), 10, 0 );
208
 
209
  }
210
 
504
  if ( ! isset( $options['content-injection-everywhere'] ) || 0 === $options['content-injection-everywhere'] ) {
505
  // check if this is a singular page within the loop or an AMP page.
506
  $is_amp = advads_is_amp();
507
+ if ( ( ! is_singular( $public_post_types ) && ! is_feed() ) || ( ! $is_amp && ! $this->in_the_loop() && ! $this->was_in_the_loop ) ) {
508
  return $content; }
509
  } else {
510
  global $wp_query;
860
  * @return bool true while main query is being executed or not in the loop, false otherwise
861
  */
862
  public function is_main_query() {
863
+ if ( ! $this->in_the_loop() ) {
864
  // the secondary query check only designed for within post content.
865
  return true;
866
  }
868
  return true === $this->is_main_query;
869
  }
870
 
871
+ /**
872
+ * Sets whether the loop has started.
873
+ */
874
+ public function set_loop_start() {
875
+ $this->in_the_loop = true;
876
+ }
877
+
878
+ /**
879
+ * Sets whether the loop has ended.
880
+ */
881
+ public function set_loop_end() {
882
+ $this->in_the_loop = false;
883
+ }
884
+
885
+
886
+ /**
887
+ * Whether the loop has started and the caller is in the loop.
888
+ *
889
+ * @return bool
890
+ */
891
+ public function in_the_loop() {
892
+ if ( in_the_loop() ) {
893
+ return true;
894
+ }
895
+
896
+ if ( $this->in_the_loop ) {
897
+ return true;
898
+ }
899
+ }
900
+
901
  /**
902
  * Find the calls to `the_content` inside functions hooked to `the_content`.
903
  *
989
  */
990
  public function set_was_in_the_loop( $content ) {
991
  if ( self::get_instance()->has_many_the_content() ) {
992
+ $this->was_in_the_loop = $this->was_in_the_loop || $this->in_the_loop();
993
  } else {
994
  // Next top level `the_content`, forget that the loop started.
995
  $this->was_in_the_loop = false;
readme.txt CHANGED
@@ -224,11 +224,14 @@ To get started, just take a look at
224
 
225
  = What about my users’ privacy and GDPR? =
226
 
227
- You can show ads only to visitors that give their consent. See [GDPR support](https://wpadvancedads.com/manual/ad-cookie-consent/).
228
-
229
  Advanced Ads itself does neither save personal information (e.g., an IP address) in your database nor cookies in the visitor’s browser.
230
 
231
- Third party services like Google Analytics are disabled by default.
 
 
 
 
 
232
 
233
  You can learn more about how Advanced Ads and its add-ons handles data and privacy of your visitors [on this page](https://wpadvancedads.com/manual/privacy-information-for-users/).
234
 
@@ -312,6 +315,18 @@ Yes. You can use plenty of [hooks](https://wpadvancedads.com/codex/) to customiz
312
 
313
  == Changelog ==
314
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  = 1.19.1 =
316
 
317
  - apply WordPress lazy loading for images to images in ad content
224
 
225
  = What about my users’ privacy and GDPR? =
226
 
 
 
227
  Advanced Ads itself does neither save personal information (e.g., an IP address) in your database nor cookies in the visitor’s browser.
228
 
229
+ The plugin comes with Privacy settings which help you to gather consent from users before showing ads to them. This works for any ads managed with the plugin, including AdSense Auto ads.
230
+
231
+ Once you enable the Privacy module in the plugin settings, Advanced Ads blockes all ads until the user gives their consent. You can disable that check for individual ads as well (e.g., for image ads).
232
+ You can also deliver non-personalized AdSense ads when that is legally allowed in your area before the consent is given.
233
+
234
+ See also [GDPR support](https://wpadvancedads.com/manual/ad-cookie-consent/).
235
 
236
  You can learn more about how Advanced Ads and its add-ons handles data and privacy of your visitors [on this page](https://wpadvancedads.com/manual/privacy-information-for-users/).
237
 
315
 
316
  == Changelog ==
317
 
318
+ = untagged =
319
+
320
+ - improve timezone methods `Advanced_Ads_Utils::get_wp_timezone()` and `Advanced_Ads_Utils::get_timezone_name()`
321
+ - Divi theme: made content injection work with the "Unlimited ad injection" setting disabled
322
+ - added missing spaces to image ad tags to fix Cache-Busting issue
323
+ - integrate with TCF 2.0 compatible consent management platforms
324
+
325
+ = 1.19.1 =
326
+
327
+ - remove duplicate lines from the ads.txt file
328
+ - fixed layout issues that happens due to the JNews theme
329
+
330
  = 1.19.1 =
331
 
332
  - apply WordPress lazy loading for images to images in ad content