home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / includes / revision.php < prev    next >
Encoding:
PHP Script  |  2017-09-21  |  14.6 KB  |  404 lines

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