module.php (7111B)
1 <?php 2 namespace Elementor\Core\Common\Modules\Ajax; 3 4 use Elementor\Core\Base\Module as BaseModule; 5 use Elementor\Core\Utils\Exceptions; 6 use Elementor\Plugin; 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; // Exit if accessed directly. 10 } 11 12 /** 13 * Elementor ajax manager. 14 * 15 * Elementor ajax manager handler class is responsible for handling Elementor 16 * ajax requests, ajax responses and registering actions applied on them. 17 * 18 * @since 2.0.0 19 */ 20 class Module extends BaseModule { 21 22 const NONCE_KEY = 'elementor_ajax'; 23 24 /** 25 * Ajax actions. 26 * 27 * Holds all the register ajax action. 28 * 29 * @since 2.0.0 30 * @access private 31 * 32 * @var array 33 */ 34 private $ajax_actions = []; 35 36 /** 37 * Ajax requests. 38 * 39 * Holds all the register ajax requests. 40 * 41 * @since 2.0.0 42 * @access private 43 * 44 * @var array 45 */ 46 private $requests = []; 47 48 /** 49 * Ajax response data. 50 * 51 * Holds all the response data for all the ajax requests. 52 * 53 * @since 2.0.0 54 * @access private 55 * 56 * @var array 57 */ 58 private $response_data = []; 59 60 /** 61 * Current ajax action ID. 62 * 63 * Holds all the ID for the current ajax action. 64 * 65 * @since 2.0.0 66 * @access private 67 * 68 * @var string|null 69 */ 70 private $current_action_id = null; 71 72 /** 73 * Ajax manager constructor. 74 * 75 * Initializing Elementor ajax manager. 76 * 77 * @since 2.0.0 78 * @access public 79 */ 80 public function __construct() { 81 add_action( 'wp_ajax_elementor_ajax', [ $this, 'handle_ajax_request' ] ); 82 } 83 84 /** 85 * Get module name. 86 * 87 * Retrieve the module name. 88 * 89 * @since 1.7.0 90 * @access public 91 * 92 * @return string Module name. 93 */ 94 public function get_name() { 95 return 'ajax'; 96 } 97 98 /** 99 * Register ajax action. 100 * 101 * Add new actions for a specific ajax request and the callback function to 102 * be handle the response. 103 * 104 * @since 2.0.0 105 * @access public 106 * 107 * @param string $tag Ajax request name/tag. 108 * @param callable $callback The callback function. 109 */ 110 public function register_ajax_action( $tag, $callback ) { 111 if ( ! did_action( 'elementor/ajax/register_actions' ) ) { 112 _doing_it_wrong( __METHOD__, esc_html( sprintf( 'Use `%s` hook to register ajax action.', 'elementor/ajax/register_actions' ) ), '2.0.0' ); 113 } 114 115 $this->ajax_actions[ $tag ] = compact( 'tag', 'callback' ); 116 } 117 118 /** 119 * Handle ajax request. 120 * 121 * Verify ajax nonce, and run all the registered actions for this request. 122 * 123 * Fired by `wp_ajax_elementor_ajax` action. 124 * 125 * @since 2.0.0 126 * @access public 127 */ 128 public function handle_ajax_request() { 129 if ( ! $this->verify_request_nonce() ) { 130 $this->add_response_data( false, esc_html__( 'Token Expired.', 'elementor' ) ) 131 ->send_error( Exceptions::UNAUTHORIZED ); 132 } 133 134 $editor_post_id = 0; 135 136 if ( ! empty( $_REQUEST['editor_post_id'] ) ) { 137 $editor_post_id = absint( $_REQUEST['editor_post_id'] ); 138 139 Plugin::$instance->db->switch_to_post( $editor_post_id ); 140 } 141 142 /** 143 * Register ajax actions. 144 * 145 * Fires when an ajax request is received and verified. 146 * 147 * Used to register new ajax action handles. 148 * 149 * @since 2.0.0 150 * 151 * @param self $this An instance of ajax manager. 152 */ 153 do_action( 'elementor/ajax/register_actions', $this ); 154 155 $this->requests = json_decode( stripslashes( $_REQUEST['actions'] ), true ); 156 157 foreach ( $this->requests as $id => $action_data ) { 158 $this->current_action_id = $id; 159 160 if ( ! isset( $this->ajax_actions[ $action_data['action'] ] ) ) { 161 $this->add_response_data( false, esc_html__( 'Action not found.', 'elementor' ), Exceptions::BAD_REQUEST ); 162 163 continue; 164 } 165 166 if ( $editor_post_id ) { 167 $action_data['data']['editor_post_id'] = $editor_post_id; 168 } 169 170 try { 171 $results = call_user_func( $this->ajax_actions[ $action_data['action'] ]['callback'], $action_data['data'], $this ); 172 173 if ( false === $results ) { 174 $this->add_response_data( false ); 175 } else { 176 $this->add_response_data( true, $results ); 177 } 178 } catch ( \Exception $e ) { 179 $this->add_response_data( false, $e->getMessage(), $e->getCode() ); 180 } 181 } 182 183 $this->current_action_id = null; 184 185 $this->send_success(); 186 } 187 188 /** 189 * Get current action data. 190 * 191 * Retrieve the data for the current ajax request. 192 * 193 * @since 2.0.1 194 * @access public 195 * 196 * @return bool|mixed Ajax request data if action exist, False otherwise. 197 */ 198 public function get_current_action_data() { 199 if ( ! $this->current_action_id ) { 200 return false; 201 } 202 203 return $this->requests[ $this->current_action_id ]; 204 } 205 206 /** 207 * Create nonce. 208 * 209 * Creates a cryptographic token to 210 * give the user an access to Elementor ajax actions. 211 * 212 * @since 2.3.0 213 * @access public 214 * 215 * @return string The nonce token. 216 */ 217 public function create_nonce() { 218 return wp_create_nonce( self::NONCE_KEY ); 219 } 220 221 /** 222 * Verify request nonce. 223 * 224 * Whether the request nonce verified or not. 225 * 226 * @since 2.3.0 227 * @access public 228 * 229 * @return bool True if request nonce verified, False otherwise. 230 */ 231 public function verify_request_nonce() { 232 return ! empty( $_REQUEST['_nonce'] ) && wp_verify_nonce( $_REQUEST['_nonce'], self::NONCE_KEY ); 233 } 234 235 protected function get_init_settings() { 236 return [ 237 'url' => admin_url( 'admin-ajax.php' ), 238 'nonce' => $this->create_nonce(), 239 ]; 240 } 241 242 /** 243 * Ajax success response. 244 * 245 * Send a JSON response data back to the ajax request, indicating success. 246 * 247 * @since 2.0.0 248 * @access protected 249 */ 250 private function send_success() { 251 $response = [ 252 'success' => true, 253 'data' => [ 254 'responses' => $this->response_data, 255 ], 256 ]; 257 258 $json = wp_json_encode( $response ); 259 260 while ( ob_get_status() ) { 261 ob_end_clean(); 262 } 263 264 if ( function_exists( 'gzencode' ) ) { 265 $response = gzencode( $json ); 266 267 header( 'Content-Type: application/json; charset=utf-8' ); 268 header( 'Content-Encoding: gzip' ); 269 header( 'Content-Length: ' . strlen( $response ) ); 270 271 echo $response; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 272 } else { 273 echo $json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 274 } 275 276 wp_die( '', '', [ 'response' => null ] ); 277 } 278 279 /** 280 * Ajax failure response. 281 * 282 * Send a JSON response data back to the ajax request, indicating failure. 283 * 284 * @since 2.0.0 285 * @access protected 286 * 287 * @param null $code 288 */ 289 private function send_error( $code = null ) { 290 wp_send_json_error( [ 291 'responses' => $this->response_data, 292 ], $code ); 293 } 294 295 /** 296 * Add response data. 297 * 298 * Add new response data to the array of all the ajax requests. 299 * 300 * @since 2.0.0 301 * @access protected 302 * 303 * @param bool $success True if the requests returned successfully, False 304 * otherwise. 305 * @param mixed $data Optional. Response data. Default is null. 306 * 307 * @param int $code Optional. Response code. Default is 200. 308 * 309 * @return Module An instance of ajax manager. 310 */ 311 private function add_response_data( $success, $data = null, $code = 200 ) { 312 $this->response_data[ $this->current_action_id ] = [ 313 'success' => $success, 314 'code' => $code, 315 'data' => $data, 316 ]; 317 318 return $this; 319 } 320 }