diff --git a/src/wp-admin/includes/privacy-tools.php b/src/wp-admin/includes/privacy-tools.php
index b547468184..81ce65baf4 100644
a
|
b
|
function _wp_personal_data_cleanup_requests() { |
215 | 215 | * Generate a single group for the personal data export report. |
216 | 216 | * |
217 | 217 | * @since 4.9.6 |
| 218 | * @since 5.4.0 Added $group_id to support the TOC implementation. |
218 | 219 | * |
219 | 220 | * @param array $group_data { |
220 | 221 | * The group data to render. |
… |
… |
function _wp_personal_data_cleanup_requests() { |
233 | 234 | * } |
234 | 235 | * @return string The HTML for this group and its items. |
235 | 236 | */ |
236 | | function wp_privacy_generate_personal_data_export_group_html( $group_data ) { |
237 | | $group_html = '<h2>'; |
| 237 | function wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id = '' ) { |
| 238 | $group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id ); |
| 239 | |
| 240 | $group_html = '<h2 id="' . esc_attr( $group_id_attr ) . '">'; |
238 | 241 | $group_html .= esc_html( $group_data['group_label'] ); |
239 | 242 | |
240 | 243 | $items_count = count( (array) $group_data['items'] ); |
… |
… |
function wp_privacy_generate_personal_data_export_group_html( $group_data ) { |
271 | 274 | $group_html .= '</table>'; |
272 | 275 | } |
273 | 276 | |
| 277 | $group_html .= '<div class="return_to_top">'; |
| 278 | $group_html .= '<a href="#top">' . esc_html__( '↑ Return to top' ) . '</a>'; |
| 279 | $group_html .= '</div>'; |
| 280 | |
274 | 281 | $group_html .= '</div>'; |
275 | 282 | |
276 | 283 | return $group_html; |
… |
… |
function wp_privacy_generate_personal_data_export_file( $request_id ) { |
409 | 416 | fwrite( $file, 'th { padding: 5px; text-align: left; width: 20%; }' ); |
410 | 417 | fwrite( $file, 'td { padding: 5px; }' ); |
411 | 418 | fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' ); |
| 419 | fwrite( $file, '.return_to_top { text-align:right; }' ); |
412 | 420 | fwrite( $file, '</style>' ); |
413 | 421 | fwrite( $file, '<title>' ); |
414 | 422 | fwrite( $file, esc_html( $title ) ); |
415 | 423 | fwrite( $file, '</title>' ); |
416 | 424 | fwrite( $file, "</head>\n" ); |
417 | 425 | fwrite( $file, "<body>\n" ); |
418 | | fwrite( $file, '<h1>' . esc_html__( 'Personal Data Export' ) . '</h1>' ); |
| 426 | fwrite( $file, '<h1 id="top">' . esc_html__( 'Personal Data Export' ) . '</h1>' ); |
| 427 | |
| 428 | // Create TOC. |
| 429 | if ( count( $groups ) > 1 ) { |
| 430 | fwrite( $file, '<div id="table_of_contents">' ); |
| 431 | fwrite( $file, '<h2>' . esc_html__( 'Table of Contents' ) . '</h2>' ); |
| 432 | fwrite( $file, '<ul>' ); |
| 433 | foreach ( (array) $groups as $group_id => $group_data ) { |
| 434 | $group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id ); |
| 435 | fwrite( $file, '<li>' ); |
| 436 | fwrite( $file, '<a href="#' . esc_attr( $group_id_attr ) . '">' . esc_html( $group_data['group_label'] ) . '</a>' ); |
| 437 | fwrite( $file, '</li>' ); |
| 438 | } |
| 439 | fwrite( $file, '</ul>' ); |
| 440 | fwrite( $file, '</div>' ); |
| 441 | } |
419 | 442 | |
420 | 443 | // Now, iterate over every group in $groups and have the formatter render it in HTML. |
421 | 444 | foreach ( (array) $groups as $group_id => $group_data ) { |
422 | | fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data ) ); |
| 445 | fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id ) ); |
423 | 446 | } |
424 | 447 | |
425 | 448 | fwrite( $file, "</body>\n" ); |
diff --git a/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php b/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportFile.php
index c38007f498..ec1cbceb36 100644
a
|
b
|
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportFile extends WP_UnitTestC |
260 | 260 | $report_contents = file_get_contents( $report_dir . 'index.html' ); |
261 | 261 | $request = wp_get_user_request_data( self::$export_request_id ); |
262 | 262 | |
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 ); |
265 | 265 | $this->assertContains( $request->email, $report_contents ); |
266 | 266 | } |
267 | 267 | |
diff --git a/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportGroupHtml.php b/tests/phpunit/tests/privacy/wpPrivacyGeneratePersonalDataExportGroupHtml.php
index 688f790e22..c98f94f87a 100644
a
|
b
|
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
39 | 39 | ), |
40 | 40 | ); |
41 | 41 | |
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' ); |
43 | 43 | $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>'; |
44 | 44 | |
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 ); |
46 | 46 | $this->assertContains( $expected_table_markup, $actual ); |
47 | 47 | } |
48 | 48 | |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
79 | 79 | ), |
80 | 80 | ); |
81 | 81 | |
82 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 82 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group' ); |
83 | 83 | |
84 | | $this->assertContains( '<h2>Test Data Group', $actual ); |
| 84 | $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group', $actual ); |
85 | 85 | $this->assertContains( '<td>Field 1 Value', $actual ); |
86 | 86 | $this->assertContains( '<td>Another Field 1 Value', $actual ); |
87 | 87 | $this->assertContains( '<td>Field 2 Value', $actual ); |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
117 | 117 | ), |
118 | 118 | ); |
119 | 119 | |
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' ); |
121 | 121 | |
122 | 122 | $this->assertContains( '<a href="http://wordpress.org">http://wordpress.org</a>', $actual ); |
123 | 123 | $this->assertContains( '<a href="https://wordpress.org">https://wordpress.org</a>', $actual ); |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
131 | 131 | */ |
132 | 132 | public function test_group_labels_escaped() { |
133 | 133 | $data = array( |
134 | | 'group_label' => '<div>Escape HTML in group lavels</div>', |
| 134 | 'group_label' => '<div>Escape HTML in group labels</div>', |
135 | 135 | 'items' => array(), |
136 | 136 | ); |
137 | 137 | |
138 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 138 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'escape-html-in-group-labels' ); |
139 | 139 | |
140 | | $this->assertContains( '<h2><div>Escape HTML in group lavels</div></h2>', $actual ); |
| 140 | $this->assertContains( '<h2 id="escape-html-in-group-labels-escape-html-in-group-labels"><div>Escape HTML in group labels</div></h2>', $actual ); |
141 | 141 | } |
142 | 142 | |
143 | 143 | /** |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
162 | 162 | ), |
163 | 163 | ); |
164 | 164 | |
165 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 165 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group' ); |
166 | 166 | |
167 | 167 | $this->assertContains( $data['items'][0]['links']['value'], $actual ); |
168 | 168 | $this->assertContains( $data['items'][0]['formatting']['value'], $actual ); |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
190 | 190 | ), |
191 | 191 | ); |
192 | 192 | |
193 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 193 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group' ); |
194 | 194 | |
195 | 195 | $this->assertNotContains( $data['items'][0]['scripts']['value'], $actual ); |
196 | 196 | $this->assertContains( '<td>Testing that script tags are stripped.</td>', $actual ); |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
223 | 223 | ), |
224 | 224 | ); |
225 | 225 | |
226 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 226 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group' ); |
227 | 227 | |
228 | | $this->assertContains( '<h2>Test Data Group', $actual ); |
| 228 | $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group', $actual ); |
229 | 229 | $this->assertContains( '<span class="count">(2)</span></h2>', $actual ); |
230 | 230 | $this->assertSame( 2, substr_count( $actual, '<table>' ) ); |
231 | 231 | } |
… |
… |
class Tests_Privacy_WpPrivacyGeneratePersonalDataExportGroupHtml extends WP_Unit |
248 | 248 | ), |
249 | 249 | ); |
250 | 250 | |
251 | | $actual = wp_privacy_generate_personal_data_export_group_html( $data ); |
| 251 | $actual = wp_privacy_generate_personal_data_export_group_html( $data, 'test-data-group' ); |
252 | 252 | |
253 | | $this->assertContains( '<h2>Test Data Group</h2>', $actual ); |
| 253 | $this->assertContains( '<h2 id="test-data-group-test-data-group">Test Data Group</h2>', $actual ); |
254 | 254 | $this->assertNotContains( '<span class="count">', $actual ); |
255 | 255 | $this->assertSame( 1, substr_count( $actual, '<table>' ) ); |
256 | 256 | } |