Make WordPress Core

Changeset 47278


Ignore:
Timestamp:
02/11/2020 09:26:25 PM (7 years ago)
Author:
SergeyBiryukov
Message:

Privacy: Add a table of contents to Personal Data Export report for easier navigation.

Props xkon, garrett-eclipse, birgire, karmatosed.
Fixes #46894.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/privacy-tools.php

    r47245 r47278  
    217217 *
    218218 * @since 4.9.6
     219 * @since 5.4.0 Added the `$group_id` and `$groups_count` parameters.
    219220 *
    220221 * @param array $group_data {
     
    233234 *     }
    234235 * }
    235  * @return string The HTML for this group and its items.
    236  */
    237 function wp_privacy_generate_personal_data_export_group_html( $group_data ) {
    238         $group_html  = '<h2>';
     236 * @param string  $group_id     The group identifier.
     237 * @param int     $groups_count The number of all groups
     238 * @return string $group_html   The HTML for this group and its items.
     239 */
     240function wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id = '', $groups_count = 1 ) {
     241        $group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id );
     242
     243        $group_html  = '<h2 id="' . esc_attr( $group_id_attr ) . '">';
    239244        $group_html .= esc_html( $group_data['group_label'] );
    240245
     
    271276                $group_html .= '</tbody>';
    272277                $group_html .= '</table>';
     278        }
     279
     280        if ( 1 < $groups_count ) {
     281                $group_html .= '<div class="return_to_top">';
     282                $group_html .= '<a href="#top">' . esc_html__( '&uarr; Return to top' ) . '</a>';
     283                $group_html .= '</div>';
    273284        }
    274285
     
    374385        $groups = array_merge( array( 'about' => $about_group ), $groups );
    375386
     387        $groups_count = count( $groups );
     388
    376389        // Convert the groups to JSON format.
    377390        $groups_json = wp_json_encode( $groups );
     
    411424        fwrite( $file, 'td { padding: 5px; }' );
    412425        fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' );
     426        fwrite( $file, '.return_to_top { text-align:right; }' );
    413427        fwrite( $file, '</style>' );
    414428        fwrite( $file, '<title>' );
     
    417431        fwrite( $file, "</head>\n" );
    418432        fwrite( $file, "<body>\n" );
    419         fwrite( $file, '<h1>' . esc_html__( 'Personal Data Export' ) . '</h1>' );
     433        fwrite( $file, '<h1 id="top">' . esc_html__( 'Personal Data Export' ) . '</h1>' );
     434
     435        // Create TOC.
     436        if ( 1 < $groups_count ) {
     437                fwrite( $file, '<div id="table_of_contents">' );
     438                fwrite( $file, '<h2>' . esc_html__( 'Table of Contents' ) . '</h2>' );
     439                fwrite( $file, '<ul>' );
     440                foreach ( (array) $groups as $group_id => $group_data ) {
     441                        $group_label       = esc_html( $group_data['group_label'] );
     442                        $group_id_attr     = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id );
     443                        $group_items_count = count( (array) $group_data['items'] );
     444                        if ( $group_items_count > 1 ) {
     445                                $group_label .= sprintf( ' <span class="count">(%d)</span>', $group_items_count );
     446                        }
     447                        fwrite( $file, '<li>' );
     448                        fwrite( $file, '<a href="#' . esc_attr( $group_id_attr ) . '">' . $group_label . '</a>' );
     449                        fwrite( $file, '</li>' );
     450                }
     451                fwrite( $file, '</ul>' );
     452                fwrite( $file, '</div>' );
     453        }
    420454
    421455        // Now, iterate over every group in $groups and have the formatter render it in HTML.
    422456        foreach ( (array) $groups as $group_id => $group_data ) {
    423                 fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data ) );
     457                fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id, $groups_count ) );
    424458        }
    425459
  • trunk/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php

    r47245 r47278  
    261261                $request         = wp_get_user_request( self::$export_request_id );
    262262
    263                 $this->assertContains( '<h1>Personal Data Export</h1>', $report_contents );
    264                 $this->assertContains( '<h2>About</h2>', $report_contents );
     263                $this->assertContains( '<h1 id="top">Personal Data Export</h1>', $report_contents );
     264                $this->assertContains( '<h2 id="about-about">About</h2>', $report_contents );
    265265                $this->assertContains( $request->email, $report_contents );
    266266        }
     
    295295                $this->assertContains( '"about"', $report_contents_json );
    296296        }
     297
     298        /**
     299         * Test the export HTML file containing one export group has no table of contents.
     300         *
     301         * @ticket 46894
     302         */
     303        public function test_single_group_export_no_toc_or_return_to_top() {
     304                $this->expectOutputString( '' );
     305                wp_privacy_generate_personal_data_export_file( self::$export_request_id );
     306                $this->assertTrue( file_exists( $this->export_file_name ) );
     307
     308                $report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
     309                mkdir( $report_dir );
     310
     311                $zip        = new ZipArchive();
     312                $opened_zip = $zip->open( $this->export_file_name );
     313                $this->assertTrue( $opened_zip );
     314
     315                $zip->extractTo( $report_dir );
     316                $zip->close();
     317                $this->assertTrue( file_exists( $report_dir . 'index.html' ) );
     318
     319                $report_contents = file_get_contents( $report_dir . 'index.html' );
     320                $request         = wp_get_user_request( self::$export_request_id );
     321
     322                $this->assertNotContains( '<div id="table_of_contents">', $report_contents );
     323                $this->assertNotContains( '<div class="return_to_top">', $report_contents );
     324                $this->assertContains( $request->email, $report_contents );
     325        }
     326
     327        /**
     328         * Test the export HTML file containing ore than one export group has a table of contents.
     329         *
     330         * @ticket 46894
     331         */
     332        public function test_multiple_group_export_has_toc_and_return_to_top() {
     333                $this->expectOutputString( '' );
     334
     335                // Setup Export Data to contain multiple groups
     336                $export_data_grouped = array(
     337                        'user' => array(
     338                                'group_label'       => 'User',
     339                                'group_description' => 'User&#8217;s profile data.',
     340                                'items'             => array(
     341                                        'user-1' => array(
     342                                                array(
     343                                                        'name'  => 'User ID',
     344                                                        'value' => 1,
     345                                                ),
     346                                                array(
     347                                                        'name'  => 'User Login Name',
     348                                                        'value' => 'user_login',
     349                                                ),
     350                                                array(
     351                                                        'name'  => 'User Nice Name',
     352                                                        'value' => 'User Name',
     353                                                ),
     354                                                array(
     355                                                        'name'  => 'User Email',
     356                                                        'value' => 'export-requester@example.com',
     357                                                ),
     358                                                array(
     359                                                        'name'  => 'User Registration Date',
     360                                                        'value' => '2020-01-31 19:29:29',
     361                                                ),
     362                                                array(
     363                                                        'name'  => 'User Display Name',
     364                                                        'value' => 'User Name',
     365                                                ),
     366                                                array(
     367                                                        'name'  => 'User Nickname',
     368                                                        'value' => 'User',
     369                                                ),
     370                                        ),
     371                                ),
     372                        ),
     373                );
     374                update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
     375
     376                // Generate Export File
     377                wp_privacy_generate_personal_data_export_file( self::$export_request_id );
     378                $this->assertTrue( file_exists( $this->export_file_name ) );
     379
     380                // Cleam-up for subsequent tests
     381                update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
     382
     383                $report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
     384                mkdir( $report_dir );
     385
     386                $zip        = new ZipArchive();
     387                $opened_zip = $zip->open( $this->export_file_name );
     388                $this->assertTrue( $opened_zip );
     389
     390                $zip->extractTo( $report_dir );
     391                $zip->close();
     392                $this->assertTrue( file_exists( $report_dir . 'index.html' ) );
     393
     394                $report_contents = file_get_contents( $report_dir . 'index.html' );
     395                $request         = wp_get_user_request( self::$export_request_id );
     396
     397                $this->assertContains( '<div id="table_of_contents">', $report_contents );
     398                $this->assertContains( '<h2 id="user-user">User</h2>', $report_contents );
     399                $this->assertContains( '<div class="return_to_top">', $report_contents );
     400                $this->assertContains( $request->email, $report_contents );
     401        }
     402
     403        /**
     404         * Test the export HTML file containing multiple export groups with multiple group items
     405         * has a table of contents with group count.
     406         *
     407         * @ticket 46894
     408         */
     409        public function test_multiple_group_export_multiple_items_group_count_in_toc() {
     410                $this->expectOutputString( '' );
     411
     412                // Setup Export Data to contain multiple groups
     413                $export_data_grouped = array(
     414                        'user'     => array(
     415                                'group_label'       => 'User',
     416                                'group_description' => 'User&#8217;s profile data.',
     417                                'items'             => array(
     418                                        'user-1' => array(
     419                                                array(
     420                                                        'name'  => 'User ID',
     421                                                        'value' => 1,
     422                                                ),
     423                                                array(
     424                                                        'name'  => 'User Login Name',
     425                                                        'value' => 'user_login',
     426                                                ),
     427                                                array(
     428                                                        'name'  => 'User Nice Name',
     429                                                        'value' => 'User Name',
     430                                                ),
     431                                                array(
     432                                                        'name'  => 'User Email',
     433                                                        'value' => 'export-requester@example.com',
     434                                                ),
     435                                                array(
     436                                                        'name'  => 'User Registration Date',
     437                                                        'value' => '2020-01-31 19:29:29',
     438                                                ),
     439                                                array(
     440                                                        'name'  => 'User Display Name',
     441                                                        'value' => 'User Name',
     442                                                ),
     443                                                array(
     444                                                        'name'  => 'User Nickname',
     445                                                        'value' => 'User',
     446                                                ),
     447                                        ),
     448                                ),
     449                        ),
     450                        'comments' => array(
     451                                'group_label'       => 'Comments',
     452                                'group_description' => 'User&#8217;s comment data.',
     453                                'items'             => array(
     454                                        'comment-2' => array(
     455                                                array(
     456                                                        'name'  => 'Comment Author',
     457                                                        'value' => 'User Name',
     458                                                ),
     459                                                array(
     460                                                        'name'  => 'Comment Author Email',
     461                                                        'value' => 'export-requester@example.com',
     462                                                ),
     463                                                array(
     464                                                        'name'  => 'Comment Author IP',
     465                                                        'value' => '::1',
     466                                                ),
     467                                                array(
     468                                                        'name'  => 'Comment Author User Agent',
     469                                                        'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
     470                                                ),
     471                                                array(
     472                                                        'name'  => 'Comment Date',
     473                                                        'value' => '2020-01-31 19:55:19',
     474                                                ),
     475                                                array(
     476                                                        'name'  => 'Comment Content',
     477                                                        'value' => 'Test',
     478                                                ),
     479                                                array(
     480                                                        'name'  => 'Comment URL',
     481                                                        'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2" target="_blank" rel="noreferrer noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a>',
     482                                                ),
     483                                        ),
     484                                        'comment-3' => array(
     485                                                array(
     486                                                        'name'  => 'Comment Author',
     487                                                        'value' => 'User Name',
     488                                                ),
     489                                                array(
     490                                                        'name'  => 'Comment Author Email',
     491                                                        'value' => 'export-requester@example.com',
     492                                                ),
     493                                                array(
     494                                                        'name'  => 'Comment Author IP',
     495                                                        'value' => '::1',
     496                                                ),
     497                                                array(
     498                                                        'name'  => 'Comment Author User Agent',
     499                                                        'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
     500                                                ),
     501                                                array(
     502                                                        'name'  => 'Comment Date',
     503                                                        'value' => '2020-01-31 20:55:19',
     504                                                ),
     505                                                array(
     506                                                        'name'  => 'Comment Content',
     507                                                        'value' => 'Test #2',
     508                                                ),
     509                                                array(
     510                                                        'name'  => 'Comment URL',
     511                                                        'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-3" target="_blank" rel="noreferrer noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-3</a>',
     512                                                ),
     513                                        ),
     514                                ),
     515                        ),
     516                );
     517                update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
     518
     519                // Generate Export File
     520                wp_privacy_generate_personal_data_export_file( self::$export_request_id );
     521                $this->assertTrue( file_exists( $this->export_file_name ) );
     522
     523                // Cleam-up for subsequent tests
     524                update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
     525
     526                $report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
     527                mkdir( $report_dir );
     528
     529                $zip        = new ZipArchive();
     530                $opened_zip = $zip->open( $this->export_file_name );
     531                $this->assertTrue( $opened_zip );
     532
     533                $zip->extractTo( $report_dir );
     534                $zip->close();
     535                $this->assertTrue( file_exists( $report_dir . 'index.html' ) );
     536
     537                $report_contents = file_get_contents( $report_dir . 'index.html' );
     538                $request         = wp_get_user_request( self::$export_request_id );
     539
     540                $this->assertContains( '<div id="table_of_contents">', $report_contents );
     541                $this->assertContains( '<a href="#comments-comments">Comments <span class="count">(2)</span></a>', $report_contents );
     542                $this->assertContains( $request->email, $report_contents );
     543        }
     544
     545        /**
     546         * Test the export HTML file containing multiple export groups with no multiple group items
     547         * has a table of contents without group count.
     548         *
     549         * @ticket 46894
     550         */
     551        public function test_multiple_group_export_single_items_no_group_count_in_toc() {
     552                $this->expectOutputString( '' );
     553
     554                // Setup Export Data to contain multiple groups
     555                $export_data_grouped = array(
     556                        'user'     => array(
     557                                'group_label'       => 'User',
     558                                'group_description' => 'User&#8217;s profile data.',
     559                                'items'             => array(
     560                                        'user-1' => array(
     561                                                array(
     562                                                        'name'  => 'User ID',
     563                                                        'value' => 1,
     564                                                ),
     565                                                array(
     566                                                        'name'  => 'User Login Name',
     567                                                        'value' => 'user_login',
     568                                                ),
     569                                                array(
     570                                                        'name'  => 'User Nice Name',
     571                                                        'value' => 'User Name',
     572                                                ),
     573                                                array(
     574                                                        'name'  => 'User Email',
     575                                                        'value' => 'export-requester@example.com',
     576                                                ),
     577                                                array(
     578                                                        'name'  => 'User Registration Date',
     579                                                        'value' => '2020-01-31 19:29:29',
     580                                                ),
     581                                                array(
     582                                                        'name'  => 'User Display Name',
     583                                                        'value' => 'User Name',
     584                                                ),
     585                                                array(
     586                                                        'name'  => 'User Nickname',
     587                                                        'value' => 'User',
     588                                                ),
     589                                        ),
     590                                ),
     591                        ),
     592                        'comments' => array(
     593                                'group_label'       => 'Comments',
     594                                'group_description' => 'User&#8217;s comment data.',
     595                                'items'             => array(
     596                                        'comment-2' => array(
     597                                                array(
     598                                                        'name'  => 'Comment Author',
     599                                                        'value' => 'User Name',
     600                                                ),
     601                                                array(
     602                                                        'name'  => 'Comment Author Email',
     603                                                        'value' => 'export-requester@example.com',
     604                                                ),
     605                                                array(
     606                                                        'name'  => 'Comment Author IP',
     607                                                        'value' => '::1',
     608                                                ),
     609                                                array(
     610                                                        'name'  => 'Comment Author User Agent',
     611                                                        'value' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
     612                                                ),
     613                                                array(
     614                                                        'name'  => 'Comment Date',
     615                                                        'value' => '2020-01-31 19:55:19',
     616                                                ),
     617                                                array(
     618                                                        'name'  => 'Comment Content',
     619                                                        'value' => 'Test',
     620                                                ),
     621                                                array(
     622                                                        'name'  => 'Comment URL',
     623                                                        'value' => '<a href="http://localhost:8888/46894/2020/01/31/hello-world/#comment-2" target="_blank" rel="noreferrer noopener">http://localhost:8888/46894/2020/01/31/hello-world/#comment-2</a>',
     624                                                ),
     625                                        ),
     626                                ),
     627                        ),
     628                );
     629                update_post_meta( self::$export_request_id, '_export_data_grouped', $export_data_grouped );
     630
     631                // Generate Export File
     632                wp_privacy_generate_personal_data_export_file( self::$export_request_id );
     633                $this->assertTrue( file_exists( $this->export_file_name ) );
     634
     635                // Cleam-up for subsequent tests
     636                update_post_meta( self::$export_request_id, '_export_data_grouped', array() );
     637
     638                $report_dir = trailingslashit( self::$exports_dir . 'test_contents' );
     639                mkdir( $report_dir );
     640
     641                $zip        = new ZipArchive();
     642                $opened_zip = $zip->open( $this->export_file_name );
     643                $this->assertTrue( $opened_zip );
     644
     645                $zip->extractTo( $report_dir );
     646                $zip->close();
     647                $this->assertTrue( file_exists( $report_dir . 'index.html' ) );
     648
     649                $report_contents = file_get_contents( $report_dir . 'index.html' );
     650                $request         = wp_get_user_request( self::$export_request_id );
     651
     652                $this->assertContains( '<div id="table_of_contents">', $report_contents );
     653                $this->assertNotContains( '<span class="count">', $report_contents );
     654                $this->assertContains( $request->email, $report_contents );
     655
     656        }
    297657}
  • trunk/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportGroupHtml.php

    r46586 r47278  
    4040                );
    4141
    42                 $actual                = wp_privacy_generate_personal_data_export_group_html( $data );
     42                $actual                = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
    4343                $expected_table_markup = '<table><tbody><tr><th>Field 1 Name</th><td>Field 1 Value</td></tr><tr><th>Field 2 Name</th><td>Field 2 Value</td></tr></tbody></table>';
    4444
    45                 $this->assertContains( '<h2>Test Data Group</h2>', $actual );
     45                $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group</h2>', $actual );
    4646                $this->assertContains( $expected_table_markup, $actual );
    4747        }
     
    8080                );
    8181
    82                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
    83 
    84                 $this->assertContains( '<h2>Test Data Group', $actual );
     82                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
     83
     84                $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group', $actual );
    8585                $this->assertContains( '<td>Field 1 Value', $actual );
    8686                $this->assertContains( '<td>Another Field 1 Value', $actual );
     
    118118                );
    119119
    120                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
     120                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
    121121
    122122                $this->assertContains( '<a href="http://wordpress.org">http://wordpress.org</a>', $actual );
     
    132132        public function test_group_labels_escaped() {
    133133                $data = array(
    134                         'group_label' => '<div>Escape HTML in group lavels</div>',
     134                        'group_label' => '<div>Escape HTML in group labels</div>',
    135135                        'items'       => array(),
    136136                );
    137137
    138                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
    139 
    140                 $this->assertContains( '<h2>&lt;div&gt;Escape HTML in group lavels&lt;/div&gt;</h2>', $actual );
     138                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'escape-html-in-group-labels', 2 );
     139
     140                $this->assertContains( '<h2 id="escape-html-in-group-labels-escape-html-in-group-labels">&lt;div&gt;Escape HTML in group labels&lt;/div&gt;</h2>', $actual );
    141141        }
    142142
     
    163163                );
    164164
    165                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
    166 
     165                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
    167166                $this->assertContains( $data['items'][0]['links']['value'], $actual );
    168167                $this->assertContains( $data['items'][0]['formatting']['value'], $actual );
     
    191190                );
    192191
    193                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
     192                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
    194193
    195194                $this->assertNotContains( $data['items'][0]['scripts']['value'], $actual );
     
    224223                );
    225224
    226                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
    227 
    228                 $this->assertContains( '<h2>Test Data Group', $actual );
     225                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
     226
     227                $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group', $actual );
    229228                $this->assertContains( '<span class="count">(2)</span></h2>', $actual );
    230229                $this->assertSame( 2, substr_count( $actual, '<table>' ) );
     
    249248                );
    250249
    251                 $actual = wp_privacy_generate_personal_data_export_group_html( $data );
    252 
    253                 $this->assertContains( '<h2>Test Data Group</h2>', $actual );
     250                $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group', 2 );
     251
     252                $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group</h2>', $actual );
    254253                $this->assertNotContains( '<span class="count">', $actual );
    255254                $this->assertSame( 1, substr_count( $actual, '<table>' ) );
Note: See TracChangeset for help on using the changeset viewer.