Make WordPress Core

Changeset 43071 for branches/4.9


Ignore:
Timestamp:
05/01/2018 11:41:37 PM (8 years ago)
Author:
SergeyBiryukov
Message:

Privacy: add new wp-admin screens for exporting and removing of personal data.

Props melchoyce, mikejolley, allendav, xkon.
Merges [42967] to the 4.9 branch.
See #43481.

Location:
branches/4.9
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/4.9

  • branches/4.9/src/wp-admin/css/forms.css

    r42865 r43071  
    14041404        }
    14051405}
     1406
     1407
     1408/* Privacy */
     1409
     1410.privacy_requests .column-email {
     1411        width: 40%;
     1412}
     1413.privacy_requests .column-type {
     1414        text-align: center;
     1415}
     1416.privacy_requests thead td:first-child,
     1417.privacy_requests tfoot td:first-child {
     1418        border-left: 4px solid #fff;
     1419}
     1420.privacy_requests tbody th {
     1421        border-left: 4px solid #fff;
     1422        background: #fff;
     1423        box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
     1424}
     1425.privacy_requests tbody td {
     1426        background: #fff;
     1427        box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
     1428}
     1429.privacy_requests .status-request-confirmed th,
     1430.privacy_requests .status-request-confirmed td {
     1431        background-color: #f7fcfe;
     1432        border-left-color: #00a0d2;
     1433}
     1434.privacy_requests .status-request-failed th,
     1435.privacy_requests .status-request-failed td {
     1436        background-color: #fef7f1;
     1437        border-left-color: #d64d21;
     1438}
     1439.status-label {
     1440        font-weight: bold;
     1441}
     1442.status-label.status-request-pending {
     1443        font-weight: normal;
     1444        font-style: italic;
     1445        color: #6c7781;
     1446}
     1447.status-label.status-request-failed {
     1448        color: #aa0000;
     1449        font-weight: bold;
     1450}
     1451.wp-privacy-request-form {
     1452        clear: both;
     1453}
     1454.wp-privacy-request-form-field {
     1455        margin: 1.5em 0;
     1456}
     1457.wp-privacy-request-form label {
     1458        font-weight: bold;
     1459        line-height: 1.5;
     1460        padding-bottom: .5em;
     1461        display: block;
     1462}
     1463.wp-privacy-request-form input {
     1464        line-height: 1.5;
     1465        margin: 0;
     1466}
     1467.email-personal-data::before {
     1468        display: inline-block;
     1469        font: normal 20px/1 dashicons;
     1470        margin: 3px 5px 0 -2px;
     1471        speak: none;
     1472        -webkit-font-smoothing: antialiased;
     1473        -moz-osx-font-smoothing: grayscale;
     1474        vertical-align: top;
     1475}
     1476.email-personal-data--sending::before {
     1477        color: #f56e28;
     1478        content: "\f463";
     1479        -webkit-animation: rotation 2s infinite linear;
     1480        animation: rotation 2s infinite linear;
     1481}
     1482.email-personal-data--sent::before {
     1483        color: #79ba49;
     1484        content: "\f147";
     1485}
     1486@-webkit-keyframes rotation {
     1487        0% {
     1488                -webkit-transform: rotate(0deg);
     1489                transform: rotate(0deg);
     1490        }
     1491        100% {
     1492                -webkit-transform: rotate(359deg);
     1493                transform: rotate(359deg);
     1494        }
     1495}
     1496@keyframes rotation {
     1497        0% {
     1498                -webkit-transform: rotate(0deg);
     1499                transform: rotate(0deg);
     1500        }
     1501        100% {
     1502                -webkit-transform: rotate(359deg);
     1503                transform: rotate(359deg);
     1504        }
     1505}
  • branches/4.9/src/wp-admin/includes/admin-filters.php

    r41741 r43071  
    4545add_action( 'admin_head', 'wp_site_icon'             );
    4646add_action( 'admin_head', '_ipad_meta'               );
     47
     48// Privacy tools
     49add_action( 'account_action_failed', '_wp_privacy_account_request_failed' );
     50add_action( 'admin_menu', '_wp_privacy_hook_requests_page' );
    4751
    4852// Prerendering.
  • branches/4.9/src/wp-admin/includes/user.php

    r41686 r43071  
    539539%%s' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), home_url(), wp_specialchars_decode( translate_user_role( $role['name'] ) ) );
    540540}
     541
     542/**
     543 * Get action description from the name.
     544 *
     545 * @since 5.0.0
     546 * @access private
     547 *
     548 * @return string
     549 */
     550function _wp_privacy_action_description( $request_type ) {
     551        switch ( $request_type ) {
     552                case 'user_export_request':
     553                        return __( 'Export Personal Data' );
     554                case 'user_remove_request':
     555                        return __( 'Remove Personal Data' );
     556        }
     557}
     558
     559/**
     560 * Log a request and send to the user.
     561 *
     562 * @since 5.0.0
     563 * @access private
     564 *
     565 * @param string $email_address Email address sending the request to.
     566 * @param string $action Action being requested.
     567 * @param string $description Description of request.
     568 * @return bool|WP_Error depending on success.
     569 */
     570function _wp_privacy_create_request( $email_address, $action, $description ) {
     571        $user_id = 0;
     572        $user    = get_user_by( 'email', $email_address );
     573
     574        if ( $user ) {
     575                $user_id = $user->ID;
     576        }
     577
     578        $privacy_request_id = wp_insert_post( array(
     579                'post_author'   => $user_id,
     580                'post_status'   => 'request-pending',
     581                'post_type'     => $action,
     582                'post_date'     => current_time( 'mysql', false ),
     583                'post_date_gmt' => current_time( 'mysql', true ),
     584        ), true );
     585
     586        if ( is_wp_error( $privacy_request_id ) ) {
     587                return $privacy_request_id;
     588        }
     589
     590        update_post_meta( $privacy_request_id, '_user_email', $email_address );
     591        update_post_meta( $privacy_request_id, '_action_name', $action );
     592        update_post_meta( $privacy_request_id, '_confirmed_timestamp', false );
     593
     594        return wp_send_account_verification_key( $email_address, $action, $description, array(
     595                'privacy_request_id' => $privacy_request_id,
     596        ) );
     597}
     598
     599/**
     600 * Resend an existing request and return the result.
     601 *
     602 * @since 5.0.0
     603 * @access private
     604 *
     605 * @param int $privacy_request_id Request ID.
     606 * @return bool|WP_Error
     607 */
     608function _wp_privacy_resend_request( $privacy_request_id ) {
     609        $privacy_request_id = absint( $privacy_request_id );
     610        $privacy_request    = get_post( $privacy_request_id );
     611
     612        if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) {
     613                return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) );
     614        }
     615
     616        $email_address = get_post_meta( $privacy_request_id, '_user_email', true );
     617        $action        = get_post_meta( $privacy_request_id, '_action_name', true );
     618        $description   = _wp_privacy_action_description( $action );
     619        $result        = wp_send_account_verification_key( $email_address, $action, $description, array(
     620                'privacy_request_id' => $privacy_request_id,
     621        ) );
     622
     623        if ( is_wp_error( $result ) ) {
     624                return $result;
     625        } elseif ( ! $result ) {
     626                return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) );
     627        }
     628
     629        wp_update_post( array(
     630                'ID'            => $privacy_request_id,
     631                'post_status'   => 'request-pending',
     632                'post_date'     => current_time( 'mysql', false ),
     633                'post_date_gmt' => current_time( 'mysql', true ),
     634        ) );
     635
     636        return true;
     637}
     638
     639/**
     640 * Marks a request as completed by the admin and logs the datetime.
     641 *
     642 * @since 5.0.0
     643 * @access private
     644 *
     645 * @param int $privacy_request_id Request ID.
     646 * @return bool|WP_Error
     647 */
     648function _wp_privacy_completed_request( $privacy_request_id ) {
     649        $privacy_request_id = absint( $privacy_request_id );
     650        $privacy_request    = get_post( $privacy_request_id );
     651
     652        if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) {
     653                return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) );
     654        }
     655
     656        wp_update_post( array(
     657                'ID'          => $privacy_request_id,
     658                'post_status' => 'request-completed',
     659        ) );
     660
     661        update_post_meta( $privacy_request_id, '_completed_timestamp', time() );
     662}
     663
     664/**
     665 * Handle list table actions.
     666 *
     667 * @since 5.0.0
     668 * @access private
     669 */
     670function _wp_personal_data_handle_actions() {
     671        if ( isset( $_POST['export_personal_data_email_retry'] ) ) { // WPCS: input var ok.
     672                check_admin_referer( 'bulk-privacy_requests' );
     673
     674                $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['export_personal_data_email_retry'] ) ) ) ); // WPCS: input var ok, sanitization ok.
     675                $result     = _wp_privacy_resend_request( $request_id );
     676
     677                if ( is_wp_error( $result ) ) {
     678                        add_settings_error(
     679                                'export_personal_data_email_retry',
     680                                'export_personal_data_email_retry',
     681                                $result->get_error_message(),
     682                                'error'
     683                        );
     684                } else {
     685                        add_settings_error(
     686                                'export_personal_data_email_retry',
     687                                'export_personal_data_email_retry',
     688                                __( 'Confirmation request re-resent successfully.' ),
     689                                'updated'
     690                        );
     691                }
     692
     693        } elseif ( isset( $_POST['export_personal_data_email_send'] ) ) { // WPCS: input var ok.
     694                check_admin_referer( 'bulk-privacy_requests' );
     695
     696                $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['export_personal_data_email_send'] ) ) ) ); // WPCS: input var ok, sanitization ok.
     697                $result     = false;
     698
     699                /**
     700                 * TODO: Email the data to the user here.
     701                 */
     702
     703                if ( is_wp_error( $result ) ) {
     704                        add_settings_error(
     705                                'export_personal_data_email_send',
     706                                'export_personal_data_email_send',
     707                                $result->get_error_message(),
     708                                'error'
     709                        );
     710                } else {
     711                        _wp_privacy_completed_request( $request_id );
     712                        add_settings_error(
     713                                'export_personal_data_email_send',
     714                                'export_personal_data_email_send',
     715                                __( 'Personal data was sent to the user successfully.' ),
     716                                'updated'
     717                        );
     718                }
     719
     720        } elseif ( isset( $_POST['action'] ) ) {
     721                $action = isset( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : ''; // WPCS: input var ok, CSRF ok.
     722
     723                switch ( $action ) {
     724                        case 'add_export_personal_data_request':
     725                        case 'add_remove_personal_data_request':
     726                                check_admin_referer( 'personal-data-request' );
     727
     728                                if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_to_export'] ) ) { // WPCS: input var ok.
     729                                        add_settings_error(
     730                                                'action_type',
     731                                                'action_type',
     732                                                __( 'Invalid action.' ),
     733                                                'error'
     734                                        );
     735                                }
     736                                $action_type               = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); // WPCS: input var ok.
     737                                $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_to_export'] ) ); // WPCS: input var ok.
     738                                $email_address             = '';
     739
     740                                if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) {
     741                                        add_settings_error(
     742                                                'action_type',
     743                                                'action_type',
     744                                                __( 'Invalid action.' ),
     745                                                'error'
     746                                        );
     747                                }
     748
     749                                if ( ! is_email( $username_or_email_address ) ) {
     750                                        $user = get_user_by( 'login', $username_or_email_address );
     751                                        if ( ! $user instanceof WP_User ) {
     752                                                add_settings_error(
     753                                                        'username_or_email_to_export',
     754                                                        'username_or_email_to_export',
     755                                                        __( 'Unable to add export request. A valid email address or username must be supplied.' ),
     756                                                        'error'
     757                                                );
     758                                        } else {
     759                                                $email_address = $user->user_email;
     760                                        }
     761                                } else {
     762                                        $email_address = $username_or_email_address;
     763                                }
     764
     765                                if ( ! empty( $email_address ) ) {
     766                                        $result = _wp_privacy_create_request( $email_address, $action_type, _wp_privacy_action_description( $action_type ) );
     767
     768                                        if ( is_wp_error( $result ) ) {
     769                                                add_settings_error(
     770                                                        'username_or_email_to_export',
     771                                                        'username_or_email_to_export',
     772                                                        $result->get_error_message(),
     773                                                        'error'
     774                                                );
     775                                        } elseif ( ! $result ) {
     776                                                add_settings_error(
     777                                                        'username_or_email_to_export',
     778                                                        'username_or_email_to_export',
     779                                                        __( 'Unable to initiate confirmation request.' ),
     780                                                        'error'
     781                                                );
     782                                        } else {
     783                                                add_settings_error(
     784                                                        'username_or_email_to_export',
     785                                                        'username_or_email_to_export',
     786                                                        __( 'Confirmation request initiated successfully.' ),
     787                                                        'updated'
     788                                                );
     789                                        }
     790                                }
     791                                break;
     792                }
     793        }
     794}
     795
     796/**
     797 * Personal data export.
     798 *
     799 * @since 5.0.0
     800 * @access private
     801 */
     802function _wp_personal_data_export_page() {
     803        if ( ! current_user_can( 'manage_options' ) ) {
     804                wp_die( esc_html__( 'Sorry, you are not allowed to manage privacy on this site.' ) );
     805        }
     806
     807        _wp_personal_data_handle_actions();
     808
     809        $requests_table = new WP_Privacy_Data_Export_Requests_Table( array(
     810                'plural'   => 'privacy_requests',
     811                'singular' => 'privacy_request',
     812        ) );
     813        $requests_table->process_bulk_action();
     814        $requests_table->prepare_items();
     815        ?>
     816        <div class="wrap nosubsub">
     817                <h1><?php esc_html_e( 'Export Personal Data' ); ?></h1>
     818                <hr class="wp-header-end" />
     819
     820                <?php settings_errors(); ?>
     821
     822                <form method="post" class="wp-privacy-request-form">
     823                        <h2><?php esc_html_e( 'Add Data Export Request' ); ?></h2>
     824                        <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p>
     825
     826                        <div class="wp-privacy-request-form-field">
     827                                <label for="username_or_email_to_export"><?php esc_html_e( 'Username or email address' ); ?></label>
     828                                <input type="text" required class="regular-text" id="username_or_email_to_export" name="username_or_email_to_export" />
     829                                <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?>
     830                        </div>
     831                        <?php wp_nonce_field( 'personal-data-request' ); ?>
     832                        <input type="hidden" name="action" value="add_export_personal_data_request" />
     833                        <input type="hidden" name="type_of_action" value="user_export_request" />
     834                </form>
     835                <hr />
     836
     837                <?php $requests_table->views(); ?>
     838
     839                <form class="search-form wp-clearfix">
     840                        <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?>
     841                        <input type="hidden" name="page" value="export_personal_data" />
     842                        <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" />
     843                        <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" />
     844                        <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" />
     845                </form>
     846
     847                <form method="post">
     848                        <?php
     849                        $requests_table->display();
     850                        $requests_table->embed_scripts();
     851                        ?>
     852                </form>
     853        </div>
     854        <?php
     855}
     856
     857/**
     858 * Personal data anonymization.
     859 *
     860 * @since 5.0.0
     861 * @access private
     862 */
     863function _wp_personal_data_removal_page() {
     864        if ( ! current_user_can( 'manage_options' ) ) {
     865                wp_die( esc_html__( 'Sorry, you are not allowed to manage privacy on this site.' ) );
     866        }
     867
     868        _wp_personal_data_handle_actions();
     869
     870        $requests_table = new WP_Privacy_Data_Removal_Requests_Table( array(
     871                'plural'   => 'privacy_requests',
     872                'singular' => 'privacy_request',
     873        ) );
     874        $requests_table->process_bulk_action();
     875        $requests_table->prepare_items();
     876        ?>
     877        <div class="wrap nosubsub">
     878                <h1><?php esc_html_e( 'Remove Personal Data' ); ?></h1>
     879                <hr class="wp-header-end" />
     880
     881                <?php settings_errors(); ?>
     882
     883                <form method="post" class="wp-privacy-request-form">
     884                        <h2><?php esc_html_e( 'Add Data Removal Request' ); ?></h2>
     885                        <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p>
     886
     887                        <div class="wp-privacy-request-form-field">
     888                                <label for="username_or_email_to_export"><?php esc_html_e( 'Username or email address' ); ?></label>
     889                                <input type="text" required class="regular-text" id="username_or_email_to_export" name="username_or_email_to_export" />
     890                                <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?>
     891                        </div>
     892                        <?php wp_nonce_field( 'personal-data-request' ); ?>
     893                        <input type="hidden" name="action" value="add_remove_personal_data_request" />
     894                        <input type="hidden" name="type_of_action" value="user_remove_request" />
     895                </form>
     896                <hr />
     897
     898                <?php $requests_table->views(); ?>
     899
     900                <form class="search-form wp-clearfix">
     901                        <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?>
     902                        <input type="hidden" name="page" value="export_personal_data" />
     903                        <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" />
     904                        <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" />
     905                        <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" />
     906                </form>
     907
     908                <form method="post">
     909                        <?php
     910                        $requests_table->display();
     911                        $requests_table->embed_scripts();
     912                        ?>
     913                </form>
     914        </div>
     915        <?php
     916}
     917
     918/**
     919 * Add requests pages.
     920 *
     921 * @since 5.0.0
     922 * @access private
     923 */
     924function _wp_privacy_hook_requests_page() {
     925        add_submenu_page( 'tools.php', __( 'Export Personal Data' ), __( 'Export Personal Data' ), 'manage_options', 'export_personal_data', '_wp_personal_data_export_page' );
     926        add_submenu_page( 'tools.php', __( 'Remove Personal Data' ), __( 'Remove Personal Data' ), 'manage_options', 'remove_personal_data', '_wp_personal_data_removal_page' );
     927}
     928
     929// TODO: move the following classes in new files.
     930if ( ! class_exists( 'WP_List_Table' ) ) {
     931        require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
     932}
     933
     934/**
     935 * WP_Privacy_Requests_Table class.
     936 */
     937abstract class WP_Privacy_Requests_Table extends WP_List_Table {
     938
     939        /**
     940         * Action name for the requests this table will work with. Classes
     941         * which inherit from WP_Privacy_Requests_Table should define this.
     942         * e.g. 'user_export_request'
     943         *
     944         * @since 5.0.0
     945         *
     946         * @var string $request_type Name of action.
     947         */
     948        protected $request_type = 'INVALID';
     949
     950        /**
     951         * Get columns to show in the list table.
     952         *
     953         * @since 5.0.0
     954         *
     955         * @param array Array of columns.
     956         */
     957        public function get_columns() {
     958                $columns = array(
     959                        'cb'         => '<input type="checkbox" />',
     960                        'email'      => __( 'Requester' ),
     961                        'status'     => __( 'Status' ),
     962                        'requested'  => __( 'Requested' ),
     963                        'next_steps' => __( 'Next Steps' ),
     964                );
     965                return $columns;
     966        }
     967
     968        /**
     969         * Get a list of sortable columns.
     970         *
     971         * @since 5.0.0
     972         *
     973         * @return array
     974         */
     975        protected function get_sortable_columns() {
     976                return array();
     977        }
     978
     979        /**
     980         * Default primary column.
     981         *
     982         * @since 5.0.0
     983         *
     984         * @return string
     985         */
     986        protected function get_default_primary_column_name() {
     987                return 'email';
     988        }
     989
     990        /**
     991         * Get an associative array ( id => link ) with the list
     992         * of views available on this table.
     993         *
     994         * @since 5.0.0
     995         *
     996         * @return array
     997         */
     998        protected function get_views() {
     999                $current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ): '';
     1000                $statuses       = _wp_privacy_statuses();
     1001                $views          = array();
     1002                $admin_url      = admin_url( 'tools.php?page=' . $this->request_type );
     1003                $counts         = wp_count_posts( $this->request_type );
     1004
     1005                $current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : '';
     1006                $views['all']            = '<a href="' . esc_url( $admin_url ) . "\" $current_link_attributes>" . esc_html__( 'All' ) . ' (' . absint( array_sum( (array) $counts ) ) . ')</a>';
     1007
     1008                foreach ( $statuses as $status => $label ) {
     1009                        $current_link_attributes = $status === $current_status ? ' class="current" aria-current="page"' : '';
     1010                        $views[ $status ]        = '<a href="' . esc_url( add_query_arg( 'filter-status', $status, $admin_url ) ) . "\" $current_link_attributes>" . esc_html( $label ) . ' (' . absint( $counts->$status ) . ')</a>';
     1011                }
     1012
     1013                return $views;
     1014        }
     1015
     1016        /**
     1017         * Get bulk actions.
     1018         *
     1019         * @since 5.0.0
     1020         *
     1021         * @return array
     1022         */
     1023        protected function get_bulk_actions() {
     1024                return array(
     1025                        'delete' => __( 'Remove' ),
     1026                        'resend' => __( 'Resend email' ),
     1027                );
     1028        }
     1029
     1030        /**
     1031         * Process bulk actions.
     1032         *
     1033         * @since 5.0.0
     1034         */
     1035        public function process_bulk_action() {
     1036                $action      = $this->current_action();
     1037                $request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array(); // WPCS: input var ok, CSRF ok.
     1038
     1039                if ( $request_ids ) {
     1040                        check_admin_referer( 'bulk-privacy_requests' );
     1041                }
     1042
     1043                switch ( $action ) {
     1044                        case 'delete':
     1045                                $count = 0;
     1046
     1047                                foreach ( $request_ids as $request_id ) {
     1048                                        if ( wp_delete_post( $request_id, true ) ) {
     1049                                                $count ++;
     1050                                        }
     1051                                }
     1052
     1053                                add_settings_error(
     1054                                        'bulk_action',
     1055                                        'bulk_action',
     1056                                        sprintf( _n( 'Deleted %d request', 'Deleted %d requests', $count ), $count ),
     1057                                        'updated'
     1058                                );
     1059                                break;
     1060                        case 'resend':
     1061                                $count = 0;
     1062
     1063                                foreach ( $request_ids as $request_id ) {
     1064                                        if ( _wp_privacy_resend_request( $request_id ) ) {
     1065                                                $count ++;
     1066                                        }
     1067                                }
     1068
     1069                                add_settings_error(
     1070                                        'bulk_action',
     1071                                        'bulk_action',
     1072                                        sprintf( _n( 'Re-sent %d request', 'Re-sent %d requests', $count ), $count ),
     1073                                        'updated'
     1074                                );
     1075                                break;
     1076                }
     1077        }
     1078
     1079        /**
     1080         * Prepare items to output.
     1081         *
     1082         * @since 5.0.0
     1083         */
     1084        public function prepare_items() {
     1085                global $wpdb;
     1086
     1087                $primary               = $this->get_primary_column_name();
     1088                $this->_column_headers = array(
     1089                        $this->get_columns(),
     1090                        array(),
     1091                        $this->get_sortable_columns(),
     1092                        $primary,
     1093                );
     1094
     1095                $this->items    = array();
     1096                $posts_per_page = 20;
     1097                $args           = array(
     1098                        'post_type'      => $this->request_type,
     1099                        'posts_per_page' => $posts_per_page,
     1100                        'offset'         => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page: 0,
     1101                        'post_status'    => 'any',
     1102                );
     1103
     1104                if ( ! empty( $_REQUEST['filter-status'] ) ) {
     1105                        $filter_status       = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
     1106                        $args['post_status'] = $filter_status;
     1107                }
     1108
     1109                if ( ! empty( $_REQUEST['s'] ) ) {
     1110                        $args['meta_query'] = array(
     1111                                $name_query,
     1112                                'relation'  => 'AND',
     1113                                array(
     1114                                        'key'     => '_user_email',
     1115                                        'value'   => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ): '',
     1116                                        'compare' => 'LIKE'
     1117                                ),
     1118                        );
     1119                }
     1120
     1121                $privacy_requests_query = new WP_Query( $args );
     1122                $privacy_requests       = $privacy_requests_query->posts;
     1123
     1124                foreach ( $privacy_requests as $privacy_request ) {
     1125                        $this->items[] = array(
     1126                                'request_id' => $privacy_request->ID,
     1127                                'user_id'    => $privacy_request->post_author,
     1128                                'email'      => get_post_meta( $privacy_request->ID, '_user_email', true ),
     1129                                'action'     => get_post_meta( $privacy_request->ID, '_action_name', true ),
     1130                                'requested'  => strtotime( $privacy_request->post_date_gmt ),
     1131                                'confirmed'  => get_post_meta( $privacy_request->ID, '_confirmed_timestamp', true ),
     1132                                'completed'  => get_post_meta( $privacy_request->ID, '_completed_timestamp', true ),
     1133                        );
     1134                }
     1135
     1136                $this->set_pagination_args(
     1137                        array(
     1138                                'total_items' => $privacy_requests_query->found_posts,
     1139                                'per_page'    => $posts_per_page,
     1140                        )
     1141                );
     1142        }
     1143
     1144        /**
     1145         * Checkbox column.
     1146         *
     1147         * @since 5.0.0
     1148         *
     1149         * @param array $item Item being shown.
     1150         * @return string
     1151         */
     1152        public function column_cb( $item ) {
     1153                return sprintf( '<input type="checkbox" name="request_id[]" value="%1$s" /><span class="spinner"></span>', esc_attr( $item['request_id'] ) );
     1154        }
     1155
     1156        /**
     1157         * Status column.
     1158         *
     1159         * @since 5.0.0
     1160         *
     1161         * @param array $item Item being shown.
     1162         * @return string
     1163         */
     1164        public function column_status( $item ) {
     1165                $status        = get_post_status( $item['request_id'] );
     1166                $status_object = get_post_status_object( $status );
     1167
     1168                if ( ! $status_object || empty( $status_object->label ) ) {
     1169                        return '-';
     1170                }
     1171
     1172                $timestamp = false;
     1173
     1174                switch ( $status ) {
     1175                        case 'request-confirmed':
     1176                                $timestamp = $item['confirmed'];
     1177                                break;
     1178                        case 'request-completed':
     1179                                $timestamp = $item['completed'];
     1180                                break;
     1181                }
     1182
     1183                echo '<span class="status-label status-' . esc_attr( $status ) . '">';
     1184                echo esc_html( $status_object->label );
     1185
     1186                if ( $timestamp ) {
     1187                        echo ' (' . $this->get_timestamp_as_date( $timestamp ) . ')';
     1188                }
     1189
     1190                echo '</span>';
     1191        }
     1192
     1193        /**
     1194         * Convert timestamp for display.
     1195         *
     1196         * @since 5.0.0
     1197         *
     1198         * @param int $timestamp Event timestamp.
     1199         * @return string
     1200         */
     1201        protected function get_timestamp_as_date( $timestamp ) {
     1202                if ( empty( $timestamp ) ) {
     1203                        return '';
     1204                }
     1205
     1206                $time_diff = current_time( 'timestamp', true ) - $timestamp;
     1207
     1208                if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
     1209                        return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) );
     1210                }
     1211
     1212                return date_i18n( get_option( 'date_format' ), $timestamp );
     1213        }
     1214
     1215        /**
     1216         * Default column handler.
     1217         *
     1218         * @since 5.0.0
     1219         *
     1220         * @param array $item         Item being shown.
     1221         * @param string $column_name Name of column being shown.
     1222         * @return string
     1223         */
     1224        public function column_default( $item, $column_name ) {
     1225                $cell_value = $item[ $column_name ];
     1226
     1227                if ( in_array( $column_name, array( 'requested' ), true ) ) {
     1228                        return $this->get_timestamp_as_date( $cell_value );
     1229                }
     1230
     1231                return $cell_value;
     1232        }
     1233
     1234        /**
     1235         * Actions column. Overriden by children.
     1236         *
     1237         * @since 5.0.0
     1238         *
     1239         * @param array $item Item being shown.
     1240         * @return string
     1241         */
     1242        public function column_email( $item ) {
     1243                return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( array() ) );
     1244        }
     1245
     1246        /**
     1247         * Next steps column. Overriden by children.
     1248         *
     1249         * @since 5.0.0
     1250         *
     1251         * @param array $item Item being shown.
     1252         */
     1253        public function column_next_steps( $item ) {}
     1254
     1255        /**
     1256         * Generates content for a single row of the table
     1257         *
     1258         * @since 5.0.0
     1259         *
     1260         * @param object $item The current item
     1261         */
     1262        public function single_row( $item ) {
     1263                $status = get_post_status( $item['request_id'] );
     1264
     1265                echo '<tr class="status-' . esc_attr( $status ) . '">';
     1266                $this->single_row_columns( $item );
     1267                echo '</tr>';
     1268        }
     1269
     1270        /**
     1271         * Embed scripts used to perform actions. Overriden by children.
     1272         *
     1273         * @since 5.0.0
     1274         */
     1275        public function embed_scripts() {}
     1276}
     1277
     1278/**
     1279 * WP_Privacy_Data_Export_Requests_Table class.
     1280 *
     1281 * @since 5.0.0
     1282 */
     1283class WP_Privacy_Data_Export_Requests_Table extends WP_Privacy_Requests_Table {
     1284        /**
     1285         * Action name for the requests this table will work with. Classes
     1286         * which inherit from WP_Privacy_Requests_Table should define this.
     1287         * e.g. 'user_export_request'
     1288         *
     1289         * @since 5.0.0
     1290         *
     1291         * @var string $request_type Name of action.
     1292         */
     1293        protected $request_type = 'user_export_request';
     1294
     1295        /**
     1296         * Actions column.
     1297         *
     1298         * @since 5.0.0
     1299         *
     1300         * @param array $item Item being shown.
     1301         * @return string
     1302         */
     1303        public function column_email( $item ) {
     1304                $row_actions = array(
     1305                        'download_data' => __( 'Download Personal Data' ),
     1306                );
     1307
     1308                return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) );
     1309        }
     1310
     1311        /**
     1312         * Next steps column.
     1313         *
     1314         * @since 5.0.0
     1315         *
     1316         * @param array $item Item being shown.
     1317         */
     1318        public function column_next_steps( $item ) {
     1319                $status = get_post_status( $item['request_id'] );
     1320
     1321                switch ( $status ) {
     1322                        case 'request-pending':
     1323                                esc_html_e( 'Waiting for confirmation' );
     1324                                break;
     1325                        case 'request-confirmed':
     1326                                // TODO Complete in follow on patch.
     1327                                break;
     1328                        case 'request-failed':
     1329                                submit_button( __( 'Retry' ), 'secondary', 'export_personal_data_email_retry[' . $item['request_id'] . ']', false );
     1330                                break;
     1331                        case 'request-completed':
     1332                                echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array(
     1333                                        'action' => 'delete',
     1334                                        'request_id' => array( $item['request_id'] )
     1335                                ), admin_url( 'tools.php?page=export_personal_data' ) ), 'bulk-privacy_requests' ) ) . '">' . esc_html__( 'Remove request' ) . '</a>';
     1336                                break;
     1337                }
     1338        }
     1339}
     1340
     1341/**
     1342 * WP_Privacy_Data_Removal_Requests_Table class.
     1343 *
     1344         * @since 5.0.0
     1345 */
     1346class WP_Privacy_Data_Removal_Requests_Table extends WP_Privacy_Requests_Table {
     1347        /**
     1348         * Action name for the requests this table will work with. Classes
     1349         * which inherit from WP_Privacy_Requests_Table should define this.
     1350         * e.g. 'user_remove_request'
     1351         *
     1352         * @since 5.0.0
     1353         *
     1354         * @var string $request_type Name of action.
     1355         */
     1356        protected $request_type = 'user_remove_request';
     1357
     1358        /**
     1359         * Actions column.
     1360         *
     1361         * @since 5.0.0
     1362         *
     1363         * @param array $item Item being shown.
     1364         * @return string
     1365         */
     1366        public function column_email( $item ) {
     1367                $row_actions = array(
     1368                        // TODO Complete in follow on patch.
     1369                        'remove_data' => __( 'Remove Personal Data' ),
     1370                );
     1371
     1372                // If we have a user ID, include a delete user action.
     1373                if ( ! empty( $item['user_id'] ) ) {
     1374                        // TODO Complete in follow on patch.
     1375                        $row_actions['delete_user'] = __( 'Delete User' );
     1376                }
     1377
     1378                return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) );
     1379        }
     1380
     1381        /**
     1382         * Next steps column.
     1383         *
     1384         * @since 5.0.0
     1385         *
     1386         * @param array $item Item being shown.
     1387         */
     1388        public function column_next_steps( $item ) {
     1389        }
     1390
     1391}
  • branches/4.9/src/wp-includes/post.php

    r42589 r43071  
    211211        ) );
    212212
     213        register_post_type( 'user_export_request', array(
     214                'labels'           => array(
     215                        'name'          => __( 'Export Personal Data Requests' ),
     216                        'singular_name' => __( 'Export Personal Data Request' ),
     217                ),
     218                'public'           => false,
     219                '_builtin'         => true, /* internal use only. don't use this when registering your own post type. */
     220                'hierarchical'     => false,
     221                'rewrite'          => false,
     222                'query_var'        => false,
     223                'can_export'       => false,
     224                'delete_with_user' => false,
     225        ) );
     226
     227        register_post_type( 'user_remove_request', array(
     228                'labels'           => array(
     229                        'name'          => __( 'Remove Personal Data Requests' ),
     230                        'singular_name' => __( 'Remove Personal Data Request' ),
     231                ),
     232                'public'           => false,
     233                '_builtin'         => true, /* internal use only. don't use this when registering your own post type. */
     234                'hierarchical'     => false,
     235                'rewrite'          => false,
     236                'query_var'        => false,
     237                'can_export'       => false,
     238                'delete_with_user' => false,
     239        ) );
     240
    213241        register_post_status( 'publish', array(
    214242                'label'       => _x( 'Published', 'post status' ),
     
    264292                'internal' => true,
    265293                '_builtin' => true, /* internal use only. */
     294                'exclude_from_search' => false,
     295        ) );
     296
     297        register_post_status( 'request-pending', array(
     298                'label'               => _x( 'Pending', 'request status' ),
     299                'internal'            => true,
     300                '_builtin'            => true, /* internal use only. */
     301                'exclude_from_search' => false,
     302        ) );
     303
     304        register_post_status( 'request-confirmed', array(
     305                'label'               => _x( 'Confirmed', 'request status' ),
     306                'internal'            => true,
     307                '_builtin'            => true, /* internal use only. */
     308                'exclude_from_search' => false,
     309        ) );
     310
     311        register_post_status( 'request-failed', array(
     312                'label'               => _x( 'Failed', 'request status' ),
     313                'internal'            => true,
     314                '_builtin'            => true, /* internal use only. */
     315                'exclude_from_search' => false,
     316        ) );
     317
     318        register_post_status( 'request-completed', array(
     319                'label'               => _x( 'Completed', 'request status' ),
     320                'internal'            => true,
     321                '_builtin'            => true, /* internal use only. */
    266322                'exclude_from_search' => false,
    267323        ) );
     
    728784
    729785        return $status;
     786}
     787
     788/**
     789 * Return statuses for privacy requests.
     790 *
     791 * @since 5.0.0
     792 *
     793 * @return array
     794 */
     795function _wp_privacy_statuses() {
     796        return array(
     797                'request-pending'   => __( 'Pending' ),      // Pending confirmation from user.
     798                'request-confirmed' => __( 'Confirmed' ),    // User has confirmed the action.
     799                'request-failed'    => __( 'Failed' ),       // User failed to confirm the action.
     800                'request-completed' => __( 'Completed' ),    // Admin has handled the request.
     801        );
    730802}
    731803
  • branches/4.9/src/wp-includes/user.php

    r43070 r43071  
    27342734
    27352735/**
     2736 * Get all user privacy request types.
     2737 *
     2738 * @since 5.0.0
     2739 * @access private
     2740 *
     2741 * @return array
     2742 */
     2743function _wp_privacy_action_request_types() {
     2744        return array(
     2745                'user_export_request',
     2746                'user_remove_request',
     2747        );
     2748}
     2749
     2750/**
     2751 * Update log when privacy request is confirmed.
     2752 *
     2753 * @since 5.0.0
     2754 * @access private
     2755 *
     2756 * @param array $result Result of the request from the user.
     2757 */
     2758function _wp_privacy_account_request_confirmed( $result ) {
     2759        if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) && in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) {
     2760                $privacy_request_id = absint( $result['request_data']['privacy_request_id'] );
     2761                $privacy_request    = get_post( $privacy_request_id );
     2762
     2763                if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) {
     2764                        return;
     2765                }
     2766
     2767                update_post_meta( $privacy_request_id, '_confirmed_timestamp', time() );
     2768                wp_update_post( array(
     2769                        'ID'          => $privacy_request_id,
     2770                        'post_status' => 'request-confirmed',
     2771                ) );
     2772        }
     2773}
     2774add_action( 'account_action_confirmed', '_wp_privacy_account_request_confirmed' );
     2775
     2776/**
     2777 * Update log when privacy request fails.
     2778 *
     2779 * @since 5.0.0
     2780 * @access private
     2781 *
     2782 * @param array $result Result of the request from the user.
     2783 */
     2784function _wp_privacy_account_request_failed( $result ) {
     2785        if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) &&
     2786                in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) {
     2787
     2788                $privacy_request_id = absint( $result['request_data']['privacy_request_id'] );
     2789                $privacy_request    = get_post( $privacy_request_id );
     2790
     2791                if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) {
     2792                        return;
     2793                }
     2794
     2795                wp_update_post( array(
     2796                        'ID'          => $privacy_request_id,
     2797                        'post_status' => 'request-failed',
     2798                ) );
     2799        }
     2800}
     2801
     2802/**
    27362803 * Send a confirmation request email to confirm an action.
    27372804 *
    27382805 * @since 5.0.0
    27392806 *
    2740  * @param string $email              User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address. 
     2807 * @param string $email              User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address.
    27412808 * @param string $action_name        Name of the action that is being confirmed. Defaults to 'confirm_email'.
    27422809 * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address".
     
    28402907         *
    28412908         * @since 5.0.0
    2842          * 
     2909         *
    28432910         * @param string $email_text     Text in the email.
    28442911         * @param array  $email_data {
     
    29623029                $email    = $user->user_email;
    29633030
    2964                 if ( false !== strpos( $confirm_action_data, ':' ) ) {
    2965                         list( $key_request_time, $saved_key ) = explode( ':', $confirm_action_data, 2 );
     3031                if ( false !== strpos( $raw_data, ':' ) ) {
     3032                        list( $key_request_time, $saved_key ) = explode( ':', $raw_data, 2 );
    29663033                }
    29673034        } else {
    29683035                $raw_data = get_site_option( '_verify_action_' . $action_name . '_' . $uid, '' );
    29693036
    2970                 if ( false !== strpos( $confirm_action_data, ':' ) ) {
    2971                         list( $key_request_time, $saved_key, $email ) = explode( ':', $confirm_action_data, 3 );
     3037                if ( false !== strpos( $raw_data, ':' ) ) {
     3038                        list( $key_request_time, $saved_key, $email ) = explode( ':', $raw_data, 3 );
    29723039                }
    29733040        }
     
    29913058         *
    29923059         * @since 5.0.0
    2993          * 
     3060         *
    29943061         * @param int $expiration The expiration time in seconds.
    29953062         */
Note: See TracChangeset for help on using the changeset viewer.