form-widget.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /*
  3. * ACF Widget Form Class
  4. *
  5. * All the logic for adding fields to widgets
  6. *
  7. * @class acf_form_widget
  8. * @package ACF
  9. * @subpackage Forms
  10. */
  11. if ( ! class_exists( 'acf_form_widget' ) ) :
  12. class acf_form_widget {
  13. /*
  14. * __construct
  15. *
  16. * This function will setup the class functionality
  17. *
  18. * @type function
  19. * @date 5/03/2014
  20. * @since 5.0.0
  21. *
  22. * @param n/a
  23. * @return n/a
  24. */
  25. function __construct() {
  26. // vars
  27. $this->preview_values = array();
  28. $this->preview_reference = array();
  29. $this->preview_errors = array();
  30. // actions
  31. add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
  32. add_action( 'in_widget_form', array( $this, 'edit_widget' ), 10, 3 );
  33. add_action( 'acf/validate_save_post', array( $this, 'acf_validate_save_post' ), 5 );
  34. // filters
  35. add_filter( 'widget_update_callback', array( $this, 'save_widget' ), 10, 4 );
  36. }
  37. /*
  38. * admin_enqueue_scripts
  39. *
  40. * This action is run after post query but before any admin script / head actions.
  41. * It is a good place to register all actions.
  42. *
  43. * @type action (admin_enqueue_scripts)
  44. * @date 26/01/13
  45. * @since 3.6.0
  46. *
  47. * @param N/A
  48. * @return N/A
  49. */
  50. function admin_enqueue_scripts() {
  51. // validate screen
  52. if ( acf_is_screen( 'widgets' ) || acf_is_screen( 'customize' ) ) {
  53. // valid
  54. } else {
  55. return;
  56. }
  57. // load acf scripts
  58. acf_enqueue_scripts();
  59. // actions
  60. add_action( 'acf/input/admin_footer', array( $this, 'admin_footer' ), 1 );
  61. }
  62. /*
  63. * acf_validate_save_post
  64. *
  65. * This function will loop over $_POST data and validate
  66. *
  67. * @type action 'acf/validate_save_post' 5
  68. * @date 7/09/2016
  69. * @since 5.4.0
  70. *
  71. * @param n/a
  72. * @return n/a
  73. */
  74. function acf_validate_save_post() {
  75. // phpcs:disable WordPress.Security.NonceVerification.Missing -- Verified elsewhere.
  76. // bail early if not widget
  77. if ( ! isset( $_POST['_acf_widget_id'] ) ) {
  78. return;
  79. }
  80. // vars
  81. $id = sanitize_text_field( $_POST['_acf_widget_id'] );
  82. $number = sanitize_text_field( $_POST['_acf_widget_number'] );
  83. $prefix = sanitize_text_field( $_POST['_acf_widget_prefix'] );
  84. $values = acf_sanitize_request_args( $_POST[ $id ][ $number ]['acf'] );
  85. // validate
  86. acf_validate_values( $values, $prefix );
  87. // phpcs:enable WordPress.Security.NonceVerification.Missing
  88. }
  89. /*
  90. * edit_widget
  91. *
  92. * This function will render the fields for a widget form
  93. *
  94. * @type function
  95. * @date 11/06/2014
  96. * @since 5.0.0
  97. *
  98. * @param $widget (object)
  99. * @param $return (null)
  100. * @param $instance (object)
  101. * @return $post_id (int)
  102. */
  103. function edit_widget( $widget, $return, $instance ) {
  104. // vars
  105. $post_id = 0;
  106. $prefix = 'widget-' . $widget->id_base . '[' . $widget->number . '][acf]';
  107. // get id
  108. if ( $widget->number !== '__i__' ) {
  109. $post_id = "widget_{$widget->id}";
  110. }
  111. // get field groups
  112. $field_groups = acf_get_field_groups(
  113. array(
  114. 'widget' => $widget->id_base,
  115. )
  116. );
  117. // render
  118. if ( ! empty( $field_groups ) ) {
  119. // render post data
  120. acf_form_data(
  121. array(
  122. 'screen' => 'widget',
  123. 'post_id' => $post_id,
  124. 'widget_id' => 'widget-' . $widget->id_base,
  125. 'widget_number' => $widget->number,
  126. 'widget_prefix' => $prefix,
  127. )
  128. );
  129. // wrap
  130. echo '<div class="acf-widget-fields acf-fields -clear">';
  131. // loop
  132. foreach ( $field_groups as $field_group ) {
  133. // load fields
  134. $fields = acf_get_fields( $field_group );
  135. // bail if not fields
  136. if ( empty( $fields ) ) {
  137. continue;
  138. }
  139. // change prefix
  140. acf_prefix_fields( $fields, $prefix );
  141. // render
  142. acf_render_fields( $fields, $post_id, 'div', $field_group['instruction_placement'] );
  143. }
  144. // wrap
  145. echo '</div>';
  146. // jQuery selector looks odd, but is necessary due to WP adding an incremental number into the ID
  147. // - not possible to find number via PHP parameters
  148. if ( $widget->updated ) : ?>
  149. <script type="text/javascript">
  150. (function($) {
  151. acf.doAction('append', $('[id^="widget"][id$="<?php echo $widget->id; ?>"]') );
  152. })(jQuery);
  153. </script>
  154. <?php
  155. endif;
  156. }
  157. }
  158. /*
  159. * save_widget
  160. *
  161. * This function will hook into the widget update filter and save ACF data
  162. *
  163. * @type function
  164. * @date 27/05/2015
  165. * @since 5.2.3
  166. *
  167. * @param $instance (array) widget settings
  168. * @param $new_instance (array) widget settings
  169. * @param $old_instance (array) widget settings
  170. * @param $widget (object) widget info
  171. * @return $instance
  172. */
  173. function save_widget( $instance, $new_instance, $old_instance, $widget ) {
  174. // validate nonce if we're not a REST API request.
  175. // the $_POST object is not available to us to validate if we're in a REST API call.
  176. if ( ! ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) ) {
  177. if ( ! acf_verify_nonce( 'widget' ) ) {
  178. return $instance;
  179. }
  180. }
  181. // bail early if not valid (!customize + acf values + nonce).
  182. if ( isset( $_POST['wp_customize'] ) || ! isset( $new_instance['acf'] ) ) {
  183. return $instance;
  184. }
  185. // save
  186. acf_save_post( "widget_{$widget->id}", $new_instance['acf'] );
  187. // return
  188. return $instance;
  189. }
  190. /*
  191. * admin_footer
  192. *
  193. * This function will add some custom HTML to the footer of the edit page
  194. *
  195. * @type function
  196. * @date 11/06/2014
  197. * @since 5.0.0
  198. *
  199. * @param n/a
  200. * @return n/a
  201. */
  202. function admin_footer() {
  203. ?>
  204. <script type="text/javascript">
  205. (function($) {
  206. // vars
  207. acf.set('post_id', 'widgets');
  208. // Only initialize visible fields.
  209. acf.addFilter('find_fields', function( $fields ){
  210. // not templates
  211. $fields = $fields.not('#available-widgets .acf-field');
  212. // not widget dragging in
  213. $fields = $fields.not('.widget.ui-draggable-dragging .acf-field');
  214. // return
  215. return $fields;
  216. });
  217. // on publish
  218. $('#widgets-right').on('click', '.widget-control-save', function( e ){
  219. // vars
  220. var $button = $(this);
  221. var $form = $button.closest('form');
  222. // validate
  223. var valid = acf.validateForm({
  224. form: $form,
  225. event: e,
  226. reset: true
  227. });
  228. // if not valid, stop event and allow validation to continue
  229. if( !valid ) {
  230. e.preventDefault();
  231. e.stopImmediatePropagation();
  232. }
  233. });
  234. // show
  235. $('#widgets-right').on('click', '.widget-top', function(){
  236. var $widget = $(this).parent();
  237. if( $widget.hasClass('open') ) {
  238. acf.doAction('hide', $widget);
  239. } else {
  240. acf.doAction('show', $widget);
  241. }
  242. });
  243. $(document).on('widget-added', function( e, $widget ){
  244. // - use delay to avoid rendering issues with customizer (ensures div is visible)
  245. setTimeout(function(){
  246. acf.doAction('append', $widget );
  247. }, 100);
  248. });
  249. })(jQuery);
  250. </script>
  251. <?php
  252. }
  253. }
  254. new acf_form_widget();
  255. endif;
  256. ?>