From 302afefea2c4a97fbcd9f8831de63737058a7d90 Mon Sep 17 00:00:00 2001 From: omarkasem Date: Thu, 19 Dec 2024 19:18:28 +0200 Subject: [PATCH 1/3] Adds heartbeat ajax functions from gf, Fixes enqueue issue and Fixes Modal issue in frontend --- .../edit-entry/class-edit-entry-locking.php | 188 +++++++++++++++++- .../edit-entry/class-edit-entry.php | 2 +- 2 files changed, 183 insertions(+), 7 deletions(-) diff --git a/includes/extensions/edit-entry/class-edit-entry-locking.php b/includes/extensions/edit-entry/class-edit-entry-locking.php index 8382df679..75da47bde 100644 --- a/includes/extensions/edit-entry/class-edit-entry-locking.php +++ b/includes/extensions/edit-entry/class-edit-entry-locking.php @@ -28,12 +28,35 @@ public function load() { add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_scripts' ) ); } + 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_action( 'wp_ajax_gf_lock_request_entry', array( $this, 'ajax_lock_request' ), 1 ); add_action( 'wp_ajax_gf_reject_lock_request_entry', array( $this, 'ajax_reject_lock_request' ), 1 ); add_action( 'wp_ajax_nopriv_gf_lock_request_entry', array( $this, 'ajax_lock_request' ) ); add_action( 'wp_ajax_nopriv_gf_reject_lock_request_entry', array( $this, 'ajax_reject_lock_request' ) ); } + + protected function get_lock_request_meta( $object_id ) { + return GFCache::get( 'lock_request_entry_' . $object_id ); + } + + 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; + } + // TODO: Convert to extending Gravity Forms public function ajax_lock_request() { $object_id = rgget( 'object_id' ); @@ -130,12 +153,8 @@ public function maybe_enqueue_scripts() { continue; } - // Make sure that the entry belongs to one of the forms connected to one of the Views in this request - $joined_forms = $view::get_joined_forms( $view->ID ); - - $entry_form_id = $entry_array['form_id']; - - if ( ! isset( $joined_forms[ $entry_form_id ] ) ) { + // Make sure that the entry belongs to the view form + if( $view->form->ID != $entry_array['form_id'] ){ continue; } @@ -164,12 +183,33 @@ public function maybe_enqueue_scripts() { */ protected function enqueue_scripts( $entry ) { + $lock_user_id = $this->check_lock( $entry['id'] ); + + // Gravity forms locking checks if #wpwrap exist in the admin dashboard, + // So we have to add the lock UI to the body before the gforms locking script is loaded. + wp_add_inline_script( 'heartbeat', ' + jQuery(document).ready(function($) { + if ($("#wpwrap").length === 0) { + var lockUI = ' . json_encode( $this->get_lock_ui( $lock_user_id ) ) . '; + $("body").prepend(lockUI); + } + }); + ', 'after' ); + + $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; $locking_path = GFCommon::get_base_url() . '/includes/locking/'; wp_enqueue_script( 'gforms_locking', $locking_path . "js/locking{$min}.js", array( 'jquery', 'heartbeat' ), GFCommon::$version ); wp_enqueue_style( 'gforms_locking_css', $locking_path . "css/locking{$min}.css", array( 'edit' ), GFCommon::$version ); + // add inline css to hide notification-dialog-wrap if it has the hidden class + wp_add_inline_style( 'gforms_locking_css', ' + .notification-dialog-wrap.hidden { + display: none; + } + ' ); + $translations = array_map( 'wp_strip_all_tags', $this->get_strings() ); $strings = array( @@ -417,4 +457,140 @@ public function set_lock( $entry_id ) { return $user_id; } + + + public function heartbeat_check_locked_objects( $response, $data, $screen_id ) { + $checked = array(); + $heartbeat_key = 'gform-check-locked-objects-entry'; + 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-entry'; + 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-entry'; + 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; + } + } diff --git a/includes/extensions/edit-entry/class-edit-entry.php b/includes/extensions/edit-entry/class-edit-entry.php index 7c531a4f7..abb5acce2 100644 --- a/includes/extensions/edit-entry/class-edit-entry.php +++ b/includes/extensions/edit-entry/class-edit-entry.php @@ -403,7 +403,7 @@ public static function check_user_cap_edit_entry( $entry, $view = 0 ) { $current_user = wp_get_current_user(); // User edit is disabled - if ( empty( $user_edit ) ) { + if ( $view_id && empty( $user_edit ) ) { gravityview()->log->debug( 'User Edit is disabled. Returning false.' ); From efea6752d95a36ad6ea93db39aaf1839049a36e0 Mon Sep 17 00:00:00 2001 From: omarkasem Date: Sun, 29 Dec 2024 19:39:18 +0200 Subject: [PATCH 2/3] Fixes gravatar issue and Fixes redirect to edit page --- .../edit-entry/class-edit-entry-locking.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/includes/extensions/edit-entry/class-edit-entry-locking.php b/includes/extensions/edit-entry/class-edit-entry-locking.php index 75da47bde..8e89636a5 100644 --- a/includes/extensions/edit-entry/class-edit-entry-locking.php +++ b/includes/extensions/edit-entry/class-edit-entry-locking.php @@ -190,7 +190,7 @@ protected function enqueue_scripts( $entry ) { wp_add_inline_script( 'heartbeat', ' jQuery(document).ready(function($) { if ($("#wpwrap").length === 0) { - var lockUI = ' . json_encode( $this->get_lock_ui( $lock_user_id ) ) . '; + var lockUI = ' . json_encode( $this->get_lock_ui( $lock_user_id, $entry ) ) . '; $("body").prepend(lockUI); } }); @@ -225,7 +225,7 @@ protected function enqueue_scripts( $entry ) { $vars = array( 'hasLock' => ! $lock_user_id ? 1 : 0, - 'lockUI' => $this->get_lock_ui( $lock_user_id ), + 'lockUI' => $this->get_lock_ui( $lock_user_id, $entry ), 'objectID' => $entry['id'], 'objectType' => 'entry', 'strings' => $strings, @@ -245,10 +245,11 @@ protected function enqueue_scripts( $entry ) { * * @param int $user_id The User ID that has the current lock. Will be empty if entry is not locked * or is locked to the current user. + * @param array $entry The entry array. * * @return string The Lock UI dialog box, etc. */ - public function get_lock_ui( $user_id ) { + public function get_lock_ui( $user_id, $entry ) { $user = get_userdata( $user_id ); $locked = $user_id && $user; @@ -256,7 +257,7 @@ public function get_lock_ui( $user_id ) { $hidden = $locked ? '' : ' hidden'; if ( $locked ) { - if ( GVCommon::has_cap( 'gravityforms_edit_entries' ) ) { + if ( GVCommon::has_cap( 'gravityforms_edit_entries' ) || $entry['created_by'] == get_current_user_id() ) { $avatar = get_avatar( $user->ID, 64 ); $person_editing_text = $user->display_name; } else { @@ -361,7 +362,7 @@ public function get_string( $string ) { public function maybe_lock_object( $entry_id ) { global $wp; - $current_url = add_query_arg( $wp->query_string, '', home_url( $wp->request ) ); + $current_url = home_url( add_query_arg( null, null ) ); if ( isset( $_GET['get-edit-lock'] ) ) { $this->set_lock( $entry_id ); @@ -373,7 +374,7 @@ public function maybe_lock_object( $entry_id ) { echo ''; exit(); } elseif ( ! $user_id = $this->check_lock( $entry_id ) ) { - $this->set_lock( $entry_id ); + $this->set_lock( $entry_id ); } } @@ -576,7 +577,7 @@ public function heartbeat_refresh_nonces( $response, $data, $screen_id ) { $object_id = $received['objectID']; - if ( ! GFCommon::current_user_can_any( $this->_capabilities ) || empty( $received['post_nonce'] ) ) { + if ( ! GVCommon::has_cap( 'gravityforms_edit_entries' ) || empty( $received['post_nonce'] ) ) { return $response; } From ad1088dde57f137511d26e86b549c62a5f1c2bbf Mon Sep 17 00:00:00 2001 From: omarkasem Date: Fri, 10 Jan 2025 23:45:03 +0200 Subject: [PATCH 3/3] Adds docblock comments to functions --- .../edit-entry/class-edit-entry-locking.php | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/includes/extensions/edit-entry/class-edit-entry-locking.php b/includes/extensions/edit-entry/class-edit-entry-locking.php index 8e89636a5..644e57fe2 100644 --- a/includes/extensions/edit-entry/class-edit-entry-locking.php +++ b/includes/extensions/edit-entry/class-edit-entry-locking.php @@ -40,10 +40,24 @@ public function load() { } + /** + * Get the lock request meta for an object. + * + * @param int $object_id The object ID. + * + * @return int|null The User ID or null. + */ protected function get_lock_request_meta( $object_id ) { return GFCache::get( 'lock_request_entry_' . $object_id ); } + /** + * Check if the current user has a lock request for an object. + * + * @param int $object_id The object ID. + * + * @return int|null The User ID or null. + */ protected function check_lock_request( $object_id ) { if ( ! $user_id = $this->get_lock_request_meta( $object_id ) ) { @@ -459,7 +473,15 @@ public function set_lock( $entry_id ) { return $user_id; } - + /** + * Check if the objects are locked. + * + * @param array $response The response array. + * @param array $data The data array. + * @param string $screen_id The screen ID. + * + * @return array The response array. + */ public function heartbeat_check_locked_objects( $response, $data, $screen_id ) { $checked = array(); $heartbeat_key = 'gform-check-locked-objects-entry'; @@ -484,6 +506,15 @@ public function heartbeat_check_locked_objects( $response, $data, $screen_id ) { return $response; } + /** + * Refresh the lock for an entry. + * + * @param array $response The response array. + * @param array $data The data array. + * @param string $screen_id The screen ID. + * + * @return array The response array. + */ public function heartbeat_refresh_lock( $response, $data, $screen_id ) { $heartbeat_key = 'gform-refresh-lock-entry'; if ( array_key_exists( $heartbeat_key, $data ) ) { @@ -535,6 +566,15 @@ public function heartbeat_refresh_lock( $response, $data, $screen_id ) { return $response; } + /** + * Request the lock for an entry. + * + * @param array $response The response array. + * @param array $data The data array. + * @param string $screen_id The screen ID. + * + * @return array The response array. + */ public function heartbeat_request_lock( $response, $data, $screen_id ) { $heartbeat_key = 'gform-request-lock-entry'; if ( array_key_exists( $heartbeat_key, $data ) ) { @@ -565,7 +605,15 @@ public function heartbeat_request_lock( $response, $data, $screen_id ) { return $response; } - + /** + * Refresh the nonces for an entry. + * + * @param array $response The response array. + * @param array $data The data array. + * @param string $screen_id The screen ID. + * + * @return array The response array. + */ public function heartbeat_refresh_nonces( $response, $data, $screen_id ) { if ( array_key_exists( 'gform-refresh-nonces', $data ) ) { $received = $data['gform-refresh-nonces'];