11 * |
11 * |
12 * @since 4.9.6 |
12 * @since 4.9.6 |
13 * @access private |
13 * @access private |
14 * |
14 * |
15 * @param int $request_id Request ID. |
15 * @param int $request_id Request ID. |
16 * @return bool|WP_Error Returns true/false based on the success of sending the email, or a WP_Error object. |
16 * @return true|WP_Error Returns true if sending the email was successful, or a WP_Error object. |
17 */ |
17 */ |
18 function _wp_privacy_resend_request( $request_id ) { |
18 function _wp_privacy_resend_request( $request_id ) { |
19 $request_id = absint( $request_id ); |
19 $request_id = absint( $request_id ); |
20 $request = get_post( $request_id ); |
20 $request = get_post( $request_id ); |
21 |
21 |
22 if ( ! $request || 'user_request' !== $request->post_type ) { |
22 if ( ! $request || 'user_request' !== $request->post_type ) { |
23 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); |
23 return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) ); |
24 } |
24 } |
25 |
25 |
26 $result = wp_send_user_request( $request_id ); |
26 $result = wp_send_user_request( $request_id ); |
27 |
27 |
28 if ( is_wp_error( $result ) ) { |
28 if ( is_wp_error( $result ) ) { |
29 return $result; |
29 return $result; |
30 } elseif ( ! $result ) { |
30 } elseif ( ! $result ) { |
31 return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) ); |
31 return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation for personal data request.' ) ); |
32 } |
32 } |
33 |
33 |
34 return true; |
34 return true; |
35 } |
35 } |
36 |
36 |
47 // Get the request. |
47 // Get the request. |
48 $request_id = absint( $request_id ); |
48 $request_id = absint( $request_id ); |
49 $request = wp_get_user_request( $request_id ); |
49 $request = wp_get_user_request( $request_id ); |
50 |
50 |
51 if ( ! $request ) { |
51 if ( ! $request ) { |
52 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); |
52 return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) ); |
53 } |
53 } |
54 |
54 |
55 update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() ); |
55 update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() ); |
56 |
56 |
57 $result = wp_update_post( |
57 $result = wp_update_post( |
102 |
102 |
103 if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) { |
103 if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) { |
104 add_settings_error( |
104 add_settings_error( |
105 'action_type', |
105 'action_type', |
106 'action_type', |
106 'action_type', |
107 __( 'Invalid action.' ), |
107 __( 'Invalid personal data action.' ), |
108 'error' |
108 'error' |
109 ); |
109 ); |
110 } |
110 } |
111 $action_type = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); |
111 $action_type = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); |
112 $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) ); |
112 $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) ); |
113 $email_address = ''; |
113 $email_address = ''; |
|
114 $status = 'pending'; |
|
115 |
|
116 if ( ! isset( $_POST['send_confirmation_email'] ) ) { |
|
117 $status = 'confirmed'; |
|
118 } |
114 |
119 |
115 if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) { |
120 if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) { |
116 add_settings_error( |
121 add_settings_error( |
117 'action_type', |
122 'action_type', |
118 'action_type', |
123 'action_type', |
119 __( 'Invalid action.' ), |
124 __( 'Invalid personal data action.' ), |
120 'error' |
125 'error' |
121 ); |
126 ); |
122 } |
127 } |
123 |
128 |
124 if ( ! is_email( $username_or_email_address ) ) { |
129 if ( ! is_email( $username_or_email_address ) ) { |
139 |
144 |
140 if ( empty( $email_address ) ) { |
145 if ( empty( $email_address ) ) { |
141 break; |
146 break; |
142 } |
147 } |
143 |
148 |
144 $request_id = wp_create_user_request( $email_address, $action_type ); |
149 $request_id = wp_create_user_request( $email_address, $action_type, array(), $status ); |
|
150 $message = ''; |
145 |
151 |
146 if ( is_wp_error( $request_id ) ) { |
152 if ( is_wp_error( $request_id ) ) { |
|
153 $message = $request_id->get_error_message(); |
|
154 } elseif ( ! $request_id ) { |
|
155 $message = __( 'Unable to initiate confirmation request.' ); |
|
156 } |
|
157 |
|
158 if ( $message ) { |
147 add_settings_error( |
159 add_settings_error( |
148 'username_or_email_for_privacy_request', |
160 'username_or_email_for_privacy_request', |
149 'username_or_email_for_privacy_request', |
161 'username_or_email_for_privacy_request', |
150 $request_id->get_error_message(), |
162 $message, |
151 'error' |
|
152 ); |
|
153 break; |
|
154 } elseif ( ! $request_id ) { |
|
155 add_settings_error( |
|
156 'username_or_email_for_privacy_request', |
|
157 'username_or_email_for_privacy_request', |
|
158 __( 'Unable to initiate confirmation request.' ), |
|
159 'error' |
163 'error' |
160 ); |
164 ); |
161 break; |
165 break; |
162 } |
166 } |
163 |
167 |
164 wp_send_user_request( $request_id ); |
168 if ( 'pending' === $status ) { |
165 |
169 wp_send_user_request( $request_id ); |
166 add_settings_error( |
170 |
167 'username_or_email_for_privacy_request', |
171 $message = __( 'Confirmation request initiated successfully.' ); |
168 'username_or_email_for_privacy_request', |
172 } elseif ( 'confirmed' === $status ) { |
169 __( 'Confirmation request initiated successfully.' ), |
173 $message = __( 'Request added successfully.' ); |
170 'success' |
174 } |
171 ); |
175 |
172 break; |
176 if ( $message ) { |
|
177 add_settings_error( |
|
178 'username_or_email_for_privacy_request', |
|
179 'username_or_email_for_privacy_request', |
|
180 $message, |
|
181 'success' |
|
182 ); |
|
183 break; |
|
184 } |
173 } |
185 } |
174 } |
186 } |
175 } |
187 } |
176 |
188 |
177 /** |
189 /** |
277 $group_html .= '</table>'; |
289 $group_html .= '</table>'; |
278 } |
290 } |
279 |
291 |
280 if ( $groups_count > 1 ) { |
292 if ( $groups_count > 1 ) { |
281 $group_html .= '<div class="return-to-top">'; |
293 $group_html .= '<div class="return-to-top">'; |
282 $group_html .= '<a href="#top"><span aria-hidden="true">↑ </span> ' . esc_html__( 'Return to top' ) . '</a>'; |
294 $group_html .= '<a href="#top"><span aria-hidden="true">↑ </span> ' . esc_html__( 'Go to top' ) . '</a>'; |
283 $group_html .= '</div>'; |
295 $group_html .= '</div>'; |
284 } |
296 } |
285 |
297 |
286 $group_html .= '</div>'; |
298 $group_html .= '</div>'; |
287 |
299 |
295 * |
307 * |
296 * @param int $request_id The export request ID. |
308 * @param int $request_id The export request ID. |
297 */ |
309 */ |
298 function wp_privacy_generate_personal_data_export_file( $request_id ) { |
310 function wp_privacy_generate_personal_data_export_file( $request_id ) { |
299 if ( ! class_exists( 'ZipArchive' ) ) { |
311 if ( ! class_exists( 'ZipArchive' ) ) { |
300 wp_send_json_error( __( 'Unable to generate export file. ZipArchive not available.' ) ); |
312 wp_send_json_error( __( 'Unable to generate personal data export file. ZipArchive not available.' ) ); |
301 } |
313 } |
302 |
314 |
303 // Get the request. |
315 // Get the request. |
304 $request = wp_get_user_request( $request_id ); |
316 $request = wp_get_user_request( $request_id ); |
305 |
317 |
306 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
318 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
307 wp_send_json_error( __( 'Invalid request ID when generating export file.' ) ); |
319 wp_send_json_error( __( 'Invalid request ID when generating personal data export file.' ) ); |
308 } |
320 } |
309 |
321 |
310 $email_address = $request->email; |
322 $email_address = $request->email; |
311 |
323 |
312 if ( ! is_email( $email_address ) ) { |
324 if ( ! is_email( $email_address ) ) { |
313 wp_send_json_error( __( 'Invalid email address when generating export file.' ) ); |
325 wp_send_json_error( __( 'Invalid email address when generating personal data export file.' ) ); |
314 } |
326 } |
315 |
327 |
316 // Create the exports folder if needed. |
328 // Create the exports folder if needed. |
317 $exports_dir = wp_privacy_exports_dir(); |
329 $exports_dir = wp_privacy_exports_dir(); |
318 $exports_url = wp_privacy_exports_url(); |
330 $exports_url = wp_privacy_exports_url(); |
319 |
331 |
320 if ( ! wp_mkdir_p( $exports_dir ) ) { |
332 if ( ! wp_mkdir_p( $exports_dir ) ) { |
321 wp_send_json_error( __( 'Unable to create export folder.' ) ); |
333 wp_send_json_error( __( 'Unable to create personal data export folder.' ) ); |
322 } |
334 } |
323 |
335 |
324 // Protect export folder from browsing. |
336 // Protect export folder from browsing. |
325 $index_pathname = $exports_dir . 'index.html'; |
337 $index_pathname = $exports_dir . 'index.php'; |
326 if ( ! file_exists( $index_pathname ) ) { |
338 if ( ! file_exists( $index_pathname ) ) { |
327 $file = fopen( $index_pathname, 'w' ); |
339 $file = fopen( $index_pathname, 'w' ); |
328 if ( false === $file ) { |
340 if ( false === $file ) { |
329 wp_send_json_error( __( 'Unable to protect export folder from browsing.' ) ); |
341 wp_send_json_error( __( 'Unable to protect personal data export folder from browsing.' ) ); |
330 } |
342 } |
331 fwrite( $file, '<!-- Silence is golden. -->' ); |
343 fwrite( $file, "<?php\n// Silence is golden.\n" ); |
332 fclose( $file ); |
344 fclose( $file ); |
333 } |
345 } |
334 |
346 |
335 $obscura = wp_generate_password( 32, false, false ); |
347 $obscura = wp_generate_password( 32, false, false ); |
336 $file_basename = 'wp-personal-data-file-' . $obscura; |
348 $file_basename = 'wp-personal-data-file-' . $obscura; |
347 $title = sprintf( |
359 $title = sprintf( |
348 /* translators: %s: User's email address. */ |
360 /* translators: %s: User's email address. */ |
349 __( 'Personal Data Export for %s' ), |
361 __( 'Personal Data Export for %s' ), |
350 $email_address |
362 $email_address |
351 ); |
363 ); |
352 |
|
353 // And now, all the Groups. |
|
354 $groups = get_post_meta( $request_id, '_export_data_grouped', true ); |
|
355 |
364 |
356 // First, build an "About" group on the fly for this report. |
365 // First, build an "About" group on the fly for this report. |
357 $about_group = array( |
366 $about_group = array( |
358 /* translators: Header for the About section in a personal data export. */ |
367 /* translators: Header for the About section in a personal data export. */ |
359 'group_label' => _x( 'About', 'personal data group label' ), |
368 'group_label' => _x( 'About', 'personal data group label' ), |
379 ), |
388 ), |
380 ), |
389 ), |
381 ), |
390 ), |
382 ); |
391 ); |
383 |
392 |
384 // Merge in the special about group. |
393 // And now, all the Groups. |
385 $groups = array_merge( array( 'about' => $about_group ), $groups ); |
394 $groups = get_post_meta( $request_id, '_export_data_grouped', true ); |
386 |
395 if ( is_array( $groups ) ) { |
387 $groups_count = count( $groups ); |
396 // Merge in the special "About" group. |
|
397 $groups = array_merge( array( 'about' => $about_group ), $groups ); |
|
398 $groups_count = count( $groups ); |
|
399 } else { |
|
400 if ( false !== $groups ) { |
|
401 _doing_it_wrong( |
|
402 __FUNCTION__, |
|
403 /* translators: %s: Post meta key. */ |
|
404 sprintf( __( 'The %s post meta must be an array.' ), '<code>_export_data_grouped</code>' ), |
|
405 '5.8.0' |
|
406 ); |
|
407 } |
|
408 |
|
409 $groups = null; |
|
410 $groups_count = 0; |
|
411 } |
388 |
412 |
389 // Convert the groups to JSON format. |
413 // Convert the groups to JSON format. |
390 $groups_json = wp_json_encode( $groups ); |
414 $groups_json = wp_json_encode( $groups ); |
391 |
415 |
|
416 if ( false === $groups_json ) { |
|
417 $error_message = sprintf( |
|
418 /* translators: %s: Error message. */ |
|
419 __( 'Unable to encode the personal data for export. Error: %s' ), |
|
420 json_last_error_msg() |
|
421 ); |
|
422 |
|
423 wp_send_json_error( $error_message ); |
|
424 } |
|
425 |
392 /* |
426 /* |
393 * Handle the JSON export. |
427 * Handle the JSON export. |
394 */ |
428 */ |
395 $file = fopen( $json_report_pathname, 'w' ); |
429 $file = fopen( $json_report_pathname, 'w' ); |
396 |
430 |
397 if ( false === $file ) { |
431 if ( false === $file ) { |
398 wp_send_json_error( __( 'Unable to open export file (JSON report) for writing.' ) ); |
432 wp_send_json_error( __( 'Unable to open personal data export file (JSON report) for writing.' ) ); |
399 } |
433 } |
400 |
434 |
401 fwrite( $file, '{' ); |
435 fwrite( $file, '{' ); |
402 fwrite( $file, '"' . $title . '":' ); |
436 fwrite( $file, '"' . $title . '":' ); |
403 fwrite( $file, $groups_json ); |
437 fwrite( $file, $groups_json ); |
408 * Handle the HTML export. |
442 * Handle the HTML export. |
409 */ |
443 */ |
410 $file = fopen( $html_report_pathname, 'w' ); |
444 $file = fopen( $html_report_pathname, 'w' ); |
411 |
445 |
412 if ( false === $file ) { |
446 if ( false === $file ) { |
413 wp_send_json_error( __( 'Unable to open export file (HTML report) for writing.' ) ); |
447 wp_send_json_error( __( 'Unable to open personal data export (HTML report) for writing.' ) ); |
414 } |
448 } |
415 |
449 |
416 fwrite( $file, "<!DOCTYPE html>\n" ); |
450 fwrite( $file, "<!DOCTYPE html>\n" ); |
417 fwrite( $file, "<html>\n" ); |
451 fwrite( $file, "<html>\n" ); |
418 fwrite( $file, "<head>\n" ); |
452 fwrite( $file, "<head>\n" ); |
502 } |
536 } |
503 |
537 |
504 $zip = new ZipArchive; |
538 $zip = new ZipArchive; |
505 if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) { |
539 if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) { |
506 if ( ! $zip->addFile( $json_report_pathname, 'export.json' ) ) { |
540 if ( ! $zip->addFile( $json_report_pathname, 'export.json' ) ) { |
507 $error = __( 'Unable to add data to JSON file.' ); |
541 $error = __( 'Unable to archive the personal data export file (JSON format).' ); |
508 } |
542 } |
509 |
543 |
510 if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) { |
544 if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) { |
511 $error = __( 'Unable to add data to HTML file.' ); |
545 $error = __( 'Unable to archive the personal data export file (HTML format).' ); |
512 } |
546 } |
513 |
547 |
514 $zip->close(); |
548 $zip->close(); |
515 |
549 |
516 if ( ! $error ) { |
550 if ( ! $error ) { |
527 * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem. |
561 * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem. |
528 */ |
562 */ |
529 do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname ); |
563 do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname ); |
530 } |
564 } |
531 } else { |
565 } else { |
532 $error = __( 'Unable to open export file (archive) for writing.' ); |
566 $error = __( 'Unable to open personal data export file (archive) for writing.' ); |
533 } |
567 } |
534 |
568 |
535 // Remove the JSON file. |
569 // Remove the JSON file. |
536 unlink( $json_report_pathname ); |
570 unlink( $json_report_pathname ); |
537 |
571 |
626 */ |
660 */ |
627 $subject = apply_filters( 'wp_privacy_personal_data_email_subject', $subject, $site_name, $email_data ); |
661 $subject = apply_filters( 'wp_privacy_personal_data_email_subject', $subject, $site_name, $email_data ); |
628 |
662 |
629 /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ |
663 /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ |
630 $email_text = __( |
664 $email_text = __( |
631 'Howdy, |
665 // phpcs:ignore Generic.WhiteSpace.ScopeIndent.Incorrect, PEAR.Functions.FunctionCallSignature.Indent |
|
666 'Howdy, |
632 |
667 |
633 Your request for an export of personal data has been completed. You may |
668 Your request for an export of personal data has been completed. You may |
634 download your personal data by clicking on the link below. For privacy |
669 download your personal data by clicking on the link below. For privacy |
635 and security, we will automatically delete the file on ###EXPIRATION###, |
670 and security, we will automatically delete the file on ###EXPIRATION###, |
636 so please download it before then. |
671 so please download it before then. |
756 |
791 |
757 // Get the request. |
792 // Get the request. |
758 $request = wp_get_user_request( $request_id ); |
793 $request = wp_get_user_request( $request_id ); |
759 |
794 |
760 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
795 if ( ! $request || 'export_personal_data' !== $request->action_name ) { |
761 wp_send_json_error( __( 'Invalid request ID when merging exporter data.' ) ); |
796 wp_send_json_error( __( 'Invalid request ID when merging personal data to export.' ) ); |
762 } |
797 } |
763 |
798 |
764 $export_data = array(); |
799 $export_data = array(); |
765 |
800 |
766 // First exporter, first page? Reset the report data accumulation array. |
801 // First exporter, first page? Reset the report data accumulation array. |
767 if ( 1 === $exporter_index && 1 === $page ) { |
802 if ( 1 === $exporter_index && 1 === $page ) { |
768 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
803 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
769 } else { |
804 } else { |
770 $export_data = get_post_meta( $request_id, '_export_data_raw', true ); |
805 $accumulated_data = get_post_meta( $request_id, '_export_data_raw', true ); |
|
806 |
|
807 if ( $accumulated_data ) { |
|
808 $export_data = $accumulated_data; |
|
809 } |
771 } |
810 } |
772 |
811 |
773 // Now, merge the data from the exporter response into the data we have accumulated already. |
812 // Now, merge the data from the exporter response into the data we have accumulated already. |
774 $export_data = array_merge( $export_data, $response['data'] ); |
813 $export_data = array_merge( $export_data, $response['data'] ); |
775 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
814 update_post_meta( $request_id, '_export_data_raw', $export_data ); |
903 |
942 |
904 // Get the request. |
943 // Get the request. |
905 $request = wp_get_user_request( $request_id ); |
944 $request = wp_get_user_request( $request_id ); |
906 |
945 |
907 if ( ! $request || 'remove_personal_data' !== $request->action_name ) { |
946 if ( ! $request || 'remove_personal_data' !== $request->action_name ) { |
908 wp_send_json_error( __( 'Invalid request ID when processing eraser data.' ) ); |
947 wp_send_json_error( __( 'Invalid request ID when processing personal data to erase.' ) ); |
909 } |
948 } |
910 |
949 |
911 /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
950 /** This filter is documented in wp-admin/includes/ajax-actions.php */ |
912 $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
951 $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
913 $is_last_eraser = count( $erasers ) === $eraser_index; |
952 $is_last_eraser = count( $erasers ) === $eraser_index; |