[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/js/ -> privacy-tools.js (source)

   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&#8217;s personal data export link was sent.' );
 111  
 112              if ( 'undefined' !== typeof zipUrl ) {
 113                  summaryMessage = __( 'This user&#8217;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  });


Generated : Sat Sep 12 08:20:32 2026 Cross-referenced by PHPXref