home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / revision.php < prev    next >
Encoding:
PHP Script  |  2016-11-09  |  20.8 KB  |  728 lines

  1. <?php
  2. /**
  3.  * Post revision functions.
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Post_Revisions
  7.  */
  8.  
  9. /**
  10.  * Determines which fields of posts are to be saved in revisions.
  11.  *
  12.  * @since 2.6.0
  13.  * @since 4.5.0 A `WP_Post` object can now be passed to the `$post` parameter.
  14.  * @since 4.5.0 The optional `$autosave` parameter was deprecated and renamed to `$deprecated`.
  15.  * @access private
  16.  *
  17.  * @staticvar array $fields
  18.  *
  19.  * @param array|WP_Post $post       Optional. A post array or a WP_Post object being processed
  20.  *                                  for insertion as a post revision. Default empty array.
  21.  * @param bool          $deprecated Not used.
  22.  * @return array Array of fields that can be versioned.
  23.  */
  24. function _wp_post_revision_fields( $post = array(), $deprecated = false ) {
  25.     static $fields = null;
  26.  
  27.     if ( ! is_array( $post ) ) {
  28.         $post = get_post( $post, ARRAY_A );
  29.     }
  30.  
  31.     if ( is_null( $fields ) ) {
  32.         // Allow these to be versioned
  33.         $fields = array(
  34.             'post_title' => __( 'Title' ),
  35.             'post_content' => __( 'Content' ),
  36.             'post_excerpt' => __( 'Excerpt' ),
  37.         );
  38.     }
  39.  
  40.     /**
  41.      * Filters the list of fields saved in post revisions.
  42.      *
  43.      * Included by default: 'post_title', 'post_content' and 'post_excerpt'.
  44.      *
  45.      * Disallowed fields: 'ID', 'post_name', 'post_parent', 'post_date',
  46.      * 'post_date_gmt', 'post_status', 'post_type', 'comment_count',
  47.      * and 'post_author'.
  48.      *
  49.      * @since 2.6.0
  50.      * @since 4.5.0 The `$post` parameter was added.
  51.      *
  52.      * @param array $fields List of fields to revision. Contains 'post_title',
  53.      *                      'post_content', and 'post_excerpt' by default.
  54.      * @param array $post   A post array being processed for insertion as a post revision.
  55.      */
  56.     $fields = apply_filters( '_wp_post_revision_fields', $fields, $post );
  57.  
  58.     // WP uses these internally either in versioning or elsewhere - they cannot be versioned
  59.     foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect ) {
  60.         unset( $fields[ $protect ] );
  61.     }
  62.  
  63.  
  64.     return $fields;
  65. }
  66.  
  67. /**
  68.  * Returns a post array ready to be inserted into the posts table as a post revision.
  69.  *
  70.  * @since 4.5.0
  71.  * @access private
  72.  *
  73.  * @param array|WP_Post $post     Optional. A post array or a WP_Post object to be processed
  74.  *                                for insertion as a post revision. Default empty array.
  75.  * @param bool          $autosave Optional. Is the revision an autosave? Default false.
  76.  * @return array Post array ready to be inserted as a post revision.
  77.  */
  78. function _wp_post_revision_data( $post = array(), $autosave = false ) {
  79.     if ( ! is_array( $post ) ) {
  80.         $post = get_post( $post, ARRAY_A );
  81.     }
  82.  
  83.     $fields = _wp_post_revision_fields( $post );
  84.  
  85.     $revision_data = array();
  86.  
  87.     foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field ) {
  88.         $revision_data[ $field ] = $post[ $field ];
  89.     }
  90.  
  91.     $revision_data['post_parent']   = $post['ID'];
  92.     $revision_data['post_status']   = 'inherit';
  93.     $revision_data['post_type']     = 'revision';
  94.     $revision_data['post_name']     = $autosave ? "$post[ID]-autosave-v1" : "$post[ID]-revision-v1"; // "1" is the revisioning system version
  95.     $revision_data['post_date']     = isset( $post['post_modified'] ) ? $post['post_modified'] : '';
  96.     $revision_data['post_date_gmt'] = isset( $post['post_modified_gmt'] ) ? $post['post_modified_gmt'] : '';
  97.  
  98.     return $revision_data;
  99. }
  100.  
  101. /**
  102.  * Creates a revision for the current version of a post.
  103.  *
  104.  * Typically used immediately after a post update, as every update is a revision,
  105.  * and the most recent revision always matches the current post.
  106.  *
  107.  * @since 2.6.0
  108.  *
  109.  * @param int $post_id The ID of the post to save as a revision.
  110.  * @return int|WP_Error|void Void or 0 if error, new revision ID, if success.
  111.  */
  112. function wp_save_post_revision( $post_id ) {
  113.     if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
  114.         return;
  115.  
  116.     if ( ! $post = get_post( $post_id ) )
  117.         return;
  118.  
  119.     if ( ! post_type_supports( $post->post_type, 'revisions' ) )
  120.         return;
  121.  
  122.     if ( 'auto-draft' == $post->post_status )
  123.         return;
  124.  
  125.     if ( ! wp_revisions_enabled( $post ) )
  126.         return;
  127.  
  128.     // Compare the proposed update with the last stored revision verifying that
  129.     // they are different, unless a plugin tells us to always save regardless.
  130.     // If no previous revisions, save one
  131.     if ( $revisions = wp_get_post_revisions( $post_id ) ) {
  132.         // grab the last revision, but not an autosave
  133.         foreach ( $revisions as $revision ) {
  134.             if ( false !== strpos( $revision->post_name, "{$revision->post_parent}-revision" ) ) {
  135.                 $last_revision = $revision;
  136.                 break;
  137.             }
  138.         }
  139.  
  140.         /**
  141.          * Filters whether the post has changed since the last revision.
  142.          *
  143.          * By default a revision is saved only if one of the revisioned fields has changed.
  144.          * This filter can override that so a revision is saved even if nothing has changed.
  145.          *
  146.          * @since 3.6.0
  147.          *
  148.          * @param bool    $check_for_changes Whether to check for changes before saving a new revision.
  149.          *                                   Default true.
  150.          * @param WP_Post $last_revision     The last revision post object.
  151.          * @param WP_Post $post              The post object.
  152.          *
  153.          */
  154.         if ( isset( $last_revision ) && apply_filters( 'wp_save_post_revision_check_for_changes', $check_for_changes = true, $last_revision, $post ) ) {
  155.             $post_has_changed = false;
  156.  
  157.             foreach ( array_keys( _wp_post_revision_fields( $post ) ) as $field ) {
  158.                 if ( normalize_whitespace( $post->$field ) != normalize_whitespace( $last_revision->$field ) ) {
  159.                     $post_has_changed = true;
  160.                     break;
  161.                 }
  162.             }
  163.  
  164.             /**
  165.              * Filters whether a post has changed.
  166.              *
  167.              * By default a revision is saved only if one of the revisioned fields has changed.
  168.              * This filter allows for additional checks to determine if there were changes.
  169.              *
  170.              * @since 4.1.0
  171.              *
  172.              * @param bool    $post_has_changed Whether the post has changed.
  173.              * @param WP_Post $last_revision    The last revision post object.
  174.              * @param WP_Post $post             The post object.
  175.              *
  176.              */
  177.             $post_has_changed = (bool) apply_filters( 'wp_save_post_revision_post_has_changed', $post_has_changed, $last_revision, $post );
  178.  
  179.             //don't save revision if post unchanged
  180.             if ( ! $post_has_changed ) {
  181.                 return;
  182.             }
  183.         }
  184.     }
  185.  
  186.     $return = _wp_put_post_revision( $post );
  187.  
  188.     // If a limit for the number of revisions to keep has been set,
  189.     // delete the oldest ones.
  190.     $revisions_to_keep = wp_revisions_to_keep( $post );
  191.  
  192.     if ( $revisions_to_keep < 0 )
  193.         return $return;
  194.  
  195.     $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) );
  196.  
  197.     $delete = count($revisions) - $revisions_to_keep;
  198.  
  199.     if ( $delete < 1 )
  200.         return $return;
  201.  
  202.     $revisions = array_slice( $revisions, 0, $delete );
  203.  
  204.     for ( $i = 0; isset( $revisions[$i] ); $i++ ) {
  205.         if ( false !== strpos( $revisions[ $i ]->post_name, 'autosave' ) )
  206.             continue;
  207.  
  208.         wp_delete_post_revision( $revisions[ $i ]->ID );
  209.     }
  210.  
  211.     return $return;
  212. }
  213.  
  214. /**
  215.  * Retrieve the autosaved data of the specified post.
  216.  *
  217.  * Returns a post object containing the information that was autosaved for the
  218.  * specified post. If the optional $user_id is passed, returns the autosave for that user
  219.  * otherwise returns the latest autosave.
  220.  *
  221.  * @since 2.6.0
  222.  *
  223.  * @param int $post_id The post ID.
  224.  * @param int $user_id Optional The post author ID.
  225.  * @return WP_Post|false The autosaved data or false on failure or when no autosave exists.
  226.  */
  227. function wp_get_post_autosave( $post_id, $user_id = 0 ) {
  228.     $revisions = wp_get_post_revisions( $post_id, array( 'check_enabled' => false ) );
  229.  
  230.     foreach ( $revisions as $revision ) {
  231.         if ( false !== strpos( $revision->post_name, "{$post_id}-autosave" ) ) {
  232.             if ( $user_id && $user_id != $revision->post_author )
  233.                 continue;
  234.  
  235.             return $revision;
  236.         }
  237.     }
  238.  
  239.     return false;
  240. }
  241.  
  242. /**
  243.  * Determines if the specified post is a revision.
  244.  *
  245.  * @since 2.6.0
  246.  *
  247.  * @param int|WP_Post $post Post ID or post object.
  248.  * @return false|int False if not a revision, ID of revision's parent otherwise.
  249.  */
  250. function wp_is_post_revision( $post ) {
  251.     if ( !$post = wp_get_post_revision( $post ) )
  252.         return false;
  253.  
  254.     return (int) $post->post_parent;
  255. }
  256.  
  257. /**
  258.  * Determines if the specified post is an autosave.
  259.  *
  260.  * @since 2.6.0
  261.  *
  262.  * @param int|WP_Post $post Post ID or post object.
  263.  * @return false|int False if not a revision, ID of autosave's parent otherwise
  264.  */
  265. function wp_is_post_autosave( $post ) {
  266.     if ( !$post = wp_get_post_revision( $post ) )
  267.         return false;
  268.  
  269.     if ( false !== strpos( $post->post_name, "{$post->post_parent}-autosave" ) )
  270.         return (int) $post->post_parent;
  271.  
  272.     return false;
  273. }
  274.  
  275. /**
  276.  * Inserts post data into the posts table as a post revision.
  277.  *
  278.  * @since 2.6.0
  279.  * @access private
  280.  *
  281.  * @param int|WP_Post|array|null $post     Post ID, post object OR post array.
  282.  * @param bool                   $autosave Optional. Is the revision an autosave?
  283.  * @return int|WP_Error WP_Error or 0 if error, new revision ID if success.
  284.  */
  285. function _wp_put_post_revision( $post = null, $autosave = false ) {
  286.     if ( is_object($post) )
  287.         $post = get_object_vars( $post );
  288.     elseif ( !is_array($post) )
  289.         $post = get_post($post, ARRAY_A);
  290.  
  291.     if ( ! $post || empty($post['ID']) )
  292.         return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
  293.  
  294.     if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
  295.         return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
  296.  
  297.     $post = _wp_post_revision_data( $post, $autosave );
  298.     $post = wp_slash($post); //since data is from db
  299.  
  300.     $revision_id = wp_insert_post( $post );
  301.     if ( is_wp_error($revision_id) )
  302.         return $revision_id;
  303.  
  304.     if ( $revision_id ) {
  305.         /**
  306.          * Fires once a revision has been saved.
  307.          *
  308.          * @since 2.6.0
  309.          *
  310.          * @param int $revision_id Post revision ID.
  311.          */
  312.         do_action( '_wp_put_post_revision', $revision_id );
  313.     }
  314.  
  315.     return $revision_id;
  316. }
  317.  
  318. /**
  319.  * Gets a post revision.
  320.  *
  321.  * @since 2.6.0
  322.  *
  323.  * @param int|WP_Post $post   The post ID or object.
  324.  * @param string      $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
  325.  *                            a WP_Post object, an associative array, or a numeric array, respectively. Default OBJECT.
  326.  * @param string      $filter Optional sanitation filter. See sanitize_post().
  327.  * @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
  328.  */
  329. function wp_get_post_revision(&$post, $output = OBJECT, $filter = 'raw') {
  330.     if ( !$revision = get_post( $post, OBJECT, $filter ) )
  331.         return $revision;
  332.     if ( 'revision' !== $revision->post_type )
  333.         return null;
  334.  
  335.     if ( $output == OBJECT ) {
  336.         return $revision;
  337.     } elseif ( $output == ARRAY_A ) {
  338.         $_revision = get_object_vars($revision);
  339.         return $_revision;
  340.     } elseif ( $output == ARRAY_N ) {
  341.         $_revision = array_values(get_object_vars($revision));
  342.         return $_revision;
  343.     }
  344.  
  345.     return $revision;
  346. }
  347.  
  348. /**
  349.  * Restores a post to the specified revision.
  350.  *
  351.  * Can restore a past revision using all fields of the post revision, or only selected fields.
  352.  *
  353.  * @since 2.6.0
  354.  *
  355.  * @param int|WP_Post $revision_id Revision ID or revision object.
  356.  * @param array       $fields      Optional. What fields to restore from. Defaults to all.
  357.  * @return int|false|null Null if error, false if no fields to restore, (int) post ID if success.
  358.  */
  359. function wp_restore_post_revision( $revision_id, $fields = null ) {
  360.     if ( !$revision = wp_get_post_revision( $revision_id, ARRAY_A ) )
  361.         return $revision;
  362.  
  363.     if ( !is_array( $fields ) )
  364.         $fields = array_keys( _wp_post_revision_fields( $revision ) );
  365.  
  366.     $update = array();
  367.     foreach ( array_intersect( array_keys( $revision ), $fields ) as $field ) {
  368.         $update[$field] = $revision[$field];
  369.     }
  370.  
  371.     if ( !$update )
  372.         return false;
  373.  
  374.     $update['ID'] = $revision['post_parent'];
  375.  
  376.     $update = wp_slash( $update ); //since data is from db
  377.  
  378.     $post_id = wp_update_post( $update );
  379.     if ( ! $post_id || is_wp_error( $post_id ) )
  380.         return $post_id;
  381.  
  382.     // Update last edit user
  383.     update_post_meta( $post_id, '_edit_last', get_current_user_id() );
  384.  
  385.     /**
  386.      * Fires after a post revision has been restored.
  387.      *
  388.      * @since 2.6.0
  389.      *
  390.      * @param int $post_id     Post ID.
  391.      * @param int $revision_id Post revision ID.
  392.      */
  393.     do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] );
  394.  
  395.     return $post_id;
  396. }
  397.  
  398. /**
  399.  * Deletes a revision.
  400.  *
  401.  * Deletes the row from the posts table corresponding to the specified revision.
  402.  *
  403.  * @since 2.6.0
  404.  *
  405.  * @param int|WP_Post $revision_id Revision ID or revision object.
  406.  * @return array|false|WP_Post|WP_Error|null Null or WP_Error if error, deleted post if success.
  407.  */
  408. function wp_delete_post_revision( $revision_id ) {
  409.     if ( ! $revision = wp_get_post_revision( $revision_id ) ) {
  410.         return $revision;
  411.     }
  412.  
  413.     $delete = wp_delete_post( $revision->ID );
  414.     if ( $delete ) {
  415.         /**
  416.          * Fires once a post revision has been deleted.
  417.          *
  418.          * @since 2.6.0
  419.          *
  420.          * @param int          $revision_id Post revision ID.
  421.          * @param object|array $revision    Post revision object or array.
  422.          */
  423.         do_action( 'wp_delete_post_revision', $revision->ID, $revision );
  424.     }
  425.  
  426.     return $delete;
  427. }
  428.  
  429. /**
  430.  * Returns all revisions of specified post.
  431.  *
  432.  * @since 2.6.0
  433.  *
  434.  * @see get_children()
  435.  *
  436.  * @param int|WP_Post $post_id Optional. Post ID or WP_Post object. Default is global `$post`.
  437.  * @param array|null  $args    Optional. Arguments for retrieving post revisions. Default null.
  438.  * @return array An array of revisions, or an empty array if none.
  439.  */
  440. function wp_get_post_revisions( $post_id = 0, $args = null ) {
  441.     $post = get_post( $post_id );
  442.     if ( ! $post || empty( $post->ID ) )
  443.         return array();
  444.  
  445.     $defaults = array( 'order' => 'DESC', 'orderby' => 'date ID', 'check_enabled' => true );
  446.     $args = wp_parse_args( $args, $defaults );
  447.  
  448.     if ( $args['check_enabled'] && ! wp_revisions_enabled( $post ) )
  449.         return array();
  450.  
  451.     $args = array_merge( $args, array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit' ) );
  452.  
  453.     if ( ! $revisions = get_children( $args ) )
  454.         return array();
  455.  
  456.     return $revisions;
  457. }
  458.  
  459. /**
  460.  * Determine if revisions are enabled for a given post.
  461.  *
  462.  * @since 3.6.0
  463.  *
  464.  * @param WP_Post $post The post object.
  465.  * @return bool True if number of revisions to keep isn't zero, false otherwise.
  466.  */
  467. function wp_revisions_enabled( $post ) {
  468.     return wp_revisions_to_keep( $post ) !== 0;
  469. }
  470.  
  471. /**
  472.  * Determine how many revisions to retain for a given post.
  473.  *
  474.  * By default, an infinite number of revisions are kept.
  475.  *
  476.  * The constant WP_POST_REVISIONS can be set in wp-config to specify the limit
  477.  * of revisions to keep.
  478.  *
  479.  * @since 3.6.0
  480.  *
  481.  * @param WP_Post $post The post object.
  482.  * @return int The number of revisions to keep.
  483.  */
  484. function wp_revisions_to_keep( $post ) {
  485.     $num = WP_POST_REVISIONS;
  486.  
  487.     if ( true === $num )
  488.         $num = -1;
  489.     else
  490.         $num = intval( $num );
  491.  
  492.     if ( ! post_type_supports( $post->post_type, 'revisions' ) )
  493.         $num = 0;
  494.  
  495.     /**
  496.      * Filters the number of revisions to save for the given post.
  497.      *
  498.      * Overrides the value of WP_POST_REVISIONS.
  499.      *
  500.      * @since 3.6.0
  501.      *
  502.      * @param int     $num  Number of revisions to store.
  503.      * @param WP_Post $post Post object.
  504.      */
  505.     return (int) apply_filters( 'wp_revisions_to_keep', $num, $post );
  506. }
  507.  
  508. /**
  509.  * Sets up the post object for preview based on the post autosave.
  510.  *
  511.  * @since 2.7.0
  512.  * @access private
  513.  *
  514.  * @param WP_Post $post
  515.  * @return WP_Post|false
  516.  */
  517. function _set_preview( $post ) {
  518.     if ( ! is_object( $post ) ) {
  519.         return $post;
  520.     }
  521.  
  522.     $preview = wp_get_post_autosave( $post->ID );
  523.     if ( ! is_object( $preview ) ) {
  524.         return $post;
  525.     }
  526.  
  527.     $preview = sanitize_post( $preview );
  528.  
  529.     $post->post_content = $preview->post_content;
  530.     $post->post_title = $preview->post_title;
  531.     $post->post_excerpt = $preview->post_excerpt;
  532.  
  533.     add_filter( 'get_the_terms', '_wp_preview_terms_filter', 10, 3 );
  534.     add_filter( 'get_post_metadata', '_wp_preview_post_thumbnail_filter', 10, 3 );
  535.  
  536.     return $post;
  537. }
  538.  
  539. /**
  540.  * Filters the latest content for preview from the post autosave.
  541.  *
  542.  * @since 2.7.0
  543.  * @access private
  544.  */
  545. function _show_post_preview() {
  546.     if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) ) {
  547.         $id = (int) $_GET['preview_id'];
  548.  
  549.         if ( false === wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $id ) )
  550.             wp_die( __('Sorry, you are not allowed to preview drafts.') );
  551.  
  552.         add_filter('the_preview', '_set_preview');
  553.     }
  554. }
  555.  
  556. /**
  557.  * Filters terms lookup to set the post format.
  558.  *
  559.  * @since 3.6.0
  560.  * @access private
  561.  *
  562.  * @param array  $terms
  563.  * @param int    $post_id
  564.  * @param string $taxonomy
  565.  * @return array
  566.  */
  567. function _wp_preview_terms_filter( $terms, $post_id, $taxonomy ) {
  568.     if ( ! $post = get_post() )
  569.         return $terms;
  570.  
  571.     if ( empty( $_REQUEST['post_format'] ) || $post->ID != $post_id || 'post_format' != $taxonomy || 'revision' == $post->post_type )
  572.         return $terms;
  573.  
  574.     if ( 'standard' == $_REQUEST['post_format'] )
  575.         $terms = array();
  576.     elseif ( $term = get_term_by( 'slug', 'post-format-' . sanitize_key( $_REQUEST['post_format'] ), 'post_format' ) )
  577.         $terms = array( $term ); // Can only have one post format
  578.  
  579.     return $terms;
  580. }
  581.  
  582. /**
  583.  * Filters post thumbnail lookup to set the post thumbnail.
  584.  *
  585.  * @since 4.6.0
  586.  * @access private
  587.  *
  588.  * @param null|array|string $value    The value to return - a single metadata value, or an array of values.
  589.  * @param int               $post_id  Post ID.
  590.  * @param string            $meta_key Meta key.
  591.  * @return null|array The default return value or the post thumbnail meta array.
  592.  */
  593. function _wp_preview_post_thumbnail_filter( $value, $post_id, $meta_key ) {
  594.     if ( ! $post = get_post() ) {
  595.         return $value;
  596.     }
  597.  
  598.     if ( empty( $_REQUEST['_thumbnail_id'] ) ||
  599.          empty( $_REQUEST['preview_id'] ) ||
  600.          $post->ID != $post_id ||
  601.          '_thumbnail_id' != $meta_key ||
  602.          'revision' == $post->post_type ||
  603.          $post_id != $_REQUEST['preview_id']
  604.     ) {
  605.         return $value;
  606.     }
  607.  
  608.     $thumbnail_id = intval( $_REQUEST['_thumbnail_id'] );
  609.     if ( $thumbnail_id <= 0 ) {
  610.         return '';
  611.     }
  612.  
  613.     return strval( $thumbnail_id );
  614. }
  615.  
  616. /**
  617.  * Gets the post revision version.
  618.  *
  619.  * @since 3.6.0
  620.  * @access private
  621.  *
  622.  * @param WP_Post $revision
  623.  * @return int|false
  624.  */
  625. function _wp_get_post_revision_version( $revision ) {
  626.     if ( is_object( $revision ) )
  627.         $revision = get_object_vars( $revision );
  628.     elseif ( !is_array( $revision ) )
  629.         return false;
  630.  
  631.     if ( preg_match( '/^\d+-(?:autosave|revision)-v(\d+)$/', $revision['post_name'], $matches ) )
  632.         return (int) $matches[1];
  633.  
  634.     return 0;
  635. }
  636.  
  637. /**
  638.  * Upgrade the revisions author, add the current post as a revision and set the revisions version to 1
  639.  *
  640.  * @since 3.6.0
  641.  * @access private
  642.  *
  643.  * @global wpdb $wpdb WordPress database abstraction object.
  644.  *
  645.  * @param WP_Post $post      Post object
  646.  * @param array   $revisions Current revisions of the post
  647.  * @return bool true if the revisions were upgraded, false if problems
  648.  */
  649. function _wp_upgrade_revisions_of_post( $post, $revisions ) {
  650.     global $wpdb;
  651.  
  652.     // Add post option exclusively
  653.     $lock = "revision-upgrade-{$post->ID}";
  654.     $now = time();
  655.     $result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no') /* LOCK */", $lock, $now ) );
  656.     if ( ! $result ) {
  657.         // If we couldn't get a lock, see how old the previous lock is
  658.         $locked = get_option( $lock );
  659.         if ( ! $locked ) {
  660.             // Can't write to the lock, and can't read the lock.
  661.             // Something broken has happened
  662.             return false;
  663.         }
  664.  
  665.         if ( $locked > $now - 3600 ) {
  666.             // Lock is not too old: some other process may be upgrading this post.  Bail.
  667.             return false;
  668.         }
  669.  
  670.         // Lock is too old - update it (below) and continue
  671.     }
  672.  
  673.     // If we could get a lock, re-"add" the option to fire all the correct filters.
  674.     update_option( $lock, $now );
  675.  
  676.     reset( $revisions );
  677.     $add_last = true;
  678.  
  679.     do {
  680.         $this_revision = current( $revisions );
  681.         $prev_revision = next( $revisions );
  682.  
  683.         $this_revision_version = _wp_get_post_revision_version( $this_revision );
  684.  
  685.         // Something terrible happened
  686.         if ( false === $this_revision_version )
  687.             continue;
  688.  
  689.         // 1 is the latest revision version, so we're already up to date.
  690.         // No need to add a copy of the post as latest revision.
  691.         if ( 0 < $this_revision_version ) {
  692.             $add_last = false;
  693.             continue;
  694.         }
  695.  
  696.         // Always update the revision version
  697.         $update = array(
  698.             'post_name' => preg_replace( '/^(\d+-(?:autosave|revision))[\d-]*$/', '$1-v1', $this_revision->post_name ),
  699.         );
  700.  
  701.         // If this revision is the oldest revision of the post, i.e. no $prev_revision,
  702.         // the correct post_author is probably $post->post_author, but that's only a good guess.
  703.         // Update the revision version only and Leave the author as-is.
  704.         if ( $prev_revision ) {
  705.             $prev_revision_version = _wp_get_post_revision_version( $prev_revision );
  706.  
  707.             // If the previous revision is already up to date, it no longer has the information we need :(
  708.             if ( $prev_revision_version < 1 )
  709.                 $update['post_author'] = $prev_revision->post_author;
  710.         }
  711.  
  712.         // Upgrade this revision
  713.         $result = $wpdb->update( $wpdb->posts, $update, array( 'ID' => $this_revision->ID ) );
  714.  
  715.         if ( $result )
  716.             wp_cache_delete( $this_revision->ID, 'posts' );
  717.  
  718.     } while ( $prev_revision );
  719.  
  720.     delete_option( $lock );
  721.  
  722.     // Add a copy of the post as latest revision.
  723.     if ( $add_last )
  724.         wp_save_post_revision( $post->ID );
  725.  
  726.     return true;
  727. }
  728.