增强套件,可改进"高级自定义字段"管理
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

729 lines
23 KiB

  1. <?php
  2. if(!defined('ABSPATH'))
  3. exit;
  4. if(!class_exists('acfe_form_front')):
  5. class acfe_form_front{
  6. function __construct(){
  7. // vars
  8. $this->fields = array(
  9. '_validate_email' => array(
  10. 'prefix' => 'acf',
  11. 'name' => '_validate_email',
  12. 'key' => '_validate_email',
  13. 'label' => __('Validate Email', 'acf'),
  14. 'type' => 'text',
  15. 'value' => '',
  16. 'wrapper' => array('style' => 'display:none !important;')
  17. )
  18. );
  19. // Submit
  20. add_action('wp', array($this, 'check_submit_form'));
  21. // Shortcode
  22. add_shortcode('acfe_form', array($this, 'add_shortcode'));
  23. // Validation
  24. add_action('acf/validate_save_post', array($this, 'validate_save_post'), 1);
  25. }
  26. function validate_save_post(){
  27. if(!acfe_form_is_front())
  28. return;
  29. if(acf_maybe_get_POST('_acf_screen') !== 'acfe_form')
  30. return;
  31. $form = acfe_form_decrypt_args();
  32. if(!$form)
  33. return;
  34. $post_id = acf_maybe_get($form, 'post_id', false);
  35. $form_name = acf_maybe_get($form, 'name');
  36. $form_id = acf_maybe_get($form, 'ID');
  37. if(!$form_name || !$form_id)
  38. return;
  39. foreach($this->fields as $k => $field){
  40. // bail early if no in $_POST
  41. if(!isset($_POST['acf'][ $k ]))
  42. continue;
  43. // register
  44. acf_add_local_field($field);
  45. }
  46. // Honeypot
  47. if(!empty($acf['_validate_email'])){
  48. acf_add_validation_error('', __('Spam Detected', 'acf'));
  49. }
  50. // Validation
  51. acf_setup_meta($_POST['acf'], 'acfe_form_validation', true);
  52. // Actions
  53. if(have_rows('acfe_form_actions', $form_id)):
  54. while(have_rows('acfe_form_actions', $form_id)): the_row();
  55. $action = get_row_layout();
  56. $alias = get_sub_field('acfe_form_custom_alias');
  57. // Custom Action
  58. if($action === 'custom'){
  59. $action = get_sub_field('acfe_form_custom_action');
  60. $alias = '';
  61. }
  62. do_action('acfe/form/validation/' . $action, $form, $post_id, $alias);
  63. do_action('acfe/form/validation/' . $action . '/form=' . $form_name, $form, $post_id, $alias);
  64. if(!empty($alias))
  65. do_action('acfe/form/validation/' . $action . '/action=' . $alias, $form, $post_id, $alias);
  66. endwhile;
  67. endif;
  68. do_action('acfe/form/validation', $form, $post_id);
  69. do_action('acfe/form/validation/form=' . $form_name, $form, $post_id);
  70. acf_reset_meta('acfe_form_validation');
  71. }
  72. function check_submit_form(){
  73. // Verify nonce.
  74. if(!acf_verify_nonce('acfe_form'))
  75. return;
  76. $form = acfe_form_decrypt_args();
  77. if(!$form)
  78. return;
  79. // ACF
  80. $_POST['acf'] = isset($_POST['acf']) ? $_POST['acf'] : array();
  81. // Run kses on all $_POST data.
  82. if($form['kses'] && isset($_POST['acf'])){
  83. $_POST['acf'] = wp_kses_post_deep($_POST['acf']);
  84. }
  85. // Validate data and show errors.
  86. acf_validate_save_post(true);
  87. // Submit form.
  88. $this->submit_form($form);
  89. }
  90. function submit_form($form){
  91. // vars
  92. $post_id = acf_maybe_get($form, 'post_id', false);
  93. $form_name = acf_maybe_get($form, 'name');
  94. $form_id = acf_maybe_get($form, 'ID');
  95. // Upload
  96. acf_save_post(false);
  97. // Unset Files
  98. if(isset($_FILES))
  99. unset($_FILES);
  100. /*
  101. * Fix Elementor + YOAST infinite loop
  102. *
  103. * https://github.com/elementor/elementor/issues/10998
  104. * https://github.com/Yoast/wordpress-seo/issues/14643
  105. */
  106. remove_shortcode('acfe_form');
  107. acf_setup_meta($_POST['acf'], 'acfe_form_submit', true);
  108. // Actions
  109. if(have_rows('acfe_form_actions', $form_id)):
  110. while(have_rows('acfe_form_actions', $form_id)): the_row();
  111. $action = get_row_layout();
  112. $alias = get_sub_field('acfe_form_custom_alias');
  113. do_action('acfe/form/make/' . $action, $form, $post_id, $alias);
  114. endwhile;
  115. endif;
  116. do_action('acfe/form/submit', $form, $post_id);
  117. do_action('acfe/form/submit/form=' . $form_name, $form, $post_id);
  118. acf_reset_meta('acfe_form_submit');
  119. add_shortcode('acfe_form', array($this, 'add_shortcode'));
  120. // vars
  121. $return = acf_maybe_get($form, 'return', '');
  122. // redirect
  123. if($return){
  124. _deprecated_function('ACF Extended - Dynamic Forms: "Redirection" setting', '0.8.7.5', "the new Redirect Action (See documentation: https://www.acf-extended.com/features/modules/dynamic-forms)");
  125. $return = acfe_form_map_field_value($return, $post_id, $form);
  126. // redirect
  127. wp_redirect($return);
  128. exit;
  129. }
  130. }
  131. function validate_form($param){
  132. $form_id = false;
  133. $form_name = false;
  134. $param_array = array();
  135. if(is_array($param)){
  136. $param_array = $param;
  137. if(acf_maybe_get($param, 'id')){
  138. $param = acf_maybe_get($param, 'id');
  139. }elseif(acf_maybe_get($param, 'ID')){
  140. $param = acf_maybe_get($param, 'ID');
  141. }elseif(acf_maybe_get($param, 'name')){
  142. $param = acf_maybe_get($param, 'name');
  143. }else{
  144. return false;
  145. }
  146. }
  147. // ID
  148. if(is_numeric($param)){
  149. if(get_post_type($param) !== 'acfe-form')
  150. return false;
  151. // Form
  152. $form_id = $param;
  153. $form_name = get_field('acfe_form_name', $form_id);
  154. }
  155. // Name
  156. elseif(is_string($param)){
  157. $form = get_page_by_path($param, OBJECT, 'acfe-form');
  158. if(!$form)
  159. return false;
  160. // Form
  161. $form_id = $form->ID;
  162. $form_name = get_field('acfe_form_name', $form_id);
  163. }
  164. // Bail early
  165. if(!$form_name || !$form_id)
  166. return false;
  167. // Unset
  168. acfe_unset($param_array, 'id');
  169. acfe_unset($param_array, 'ID');
  170. acfe_unset($param_array, 'name');
  171. // Form Attributes
  172. $form_attributes = get_field('acfe_form_attributes', $form_id);
  173. $fields_attributes = get_field('acfe_form_fields_attributes', $form_id);
  174. // Defaults
  175. $defaults = array(
  176. // General
  177. 'ID' => $form_id,
  178. 'name' => $form_name,
  179. 'title' => get_the_title($form_id),
  180. // Settings
  181. 'post_id' => acf_get_valid_post_id(),
  182. 'field_groups' => get_field('acfe_form_field_groups', $form_id),
  183. 'field_groups_rules' => get_field('acfe_form_field_groups_rules', $form_id),
  184. 'post_field_groups' => get_field('acfe_form_post_field_groups', $form_id), // Deprecated
  185. 'form' => get_field('acfe_form_form_element', $form_id),
  186. 'html_before_fields' => get_field('acfe_form_html_before_fields', $form_id),
  187. 'custom_html_enabled' => get_field('acfe_form_custom_html_enable', $form_id),
  188. 'custom_html' => get_field('acfe_form_custom_html', $form_id),
  189. 'html_after_fields' => get_field('acfe_form_html_after_fields', $form_id),
  190. 'form_submit' => get_field('acfe_form_form_submit', $form_id),
  191. 'submit_value' => get_field('acfe_form_submit_value', $form_id),
  192. 'html_submit_button' => get_field('acfe_form_html_submit_button', $form_id),
  193. 'html_submit_spinner' => get_field('acfe_form_html_submit_spinner', $form_id),
  194. // Submission
  195. 'hide_error' => get_field('acfe_form_hide_error', $form_id),
  196. 'hide_unload' => get_field('acfe_form_hide_unload', $form_id),
  197. 'hide_revalidation' => get_field('acfe_form_hide_revalidation', $form_id),
  198. 'errors_position' => get_field('acfe_form_errors_position', $form_id),
  199. 'errors_class' => get_field('acfe_form_errors_class', $form_id),
  200. 'updated_message' => get_field('acfe_form_updated_message', $form_id),
  201. 'html_updated_message' => get_field('acfe_form_html_updated_message', $form_id),
  202. 'updated_hide_form' => get_field('acfe_form_updated_hide_form', $form_id),
  203. 'return' => get_field('acfe_form_return', $form_id), // Deprecated
  204. // Advanced
  205. 'honeypot' => get_field('acfe_form_honeypot', $form_id),
  206. 'kses' => get_field('acfe_form_kses', $form_id),
  207. 'uploader' => get_field('acfe_form_uploader', $form_id),
  208. 'field_el' => get_field('acfe_form_form_field_el', $form_id),
  209. 'label_placement' => get_field('acfe_form_label_placement', $form_id),
  210. 'instruction_placement' => get_field('acfe_form_instruction_placement', $form_id),
  211. // Mapping
  212. 'map' => array(),
  213. // Form Attributes
  214. 'form_attributes' => array(
  215. 'id' => acf_maybe_get($form_attributes, 'acfe_form_attributes_id'),
  216. 'class' => 'acfe-form ' . acf_maybe_get($form_attributes, 'acfe_form_attributes_class'),
  217. 'action' => '',
  218. 'method' => 'post',
  219. 'data-fields-class' => '',
  220. 'data-hide-error' => '',
  221. 'data-hide-unload' => '',
  222. 'data-hide-revalidation'=> '',
  223. 'data-errors-position' => '',
  224. 'data-errors-class' => '',
  225. ),
  226. // Fields Attributes
  227. 'fields_attributes' => array(
  228. 'wrapper_class' => acf_maybe_get($fields_attributes, 'acfe_form_fields_wrapper_class'),
  229. 'class' => acf_maybe_get($fields_attributes, 'acfe_form_fields_class'),
  230. ),
  231. );
  232. // Override
  233. $args = wp_parse_args($param_array, $defaults);
  234. if(acf_maybe_get($param_array, 'form_attributes'))
  235. $args['form_attributes'] = wp_parse_args($param_array['form_attributes'], $defaults['form_attributes']);
  236. if(acf_maybe_get($param_array, 'fields_attributes'))
  237. $args['fields_attributes'] = wp_parse_args($param_array['fields_attributes'], $defaults['fields_attributes']);
  238. // Advanced Override
  239. $args['form_attributes']['data-fields-class'] = $args['fields_attributes']['class'];
  240. $args['form_attributes']['data-hide-error'] = $args['hide_error'];
  241. $args['form_attributes']['data-hide-unload'] = $args['hide_unload'];
  242. $args['form_attributes']['data-hide-revalidation'] = $args['hide_revalidation'];
  243. $args['form_attributes']['data-errors-position'] = $args['errors_position'];
  244. $args['form_attributes']['data-errors-class'] = $args['errors_class'];
  245. if(acf_maybe_get_POST('acf')){
  246. acf_setup_meta($_POST['acf'], 'acfe_form_load', true);
  247. }
  248. // Args
  249. $args = apply_filters('acfe/form/load', $args, $args['post_id']);
  250. $args = apply_filters('acfe/form/load/form=' . $form_name, $args, $args['post_id']);
  251. // Load
  252. if(have_rows('acfe_form_actions', $form_id)):
  253. while(have_rows('acfe_form_actions', $form_id)): the_row();
  254. $action = get_row_layout();
  255. $alias = get_sub_field('acfe_form_custom_alias');
  256. // Custom Action
  257. if($action === 'custom'){
  258. $action = get_sub_field('acfe_form_custom_action');
  259. $alias = '';
  260. }
  261. $args = apply_filters('acfe/form/load/' . $action, $args, $args['post_id'], $alias);
  262. $args = apply_filters('acfe/form/load/' . $action . '/form=' . $form_name, $args, $args['post_id'], $alias);
  263. if(!empty($alias))
  264. $args = apply_filters('acfe/form/load/' . $action . '/action=' . $alias, $args, $args['post_id'], $alias);
  265. endwhile;
  266. endif;
  267. if(acf_maybe_get_POST('acf')){
  268. acf_reset_meta('acfe_form_load');
  269. }
  270. return $args;
  271. }
  272. /*
  273. * ACFE Form: render_form
  274. *
  275. */
  276. function render_form($args = array()){
  277. $args = $this->validate_form($args);
  278. // bail early if no args
  279. if(!$args)
  280. return false;
  281. // load acf scripts
  282. acf_enqueue_scripts();
  283. // Register local fields.
  284. foreach($this->fields as $k => $field){
  285. acf_add_local_field($field);
  286. }
  287. // honeypot
  288. if($args['honeypot']){
  289. $fields[] = acf_get_field('_validate_email');
  290. }
  291. // Updated message
  292. if(acfe_is_form_success($args['name'])){
  293. // Trigger Success JS
  294. echo '<div class="acfe-form-success" data-form-name="' . $args['name'] . '" data-form-id="' . $args['ID'] . '"></div>';
  295. if(!empty($args['updated_message'])){
  296. $message = $args['updated_message'];
  297. if(acf_maybe_get_POST('acf')){
  298. $message = acfe_form_map_field_value($args['updated_message'], $args['post_id'], $args);
  299. }
  300. if(!empty($args['html_updated_message'])){
  301. printf($args['html_updated_message'], wp_unslash($message));
  302. }else{
  303. echo $message;
  304. }
  305. }
  306. // Hide form
  307. if($args['updated_hide_form']){
  308. return false;
  309. }
  310. }
  311. if(!empty($args['fields_attributes']['wrapper_class']) || !empty($args['fields_attributes']['class']) || $args['label_placement'] === 'hidden'){
  312. add_filter('acf/prepare_field', function($field) use($args){
  313. if(!$field)
  314. return $field;
  315. if(!empty($args['fields_attributes']['wrapper_class']))
  316. $field['wrapper']['class'] .= ' ' . $args['fields_attributes']['wrapper_class'];
  317. if(!empty($args['fields_attributes']['class']))
  318. $field['class'] .= ' ' . $args['fields_attributes']['class'];
  319. if($args['label_placement'] === 'hidden')
  320. $field['label'] = false;
  321. return $field;
  322. });
  323. }
  324. if(!empty($args['map'])){
  325. foreach($args['map'] as $field_key => $array){
  326. add_filter('acf/prepare_field/key=' . $field_key, function($field) use($array){
  327. if(!$field)
  328. return $field;
  329. $field = array_merge($field, $array);
  330. return $field;
  331. });
  332. }
  333. }
  334. // uploader (always set incase of multiple forms on the page)
  335. acf_disable_filter('acfe/form/uploader');
  336. if($args['uploader'] !== 'default'){
  337. acf_enable_filter('acfe/form/uploader');
  338. acf_update_setting('uploader', $args['uploader']);
  339. }
  340. $wrapper = $args['form'] ? 'form' : 'div';
  341. ?>
  342. <<?php echo $wrapper; ?> <?php acf_esc_attr_e($args['form_attributes']); ?>>
  343. <?php
  344. // render post data
  345. acf_form_data(array(
  346. 'screen' => 'acfe_form',
  347. 'post_id' => $args['post_id'],
  348. 'form' => acf_encrypt(json_encode($args))
  349. ));
  350. $label_placement = false;
  351. if($args['label_placement'] !== 'hidden')
  352. $label_placement = '-' . $args['label_placement'];
  353. ?>
  354. <div class="acf-fields acf-form-fields <?php echo $label_placement; ?>">
  355. <?php
  356. // html before fields
  357. echo $args['html_before_fields'];
  358. // Custom HTML
  359. if(!empty($args['custom_html_enabled']) && !empty($args['custom_html'])){
  360. echo acfe_form_render_fields($args['custom_html'], $args['post_id'], $args);
  361. }
  362. // Normal Render
  363. else{
  364. // vars
  365. $field_groups = array();
  366. $fields = array();
  367. // Post Field groups (Deprecated)
  368. if($args['post_field_groups']){
  369. // Override Field Groups
  370. $post_field_groups = acf_get_field_groups(array(
  371. 'post_id' => $args['post_field_groups']
  372. ));
  373. $args['field_groups'] = wp_list_pluck($post_field_groups, 'key');
  374. }
  375. // Field groups
  376. if($args['field_groups']){
  377. foreach($args['field_groups'] as $selector){
  378. // Bypass Author Module
  379. if($selector === 'group_acfe_author')
  380. continue;
  381. $field_groups[] = acf_get_field_group($selector);
  382. }
  383. }
  384. // Apply Field Groups Rules
  385. if($args['field_groups_rules']){
  386. if(!empty($field_groups)){
  387. $post_id = get_the_ID();
  388. $filter = array(
  389. 'post_id' => $post_id,
  390. 'post_type' => get_post_type($post_id),
  391. );
  392. $filtered = array();
  393. foreach($field_groups as $field_group){
  394. // Deleted field group
  395. if(!isset($field_group['location']))
  396. continue;
  397. // Force active
  398. $field_group['active'] = true;
  399. if(acf_get_field_group_visibility($field_group, $filter)){
  400. $filtered[] = $field_group;
  401. }
  402. }
  403. $field_groups = $filtered;
  404. }
  405. }
  406. //load fields based on field groups
  407. if(!empty($field_groups)){
  408. foreach($field_groups as $field_group){
  409. $field_group_fields = acf_get_fields($field_group);
  410. if(!empty($field_group_fields)){
  411. foreach(array_keys($field_group_fields) as $i){
  412. $fields[] = acf_extract_var($field_group_fields, $i);
  413. }
  414. }
  415. }
  416. }
  417. acf_render_fields($fields, acf_uniqid('acfe_form'), $args['field_el'], $args['instruction_placement']);
  418. }
  419. // html after fields
  420. echo $args['html_after_fields'];
  421. ?>
  422. </div>
  423. <?php if($args['form_submit']): ?>
  424. <div class="acf-form-submit">
  425. <?php printf($args['html_submit_button'], $args['submit_value']); ?>
  426. <?php echo $args['html_submit_spinner']; ?>
  427. </div>
  428. <?php endif; ?>
  429. </<?php echo $wrapper; ?>>
  430. <?php
  431. return false;
  432. }
  433. function add_shortcode($atts){
  434. if(is_admin())
  435. return false;
  436. $atts = shortcode_atts(array(
  437. 'name' => false,
  438. 'id' => false,
  439. 'ID' => false,
  440. ), $atts, 'acfe_form');
  441. if(!empty($atts['name'])){
  442. ob_start();
  443. acfe_form($atts['name']);
  444. return ob_get_clean();
  445. }
  446. if(!empty($atts['id'])){
  447. ob_start();
  448. acfe_form($atts['id']);
  449. return ob_get_clean();
  450. }
  451. if(!empty($atts['ID'])){
  452. ob_start();
  453. acfe_form($atts['ID']);
  454. return ob_get_clean();
  455. }
  456. return false;
  457. }
  458. }
  459. acfe()->form_front = new acfe_form_front();
  460. endif;
  461. function acfe_form($args = array()){
  462. acfe()->form_front->render_form($args);
  463. }