Completed
Push — branch-8.7-built ( 50de0f...7a10d7 )
by Jeremy
32:57 queued 24:29
created

WordAds_California_Privacy   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 0
dl 0
loc 263
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 4 1
A enqueue_scripts() 0 27 2
A init_ajax_actions() 0 7 1
A do_not_sell_link_shortcode() 0 12 2
A get_optout_link_text() 0 3 1
A build_iab_privacy_string() 0 10 2
A get_cookie_name() 0 3 1
A get_cookie_domain() 0 9 3
A get_optout_cookie_string() 0 3 1
A get_optin_cookie_string() 0 3 1
A set_optout_cookie() 0 3 1
A set_optin_cookie() 0 3 1
A handle_optout_request() 0 8 2
B handle_optout_markup() 0 86 2
1
<?php
2
/**
3
 * CCPA Class
4
 *
5
 * @package Jetpack.
6
 */
7
8
use Automattic\Jetpack\Assets;
9
10
/**
11
 * Class WordAds_California_Privacy
12
 *
13
 * Implementation of [California Consumer Privacy Act] (https://leginfo.legislature.ca.gov/faces/codes_displayText.xhtml?lawCode=CIV&division=3.&title=1.81.5.&part=4.&chapter=&article=) as applicable to WordAds.
14
 * Includes:
15
 * - Do Not Sell My Personal Information shortcode and widget.
16
 * - Modal notice to toggle opt-in/opt-out.
17
 * - Cookie handling. Implements IAB usprivacy cookie specifications.
18
 * - Client side geo-detection of California visitors by IP address. Avoids issues with page caching.
19
 */
20
class WordAds_California_Privacy {
21
22
	/**
23
	 * Initializes required scripts and shortcode.
24
	 */
25
	public static function init() {
26
		// Initialize shortcode.
27
		add_shortcode( 'ccpa-do-not-sell-link', array( __CLASS__, 'do_not_sell_link_shortcode' ) );
28
	}
29
30
	/**
31
	 * Enqueue required CCPA JavaScript on the frontend.
32
	 */
33
	public static function enqueue_scripts() {
34
		wp_enqueue_script(
35
			'wordads_ccpa',
36
			Assets::get_file_url_for_environment(
37
				'_inc/build/wordads/js/wordads-ccpa.min.js',
38
				'modules/wordads/js/wordads-ccpa.js'
39
			),
40
			array(),
41
			JETPACK__VERSION,
42
			true
43
		);
44
45
		wp_localize_script(
46
			'wordads_ccpa',
47
			'ccpaSettings',
48
			array(
49
				'defaultOptinCookieString' => esc_html( self::get_optin_cookie_string() ),
50
				'ccpaCssUrl'               => esc_url( Assets::get_file_url_for_environment( '/css/wordads-ccpa.min.css', '/css/wordads-ccpa.css' ) . '?ver=' . JETPACK__VERSION ),
51
				'ajaxUrl'                  => esc_url( admin_url( 'admin-ajax.php' ) ),
52
				'ajaxNonce'                => wp_create_nonce( 'ccpa_optout' ),
53
				'forceApplies'             => wp_json_encode( is_user_logged_in() && current_user_can( 'manage_options' ) ),
54
				'strings'                  => array(
55
					'pleaseWait' => esc_html__( 'Please Wait', 'jetpack' ),
56
				),
57
			)
58
		);
59
	}
60
61
	/**
62
	 * Initializes handlers for admin AJAX.
63
	 */
64
	public static function init_ajax_actions() {
65
		add_action( 'wp_ajax_privacy_optout', array( __CLASS__, 'handle_optout_request' ) );
66
		add_action( 'wp_ajax_nopriv_privacy_optout', array( __CLASS__, 'handle_optout_request' ) );
67
68
		add_action( 'wp_ajax_privacy_optout_markup', array( __CLASS__, 'handle_optout_markup' ) );
69
		add_action( 'wp_ajax_nopriv_privacy_optout_markup', array( __CLASS__, 'handle_optout_markup' ) );
70
	}
71
72
	/**
73
	 * Outputs [ccpa-do-not-sell-link] shortcode markup.
74
	 *
75
	 * @return string The generated shortcode markup.
76
	 */
77
	public static function do_not_sell_link_shortcode() {
78
79
		// If in the customizer always display the link.
80
		if ( is_customize_preview() ) {
81
			return '<a href="#" class="ccpa-do-not-sell">' . self::get_optout_link_text() . '</a>';
82
		}
83
84
		// Load required scripts if the shortcode/widget is loaded on the page.
85
		self::enqueue_scripts();
86
87
		return '<a href="#" class="ccpa-do-not-sell" style="display: none;">' . self::get_optout_link_text() . '</a>';
88
	}
89
90
91
	/**
92
	 * Gets the text used to link to the opt-out page. By law must read 'Do Not Sell My Personal Information'.
93
	 *
94
	 * @return mixed|string|void The text of the opt-out link.
95
	 */
96
	private static function get_optout_link_text() {
97
		return __( 'Do Not Sell My Personal Information', 'jetpack' );
98
	}
99
100
	/**
101
	 * Builds the value of the opt-out cookie.
102
	 * Format matches spec of [IAB U.S. Privacy String](https://github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/CCPA/US%20Privacy%20String.md).
103
	 *
104
	 * @param bool $optout True if setting an opt-out cookie.
105
	 *
106
	 * @return string The value to be stored in the opt-out cookie.
107
	 */
108
	private static function build_iab_privacy_string( $optout ) {
109
		$values = array(
110
			'1', // Specification version.
111
			'Y', // Explicit notice to opt-out provided.
112
			$optout ? 'Y' : 'N', // Opt-out of data sale.
113
			'N', // Signatory to IAB Limited Service Provider Agreement.
114
		);
115
116
		return join( $values );
117
	}
118
119
	/**
120
	 * Gets the name to be used for the opt-out cookie.
121
	 * Name matches spec of [IAB U.S. Privacy String](https://github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/CCPA/US%20Privacy%20String.md).
122
	 *
123
	 * @return string The name of the opt-out cookie.
124
	 */
125
	private static function get_cookie_name() {
126
		return 'usprivacy';
127
	}
128
129
	/**
130
	 * Gets the domain to be used for the opt-out cookie.
131
	 * Use the site's custom domain, or if the site has a wordpress.com subdomain, use .wordpress.com to share the cookie.
132
	 *
133
	 * @return string The domain to set for the opt-out cookie.
134
	 */
135
	public static function get_cookie_domain() {
136
		$host = 'localhost';
137
138
		if ( isset( $_SERVER['HTTP_HOST'] ) ) {
139
			$host = $_SERVER['HTTP_HOST'];
140
		}
141
142
		return '.wordpress.com' === substr( $host, -strlen( '.wordpress.com' ) ) ? '.wordpress.com' : '.' . $host;
143
	}
144
145
	/**
146
	 * Gets the value to be used when an opt-out cookie is set.
147
	 *
148
	 * @return string The value to store in the opt-out cookie.
149
	 */
150
	private static function get_optout_cookie_string() {
151
		return self::build_iab_privacy_string( true );
152
	}
153
154
	/**
155
	 * Gets the value to be used when an opt-in cookie is set.
156
	 *
157
	 * @return string The value to store in the opt-in cookie.
158
	 */
159
	private static function get_optin_cookie_string() {
160
		return self::build_iab_privacy_string( false );
161
	}
162
163
	/**
164
	 * Sets a cookie in the HTTP response to opt-out visitors from data sales.
165
	 *
166
	 * @return bool True if the cookie could be set.
167
	 */
168
	private static function set_optout_cookie() {
169
		return setcookie( self::get_cookie_name(), self::get_optout_cookie_string(), time() + ( 5 * YEAR_IN_SECONDS ), '/', self::get_cookie_domain() );
170
	}
171
172
	/**
173
	 * Sets a cookie in the HTTP response to opt-in visitors from data sales.
174
	 *
175
	 * @return bool True if the cookie could be set.
176
	 */
177
	private static function set_optin_cookie() {
178
		return setcookie( self::get_cookie_name(), self::get_optin_cookie_string(), time() + YEAR_IN_SECONDS, '/', self::get_cookie_domain() );
179
	}
180
181
	/**
182
	 * Handler for opt-in/opt-out AJAX request.
183
	 */
184
	public static function handle_optout_request() {
185
		check_ajax_referer( 'ccpa_optout', 'security' );
186
187
		$optout = 'true' === $_POST['optout'];
188
		$optout ? self::set_optout_cookie() : self::set_optin_cookie();
189
190
		wp_send_json_success( $optout );
191
	}
192
193
	/**
194
	 * Handler for modal popup notice markup.
195
	 */
196
	public static function handle_optout_markup() {
197
		check_ajax_referer( 'ccpa_optout', 'security' );
198
199
		header( 'Content-Type: text/html; charset=utf-8' );
200
		$policy_url = get_option( 'wordads_ccpa_privacy_policy_url' );
201
202
		$default_disclosure = sprintf(
203
			'<p>%s</p>
204
			<p>%s</p>
205
			<p><strong>%s</strong></p>
206
			<p>%s</p>
207
			<p>%s</p>
208
			<p>%s</p>',
209
			esc_html__( 'If you are a California resident, you have the right to opt out of the "sale" of your "personal information" under the California Consumer Privacy Act ("CCPA")', 'jetpack' ),
210
			esc_html__( 'This site operates an ads program in partnership with third-party vendors who help place ads. Advertising cookies enable these ads partners to serve ads, to personalize those ads based on information like visits to this site and other sites on the internet, and to understand how users engage with those ads. Cookies collect certain information as part of the ads program, and we provide the following categories of information to third-party advertising partners: online identifiers and internet or other network or device activity (such as unique identifiers, cookie information, and IP address), and geolocation data (approximate location information from your IP address). This type of sharing with ads partners may be considered a "sale" of personal information under the CCPA.', 'jetpack' ),
211
			esc_html__( 'We never share information that identifies you personally, like your name or email address, as part of the advertising program.', 'jetpack' ),
212
			esc_html__( 'If you\'d prefer not to see ads that are personalized based on information from your visits to this site, you can opt-out by toggling the "Do Not Sell My Personal Information" switch below to the On position.', 'jetpack' ),
213
			esc_html__( 'This opt-out is managed through cookies, so if you delete cookies, your browser is set to delete cookies automatically after a certain length of time, or if you visit this site with a different browser, you\'ll need to make this selection again.', 'jetpack' ),
214
			esc_html__( 'After you opt-out you may still see ads, including personalized ones, on this site and other sites - they just won\'t be personalized based on information from your visits to this site.', 'jetpack' )
215
		);
216
217
		/**
218
		 * Filter on the default CCPA disclosure text.
219
		 *
220
		 * @see https://jetpack.com/support/ads/
221
		 *
222
		 * @module wordads
223
		 *
224
		 * @since 8.7.0
225
		 *
226
		 * @param string Default CCPA disclosure for WordAds.
227
		 */
228
		$disclosure = apply_filters( 'wordads_ccpa_disclosure', $default_disclosure );
229
		?>
230
			<div id="ccpa-modal" class="cleanslate">
231
				<div class="components-modal__screen-overlay">
232
					<div tabindex="0"></div>
233
					<div role="dialog" aria-labelledby="dialog_label" aria-modal="true" class="components-modal__frame">
234
						<div class="components-modal__content ccpa-settings">
235
							<div class="components-modal__header">
236
								<div class="components-modal__header-heading-container">
237
									<h1 id="dialog_label" class="components-modal__header-heading"><?php esc_html_e( 'Do Not Sell My Personal Information', 'jetpack' ); ?></h1>
238
								</div>
239
								<button type="button" aria-label="<?php esc_html_e( 'Close dialog', 'jetpack' ); ?>" class="components-button components-icon-button">
240
									<svg aria-hidden="true" role="img" focusable="false" class="dashicon dashicons-no-alt" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
241
										<path d="M14.95 6.46L11.41 10l3.54 3.54-1.41 1.41L10 11.42l-3.53 3.53-1.42-1.42L8.58 10 5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z"></path>
242
									</svg>
243
								</button>
244
							</div>
245
							<div class="ccpa-settings__intro-txt"><?php echo wp_kses( $disclosure, wp_kses_allowed_html( 'post' ) ); ?></div>
246
							<div class="components-modal__footer">
247
								<div role="form" class="ccpa-setting">
248
									<label>
249
										<span class="ccpa-setting__header"><?php esc_html_e( 'Do Not Sell My Personal Information', 'jetpack' ); ?></span>
250
										<span class="ccpa-setting__toggle components-form-toggle">
251
											<input id="ccpa-opt-out" class="components-form-toggle__input opt-out" type="checkbox" value="false" autofocus />
252
											<span class="components-form-toggle__track"></span>
253
											<span class="components-form-toggle__thumb"></span>
254
											<svg class="components-form-toggle__on" width="2" height="6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6" role="img" aria-hidden="true" focusable="false"><path d="M0 0h2v6H0z"></path></svg>
255
											<svg class="components-form-toggle__off" width="6" height="6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6" role="img" aria-hidden="true" focusable="false"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z"></path></svg>
256
										</span>
257
										<span class="ccpa-setting__toggle-text ccpa-setting__toggle-text-off"><?php esc_html_e( 'Off', 'jetpack' ); ?></span>
258
										<span class="ccpa-setting__toggle-text ccpa-setting__toggle-text-on"><?php esc_html_e( 'On', 'jetpack' ); ?></span>
259
									</label>
260
								</div>
261
								<div class="components-modal__footer-bottom">
262
									<button class="components-button is-button is-primary"><?php esc_html_e( 'Close', 'jetpack' ); ?></button>
263
									<?php
264
									if ( $policy_url ) {
265
										printf(
266
											'<a href="%s" class="ccpa-privacy">%s</a>',
267
											esc_url( $policy_url ),
268
											esc_html__( 'View Privacy Policy', 'jetpack' )
269
										);
270
									}
271
									?>
272
								</div>
273
							</div>
274
						</div>
275
					</div>
276
				</div>
277
			</div>
278
		<?php
279
280
		wp_die();
281
	}
282
}
283