GravityView  1.22.6
The best, easiest way to display Gravity Forms entries on your website.
class-edit-entry-render.php
Go to the documentation of this file.
1 <?php
2 /**
3  * GravityView Edit Entry - render frontend
4  *
5  * @package GravityView
6  * @license GPL2+
7  * @author Katz Web Services, Inc.
8  * @link http://gravityview.co
9  * @copyright Copyright 2014, Katz Web Services, Inc.
10  */
11 
12 if ( ! defined( 'WPINC' ) ) {
13  die;
14 }
15 
17 
18  /**
19  * @var GravityView_Edit_Entry
20  */
21  protected $loader;
22 
23  /**
24  * @var string String used to generate unique nonce for the entry/form/view combination. Allows access to edit page.
25  */
26  static $nonce_key;
27 
28  /**
29  * @since 1.9
30  * @var string String used for check valid edit entry form submission. Allows saving edit form values.
31  */
32  private static $nonce_field = 'is_gv_edit_entry';
33 
34  /**
35  * @since 1.9
36  * @var bool Whether to allow save and continue functionality
37  */
38  private static $supports_save_and_continue = false;
39 
40  /**
41  * Gravity Forms entry array
42  *
43  * @var array
44  */
45  public $entry;
46 
47  /**
48  * Gravity Forms entry array (it won't get changed during this class lifecycle)
49  * @since 1.17.2
50  * @var array
51  */
52  private static $original_entry = array();
53 
54  /**
55  * Gravity Forms form array (GravityView modifies the content through this class lifecycle)
56  *
57  * @var array
58  */
59  public $form;
60 
61  /**
62  * Gravity Forms form array (it won't get changed during this class lifecycle)
63  * @since 1.16.2.1
64  * @var array
65  */
66  private static $original_form;
67 
68  /**
69  * Gravity Forms form array after the form validation process
70  * @since 1.13
71  * @var array
72  */
73  public $form_after_validation = null;
74 
75  /**
76  * Hold an array of GF field objects that have calculation rules
77  * @var array
78  */
79  public $fields_with_calculation = array();
80 
81  /**
82  * Gravity Forms form id
83  *
84  * @var int
85  */
86  public $form_id;
87 
88  /**
89  * ID of the current view
90  *
91  * @var int
92  */
93  public $view_id;
94 
95  /**
96  * Updated entry is valid (GF Validation object)
97  *
98  * @var array
99  */
100  public $is_valid = NULL;
101 
103  $this->loader = $loader;
104  }
105 
106  function load() {
107 
108  /** @define "GRAVITYVIEW_DIR" "../../../" */
109  include_once( GRAVITYVIEW_DIR .'includes/class-admin-approve-entries.php' );
110 
111  // Don't display an embedded form when editing an entry
112  add_action( 'wp_head', array( $this, 'prevent_render_form' ) );
113  add_action( 'wp_footer', array( $this, 'prevent_render_form' ) );
114 
115  // Stop Gravity Forms processing what is ours!
116  add_filter( 'wp', array( $this, 'prevent_maybe_process_form'), 8 );
117 
118  add_filter( 'gravityview_is_edit_entry', array( $this, 'is_edit_entry') );
119 
120  add_action( 'gravityview_edit_entry', array( $this, 'init' ) );
121 
122  // Disable conditional logic if needed (since 1.9)
123  add_filter( 'gform_has_conditional_logic', array( $this, 'manage_conditional_logic' ), 10, 2 );
124 
125  // Make sure GF doesn't validate max files (since 1.9)
126  add_filter( 'gform_plupload_settings', array( $this, 'modify_fileupload_settings' ), 10, 3 );
127 
128  // Add fields expected by GFFormDisplay::validate()
129  add_filter( 'gform_pre_validation', array( $this, 'gform_pre_validation') );
130 
131  // Fix multiselect value for GF 2.2
132  add_filter( 'gravityview/edit_entry/field_value_multiselect', array( $this, 'fix_multiselect_value_serialization' ), 10, 3 );
133  }
134 
135  /**
136  * Don't show any forms embedded on a page when GravityView is in Edit Entry mode
137  *
138  * Adds a `__return_empty_string` filter on the Gravity Forms shortcode on the `wp_head` action
139  * And then removes it on the `wp_footer` action
140  *
141  * @since 1.16.1
142  *
143  * @return void
144  */
145  public function prevent_render_form() {
146  if( $this->is_edit_entry() ) {
147  if( 'wp_head' === current_filter() ) {
148  add_filter( 'gform_shortcode_form', '__return_empty_string' );
149  } else {
150  remove_filter( 'gform_shortcode_form', '__return_empty_string' );
151  }
152  }
153  }
154 
155  /**
156  * Because we're mimicking being a front-end Gravity Forms form while using a Gravity Forms
157  * backend form, we need to prevent them from saving twice.
158  * @return void
159  */
160  public function prevent_maybe_process_form() {
161 
162  if( ! empty( $_POST ) ) {
163  do_action( 'gravityview_log_debug', 'GravityView_Edit_Entry[prevent_maybe_process_form] $_POSTed data (sanitized): ', esc_html( print_r( $_POST, true ) ) );
164  }
165 
166  if( $this->is_edit_entry_submission() ) {
167  remove_action( 'wp', array( 'RGForms', 'maybe_process_form'), 9 );
168  remove_action( 'wp', array( 'GFForms', 'maybe_process_form'), 9 );
169  }
170  }
171 
172  /**
173  * Is the current page an Edit Entry page?
174  * @return boolean
175  */
176  public function is_edit_entry() {
177 
178  $is_edit_entry = GravityView_frontend::is_single_entry() && ! empty( $_GET['edit'] );
179 
180  return ( $is_edit_entry || $this->is_edit_entry_submission() );
181  }
182 
183  /**
184  * Is the current page an Edit Entry page?
185  * @since 1.9
186  * @return boolean
187  */
188  public function is_edit_entry_submission() {
189  return !empty( $_POST[ self::$nonce_field ] );
190  }
191 
192  /**
193  * When Edit entry view is requested setup the vars
194  */
195  private function setup_vars() {
197 
198 
199  $entries = $gravityview_view->getEntries();
200  self::$original_entry = $entries[0];
201  $this->entry = $entries[0];
202 
203  self::$original_form = $gravityview_view->getForm();
204  $this->form = $gravityview_view->getForm();
205  $this->form_id = $gravityview_view->getFormId();
206  $this->view_id = $gravityview_view->getViewId();
207 
208  self::$nonce_key = GravityView_Edit_Entry::get_nonce_key( $this->view_id, $this->form_id, $this->entry['id'] );
209  }
210 
211 
212  /**
213  * Load required files and trigger edit flow
214  *
215  * Run when the is_edit_entry returns true.
216  *
217  * @param GravityView_View_Data $gv_data GravityView Data object
218  * @return void
219  */
220  public function init( $gv_data ) {
221 
222  require_once( GFCommon::get_base_path() . '/form_display.php' );
223  require_once( GFCommon::get_base_path() . '/entry_detail.php' );
224 
225  $this->setup_vars();
226 
227  // Multiple Views embedded, don't proceed if nonce fails
228  $multiple_views = defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) ? gravityview()->views->count() > 1 : $gv_data->has_multiple_views();
229  if( $multiple_views && ! wp_verify_nonce( $_GET['edit'], self::$nonce_key ) ) {
230  do_action('gravityview_log_error', __METHOD__ . ': Nonce validation failed for the Edit Entry request; returning' );
231  return;
232  }
233 
234  // Sorry, you're not allowed here.
235  if( false === $this->user_can_edit_entry( true ) ) {
236  do_action('gravityview_log_error', __METHOD__ . ': User is not allowed to edit this entry; returning', $this->entry );
237  return;
238  }
239 
240  $this->print_scripts();
241 
242  $this->process_save();
243 
244  $this->edit_entry_form();
245 
246  }
247 
248 
249  /**
250  * Force Gravity Forms to output scripts as if it were in the admin
251  * @return void
252  */
253  private function print_scripts() {
255 
256  wp_register_script( 'gform_gravityforms', GFCommon::get_base_url().'/js/gravityforms.js', array( 'jquery', 'gform_json', 'gform_placeholder', 'sack', 'plupload-all', 'gravityview-fe-view' ) );
257 
258  GFFormDisplay::enqueue_form_scripts($gravityview_view->getForm(), false);
259 
260  // Sack is required for images
261  wp_print_scripts( array( 'sack', 'gform_gravityforms' ) );
262  }
263 
264 
265  /**
266  * Process edit entry form save
267  */
268  private function process_save() {
269 
270  if( empty( $_POST ) || ! isset( $_POST['lid'] ) ) {
271  return;
272  }
273 
274  // Make sure the entry, view, and form IDs are all correct
275  $valid = $this->verify_nonce();
276 
277  if( !$valid ) {
278  do_action('gravityview_log_error', __METHOD__ . ' Nonce validation failed.' );
279  return;
280  }
281 
282  if( $this->entry['id'] !== $_POST['lid'] ) {
283  do_action('gravityview_log_error', __METHOD__ . ' Entry ID did not match posted entry ID.' );
284  return;
285  }
286 
287  do_action('gravityview_log_debug', __METHOD__ . ': $_POSTed data (sanitized): ', esc_html( print_r( $_POST, true ) ) );
288 
289  $this->process_save_process_files( $this->form_id );
290 
291  $this->validate();
292 
293  if( $this->is_valid ) {
294 
295  do_action('gravityview_log_debug', __METHOD__ . ': Submission is valid.' );
296 
297  /**
298  * @hack This step is needed to fix field visibility, to add the calculation fields
299  */
300  $form = $this->form_prepare_for_save();
301 
302  /**
303  * @hack to avoid the capability validation of the method save_lead for GF 1.9+
304  */
305  unset( $_GET['page'] );
306 
307  $date_created = $this->entry['date_created'];
308 
309  /**
310  * @hack to force Gravity Forms to use $read_value_from_post in GFFormsModel::save_lead()
311  * @since 1.17.2
312  */
313  unset( $this->entry['date_created'] );
314 
315  GFFormsModel::save_lead( $form, $this->entry );
316 
317  // Delete the values for hidden inputs
318  $this->unset_hidden_field_values();
319 
320  $this->entry['date_created'] = $date_created;
321 
322  // Process calculation fields
323  $this->update_calculation_fields();
324 
325  // Perform actions normally performed after updating a lead
326  $this->after_update();
327 
328  /**
329  * Must be AFTER after_update()!
330  * @see https://github.com/gravityview/GravityView/issues/764
331  */
333 
334  /**
335  * @action `gravityview/edit_entry/after_update` Perform an action after the entry has been updated using Edit Entry
336  * @param array $form Gravity Forms form array
337  * @param string $entry_id Numeric ID of the entry that was updated
338  * @param GravityView_Edit_Entry_Render $this This object
339  */
340  do_action( 'gravityview/edit_entry/after_update', $this->form, $this->entry['id'], $this );
341 
342  } else {
343  do_action('gravityview_log_error', __METHOD__ . ': Submission is NOT valid.', $this->entry );
344  }
345 
346  } // process_save
347 
348  /**
349  * Delete the value of fields hidden by conditional logic when the entry is edited
350  *
351  * @uses GFFormsModel::update_lead_field_value()
352  *
353  * @since 1.17.4
354  *
355  * @return void
356  */
357  private function unset_hidden_field_values() {
358  global $wpdb;
359 
360  /**
361  * @filter `gravityview/edit_entry/unset_hidden_field_values` Whether to delete values of fields hidden by conditional logic
362  * @since 1.22.2
363  * @param bool $unset_hidden_field_values Default: true
364  * @param GravityView_Edit_Entry_Render $this This object
365  */
366  $unset_hidden_field_values = apply_filters( 'gravityview/edit_entry/unset_hidden_field_values', true, $this );
367 
368  if( ! $unset_hidden_field_values ) {
369  return;
370  }
371 
372  if ( version_compare( GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '>=' ) && method_exists( 'GFFormsModel', 'get_entry_meta_table_name' ) ) {
373  $entry_meta_table = GFFormsModel::get_entry_meta_table_name();
374  $current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM $entry_meta_table WHERE entry_id=%d", $this->entry['id'] ) );
375  } else {
376  $lead_detail_table = GFFormsModel::get_lead_details_table_name();
377  $current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT id, field_number FROM $lead_detail_table WHERE lead_id=%d", $this->entry['id'] ) );
378  }
379 
380  foreach ( $this->entry as $input_id => $field_value ) {
381 
382  $field = RGFormsModel::get_field( $this->form, $input_id );
383 
384  // Reset fields that are hidden
385  // Don't pass $entry as fourth parameter; force using $_POST values to calculate conditional logic
386  if ( GFFormsModel::is_field_hidden( $this->form, $field, array(), NULL ) ) {
387 
388  // List fields are stored as empty arrays when empty
389  $empty_value = $this->is_field_json_encoded( $field ) ? '[]' : '';
390 
391  $lead_detail_id = GFFormsModel::get_lead_detail_id( $current_fields, $input_id );
392 
393  GFFormsModel::update_lead_field_value( $this->form, $this->entry, $field, $lead_detail_id, $input_id, $empty_value );
394 
395  // Prevent the $_POST values of hidden fields from being used as default values when rendering the form
396  // after submission
397  $post_input_id = 'input_' . str_replace( '.', '_', $input_id );
398  $_POST[ $post_input_id ] = '';
399  }
400  }
401  }
402 
403  /**
404  * Have GF handle file uploads
405  *
406  * Copy of code from GFFormDisplay::process_form()
407  *
408  * @param int $form_id
409  */
410  private function process_save_process_files( $form_id ) {
411 
412  //Loading files that have been uploaded to temp folder
413  $files = GFCommon::json_decode( stripslashes( RGForms::post( 'gform_uploaded_files' ) ) );
414  if ( ! is_array( $files ) ) {
415  $files = array();
416  }
417 
418  /**
419  * Make sure the fileuploads are not overwritten if no such request was done.
420  * @since 1.20.1
421  */
422  add_filter( "gform_save_field_value_$form_id", array( $this, 'save_field_value' ), 99, 5 );
423 
424  RGFormsModel::$uploaded_files[ $form_id ] = $files;
425  }
426 
427  /**
428  * Make sure the fileuploads are not overwritten if no such request was done.
429  *
430  * TO ONLY BE USED INTERNALLY; DO NOT DEVELOP ON; MAY BE REMOVED AT ANY TIME.
431  *
432  * @since 1.20.1
433  *
434  * @param string $value Field value
435  * @param array $entry GF entry array
436  * @param GF_Field_FileUpload $field
437  * @param array $form GF form array
438  * @param string $input_id ID of the input being saved
439  *
440  * @return string
441  */
442  public function save_field_value( $value = '', $entry = array(), $field = null, $form = array(), $input_id = '' ) {
443 
444  if ( ! $field || $field->type != 'fileupload' ) {
445  return $value;
446  }
447 
448  $input_name = 'input_' . str_replace( '.', '_', $input_id );
449 
450  if ( $field->multipleFiles ) {
451  if ( empty( $value ) ) {
452  return json_decode( $entry[ $input_id ], true );
453  }
454  return $value;
455  }
456 
457  /** No file is being uploaded. */
458  if ( empty( $_FILES[ $input_name ]['name'] ) ) {
459  /** So return the original upload */
460  return $entry[ $input_id ];
461  }
462 
463  return $value;
464  }
465 
466  /**
467  * Remove max_files validation (done on gravityforms.js) to avoid conflicts with GravityView
468  * Late validation done on self::custom_validation
469  *
470  * @param $plupload_init array Plupload settings
471  * @param $form_id
472  * @param $instance
473  * @return mixed
474  */
475  public function modify_fileupload_settings( $plupload_init, $form_id, $instance ) {
476  if( ! $this->is_edit_entry() ) {
477  return $plupload_init;
478  }
479 
480  $plupload_init['gf_vars']['max_files'] = 0;
481 
482  return $plupload_init;
483  }
484 
485 
486  /**
487  * Set visibility to visible and convert field input key to string
488  * @return array $form
489  */
490  private function form_prepare_for_save() {
491 
492  $form = $this->form;
493 
494  /** @var GF_Field $field */
495  foreach( $form['fields'] as $k => &$field ) {
496 
497  /**
498  * Remove the fields with calculation formulas before save to avoid conflicts with GF logic
499  * @since 1.16.3
500  * @var GF_Field $field
501  */
502  if( $field->has_calculation() ) {
503  unset( $form['fields'][ $k ] );
504  }
505 
506  $field->adminOnly = false;
507 
508  if( isset( $field->inputs ) && is_array( $field->inputs ) ) {
509  foreach( $field->inputs as $key => $input ) {
510  $field->inputs[ $key ][ 'id' ] = (string)$input['id'];
511  }
512  }
513  }
514 
515  return $form;
516  }
517 
518  private function update_calculation_fields() {
519 
520  $form = self::$original_form;
521  $update = false;
522 
523  // get the most up to date entry values
524  $entry = GFAPI::get_entry( $this->entry['id'] );
525 
526  if( !empty( $this->fields_with_calculation ) ) {
527  $update = true;
528  foreach ( $this->fields_with_calculation as $calc_field ) {
529  $inputs = $calc_field->get_entry_inputs();
530  if ( is_array( $inputs ) ) {
531  foreach ( $inputs as $input ) {
532  $input_name = 'input_' . str_replace( '.', '_', $input['id'] );
533  list( $prefix, $field_id, $input_id ) = rgexplode( '_', $input_name, 3 );
534 
535  switch ( $input_id ) {
536  case 1:
537  /** Never void the labels. */
538  $value = $entry[ $input['id'] ];
539  break;
540  case 2:
541  /** Always recalcualte the final price. */
542  $value = '';
543  break;
544  case 3:
545  /** Fetch the quantity form the request. */
546  $value = rgpost( $input_name, $entry[ $input['id'] ] );
547  break;
548  }
549 
550  $entry[ strval( $input['id'] ) ] = RGFormsModel::prepare_value( $form, $calc_field, $value, $input_name, $entry['id'], $entry );
551  }
552  } else {
553  $input_name = 'input_' . str_replace( '.', '_', $calc_field->id);
554  $entry[ strval( $calc_field->id ) ] = RGFormsModel::prepare_value( $form, $calc_field, '', $input_name, $entry['id'], $entry );
555  }
556  }
557 
558  }
559 
560  if( $update ) {
561 
562  $return_entry = GFAPI::update_entry( $entry );
563 
564  if( is_wp_error( $return_entry ) ) {
565  do_action( 'gravityview_log_error', 'Updating the entry calculation fields failed', $return_entry );
566  } else {
567  do_action( 'gravityview_log_debug', 'Updating the entry calculation fields succeeded' );
568  }
569  }
570  }
571 
572  /**
573  * Handle updating the Post Image field
574  *
575  * Sets a new Featured Image if configured in Gravity Forms; otherwise uploads/updates media
576  *
577  * @since 1.17
578  *
579  * @uses GFFormsModel::media_handle_upload
580  * @uses set_post_thumbnail
581  *
582  * @param array $form GF Form array
583  * @param GF_Field $field GF Field
584  * @param string $field_id Numeric ID of the field
585  * @param string $value
586  * @param array $entry GF Entry currently being edited
587  * @param int $post_id ID of the Post being edited
588  *
589  * @return mixed|string
590  */
591  private function update_post_image( $form, $field, $field_id, $value, $entry, $post_id ) {
592 
593  $input_name = 'input_' . $field_id;
594 
595  if ( !empty( $_FILES[ $input_name ]['name'] ) ) {
596 
597  // We have a new image
598 
599  $value = RGFormsModel::prepare_value( $form, $field, $value, $input_name, $entry['id'] );
600 
601  $ary = ! empty( $value ) ? explode( '|:|', $value ) : array();
602  $ary = stripslashes_deep( $ary );
603  $img_url = rgar( $ary, 0 );
604 
605  $img_title = count( $ary ) > 1 ? $ary[1] : '';
606  $img_caption = count( $ary ) > 2 ? $ary[2] : '';
607  $img_description = count( $ary ) > 3 ? $ary[3] : '';
608 
609  $image_meta = array(
610  'post_excerpt' => $img_caption,
611  'post_content' => $img_description,
612  );
613 
614  //adding title only if it is not empty. It will default to the file name if it is not in the array
615  if ( ! empty( $img_title ) ) {
616  $image_meta['post_title'] = $img_title;
617  }
618 
619  /**
620  * todo: As soon as \GFFormsModel::media_handle_upload becomes a public method, move this call to \GFFormsModel::media_handle_upload and remove the hack from this class.
621  * Note: the method became public in GF 1.9.17.7, but we don't require that version yet.
622  */
623  $media_id = GravityView_GFFormsModel::media_handle_upload( $img_url, $post_id, $image_meta );
624 
625  // is this field set as featured image?
626  if ( $media_id && $field->postFeaturedImage ) {
627  set_post_thumbnail( $post_id, $media_id );
628  }
629 
630  } elseif ( !empty( $_POST[ $input_name ] ) && is_array( $value ) ) {
631 
632  $img_url = rgpost( $input_name );
633  $img_title = rgpost( $input_name . '_1' );
634  $img_caption = rgpost( $input_name . '_4' );
635  $img_description = rgpost( $input_name . '_7' );
636 
637  $value = ! empty( $img_url ) ? $img_url . "|:|" . $img_title . "|:|" . $img_caption . "|:|" . $img_description : '';
638 
639  if ( $field->postFeaturedImage ) {
640 
641  $image_meta = array(
642  'ID' => get_post_thumbnail_id( $post_id ),
643  'post_title' => $img_title,
644  'post_excerpt' => $img_caption,
645  'post_content' => $img_description,
646  );
647 
648  // update image title, caption or description
649  wp_update_post( $image_meta );
650  }
651  } else {
652 
653  // if we get here, image was removed or not set.
654  $value = '';
655 
656  if ( $field->postFeaturedImage ) {
657  delete_post_thumbnail( $post_id );
658  }
659  }
660 
661  return $value;
662  }
663 
664  /**
665  * Loop through the fields being edited and if they include Post fields, update the Entry's post object
666  *
667  * @param array $form Gravity Forms form
668  *
669  * @return void
670  */
671  private function maybe_update_post_fields( $form ) {
672 
673  if( empty( $this->entry['post_id'] ) ) {
674  do_action( 'gravityview_log_debug', __METHOD__ . ': This entry has no post fields. Continuing...' );
675  return;
676  }
677 
678  $post_id = $this->entry['post_id'];
679 
680  // Security check
681  if( false === GVCommon::has_cap( 'edit_post', $post_id ) ) {
682  do_action( 'gravityview_log_error', 'The current user does not have the ability to edit Post #'.$post_id );
683  return;
684  }
685 
686  $update_entry = false;
687 
688  $updated_post = $original_post = get_post( $post_id );
689 
690  foreach ( $this->entry as $field_id => $value ) {
691 
692  $field = RGFormsModel::get_field( $form, $field_id );
693 
694  if( ! $field ) {
695  continue;
696  }
697 
698  if( GFCommon::is_post_field( $field ) && 'post_category' !== $field->type ) {
699 
700  // Get the value of the field, including $_POSTed value
701  $value = RGFormsModel::get_field_value( $field );
702 
703  // Use temporary entry variable, to make values available to fill_post_template() and update_post_image()
704  $entry_tmp = $this->entry;
705  $entry_tmp["{$field_id}"] = $value;
706 
707  switch( $field->type ) {
708 
709  case 'post_title':
710  $post_title = $value;
711  if( rgar( $form, 'postTitleTemplateEnabled' ) ) {
712  $post_title = $this->fill_post_template( $form['postTitleTemplate'], $form, $entry_tmp );
713  }
714  $updated_post->post_title = $post_title;
715  $updated_post->post_name = $post_title;
716  unset( $post_title );
717  break;
718 
719  case 'post_content':
720  $post_content = $value;
721  if( rgar( $form, 'postContentTemplateEnabled' ) ) {
722  $post_content = $this->fill_post_template( $form['postContentTemplate'], $form, $entry_tmp, true );
723  }
724  $updated_post->post_content = $post_content;
725  unset( $post_content );
726  break;
727  case 'post_excerpt':
728  $updated_post->post_excerpt = $value;
729  break;
730  case 'post_tags':
731  wp_set_post_tags( $post_id, $value, false );
732  break;
733  case 'post_category':
734  break;
735  case 'post_custom_field':
736  if ( is_array( $value ) && ( floatval( $field_id ) !== floatval( $field->id ) ) ) {
737  $value = $value[ $field_id ];
738  }
739 
740  if( ! empty( $field->customFieldTemplateEnabled ) ) {
741  $value = $this->fill_post_template( $field->customFieldTemplate, $form, $entry_tmp, true );
742  }
743 
744  if ( $this->is_field_json_encoded( $field ) && ! is_string( $value ) ) {
745  $value = function_exists('wp_json_encode') ? wp_json_encode( $value ) : json_encode( $value );
746  }
747 
748  update_post_meta( $post_id, $field->postCustomFieldName, $value );
749  break;
750 
751  case 'post_image':
752  $value = $this->update_post_image( $form, $field, $field_id, $value, $this->entry, $post_id );
753  break;
754 
755  }
756 
757  // update entry after
758  $this->entry["{$field_id}"] = $value;
759 
760  $update_entry = true;
761 
762  unset( $entry_tmp );
763  }
764 
765  }
766 
767  if( $update_entry ) {
768 
769  $return_entry = GFAPI::update_entry( $this->entry );
770 
771  if( is_wp_error( $return_entry ) ) {
772  do_action( 'gravityview_log_error', 'Updating the entry post fields failed', array( '$this->entry' => $this->entry, '$return_entry' => $return_entry ) );
773  } else {
774  do_action( 'gravityview_log_debug', 'Updating the entry post fields for post #'.$post_id.' succeeded' );
775  }
776 
777  }
778 
779  $return_post = wp_update_post( $updated_post, true );
780 
781  if( is_wp_error( $return_post ) ) {
782  $return_post->add_data( $updated_post, '$updated_post' );
783  do_action( 'gravityview_log_error', 'Updating the post content failed', compact( 'updated_post', 'return_post' ) );
784  } else {
785  do_action( 'gravityview_log_debug', 'Updating the post content for post #'.$post_id.' succeeded', $updated_post );
786  }
787  }
788 
789  /**
790  * Is the field stored in a JSON-encoded manner?
791  *
792  * @param GF_Field $field
793  *
794  * @return bool True: stored in DB json_encode()'d; False: not encoded
795  */
796  private function is_field_json_encoded( $field ) {
797 
798  $json_encoded = false;
799 
800  $input_type = RGFormsModel::get_input_type( $field );
801 
802  // Only certain custom field types are supported
803  switch( $input_type ) {
804  case 'fileupload':
805  case 'list':
806  case 'multiselect':
807  $json_encoded = true;
808  break;
809  }
810 
811  return $json_encoded;
812  }
813 
814  /**
815  * Convert a field content template into prepared output
816  *
817  * @uses GravityView_GFFormsModel::get_post_field_images()
818  *
819  * @since 1.17
820  *
821  * @param string $template The content template for the field
822  * @param array $form Gravity Forms form
823  * @param bool $do_shortcode Whether to process shortcode inside content. In GF, only run on Custom Field and Post Content fields
824  *
825  * @return string
826  */
827  private function fill_post_template( $template, $form, $entry, $do_shortcode = false ) {
828 
829  require_once GRAVITYVIEW_DIR . 'includes/class-gravityview-gfformsmodel.php';
830 
832 
833  //replacing post image variables
834  $output = GFCommon::replace_variables_post_image( $template, $post_images, $entry );
835 
836  //replacing all other variables
837  $output = GFCommon::replace_variables( $output, $form, $entry, false, false, false );
838 
839  // replace conditional shortcodes
840  if( $do_shortcode ) {
841  $output = do_shortcode( $output );
842  }
843 
844  return $output;
845  }
846 
847 
848  /**
849  * Perform actions normally performed after updating a lead
850  *
851  * @since 1.8
852  *
853  * @see GFEntryDetail::lead_detail_page()
854  *
855  * @return void
856  */
857  private function after_update() {
858 
859  do_action( 'gform_after_update_entry', $this->form, $this->entry['id'], self::$original_entry );
860  do_action( "gform_after_update_entry_{$this->form['id']}", $this->form, $this->entry['id'], self::$original_entry );
861 
862  // Re-define the entry now that we've updated it.
863  $entry = RGFormsModel::get_lead( $this->entry['id'] );
864 
865  $entry = GFFormsModel::set_entry_meta( $entry, $this->form );
866 
867  if ( version_compare( GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) {
868  // We need to clear the cache because Gravity Forms caches the field values, which
869  // we have just updated.
870  foreach ($this->form['fields'] as $key => $field) {
871  GFFormsModel::refresh_lead_field_value( $entry['id'], $field->id );
872  }
873  }
874 
875  $this->entry = $entry;
876  }
877 
878 
879  /**
880  * Display the Edit Entry form
881  *
882  * @return void
883  */
884  public function edit_entry_form() {
885 
886  ?>
887 
888  <div class="gv-edit-entry-wrapper"><?php
889 
890  $javascript = gravityview_ob_include( GravityView_Edit_Entry::$file .'/partials/inline-javascript.php', $this );
891 
892  /**
893  * Fixes weird wpautop() issue
894  * @see https://github.com/katzwebservices/GravityView/issues/451
895  */
896  echo gravityview_strip_whitespace( $javascript );
897 
898  ?><h2 class="gv-edit-entry-title">
899  <span><?php
900 
901  /**
902  * @filter `gravityview_edit_entry_title` Modify the edit entry title
903  * @param string $edit_entry_title Modify the "Edit Entry" title
904  * @param GravityView_Edit_Entry_Render $this This object
905  */
906  $edit_entry_title = apply_filters('gravityview_edit_entry_title', __('Edit Entry', 'gravityview'), $this );
907 
908  echo esc_attr( $edit_entry_title );
909  ?></span>
910  </h2>
911 
912  <?php $this->maybe_print_message(); ?>
913 
914  <?php // The ID of the form needs to be `gform_{form_id}` for the pluploader ?>
915 
916  <form method="post" id="gform_<?php echo $this->form_id; ?>" enctype="multipart/form-data">
917 
918  <?php
919 
920  wp_nonce_field( self::$nonce_key, self::$nonce_key );
921 
922  wp_nonce_field( self::$nonce_field, self::$nonce_field, false );
923 
924  // Print the actual form HTML
925  $this->render_edit_form();
926 
927  ?>
928  </form>
929 
930  <script>
931  gform.addFilter('gform_reset_pre_conditional_logic_field_action', function ( reset, formId, targetId, defaultValues, isInit ) {
932  return false;
933  });
934  </script>
935 
936  </div>
937 
938  <?php
939  }
940 
941  /**
942  * Display success or error message if the form has been submitted
943  *
944  * @uses GVCommon::generate_notice
945  *
946  * @since 1.16.2.2
947  *
948  * @return void
949  */
950  private function maybe_print_message() {
951 
952  if( rgpost('action') === 'update' ) {
953 
954  $back_link = esc_url( remove_query_arg( array( 'page', 'view', 'edit' ) ) );
955 
956  if( ! $this->is_valid ){
957 
958  // Keeping this compatible with Gravity Forms.
959  $validation_message = "<div class='validation_error'>" . __('There was a problem with your submission.', 'gravityview') . " " . __('Errors have been highlighted below.', 'gravityview') . "</div>";
960  $message = apply_filters("gform_validation_message_{$this->form['id']}", apply_filters("gform_validation_message", $validation_message, $this->form), $this->form);
961 
962  echo GVCommon::generate_notice( $message , 'gv-error' );
963 
964  } else {
965  $entry_updated_message = sprintf( esc_attr__('Entry Updated. %sReturn to Entry%s', 'gravityview'), '<a href="'. $back_link .'">', '</a>' );
966 
967  /**
968  * @filter `gravityview/edit_entry/success` Modify the edit entry success message (including the anchor link)
969  * @since 1.5.4
970  * @param string $entry_updated_message Existing message
971  * @param int $view_id View ID
972  * @param array $entry Gravity Forms entry array
973  * @param string $back_link URL to return to the original entry. @since 1.6
974  */
975  $message = apply_filters( 'gravityview/edit_entry/success', $entry_updated_message , $this->view_id, $this->entry, $back_link );
976 
977  echo GVCommon::generate_notice( $message );
978  }
979 
980  }
981  }
982 
983  /**
984  * Display the Edit Entry form in the original Gravity Forms format
985  *
986  * @since 1.9
987  *
988  * @return void
989  */
990  private function render_edit_form() {
991 
992  /**
993  * @action `gravityview/edit-entry/render/before` Before rendering the Edit Entry form
994  * @since 1.17
995  * @param GravityView_Edit_Entry_Render $this
996  */
997  do_action( 'gravityview/edit-entry/render/before', $this );
998 
999  add_filter( 'gform_pre_render', array( $this, 'filter_modify_form_fields'), 5000, 3 );
1000  add_filter( 'gform_submit_button', array( $this, 'render_form_buttons') );
1001  add_filter( 'gform_disable_view_counter', '__return_true' );
1002 
1003  add_filter( 'gform_field_input', array( $this, 'verify_user_can_edit_post' ), 5, 5 );
1004  add_filter( 'gform_field_input', array( $this, 'modify_edit_field_input' ), 10, 5 );
1005 
1006  // We need to remove the fake $_GET['page'] arg to avoid rendering form as if in admin.
1007  unset( $_GET['page'] );
1008 
1009  // TODO: Verify multiple-page forms
1010 
1011  ob_start(); // Prevent PHP warnings possibly caused by prefilling list fields for conditional logic
1012 
1013  $html = GFFormDisplay::get_form( $this->form['id'], false, false, true, $this->entry );
1014 
1015  ob_get_clean();
1016 
1017  remove_filter( 'gform_pre_render', array( $this, 'filter_modify_form_fields' ), 5000 );
1018  remove_filter( 'gform_submit_button', array( $this, 'render_form_buttons' ) );
1019  remove_filter( 'gform_disable_view_counter', '__return_true' );
1020  remove_filter( 'gform_field_input', array( $this, 'verify_user_can_edit_post' ), 5 );
1021  remove_filter( 'gform_field_input', array( $this, 'modify_edit_field_input' ), 10 );
1022 
1023  echo $html;
1024 
1025  /**
1026  * @action `gravityview/edit-entry/render/after` After rendering the Edit Entry form
1027  * @since 1.17
1028  * @param GravityView_Edit_Entry_Render $this
1029  */
1030  do_action( 'gravityview/edit-entry/render/after', $this );
1031  }
1032 
1033  /**
1034  * Display the Update/Cancel/Delete buttons for the Edit Entry form
1035  * @since 1.8
1036  * @return string
1037  */
1038  public function render_form_buttons() {
1039  return gravityview_ob_include( GravityView_Edit_Entry::$file .'/partials/form-buttons.php', $this );
1040  }
1041 
1042 
1043  /**
1044  * Modify the form fields that are shown when using GFFormDisplay::get_form()
1045  *
1046  * By default, all fields will be shown. We only want the Edit Tab configured fields to be shown.
1047  *
1048  * @param array $form
1049  * @param boolean $ajax Whether in AJAX mode
1050  * @param array|string $field_values Passed parameters to the form
1051  *
1052  * @since 1.9
1053  *
1054  * @return array Modified form array
1055  */
1056  public function filter_modify_form_fields( $form, $ajax = false, $field_values = '' ) {
1057 
1058  // In case we have validated the form, use it to inject the validation results into the form render
1059  if( isset( $this->form_after_validation ) ) {
1061  } else {
1062  $form['fields'] = $this->get_configured_edit_fields( $form, $this->view_id );
1063  }
1064 
1065  $form = $this->filter_conditional_logic( $form );
1066 
1067  $form = $this->prefill_conditional_logic( $form );
1068 
1069  // for now we don't support Save and Continue feature.
1070  if( ! self::$supports_save_and_continue ) {
1071  unset( $form['save'] );
1072  }
1073 
1074  return $form;
1075  }
1076 
1077  /**
1078  * When displaying a field, check if it's a Post Field, and if so, make sure the post exists and current user has edit rights.
1079  *
1080  * @since 1.16.2.2
1081  *
1082  * @param string $field_content Always empty. Returning not-empty overrides the input.
1083  * @param GF_Field $field
1084  * @param string|array $value If array, it's a field with multiple inputs. If string, single input.
1085  * @param int $lead_id Lead ID. Always 0 for the `gform_field_input` filter.
1086  * @param int $form_id Form ID
1087  *
1088  * @return string If error, the error message. If no error, blank string (modify_edit_field_input() runs next)
1089  */
1090  public function verify_user_can_edit_post( $field_content = '', $field, $value, $lead_id = 0, $form_id ) {
1091 
1092  if( GFCommon::is_post_field( $field ) ) {
1093 
1094  $message = null;
1095 
1096  // First, make sure they have the capability to edit the post.
1097  if( false === current_user_can( 'edit_post', $this->entry['post_id'] ) ) {
1098 
1099  /**
1100  * @filter `gravityview/edit_entry/unsupported_post_field_text` Modify the message when someone isn't able to edit a post
1101  * @param string $message The existing "You don't have permission..." text
1102  */
1103  $message = apply_filters('gravityview/edit_entry/unsupported_post_field_text', __('You don&rsquo;t have permission to edit this post.', 'gravityview') );
1104 
1105  } elseif( null === get_post( $this->entry['post_id'] ) ) {
1106  /**
1107  * @filter `gravityview/edit_entry/no_post_text` Modify the message when someone is editing an entry attached to a post that no longer exists
1108  * @param string $message The existing "This field is not editable; the post no longer exists." text
1109  */
1110  $message = apply_filters('gravityview/edit_entry/no_post_text', __('This field is not editable; the post no longer exists.', 'gravityview' ) );
1111  }
1112 
1113  if( $message ) {
1114  $field_content = sprintf('<div class="ginput_container ginput_container_' . $field->type . '">%s</div>', wpautop( $message ) );
1115  }
1116  }
1117 
1118  return $field_content;
1119  }
1120 
1121  /**
1122  *
1123  * Fill-in the saved values into the form inputs
1124  *
1125  * @param string $field_content Always empty. Returning not-empty overrides the input.
1126  * @param GF_Field $field
1127  * @param string|array $value If array, it's a field with multiple inputs. If string, single input.
1128  * @param int $lead_id Lead ID. Always 0 for the `gform_field_input` filter.
1129  * @param int $form_id Form ID
1130  *
1131  * @return mixed
1132  */
1133  public function modify_edit_field_input( $field_content = '', $field, $value, $lead_id = 0, $form_id ) {
1134 
1136 
1137  // If the form has been submitted, then we don't need to pre-fill the values,
1138  // Except for fileupload type and when a field input is overridden- run always!!
1139  if(
1140  ( $this->is_edit_entry_submission() && !in_array( $field->type, array( 'fileupload', 'post_image' ) ) )
1141  && false === ( $gv_field && is_callable( array( $gv_field, 'get_field_input' ) ) )
1142  && ! GFCommon::is_product_field( $field->type )
1143  || ! empty( $field_content )
1144  || in_array( $field->type, array( 'honeypot' ) )
1145  ) {
1146  return $field_content;
1147  }
1148 
1149  // SET SOME FIELD DEFAULTS TO PREVENT ISSUES
1150  $field->adminOnly = false; /** @see GFFormDisplay::get_counter_init_script() need to make field visible */
1151 
1152  $field_value = $this->get_field_value( $field );
1153 
1154  // Prevent any PHP warnings, like undefined index
1155  ob_start();
1156 
1157  $return = null;
1158 
1159  /** @var GravityView_Field $gv_field */
1160  if( $gv_field && is_callable( array( $gv_field, 'get_field_input' ) ) ) {
1161  $return = $gv_field->get_field_input( $this->form, $field_value, $this->entry, $field );
1162  } else {
1163  $return = $field->get_field_input( $this->form, $field_value, $this->entry );
1164  }
1165 
1166  // If there was output, it's an error
1167  $warnings = ob_get_clean();
1168 
1169  if( !empty( $warnings ) ) {
1170  do_action( 'gravityview_log_error', __METHOD__ . $warnings, $field_value );
1171  }
1172 
1173  return $return;
1174  }
1175 
1176  /**
1177  * Modify the value for the current field input
1178  *
1179  * @param GF_Field $field
1180  *
1181  * @return array|mixed|string
1182  */
1183  private function get_field_value( $field ) {
1184 
1185  /**
1186  * @filter `gravityview/edit_entry/pre_populate/override` Allow the pre-populated value to override saved value in Edit Entry form. By default, pre-populate mechanism only kicks on empty fields.
1187  * @param boolean True: override saved values; False: don't override (default)
1188  * @param $field GF_Field object Gravity Forms field object
1189  * @since 1.13
1190  */
1191  $override_saved_value = apply_filters( 'gravityview/edit_entry/pre_populate/override', false, $field );
1192 
1193  // We're dealing with multiple inputs (e.g. checkbox) but not time or date (as it doesn't store data in input IDs)
1194  if( isset( $field->inputs ) && is_array( $field->inputs ) && !in_array( $field->type, array( 'time', 'date' ) ) ) {
1195 
1196  $field_value = array();
1197 
1198  // only accept pre-populated values if the field doesn't have any choice selected.
1199  $allow_pre_populated = $field->allowsPrepopulate;
1200 
1201  foreach ( (array)$field->inputs as $input ) {
1202 
1203  $input_id = strval( $input['id'] );
1204 
1205  if ( isset( $this->entry[ $input_id ] ) && ! gv_empty( $this->entry[ $input_id ], false, false ) ) {
1206  $field_value[ $input_id ] = 'post_category' === $field->type ? GFCommon::format_post_category( $this->entry[ $input_id ], true ) : $this->entry[ $input_id ];
1207  $allow_pre_populated = false;
1208  }
1209 
1210  }
1211 
1212  $pre_value = $field->get_value_submission( array(), false );
1213 
1214  $field_value = ! $allow_pre_populated && ! ( $override_saved_value && !gv_empty( $pre_value, false, false ) ) ? $field_value : $pre_value;
1215 
1216  } else {
1217 
1218  $id = intval( $field->id );
1219 
1220  // get pre-populated value if exists
1221  $pre_value = $field->allowsPrepopulate ? GFFormsModel::get_parameter_value( $field->inputName, array(), $field ) : '';
1222 
1223  // saved field entry value (if empty, fallback to the pre-populated value, if exists)
1224  // or pre-populated value if not empty and set to override saved value
1225  $field_value = !gv_empty( $this->entry[ $id ], false, false ) && ! ( $override_saved_value && !gv_empty( $pre_value, false, false ) ) ? $this->entry[ $id ] : $pre_value;
1226 
1227  // in case field is post_category but inputType is select, multi-select or radio, convert value into array of category IDs.
1228  if ( 'post_category' === $field->type && !gv_empty( $field_value, false, false ) ) {
1229  $categories = array();
1230  foreach ( explode( ',', $field_value ) as $cat_string ) {
1231  $categories[] = GFCommon::format_post_category( $cat_string, true );
1232  }
1233  $field_value = 'multiselect' === $field->get_input_type() ? $categories : implode( '', $categories );
1234  }
1235 
1236  }
1237 
1238  // if value is empty get the default value if defined
1239  $field_value = $field->get_value_default_if_empty( $field_value );
1240 
1241  /**
1242  * @filter `gravityview/edit_entry/field_value` Change the value of an Edit Entry field, if needed
1243  * @since 1.11
1244  * @since 1.20 Added third param
1245  * @param mixed $field_value field value used to populate the input
1246  * @param object $field Gravity Forms field object ( Class GF_Field )
1247  * @param GravityView_Edit_Entry_Render $this Current object
1248  */
1249  $field_value = apply_filters( 'gravityview/edit_entry/field_value', $field_value, $field, $this );
1250 
1251  /**
1252  * @filter `gravityview/edit_entry/field_value_{field_type}` Change the value of an Edit Entry field for a specific field type
1253  * @since 1.17
1254  * @since 1.20 Added third param
1255  * @param mixed $field_value field value used to populate the input
1256  * @param GF_Field $field Gravity Forms field object
1257  * @param GravityView_Edit_Entry_Render $this Current object
1258  */
1259  $field_value = apply_filters( 'gravityview/edit_entry/field_value_' . $field->type , $field_value, $field, $this );
1260 
1261  return $field_value;
1262  }
1263 
1264 
1265  // ---- Entry validation
1266 
1267  /**
1268  * Add field keys that Gravity Forms expects.
1269  *
1270  * @see GFFormDisplay::validate()
1271  * @param array $form GF Form
1272  * @return array Modified GF Form
1273  */
1274  public function gform_pre_validation( $form ) {
1275 
1276  if( ! $this->verify_nonce() ) {
1277  return $form;
1278  }
1279 
1280  // Fix PHP warning regarding undefined index.
1281  foreach ( $form['fields'] as &$field) {
1282 
1283  // This is because we're doing admin form pretending to be front-end, so Gravity Forms
1284  // expects certain field array items to be set.
1285  foreach ( array( 'noDuplicates', 'inputType', 'isRequired', 'enablePrice', 'inputs', 'allowedExtensions' ) as $key ) {
1286  $field->{$key} = isset( $field->{$key} ) ? $field->{$key} : NULL;
1287  }
1288 
1289  // TODO: Remove when minimum GF Version is 2.0.6.5
1290  if( ! isset( $field->visibility ) && ! isset( $field->adminOnly ) ) {
1291  $field->adminOnly = NULL;
1292  }
1293 
1294  switch( RGFormsModel::get_input_type( $field ) ) {
1295 
1296  /**
1297  * this whole fileupload hack is because in the admin, Gravity Forms simply doesn't update any fileupload field if it's empty, but it DOES in the frontend.
1298  *
1299  * What we have to do is set the value so that it doesn't get overwritten as empty on save and appears immediately in the Edit Entry screen again.
1300  *
1301  * @hack
1302  */
1303  case 'fileupload':
1304 
1305  // Set the previous value
1306  $entry = $this->get_entry();
1307 
1308  $input_name = 'input_'.$field->id;
1309  $form_id = $form['id'];
1310 
1311  $value = NULL;
1312 
1313  // Use the previous entry value as the default.
1314  if( isset( $entry[ $field->id ] ) ) {
1315  $value = $entry[ $field->id ];
1316  }
1317 
1318  // If this is a single upload file
1319  if( !empty( $_FILES[ $input_name ] ) && !empty( $_FILES[ $input_name ]['name'] ) ) {
1320  $file_path = GFFormsModel::get_file_upload_path( $form['id'], $_FILES[ $input_name ]['name'] );
1321  $value = $file_path['url'];
1322 
1323  } else {
1324 
1325  // Fix PHP warning on line 1498 of form_display.php for post_image fields
1326  // Fix PHP Notice: Undefined index: size in form_display.php on line 1511
1327  $_FILES[ $input_name ] = array('name' => '', 'size' => '' );
1328 
1329  }
1330 
1331  if( rgar($field, "multipleFiles") ) {
1332 
1333  // If there are fresh uploads, process and merge them.
1334  // Otherwise, use the passed values, which should be json-encoded array of URLs
1335  if( isset( GFFormsModel::$uploaded_files[$form_id][$input_name] ) ) {
1336  $value = empty( $value ) ? '[]' : $value;
1337  $value = stripslashes_deep( $value );
1338  $value = GFFormsModel::prepare_value( $form, $field, $value, $input_name, $entry['id'], array());
1339  }
1340 
1341  } else {
1342 
1343  // A file already exists when editing an entry
1344  // We set this to solve issue when file upload fields are required.
1345  GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] = $value;
1346 
1347  }
1348 
1349  $this->entry[ $input_name ] = $value;
1350  $_POST[ $input_name ] = $value;
1351 
1352  break;
1353 
1354  case 'number':
1355  // Fix "undefined index" issue at line 1286 in form_display.php
1356  if( !isset( $_POST['input_'.$field->id ] ) ) {
1357  $_POST['input_'.$field->id ] = NULL;
1358  }
1359  break;
1360  }
1361 
1362  }
1363 
1364  return $form;
1365  }
1366 
1367 
1368  /**
1369  * Process validation for a edit entry submission
1370  *
1371  * Sets the `is_valid` object var
1372  *
1373  * @return void
1374  */
1375  private function validate() {
1376 
1377  /**
1378  * If using GF User Registration Add-on, remove the validation step, otherwise generates error when updating the entry
1379  * GF User Registration Add-on version > 3.x has a different class name
1380  * @since 1.16.2
1381  */
1382  if ( class_exists( 'GF_User_Registration' ) ) {
1383  remove_filter( 'gform_validation', array( GF_User_Registration::get_instance(), 'validate' ) );
1384  /**
1385  * Add some custom validation either way.
1386  * https://secure.helpscout.net/conversation/430858351/10957/?folderId=1210164
1387  */
1388  add_filter( 'gform_validation_' . $this->form_id, array( $this, 'user_registration_validation' ), 10, 4 );
1389  } else if ( class_exists( 'GFUser' ) ) {
1390  remove_filter( 'gform_validation', array( 'GFUser', 'user_registration_validation' ) );
1391  }
1392 
1393 
1394  /**
1395  * For some crazy reason, Gravity Forms doesn't validate Edit Entry form submissions.
1396  * You can enter whatever you want!
1397  * We try validating, and customize the results using `self::custom_validation()`
1398  */
1399  add_filter( 'gform_validation_'. $this->form_id, array( $this, 'custom_validation' ), 10, 4);
1400 
1401  // Needed by the validate funtion
1402  $failed_validation_page = NULL;
1403  $field_values = RGForms::post( 'gform_field_values' );
1404 
1405  // Prevent entry limit from running when editing an entry, also
1406  // prevent form scheduling from preventing editing
1407  unset( $this->form['limitEntries'], $this->form['scheduleForm'] );
1408 
1409  // Hide fields depending on Edit Entry settings
1410  $this->form['fields'] = $this->get_configured_edit_fields( $this->form, $this->view_id );
1411 
1412  $this->is_valid = GFFormDisplay::validate( $this->form, $field_values, 1, $failed_validation_page );
1413 
1414  remove_filter( 'gform_validation_'. $this->form_id, array( $this, 'custom_validation' ), 10 );
1415  remove_filter( 'gform_validation_' . $this->form_id, array( $this, 'user_registration_validation' ), 10 );
1416  }
1417 
1418  /**
1419  * Make validation work for User Registration feeds.
1420  *
1421  * The default validation does a bit too much for our liking.
1422  * Email, username validation should work. Called on the `gform_validation` filter.
1423  *
1424  * @param array $validation_results The validation results.
1425  * @return array The modified validation results.
1426  */
1427  public function user_registration_validation( $validation_results ) {
1428  $user_registration = GF_User_Registration::get_instance();
1429 
1430  $entry = $this->get_entry();
1431  $form = $validation_results['form'];
1432 
1433  if ( ! $feed = $user_registration->get_single_submission_feed( $entry, $form ) ) {
1434  return $validation_results;
1435  }
1436 
1437  $username_field = GFFormsModel::get_field( $form, rgars( $feed, 'meta/username' ) );
1438  $email_field = GFFormsModel::get_field( $form, rgars( $feed, 'meta/email' ) );
1439 
1440  $username = $user_registration->get_meta_value( 'username', $feed, $form, $entry );
1441  $user_email = $user_registration->get_meta_value( 'email', $feed, $form, $entry );
1442 
1443  $value = RGFormsModel::get_field_value( $email_field );
1444  if ( $user_email != $value && email_exists( $value ) ) {
1445  $email_field->failed_validation = 1;
1446  $email_field->validation_message = __( 'This email is already in use', 'gravityview' );
1447  $validation_results['is_valid'] = false;
1448  }
1449 
1450  $value = RGFormsModel::get_field_value( $username_field );
1451  if ( $username != $value ) {
1452  $username_field->failed_validation = 1;
1453  $username_field->validation_message = __( 'Usernames cannot be changed', 'gravityview' );
1454  $validation_results['is_valid'] = false;
1455  }
1456 
1457  // We'll need this result when rendering the form ( on GFFormDisplay::get_form )
1458  $this->form_after_validation = $validation_results['form'];
1459 
1460  return $validation_results;
1461  }
1462 
1463  /**
1464  * Make validation work for Edit Entry
1465  *
1466  * Because we're calling the GFFormDisplay::validate() in an unusual way (as a front-end
1467  * form pretending to be a back-end form), validate() doesn't know we _can't_ edit post
1468  * fields. This goes through all the fields and if they're an invalid post field, we
1469  * set them as valid. If there are still issues, we'll return false.
1470  *
1471  * @param [type] $validation_results [description]
1472  * @return [type] [description]
1473  */
1474  public function custom_validation( $validation_results ) {
1475 
1476  do_action('gravityview_log_debug', 'GravityView_Edit_Entry[custom_validation] Validation results: ', $validation_results );
1477 
1478  do_action('gravityview_log_debug', 'GravityView_Edit_Entry[custom_validation] $_POSTed data (sanitized): ', esc_html( print_r( $_POST, true ) ) );
1479 
1480  $gv_valid = true;
1481 
1482  foreach ( $validation_results['form']['fields'] as $key => &$field ) {
1483 
1484  $value = RGFormsModel::get_field_value( $field );
1485  $field_type = RGFormsModel::get_input_type( $field );
1486 
1487  // Validate always
1488  switch ( $field_type ) {
1489 
1490 
1491  case 'fileupload' :
1492  case 'post_image':
1493 
1494  // in case nothing is uploaded but there are already files saved
1495  if( !empty( $field->failed_validation ) && !empty( $field->isRequired ) && !empty( $value ) ) {
1496  $field->failed_validation = false;
1497  unset( $field->validation_message );
1498  }
1499 
1500  // validate if multi file upload reached max number of files [maxFiles] => 2
1501  if( rgobj( $field, 'maxFiles') && rgobj( $field, 'multipleFiles') ) {
1502 
1503  $input_name = 'input_' . $field->id;
1504  //uploaded
1505  $file_names = isset( GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ][ $input_name ] : array();
1506 
1507  //existent
1508  $entry = $this->get_entry();
1509  $value = NULL;
1510  if( isset( $entry[ $field->id ] ) ) {
1511  $value = json_decode( $entry[ $field->id ], true );
1512  }
1513 
1514  // count uploaded files and existent entry files
1515  $count_files = count( $file_names ) + count( $value );
1516 
1517  if( $count_files > $field->maxFiles ) {
1518  $field->validation_message = __( 'Maximum number of files reached', 'gravityview' );
1519  $field->failed_validation = 1;
1520  $gv_valid = false;
1521 
1522  // in case of error make sure the newest upload files are removed from the upload input
1523  GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ] = null;
1524  }
1525 
1526  }
1527 
1528 
1529  break;
1530 
1531  }
1532 
1533  // This field has failed validation.
1534  if( !empty( $field->failed_validation ) ) {
1535 
1536  do_action( 'gravityview_log_debug', 'GravityView_Edit_Entry[custom_validation] Field is invalid.', array( 'field' => $field, 'value' => $value ) );
1537 
1538  switch ( $field_type ) {
1539 
1540  // Captchas don't need to be re-entered.
1541  case 'captcha':
1542 
1543  // Post Image fields aren't editable, so we un-fail them.
1544  case 'post_image':
1545  $field->failed_validation = false;
1546  unset( $field->validation_message );
1547  break;
1548 
1549  }
1550 
1551  // You can't continue inside a switch, so we do it after.
1552  if( empty( $field->failed_validation ) ) {
1553  continue;
1554  }
1555 
1556  // checks if the No Duplicates option is not validating entry against itself, since
1557  // we're editing a stored entry, it would also assume it's a duplicate.
1558  if( !empty( $field->noDuplicates ) ) {
1559 
1560  $entry = $this->get_entry();
1561 
1562  // If the value of the entry is the same as the stored value
1563  // Then we can assume it's not a duplicate, it's the same.
1564  if( !empty( $entry ) && $value == $entry[ $field->id ] ) {
1565  //if value submitted was not changed, then don't validate
1566  $field->failed_validation = false;
1567 
1568  unset( $field->validation_message );
1569 
1570  do_action('gravityview_log_debug', 'GravityView_Edit_Entry[custom_validation] Field not a duplicate; it is the same entry.', $entry );
1571 
1572  continue;
1573  }
1574  }
1575 
1576  // if here then probably we are facing the validation 'At least one field must be filled out'
1577  if( GFFormDisplay::is_empty( $field, $this->form_id ) && empty( $field->isRequired ) ) {
1578  unset( $field->validation_message );
1579  $field->validation_message = false;
1580  continue;
1581  }
1582 
1583  $gv_valid = false;
1584 
1585  }
1586 
1587  }
1588 
1589  $validation_results['is_valid'] = $gv_valid;
1590 
1591  do_action('gravityview_log_debug', 'GravityView_Edit_Entry[custom_validation] Validation results.', $validation_results );
1592 
1593  // We'll need this result when rendering the form ( on GFFormDisplay::get_form )
1594  $this->form_after_validation = $validation_results['form'];
1595 
1596  return $validation_results;
1597  }
1598 
1599 
1600  /**
1601  * TODO: This seems to be hacky... we should remove it. Entry is set when updating the form using setup_vars()!
1602  * Get the current entry and set it if it's not yet set.
1603  * @return array Gravity Forms entry array
1604  */
1605  public function get_entry() {
1606 
1607  if( empty( $this->entry ) ) {
1608  // Get the database value of the entry that's being edited
1610  }
1611 
1612  return $this->entry;
1613  }
1614 
1615 
1616 
1617  // --- Filters
1618 
1619  /**
1620  * Get the Edit Entry fields as configured in the View
1621  *
1622  * @since 1.8
1623  *
1624  * @param int $view_id
1625  *
1626  * @return array Array of fields that are configured in the Edit tab in the Admin
1627  */
1629 
1630  // Get all fields for form
1631  if ( defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) ) {
1632  if ( \GV\View::exists( $view_id ) ) {
1633  $view = \GV\View::by_id( $view_id );
1634  $properties = $view->fields->as_configuration();
1635  }
1636  } else {
1637  /** GravityView_View_Data is deprecated. */
1638  $properties = GravityView_View_Data::getInstance()->get_fields( $view_id );
1639  }
1640 
1641  // If edit tab not yet configured, show all fields
1642  $edit_fields = !empty( $properties['edit_edit-fields'] ) ? $properties['edit_edit-fields'] : NULL;
1643 
1644  // Hide fields depending on admin settings
1645  $fields = $this->filter_fields( $form['fields'], $edit_fields );
1646 
1647  // If Edit Entry fields are configured, remove hidden/administrative field settings. Otherwise, don't.
1648  $fields = $this->filter_admin_only_fields( $fields, $edit_fields, $form, $view_id );
1649 
1650  /**
1651  * @filter `gravityview/edit_entry/form_fields` Modify the fields displayed in Edit Entry form
1652  * @since 1.17
1653  * @param GF_Field[] $fields Gravity Forms form fields
1654  * @param array|null $edit_fields Fields for the Edit Entry tab configured in the View Configuration
1655  * @param array $form GF Form array (`fields` key modified to have only fields configured to show in Edit Entry)
1656  * @param int $view_id View ID
1657  */
1658  $fields = apply_filters( 'gravityview/edit_entry/form_fields', $fields, $edit_fields, $form, $view_id );
1659 
1660  return $fields;
1661  }
1662 
1663 
1664  /**
1665  * Filter area fields based on specified conditions
1666  * - This filter removes the fields that have calculation configured
1667  *
1668  * @uses GravityView_Edit_Entry::user_can_edit_field() Check caps
1669  * @access private
1670  * @param GF_Field[] $fields
1671  * @param array $configured_fields
1672  * @since 1.5
1673  * @return array $fields
1674  */
1675  private function filter_fields( $fields, $configured_fields ) {
1676 
1677  if( empty( $fields ) || !is_array( $fields ) ) {
1678  return $fields;
1679  }
1680 
1681  $edit_fields = array();
1682 
1683  $field_type_blacklist = $this->loader->get_field_blacklist( $this->entry );
1684 
1685  // First, remove blacklist or calculation fields
1686  foreach ( $fields as $key => $field ) {
1687 
1688  // Remove the fields that have calculation properties and keep them to be used later
1689  // @since 1.16.2
1690  if( $field->has_calculation() ) {
1691  $this->fields_with_calculation[] = $field;
1692  // don't remove the calculation fields on form render.
1693  }
1694 
1695  if( in_array( $field->type, $field_type_blacklist ) ) {
1696  unset( $fields[ $key ] );
1697  }
1698  }
1699 
1700  // The Edit tab has not been configured, so we return all fields by default.
1701  if( empty( $configured_fields ) ) {
1702  return $fields;
1703  }
1704 
1705  // The edit tab has been configured, so we loop through to configured settings
1706  foreach ( $configured_fields as $configured_field ) {
1707 
1708  /** @var GF_Field $field */
1709  foreach ( $fields as $field ) {
1710 
1711  if( intval( $configured_field['id'] ) === intval( $field->id ) && $this->user_can_edit_field( $configured_field, false ) ) {
1712  $edit_fields[] = $this->merge_field_properties( $field, $configured_field );
1713  break;
1714  }
1715 
1716  }
1717 
1718  }
1719 
1720  return $edit_fields;
1721 
1722  }
1723 
1724  /**
1725  * Override GF Form field properties with the ones defined on the View
1726  * @param GF_Field $field GF Form field object
1727  * @param array $field_setting GV field options
1728  * @since 1.5
1729  * @return array|GF_Field
1730  */
1731  private function merge_field_properties( $field, $field_setting ) {
1732 
1733  $return_field = $field;
1734 
1735  if( empty( $field_setting['show_label'] ) ) {
1736  $return_field->label = '';
1737  } elseif ( !empty( $field_setting['custom_label'] ) ) {
1738  $return_field->label = $field_setting['custom_label'];
1739  }
1740 
1741  if( !empty( $field_setting['custom_class'] ) ) {
1742  $return_field->cssClass .= ' '. gravityview_sanitize_html_class( $field_setting['custom_class'] );
1743  }
1744 
1745  /**
1746  * Normalize page numbers - avoid conflicts with page validation
1747  * @since 1.6
1748  */
1749  $return_field->pageNumber = 1;
1750 
1751  return $return_field;
1752 
1753  }
1754 
1755  /**
1756  * Remove fields that shouldn't be visible based on the Gravity Forms visibility field property
1757  *
1758  * @since 1.9.1
1759  *
1760  * @param array|GF_Field[] $fields Gravity Forms form fields
1761  * @param array|null $edit_fields Fields for the Edit Entry tab configured in the View Configuration
1762  * @param array $form GF Form array
1763  * @param int $view_id View ID
1764  *
1765  * @return array Possibly modified form array
1766  */
1767  private function filter_admin_only_fields( $fields = array(), $edit_fields = null, $form = array(), $view_id = 0 ) {
1768 
1769  /**
1770  * @filter `gravityview/edit_entry/use_gf_admin_only_setting` When Edit tab isn't configured, should the Gravity Forms "Admin Only" field settings be used to control field display to non-admins? Default: true
1771  * If the Edit Entry tab is not configured, adminOnly fields will not be shown to non-administrators.
1772  * If the Edit Entry tab *is* configured, adminOnly fields will be shown to non-administrators, using the configured GV permissions
1773  * @since 1.9.1
1774  * @since 1.22.6 Deprecated
1775  * @deprecated Use gravityview/edit_entry/use_gf_visibility_setting instead
1776  * @param boolean $use_gf_adminonly_setting True: Hide field if set to Admin Only in GF and the user is not an admin. False: show field based on GV permissions, ignoring GF permissions.
1777  * @param array $form GF Form array
1778  * @param int $view_id View ID
1779  */
1780  $use_gf_adminonly_setting = apply_filters( 'gravityview/edit_entry/use_gf_admin_only_setting', empty( $edit_fields ), $form, $view_id );
1781 
1782  /**
1783  * @filter `gravityview/edit_entry/use_gf_visibility_setting` When Edit tab isn't configured, should the Gravity Forms "Visibility" field settings be used to control field display? Default: true
1784  * If the Edit Entry tab is not configured, Administrative fields will not be shown to non-administrators.
1785  * If the Edit Entry tab *is* configured, Administrative fields will be shown to non-administrators, using the configured GV permissions
1786  * @since 1.22.6
1787  * @param boolean $use_gf_visibility_setting True: Hide field if set to Administrative in GF and the user is not an admin. False: show field based on GV permissions, ignoring GF permissions.
1788  * @param array $form GF Form array
1789  * @param int $view_id View ID
1790  */
1791  $use_gf_visibility_setting = apply_filters( 'gravityview/edit_entry/use_gf_visibility_setting', empty( $edit_fields ), $form, $view_id );
1792 
1793  if( $use_gf_adminonly_setting && $use_gf_visibility_setting && false === GVCommon::has_cap( 'gravityforms_edit_entries', $this->entry['id'] ) ) {
1794  foreach( $fields as $k => $field ) {
1795  // TODO: Remove when minimum GF Version is 2.0.6.5
1796  if( ! isset( $field->visibility ) ) {
1797  if( $field->adminOnly ) {
1798  unset( $fields[ $k ] );
1799  }
1800  } else{
1801  if( 'administrative' === $field->visibility ) {
1802  unset( $fields[ $k ] );
1803  }
1804  }
1805  }
1806  return $fields;
1807  }
1808 
1809  foreach( $fields as &$field ) {
1810 
1811  // TODO: Remove when minimum GF Version is 2.0.6.5
1812  if( ! isset( $field->visibility ) ) {
1813 
1814  $field->adminOnly = false;
1815 
1816  } else {
1817 
1818  /**
1819  * @filter `gravityview/edit_entry/make_all_fields_visible` Set field visibility to "visible" for all fields
1820  * @since 1.22.6
1821  *
1822  * @param string $visibility 'visible' by default. Default options are 'hidden', 'visible', and 'administrative'
1823  * @param GF_Field $field Form field being modified
1824  * @param int $view_id Current View ID
1825  */
1826  $visibility = apply_filters( 'gravityview/edit_entry/field_visibility', 'visible', $field, $view_id );
1827 
1828  $field->visibility = $visibility;
1829  }
1830  }
1831 
1832  return $fields;
1833  }
1834 
1835  // --- Conditional Logic
1836 
1837  /**
1838  * Conditional logic isn't designed to work with forms that already have content. When switching input values,
1839  * the dependent fields will be blank.
1840  *
1841  * Note: This is because GF populates a JavaScript variable with the input values. This is tough to filter at the input level;
1842  * via the `gform_field_value` filter; it requires lots of legwork. Doing it at the form level is easier.
1843  *
1844  * @since 1.17.4
1845  *
1846  * @param array $form Gravity Forms array object
1847  *
1848  * @return array $form, modified to fix conditional
1849  */
1850  function prefill_conditional_logic( $form ) {
1851 
1852  if( ! GFFormDisplay::has_conditional_logic( $form ) ) {
1853  return $form;
1854  }
1855 
1856  // Have Conditional Logic pre-fill fields as if the data were default values
1857  /** @var GF_Field $field */
1858  foreach ( $form['fields'] as &$field ) {
1859 
1860  if( 'checkbox' === $field->type ) {
1861  foreach ( $field->get_entry_inputs() as $key => $input ) {
1862  $input_id = $input['id'];
1863  $choice = $field->choices[ $key ];
1864  $value = rgar( $this->entry, $input_id );
1865  $match = RGFormsModel::choice_value_match( $field, $choice, $value );
1866  if( $match ) {
1867  $field->choices[ $key ]['isSelected'] = true;
1868  }
1869  }
1870  } else {
1871 
1872  // We need to run through each field to set the default values
1873  foreach ( $this->entry as $field_id => $field_value ) {
1874 
1875  if( floatval( $field_id ) === floatval( $field->id ) ) {
1876 
1877  if( 'list' === $field->type ) {
1878  $list_rows = maybe_unserialize( $field_value );
1879 
1880  $list_field_value = array();
1881  foreach ( (array) $list_rows as $row ) {
1882  foreach ( (array) $row as $column ) {
1883  $list_field_value[] = $column;
1884  }
1885  }
1886 
1887  $field->defaultValue = serialize( $list_field_value );
1888  } else {
1889  $field->defaultValue = $field_value;
1890  }
1891  }
1892  }
1893  }
1894  }
1895 
1896  return $form;
1897  }
1898 
1899  /**
1900  * Remove the conditional logic rules from the form button and the form fields, if needed.
1901  *
1902  * @todo Merge with caller method
1903  * @since 1.9
1904  *
1905  * @param array $form Gravity Forms form
1906  * @return array Modified form, if not using Conditional Logic
1907  */
1908  private function filter_conditional_logic( $form ) {
1909 
1910  /**
1911  * @filter `gravityview/edit_entry/conditional_logic` Should the Edit Entry form use Gravity Forms conditional logic showing/hiding of fields?
1912  * @since 1.9
1913  * @param bool $use_conditional_logic True: Gravity Forms will show/hide fields just like in the original form; False: conditional logic will be disabled and fields will be shown based on configuration. Default: true
1914  * @param array $form Gravity Forms form
1915  */
1916  $use_conditional_logic = apply_filters( 'gravityview/edit_entry/conditional_logic', true, $form );
1917 
1918  if( $use_conditional_logic ) {
1919  return $form;
1920  }
1921 
1922  foreach( $form['fields'] as &$field ) {
1923  /* @var GF_Field $field */
1924  $field->conditionalLogic = null;
1925  }
1926 
1927  unset( $form['button']['conditionalLogic'] );
1928 
1929  return $form;
1930 
1931  }
1932 
1933  /**
1934  * Disable the Gravity Forms conditional logic script and features on the Edit Entry screen
1935  *
1936  * @since 1.9
1937  *
1938  * @param $has_conditional_logic
1939  * @param $form
1940  * @return mixed
1941  */
1942  public function manage_conditional_logic( $has_conditional_logic, $form ) {
1943 
1944  if( ! $this->is_edit_entry() ) {
1945  return $has_conditional_logic;
1946  }
1947 
1948  /** @see GravityView_Edit_Entry_Render::filter_conditional_logic for filter documentation */
1949  return apply_filters( 'gravityview/edit_entry/conditional_logic', $has_conditional_logic, $form );
1950  }
1951 
1952 
1953  // --- User checks and nonces
1954 
1955  /**
1956  * Check if the user can edit the entry
1957  *
1958  * - Is the nonce valid?
1959  * - Does the user have the right caps for the entry
1960  * - Is the entry in the trash?
1961  *
1962  * @todo Move to GVCommon
1963  *
1964  * @param boolean $echo Show error messages in the form?
1965  * @return boolean True: can edit form. False: nope.
1966  */
1967  private function user_can_edit_entry( $echo = false ) {
1968 
1969  $error = NULL;
1970 
1971  /**
1972  * 1. Permalinks are turned off
1973  * 2. There are two entries embedded using oEmbed
1974  * 3. One of the entries has just been saved
1975  */
1976  if( !empty( $_POST['lid'] ) && !empty( $_GET['entry'] ) && ( $_POST['lid'] !== $_GET['entry'] ) ) {
1977 
1978  $error = true;
1979 
1980  }
1981 
1982  if( !empty( $_GET['entry'] ) && (string)$this->entry['id'] !== $_GET['entry'] ) {
1983 
1984  $error = true;
1985 
1986  } elseif( ! $this->verify_nonce() ) {
1987 
1988  /**
1989  * If the Entry is embedded, there may be two entries on the same page.
1990  * If that's the case, and one is being edited, the other should fail gracefully and not display an error.
1991  */
1992  if( GravityView_oEmbed::getInstance()->get_entry_id() ) {
1993  $error = true;
1994  } else {
1995  $error = __( 'The link to edit this entry is not valid; it may have expired.', 'gravityview');
1996  }
1997 
1998  }
1999 
2000  if( ! GravityView_Edit_Entry::check_user_cap_edit_entry( $this->entry ) ) {
2001  $error = __( 'You do not have permission to edit this entry.', 'gravityview');
2002  }
2003 
2004  if( $this->entry['status'] === 'trash' ) {
2005  $error = __('You cannot edit the entry; it is in the trash.', 'gravityview' );
2006  }
2007 
2008  // No errors; everything's fine here!
2009  if( empty( $error ) ) {
2010  return true;
2011  }
2012 
2013  if( $echo && $error !== true ) {
2014 
2015  $error = esc_html( $error );
2016 
2017  /**
2018  * @since 1.9
2019  */
2020  if ( ! empty( $this->entry ) ) {
2021  $error .= ' ' . gravityview_get_link( '#', _x('Go back.', 'Link shown when invalid Edit Entry link is clicked', 'gravityview' ), array( 'onclick' => "window.history.go(-1); return false;" ) );
2022  }
2023 
2024  echo GVCommon::generate_notice( wpautop( $error ), 'gv-error error');
2025  }
2026 
2027  do_action('gravityview_log_error', 'GravityView_Edit_Entry[user_can_edit_entry]' . $error );
2028 
2029  return false;
2030  }
2031 
2032 
2033  /**
2034  * Check whether a field is editable by the current user, and optionally display an error message
2035  * @uses GravityView_Edit_Entry->check_user_cap_edit_field() Check user capabilities
2036  * @param array $field Field or field settings array
2037  * @param boolean $echo Whether to show error message telling user they aren't allowed
2038  * @return boolean True: user can edit the current field; False: nope, they can't.
2039  */
2040  private function user_can_edit_field( $field, $echo = false ) {
2041 
2042  $error = NULL;
2043 
2044  if( ! $this->check_user_cap_edit_field( $field ) ) {
2045  $error = __( 'You do not have permission to edit this field.', 'gravityview');
2046  }
2047 
2048  // No errors; everything's fine here!
2049  if( empty( $error ) ) {
2050  return true;
2051  }
2052 
2053  if( $echo ) {
2054  echo GVCommon::generate_notice( wpautop( esc_html( $error ) ), 'gv-error error');
2055  }
2056 
2057  do_action('gravityview_log_error', 'GravityView_Edit_Entry[user_can_edit_field]' . $error );
2058 
2059  return false;
2060 
2061  }
2062 
2063 
2064  /**
2065  * checks if user has permissions to edit a specific field
2066  *
2067  * Needs to be used combined with GravityView_Edit_Entry::user_can_edit_field for maximum security!!
2068  *
2069  * @param [type] $field [description]
2070  * @return bool
2071  */
2072  private function check_user_cap_edit_field( $field ) {
2073 
2074  // If they can edit any entries (as defined in Gravity Forms), we're good.
2075  if( GVCommon::has_cap( array( 'gravityforms_edit_entries', 'gravityview_edit_others_entries' ) ) ) {
2076  return true;
2077  }
2078 
2079  $field_cap = isset( $field['allow_edit_cap'] ) ? $field['allow_edit_cap'] : false;
2080 
2081  // If the field has custom editing capaibilities set, check those
2082  if( $field_cap ) {
2083  return GVCommon::has_cap( $field['allow_edit_cap'] );
2084  }
2085 
2086  return false;
2087  }
2088 
2089 
2090  /**
2091  * Is the current nonce valid for editing the entry?
2092  * @return boolean
2093  */
2094  public function verify_nonce() {
2095 
2096  // Verify form submitted for editing single
2097  if( $this->is_edit_entry_submission() ) {
2098  $valid = wp_verify_nonce( $_POST[ self::$nonce_field ], self::$nonce_field );
2099  }
2100 
2101  // Verify
2102  else if( ! $this->is_edit_entry() ) {
2103  $valid = false;
2104  }
2105 
2106  else {
2107  $valid = wp_verify_nonce( $_GET['edit'], self::$nonce_key );
2108  }
2109 
2110  /**
2111  * @filter `gravityview/edit_entry/verify_nonce` Override Edit Entry nonce validation. Return true to declare nonce valid.
2112  * @since 1.13
2113  * @param int|boolean $valid False if invalid; 1 or 2 when nonce was generated
2114  * @param string $nonce_field Key used when validating submissions. Default: is_gv_edit_entry
2115  */
2116  $valid = apply_filters( 'gravityview/edit_entry/verify_nonce', $valid, self::$nonce_field );
2117 
2118  return $valid;
2119  }
2120 
2121 
2122  /**
2123  * Multiselect in GF 2.2 became a json_encoded value. Fix it.
2124  *
2125  * As a hack for now we'll implode it back.
2126  */
2128  if ( empty ( $field->storageType ) || $field->storageType != 'json' ) {
2129  return $field_value;
2130  }
2131 
2132  $maybe_json = @json_decode( $field_value, true );
2133 
2134  if ( $maybe_json ) {
2135  return implode( ',', $maybe_json );
2136  }
2137 
2138  return $field_value;
2139  }
2140 
2141 
2142 
2143 } //end class
get_configured_edit_fields( $form, $view_id)
Get the Edit Entry fields as configured in the View.
const GRAVITYVIEW_DIR
"GRAVITYVIEW_DIR" "./" The absolute path to the plugin directory, with trailing slash ...
Definition: gravityview.php:35
$entry
static media_handle_upload( $url, $post_id, $post_data=array())
Copied function from Gravity Forms plugin ::media_handle_upload since the method is private...
static getInstance( $passed_post=NULL)
edit_entry_form()
Display the Edit Entry form.
$fields_with_calculation
$image_meta
Definition: post_image.php:106
static getInstance( $passed_post=NULL)
Definition: class-data.php:164
fill_post_template( $template, $form, $entry, $do_shortcode=false)
Convert a field content template into prepared output.
gravityview_get_entry( $entry_slug, $force_allow_ids=false, $check_entry_display=true)
Return a single entry object.
if(! function_exists( 'gravityview_sanitize_html_class')) gravityview_strip_whitespace( $string)
Replace multiple newlines, tabs, and spaces with a single space.
$back_link
load()
update_calculation_fields()
$is_valid
filter_conditional_logic( $form)
Remove the conditional logic rules from the form button and the form fields, if needed.
is_edit_entry()
Is the current page an Edit Entry page?
static check_user_cap_edit_entry( $entry, $view_id=0)
checks if user has permissions to edit a specific entry
static get_post_field_images( $form, $entry)
Given information provided in an entry, get array of media IDs.
$ary
Definition: post_image.php:24
is_field_json_encoded( $field)
Is the field stored in a JSON-encoded manner?
render_edit_form()
Display the Edit Entry form in the original Gravity Forms format.
static generate_notice( $notice, $class='', $cap='', $object_id=null)
Display updated/error notice.
process_save_process_files( $form_id)
Have GF handle file uploads.
$entries
gravityview_get_link( $href='', $anchor_text='', $atts=array())
Generate an HTML anchor tag with a list of supported attributes.
save_field_value( $value='', $entry=array(), $field=null, $form=array(), $input_id='')
Make sure the fileuploads are not overwritten if no such request was done.
after_update()
Perform actions normally performed after updating a lead.
static $nonce_field
maybe_print_message()
Display success or error message if the form has been submitted.
setup_vars()
When Edit entry view is requested setup the vars.
unset_hidden_field_values()
Delete the value of fields hidden by conditional logic when the entry is edited.
gravityview_ob_include( $file_path, $object=NULL)
Get the contents of a file using include() and ob_start()
$view_id
gform_pre_validation( $form)
Add field keys that Gravity Forms expects.
update_post_image( $form, $field, $field_id, $value, $entry, $post_id)
Handle updating the Post Image field.
static $nonce_key
print_scripts()
Force Gravity Forms to output scripts as if it were in the admin.
manage_conditional_logic( $has_conditional_logic, $form)
Disable the Gravity Forms conditional logic script and features on the Edit Entry screen...
$form_id
check_user_cap_edit_field( $field)
checks if user has permissions to edit a specific field
$gv_field
Definition: time.php:11
__construct(GravityView_Edit_Entry $loader)
static get_associated_field( $gf_field)
Alias for get_instance()
user_can_edit_field( $field, $echo=false)
Check whether a field is editable by the current user, and optionally display an error message Gravi...
static by_id( $post_id)
Construct a instance from a post ID.
static get_nonce_key( $view_id, $form_id, $entry_id)
Return a well formatted nonce key according to GravityView Edit Entry protocol.
static $original_form
merge_field_properties( $field, $field_setting)
Override GF Form field properties with the ones defined on the View.
prevent_maybe_process_form()
Because we&#39;re mimicking being a front-end Gravity Forms form while using a Gravity Forms backend form...
static $file
get_field_value( $field)
Modify the value for the current field input.
modify_fileupload_settings( $plupload_init, $form_id, $instance)
Remove max_files validation (done on gravityforms.js) to avoid conflicts with GravityView Late valida...
static is_single_entry()
Verify if user requested a single entry view.
$field_id
Definition: time.php:17
process_save()
Process edit entry form save.
static $original_entry
static $supports_save_and_continue
validate()
Process validation for a edit entry submission.
verify_nonce()
Is the current nonce valid for editing the entry?
get_entry()
TODO: This seems to be hacky...
render_form_buttons()
Display the Update/Cancel/Delete buttons for the Edit Entry form.
filter_modify_form_fields( $form, $ajax=false, $field_values='')
Modify the form fields that are shown when using GFFormDisplay::get_form()
gravityview()
The main GravityView wrapper function.
is_edit_entry_submission()
Is the current page an Edit Entry page?
static get_database_version()
Make sure the method exists, regardless of GF version.
$form_after_validation
init( $gv_data)
Load required files and trigger edit flow.
gv_empty( $value, $zero_is_empty=true, $allow_string_booleans=true)
Is the value empty?
static has_cap( $caps='', $object_id=null, $user_id=null)
Alias of GravityView_Roles_Capabilities::has_cap()
maybe_update_post_fields( $form)
Loop through the fields being edited and if they include Post fields, update the Entry&#39;s post object...
$loader
user_registration_validation( $validation_results)
Make validation work for User Registration feeds.
user_can_edit_entry( $echo=false)
Check if the user can edit the entry.
$form
verify_user_can_edit_post( $field_content='', $field, $value, $lead_id=0, $form_id)
When displaying a field, check if it&#39;s a Post Field, and if so, make sure the post exists and current...
custom_validation( $validation_results)
Make validation work for Edit Entry.
fix_multiselect_value_serialization( $field_value, $field, $_this)
Multiselect in GF 2.2 became a json_encoded value.
filter_admin_only_fields( $fields=array(), $edit_fields=null, $form=array(), $view_id=0)
Remove fields that shouldn&#39;t be visible based on the Gravity Forms visibility field property...
prevent_render_form()
Don&#39;t show any forms embedded on a page when GravityView is in Edit Entry mode.
$field_value
Definition: checkbox.php:24
$field
Definition: gquiz_grade.php:11