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