/includes/AJAX/Controllers/Submission.php

  1. <?php if ( ! defined( 'ABSPATH' ) ) exit; 
  2.  
  3. class NF_AJAX_Controllers_Submission extends NF_Abstracts_Controller 
  4. protected $_form_data = array(); 
  5.  
  6. protected $_form_cache = array(); 
  7.  
  8. protected $_preview_data = array(); 
  9.  
  10. protected $_form_id = ''; 
  11.  
  12. public function __construct() 
  13. if( isset( $_POST[ 'nf_resume' ] ) && isset( $_COOKIE[ 'nf_wp_session' ] ) ) { 
  14. add_action( 'ninja_forms_loaded', array( $this, 'resume' ) ); 
  15. return; 
  16.  
  17. if( isset( $_POST['formData'] ) ) { 
  18. $this->_form_data = json_decode( $_POST['formData'], TRUE ); 
  19.  
  20. // php5.2 fallback 
  21. if( ! $this->_form_data ) $this->_form_data = json_decode( stripslashes( $_POST['formData'] ), TRUE ); 
  22.  
  23.  
  24. add_action( 'wp_ajax_nf_ajax_submit', array( $this, 'submit' ) ); 
  25. add_action( 'wp_ajax_nopriv_nf_ajax_submit', array( $this, 'submit' ) ); 
  26.  
  27. add_action( 'wp_ajax_nf_ajax_resume', array( $this, 'resume' ) ); 
  28. add_action( 'wp_ajax_nopriv_nf_ajax_resume', array( $this, 'resume' ) ); 
  29.  
  30. public function submit() 
  31. check_ajax_referer( 'ninja_forms_display_nonce', 'security' ); 
  32.  
  33. register_shutdown_function( array( $this, 'shutdown' ) ); 
  34.  
  35. $this->form_data_check(); 
  36.  
  37. $this->_form_id = $this->_form_data['id']; 
  38.  
  39. if( $this->is_preview() ) { 
  40.  
  41. $this->_form_cache = get_user_option( 'nf_form_preview_' . $this->_form_id ); 
  42.  
  43. if( ! $this->_form_cache ) { 
  44. $this->_errors[ 'preview' ] = __( 'Preview does not exist.', 'ninja-forms' ); 
  45. $this->_respond(); 
  46. } else { 
  47. $this->_form_cache = get_option( 'nf_form_' . $this->_form_id ); 
  48.  
  49. // TODO: Update Conditional Logic to preserve field ID => [ Settings, ID ] structure. 
  50. $this->_form_data = apply_filters( 'ninja_forms_submit_data', $this->_form_data ); 
  51.  
  52. $this->process(); 
  53.  
  54. public function resume() 
  55. $this->_form_data = Ninja_Forms()->session()->get( 'nf_processing_form_data' ); 
  56. $this->_form_cache = Ninja_Forms()->session()->get( 'nf_processing_form_cache' ); 
  57. $this->_data = Ninja_Forms()->session()->get( 'nf_processing_data' ); 
  58. $this->_data[ 'resume' ] = $_POST[ 'nf_resume' ]; 
  59.  
  60. $this->_form_id = $this->_data[ 'form_id' ]; 
  61.  
  62. unset( $this->_data[ 'halt' ] ); 
  63. unset( $this->_data[ 'actions' ][ 'redirect' ] ); 
  64.  
  65. $this->process(); 
  66.  
  67. protected function process() 
  68. // Init Field Merge Tags. 
  69. $field_merge_tags = Ninja_Forms()->merge_tags[ 'fields' ]; 
  70. $field_merge_tags->set_form_id( $this->_form_id ); 
  71.  
  72. // Init Calc Merge Tags. 
  73. $calcs_merge_tags = Ninja_Forms()->merge_tags[ 'calcs' ]; 
  74.  
  75. $form_settings = $this->_form_cache[ 'settings' ]; 
  76. if( ! $form_settings ) { 
  77. $form = Ninja_Forms()->form( $this->_form_id )->get(); 
  78. $form_settings = $form->get_settings(); 
  79.  
  80. $this->_data[ 'form_id' ] = $this->_form_data[ 'form_id' ] = $this->_form_id; 
  81. $this->_data[ 'settings' ] = $form_settings; 
  82. $this->_data[ 'settings' ][ 'is_preview' ] = $this->is_preview(); 
  83. $this->_data[ 'extra' ] = $this->_form_data[ 'extra' ]; 
  84.  
  85. /** 
  86. |-------------------------------------------------------------------------- 
  87. | Fields 
  88. |-------------------------------------------------------------------------- 
  89. */ 
  90.  
  91. $form_fields = Ninja_Forms()->form( $this->_form_id )->get_fields(); 
  92.  
  93. /** 
  94. * The Field Processing Loop. 
  95. * 
  96. * There can only be one! 
  97. * For performance reasons, this should be the only time that the fields array is traversed. 
  98. * Anything needing to loop through fields should integrate here. 
  99. */ 
  100. $validate_fields = apply_filters( 'ninja_forms_validate_fields', true, $this->_data ); 
  101. foreach( $form_fields as $key => $field ) { 
  102.  
  103. if( is_object( $field ) ) { 
  104. $field = array( 
  105. 'id' => $field->get_id(),  
  106. 'settings' => $field->get_settings() 
  107. ); 
  108.  
  109. /** Get the field ID */ 
  110. /** 
  111. * TODO: Refactor data structures to match. 
  112. * Preview: Field IDs are stored as the associated array key. 
  113. * Publish: Field IDs are stored as an array key=>value pair. 
  114. */ 
  115. if( $this->is_preview() ) { 
  116. $field[ 'id' ] = $key; 
  117.  
  118. // Duplicate field ID as single variable for more readable array access. 
  119. $field_id = $field[ 'id' ]; 
  120.  
  121. // Check that the field ID exists in the submitted for data and has a submitted value. 
  122. if( isset( $this->_form_data[ 'fields' ][ $field_id ] ) && isset( $this->_form_data[ 'fields' ][ $field_id ][ 'value' ] ) ) { 
  123. $field[ 'value' ] = $this->_form_data[ 'fields' ][ $field_id ][ 'value' ]; 
  124. } else { 
  125. $field[ 'value' ] = ''; 
  126.  
  127. // Duplicate field value to settings and top level array item for backwards compatible access (ie Save Action). 
  128. $field[ 'settings' ][ 'value' ] = $field[ 'value' ]; 
  129.  
  130. // Duplicate field value to form cache for passing to the action filter. 
  131. $this->_form_cache[ 'fields' ][ $key ][ 'settings' ][ 'value' ] = $this->_form_data[ 'fields' ][ $field_id ][ 'value' ]; 
  132.  
  133. // Duplicate the Field ID for access as a setting. 
  134. $field[ 'settings' ][ 'id' ] = $field[ 'id' ]; 
  135.  
  136. // Combine with submitted data. 
  137. $field = array_merge( $field, $this->_form_data[ 'fields' ][ $field_id ] ); 
  138.  
  139. // Flatten the field array. 
  140. $field = array_merge( $field, $field[ 'settings' ] ); 
  141.  
  142. /** Validate the Field */ 
  143. if( $validate_fields && ! isset( $this->_data[ 'resume' ] ) ) { 
  144. $this->validate_field( $field ); 
  145.  
  146. /** Process the Field */ 
  147. if( ! isset( $this->_data[ 'resume' ] ) ) { 
  148. $this->process_field($field); 
  149. $field = array_merge( $field, $this->_form_data[ 'fields' ][ $field_id ] ); 
  150.  
  151. // Check for field errors after processing. 
  152. if ( isset( $this->_form_data['errors']['fields'][ $field_id ] ) ) { 
  153. $this->_errors['fields'][ $field_id ] = $this->_form_data['errors']['fields'][ $field_id ]; 
  154. $this->_respond(); 
  155.  
  156. /** Populate Field Merge Tag */ 
  157. $field_merge_tags->add_field( $field ); 
  158.  
  159. $this->_data[ 'fields' ][ $field_id ] = $field; 
  160.  
  161. /** 
  162. |-------------------------------------------------------------------------- 
  163. | Calculations 
  164. |-------------------------------------------------------------------------- 
  165. */ 
  166.  
  167. if( isset( $this->_form_cache[ 'settings' ][ 'calculations' ] ) ) { 
  168.  
  169. /** 
  170. * The Calculation Processing Loop 
  171. */ 
  172. foreach( $this->_form_cache[ 'settings' ][ 'calculations' ] as $calc ) { 
  173. $eq = apply_filters( 'ninja_forms_calc_setting', $calc[ 'eq' ] ); 
  174. $dec = ( isset( $calc[ 'dec' ] ) && $calc[ 'dec' ] ) ? $calc[ 'dec' ] : 2; 
  175. $calcs_merge_tags->set_merge_tags( $calc[ 'name' ], $eq, $dec ); 
  176. $this->_data[ 'extra' ][ 'calculations' ][ $calc[ 'name' ] ] = array( 
  177. 'raw' => $calc[ 'eq' ],  
  178. 'parsed' => $eq,  
  179. 'value' => $calcs_merge_tags->get_calc_value( $calc[ 'name' ] ),  
  180. ); 
  181.  
  182.  
  183. /** 
  184. |-------------------------------------------------------------------------- 
  185. | Actions 
  186. |-------------------------------------------------------------------------- 
  187. */ 
  188.  
  189. /** 
  190. * TODO: This section has become convoluted, but will be refactored along with the submission controller. 
  191. */ 
  192.  
  193. if( isset( $this->_data[ 'resume' ] ) && $this->_data[ 'resume' ] ) { 
  194. // On Resume Submission, the action data is loaded form the session. 
  195. // This section intentionally left blank. 
  196. } elseif( ! $this->is_preview() ) { 
  197. // Published forms rely on the Database for the "truth" about Actions. 
  198. $actions = Ninja_Forms()->form($this->_form_id)->get_actions(); 
  199. $this->_form_cache[ 'actions' ] = array(); 
  200. foreach( $actions as $action ) { 
  201. $action_id = $action->get_id(); 
  202. $this->_form_cache[ 'actions' ][ $action_id ] = array( 
  203. 'id' => $action_id,  
  204. 'settings' => $action->get_settings() 
  205. ); 
  206. } else { 
  207. // Previews uses user option for stored data. 
  208. $preview_data = get_user_option( 'nf_form_preview_' . $this->_form_id ); 
  209. $this->_form_cache[ 'actions' ] = $preview_data[ 'actions' ]; 
  210. /** END form cache bypass. */ 
  211.  
  212. // Sort Actions by Timing Order, then by Priority Order. 
  213. usort( $this->_form_cache[ 'actions' ], array( $this, 'sort_form_actions' ) ); 
  214.  
  215. /** 
  216. * Filter Actions so that they can be pragmatically disabled by add-ons. 
  217. * 
  218. * ninja_forms_submission_actions 
  219. * ninja_forms_submission_actions_preview 
  220. */ 
  221. $this->_form_cache[ 'actions' ] = apply_filters( 'ninja_forms_submission_actions', $this->_form_cache[ 'actions' ], $this->_form_cache, $this->_form_data ); 
  222. if( $this->is_preview() ) { 
  223. $this->_form_cache['actions'] = apply_filters('ninja_forms_submission_actions_preview', $this->_form_cache['actions'], $this->_form_cache); 
  224.  
  225. // Initialize the process actions log. 
  226. if( ! isset( $this->_data[ 'processed_actions' ] ) ) $this->_data[ 'processed_actions' ] = array(); 
  227.  
  228. /** 
  229. * The Action Processing Loop 
  230. */ 
  231. foreach( $this->_form_cache[ 'actions' ] as $key => $action ) { 
  232.  
  233. /** Get the action ID */ 
  234. /** 
  235. * TODO: Refactor data structures to match. 
  236. * Preview: Action IDs are stored as the associated array key. 
  237. * Publish: Action IDs are stored as an array key=>value pair. 
  238. */ 
  239. if( $this->is_preview() ) { 
  240. $action[ 'id' ] = $key; 
  241.  
  242. // Duplicate the Action ID for access as a setting. 
  243. $action[ 'settings' ][ 'id' ] = $action[ 'id' ]; 
  244.  
  245. // Duplicate action ID as single variable for more readable array access. 
  246. $action_id = $action[ 'id' ]; 
  247.  
  248. // If an action has already run (ie resume submission), do not re-process. 
  249. if( in_array( $action[ 'id' ], $this->_data[ 'processed_actions' ] ) ) continue; 
  250.  
  251. $action[ 'settings' ] = apply_filters( 'ninja_forms_run_action_settings', $action[ 'settings' ], $this->_form_id, $action[ 'id' ], $this->_form_data['settings'] ); 
  252. if( $this->is_preview() ) { 
  253. $action[ 'settings' ] = apply_filters( 'ninja_forms_run_action_settings_preview', $action[ 'settings' ], $this->_form_id, $action[ 'id' ], $this->_form_data['settings'] ); 
  254.  
  255. if( ! $action[ 'settings' ][ 'active' ] ) continue; 
  256.  
  257. if( ! apply_filters( 'ninja_forms_run_action_type_' . $action[ 'settings' ][ 'type' ], TRUE ) ) continue; 
  258.  
  259. $type = $action[ 'settings' ][ 'type' ]; 
  260.  
  261. if( ! is_string( $type ) ) continue; 
  262.  
  263. $action_class = Ninja_Forms()->actions[ $type ]; 
  264.  
  265. if( ! method_exists( $action_class, 'process' ) ) continue; 
  266.  
  267. if( $data = $action_class->process($action[ 'settings' ], $this->_form_id, $this->_data ) ) { 
  268. $this->_data = apply_filters( 'ninja_forms_post_run_action_type_' . $action[ 'settings' ][ 'type' ], $data ); 
  269.  
  270. // $this->_data[ 'actions' ][ $type ][] = $action; 
  271.  
  272. $this->maybe_halt( $action[ 'id' ] ); 
  273.  
  274. do_action( 'ninja_forms_after_submission', $this->_data ); 
  275.  
  276. $this->_respond(); 
  277.  
  278. protected function validate_field( $field_settings ) 
  279. $field_settings = apply_filters( 'ninja_forms_pre_validate_field_settings', $field_settings ); 
  280.  
  281. if( ! is_string( $field_settings['type'] ) ) return; 
  282.  
  283. $field_class = Ninja_Forms()->fields[ $field_settings['type'] ]; 
  284.  
  285. if( ! method_exists( $field_class, 'validate' ) ) return; 
  286.  
  287. if( $errors = $field_class->validate( $field_settings, $this->_form_data ) ) { 
  288. $field_id = $field_settings[ 'id' ]; 
  289. $this->_errors[ 'fields' ][ $field_id ] = $errors; 
  290. $this->_respond(); 
  291.  
  292. protected function process_field( $field_settings ) 
  293. if( ! is_string( $field_settings['type'] ) ) return; 
  294.  
  295. $field_class = Ninja_Forms()->fields[ $field_settings['type'] ]; 
  296.  
  297. if( ! method_exists( $field_class, 'process' ) ) return; 
  298.  
  299. if( $data = $field_class->process( $field_settings, $this->_form_data ) ) { 
  300. $this->_form_data = $data; 
  301.  
  302. protected function maybe_halt( $action_id ) 
  303. if( isset( $this->_data[ 'errors' ] ) && $this->_data[ 'errors' ] ) { 
  304. $this->_respond(); 
  305.  
  306. if( isset( $this->_data[ 'halt' ] ) && $this->_data[ 'halt' ] ) { 
  307.  
  308. Ninja_Forms()->session()->set( 'nf_processing_data', $this->_data ); 
  309. Ninja_Forms()->session()->set( 'nf_processing_form_data', $this->_form_data ); 
  310. Ninja_Forms()->session()->set( 'nf_processing_form_cache', $this->_form_cache ); 
  311.  
  312. $this->_respond(); 
  313.  
  314. array_push( $this->_data[ 'processed_actions' ], $action_id ); 
  315.  
  316. protected function sort_form_actions( $a, $b ) 
  317. if( is_object( $a ) ) { 
  318. if( ! isset( Ninja_Forms()->actions[ $a->get_setting( 'type' ) ] ) ) return -1; 
  319. $a = Ninja_Forms()->actions[ $a->get_setting( 'type' ) ]; 
  320. } else { 
  321. if( ! isset( Ninja_Forms()->actions[ $a[ 'settings' ][ 'type' ] ] ) ) return -1; 
  322. $a = Ninja_Forms()->actions[ $a[ 'settings' ][ 'type' ] ]; 
  323.  
  324. if( is_object( $b ) ) { 
  325. if( ! isset( Ninja_Forms()->actions[ $b->get_setting( 'type' ) ] ) ) return 1; 
  326. $b = Ninja_Forms()->actions[ $b->get_setting( 'type' ) ]; 
  327. } else { 
  328. if( ! isset( Ninja_Forms()->actions[ $b[ 'settings' ][ 'type' ] ] ) ) return 1; 
  329. $b = Ninja_Forms()->actions[ $b[ 'settings' ][ 'type' ] ]; 
  330.  
  331. if ( $a->get_timing() == $b->get_timing() ) { 
  332. if ( $a->get_priority() == $b->get_priority() ) { 
  333. return 0; 
  334. return ( $a->get_priority() < $b->get_priority() ) ? -1 : 1; 
  335.  
  336. return ( $a->get_timing() < $b->get_timing() ) ? -1 : 1; 
  337.  
  338. public function shutdown() 
  339. $error = error_get_last(); 
  340. if( $error !== NULL && in_array( $error[ 'type' ], array( E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ) ) ) { 
  341.  
  342. $this->_errors[ 'form' ][ 'last' ] = __( 'The server encountered an error during processing.', 'ninja-forms' ); 
  343.  
  344. if( current_user_can( 'manage_options' ) && isset( $error[ 'message' ] ) ) { 
  345. $this->_errors[ 'form' ][ 'last_admin' ] = '<pre>' . $error[ 'message' ] . '</pre>'; 
  346.  
  347. $this->_errors[ 'last' ] = $error; 
  348. $this->_respond(); 
  349.  
  350. protected function form_data_check() 
  351. if( $this->_form_data ) return; 
  352.  
  353. if( function_exists( 'json_last_error' ) // Function not supported in php5.2 
  354. && function_exists( 'json_last_error_msg' )// Function not supported in php5.2 
  355. && json_last_error() ) { 
  356. $this->_errors[] = json_last_error_msg(); 
  357. } else { 
  358. $this->_errors[] = __( 'An unexpected error occurred.', 'ninja-forms' ); 
  359.  
  360. $this->_respond(); 
  361.  
  362. protected function is_preview() 
  363. if( ! isset( $this->_form_data[ 'settings' ][ 'is_preview' ] ) ) return false; 
  364. return $this->_form_data[ 'settings' ][ 'is_preview' ]; 
.