_object_type = $object_type; $this->_redirect_url = $redirect_url; $this->_capabilities = $capabilities; if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { $this->init_ajax(); } else { $this->register_scripts(); $is_locking_page = false; $is_edit_page = false; if ( $this->is_edit_page() ) { $this->init_edit_lock(); $is_locking_page = true; $is_edit_page = true; } else if ( $this->is_list_page() ) { $this->init_list_page(); $is_locking_page = true; } else if ( $this->is_view_page() ) { $this->init_view_page(); $is_locking_page = true; } if ( $is_locking_page ) { $this->_object_id = $this->get_object_id(); $this->_edit_url = $edit_url; $this->maybe_lock_object( $is_edit_page ); } } } /** * Override this method to check the condition for the edit page. * * @return bool */ protected function is_edit_page() { return false; } /** * Override this method to check the condition for the list page. * * @return bool */ protected function is_list_page() { return false; } /** * Override this method to check the condition for the view page. * * @return bool */ protected function is_view_page() { return false; } /** * Override this method to provide the class with the correct object id. * * @return bool */ protected function get_object_id() { $id = rgget( 'id' ); $id = absint( $id ); return $id; // example in the case of form id } public function init_edit_lock() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); } public function init_ajax() { add_filter( 'heartbeat_received', array( $this, 'heartbeat_refresh_nonces' ), 10, 3 ); add_filter( 'heartbeat_received', array( $this, 'heartbeat_check_locked_objects' ), 10, 3 ); add_filter( 'heartbeat_received', array( $this, 'heartbeat_refresh_lock' ), 10, 3 ); add_filter( 'heartbeat_received', array( $this, 'heartbeat_request_lock' ), 10, 3 ); add_filter( 'wp_ajax_gf_lock_request_' . $this->_object_type, array( $this, 'ajax_lock_request' ) ); add_filter( 'wp_ajax_gf_reject_lock_request_' . $this->_object_type, array( $this, 'ajax_reject_lock_request' ) ); } public function init_list_page() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_list_scripts' ) ); } public function init_view_page() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_view_page_scripts' ) ); } public function register_scripts() { $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; $locking_path = GFCommon::get_base_url() . '/includes/locking/'; wp_register_script( 'gforms_locking', $locking_path . "js/locking{$min}.js", array( 'jquery', 'heartbeat' ), GFCommon::$version ); wp_register_script( 'gforms_locking_view', $locking_path . "js/locking-view{$min}.js", array( 'jquery', 'heartbeat' ), GFCommon::$version ); wp_register_script( 'gforms_locking_list', $locking_path . "js/locking-list{$min}.js", array( 'jquery', 'heartbeat' ), GFCommon::$version ); wp_register_style( 'gforms_locking_css', $locking_path . "css/locking{$min}.css", array(), GFCommon::$version ); wp_register_style( 'gforms_locking_list_css', $locking_path . "css/locking-list{$min}.css", array(), GFCommon::$version ); // No conflict scripts add_filter( 'gform_noconflict_scripts', array( $this, 'register_noconflict_scripts' ) ); add_filter( 'gform_noconflict_styles', array( $this, 'register_noconflict_styles' ) ); } public function register_noconflict_scripts( $scripts ) { $locking_scripts = array( 'gforms_locking', 'gforms_locking_list', 'gforms_locking_view' ); return array_merge( $scripts, $locking_scripts ); } public function register_noconflict_styles( $styles ) { $locking_styles = array( 'gforms_locking_css', 'gforms_locking_list_css' ); return array_merge( $styles, $locking_styles ); } public function enqueue_scripts() { wp_enqueue_script( 'gforms_locking' ); wp_enqueue_style( 'gforms_locking_css' ); $lock_user_id = $this->check_lock( $this->get_object_id() ); $strings = array( 'noResponse' => $this->get_string( 'no_response' ), 'requestAgain' => $this->get_string( 'request_again' ), 'requestError' => $this->get_string( 'request_error' ), 'gainedControl' => $this->get_string( 'gained_control' ), 'rejected' => $this->get_string( 'request_rejected' ), 'pending' => $this->get_string( 'request_pending' ) ); $vars = array( 'hasLock' => ! $lock_user_id ? 1 : 0, 'lockUI' => $this->get_lock_ui( $lock_user_id ), 'objectID' => $this->_object_id, 'objectType' => $this->_object_type, 'strings' => $strings, ); wp_localize_script( 'gforms_locking', 'gflockingVars', $vars ); } public function enqueue_list_scripts() { wp_enqueue_script( 'gforms_locking_list' ); wp_enqueue_style( 'gforms_locking_list_css' ); $vars = array( 'objectType' => $this->_object_type, ); wp_localize_script( 'gforms_locking_list', 'gflockingVars', $vars ); } public function enqueue_view_page_scripts() { wp_enqueue_script( 'gforms_locking_view' ); wp_enqueue_style( 'gforms_locking_view_css' ); $lock_user_id = $this->check_lock( $this->get_object_id() ); $vars = array( 'hasLock' => ! $lock_user_id ? 1 : 0, 'objectID' => $this->_object_id, 'objectType' => $this->_object_type, ); wp_localize_script( 'gforms_locking_view', 'gflockingVars', $vars ); } protected function get_strings() { $strings = array( 'currently_locked' => wp_strip_all_tags( __( 'This page is currently locked. Click on the "Request Control" button to let %s know you\'d like to take over.', 'gravityforms' ) ), 'accept' => wp_strip_all_tags( __( 'Accept', 'gravityforms' ) ), 'cancel' => wp_strip_all_tags( __( 'Cancel', 'gravityforms' ) ), 'currently_editing' => wp_strip_all_tags( __( '%s is currently editing', 'gravityforms' ) ), 'taken_over' => wp_strip_all_tags( __( '%s has taken over and is currently editing.', 'gravityforms' ) ), 'lock_requested' => wp_strip_all_tags( __( '%s has requested permission to take over control.', 'gravityforms' ) ), 'gained_control' => wp_strip_all_tags( __( 'You now have control', 'gravityforms' ) ), 'request_pending' => wp_strip_all_tags( __( 'Pending', 'gravityforms' ) ), 'no_response' => wp_strip_all_tags( __( 'No response', 'gravityforms' ) ), 'request_again' => wp_strip_all_tags( __( 'Request again', 'gravityforms' ) ), 'request_error' => wp_strip_all_tags( __( 'Error', 'gravityforms' ) ), 'request_rejected' => wp_strip_all_tags( __( 'Your request was rejected', 'gravityforms' ) ), ); return $strings; } public function ajax_lock_request() { $object_id = rgget( 'object_id' ); $response = $this->request_lock( $object_id ); echo json_encode( $response ); die(); } public function ajax_reject_lock_request() { $object_id = rgget( 'object_id' ); $response = $this->delete_lock_request_meta( $object_id ); echo json_encode( $response ); die(); } protected function has_lock() { return $this->check_lock( $this->get_object_id() ) ? true : false; } protected function check_lock( $object_id ) { if ( ! $user_id = $this->get_lock_meta( $object_id ) ) { return false; } if ( $user_id != get_current_user_id() ) { return $user_id; } return false; } protected function check_lock_request( $object_id ) { if ( ! $user_id = $this->get_lock_request_meta( $object_id ) ) { return false; } if ( $user_id != get_current_user_id() ) { return $user_id; } return false; } protected function set_lock( $object_id ) { if ( ! GFCommon::current_user_can_any( $this->_capabilities ) ) { return false; } if ( 0 == ( $user_id = get_current_user_id() ) ) { return false; } $this->update_lock_meta( $object_id, $user_id ); return $user_id; } protected function request_lock( $object_id ) { if ( 0 == ( $user_id = get_current_user_id() ) ) { return false; } $lock_holder_user_id = $this->check_lock( $object_id ); $result = array(); if ( ! $lock_holder_user_id ) { $this->set_lock( $object_id ); $result['html'] = __( 'You now have control', 'gravityforms' ); $result['status'] = 'lock_obtained'; } else { $user = get_userdata( $lock_holder_user_id ); $this->update_lock_request_meta( $object_id, $user_id ); $result['html'] = sprintf( __( 'Your request has been sent to %s.', 'gravityforms' ), $user->display_name ); $result['status'] = 'lock_requested'; } return $result; } protected function get_lock_request_meta( $object_id ) { return GFCache::get( self::PREFIX_EDIT_LOCK_REQUEST . $this->_object_type . '_' . $object_id ); } protected function get_lock_meta( $object_id ) { return GFCache::get( self::PREFIX_EDIT_LOCK . $this->_object_type . '_' . $object_id ); } protected function update_lock_meta( $object_id, $lock_value ) { GFCache::set( self::PREFIX_EDIT_LOCK . $this->_object_type . '_' . $object_id, $lock_value, true, 150 ); } protected function update_lock_request_meta( $object_id, $lock_request_value ) { GFCache::set( self::PREFIX_EDIT_LOCK_REQUEST . $this->_object_type . '_' . $object_id, $lock_request_value, true, 120 ); } protected function delete_lock_request_meta( $object_id ) { GFCache::delete( self::PREFIX_EDIT_LOCK_REQUEST . $this->_object_type . '_' . $object_id ); return true; } protected function delete_lock_meta( $object_id ) { GFCache::delete( self::PREFIX_EDIT_LOCK . $this->_object_type . '_' . $object_id ); return true; } public function maybe_lock_object( $is_edit_page ) { if ( isset( $_GET['get-edit-lock'] ) ) { $this->set_lock( $this->_object_id ); wp_safe_redirect( $this->_edit_url ); exit(); } else if ( isset( $_GET['release-edit-lock'] ) ) { $this->delete_lock_meta( $this->_object_id ); wp_safe_redirect( $this->_redirect_url ); exit(); } else { if ( $is_edit_page && ! $user_id = $this->check_lock( $this->_object_id ) ) { $this->set_lock( $this->_object_id ); } } } public function heartbeat_check_locked_objects( $response, $data, $screen_id ) { $checked = array(); $heartbeat_key = 'gform-check-locked-objects-' . $this->_object_type; if ( array_key_exists( $heartbeat_key, $data ) && is_array( $data[ $heartbeat_key ] ) ) { foreach ( $data[ $heartbeat_key ] as $object_id ) { if ( ( $user_id = $this->check_lock( $object_id ) ) && ( $user = get_userdata( $user_id ) ) ) { $send = array( 'text' => sprintf( __( $this->get_string( 'currently_editing' ) ), $user->display_name ) ); if ( ( $avatar = get_avatar( $user->ID, 18 ) ) && preg_match( "|src='([^']+)'|", $avatar, $matches ) ) { $send['avatar_src'] = $matches[1]; } $checked[ $object_id ] = $send; } } } if ( ! empty( $checked ) ) { $response[ $heartbeat_key ] = $checked; } return $response; } public function heartbeat_refresh_lock( $response, $data, $screen_id ) { $heartbeat_key = 'gform-refresh-lock-' . $this->_object_type; if ( array_key_exists( $heartbeat_key, $data ) ) { $received = $data[ $heartbeat_key ]; $send = array(); if ( ! isset( $received['objectID'] ) ) { return $response; } $object_id = $received['objectID']; if ( ( $user_id = $this->check_lock( $object_id ) ) && ( $user = get_userdata( $user_id ) ) ) { $error = array( 'text' => sprintf( __( $this->get_string( 'taken_over' ) ), $user->display_name ) ); if ( $avatar = get_avatar( $user->ID, 64 ) ) { if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) { $error['avatar_src'] = $matches[1]; } } $send['lock_error'] = $error; } else { if ( $new_lock = $this->set_lock( $object_id ) ) { $send['new_lock'] = $new_lock; if ( ( $lock_requester = $this->check_lock_request( $object_id ) ) && ( $user = get_userdata( $lock_requester ) ) ) { $lock_request = array( 'text' => sprintf( __( $this->get_string( 'lock_requested' ) ), $user->display_name ) ); if ( $avatar = get_avatar( $user->ID, 64 ) ) { if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) { $lock_request['avatar_src'] = $matches[1]; } } $send['lock_request'] = $lock_request; } } } $response[ $heartbeat_key ] = $send; } return $response; } public function heartbeat_request_lock( $response, $data, $screen_id ) { $heartbeat_key = 'gform-request-lock-' . $this->_object_type; if ( array_key_exists( $heartbeat_key, $data ) ) { $received = $data[ $heartbeat_key ]; $send = array(); if ( ! isset( $received['objectID'] ) ) { return $response; } $object_id = $received['objectID']; if ( ( $user_id = $this->check_lock( $object_id ) ) && ( $user = get_userdata( $user_id ) ) ) { if ( $this->get_lock_request_meta( $object_id ) ) { $send['status'] = 'pending'; } else { $send['status'] = 'deleted'; } } else { if ( $new_lock = $this->set_lock( $object_id ) ) { $send['status'] = 'granted'; } } $response[ $heartbeat_key ] = $send; } return $response; } public function heartbeat_refresh_nonces( $response, $data, $screen_id ) { if ( array_key_exists( 'gform-refresh-nonces', $data ) ) { $received = $data['gform-refresh-nonces']; $response['gform-refresh-nonces'] = array( 'check' => 1 ); if ( ! isset( $received['objectID'] ) ) { return $response; } $object_id = $received['objectID']; if ( ! GFCommon::current_user_can_any( $this->_capabilities ) || empty( $received['post_nonce'] ) ) { return $response; } if ( 2 === wp_verify_nonce( $received['object_nonce'], 'update-contact_' . $object_id ) ) { $response['gform-refresh-nonces'] = array( 'replace' => array( '_wpnonce' => wp_create_nonce( 'update-object_' . $object_id ), ), 'heartbeatNonce' => wp_create_nonce( 'heartbeat-nonce' ), ); } } return $response; } public function get_lock_ui( $user_id ) { $user = get_userdata( $user_id ); $locked = $user_id && $user; $edit_url = $this->_edit_url; $hidden = $locked ? '' : ' hidden'; if ( $locked ) { $message = '
'; } else { $message = ''; } $html = '