class-fl-builder-ajax.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <?php
  2. /**
  3. * Front-end AJAX handler for the builder interface. We use this
  4. * instead of wp_ajax because that only works in the admin and
  5. * certain things like some shortcodes won't render there. AJAX
  6. * requests handled through this method only run for logged in users
  7. * for extra security. Developers creating custom modules that need
  8. * AJAX should use wp_ajax instead.
  9. *
  10. * @since 1.7
  11. */
  12. final class FLBuilderAJAX {
  13. /**
  14. * An array of registered action data.
  15. *
  16. * @since 1.7
  17. * @access private
  18. * @var array $actions
  19. */
  20. static private $actions = array();
  21. /**
  22. * Initializes hooks.
  23. *
  24. * @since 1.8
  25. * @return void
  26. */
  27. static public function init() {
  28. add_action( 'wp', __CLASS__ . '::run' );
  29. }
  30. /**
  31. * Runs builder's frontend AJAX.
  32. *
  33. * @since 1.7
  34. * @return void
  35. */
  36. static public function run() {
  37. self::add_actions();
  38. self::call_action();
  39. }
  40. /**
  41. * Adds a callable AJAX action.
  42. *
  43. * @since 1.7
  44. * @param string $action The action name.
  45. * @param string $method The method to call.
  46. * @param array $args An array of method arg names that are present in the post data.
  47. * @return void
  48. */
  49. static public function add_action( $action, $method, $args = array() ) {
  50. self::$actions[ $action ] = array(
  51. 'action' => $action,
  52. 'method' => $method,
  53. 'args' => $args,
  54. );
  55. }
  56. /**
  57. * Removes an AJAX action.
  58. *
  59. * @since 1.8
  60. * @param string $action The action to remove.
  61. * @return void
  62. */
  63. static public function remove_action( $action ) {
  64. if ( isset( self::$actions[ $action ] ) ) {
  65. unset( self::$actions[ $action ] );
  66. }
  67. }
  68. /**
  69. * Adds all callable AJAX actions.
  70. *
  71. * @since 1.7
  72. * @access private
  73. * @return void
  74. */
  75. static private function add_actions() {
  76. // FLBuilderModel
  77. self::add_action( 'get_node_settings', 'FLBuilderModel::get_node_settings', array( 'node_id' ) );
  78. self::add_action( 'delete_node', 'FLBuilderModel::delete_node', array( 'node_id' ) );
  79. self::add_action( 'delete_col', 'FLBuilderModel::delete_col', array( 'node_id', 'new_width' ) );
  80. self::add_action( 'reorder_node', 'FLBuilderModel::reorder_node', array( 'node_id', 'position' ) );
  81. self::add_action( 'reorder_col', 'FLBuilderModel::reorder_col', array( 'node_id', 'position' ) );
  82. self::add_action( 'move_node', 'FLBuilderModel::move_node', array( 'node_id', 'new_parent', 'position' ) );
  83. self::add_action( 'move_col', 'FLBuilderModel::move_col', array( 'node_id', 'new_parent', 'position', 'resize' ) );
  84. self::add_action( 'resize_cols', 'FLBuilderModel::resize_cols', array( 'col_id', 'col_width', 'sibling_id', 'sibling_width' ) );
  85. self::add_action( 'reset_col_widths', 'FLBuilderModel::reset_col_widths', array( 'group_id' ) );
  86. self::add_action( 'resize_row_content', 'FLBuilderModel::resize_row_content', array( 'node', 'width' ) );
  87. self::add_action( 'save_settings', 'FLBuilderModel::save_settings', array( 'node_id', 'settings' ) );
  88. self::add_action( 'save_layout_settings', 'FLBuilderModel::save_layout_settings', array( 'settings' ) );
  89. self::add_action( 'save_global_settings', 'FLBuilderModel::save_global_settings', array( 'settings' ) );
  90. self::add_action( 'save_color_presets', 'FLBuilderModel::save_color_presets', array( 'presets' ) );
  91. self::add_action( 'duplicate_post', 'FLBuilderModel::duplicate_post' );
  92. self::add_action( 'duplicate_wpml_layout', 'FLBuilderModel::duplicate_wpml_layout', array( 'original_post_id', 'post_id' ) );
  93. self::add_action( 'apply_user_template', 'FLBuilderModel::apply_user_template', array( 'template_id', 'append' ) );
  94. self::add_action( 'apply_template', 'FLBuilderModel::apply_template', array( 'template_id', 'append' ) );
  95. self::add_action( 'save_layout', 'FLBuilderModel::save_layout' );
  96. self::add_action( 'save_draft', 'FLBuilderModel::save_draft' );
  97. self::add_action( 'clear_draft_layout', 'FLBuilderModel::clear_draft_layout' );
  98. self::add_action( 'disable_builder', 'FLBuilderModel::disable' );
  99. self::add_action( 'clear_cache', 'FLBuilderModel::delete_all_asset_cache' );
  100. // FLBuilderAJAXLayout
  101. self::add_action( 'render_layout', 'FLBuilderAJAXLayout::render' );
  102. self::add_action( 'render_node', 'FLBuilderAJAXLayout::render', array( 'node_id' ) );
  103. self::add_action( 'render_new_row', 'FLBuilderAJAXLayout::render_new_row', array( 'cols', 'position', 'template_id', 'template_type' ) );
  104. self::add_action( 'copy_row', 'FLBuilderAJAXLayout::copy_row', array( 'node_id', 'settings', 'settings_id' ) );
  105. self::add_action( 'render_new_column_group', 'FLBuilderAJAXLayout::render_new_column_group', array( 'node_id', 'cols', 'position' ) );
  106. self::add_action( 'render_new_columns', 'FLBuilderAJAXLayout::render_new_columns', array( 'node_id', 'insert', 'type', 'nested' ) );
  107. self::add_action( 'render_new_col_template', 'FLBuilderAJAXLayout::render_new_col_template', array( 'template_id', 'parent_id', 'position', 'template_type' ) );
  108. self::add_action( 'copy_col', 'FLBuilderAJAXLayout::copy_col', array( 'node_id', 'settings', 'settings_id' ) );
  109. self::add_action( 'render_new_module', 'FLBuilderAJAXLayout::render_new_module', array( 'parent_id', 'position', 'type', 'alias', 'template_id', 'template_type' ) );
  110. self::add_action( 'copy_module', 'FLBuilderAJAXLayout::copy_module', array( 'node_id', 'settings' ) );
  111. // FLBuilderUISettingsForms
  112. self::add_action( 'render_legacy_settings', 'FLBuilderUISettingsForms::render_legacy_settings', array( 'data', 'form', 'group', 'lightbox' ) );
  113. self::add_action( 'render_settings_form', 'FLBuilderUISettingsForms::render_settings_form', array( 'type', 'settings' ) );
  114. self::add_action( 'render_icon_selector', 'FLBuilderUISettingsForms::render_icon_selector' );
  115. // FLBuilderRevisions
  116. self::add_action( 'render_revision_preview', 'FLBuilderRevisions::render_preview', array( 'revision_id' ) );
  117. self::add_action( 'restore_revision', 'FLBuilderRevisions::restore', array( 'revision_id' ) );
  118. self::add_action( 'refresh_revision_items', 'FLBuilderRevisions::get_config', array( 'post_id' ) );
  119. // FLBuilderServices
  120. self::add_action( 'render_service_settings', 'FLBuilderServices::render_settings' );
  121. self::add_action( 'render_service_fields', 'FLBuilderServices::render_fields' );
  122. self::add_action( 'connect_service', 'FLBuilderServices::connect_service' );
  123. self::add_action( 'delete_service_account', 'FLBuilderServices::delete_account' );
  124. self::add_action( 'delete_service_account', 'FLBuilderServices::delete_account' );
  125. // FLBuilderAutoSuggest
  126. self::add_action( 'fl_builder_autosuggest', 'FLBuilderAutoSuggest::init' );
  127. self::add_action( 'get_autosuggest_values', 'FLBuilderAutoSuggest::get_values', array( 'fields' ) );
  128. }
  129. /**
  130. * Runs the current AJAX action.
  131. *
  132. * @since 1.7
  133. * @access private
  134. * @return void
  135. */
  136. static private function call_action() {
  137. // Only run for logged in users.
  138. if ( ! is_user_logged_in() ) {
  139. return;
  140. }
  141. // Verify the AJAX nonce.
  142. if ( ! self::verify_nonce() ) {
  143. return;
  144. }
  145. // Get the $_POST data.
  146. $post_data = FLBuilderModel::get_post_data();
  147. // Get the post ID.
  148. $post_id = FLBuilderModel::get_post_id();
  149. // Make sure we have a post ID.
  150. if ( ! $post_id ) {
  151. return;
  152. }
  153. // Make sure the user can edit this post.
  154. if ( ! current_user_can( 'edit_post', $post_id ) ) {
  155. return;
  156. }
  157. // Get the action.
  158. if ( ! empty( $_REQUEST['fl_action'] ) ) {
  159. $action = $_REQUEST['fl_action'];
  160. } elseif ( ! empty( $post_data['fl_action'] ) ) {
  161. $action = $post_data['fl_action'];
  162. } else {
  163. return;
  164. }
  165. /**
  166. * Allow developers to modify actions before they are called.
  167. * @see fl_ajax_before_call_action
  168. */
  169. do_action( 'fl_ajax_before_call_action', $action );
  170. // Make sure the action exists.
  171. if ( ! isset( self::$actions[ $action ] ) ) {
  172. return;
  173. }
  174. // Get the action data.
  175. $action = self::$actions[ $action ];
  176. $args = array();
  177. $keys_args = array();
  178. // Build the args array.
  179. foreach ( $action['args'] as $arg ) {
  180. // @codingStandardsIgnoreLine
  181. $args[] = $keys_args[ $arg ] = isset( $post_data[ $arg ] ) ? $post_data[ $arg ] : null;
  182. }
  183. // Tell WordPress this is an AJAX request.
  184. if ( ! defined( 'DOING_AJAX' ) ) {
  185. define( 'DOING_AJAX', true );
  186. }
  187. /**
  188. * Allow developers to hook before the action runs.
  189. * @see fl_ajax_before_
  190. * @link https://kb.wpbeaverbuilder.com/article/116-plugin-action-reference
  191. */
  192. do_action( 'fl_ajax_before_' . $action['action'], $keys_args );
  193. /**
  194. * Call the action and allow developers to filter the result.
  195. * @see fl_ajax_
  196. */
  197. $result = apply_filters( 'fl_ajax_' . $action['action'], call_user_func_array( $action['method'], $args ), $keys_args );
  198. /**
  199. * Allow developers to hook after the action runs.
  200. * @see fl_ajax_after_
  201. * @link https://kb.wpbeaverbuilder.com/article/116-plugin-action-reference
  202. */
  203. do_action( 'fl_ajax_after_' . $action['action'], $keys_args );
  204. // JSON encode the result.
  205. echo json_encode( $result );
  206. // Complete the request.
  207. die();
  208. }
  209. /**
  210. * Checks to make sure the AJAX nonce is valid.
  211. *
  212. * @since 1.7.2
  213. * @access private
  214. * @return bool
  215. */
  216. static private function verify_nonce() {
  217. $post_data = FLBuilderModel::get_post_data();
  218. $nonce = false;
  219. if ( isset( $post_data['_wpnonce'] ) ) {
  220. $nonce = $post_data['_wpnonce'];
  221. } elseif ( isset( $_REQUEST['_wpnonce'] ) ) {
  222. $nonce = $_REQUEST['_wpnonce'];
  223. }
  224. if ( ! $nonce || ! wp_verify_nonce( $nonce, 'fl_ajax_update' ) ) {
  225. return false;
  226. }
  227. return true;
  228. }
  229. /**
  230. * Is this an AJAX response?
  231. * @since 2.0.7
  232. * @return bool
  233. */
  234. static public function doing_ajax() {
  235. if ( function_exists( 'wp_doing_ajax' ) ) {
  236. return wp_doing_ajax();
  237. }
  238. if ( defined( 'DOING_AJAX' ) ) {
  239. return DOING_AJAX;
  240. }
  241. return false;
  242. }
  243. }
  244. FLBuilderAJAX::init();