Make WordPress Core

Ticket #32202: edit_lock.patch

File edit_lock.patch, 11.7 KB (added by daxelrod, 9 years ago)

Generic edit locking functions for wp-admin

  • wp-admin/includes/admin.php

     
    3333/** WordPress Import Administration API */
    3434require_once(ABSPATH . 'wp-admin/includes/import.php');
    3535
     36/** WordPress Edit Lock Administration API */
     37require_once(ABSPATH . 'wp-admin/includes/edit-lock.php');
     38
    3639/** WordPress Misc Administration API */
    3740require_once(ABSPATH . 'wp-admin/includes/misc.php');
    3841
  • wp-admin/includes/ajax-actions.php

     
    21132113 * @since 3.1.0
    21142114 */
    21152115function wp_ajax_wp_remove_post_lock() {
    2116         if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
     2116        if ( empty( $_POST['post_ID'] ) )
    21172117                wp_die( 0 );
    21182118        $post_id = (int) $_POST['post_ID'];
    21192119        if ( ! $post = get_post( $post_id ) )
     
    21242124        if ( ! current_user_can( 'edit_post', $post_id ) )
    21252125                wp_die( -1 );
    21262126
    2127         $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
     2127        $active_lock = wp_get_edit_lock('post', $post_id);
    21282128        if ( $active_lock[1] != get_current_user_id() )
    21292129                wp_die( 0 );
    21302130
     
    21362136         * @param int $interval The interval in seconds the post lock duration
    21372137         *                      should last, plus 5 seconds. Default 150.
    21382138         */
    2139         $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
    2140         update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
     2139        $time_window = apply_filters( 'wp_check_post_lock_window', 150 );
     2140        /** This filter is documented in wp-admin/includes/edit-lock.php */
     2141        $time_window = apply_filters( 'wp_edit_' . 'post' . '_lock_window', $time_window );
     2142        // Set lock time in the past, add 5 second buffer in case user changes their mind.
     2143        $time = time() - $time_window + 5;
     2144        wp_set_edit_lock('post', $post_id, $time, $active_lock[1]);
    21412145        wp_die( 1 );
    21422146}
    21432147
  • wp-admin/includes/edit-lock.php

     
     1<?php
     2/**
     3 * Functions for edit locking functionality
     4 *
     5 * @package WordPress\Administration
     6 */
     7
     8/**
     9 * Get an edit lock for an object if it exists.
     10 *
     11 * @param string $type Type of object to check for editing (e.g. post). Type 'site' will be treated as a multisite lock.
     12 * @param int|string $id Unique identifier of the object to check for editing (e.g. post_id).
     13 * @return bool|array False: not locked. Array: Lock information consisting of time and user_id.
     14 */
     15function wp_get_edit_lock( $type, $id ) {
     16        if ( in_array( $type, array( 'comment', 'post', 'user' ), true ) ) {
     17                // Locks stored in *meta
     18
     19                switch ( $type ) {
     20                        case 'comment':
     21                                if ( ! $comment = get_comment( $id ) )
     22                                        return false;
     23
     24                                $object_id = $comment->comment_ID;
     25                                break;
     26                        case 'post':
     27                                if ( ! $post = get_post( $id ) )
     28                                        return false;
     29
     30                                $object_id = $post->ID;
     31                                break;
     32                        case 'user':
     33                                if ( ! $user = get_user_by( 'id', $id ) )
     34                                        return false;
     35
     36                                $object_id = $user->ID;
     37                                break;
     38                }
     39
     40                if ( ! $lock = get_metadata( $type, $object_id, '_edit_lock', true ) )
     41                        return false;
     42
     43        } else {
     44                // Locks stored in options
     45
     46                $locks = _wp_edit_lock_get_locks( $type );
     47
     48                if ( ! isset( $locks[$id] ) )
     49                        return false;
     50
     51                $lock = $locks[$id];
     52        }
     53
     54        $lock = wp_read_edit_lock( $lock );
     55        $time = $lock[0];
     56        if ( 'post' === $type ) {
     57                // Posts have a special fallback for user
     58                $user_id = is_null( $lock[1] ) ? get_post_meta( $post->ID, '_edit_last', true ) : $lock[1];
     59        } else {
     60                $user_id = is_null( $lock[1] ) ? get_current_user_id() : $lock[1];
     61        }
     62
     63        return array( $time, $user_id );
     64}
     65
     66/**
     67 * Check to see if the object is currently being edited by another user.
     68 *
     69 * @param string $type Type of object to check for editing (e.g. post). Type 'site' will be treated as a multisite lock.
     70 * @param int|string $id Unique identifier of the object to check for editing (e.g. post_id).
     71 * @return bool|int False: not locked or locked by current user. Int: user ID of the user with lock.
     72 */
     73function wp_check_edit_lock( $type, $id ) {
     74
     75        /**
     76         * Filter allow edit lock. Short circuits function.
     77         *
     78         * @param bool $allow Allow edit lock. Default true.
     79         * @param string $type Type of object to check for editing (e.g. post).
     80         * @param int|string $id Unique identifier of the object to check for editing (e.g. post_id).
     81         */
     82        $allow = apply_filters( 'allow_edit_lock', true, $type, $id );
     83
     84        if ( ! $allow )
     85                return false;
     86
     87        $lock = wp_get_edit_lock( $type, $id );
     88
     89        if ( ! $lock )
     90                return false;
     91
     92        $time = $lock[0];
     93        $user_id = $lock[1];
     94
     95        if ( 'post' === $type ) {
     96                /** This filter is documented in wp-admin/includes/ajax-actions.php */
     97                $time_window = apply_filters( 'wp_check_post_lock_window', 150 );
     98        } else {
     99                $time_window = 150; // Default value
     100        }
     101
     102        /**
     103         * Filter the post lock window duration.
     104         *
     105         * @param int $interval The interval in seconds of the edit lock duration. Default 150.
     106         */
     107        $time_window = apply_filters( 'wp_edit_' . $type . '_lock_window', $time_window );
     108
     109        if ( $time && $time > time() - $time_window && $user_id != get_current_user_id() )
     110                return $user_id;
     111
     112        return false;
     113}
     114
     115/**
     116 * Mark the object as currently being edited by the current user.
     117 *
     118 * @param string $type Type of object to be edited (e.g. post). Type 'site' will be treated as a multisite lock.
     119 * @param int|string $id Unique identifier of the object to edit (e.g. post_id).
     120 * @param int $time Optional. Unix timestamp of the lock. Default time().
     121 * @param int $user_id Optional. User ID to give the lock. Default get_current_user_id().
     122 * @return bool|string False: if there is no current user. String: value of the edit lock.
     123 */
     124function wp_set_edit_lock( $type, $id, $time = null, $user_id = null ) {
     125        $lock = wp_create_edit_lock( $time, $user_id );
     126
     127        if ( ! $lock )
     128                return false;
     129
     130        if ( in_array( $type, array( 'comment', 'post', 'user' ), true ) ) {
     131                // Locks stored in *meta
     132
     133                switch ( $type ) {
     134                        case 'comment':
     135                                if ( ! $comment = get_comment( $id ) )
     136                                        return false;
     137
     138                                $object_id = $comment->comment_ID;
     139                                break;
     140                        case 'post':
     141                                if ( ! $post = get_post( $id ) )
     142                                        return false;
     143
     144                                $object_id = $post->ID;
     145                                break;
     146                        case 'user':
     147                                if ( ! $user = get_user_by( 'id', $id ) )
     148                                        return false;
     149
     150                                $object_id = $user->ID;
     151                                break;
     152                }
     153
     154        } else {
     155                // Locks stored in options
     156
     157                $locks = _wp_edit_lock_get_locks( $type );
     158
     159                /** This filter is documented in wp-admin/includes/edit-lock.php */
     160                $time_window = apply_filters( 'wp_edit_' . $type . '_lock_window', 150 );
     161
     162                // Remove expired locks
     163                foreach ( $locks as $_id => $_lock ) {
     164                        $_lock = wp_read_edit_lock( $_lock );
     165                        $time = $_lock[0];
     166
     167                        if ( ! $time || $time <= time() - $time_window )
     168                                unset( $locks[$_id] );
     169                }
     170
     171        }
     172
     173        // Update locks
     174        if ( in_array( $type, array( 'comment', 'post', 'user' ), true ) ) {
     175                update_metadata( $type, $object_id, '_edit_lock', $lock );
     176        } else {
     177                $locks[$id] = $lock;
     178                _wp_edit_lock_update_locks( $type, $locks );
     179        }
     180
     181        return wp_read_edit_lock( $lock );
     182}
     183
     184/**
     185 * Read lock information from an edit lock string.
     186 *
     187 * @param string $lock Edit lock string consisting of time and user_id.
     188 * @return array Lock information consisting of time and user_id. Note: user_id may be null.
     189 */
     190function wp_read_edit_lock( $lock ) {
     191        $lock = explode( ':', $lock, 2 );
     192
     193        $time = absint( $lock[0] );
     194        $user_id = empty( $lock[1] ) ? null : absint( $lock[1] );
     195
     196        return array( $time, $user_id );
     197}
     198
     199/**
     200 * Pack lock information into an edit lock string.
     201 *
     202 * @param int $time Optional. Unix timestamp of the lock. Default time().
     203 * @param int $user_id Optional. User ID to give the lock. Default get_current_user_id().
     204 * @return bool|string False: if there is no current user. String: Edit lock string consisting of time and user_id.
     205 */
     206function wp_create_edit_lock( $time = null, $user_id = null ) {
     207        if ( ! is_numeric( $time ) || ! $time = absint( $time ) ) {
     208                $time = time();
     209        }
     210
     211        if ( ! $user_id = absint( $user_id ) ) {
     212                if ( 0 == ($user_id = get_current_user_id()) )
     213                        return false;
     214        }
     215
     216        return $time . ':' . $user_id;
     217}
     218 
     219/**
     220 * Edit lock name for the options table.
     221 *
     222 * @access private
     223 *
     224 * @param string $type Type of edit lock.
     225 * @return string Edit lock option name.
     226 */
     227function _wp_edit_lock_option_name( $type ) {
     228        return '_edit_' . trim($type) . '_lock';
     229}
     230
     231/**
     232 * Get all locks from options that are associated with a type.
     233 *
     234 * @access private
     235 *
     236 * @param string $type Type of edit lock.
     237 * @return array Existing locks (may be empty).
     238 */
     239function _wp_edit_lock_get_locks( $type ) {
     240        $edit_lock_name = _wp_edit_lock_option_name( $type );
     241
     242        if ( 'site' === $type && is_multisite() ) {
     243                if ( ! $locks = get_site_option( $edit_lock_name ) )
     244                        return array();
     245        } else {
     246                if ( ! $locks = get_option( $edit_lock_name ) )
     247                        return array();
     248        }
     249
     250        if ( ! is_array($locks) )
     251                return array();
     252
     253        return $locks;
     254}
     255
     256/**
     257 * Updates all locks in options that are associated with a type.
     258 *
     259 * @access private
     260 *
     261 * @param string $type Type of edit lock.
     262 * @param array $locks Existing locks.
     263 * @return bool False if value was not updated and true if value was updated.
     264 */
     265function _wp_edit_lock_update_locks( $type, $locks ) {
     266        $edit_lock_name = _wp_edit_lock_option_name( $type );
     267
     268        if ( $type === 'site' && is_multisite() ) {
     269                return update_site_option( $edit_lock_name, $locks );
     270        } else {
     271                return update_option( $edit_lock_name, $locks, 'no' );
     272        }
     273}
     274 No newline at end of file
  • wp-admin/includes/post.php

     
    13861386 * @return integer False: not locked or locked by current user. Int: user ID of user with lock.
    13871387 */
    13881388function wp_check_post_lock( $post_id ) {
    1389         if ( !$post = get_post( $post_id ) )
    1390                 return false;
    1391 
    1392         if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) )
    1393                 return false;
    1394 
    1395         $lock = explode( ':', $lock );
    1396         $time = $lock[0];
    1397         $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
    1398 
    1399         /** This filter is documented in wp-admin/includes/ajax-actions.php */
    1400         $time_window = apply_filters( 'wp_check_post_lock_window', 150 );
    1401 
    1402         if ( $time && $time > time() - $time_window && $user != get_current_user_id() )
    1403                 return $user;
    1404         return false;
     1389        return wp_check_edit_lock( 'post', $post_id );
    14051390}
    14061391
    14071392/**
     
    14141399 *      an array of the lock time and the user ID.
    14151400 */
    14161401function wp_set_post_lock( $post_id ) {
    1417         if ( !$post = get_post( $post_id ) )
    1418                 return false;
    1419         if ( 0 == ($user_id = get_current_user_id()) )
    1420                 return false;
    1421 
    1422         $now = time();
    1423         $lock = "$now:$user_id";
    1424 
    1425         update_post_meta( $post->ID, '_edit_lock', $lock );
    1426         return array( $now, $user_id );
     1402        return wp_set_edit_lock( 'post', $post_id );
    14271403}
    14281404
    14291405/**
  • wp-admin/js/post.js

     
    391391                        data: {
    392392                                action: 'wp-remove-post-lock',
    393393                                _wpnonce: $('#_wpnonce').val(),
    394                                 post_ID: $('#post_ID').val(),
    395                                 active_post_lock: $('#active_post_lock').val()
     394                                post_ID: $('#post_ID').val()
    396395                        }
    397396                });
    398397        });