PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.24
Advanced Ads – Ad Manager & AdSense v2.0.24
2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / modules / privacy / classes / class-privacy.php
advanced-ads / modules / privacy / classes Last commit date
class-privacy.php 1 year ago
class-privacy.php
273 lines
1 <?php // phpcs:ignore WordPress.Files.FileName
2
3 use AdvancedAds\Abstracts\Ad;
4 use AdvancedAds\Utilities\Conditional;
5 use AdvancedAds\Framework\Utilities\Str;
6 use AdvancedAds\Framework\Utilities\Params;
7
8 /**
9 * Handles Advanced Ads privacy settings.
10 */
11 class Advanced_Ads_Privacy {
12 /**
13 * Singleton instance of the plugin
14 *
15 * @var self
16 */
17 protected static $instance;
18
19 /**
20 * Module options
21 *
22 * @var null|array
23 */
24 protected $options;
25
26 /**
27 * Option key
28 *
29 * @const string
30 */
31 const OPTION_KEY = 'advanced-ads-privacy';
32
33 /**
34 * Initialize the module
35 */
36 private function __construct() {
37 add_filter( 'advanced-ads-can-display-ad', [ $this, 'can_display_by_consent' ], 10, 3 );
38
39 $this->options();
40
41 if ( ! empty( $this->options['enabled'] ) ) {
42 add_filter( 'advanced-ads-activate-advanced-js', '__return_true' );
43
44 if ( 'iab_tcf_20' === $this->options['consent-method'] ) {
45 add_filter( 'advanced-ads-ad-output', [ $this, 'final_ad_output' ], 10, 2 );
46 }
47 }
48 }
49
50 /**
51 * If this ad is not image or dummy base64_encode the text.
52 *
53 * @param string $output The output string.
54 * @param Ad $ad Ad instance.
55 *
56 * @return string
57 */
58 public function final_ad_output( $output, Ad $ad ) {
59 if (
60 Conditional::is_amp() ||
61 ! $this->ad_type_needs_consent( $ad->get_type() ) ||
62 ( ! $ad->is_type( 'adsense' ) && $ad->get_prop( 'privacy.ignore-consent' ) )
63 ) {
64 return $output;
65 }
66
67 return $this->encode_ad( $output, $ad );
68 }
69
70 /**
71 * Encode the ad output.
72 *
73 * @param string $output The output string.
74 * @param Ad $ad Ad intance.
75 *
76 * @return string
77 */
78 public function encode_ad( $output, Ad $ad ) {
79 $data_attributes = [
80 'id' => $ad->get_id(),
81 'bid' => get_current_blog_id(),
82 ];
83
84 if ( $ad->is_parent_placement() ) {
85 $data_attributes['placement'] = $ad->get_parent()->get_id();
86 }
87
88 /**
89 * Filter the data attributes and allow removing/adding attributes.
90 * All attributes will be prefix with `data-` on output.
91 *
92 * @param array $data_attributes The default data attributes.
93 * @param Ad $ad Ad instance.
94 */
95 $data_attributes = (array) apply_filters( 'advanced-ads-privacy-output-attributes', $data_attributes, $ad );
96
97 // Convert the data-attributes array into a string.
98 $attributes_string = '';
99 array_walk(
100 $data_attributes,
101 function ( $value, $key ) use ( &$attributes_string ) {
102 $attributes_string .= sprintf( ' data-%s="%s"', sanitize_key( $key ), esc_attr( $value ) );
103 }
104 );
105
106 return sprintf(
107 '<script type="text/plain" data-tcf="waiting-for-consent"%s>%s</script>',
108 $attributes_string,
109 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- we need to obfuscate the html (and maybe decode it in JS).
110 base64_encode( $output )
111 );
112 }
113
114 /**
115 * Check if the current ad output is encoded.
116 *
117 * @param string $output The ad output.
118 *
119 * @return bool
120 */
121 public function is_ad_output_encoded( $output ) {
122 return (bool) strpos( $output, 'data-tcf="waiting-for-consent"' );
123 }
124
125 /**
126 * Return an instance of Advanced_Ads_Privacy
127 *
128 * @return self
129 */
130 public static function get_instance() {
131 // If the single instance hasn't been set, set it now.
132 if ( null === self::$instance ) {
133 self::$instance = new self();
134 }
135
136 return self::$instance;
137 }
138
139 /**
140 * Return module options
141 *
142 * @return array
143 */
144 public function options() {
145 if ( ! isset( $this->options ) ) {
146 $this->options = get_option( self::OPTION_KEY, [] );
147 if ( isset( $this->options['enabled'] ) && empty( $this->options['consent-method'] ) ) {
148 $this->options['enabled'] = false;
149 }
150 }
151
152 return $this->options;
153 }
154
155 /**
156 * Check if ad can be displayed based on user's consent.
157 *
158 * @param bool $can_display Whether to display this ad.
159 * @param Ad $ad Ad instance.
160 * @param array $check_options Additional options passed to can_display.
161 *
162 * @return bool
163 */
164 public function can_display_by_consent( $can_display, Ad $ad, $check_options ) {
165 // Early bail!!
166 if ( ! $can_display ) {
167 return $can_display;
168 }
169
170 // Passive cache busting enabled.
171 if ( $check_options['passive_cache_busting'] ) {
172 return true;
173 }
174
175 // privacy module not active, bail early.
176 if ( empty( $this->options['enabled'] ) ) {
177 return true;
178 }
179
180 // If consent is overriden for the ad.
181 if ( ! empty( $ad->get_prop( 'privacy.ignore-consent' ) ) ) {
182 return true;
183 }
184
185 $consent_method = $this->options['consent-method'] ?? '';
186
187 // If the consent method is set to cookie and the ad type does not need consent.
188 if ( 'custom' === $consent_method && ! $this->ad_type_needs_consent( $ad->get_type() ) ) {
189 return true;
190 }
191
192 // If method is iab_tcf_20, always set to true, JS needs to decide whether to display ad or not.
193 if ( 'iab_tcf_20' === $consent_method ) {
194 return true;
195 }
196
197 // Either personalized or non-personalized ad will be shown.
198 if ( $ad->is_type( 'adsense' ) && ! empty( $this->options()['show-non-personalized-adsense'] ) ) {
199 return true;
200 }
201
202 return $this->get_state() !== 'unknown';
203 }
204
205 /**
206 * Check whether this ad_type needs consent.
207 *
208 * @param string $type The ad type.
209 *
210 * @return bool
211 */
212 public function ad_type_needs_consent( $type ) {
213 return ! in_array( $type, [ 'image', 'dummy', 'group' ], true );
214 }
215
216 /**
217 * Check if consent is not needed or was given by the user.
218 *
219 * @return string
220 * 'not_needed' - consent is not needed.
221 * 'accepted' - consent was given.
222 * 'unknown' - consent was not given yet.
223 */
224 public function get_state() {
225 static $state;
226 if ( is_null( $state ) ) {
227 $state = $this->parse_state();
228 }
229
230 return $state;
231 }
232
233 /**
234 * Used by get_state() to parse the state of privacy/consent.
235 *
236 * @return string
237 * 'not_needed' - consent is not needed.
238 * 'accepted' - consent was given.
239 * 'unknown' - consent was not given yet.
240 */
241 private function parse_state() {
242 if ( empty( $this->options['enabled'] ) || Conditional::is_amp() ) {
243 return 'not_needed';
244 }
245
246 $consent_method = $this->options['consent-method'] ?? '';
247 switch ( $consent_method ) {
248 case 'custom':
249 $name = $this->options['custom-cookie-name'];
250 if ( empty( $name ) ) {
251 return 'not_needed';
252 }
253
254 if ( ! Params::cookie( $name ) ) {
255 return 'unknown';
256 }
257
258 $value = $this->options['custom-cookie-value'] ?? '';
259 if (
260 ( '' === $value && '' === Params::cookie( $name ) ) ||
261 Str::contains( $value, Params::cookie( $name ) )
262 ) {
263 return 'accepted';
264 }
265
266 return 'unknown';
267 case 'iab_tcf_20':
268 default:
269 return 'unknown';
270 }
271 }
272 }
273