| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /** 2 * @output wp-admin/js/privacy-tools.js 3 */ 4 5 /** 6 * Sets the interactions used by the User Privacy tools in WordPress. 7 * 8 * @param {jQueryStatic} $ The jQuery object. 9 */ 10 jQuery( function( $ ) { 11 var __ = wp.i18n.__, 12 copiedNoticeTimeout; 13 14 /** 15 * Sets the state of the action. 16 * 17 * @param {jQuery} $action The action to set the state for. 18 * @param {string} state The state to set the action to. 19 * @return {void} 20 */ 21 function setActionState( $action, state ) { 22 $action.children().addClass( 'hidden' ); 23 $action.children( '.' + state ).removeClass( 'hidden' ); 24 } 25 26 /** 27 * Clears any results row after the request row. 28 * 29 * @param {jQuery} $requestRow The request row to clear results for. 30 * @return {void} 31 */ 32 function clearResultsAfterRow( $requestRow ) { 33 $requestRow.removeClass( 'has-request-results' ); 34 35 if ( $requestRow.next().hasClass( 'request-results' ) ) { 36 $requestRow.next().remove(); 37 } 38 } 39 40 /** 41 * Appends a results row after the request row. 42 * 43 * @param {jQuery} $requestRow The request row to append the results after. 44 * @param {string} classes The classes to add to the results row. 45 * @param {string} summaryMessage The summary message to display in the results row. 46 * @param {string[]} additionalMessages Additional messages to display in the results row. 47 * @return {void} 48 */ 49 function appendResultsAfterRow( $requestRow, classes, summaryMessage, additionalMessages ) { 50 var itemList = '', 51 resultRowClasses = 'request-results'; 52 53 clearResultsAfterRow( $requestRow ); 54 55 if ( additionalMessages.length ) { 56 $.each( additionalMessages, function( index, value ) { 57 itemList = itemList + '<li>' + value + '</li>'; 58 }); 59 itemList = '<ul>' + itemList + '</ul>'; 60 } 61 62 $requestRow.addClass( 'has-request-results' ); 63 64 if ( $requestRow.hasClass( 'status-request-confirmed' ) ) { 65 resultRowClasses = resultRowClasses + ' status-request-confirmed'; 66 } 67 68 if ( $requestRow.hasClass( 'status-request-failed' ) ) { 69 resultRowClasses = resultRowClasses + ' status-request-failed'; 70 } 71 72 $requestRow.after( function() { 73 return '<tr class="' + resultRowClasses + '"><th colspan="5">' + 74 '<div class="notice inline notice-alt ' + classes + '" role="alert">' + 75 '<p>' + summaryMessage + '</p>' + 76 itemList + 77 '</div>' + 78 '</th>' + 79 '</tr>'; 80 }); 81 } 82 83 $( '.export-personal-data-handle' ).on( 'click', function( event ) { 84 var $this = $( this ), 85 $action = $this.parents( '.export-personal-data' ), 86 $requestRow = $this.parents( 'tr' ), 87 $progress = $requestRow.find( '.export-progress' ), 88 $rowActions = $this.parents( '.row-actions' ), 89 requestID = $action.data( 'request-id' ), 90 nonce = $action.data( 'nonce' ), 91 exportersCount = $action.data( 'exporters-count' ), 92 sendAsEmail = $action.data( 'send-as-email' ) ? true : false; 93 94 event.preventDefault(); 95 event.stopPropagation(); 96 97 $rowActions.addClass( 'processing' ); 98 99 $action.trigger( 'blur' ); 100 clearResultsAfterRow( $requestRow ); 101 setExportProgress( 0 ); 102 103 /** 104 * Handles a successful export. 105 * 106 * @param {string} zipUrl The URL of the generated ZIP file, if available. 107 * @return {void} 108 */ 109 function onExportDoneSuccess( zipUrl ) { 110 var summaryMessage = __( 'This user’s personal data export link was sent.' ); 111 112 if ( 'undefined' !== typeof zipUrl ) { 113 summaryMessage = __( 'This user’s personal data export file was downloaded.' ); 114 } 115 116 setActionState( $action, 'export-personal-data-success' ); 117 118 appendResultsAfterRow( $requestRow, 'notice-success', summaryMessage, [] ); 119 120 if ( 'undefined' !== typeof zipUrl ) { 121 window.location = zipUrl; 122 } else if ( ! sendAsEmail ) { 123 onExportFailure( __( 'No personal data export file was generated.' ) ); 124 } 125 126 setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); 127 } 128 129 /** 130 * Handles an export failure. 131 * 132 * @param {string} errorMessage The error message to display. 133 * @return {void} 134 */ 135 function onExportFailure( errorMessage ) { 136 var summaryMessage = __( 'An error occurred while attempting to export personal data.' ); 137 138 setActionState( $action, 'export-personal-data-failed' ); 139 140 if ( errorMessage ) { 141 appendResultsAfterRow( $requestRow, 'notice-error', summaryMessage, [ errorMessage ] ); 142 } 143 144 setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); 145 } 146 147 /** 148 * Updates the progress of the export process. 149 * 150 * @param {number} exporterIndex The index of the exporter to process. 151 * @return {void} 152 */ 153 function setExportProgress( exporterIndex ) { 154 var progress = ( exportersCount > 0 ? exporterIndex / exportersCount : 0 ), 155 progressString = Math.round( progress * 100 ).toString() + '%'; 156 157 $progress.html( progressString ); 158 } 159 160 /** 161 * Performs the next export request. 162 * 163 * @param {number} exporterIndex The index of the exporter to process. 164 * @param {number} pageIndex The index of the page to process for the current exporter. 165 * @return {void} 166 */ 167 function doNextExport( exporterIndex, pageIndex ) { 168 $.ajax( 169 { 170 url: window.ajaxurl, 171 data: { 172 action: 'wp-privacy-export-personal-data', 173 exporter: exporterIndex, 174 id: requestID, 175 page: pageIndex, 176 security: nonce, 177 sendAsEmail: sendAsEmail 178 }, 179 method: 'post' 180 } 181 ).done( function( response ) { 182 var responseData = response.data; 183 184 if ( ! response.success ) { 185 // e.g. invalid request ID. 186 setTimeout( function() { onExportFailure( response.data ); }, 500 ); 187 return; 188 } 189 190 if ( ! responseData.done ) { 191 setTimeout( doNextExport( exporterIndex, pageIndex + 1 ) ); 192 } else { 193 setExportProgress( exporterIndex ); 194 if ( exporterIndex < exportersCount ) { 195 setTimeout( doNextExport( exporterIndex + 1, 1 ) ); 196 } else { 197 setTimeout( function() { onExportDoneSuccess( responseData.url ); }, 500 ); 198 } 199 } 200 }).fail( function( jqxhr, textStatus, error ) { 201 // e.g. Nonce failure. 202 setTimeout( function() { onExportFailure( error ); }, 500 ); 203 }); 204 } 205 206 // And now, let's begin. 207 setActionState( $action, 'export-personal-data-processing' ); 208 doNextExport( 1, 1 ); 209 }); 210 211 $( '.remove-personal-data-handle' ).on( 'click', function( event ) { 212 var $this = $( this ), 213 $action = $this.parents( '.remove-personal-data' ), 214 $requestRow = $this.parents( 'tr' ), 215 $progress = $requestRow.find( '.erasure-progress' ), 216 $rowActions = $this.parents( '.row-actions' ), 217 requestID = $action.data( 'request-id' ), 218 nonce = $action.data( 'nonce' ), 219 erasersCount = $action.data( 'erasers-count' ), 220 hasRemoved = false, 221 hasRetained = false, 222 messages = []; 223 224 event.preventDefault(); 225 event.stopPropagation(); 226 227 $rowActions.addClass( 'processing' ); 228 229 $action.trigger( 'blur' ); 230 clearResultsAfterRow( $requestRow ); 231 setErasureProgress( 0 ); 232 233 /** 234 * Handles a successful erasure. 235 * 236 * @return {void} 237 */ 238 function onErasureDoneSuccess() { 239 var summaryMessage = __( 'No personal data was found for this user.' ), 240 classes = 'notice-success'; 241 242 setActionState( $action, 'remove-personal-data-success' ); 243 244 if ( false === hasRemoved ) { 245 if ( false === hasRetained ) { 246 summaryMessage = __( 'No personal data was found for this user.' ); 247 } else { 248 summaryMessage = __( 'Personal data was found for this user but was not erased.' ); 249 classes = 'notice-warning'; 250 } 251 } else { 252 if ( false === hasRetained ) { 253 summaryMessage = __( 'All of the personal data found for this user was erased.' ); 254 } else { 255 summaryMessage = __( 'Personal data was found for this user but some of the personal data found was not erased.' ); 256 classes = 'notice-warning'; 257 } 258 } 259 appendResultsAfterRow( $requestRow, classes, summaryMessage, messages ); 260 261 setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); 262 } 263 264 /** 265 * Handles an erasure failure. 266 */ 267 function onErasureFailure() { 268 var summaryMessage = __( 'An error occurred while attempting to find and erase personal data.' ); 269 270 setActionState( $action, 'remove-personal-data-failed' ); 271 272 appendResultsAfterRow( $requestRow, 'notice-error', summaryMessage, [] ); 273 274 setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); 275 } 276 277 /** 278 * Updates the progress of the erasure process. 279 * 280 * @param {number} eraserIndex The index of the eraser to process. 281 * @return {void} 282 */ 283 function setErasureProgress( eraserIndex ) { 284 var progress = ( erasersCount > 0 ? eraserIndex / erasersCount : 0 ), 285 progressString = Math.round( progress * 100 ).toString() + '%'; 286 287 $progress.html( progressString ); 288 } 289 290 /** 291 * Performs the next erasure request. 292 * 293 * @param {number} eraserIndex The index of the eraser to process. 294 * @param {number} pageIndex The index of the page to process for the current eraser. 295 * @return {void} 296 */ 297 function doNextErasure( eraserIndex, pageIndex ) { 298 $.ajax({ 299 url: window.ajaxurl, 300 data: { 301 action: 'wp-privacy-erase-personal-data', 302 eraser: eraserIndex, 303 id: requestID, 304 page: pageIndex, 305 security: nonce 306 }, 307 method: 'post' 308 }).done( function( response ) { 309 var responseData = response.data; 310 311 if ( ! response.success ) { 312 setTimeout( function() { onErasureFailure(); }, 500 ); 313 return; 314 } 315 if ( responseData.items_removed ) { 316 hasRemoved = hasRemoved || responseData.items_removed; 317 } 318 if ( responseData.items_retained ) { 319 hasRetained = hasRetained || responseData.items_retained; 320 } 321 if ( responseData.messages ) { 322 messages = messages.concat( responseData.messages ); 323 } 324 if ( ! responseData.done ) { 325 setTimeout( doNextErasure( eraserIndex, pageIndex + 1 ) ); 326 } else { 327 setErasureProgress( eraserIndex ); 328 if ( eraserIndex < erasersCount ) { 329 setTimeout( doNextErasure( eraserIndex + 1, 1 ) ); 330 } else { 331 setTimeout( function() { onErasureDoneSuccess(); }, 500 ); 332 } 333 } 334 }).fail( function() { 335 setTimeout( function() { onErasureFailure(); }, 500 ); 336 }); 337 } 338 339 // And now, let's begin. 340 setActionState( $action, 'remove-personal-data-processing' ); 341 342 doNextErasure( 1, 1 ); 343 }); 344 345 // Privacy Policy page, copy action. 346 $( document ).on( 'click', function( event ) { 347 var $parent, 348 range, 349 $target = $( event.target ), 350 copiedNotice = $target.siblings( '.success' ); 351 352 clearTimeout( copiedNoticeTimeout ); 353 354 if ( $target.is( 'button.privacy-text-copy' ) ) { 355 $parent = $target.closest( '.privacy-settings-accordion-panel' ); 356 357 if ( $parent.length ) { 358 try { 359 var documentPosition = document.documentElement.scrollTop, 360 bodyPosition = document.body.scrollTop; 361 362 // Setup copy. 363 window.getSelection().removeAllRanges(); 364 365 // Hide tutorial content to remove from copied content. 366 range = document.createRange(); 367 $parent.addClass( 'hide-privacy-policy-tutorial' ); 368 369 // Copy action. Select only the dedicated copy-content wrapper 370 // so the actions toolbar (which contains the "Copied!" notice 371 // and button label) is never part of the selection - see #58969. 372 range.selectNodeContents( $parent.find( '.privacy-text-copy-content' )[0] ); 373 window.getSelection().addRange( range ); 374 document.execCommand( 'copy' ); 375 376 // Reset section. 377 $parent.removeClass( 'hide-privacy-policy-tutorial' ); 378 window.getSelection().removeAllRanges(); 379 380 // Return scroll position - see #49540. 381 if ( documentPosition > 0 && documentPosition !== document.documentElement.scrollTop ) { 382 document.documentElement.scrollTop = documentPosition; 383 } else if ( bodyPosition > 0 && bodyPosition !== document.body.scrollTop ) { 384 document.body.scrollTop = bodyPosition; 385 } 386 387 // Display and speak notice to indicate action complete. 388 copiedNotice.addClass( 'visible' ); 389 wp.a11y.speak( __( 'The suggested policy text has been copied to your clipboard.' ) ); 390 391 // Delay notice dismissal. 392 copiedNoticeTimeout = setTimeout( function() { 393 copiedNotice.removeClass( 'visible' ); 394 }, 3000 ); 395 } catch ( er ) {} 396 } 397 } 398 }); 399 400 // Label handling to focus the create page button on Privacy settings page. 401 $( 'body.options-privacy-php label[for=create-page]' ).on( 'click', function( e ) { 402 e.preventDefault(); 403 $( 'input#create-page' ).trigger( 'focus' ); 404 } ); 405 406 // Accordion handling in various new Privacy settings pages. 407 $( '.privacy-settings-accordion' ).on( 'click', '.privacy-settings-accordion-trigger', function() { 408 var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) ); 409 410 if ( isExpanded ) { 411 $( this ).attr( 'aria-expanded', 'false' ); 412 $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', true ); 413 } else { 414 $( this ).attr( 'aria-expanded', 'true' ); 415 $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false ); 416 } 417 } ); 418 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Sep 12 08:20:32 2026 | Cross-referenced by PHPXref |