<?php
/**
 * @package ACF
 * @author  WP Engine
 *
 * © 2025 Advanced Custom Fields (ACF®). All rights reserved.
 * "ACF" is a trademark of WP Engine.
 * Licensed under the GNU General Public License v2 or later.
 * https://www.gnu.org/licenses/gpl-2.0.html
 */

if ( ! class_exists( 'acf_form_widget' ) ) :
	class acf_form_widget {

		/**
		 * Preview values.
		 * @var array
		 */
		public $preview_values = array();

		/**
		 * Preview reference.
		 * @var array
		 */
		public $preview_reference = array();

		/**
		 * Preview errors.
		 * @var array
		 */
		public $preview_errors = array();

		/**
		 * This function will setup the class functionality
		 *
		 * @type    function
		 * @date    5/03/2014
		 * @since   5.0.0
		 *
		 * @param   n/a
		 * @return  n/a
		 */
		function __construct() {

			// actions
			add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
			add_action( 'in_widget_form', array( $this, 'edit_widget' ), 10, 3 );
			add_action( 'acf/validate_save_post', array( $this, 'acf_validate_save_post' ), 5 );

			// filters
			add_filter( 'widget_update_callback', array( $this, 'save_widget' ), 10, 4 );
		}


		/**
		 * This action is run after post query but before any admin script / head actions.
		 * It is a good place to register all actions.
		 *
		 * @type    action (admin_enqueue_scripts)
		 * @date    26/01/13
		 * @since   3.6.0
		 *
		 * @param   N/A
		 * @return  N/A
		 */
		function admin_enqueue_scripts() {

			// validate screen
			if ( acf_is_screen( 'widgets' ) || acf_is_screen( 'customize' ) ) {

				// valid
			} else {
				return;
			}

			// load acf scripts
			acf_enqueue_scripts();

			// actions
			add_action( 'acf/input/admin_footer', array( $this, 'admin_footer' ), 1 );
		}


		/**
		 * This function will loop over $_POST data and validate
		 *
		 * @type  action 'acf/validate_save_post' 5
		 * @since 5.4.0
		 */
		public function acf_validate_save_post() {
			// phpcs:disable WordPress.Security.NonceVerification.Missing -- Verified elsewhere.
			// bail early if not widget
			if ( ! isset( $_POST['_acf_widget_id'] ) ) {
				return;
			}

			// vars
			$id     = sanitize_text_field( wp_unslash( $_POST['_acf_widget_id'] ) );
			$number = acf_maybe_get_POST( '_acf_widget_number' );
			$prefix = acf_maybe_get_POST( '_acf_widget_prefix' );
			$values = ! empty( $_POST[ $id ][ $number ]['acf'] ) ? acf_sanitize_request_args( $_POST[ $id ][ $number ]['acf'] ) : ''; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- unslash not required.

			// validate
			acf_validate_values( $values, $prefix );
			// phpcs:enable WordPress.Security.NonceVerification.Missing
		}


		/**
		 * This function will render the fields for a widget form
		 *
		 * @type    function
		 * @date    11/06/2014
		 * @since   5.0.0
		 *
		 * @param   $widget (object)
		 * @param   $return (null)
		 * @param   $instance (object)
		 * @return  $post_id (int)
		 */
		function edit_widget( $widget, $return, $instance ) {

			// vars
			$post_id = 0;
			$prefix  = 'widget-' . $widget->id_base . '[' . $widget->number . '][acf]';

			// get id
			if ( $widget->number !== '__i__' ) {
				$post_id = "widget_{$widget->id}";
			}

			// get field groups
			$field_groups = acf_get_field_groups(
				array(
					'widget' => $widget->id_base,
				)
			);

			// render
			if ( ! empty( $field_groups ) ) {

				// render post data
				acf_form_data(
					array(
						'screen'        => 'widget',
						'post_id'       => $post_id,
						'widget_id'     => 'widget-' . $widget->id_base,
						'widget_number' => $widget->number,
						'widget_prefix' => $prefix,
					)
				);

				// wrap
				echo '<div class="acf-widget-fields acf-fields -clear">';

				// loop
				foreach ( $field_groups as $field_group ) {

						// load fields
						$fields = acf_get_fields( $field_group );

						// bail if not fields
					if ( empty( $fields ) ) {
						continue;
					}

						// change prefix
						acf_prefix_fields( $fields, $prefix );

						// render
						acf_render_fields( $fields, $post_id, 'div', $field_group['instruction_placement'] );
				}

				// wrap
				echo '</div>';

				// jQuery selector looks odd, but is necessary due to WP adding an incremental number into the ID
				// - not possible to find number via PHP parameters
				if ( $widget->updated ) : ?>
			<script type="text/javascript">
			(function($) {

				acf.doAction('append', $('[id^="widget"][id$="<?php echo esc_attr( $widget->id ); ?>"]') );

			})(jQuery);	
			</script>
					<?php
			endif;
			}
		}


		/**
		 * This function will hook into the widget update filter and save ACF data
		 *
		 * @type    function
		 * @date    27/05/2015
		 * @since   5.2.3
		 *
		 * @param   $instance (array) widget settings
		 * @param   $new_instance (array) widget settings
		 * @param   $old_instance (array) widget settings
		 * @param   $widget (object) widget info
		 * @return  $instance
		 */
		function save_widget( $instance, $new_instance, $old_instance, $widget ) {

			// validate nonce if we're not a REST API request.
			// the $_POST object is not available to us to validate if we're in a REST API call.
			if ( ! ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) ) {
				if ( ! acf_verify_nonce( 'widget' ) ) {
					return $instance;
				}
			}

			// bail early if not valid (!customize + acf values + nonce).
			if ( isset( $_POST['wp_customize'] ) || ! isset( $new_instance['acf'] ) ) {
				return $instance;
			}

			// save
			acf_save_post( "widget_{$widget->id}", $new_instance['acf'] );

			// return
			return $instance;
		}


		/**
		 * This function will add some custom HTML to the footer of the edit page
		 *
		 * @type    function
		 * @date    11/06/2014
		 * @since   5.0.0
		 *
		 * @param   n/a
		 * @return  n/a
		 */
		function admin_footer() {
			?>
<script type="text/javascript">
(function($) {

	// vars
	acf.set('post_id', 'widgets');

	// Only initialize visible fields.
	acf.addFilter('find_fields', function( $fields ){

		// not templates
		$fields = $fields.not('#available-widgets .acf-field');

		// not widget dragging in
		$fields = $fields.not('.widget.ui-draggable-dragging .acf-field');

		// return
		return $fields;
	});

	// on publish
	$('#widgets-right').on('click', '.widget-control-save', function( e ){

		// vars
		var $button = $(this);
		var $form = $button.closest('form');

		// validate
		var valid = acf.validateForm({
			form: $form,
			event: e,
			reset: true
		});

		// if not valid, stop event and allow validation to continue
		if( !valid ) {
			e.preventDefault();
			e.stopImmediatePropagation();
		}
	});

	// show
	$('#widgets-right').on('click', '.widget-top', function(){
		var $widget = $(this).parent();
		if( $widget.hasClass('open') ) {
			acf.doAction('hide', $widget);
		} else {
			acf.doAction('show', $widget);
		}
	});

	$(document).on('widget-added', function( e, $widget ){

		// - use delay to avoid rendering issues with customizer (ensures div is visible)
		setTimeout(function(){
			acf.doAction('append', $widget );
		}, 100);
	});

})(jQuery);	
</script>
			<?php
		}
	}

	new acf_form_widget();
endif;

?>
