Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/acf-frontend-form-element/acf-frontend.php
+++ b/acf-frontend-form-element/acf-frontend.php
@@ -3,7 +3,7 @@
* Plugin Name: Frontend Admin
* Plugin URI: https://www.dynamiapps.com/frontend-admin/
* Description: This awesome plugin allows you to easily display admin forms to the frontend of your site so your clients can easily edit content on their own from the frontend.
- * Version: 3.29.9
+ * Version: 3.29.10
* Author: Shabti Kaplan
* Author URI: https://www.dynamiapps.com/
* Text Domain: frontend-admin
--- a/acf-frontend-form-element/main/elementor/widgets/general/submit-button.php
+++ b/acf-frontend-form-element/main/elementor/widgets/general/submit-button.php
@@ -190,8 +190,9 @@
'label' => __( 'Submit Type', 'frontend-admin' ),
'type' => Controls_Manager::SELECT,
'options' => array(
- 'submit' => __( 'Submit', 'frontend-admin' ),
- 'save' => __( 'Save Progress', 'frontend-admin' ),
+ 'submit' => __( 'Submit', 'frontend-admin' ),
+ 'save' => __( 'Save Progress', 'frontend-admin' ),
+ 'preview' => __( 'Preview', 'frontend-admin' ),
),
'default' => 'submit',
)
--- a/acf-frontend-form-element/main/frontend/fields/general/class-preview-button.php
+++ b/acf-frontend-form-element/main/frontend/fields/general/class-preview-button.php
@@ -0,0 +1,114 @@
+<?php
+namespace Frontend_AdminField_Types;
+
+if ( ! class_exists( 'preview_button' ) ) :
+
+ class preview_button extends Field_Base {
+
+
+
+ /*
+ * __construct
+ *
+ * This function will setup the field type data
+ *
+ * @type function
+ * @date 5/03/2014
+ * @since 5.0.0
+ *
+ * @param n/a
+ * @return n/a
+ */
+
+ function initialize() {
+ // vars
+ $this->name = 'preview_button';
+ $this->label = __( 'Preview Button', 'frontend-admin' );
+ $this->category = __( 'Form', 'frontend-admin' );
+ $this->defaults = array(
+ 'button_text' => __( 'Preview', 'frontend-admin' ),
+ 'field_label_hide' => 1,
+ );
+
+ }
+
+
+ /*
+ * render_field()
+ *
+ * Create the HTML interface for your field
+ *
+ * @param $field - an array holding all the field's data
+ *
+ * @type action
+ * @since 3.6
+ * @date 23/01/13
+ */
+
+ function render_field( $field ) {
+ // vars
+ $m = '<button type="button" class="fea-submit-button button button-primary" data-state="preview">' . $field['button_text'] . '</button>';
+
+ // wptexturize (improves "quotes")
+ $m = wptexturize( $m );
+
+ echo wp_kses_post( $m );
+ }
+
+
+ /*
+ * load_field()
+ *
+ * This filter is appied to the $field after it is loaded from the database
+ *
+ * @type filter
+ * @since 3.6
+ * @date 23/01/13
+ *
+ * @param $field - the field array holding all the field options
+ *
+ * @return $field - the field array holding all the field options
+ */
+ function load_field( $field ) {
+ // remove name to avoid caching issue
+ $field['name'] = '';
+
+ // remove instructions
+ $field['instructions'] = '';
+
+ // remove required to avoid JS issues
+ $field['required'] = 0;
+
+ // set value other than 'null' to avoid ACF loading / caching issue
+ $field['value'] = false;
+
+ $field['field_label_hide'] = 1;
+
+ if ( empty( $field['button_text'] ) ) {
+ $field['button_text'] = $field['label'];
+ }
+
+ // return
+ return $field;
+ }
+
+ function render_field_settings( $field ) {
+ acf_render_field_setting(
+ $field,
+ array(
+ 'label' => __( 'Button Text', 'frontend-admin' ),
+ 'type' => 'text',
+ 'name' => 'button_text',
+ 'class' => 'update-label',
+ )
+ );
+ }
+
+ }
+
+
+
+
+endif; // class_exists check
+
+
--- a/acf-frontend-form-element/main/frontend/fields/post/class-post-status.php
+++ b/acf-frontend-form-element/main/frontend/fields/post/class-post-status.php
@@ -32,7 +32,12 @@
function pre_update_value( $checked, $value, $post_id, $field ) {
if( $this->name !== $field['type'] ){
return $checked;
- }if ( $post_id && is_numeric( $post_id ) ) {
+ }
+ if ( ! empty( $GLOBALS['admin_form']['is_preview'] ) ) {
+ // previews must stay drafts - don't let a status field publish the post
+ return true;
+ }
+ if ( $post_id && is_numeric( $post_id ) ) {
$post_to_edit = array(
'ID' => $post_id,
);
--- a/acf-frontend-form-element/main/frontend/fields/user/class-user-password.php
+++ b/acf-frontend-form-element/main/frontend/fields/user/class-user-password.php
@@ -142,6 +142,11 @@
if ( ! empty( $user[1] ) ) {
$user_id = $user[1];
+
+ if ( ! current_user_can( 'edit_user', $user_id ) ) {
+ return true;
+ }
+
remove_action( 'acf/save_post', '_acf_do_save_post' );
wp_update_user(
array(
--- a/acf-frontend-form-element/main/frontend/forms/actions/post.php
+++ b/acf-frontend-form-element/main/frontend/forms/actions/post.php
@@ -1085,8 +1085,22 @@
}
}
+ if ( ! empty( $form['is_preview'] ) ) {
+ // a preview may never publish: ignore any submitted status field
+ unset( $post_to_edit['post_status'] );
+ if ( empty( $old_status ) || in_array( $old_status, array( 'auto-draft', 'draft' ), true ) ) {
+ $record['status'] = 'draft';
+ }
+ }
+
+ $was_preview_draft = false;
+ if ( empty( $form['is_preview'] ) && is_numeric( $post_id ) && get_post_meta( $post_id, '_fea_preview_draft', true ) ) {
+ $was_preview_draft = true;
+ delete_post_meta( $post_id, '_fea_preview_draft' );
+ }
+
if ( empty( $post_to_edit['post_status'] ) ) {
-
+
if ( isset( $record['status'] ) && $record['status'] == 'draft' ) {
$post_to_edit['post_status'] = 'draft';
} else {
@@ -1096,9 +1110,12 @@
$post_to_edit['post_status'] = $status;
} elseif ( empty( $old_status ) || $old_status == 'auto-draft' ) {
$post_to_edit['post_status'] = 'publish';
+ } elseif ( $was_preview_draft && 'draft' == $old_status ) {
+ // draft only existed because of a preview - treat like a new post
+ $post_to_edit['post_status'] = 'publish';
}
}
-
+
}
$form = $this->save_post( $form, $post_to_edit, $metas, $post_to_duplicate );
@@ -1210,10 +1227,18 @@
if ( ! current_user_can( 'edit_post', $post_id ) ) {
- if( ! in_array( 'edit_posts', $condition['special_permissions'] ) ){
+ if ( ! empty( fea_instance()->form_preview ) && fea_instance()->form_preview->is_own_preview_draft( $post_id, $settings ) ) {
+ return $settings;
+ }
+ // map_meta_cap requires the post type's blanket edit_posts capability even
+ // for editing one's own post; roles with no native content capabilities
+ // (e.g. WooCommerce Customer) still own the post they authored, so allow
+ // that case without requiring the broader "Edit Other's Posts" permission.
+ $is_author = get_post_field( 'post_author', $post_id ) == $user->ID;
+ if ( ! $is_author && ! in_array( 'edit_posts', $condition['special_permissions'] ) ){
$settings['post_id'] = 'none';
}
- }
+ }
return $settings;
}
--- a/acf-frontend-form-element/main/frontend/forms/actions/user.php
+++ b/acf-frontend-form-element/main/frontend/forms/actions/user.php
@@ -433,6 +433,16 @@
// allow for custom save
$user_id = apply_filters( 'acf/pre_save_user', $user_id, $form );
+ // 'add_user' is the only legitimate non-numeric target (new user creation);
+ // anything else must resolve to a real user id the current requester can edit,
+ // independent of the show_form/special_permissions gating that ran earlier.
+ if ( 'add_user' !== $user_id ) {
+ if ( ! is_numeric( $user_id ) || ! current_user_can( 'edit_user', (int) $user_id ) ) {
+ return $form;
+ }
+ $user_id = (int) $user_id;
+ }
+
$username_generated = false;
$user_to_insert = array();
$metas = array();
@@ -677,7 +687,14 @@
public function conditions_logic( $settings, $condition, $user ){
$user_id = $settings['user_id'] ?? 'none';
+ // 'none' and 'add_user' are the only legitimate non-numeric states;
+ // anything else is unexpected input and must not bypass the capability check below.
+ if ( in_array( $user_id, array( 'none', 'add_user' ), true ) ) {
+ return $settings;
+ }
+
if( ! is_numeric( $user_id ) ){
+ $settings['user_id'] = 'none';
return $settings;
}
--- a/acf-frontend-form-element/main/frontend/forms/classes/display.php
+++ b/acf-frontend-form-element/main/frontend/forms/classes/display.php
@@ -1650,14 +1650,17 @@
}
if ( $request['item_id'] ) {
- $type = $request['type'];
- $form[ $type . '_id' ] = $request['item_id'];
- if ( $form[ $type . '_id' ] ) {
- if ( is_numeric( $form[ $type . '_id' ] ) ) {
- $form[ 'save_to_' . $type ] = 'edit_' . $type;
- } else {
- $form[ 'save_to_' . $type ] = 'new_' . $type;
- }
+ $type = $request['type'];
+
+ // only trust item_id as an object id when it's actually numeric; a non-numeric
+ // value (or garbage crafted to look like one, e.g. "1one") must not be stored
+ // as-is, since downstream code loosely casts ids and would coerce it to an
+ // unintended integer id instead of triggering the "new" flow.
+ if ( is_numeric( $request['item_id'] ) ) {
+ $form[ $type . '_id' ] = absint( $request['item_id'] );
+ $form[ 'save_to_' . $type ] = 'edit_' . $type;
+ } else {
+ $form[ 'save_to_' . $type ] = 'new_' . $type;
}
if( $field && 'post' == $type ){
--- a/acf-frontend-form-element/main/frontend/forms/classes/permissions.php
+++ b/acf-frontend-form-element/main/frontend/forms/classes/permissions.php
@@ -231,20 +231,19 @@
if( empty( $condition['special_permissions'] ) || ! is_array( $condition['special_permissions'] ) ){
$condition['special_permissions'] = [];
- }
-
- $settings = apply_filters( 'frontend_admin/special_permissions', $settings, $condition, $active_user );
+ }
+
+ if ( $settings['display'] ) {
+ $settings = apply_filters( 'frontend_admin/special_permissions', $settings, $condition, $active_user );
+ $settings['special_permissions'] = $condition['special_permissions'];
+ return $settings;
+ }
if ( $condition['not_allowed'] == 'show_message' ) {
$settings['message'] = '<div class="acf-notice -limit frontend-admin-limit-message"><p>' . esc_html( $condition['not_allowed_message'] ) . '</p></div>';
} elseif ( $condition['not_allowed'] == 'custom_content' ) {
$settings['message'] = wp_kses_post( $condition['not_allowed_content'] );
}
-
- if ( $settings['display'] ) {
- $settings['special_permissions'] = $condition['special_permissions'];
- break;
- }
}
if ( empty( $settings['display'] ) ) {
--- a/acf-frontend-form-element/main/frontend/forms/classes/preview.php
+++ b/acf-frontend-form-element/main/frontend/forms/classes/preview.php
@@ -0,0 +1,120 @@
+<?php
+namespace Frontend_AdminClasses;
+
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly.
+}
+
+if ( ! class_exists( 'Frontend_AdminClassesForm_Preview' ) ) :
+
+ class Form_Preview {
+
+ /**
+ * How long a preview link stays valid, in seconds.
+ *
+ * @var int
+ */
+ public $expiration = HOUR_IN_SECONDS;
+
+ public function get_token( $post_id, $expiry ) {
+ return hash_hmac( 'sha256', 'fea_preview|' . absint( $post_id ) . '|' . absint( $expiry ), wp_salt( 'auth' ) );
+ }
+
+ /**
+ * Build a signed, expiring URL that lets anyone (including guests)
+ * view an unpublished post in the theme template.
+ *
+ * @param int $post_id The draft/pending post to preview.
+ * @return string
+ */
+ public function get_preview_url( $post_id ) {
+ $expiry = time() + apply_filters( 'frontend_admin/form/preview_expiration', $this->expiration );
+
+ $args = array(
+ 'p' => $post_id,
+ 'preview' => 'true',
+ '_fea_preview' => $this->get_token( $post_id, $expiry ),
+ '_fea_exp' => $expiry,
+ );
+
+ $post_type = get_post_type( $post_id );
+ if ( 'page' === $post_type ) {
+ unset( $args['p'] );
+ $args['page_id'] = $post_id;
+ } elseif ( 'post' !== $post_type ) {
+ $args['post_type'] = $post_type;
+ }
+
+ $url = add_query_arg( $args, home_url( '/' ) );
+
+ return apply_filters( 'frontend_admin/form/submission_preview', $url, $GLOBALS['admin_form'] ?? array() );
+ }
+
+ /**
+ * Whether the current visitor may keep editing an unpublished draft that
+ * only exists because they previewed this form - covers guests and users
+ * without the edit_post capability. The post id can only reach the form
+ * through the encrypted _acf_objects blob minted by return_preview().
+ *
+ * @param int $post_id
+ * @param array $form
+ * @return bool
+ */
+ public function is_own_preview_draft( $post_id, $form ) {
+ if ( empty( $form['id'] ) ) {
+ return false;
+ }
+ if ( get_post_meta( $post_id, '_fea_preview_draft', true ) != $form['id'] ) {
+ return false;
+ }
+ if ( ! in_array( get_post_status( $post_id ), array( 'draft', 'pending' ), true ) ) {
+ return false;
+ }
+ return (int) get_post_field( 'post_author', $post_id ) === (int) get_current_user_id();
+ }
+
+ /**
+ * Let a request carrying a valid preview token view an unpublished post.
+ *
+ * Runs before WP_Query's singular status/404 check, so flipping the
+ * in-memory status to publish is enough — no capability grant, no DB write.
+ *
+ * @param array $posts
+ * @param WP_Query $query
+ * @return array
+ */
+ public function show_draft( $posts, $query ) {
+ if ( empty( $_GET['_fea_preview'] ) || empty( $_GET['_fea_exp'] ) ) {
+ return $posts;
+ }
+ if ( ! $query->is_main_query() || ! $query->is_singular() || count( $posts ) !== 1 ) {
+ return $posts;
+ }
+
+ $post = $posts[0];
+ $expiry = absint( $_GET['_fea_exp'] );
+
+ if ( time() > $expiry ) {
+ return $posts;
+ }
+ if ( ! in_array( $post->post_status, array( 'draft', 'pending' ), true ) ) {
+ return $posts;
+ }
+ if ( ! hash_equals( $this->get_token( $post->ID, $expiry ), (string) $_GET['_fea_preview'] ) ) {
+ return $posts;
+ }
+
+ $post->post_status = 'publish';
+ nocache_headers();
+
+ return $posts;
+ }
+
+ public function __construct() {
+ add_filter( 'posts_results', array( $this, 'show_draft' ), 10, 2 );
+ }
+ }
+
+ fea_instance()->form_preview = new Form_Preview();
+
+endif;
--- a/acf-frontend-form-element/main/frontend/forms/classes/submit.php
+++ b/acf-frontend-form-element/main/frontend/forms/classes/submit.php
@@ -35,6 +35,10 @@
foreach ( $_post as $source => $fields ) {
$source = fea_decrypt( $source );
+ if ( ! $this->current_user_can_edit_object( $source ) ) {
+ continue;
+ }
+
foreach ( $fields as $key => $value ) {
$field = $fea_instance->frontend->get_field( $key );
if( ! $field ) continue;
@@ -87,6 +91,36 @@
wp_send_json_success( $json );
}
+ /**
+ * Checks whether the current user may edit the object a decrypted
+ * inline-update source resolves to. The source string comes from a
+ * client-supplied token, so it must be re-authorized here rather than
+ * trusted as-is.
+ */
+ public function current_user_can_edit_object( $source ) {
+ if ( empty( $source ) ) {
+ return false;
+ }
+
+ if ( is_numeric( $source ) ) {
+ return current_user_can( 'edit_post', $source );
+ }
+
+ if ( ! is_string( $source ) ) {
+ return false;
+ }
+
+ if ( 0 === strpos( $source, 'user_' ) ) {
+ return current_user_can( 'edit_user', substr( $source, 5 ) );
+ }
+
+ if ( 0 === strpos( $source, 'term_' ) ) {
+ return current_user_can( 'edit_term', substr( $source, 5 ) );
+ }
+
+ return false;
+ }
+
public function check_submit_form() {
@@ -112,11 +146,20 @@
}
+ // permissions.php echoes a not-allowed message as a side effect when denying
+ // access; buffer and discard it here so it can't leak raw HTML in front of
+ // the JSON response body below and break the front-end's JSON.parse().
+ ob_start();
$form = apply_filters( 'frontend_admin/show_form', $form );
+ ob_end_clean();
if( empty( $form['display'] ) ) {
wp_send_json_error( __( 'You do not have permission to submit this form.', 'frontend-admin' ) );
}
-
+
+ $form['is_preview'] = isset( $_POST['_acf_status'] ) && 'preview' == $_POST['_acf_status'];
+
+ error_log( '[FEA_PREVIEW_DEBUG] check_submit_form: _acf_status=' . ( $_POST['_acf_status'] ?? '<unset>' ) . ' is_preview=' . var_export( $form['is_preview'], true ) . ' form_id=' . $form['id'] );
+
// submit
$this->submit_form( $form );
@@ -387,6 +430,14 @@
$form['submission_status'] = 'approved';
$form = $this->should_save_content( $form );
+
+ error_log( '[FEA_PREVIEW_DEBUG] submit_form: is_preview=' . var_export( ! empty( $form['is_preview'] ), true ) . ' save_data=' . var_export( $form['save_data'] ?? null, true ) . ' save_to_post=' . ( $form['save_to_post'] ?? '<unset>' ) . ' post_id=' . var_export( $form['post_id'] ?? null, true ) );
+
+ if ( ! empty( $form['is_preview'] ) ) {
+ $form = $this->prepare_preview( $form );
+ error_log( '[FEA_PREVIEW_DEBUG] after prepare_preview: record.status=' . var_export( $form['record']['status'] ?? null, true ) . ' new_post_status=' . var_export( $form['new_post_status'] ?? null, true ) . ' new_product_status=' . var_export( $form['new_product_status'] ?? null, true ) );
+ }
+
foreach ( $fea_instance->local_actions as $name => $action ) {
if ( $name != 'options' && isset( $form[ "{$name}_id" ] ) ) {
$form['record'][ $name ] = $form[ "{$name}_id" ];
@@ -397,12 +448,118 @@
}
$fea_form = $form;
-
+
+ error_log( '[FEA_PREVIEW_DEBUG] after local actions: record=' . wp_json_encode( $form['record'] ?? null ) );
+
$form = $this->run_actions( $form );
$this->return_form( $form );
}
+ /**
+ * Field group key each local action actually checks before writing - mirrors the
+ * bail conditions in ActionPost::run() / ActionProduct::run() / etc. A type's
+ * save_to_$type/{$type}_id can be non-empty (e.g. "current_post" defaulting to
+ * whatever page hosts the form) even though the submission never touches it.
+ */
+ public function preview_active_types( $form ) {
+ $fields_key = array(
+ 'post' => 'post',
+ 'user' => 'user',
+ 'term' => 'term',
+ 'product' => 'woo_product',
+ );
+ $active = array();
+ foreach ( $fields_key as $type => $key ) {
+ if ( ! empty( $form['record']['fields'][ $key ] ) ) {
+ $active[] = $type;
+ }
+ }
+ return $active;
+ }
+
+ public function prepare_preview( $form ) {
+ $active_types = $this->preview_active_types( $form );
+ foreach ( array( 'post', 'product' ) as $type ) {
+ if ( ! in_array( $type, $active_types, true ) ) {
+ continue;
+ }
+ $save_to = $form[ "save_to_$type" ] ?? '';
+ if ( ! $save_to && empty( $form[ "{$type}_id" ] ) ) {
+ continue;
+ }
+ if ( "duplicate_$type" == $save_to ) {
+ // duplicating creates a new object; the (possibly published) source is untouched
+ $form['record']['status'] = 'draft';
+ continue;
+ }
+ // key off the resolved target id, not save_to: a form rehydrated from
+ // _acf_objects keeps save_to "new_*" while the id is already numeric
+ $target = $form[ "{$type}_id" ] ?? '';
+ if ( ! is_numeric( $target ) && ! empty( $form['record'][ $type ] ) ) {
+ $target = $form['record'][ $type ];
+ }
+ if ( is_numeric( $target ) ) {
+ if ( 'publish' == get_post_status( $target ) ) {
+ wp_send_json_error( __( 'Preview is only available for unpublished content.', 'frontend-admin' ) );
+ }
+ // keep the current draft/pending status instead of applying the form's configured status
+ $form[ "new_{$type}_status" ] = 'no_change';
+ } else {
+ $form['record']['status'] = 'draft';
+ }
+ }
+ return $form;
+ }
+
+ public function return_preview( $form ) {
+ global $fea_instance;
+
+ $active_types = $this->preview_active_types( $form );
+
+ $preview_id = false;
+ foreach ( array( 'product', 'post' ) as $type ) {
+ if ( in_array( $type, $active_types, true ) && ! empty( $form['record'][ $type ] ) && is_numeric( $form['record'][ $type ] ) ) {
+ $preview_id = $form['record'][ $type ];
+ break;
+ }
+ }
+
+ error_log( '[FEA_PREVIEW_DEBUG] return_preview: preview_id=' . var_export( $preview_id, true ) . ' active_types=' . implode( ',', $active_types ) . ' record=' . wp_json_encode( $form['record'] ?? null ) );
+
+ if ( ! $preview_id ) {
+ wp_send_json_error( __( 'Nothing to preview.', 'frontend-admin' ) );
+ }
+
+ $objects = array();
+ foreach ( array( 'post', 'user', 'term', 'product' ) as $type ) {
+ if ( in_array( $type, $active_types, true ) && ! empty( $form['record'][ $type ] ) && is_numeric( $form['record'][ $type ] ) ) {
+ if ( in_array( $type, array( 'post', 'product' ), true ) && "edit_$type" != ( $form[ "save_to_$type" ] ?? '' ) ) {
+ // remember this draft only exists because of a preview, so the
+ // real submit can still apply the form's new-object status logic
+ update_post_meta( $form['record'][ $type ], '_fea_preview_draft', $form['id'] );
+ }
+ $form[ $type . '_id' ] = $form['record'][ $type ];
+ $form[ "save_to_$type" ] = "edit_$type";
+ $objects[ $type ] = $form['record'][ $type ];
+ }
+ }
+ if ( ! empty( $form['submission'] ) ) {
+ $objects['submission'] = $form['submission'];
+ }
+
+ $response = array(
+ 'location' => 'current',
+ 'form_element' => $form['id'],
+ 'objects' => fea_encrypt( json_encode( $objects ) ),
+ 'preview' => $fea_instance->form_preview->get_preview_url( $preview_id ),
+ );
+
+ do_action( 'frontend_admin/form/after_preview', $form, $response );
+
+ wp_send_json_success( $response );
+ }
+
public function run_actions( $form ) {
global $fea_instance;
$run_actions = apply_filters( 'frontend_admin/form/run_actions', true, $form );
@@ -423,7 +580,7 @@
if ( ! empty( $remote_actions ) ) {
- if ( empty( $form['approval'] ) ) {
+ if ( empty( $form['approval'] ) && empty( $form['is_preview'] ) ) {
if ( ! empty( $form['submit_actions'] ) ) {
foreach ( $remote_actions as $name => $action ) {
$action->run( $form );
@@ -480,6 +637,10 @@
$form = apply_filters( 'frontend_admin/form/return', $form );
+ if ( ! empty( $form['is_preview'] ) ) {
+ $this->return_preview( $form );
+ }
+
if( ! empty( $_POST['redirect'] ) ){
$form['redirect'] = $_POST['redirect'];
}
--- a/acf-frontend-form-element/main/frontend/module.php
+++ b/acf-frontend-form-element/main/frontend/module.php
@@ -477,11 +477,11 @@
$basic_settings = array( 'name', 'instructions', 'required', 'wrapper', 'frontend_admin_display_mode', 'field_label_hide', 'only_front' );
foreach( $basic_settings as $setting ){
$setting = $setting;
- echo ".acf-field-object-form-step .acf-field-setting-{$setting}, .acf-field-object-submit-button .acf-field-setting-{$setting}, .acf-field-object-save-progress .acf-field-setting-{$setting}, .acf-field-object-fields-select .acf-field-setting-{$setting}{display:none}";
+ echo ".acf-field-object-form-step .acf-field-setting-{$setting}, .acf-field-object-submit-button .acf-field-setting-{$setting}, .acf-field-object-save-progress .acf-field-setting-{$setting}, .acf-field-object-preview-button .acf-field-setting-{$setting}, .acf-field-object-fields-select .acf-field-setting-{$setting}{display:none}";
echo ".acf-field-object-form-step .acf-field-setting-{$setting}, .acf-field-object-save-progress .acf-field-setting-{$setting}, .acf-field-object-fields-select .acf-field-setting-{$setting}{display:none}";
}
- echo '.acf-field-object-form-step .acf-field-setting-custom_fields_save, .acf-field-object-submit-button .acf-field-setting-custom_fields_save, .acf-field-object-save-progress .acf-field-setting-custom_fields_save{display:none}';
- echo '.acf-field-object-form-step[data-step="1"] .acf-field-setting-prev_button_text,.acf-field-object-form-step .acf-field-setting-name,.acf-field-object-submit-button .acf-field-setting-label,.acf-field-object-save-progress .acf-field-setting-label,.acf-field-object-delete-post .acf-field-setting-label,.acf-field-object-delete-term .acf-field-setting-label,.acf-field-object-delete-user .acf-field-setting-label,.acf-field-object-delete-product .acf-field-setting-label,.acf-field-object-custom-terms .acf-field-setting-ui{display:none}';
+ echo '.acf-field-object-form-step .acf-field-setting-custom_fields_save, .acf-field-object-submit-button .acf-field-setting-custom_fields_save, .acf-field-object-save-progress .acf-field-setting-custom_fields_save, .acf-field-object-preview-button .acf-field-setting-custom_fields_save{display:none}';
+ echo '.acf-field-object-form-step[data-step="1"] .acf-field-setting-prev_button_text,.acf-field-object-form-step .acf-field-setting-name,.acf-field-object-submit-button .acf-field-setting-label,.acf-field-object-save-progress .acf-field-setting-label,.acf-field-object-preview-button .acf-field-setting-label,.acf-field-object-delete-post .acf-field-setting-label,.acf-field-object-delete-term .acf-field-setting-label,.acf-field-object-delete-user .acf-field-setting-label,.acf-field-object-delete-product .acf-field-setting-label,.acf-field-object-custom-terms .acf-field-setting-ui{display:none}';
echo '</style>';
}
@@ -516,6 +516,7 @@
'related-items',
'submit-button',
'save-progress',
+ 'preview-button',
'time',
'date',
'color',
@@ -645,6 +646,7 @@
include_once __DIR__ . '/forms/classes/limit-submit.php';
include_once __DIR__ . '/forms/classes/permissions.php';
+ include_once __DIR__ . '/forms/classes/preview.php';
include_once __DIR__ . '/forms/classes/shortcodes.php';
include_once __DIR__ . '/forms/actions/action-base.php';
--- a/acf-frontend-form-element/main/plugin.php
+++ b/acf-frontend-form-element/main/plugin.php
@@ -45,6 +45,7 @@
public $form_display = null;
public $form_actions = null;
public $form_validate = null;
+ public $form_preview = null;
//form actions
public $local_actions = array();
@@ -103,7 +104,7 @@
define( 'FEA_URL', $data['plugin_url'] );
define( 'FEA_DIR', $data['plugin_dir'] );
define( 'FEA_PLUGIN', $data['plugin'] );
- define( 'FEA_VERSION', '3.29.9' );
+ define( 'FEA_VERSION', '3.29.10' );
do_action( 'front_end_admin_loaded' );
// Add tutorial videos to plugin item on plugins page