[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-admin/includes/ -> revision.php (source)

   1  <?php
   2  /**
   3   * WordPress Administration Revisions API
   4   *
   5   * @package WordPress
   6   * @subpackage Administration
   7   * @since 3.6.0
   8   */
   9  
  10  /**
  11   * Get the revision UI diff.
  12   *
  13   * @since 3.6.0
  14   *
  15   * @param object|int $post         The post object. Also accepts a post ID.
  16   * @param int        $compare_from The revision ID to compare from.
  17   * @param int        $compare_to   The revision ID to come to.
  18   *
  19   * @return array|bool Associative array of a post's revisioned fields and their diffs.
  20   *                    Or, false on failure.
  21   */
  22  function wp_get_revision_ui_diff( $post, $compare_from, $compare_to ) {
  23      $post = get_post( $post );
  24      if ( ! $post ) {
  25          return false;
  26      }
  27  
  28      if ( $compare_from ) {
  29          $compare_from = get_post( $compare_from );
  30          if ( ! $compare_from ) {
  31              return false;
  32          }
  33      } else {
  34          // If we're dealing with the first revision...
  35          $compare_from = false;
  36      }
  37  
  38      $compare_to = get_post( $compare_to );
  39      if ( ! $compare_to ) {
  40          return false;
  41      }
  42  
  43      // If comparing revisions, make sure we're dealing with the right post parent.
  44      // The parent post may be a 'revision' when revisions are disabled and we're looking at autosaves.
  45      if ( $compare_from && $compare_from->post_parent !== $post->ID && $compare_from->ID !== $post->ID ) {
  46          return false;
  47      }
  48      if ( $compare_to->post_parent !== $post->ID && $compare_to->ID !== $post->ID ) {
  49          return false;
  50      }
  51  
  52      if ( $compare_from && strtotime( $compare_from->post_date_gmt ) > strtotime( $compare_to->post_date_gmt ) ) {
  53          $temp         = $compare_from;
  54          $compare_from = $compare_to;
  55          $compare_to   = $temp;
  56      }
  57  
  58      // Add default title if title field is empty
  59      if ( $compare_from && empty( $compare_from->post_title ) ) {
  60          $compare_from->post_title = __( '(no title)' );
  61      }
  62      if ( empty( $compare_to->post_title ) ) {
  63          $compare_to->post_title = __( '(no title)' );
  64      }
  65  
  66      $return = array();
  67  
  68      foreach ( _wp_post_revision_fields( $post ) as $field => $name ) {
  69          /**
  70           * Contextually filter a post revision field.
  71           *
  72           * The dynamic portion of the hook name, `$field`, corresponds to each of the post
  73           * fields of the revision object being iterated over in a foreach statement.
  74           *
  75           * @since 3.6.0
  76           *
  77           * @param string  $compare_from->$field The current revision field to compare to or from.
  78           * @param string  $field                The current revision field.
  79           * @param WP_Post $compare_from         The revision post object to compare to or from.
  80           * @param string  null                  The context of whether the current revision is the old
  81           *                                      or the new one. Values are 'to' or 'from'.
  82           */
  83          $content_from = $compare_from ? apply_filters( "_wp_post_revision_field_{$field}", $compare_from->$field, $field, $compare_from, 'from' ) : '';
  84  
  85          /** This filter is documented in wp-admin/includes/revision.php */
  86          $content_to = apply_filters( "_wp_post_revision_field_{$field}", $compare_to->$field, $field, $compare_to, 'to' );
  87  
  88          $args = array(
  89              'show_split_view' => true,
  90          );
  91  
  92          /**
  93           * Filters revisions text diff options.
  94           *
  95           * Filters the options passed to wp_text_diff() when viewing a post revision.
  96           *
  97           * @since 4.1.0
  98           *
  99           * @param array   $args {
 100           *     Associative array of options to pass to wp_text_diff().
 101           *
 102           *     @type bool $show_split_view True for split view (two columns), false for
 103           *                                 un-split view (single column). Default true.
 104           * }
 105           * @param string  $field        The current revision field.
 106           * @param WP_Post $compare_from The revision post to compare from.
 107           * @param WP_Post $compare_to   The revision post to compare to.
 108           */
 109          $args = apply_filters( 'revision_text_diff_options', $args, $field, $compare_from, $compare_to );
 110  
 111          $diff = wp_text_diff( $content_from, $content_to, $args );
 112  
 113          if ( ! $diff && 'post_title' === $field ) {
 114              // It's a better user experience to still show the Title, even if it didn't change.
 115              // No, you didn't see this.
 116              $diff = '<table class="diff"><colgroup><col class="content diffsplit left"><col class="content diffsplit middle"><col class="content diffsplit right"></colgroup><tbody><tr>';
 117  
 118              // In split screen mode, show the title before/after side by side.
 119              if ( true === $args['show_split_view'] ) {
 120                  $diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td><td></td><td>' . esc_html( $compare_to->post_title ) . '</td>';
 121              } else {
 122                  $diff .= '<td>' . esc_html( $compare_from->post_title ) . '</td>';
 123  
 124                  // In single column mode, only show the title once if unchanged.
 125                  if ( $compare_from->post_title !== $compare_to->post_title ) {
 126                      $diff .= '</tr><tr><td>' . esc_html( $compare_to->post_title ) . '</td>';
 127                  }
 128              }
 129  
 130              $diff .= '</tr></tbody>';
 131              $diff .= '</table>';
 132          }
 133  
 134          if ( $diff ) {
 135              $return[] = array(
 136                  'id'   => $field,
 137                  'name' => $name,
 138                  'diff' => $diff,
 139              );
 140          }
 141      }
 142  
 143      /**
 144       * Filters the fields displayed in the post revision diff UI.
 145       *
 146       * @since 4.1.0
 147       *
 148       * @param array[] $return       Array of revision UI fields. Each item is an array of id, name, and diff.
 149       * @param WP_Post $compare_from The revision post to compare from.
 150       * @param WP_Post $compare_to   The revision post to compare to.
 151       */
 152      return apply_filters( 'wp_get_revision_ui_diff', $return, $compare_from, $compare_to );
 153  
 154  }
 155  
 156  /**
 157   * Prepare revisions for JavaScript.
 158   *
 159   * @since 3.6.0
 160   *
 161   * @param object|int $post                 The post object. Also accepts a post ID.
 162   * @param int        $selected_revision_id The selected revision ID.
 163   * @param int        $from                 Optional. The revision ID to compare from.
 164   *
 165   * @return array An associative array of revision data and related settings.
 166   */
 167  function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) {
 168      $post    = get_post( $post );
 169      $authors = array();
 170      $now_gmt = time();
 171  
 172      $revisions = wp_get_post_revisions(
 173          $post->ID,
 174          array(
 175              'order'         => 'ASC',
 176              'check_enabled' => false,
 177          )
 178      );
 179      // If revisions are disabled, we only want autosaves and the current post.
 180      if ( ! wp_revisions_enabled( $post ) ) {
 181          foreach ( $revisions as $revision_id => $revision ) {
 182              if ( ! wp_is_post_autosave( $revision ) ) {
 183                  unset( $revisions[ $revision_id ] );
 184              }
 185          }
 186          $revisions = array( $post->ID => $post ) + $revisions;
 187      }
 188  
 189      $show_avatars = get_option( 'show_avatars' );
 190  
 191      cache_users( wp_list_pluck( $revisions, 'post_author' ) );
 192  
 193      $can_restore = current_user_can( 'edit_post', $post->ID );
 194      $current_id  = false;
 195  
 196      foreach ( $revisions as $revision ) {
 197          $modified     = strtotime( $revision->post_modified );
 198          $modified_gmt = strtotime( $revision->post_modified_gmt . ' +0000' );
 199          if ( $can_restore ) {
 200              $restore_link = str_replace(
 201                  '&amp;',
 202                  '&',
 203                  wp_nonce_url(
 204                      add_query_arg(
 205                          array(
 206                              'revision' => $revision->ID,
 207                              'action'   => 'restore',
 208                          ),
 209                          admin_url( 'revision.php' )
 210                      ),
 211                      "restore-post_{$revision->ID}"
 212                  )
 213              );
 214          }
 215  
 216          if ( ! isset( $authors[ $revision->post_author ] ) ) {
 217              $authors[ $revision->post_author ] = array(
 218                  'id'     => (int) $revision->post_author,
 219                  'avatar' => $show_avatars ? get_avatar( $revision->post_author, 32 ) : '',
 220                  'name'   => get_the_author_meta( 'display_name', $revision->post_author ),
 221              );
 222          }
 223  
 224          $autosave = (bool) wp_is_post_autosave( $revision );
 225          $current  = ! $autosave && $revision->post_modified_gmt === $post->post_modified_gmt;
 226          if ( $current && ! empty( $current_id ) ) {
 227              // If multiple revisions have the same post_modified_gmt, highest ID is current.
 228              if ( $current_id < $revision->ID ) {
 229                  $revisions[ $current_id ]['current'] = false;
 230                  $current_id                          = $revision->ID;
 231              } else {
 232                  $current = false;
 233              }
 234          } elseif ( $current ) {
 235              $current_id = $revision->ID;
 236          }
 237  
 238          $revisions_data = array(
 239              'id'         => $revision->ID,
 240              'title'      => get_the_title( $post->ID ),
 241              'author'     => $authors[ $revision->post_author ],
 242              'date'       => date_i18n( __( 'M j, Y @ H:i' ), $modified ),
 243              'dateShort'  => date_i18n( _x( 'j M @ H:i', 'revision date short format' ), $modified ),
 244              /* translators: %s: Human-readable time difference. */
 245              'timeAgo'    => sprintf( __( '%s ago' ), human_time_diff( $modified_gmt, $now_gmt ) ),
 246              'autosave'   => $autosave,
 247              'current'    => $current,
 248              'restoreUrl' => $can_restore ? $restore_link : false,
 249          );
 250  
 251          /**
 252           * Filters the array of revisions used on the revisions screen.
 253           *
 254           * @since 4.4.0
 255           *
 256           * @param array   $revisions_data {
 257           *     The bootstrapped data for the revisions screen.
 258           *
 259           *     @type int        $id         Revision ID.
 260           *     @type string     $title      Title for the revision's parent WP_Post object.
 261           *     @type int        $author     Revision post author ID.
 262           *     @type string     $date       Date the revision was modified.
 263           *     @type string     $dateShort  Short-form version of the date the revision was modified.
 264           *     @type string     $timeAgo    GMT-aware amount of time ago the revision was modified.
 265           *     @type bool       $autosave   Whether the revision is an autosave.
 266           *     @type bool       $current    Whether the revision is both not an autosave and the post
 267           *                                  modified date matches the revision modified date (GMT-aware).
 268           *     @type bool|false $restoreUrl URL if the revision can be restored, false otherwise.
 269           * }
 270           * @param WP_Post $revision       The revision's WP_Post object.
 271           * @param WP_Post $post           The revision's parent WP_Post object.
 272           */
 273          $revisions[ $revision->ID ] = apply_filters( 'wp_prepare_revision_for_js', $revisions_data, $revision, $post );
 274      }
 275  
 276      /**
 277       * If we only have one revision, the initial revision is missing; This happens
 278       * when we have an autsosave and the user has clicked 'View the Autosave'
 279       */
 280      if ( 1 === sizeof( $revisions ) ) {
 281          $revisions[ $post->ID ] = array(
 282              'id'         => $post->ID,
 283              'title'      => get_the_title( $post->ID ),
 284              'author'     => $authors[ $post->post_author ],
 285              'date'       => date_i18n( __( 'M j, Y @ H:i' ), strtotime( $post->post_modified ) ),
 286              'dateShort'  => date_i18n( _x( 'j M @ H:i', 'revision date short format' ), strtotime( $post->post_modified ) ),
 287              /* translators: %s: Human-readable time difference. */
 288              'timeAgo'    => sprintf( __( '%s ago' ), human_time_diff( strtotime( $post->post_modified_gmt ), $now_gmt ) ),
 289              'autosave'   => false,
 290              'current'    => true,
 291              'restoreUrl' => false,
 292          );
 293          $current_id             = $post->ID;
 294      }
 295  
 296      /*
 297       * If a post has been saved since the last revision (no revisioned fields
 298       * were changed), we may not have a "current" revision. Mark the latest
 299       * revision as "current".
 300       */
 301      if ( empty( $current_id ) ) {
 302          if ( $revisions[ $revision->ID ]['autosave'] ) {
 303              $revision = end( $revisions );
 304              while ( $revision['autosave'] ) {
 305                  $revision = prev( $revisions );
 306              }
 307              $current_id = $revision['id'];
 308          } else {
 309              $current_id = $revision->ID;
 310          }
 311          $revisions[ $current_id ]['current'] = true;
 312      }
 313  
 314      // Now, grab the initial diff.
 315      $compare_two_mode = is_numeric( $from );
 316      if ( ! $compare_two_mode ) {
 317          $found = array_search( $selected_revision_id, array_keys( $revisions ) );
 318          if ( $found ) {
 319              $from = array_keys( array_slice( $revisions, $found - 1, 1, true ) );
 320              $from = reset( $from );
 321          } else {
 322              $from = 0;
 323          }
 324      }
 325  
 326      $from = absint( $from );
 327  
 328      $diffs = array(
 329          array(
 330              'id'     => $from . ':' . $selected_revision_id,
 331              'fields' => wp_get_revision_ui_diff( $post->ID, $from, $selected_revision_id ),
 332          ),
 333      );
 334  
 335      return array(
 336          'postId'         => $post->ID,
 337          'nonce'          => wp_create_nonce( 'revisions-ajax-nonce' ),
 338          'revisionData'   => array_values( $revisions ),
 339          'to'             => $selected_revision_id,
 340          'from'           => $from,
 341          'diffData'       => $diffs,
 342          'baseUrl'        => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ),
 343          'compareTwoMode' => absint( $compare_two_mode ), // Apparently booleans are not allowed
 344          'revisionIds'    => array_keys( $revisions ),
 345      );
 346  }
 347  
 348  /**
 349   * Print JavaScript templates required for the revisions experience.
 350   *
 351   * @since 4.1.0
 352   *
 353   * @global WP_Post $post Global post object.
 354   */
 355  function wp_print_revision_templates() {
 356      global $post;
 357      ?><script id="tmpl-revisions-frame" type="text/html">
 358          <div class="revisions-control-frame"></div>
 359          <div class="revisions-diff-frame"></div>
 360      </script>
 361  
 362      <script id="tmpl-revisions-buttons" type="text/html">
 363          <div class="revisions-previous">
 364              <input class="button" type="button" value="<?php echo esc_attr_x( 'Previous', 'Button label for a previous revision' ); ?>" />
 365          </div>
 366  
 367          <div class="revisions-next">
 368              <input class="button" type="button" value="<?php echo esc_attr_x( 'Next', 'Button label for a next revision' ); ?>" />
 369          </div>
 370      </script>
 371  
 372      <script id="tmpl-revisions-checkbox" type="text/html">
 373          <div class="revision-toggle-compare-mode">
 374              <label>
 375                  <input type="checkbox" class="compare-two-revisions"
 376                  <#
 377                  if ( 'undefined' !== typeof data && data.model.attributes.compareTwoMode ) {
 378                      #> checked="checked"<#
 379                  }
 380                  #>
 381                  />
 382                  <?php esc_html_e( 'Compare any two revisions' ); ?>
 383              </label>
 384          </div>
 385      </script>
 386  
 387      <script id="tmpl-revisions-meta" type="text/html">
 388          <# if ( ! _.isUndefined( data.attributes ) ) { #>
 389              <div class="diff-title">
 390                  <# if ( 'from' === data.type ) { #>
 391                      <strong><?php _ex( 'From:', 'Followed by post revision info' ); ?></strong>
 392                  <# } else if ( 'to' === data.type ) { #>
 393                      <strong><?php _ex( 'To:', 'Followed by post revision info' ); ?></strong>
 394                  <# } #>
 395                  <div class="author-card<# if ( data.attributes.autosave ) { #> autosave<# } #>">
 396                      {{{ data.attributes.author.avatar }}}
 397                      <div class="author-info">
 398                      <# if ( data.attributes.autosave ) { #>
 399                          <span class="byline">
 400                          <?php
 401                          printf(
 402                              /* translators: %s: User's display name. */
 403                              __( 'Autosave by %s' ),
 404                              '<span class="author-name">{{ data.attributes.author.name }}</span>'
 405                          );
 406                          ?>
 407                              </span>
 408                      <# } else if ( data.attributes.current ) { #>
 409                          <span class="byline">
 410                          <?php
 411                          printf(
 412                              /* translators: %s: User's display name. */
 413                              __( 'Current Revision by %s' ),
 414                              '<span class="author-name">{{ data.attributes.author.name }}</span>'
 415                          );
 416                          ?>
 417                              </span>
 418                      <# } else { #>
 419                          <span class="byline">
 420                          <?php
 421                          printf(
 422                              /* translators: %s: User's display name. */
 423                              __( 'Revision by %s' ),
 424                              '<span class="author-name">{{ data.attributes.author.name }}</span>'
 425                          );
 426                          ?>
 427                              </span>
 428                      <# } #>
 429                          <span class="time-ago">{{ data.attributes.timeAgo }}</span>
 430                          <span class="date">({{ data.attributes.dateShort }})</span>
 431                      </div>
 432                  <# if ( 'to' === data.type && data.attributes.restoreUrl ) { #>
 433                      <input  <?php if ( wp_check_post_lock( $post->ID ) ) { ?>
 434                          disabled="disabled"
 435                      <?php } else { ?>
 436                          <# if ( data.attributes.current ) { #>
 437                              disabled="disabled"
 438                          <# } #>
 439                      <?php } ?>
 440                      <# if ( data.attributes.autosave ) { #>
 441                          type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Autosave' ); ?>" />
 442                      <# } else { #>
 443                          type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Revision' ); ?>" />
 444                      <# } #>
 445                  <# } #>
 446              </div>
 447          <# if ( 'tooltip' === data.type ) { #>
 448              <div class="revisions-tooltip-arrow"><span></span></div>
 449          <# } #>
 450      <# } #>
 451      </script>
 452  
 453      <script id="tmpl-revisions-diff" type="text/html">
 454          <div class="loading-indicator"><span class="spinner"></span></div>
 455          <div class="diff-error"><?php _e( 'Sorry, something went wrong. The requested comparison could not be loaded.' ); ?></div>
 456          <div class="diff">
 457          <# _.each( data.fields, function( field ) { #>
 458              <h3>{{ field.name }}</h3>
 459              {{{ field.diff }}}
 460          <# }); #>
 461          </div>
 462      </script>
 463      <?php
 464  }


Generated: Tue Oct 22 08:20:01 2019 Cross-referenced by PHPXref 0.7