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


Generated : Sun Oct 11 08:20:32 2026 Cross-referenced by PHPXref