Completed
Push — develop ( 87b48c...882b48 )
by Gennady
23:47 queued 03:49
created

GravityView_Edit_Entry_Render::init()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.1374

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 4
dl 0
loc 32
ccs 14
cts 17
cp 0.8235
crap 5.1374
rs 9.0968
c 0
b 0
f 0
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
16
class GravityView_Edit_Entry_Render {
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
	 * The View.
49
	 *
50
	 * @var \GV\View.
51
	 * @since develop
52
	 */
53
	public $view;
54
55
	/**
56
	 * Gravity Forms entry array (it won't get changed during this class lifecycle)
57
	 * @since 1.17.2
58
	 * @var array
59
	 */
60
	private static $original_entry = array();
61
62
	/**
63
	 * Gravity Forms form array (GravityView modifies the content through this class lifecycle)
64
	 *
65
	 * @var array
66
	 */
67
	public $form;
68
69
	/**
70
	 * Gravity Forms form array (it won't get changed during this class lifecycle)
71
	 * @since 1.16.2.1
72
	 * @var array
73
	 */
74
	private static $original_form;
75
76
	/**
77
	 * Gravity Forms form array after the form validation process
78
	 * @since 1.13
79
	 * @var array
80
	 */
81
	public $form_after_validation = null;
82
83
	/**
84
	 * Hold an array of GF field objects that have calculation rules
85
	 * @var array
86
	 */
87
	public $fields_with_calculation = array();
88
89
	/**
90
	 * Gravity Forms form id
91
	 *
92
	 * @var int
93
	 */
94
	public $form_id;
95
96
	/**
97
	 * ID of the current view
98
	 *
99
	 * @var int
100
	 */
101
	public $view_id;
102
103
	/**
104
	 * ID of the current post. May also be ID of the current View.
105
     *
106
     * @since 2.0.13
107
     * 
108
     * @var int
109
	 */
110
	public $post_id;
111
112
	/**
113
	 * Updated entry is valid (GF Validation object)
114
	 *
115
	 * @var array
116
	 */
117
	public $is_valid = NULL;
118
119
	/**
120
	 * Internal page button states.
121
	 *
122
	 * @var bool
123
	 *
124
	 * @since develop
125
	 */
126
	public $show_previous_button;
127
	public $show_next_button;
128
	public $show_update_button;
129
	public $is_paged_submitted;
130
131 22
	function __construct( GravityView_Edit_Entry $loader ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
132 22
		$this->loader = $loader;
133 22
	}
134
135 22
	function load() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
136
137
		/** @define "GRAVITYVIEW_DIR" "../../../" */
138 22
		include_once( GRAVITYVIEW_DIR .'includes/class-admin-approve-entries.php' );
139
140
		// Don't display an embedded form when editing an entry
141 22
		add_action( 'wp_head', array( $this, 'prevent_render_form' ) );
142 22
		add_action( 'wp_footer', array( $this, 'prevent_render_form' ) );
143
144
		// Stop Gravity Forms processing what is ours!
145 22
		add_filter( 'wp', array( $this, 'prevent_maybe_process_form'), 8 );
146
147 22
		add_filter( 'gravityview_is_edit_entry', array( $this, 'is_edit_entry') );
148
149 22
		add_action( 'gravityview_edit_entry', array( $this, 'init' ), 10, 4 );
150
151
		// Disable conditional logic if needed (since 1.9)
152 22
		add_filter( 'gform_has_conditional_logic', array( $this, 'manage_conditional_logic' ), 10, 2 );
153
154
		// Make sure GF doesn't validate max files (since 1.9)
155 22
		add_filter( 'gform_plupload_settings', array( $this, 'modify_fileupload_settings' ), 10, 3 );
156
157
		// Add fields expected by GFFormDisplay::validate()
158 22
		add_filter( 'gform_pre_validation', array( $this, 'gform_pre_validation') );
159
160
		// Fix multiselect value for GF 2.2
161 22
		add_filter( 'gravityview/edit_entry/field_value_multiselect', array( $this, 'fix_multiselect_value_serialization' ), 10, 3 );
162 22
	}
163
164
	/**
165
	 * Don't show any forms embedded on a page when GravityView is in Edit Entry mode
166
	 *
167
	 * Adds a `__return_empty_string` filter on the Gravity Forms shortcode on the `wp_head` action
168
	 * And then removes it on the `wp_footer` action
169
	 *
170
	 * @since 1.16.1
171
	 *
172
	 * @return void
173
	 */
174 1
	public function prevent_render_form() {
175 1
		if( $this->is_edit_entry() ) {
176 1
			if( 'wp_head' === current_filter() ) {
177 1
				add_filter( 'gform_shortcode_form', '__return_empty_string' );
178
			} else {
179 1
				remove_filter( 'gform_shortcode_form', '__return_empty_string' );
180
			}
181
		}
182 1
	}
183
184
	/**
185
	 * Because we're mimicking being a front-end Gravity Forms form while using a Gravity Forms
186
	 * backend form, we need to prevent them from saving twice.
187
	 * @return void
188
	 */
189 1
	public function prevent_maybe_process_form() {
190
191 1
	    if( ! $this->is_edit_entry_submission() ) {
192 1
			return;
193
		}
194
195
		gravityview()->log->debug( 'GravityView_Edit_Entry[prevent_maybe_process_form] Removing GFForms::maybe_process_form() action.' );
196
197
		remove_action( 'wp',  array( 'RGForms', 'maybe_process_form'), 9 );
198
		remove_action( 'wp',  array( 'GFForms', 'maybe_process_form'), 9 );
199
	}
200
201
	/**
202
	 * Is the current page an Edit Entry page?
203
	 * @return boolean
204
	 */
205 27
	public function is_edit_entry() {
206
207 27
		$is_edit_entry = GravityView_frontend::is_single_entry() && ! empty( $_GET['edit'] );
208
209 27
		return ( $is_edit_entry || $this->is_edit_entry_submission() );
210
	}
211
212
	/**
213
	 * Is the current page an Edit Entry page?
214
	 * @since 1.9
215
	 * @return boolean
216
	 */
217 27
	public function is_edit_entry_submission() {
218 27
		return !empty( $_POST[ self::$nonce_field ] );
219
	}
220
221
	/**
222
	 * When Edit entry view is requested setup the vars
223
	 */
224 22
	private function setup_vars() {
225 22
        global $post;
226
227 22
		$gravityview_view = GravityView_View::getInstance();
228
229
230 22
		$entries = $gravityview_view->getEntries();
231 22
	    self::$original_entry = $entries[0];
232 22
	    $this->entry = $entries[0];
233
234 22
		self::$original_form = $gravityview_view->getForm();
235 22
		$this->form = $gravityview_view->getForm();
236 22
		$this->form_id = $this->entry['form_id'];
237 22
		$this->view_id = $gravityview_view->getViewId();
238 22
		$this->post_id = \GV\Utils::get( $post, 'ID', null );
239
240 22
		self::$nonce_key = GravityView_Edit_Entry::get_nonce_key( $this->view_id, $this->form_id, $this->entry['id'] );
241 22
	}
242
243
244
	/**
245
	 * Load required files and trigger edit flow
246
	 *
247
	 * Run when the is_edit_entry returns true.
248
	 *
249
	 * @param \GravityView_View_Data $gv_data GravityView Data object
250
	 * @param \GV\Entry   $entry   The Entry.
251
	 * @param \GV\View    $view    The View.
252
	 * @param \GV\Request $request The Request.
253
	 *
254
	 * @since develop Added $entry, $view, $request adhocs.
255
	 *
256
	 * @return void
257
	 */
258 23
	public function init( $gv_data = null, $entry = null, $view = null, $request = null ) {
259
260 23
		require_once( GFCommon::get_base_path() . '/form_display.php' );
261 23
		require_once( GFCommon::get_base_path() . '/entry_detail.php' );
262
263 23
		$this->setup_vars();
264
265 23
		if ( ! $gv_data ) {
266
			$gv_data = GravityView_View_Data::getInstance();
267
		}
268
269
		// Multiple Views embedded, don't proceed if nonce fails
270 23
		if ( $gv_data->has_multiple_views() && ! $this->verify_nonce() ) {
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::has_multiple_views() has been deprecated.

This method has been deprecated.

Loading history...
271
			gravityview()->log->error( 'Nonce validation failed for the Edit Entry request; returning' );
272
			return;
273
		}
274
275
		// Sorry, you're not allowed here.
276 23
		if ( false === $this->user_can_edit_entry( true ) ) {
277 2
			gravityview()->log->error( 'User is not allowed to edit this entry; returning', array( 'data' => $this->entry ) );
278 2
			return;
279
		}
280
281 23
		$this->view = $view;
282
283 23
		$this->print_scripts();
284
285 23
		$this->process_save( $gv_data );
286
287 23
		$this->edit_entry_form();
288
289 23
	}
290
291
292
	/**
293
	 * Force Gravity Forms to output scripts as if it were in the admin
294
	 * @return void
295
	 */
296 22
	private function print_scripts() {
297 22
		$gravityview_view = GravityView_View::getInstance();
298
299 22
		wp_register_script( 'gform_gravityforms', GFCommon::get_base_url().'/js/gravityforms.js', array( 'jquery', 'gform_json', 'gform_placeholder', 'sack', 'plupload-all', 'gravityview-fe-view' ) );
300
301 22
		GFFormDisplay::enqueue_form_scripts( $gravityview_view->getForm(), false);
302
303 22
		wp_localize_script( 'gravityview-fe-view', 'gvGlobals', array( 'cookiepath' => COOKIEPATH ) );
304
305
		// Sack is required for images
306 22
		wp_print_scripts( array( 'sack', 'gform_gravityforms', 'gravityview-fe-view' ) );
307 22
	}
308
309
310
	/**
311
	 * Process edit entry form save
312
	 *
313
	 * @param array $gv_data The View data.
314
	 */
315 23
	private function process_save( $gv_data ) {
316
317 23
		if ( empty( $_POST ) || ! isset( $_POST['lid'] ) ) {
318 5
			return;
319
		}
320
321
		// Make sure the entry, view, and form IDs are all correct
322 22
		$valid = $this->verify_nonce();
323
324 22
		if ( !$valid ) {
325
			gravityview()->log->error( 'Nonce validation failed.' );
326
			return;
327
		}
328
329 22
		if ( $this->entry['id'] !== $_POST['lid'] ) {
330
			gravityview()->log->error( 'Entry ID did not match posted entry ID.' );
331
			return;
332
		}
333
334 22
		gravityview()->log->debug( '$_POSTed data (sanitized): ', array( 'data' => esc_html( print_r( $_POST, true ) ) ) );
335
336 22
		$this->process_save_process_files( $this->form_id );
337
338 22
		$this->validate();
339
340 22
		if( $this->is_valid ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_valid of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
341
342 22
			gravityview()->log->debug( 'Submission is valid.' );
343
344
			/**
345
			 * @hack This step is needed to unset the adminOnly from form fields, to add the calculation fields
346
			 */
347 22
			$form = $this->form_prepare_for_save();
348
349
			/**
350
			 * @hack to avoid the capability validation of the method save_lead for GF 1.9+
351
			 */
352 22
			unset( $_GET['page'] );
353
354 22
			$date_created = $this->entry['date_created'];
355
356
			/**
357
			 * @hack to force Gravity Forms to use $read_value_from_post in GFFormsModel::save_lead()
358
			 * @since 1.17.2
359
			 */
360 22
			unset( $this->entry['date_created'] );
361
362
			/**
363
			 * @action `gravityview/edit_entry/before_update` Perform an action after the entry has been updated using Edit Entry
364
			 * @since develop
365
			 * @param array $form Gravity Forms form array
366
			 * @param string $entry_id Numeric ID of the entry that is being updated
367
			 * @param GravityView_Edit_Entry_Render $this This object
368
			 * @param GravityView_View_Data $gv_data The View data
369
			 */
370 22
			do_action( 'gravityview/edit_entry/before_update', $form, $this->entry['id'], $this, $gv_data );
371
372 22
			GFFormsModel::save_lead( $form, $this->entry );
373
374
	        // Delete the values for hidden inputs
375 22
	        $this->unset_hidden_field_values();
376
			
377 22
			$this->entry['date_created'] = $date_created;
378
379
			// Process calculation fields
380 22
			$this->update_calculation_fields();
381
382
			// Handle hidden approval fields (or their absense)
383 22
			$this->preset_approval_fields();
384
385
			// Perform actions normally performed after updating a lead
386 22
			$this->after_update();
387
388
	        /**
389
			 * Must be AFTER after_update()!
390
			 * @see https://github.com/gravityview/GravityView/issues/764
391
			 */
392 22
			$this->maybe_update_post_fields( $form );
393
394
			/**
395
			 * @action `gravityview/edit_entry/after_update` Perform an action after the entry has been updated using Edit Entry
396
             * @since 2.1 Added $gv_data parameter
397
			 * @param array $form Gravity Forms form array
398
			 * @param string $entry_id Numeric ID of the entry that was updated
399
			 * @param GravityView_Edit_Entry_Render $this This object
400
			 * @param GravityView_View_Data $gv_data The View data
401
			 */
402 22
			do_action( 'gravityview/edit_entry/after_update', $this->form, $this->entry['id'], $this, $gv_data );
403
404
		} else {
405
			gravityview()->log->error( 'Submission is NOT valid.', array( 'entry' => $this->entry ) );
406
		}
407
408 22
	} // process_save
409
410
	/**
411
	 * Delete the value of fields hidden by conditional logic when the entry is edited
412
	 *
413
	 * @uses GFFormsModel::update_lead_field_value()
414
	 *
415
	 * @since 1.17.4
416
	 *
417
	 * @return void
418
	 */
419 21
	private function unset_hidden_field_values() {
420 21
	    global $wpdb;
421
422
		/**
423
		 * @filter `gravityview/edit_entry/unset_hidden_field_values` Whether to delete values of fields hidden by conditional logic
424
		 * @since 1.22.2
425
		 * @param bool $unset_hidden_field_values Default: true
426
		 * @param GravityView_Edit_Entry_Render $this This object
427
		 */
428 21
		$unset_hidden_field_values = apply_filters( 'gravityview/edit_entry/unset_hidden_field_values', true, $this );
429
430 21
		$this->unset_hidden_calculations = array();
0 ignored issues
show
Bug introduced by
The property unset_hidden_calculations does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
431
432 21
		if ( ! $unset_hidden_field_values ) {
433
			return;
434
		}
435
436 21
		if ( version_compare( GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '>=' ) && method_exists( 'GFFormsModel', 'get_entry_meta_table_name' ) ) {
437 21
			$entry_meta_table = GFFormsModel::get_entry_meta_table_name();
438 21
			$current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $entry_meta_table WHERE entry_id=%d", $this->entry['id'] ) );
439
		} else {
440
			$lead_detail_table = GFFormsModel::get_lead_details_table_name();
441
			$current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $lead_detail_table WHERE lead_id=%d", $this->entry['id'] ) );
442
		}
443
444 21
	    foreach ( $this->entry as $input_id => $field_value ) {
445
446 21
			if ( ! is_numeric( $input_id ) ) {
447 21
				continue;
448
			}
449
450 21
			if ( ! $field = RGFormsModel::get_field( $this->form, $input_id ) ) {
451 7
				continue;
452
			}
453
454
		    // Reset fields that are or would be hidden
455 21
		    if ( GFFormsModel::is_field_hidden( $this->form, $field, array(), $this->entry ) ) {
456
457 3
				$empty_value = $field->get_value_save_entry(
458 3
					is_array( $field->get_entry_inputs() ) ? array() : '',
459 3
					$this->form, '', $this->entry['id'], $this->entry
460
				);
461
462 3
				if ( $field->has_calculation() ) {
463 1
					$this->unset_hidden_calculations[] = $field->id; // Unset
464 1
					$empty_value = '';
465
				}
466
467 3
			    $lead_detail_id = GFFormsModel::get_lead_detail_id( $current_fields, $input_id );
468
469 3
			    GFFormsModel::update_lead_field_value( $this->form, $this->entry, $field, $lead_detail_id, $input_id, $empty_value );
470
471
			    // Prevent the $_POST values of hidden fields from being used as default values when rendering the form
472
				// after submission
473 3
			    $post_input_id = 'input_' . str_replace( '.', '_', $input_id );
474 3
			    $_POST[ $post_input_id ] = '';
475
		    }
476
	    }
477 21
	}
478
479
	/**
480
	 * Leverage `gravityview/approve_entries/update_unapproved_meta` to prevent
481
	 * the missing/empty approval field to affect is_approved meta at all.
482
	 *
483
	 * Called before the Gravity Forms after_update triggers.
484
	 * 
485
	 * @since 2.5
486
	 *
487
	 * @return void
488
	 */
489 21
	private function preset_approval_fields() {
490 21
		$has_approved_field = false;
491
492 21
		foreach ( self::$original_form['fields'] as $field ) {
493 21
			if ( $field->gravityview_approved ) {
494 1
				$has_approved_field = true;
495 1
				break;
496
			}
497
		}
498
499 21
		if ( ! $has_approved_field ) {
500 20
			return;
501
		}
502
503 1
		$is_field_hidden = true;
504
505 1
		foreach ( $this->form['fields'] as $field ) {
506 1
			if ( $field->gravityview_approved ) {
507 1
				$is_field_hidden = false;
508 1
				break;
509
			}
510
		}
511
512 1
		if ( ! $is_field_hidden ) {
513 1
			return;
514
		}
515
516 1
		add_filter( 'gravityview/approve_entries/update_unapproved_meta', array( $this, 'prevent_update_unapproved_meta' ), 9, 3 );
517 1
	}
518
519
	/**
520
	 * Done once from self::preset_approval_fields
521
	 *
522
	 * @since 2.5
523
	 *
524
	 * @return string UNAPPROVED unless something else is inside the entry.
525
	 */
526 1
	public function prevent_update_unapproved_meta( $value, $form, $entry ) {
527
		
528 1
		remove_filter( 'gravityview/approve_entries/update_unapproved_meta', array( $this, 'prevent_update_unapproved_meta' ), 9 );
529
		
530 1
		if ( ! $value = gform_get_meta( $entry['id'], 'is_approved' ) ) {
531
			
532
			$value = GravityView_Entry_Approval_Status::UNAPPROVED;
533
			
534
			$value = apply_filters( 'gravityview/approve_entries/after_submission/default_status', $value );
535
		}
536
		
537 1
		return $value;
538
	}
539
540
	/**
541
	 * Have GF handle file uploads
542
	 *
543
	 * Copy of code from GFFormDisplay::process_form()
544
	 *
545
	 * @param int $form_id
546
	 */
547 21
	private function process_save_process_files( $form_id ) {
548
549
		//Loading files that have been uploaded to temp folder
550 21
		$files = GFCommon::json_decode( stripslashes( RGForms::post( 'gform_uploaded_files' ) ) );
551 21
		if ( ! is_array( $files ) ) {
552 20
			$files = array();
553
		}
554
555
		/**
556
		 * Make sure the fileuploads are not overwritten if no such request was done.
557
		 * @since 1.20.1
558
		 */
559 21
		add_filter( "gform_save_field_value_$form_id", array( $this, 'save_field_value' ), 99, 5 );
560
561 21
		RGFormsModel::$uploaded_files[ $form_id ] = $files;
562 21
	}
563
564
	/**
565
	 * Make sure the fileuploads are not overwritten if no such request was done.
566
	 *
567
	 * TO ONLY BE USED INTERNALLY; DO NOT DEVELOP ON; MAY BE REMOVED AT ANY TIME.
568
	 *
569
	 * @since 1.20.1
570
	 *
571
	 * @param string $value Field value
572
	 * @param array $entry GF entry array
573
	 * @param GF_Field_FileUpload $field
574
	 * @param array $form GF form array
575
	 * @param string $input_id ID of the input being saved
576
	 *
577
	 * @return string
578
	 */
579 21
	public function save_field_value( $value = '', $entry = array(), $field = null, $form = array(), $input_id = '' ) {
580
581 21
		if ( ! $field || $field->type != 'fileupload' ) {
582 21
			return $value;
583
		}
584
585 1
		$input_name = 'input_' . str_replace( '.', '_', $input_id );
586
587 1
		if ( $field->multipleFiles ) {
588
			if ( empty( $value ) ) {
589
				return json_decode( $entry[ $input_id ], true );
590
			}
591
			return $value;
592
		}
593
594
		/** No file is being uploaded. */
595 1
		if ( empty( $_FILES[ $input_name ]['name'] ) ) {
596
			/** So return the original upload */
597 1
			return $entry[ $input_id ];
598
		}
599
600 1
		return $value;
601
	}
602
603
	/**
604
	 * Remove max_files validation (done on gravityforms.js) to avoid conflicts with GravityView
605
	 * Late validation done on self::custom_validation
606
	 *
607
	 * @param $plupload_init array Plupload settings
608
	 * @param $form_id
609
	 * @param $instance
610
	 * @return mixed
611
	 */
612 2
	public function modify_fileupload_settings( $plupload_init, $form_id, $instance ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $instance is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
613 2
		if( ! $this->is_edit_entry() ) {
614
			return $plupload_init;
615
		}
616
617 2
		$plupload_init['gf_vars']['max_files'] = 0;
618
619 2
		return $plupload_init;
620
	}
621
622
623
	/**
624
	 * Set visibility to visible and convert field input key to string
625
	 * @return array $form
626
	 */
627 21
	private function form_prepare_for_save() {
628
629 21
		$form = $this->filter_conditional_logic( $this->form );
630
631
	    /** @var GF_Field $field */
632 21
		foreach( $form['fields'] as $k => &$field ) {
633
634
			/**
635
			 * Remove the fields with calculation formulas before save to avoid conflicts with GF logic
636
			 * @since 1.16.3
637
			 * @var GF_Field $field
638
			 */
639 21
			if( $field->has_calculation() ) {
640 4
				unset( $form['fields'][ $k ] );
641
			}
642
643 21
			$field->adminOnly = false;
644
645 21
			if( isset( $field->inputs ) && is_array( $field->inputs ) ) {
646 3
				foreach( $field->inputs as $key => $input ) {
647 3
				    $field->inputs[ $key ][ 'id' ] = (string)$input['id'];
648
				}
649
			}
650
		}
651
652 21
		$form['fields'] = array_values( $form['fields'] );
653
654 21
		return $form;
655
	}
656
657 21
	private function update_calculation_fields() {
658 21
		global $wpdb;
659
660 21
		$form = self::$original_form;
661 21
		$update = false;
662
663
		// get the most up to date entry values
664 21
		$entry = GFAPI::get_entry( $this->entry['id'] );
665
666 21
		if ( version_compare( GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '>=' ) && method_exists( 'GFFormsModel', 'get_entry_meta_table_name' ) ) {
667 21
			$entry_meta_table = GFFormsModel::get_entry_meta_table_name();
668 21
			$current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $entry_meta_table WHERE entry_id=%d", $entry['id'] ) );
669
		} else {
670
			$lead_detail_table = GFFormsModel::get_lead_details_table_name();
671
			$current_fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $lead_detail_table WHERE lead_id=%d", $entry['id'] ) );
672
		}
673
674
675 21
		if ( ! empty( $this->fields_with_calculation ) ) {
676 5
			$allowed_fields = $this->get_configured_edit_fields( $form, $this->view_id );
677 5
			$allowed_fields = wp_list_pluck( $allowed_fields, 'id' );
678
679 5
			foreach ( $this->fields_with_calculation as $field ) {
680
681 5
				if ( in_array( $field->id, $this->unset_hidden_calculations, true ) ) {
682 1
					continue;
683
				}
684
685 5
				$inputs = $field->get_entry_inputs();
686 5
				if ( is_array( $inputs ) ) {
687 4
				    foreach ( $inputs as $input ) {
688 4
						list( $field_id, $input_id ) = rgexplode( '.', $input['id'], 2 );
689
690 4
						if ( 'product' === $field->type ) {
691 4
							$input_name = 'input_' . str_replace( '.', '_', $input['id'] );
692
693
							// Only allow quantity to be set if it's allowed to be edited
694 4
							if ( in_array( $field_id, $allowed_fields ) && $input_id == 3 ) {
695
							} else { // otherwise set to what it previously was
696 4
								$_POST[ $input_name ] = $entry[ $input['id'] ];
697
							}
698
						} else {
699
							// Set to what it previously was if it's not editable
700
							if ( ! in_array( $field_id, $allowed_fields ) ) {
701
								$_POST[ $input_name ] = $entry[ $input['id'] ];
0 ignored issues
show
Bug introduced by
The variable $input_name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
702
							}
703
						}
704
705 4
						GFFormsModel::save_input( $form, $field, $entry, $current_fields, $input['id'] );
706
				    }
707
				} else {
708
					// Set to what it previously was if it's not editable
709 3
					if ( ! in_array( $field->id, $allowed_fields ) ) {
710 2
						$_POST[ 'input_' . $field->id ] = $entry[ $field->id ];
711
					}
712 3
					GFFormsModel::save_input( $form, $field, $entry, $current_fields, $field->id );
713
				}
714
			}
715
716 5
			if ( method_exists( 'GFFormsModel', 'commit_batch_field_operations' ) ) {
717 5
				GFFormsModel::commit_batch_field_operations();
718
			}
719
		}
720 21
	}
721
722
	/**
723
	 * Handle updating the Post Image field
724
	 *
725
	 * Sets a new Featured Image if configured in Gravity Forms; otherwise uploads/updates media
726
	 *
727
	 * @since 1.17
728
	 *
729
	 * @uses GFFormsModel::media_handle_upload
730
	 * @uses set_post_thumbnail
731
	 *
732
	 * @param array $form GF Form array
733
	 * @param GF_Field $field GF Field
734
	 * @param string $field_id Numeric ID of the field
735
	 * @param string $value
736
	 * @param array $entry GF Entry currently being edited
737
	 * @param int $post_id ID of the Post being edited
738
	 *
739
	 * @return mixed|string
740
	 */
741 1
	private function update_post_image( $form, $field, $field_id, $value, $entry, $post_id ) {
742
743 1
		$input_name = 'input_' . $field_id;
744
745 1
		if ( !empty( $_FILES[ $input_name ]['name'] ) ) {
746
747
			// We have a new image
748
749
			$value = RGFormsModel::prepare_value( $form, $field, $value, $input_name, $entry['id'] );
750
751
			$ary = ! empty( $value ) ? explode( '|:|', $value ) : array();
752
	        $ary = stripslashes_deep( $ary );
753
			$img_url = \GV\Utils::get( $ary, 0 );
754
755
			$img_title       = count( $ary ) > 1 ? $ary[1] : '';
756
			$img_caption     = count( $ary ) > 2 ? $ary[2] : '';
757
			$img_description = count( $ary ) > 3 ? $ary[3] : '';
758
759
			$image_meta = array(
760
				'post_excerpt' => $img_caption,
761
				'post_content' => $img_description,
762
			);
763
764
			//adding title only if it is not empty. It will default to the file name if it is not in the array
765
			if ( ! empty( $img_title ) ) {
766
				$image_meta['post_title'] = $img_title;
767
			}
768
769
			/**
770
			 * 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.
771
			 * Note: the method became public in GF 1.9.17.7, but we don't require that version yet.
772
			 */
773
			require_once GRAVITYVIEW_DIR . 'includes/class-gravityview-gfformsmodel.php';
774
			$media_id = GravityView_GFFormsModel::media_handle_upload( $img_url, $post_id, $image_meta );
775
776
			// is this field set as featured image?
777
			if ( $media_id && $field->postFeaturedImage ) {
778
				set_post_thumbnail( $post_id, $media_id );
779
			}
780
781 1
		} elseif ( ! empty( $_POST[ $input_name ] ) && is_array( $value ) ) {
782
783 1
			$img_url         = stripslashes_deep( $_POST[ $input_name ] );
784 1
			$img_title       = stripslashes_deep( \GV\Utils::_POST( $input_name . '_1' ) );
785 1
			$img_caption     = stripslashes_deep( \GV\Utils::_POST( $input_name . '_4' ) );
786 1
			$img_description = stripslashes_deep( \GV\Utils::_POST( $input_name . '_7' ) );
787
788 1
			$value = ! empty( $img_url ) ? $img_url . "|:|" . $img_title . "|:|" . $img_caption . "|:|" . $img_description : '';
789
790 1
			if ( $field->postFeaturedImage ) {
791
792
				$image_meta = array(
793 1
					'ID' => get_post_thumbnail_id( $post_id ),
794 1
					'post_title' => $img_title,
795 1
					'post_excerpt' => $img_caption,
796 1
					'post_content' => $img_description,
797
				);
798
799
				// update image title, caption or description
800 1
				wp_update_post( $image_meta );
801
			}
802
		} else {
803
804
			// if we get here, image was removed or not set.
805
			$value = '';
806
807
			if ( $field->postFeaturedImage ) {
808
				delete_post_thumbnail( $post_id );
809
			}
810
		}
811
812 1
		return $value;
813
	}
814
815
	/**
816
	 * Loop through the fields being edited and if they include Post fields, update the Entry's post object
817
	 *
818
	 * @param array $form Gravity Forms form
819
	 *
820
	 * @return void
821
	 */
822 21
	private function maybe_update_post_fields( $form ) {
823
824 21
		if( empty( $this->entry['post_id'] ) ) {
825 20
	        gravityview()->log->debug( 'This entry has no post fields. Continuing...' );
826 20
			return;
827
		}
828
829 1
		$post_id = $this->entry['post_id'];
830
831
		// Security check
832 1
		if( false === GVCommon::has_cap( 'edit_post', $post_id ) ) {
833
			gravityview()->log->error( 'The current user does not have the ability to edit Post #{post_id}', array( 'post_id' => $post_id ) );
834
			return;
835
		}
836
837 1
		$update_entry = false;
838
839 1
		$updated_post = $original_post = get_post( $post_id );
840
841 1
		foreach ( $this->entry as $field_id => $value ) {
842
843 1
			$field = RGFormsModel::get_field( $form, $field_id );
844
845 1
			if( ! $field ) {
846 1
				continue;
847
			}
848
849 1
			if( GFCommon::is_post_field( $field ) && 'post_category' !== $field->type ) {
850
851
				// Get the value of the field, including $_POSTed value
852 1
				$value = RGFormsModel::get_field_value( $field );
853
854
				// Use temporary entry variable, to make values available to fill_post_template() and update_post_image()
855 1
				$entry_tmp = $this->entry;
856 1
				$entry_tmp["{$field_id}"] = $value;
857
858 1
				switch( $field->type ) {
859
860 1
				    case 'post_title':
861
				        $post_title = $value;
862
				        if ( \GV\Utils::get( $form, 'postTitleTemplateEnabled' ) ) {
863
				            $post_title = $this->fill_post_template( $form['postTitleTemplate'], $form, $entry_tmp );
864
				        }
865
				        $updated_post->post_title = $post_title;
866
				        $updated_post->post_name  = $post_title;
867
				        unset( $post_title );
868
				        break;
869
870 1
				    case 'post_content':
871
				        $post_content = $value;
872
				        if ( \GV\Utils::get( $form, 'postContentTemplateEnabled' ) ) {
873
				            $post_content = $this->fill_post_template( $form['postContentTemplate'], $form, $entry_tmp, true );
874
				        }
875
				        $updated_post->post_content = $post_content;
876
				        unset( $post_content );
877
				        break;
878 1
				    case 'post_excerpt':
879
				        $updated_post->post_excerpt = $value;
880
				        break;
881 1
				    case 'post_tags':
882
				        wp_set_post_tags( $post_id, $value, false );
883
				        break;
884 1
				    case 'post_category':
885
				        break;
886 1
				    case 'post_custom_field':
887
						if ( is_array( $value ) && ( floatval( $field_id ) !== floatval( $field->id ) ) ) {
888
							$value = $value[ $field_id ];
889
						}
890
891
				        if( ! empty( $field->customFieldTemplateEnabled ) ) {
892
				            $value = $this->fill_post_template( $field->customFieldTemplate, $form, $entry_tmp, true );
893
				        }
894
895
						$value = $field->get_value_save_entry( $value, $form, '', $this->entry['id'], $this->entry );
896
897
				        update_post_meta( $post_id, $field->postCustomFieldName, $value );
898
				        break;
899
900 1
				    case 'post_image':
901 1
				        $value = $this->update_post_image( $form, $field, $field_id, $value, $this->entry, $post_id );
902 1
				        break;
903
904
				}
905
906
				// update entry after
907 1
				$this->entry["{$field_id}"] = $value;
908
909 1
				$update_entry = true;
910
911 1
				unset( $entry_tmp );
912
			}
913
914
		}
915
916 1
		if( $update_entry ) {
917
918 1
			$return_entry = GFAPI::update_entry( $this->entry );
919
920 1
			if( is_wp_error( $return_entry ) ) {
921
				gravityview()->log->error( 'Updating the entry post fields failed', array( 'data' => array( '$this->entry' => $this->entry, '$return_entry' => $return_entry ) ) );
922
			} else {
923 1
				gravityview()->log->debug( 'Updating the entry post fields for post #{post_id} succeeded', array( 'post_id' => $post_id ) );
924
			}
925
926
		}
927
928 1
		$return_post = wp_update_post( $updated_post, true );
929
930 1
		if( is_wp_error( $return_post ) ) {
931
			$return_post->add_data( $updated_post, '$updated_post' );
932
			gravityview()->log->error( 'Updating the post content failed', array( 'data' => compact( 'updated_post', 'return_post' ) ) );
933
		} else {
934 1
			gravityview()->log->debug( 'Updating the post content for post #{post_id} succeeded', array( 'post_id' => $post_id, 'data' => $updated_post ) );
935
		}
936 1
	}
937
938
	/**
939
	 * Convert a field content template into prepared output
940
	 *
941
	 * @uses GravityView_GFFormsModel::get_post_field_images()
942
	 *
943
	 * @since 1.17
944
	 *
945
	 * @param string $template The content template for the field
946
	 * @param array $form Gravity Forms form
947
	 * @param bool $do_shortcode Whether to process shortcode inside content. In GF, only run on Custom Field and Post Content fields
948
	 *
949
	 * @return string
950
	 */
951
	private function fill_post_template( $template, $form, $entry, $do_shortcode = false ) {
952
953
		require_once GRAVITYVIEW_DIR . 'includes/class-gravityview-gfformsmodel.php';
954
955
		$post_images = GravityView_GFFormsModel::get_post_field_images( $form, $entry );
956
957
		//replacing post image variables
958
		$output = GFCommon::replace_variables_post_image( $template, $post_images, $entry );
959
960
		//replacing all other variables
961
		$output = GFCommon::replace_variables( $output, $form, $entry, false, false, false );
962
963
		// replace conditional shortcodes
964
		if( $do_shortcode ) {
965
			$output = do_shortcode( $output );
966
		}
967
968
		return $output;
969
	}
970
971
972
	/**
973
	 * Perform actions normally performed after updating a lead
974
	 *
975
	 * @since 1.8
976
	 *
977
	 * @see GFEntryDetail::lead_detail_page()
978
	 *
979
	 * @return void
980
	 */
981 21
	private function after_update() {
982
983 21
		do_action( 'gform_after_update_entry', self::$original_form, $this->entry['id'], self::$original_entry );
984 21
		do_action( "gform_after_update_entry_{$this->form['id']}", self::$original_form, $this->entry['id'], self::$original_entry );
985
986
		// Re-define the entry now that we've updated it.
987 21
		$entry = RGFormsModel::get_lead( $this->entry['id'] );
988
989 21
		$entry = GFFormsModel::set_entry_meta( $entry, self::$original_form );
990
991 21
		if ( version_compare( GFFormsModel::get_database_version(), '2.3-dev-1', '<' ) ) {
992
			// We need to clear the cache because Gravity Forms caches the field values, which
993
			// we have just updated.
994
			foreach ($this->form['fields'] as $key => $field) {
995
				GFFormsModel::refresh_lead_field_value( $entry['id'], $field->id );
996
			}
997
		}
998
999
		/**
1000
		 * Maybe process feeds.
1001
		 *
1002
		 * @since develop
1003
		 */
1004 21
		if ( $allowed_feeds = $this->view->settings->get( 'edit_feeds', array() ) ) {
1005 1
			$feeds = GFAPI::get_feeds( null, $entry['form_id'] );
1006 1
			if ( ! is_wp_error( $feeds ) ) {
1007 1
				$registered_feeds = array();
1008 1
				foreach ( GFAddOn::get_registered_addons() as $registered_feed ) {
1009 1
					if ( is_subclass_of( $registered_feed,  'GFFeedAddOn' ) ) {
1010 1
						if ( method_exists( $registered_feed, 'get_instance' ) ) {
1011 1
							$registered_feed = call_user_func( array( $registered_feed, 'get_instance' ) );
1012 1
							$registered_feeds[ $registered_feed->get_slug() ] = $registered_feed;
1013
						}
1014
					}
1015
				}
1016 1
				foreach ( $feeds as $feed ) {
1017 1
					if ( in_array( $feed['id'], $allowed_feeds ) ) {
1018 1
						if ( $feed_object = \GV\Utils::get( $registered_feeds, $feed['addon_slug'] ) ) {
1019 1
							$returned_entry = $feed_object->process_feed( $feed, $entry, self::$original_form );
1020 1
							if ( is_array( $returned_entry ) && rgar( $returned_entry, 'id' ) ) {
1021
								$entry = $returned_entry;
1022
							}
1023
1024 1
							do_action( 'gform_post_process_feed', $feed, $entry, self::$original_form, $feed_object );
1025 1
							$slug = $feed_object->get_slug();
1026 1
							do_action( "gform_{$slug}_post_process_feed", $feed, $entry, self::$original_form, $feed_object );
1027
						}
1028
					}
1029
				}
1030
			}
1031
		}
1032
1033 21
		$this->entry = $entry;
1034 21
	}
1035
1036
1037
	/**
1038
	 * Display the Edit Entry form
1039
	 *
1040
	 * @return void
1041
	 */
1042 22
	public function edit_entry_form() {
1043
1044
		?>
1045
1046
		<div class="gv-edit-entry-wrapper"><?php
1047
1048 22
			$javascript = gravityview_ob_include( GravityView_Edit_Entry::$file .'/partials/inline-javascript.php', $this );
0 ignored issues
show
Bug introduced by
The property file cannot be accessed from this context as it is declared private in class GravityView_Edit_Entry.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1049
1050
			/**
1051
			 * Fixes weird wpautop() issue
1052
			 * @see https://github.com/katzwebservices/GravityView/issues/451
1053
			 */
1054 22
			echo gravityview_strip_whitespace( $javascript );
1055
1056
			?><h2 class="gv-edit-entry-title">
1057
				<span><?php
1058
1059
				    /**
1060
				     * @filter `gravityview_edit_entry_title` Modify the edit entry title
1061
				     * @param string $edit_entry_title Modify the "Edit Entry" title
1062
				     * @param GravityView_Edit_Entry_Render $this This object
1063
				     */
1064 22
				    $edit_entry_title = apply_filters('gravityview_edit_entry_title', __('Edit Entry', 'gravityview'), $this );
1065
1066 22
				    echo esc_attr( $edit_entry_title );
1067
			?></span>
1068
			</h2>
1069
1070
			<?php $this->maybe_print_message(); ?>
1071
1072
			<?php // The ID of the form needs to be `gform_{form_id}` for the pluploader ?>
1073
1074
			<form method="post" id="gform_<?php echo $this->form_id; ?>" enctype="multipart/form-data">
1075
1076
				<?php
1077
1078 22
				wp_nonce_field( self::$nonce_key, self::$nonce_key );
1079
1080 22
				wp_nonce_field( self::$nonce_field, self::$nonce_field, false );
1081
1082
				// Print the actual form HTML
1083 22
				$this->render_edit_form();
1084
1085
				?>
1086 22
			</form>
1087
1088
			<script>
1089
				gform.addFilter('gform_reset_pre_conditional_logic_field_action', function ( reset, formId, targetId, defaultValues, isInit ) {
1090
				    return false;
1091
				});
1092
			</script>
1093
1094
		</div>
1095
1096
	<?php
1097 22
	}
1098
1099
	/**
1100
	 * Display success or error message if the form has been submitted
1101
	 *
1102
	 * @uses GVCommon::generate_notice
1103
	 *
1104
	 * @since 1.16.2.2
1105
	 *
1106
	 * @return void
1107
	 */
1108 22
	private function maybe_print_message() {
1109
1110 22
		if ( \GV\Utils::_POST( 'action' ) === 'update' ) {
1111
1112 21
			if ( GFCommon::has_pages( $this->form ) && apply_filters( 'gravityview/features/paged-edit', false ) ) {
1113
				$labels = array(
1114
					'cancel'   => __( 'Cancel', 'gravityview' ),
1115
					'submit'   => __( 'Update', 'gravityview' ),
1116
					'next'     => __( 'Next', 'gravityview' ),
1117
					'previous' => __( 'Previous', 'gravityview' ),
1118
				);
1119
1120
				/**
1121
				* @filter `gravityview/edit_entry/button_labels` Modify the cancel/submit buttons' labels
1122
				* @since 1.16.3
1123
				* @param array $labels Default button labels associative array
1124
				* @param array $form The Gravity Forms form
1125
				* @param array $entry The Gravity Forms entry
1126
				* @param int $view_id The current View ID
1127
				*/
1128
				$labels = apply_filters( 'gravityview/edit_entry/button_labels', $labels, $this->form, $this->entry, $this->view_id );
1129
1130
				$this->is_paged_submitted = \GV\Utils::_POST( 'save' ) === $labels['submit'];
1131
			}
1132
1133 21
			$back_link = remove_query_arg( array( 'page', 'view', 'edit' ) );
1134
1135 21
			if( ! $this->is_valid ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_valid of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1136
1137
				// Keeping this compatible with Gravity Forms.
1138
				$validation_message = "<div class='validation_error'>" . __('There was a problem with your submission.', 'gravityview') . " " . __('Errors have been highlighted below.', 'gravityview') . "</div>";
1139
				$message = apply_filters("gform_validation_message_{$this->form['id']}", apply_filters("gform_validation_message", $validation_message, $this->form), $this->form);
1140
1141
				echo GVCommon::generate_notice( $message , 'gv-error' );
1142
1143 21
			} elseif ( false === $this->is_paged_submitted ) {
1144
				// Paged form that hasn't been submitted on the last page yet
1145
				$entry_updated_message = sprintf( esc_attr__( 'Entry Updated.', 'gravityview' ), '<a href="' . esc_url( $back_link ) . '">', '</a>' );
1146
1147
				/**
1148
				 * @filter `gravityview/edit_entry/page/success` Modify the edit entry success message on pages
1149
				 * @since develop
1150
				 * @param string $entry_updated_message Existing message
1151
				 * @param int $view_id View ID
1152
				 * @param array $entry Gravity Forms entry array
1153
				 */
1154
				$message = apply_filters( 'gravityview/edit_entry/page/success', $entry_updated_message , $this->view_id, $this->entry );
1155
1156
				echo GVCommon::generate_notice( $message );
1157
			} else {
1158 21
				$view = \GV\View::by_id( $this->view_id );
1159 21
				$edit_redirect = $view->settings->get( 'edit_redirect' );
1160 21
				$edit_redirect_url = $view->settings->get( 'edit_redirect_url' );
1161
1162
				switch ( $edit_redirect ) {
1163
1164 21
                    case '0':
1165 1
	                    $redirect_url = $back_link;
1166 1
	                    $entry_updated_message = sprintf( esc_attr_x('Entry Updated. %sReturning to Entry%s', 'Replacements are HTML', 'gravityview'), '<a href="'. esc_url( $redirect_url ) .'">', '</a>' );
1167 1
                        break;
1168
1169 20
                    case '1':
1170 1
	                    $redirect_url = $directory_link = GravityView_API::directory_link();
1171 1
	                    $entry_updated_message = sprintf( esc_attr_x('Entry Updated. %sReturning to %s%s', 'Replacement 1 is HTML. Replacement 2 is the title of the page where the user will be taken. Replacement 3 is HTML.','gravityview'), '<a href="'. esc_url( $redirect_url ) . '">', esc_html( $view->post_title ), '</a>' );
0 ignored issues
show
Documentation introduced by
The property post_title does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1172 1
	                    break;
1173
1174 19
                    case '2':
1175 1
	                    $redirect_url = $edit_redirect_url;
1176 1
	                    $redirect_url = GFCommon::replace_variables( $redirect_url, $this->form, $this->entry, false, false, false, 'text' );
1177 1
	                    $entry_updated_message = sprintf( esc_attr_x('Entry Updated. %sRedirecting to %s%s', 'Replacement 1 is HTML. Replacement 2 is the URL where the user will be taken. Replacement 3 is HTML.','gravityview'), '<a href="'. esc_url( $redirect_url ) . '">', esc_html( $edit_redirect_url ), '</a>' );
1178 1
                        break;
1179
1180 18
                    case '':
1181
                    default:
1182 18
					    $entry_updated_message = sprintf( esc_attr__('Entry Updated. %sReturn to Entry%s', 'gravityview'), '<a href="'. esc_url( $back_link ) .'">', '</a>' );
1183 18
                        break;
1184
				}
1185
1186 21
				if ( isset( $redirect_url ) ) {
1187 3
					$entry_updated_message .= sprintf( '<script>window.location.href = %s;</script><noscript><meta http-equiv="refresh" content="0;URL=%s" /></noscript>', json_encode( $redirect_url ), esc_attr( $redirect_url ) );
1188
				}
1189
1190
				/**
1191
				 * @filter `gravityview/edit_entry/success` Modify the edit entry success message (including the anchor link)
1192
				 * @since 1.5.4
1193
				 * @param string $entry_updated_message Existing message
1194
				 * @param int $view_id View ID
1195
				 * @param array $entry Gravity Forms entry array
1196
				 * @param string $back_link URL to return to the original entry. @since 1.6
1197
				 */
1198 21
				$message = apply_filters( 'gravityview/edit_entry/success', $entry_updated_message , $this->view_id, $this->entry, $back_link );
1199
1200 21
				echo GVCommon::generate_notice( $message );
1201
			}
1202
1203
		}
1204 22
	}
1205
1206
	/**
1207
	 * Display the Edit Entry form in the original Gravity Forms format
1208
	 *
1209
	 * @since 1.9
1210
	 *
1211
	 * @return void
1212
	 */
1213 22
	private function render_edit_form() {
1214
1215
		/**
1216
		 * @action `gravityview/edit-entry/render/before` Before rendering the Edit Entry form
1217
		 * @since 1.17
1218
		 * @param GravityView_Edit_Entry_Render $this
1219
		 */
1220 22
		do_action( 'gravityview/edit-entry/render/before', $this );
1221
1222 22
		add_filter( 'gform_pre_render', array( $this, 'filter_modify_form_fields'), 5000, 3 );
1223 22
		add_filter( 'gform_submit_button', array( $this, 'render_form_buttons') );
1224 22
		add_filter( 'gform_next_button', array( $this, 'render_form_buttons' ) );
1225 22
		add_filter( 'gform_previous_button', array( $this, 'render_form_buttons' ) );
1226 22
		add_filter( 'gform_disable_view_counter', '__return_true' );
1227
1228 22
		add_filter( 'gform_field_input', array( $this, 'verify_user_can_edit_post' ), 5, 5 );
1229 22
		add_filter( 'gform_field_input', array( $this, 'modify_edit_field_input' ), 10, 5 );
1230
1231
		// We need to remove the fake $_GET['page'] arg to avoid rendering form as if in admin.
1232 22
		unset( $_GET['page'] );
1233
1234 22
		$this->show_next_button = false;
1235 22
		$this->show_previous_button = false;
1236
1237
		// TODO: Verify multiple-page forms
1238 22
		if ( GFCommon::has_pages( $this->form ) && apply_filters( 'gravityview/features/paged-edit', false ) ) {
1239
			if ( intval( $page_number = \GV\Utils::_POST( 'gform_source_page_number_' . $this->form['id'], 0 ) ) ) {
1240
1241
				$labels = array(
1242
					'cancel'   => __( 'Cancel', 'gravityview' ),
1243
					'submit'   => __( 'Update', 'gravityview' ),
1244
					'next'     => __( 'Next', 'gravityview' ),
1245
					'previous' => __( 'Previous', 'gravityview' ),
1246
				);
1247
1248
				/**
1249
				* @filter `gravityview/edit_entry/button_labels` Modify the cancel/submit buttons' labels
1250
				* @since 1.16.3
1251
				* @param array $labels Default button labels associative array
1252
				* @param array $form The Gravity Forms form
1253
				* @param array $entry The Gravity Forms entry
1254
				* @param int $view_id The current View ID
1255
				*/
1256
				$labels = apply_filters( 'gravityview/edit_entry/button_labels', $labels, $this->form, $this->entry, $this->view_id );
1257
1258
				GFFormDisplay::$submission[ $this->form['id'] ][ 'form' ] = $this->form;
1259
				GFFormDisplay::$submission[ $this->form['id'] ][ 'is_valid' ] = true;
1260
1261
				if ( \GV\Utils::_POST( 'save' ) === $labels['next'] ) {
1262
					$last_page = \GFFormDisplay::get_max_page_number( $this->form );
1263
1264
					while ( ++$page_number < $last_page && RGFormsModel::is_page_hidden( $this->form, $page_number, \GV\Utils::_POST( 'gform_field_values' ) ) ) {
1265
					} // Advance to next visible page
1266
				} elseif ( \GV\Utils::_POST( 'save' ) === $labels['previous'] ) {
1267
					while ( --$page_number > 1 && RGFormsModel::is_page_hidden( $this->form, $page_number, \GV\Utils::_POST( 'gform_field_values' ) ) ) {
1268
					} // Advance to next visible page
1269
				}
1270
1271
				GFFormDisplay::$submission[ $this->form['id'] ]['page_number'] = $page_number;
1272
			}
1273
1274
			if ( ( $page_number = intval( $page_number ) ) < 2 ) {
1275
				$this->show_next_button = true; // First page
1276
			}
1277
1278
			$last_page = \GFFormDisplay::get_max_page_number( $this->form );
1279
1280
			$has_more_pages = $page_number < $last_page;
1281
1282
			if ( $has_more_pages ) {
1283
				$this->show_next_button = true; // Not the last page
1284
			} else {
1285
				$this->show_update_button = true; // The last page
1286
			}
1287
1288
			if ( $page_number > 1 ) {
1289
				$this->show_previous_button = true; // Not the first page
1290
			}
1291
		} else {
1292 22
			$this->show_update_button = true;
1293
		}
1294
1295 22
		ob_start(); // Prevent PHP warnings possibly caused by prefilling list fields for conditional logic
1296
1297 22
		$html = GFFormDisplay::get_form( $this->form['id'], false, false, true, $this->entry );
1298
1299 22
		ob_get_clean();
1300
1301 22
	    remove_filter( 'gform_pre_render', array( $this, 'filter_modify_form_fields' ), 5000 );
1302 22
		remove_filter( 'gform_submit_button', array( $this, 'render_form_buttons' ) );
1303 22
		remove_filter( 'gform_next_button', array( $this, 'render_form_buttons' ) );
1304 22
		remove_filter( 'gform_previous_button', array( $this, 'render_form_buttons' ) );
1305 22
		remove_filter( 'gform_disable_view_counter', '__return_true' );
1306 22
		remove_filter( 'gform_field_input', array( $this, 'verify_user_can_edit_post' ), 5 );
1307 22
		remove_filter( 'gform_field_input', array( $this, 'modify_edit_field_input' ), 10 );
1308
1309 22
		echo $html;
1310
1311
		/**
1312
		 * @action `gravityview/edit-entry/render/after` After rendering the Edit Entry form
1313
		 * @since 1.17
1314
		 * @param GravityView_Edit_Entry_Render $this
1315
		 */
1316 22
		do_action( 'gravityview/edit-entry/render/after', $this );
1317 22
	}
1318
1319
	/**
1320
	 * Display the Update/Cancel/Delete buttons for the Edit Entry form
1321
	 * @since 1.8
1322
	 * @return string
1323
	 */
1324 22
	public function render_form_buttons() {
1325 22
		return gravityview_ob_include( GravityView_Edit_Entry::$file .'/partials/form-buttons.php', $this );
0 ignored issues
show
Bug introduced by
The property file cannot be accessed from this context as it is declared private in class GravityView_Edit_Entry.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1326
	}
1327
1328
1329
	/**
1330
	 * Modify the form fields that are shown when using GFFormDisplay::get_form()
1331
	 *
1332
	 * By default, all fields will be shown. We only want the Edit Tab configured fields to be shown.
1333
	 *
1334
	 * @param array $form
1335
	 * @param boolean $ajax Whether in AJAX mode
1336
	 * @param array|string $field_values Passed parameters to the form
1337
	 *
1338
	 * @since 1.9
1339
	 *
1340
	 * @return array Modified form array
1341
	 */
1342 22
	public function filter_modify_form_fields( $form, $ajax = false, $field_values = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $ajax is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $field_values is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1343
1344 22
		if( $form['id'] != $this->form_id ) {
1345
			return $form;
1346
		}
1347
1348
		// In case we have validated the form, use it to inject the validation results into the form render
1349 22
		if( isset( $this->form_after_validation ) && $this->form_after_validation['id'] === $form['id'] ) {
1350 21
			$form = $this->form_after_validation;
1351
		} else {
1352 4
			$form['fields'] = $this->get_configured_edit_fields( $form, $this->view_id );
1353
		}
1354
1355 22
		$form = $this->filter_conditional_logic( $form );
1356
1357 22
		$form = $this->prefill_conditional_logic( $form );
1358
1359
		// for now we don't support Save and Continue feature.
1360 22
		if( ! self::$supports_save_and_continue ) {
1361 22
	        unset( $form['save'] );
1362
		}
1363
1364 22
		$form = $this->unselect_default_values( $form );
1365
1366 22
		return $form;
1367
	}
1368
1369
	/**
1370
	 * 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.
1371
	 *
1372
	 * @since 1.16.2.2
1373
	 *
1374
	 * @param string $field_content Always empty. Returning not-empty overrides the input.
1375
	 * @param GF_Field $field
1376
	 * @param string|array $value If array, it's a field with multiple inputs. If string, single input.
1377
	 * @param int $lead_id Lead ID. Always 0 for the `gform_field_input` filter.
1378
	 * @param int $form_id Form ID
1379
	 *
1380
	 * @return string If error, the error message. If no error, blank string (modify_edit_field_input() runs next)
1381
	 */
1382 22
	public function verify_user_can_edit_post( $field_content = '', $field, $value, $lead_id = 0, $form_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $lead_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $form_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1383
1384 22
		if( ! GFCommon::is_post_field( $field ) ) {
1385 22
			return $field_content;
1386
		}
1387
1388 2
        $message = null;
1389
1390
        // First, make sure they have the capability to edit the post.
1391 2
        if( false === current_user_can( 'edit_post', $this->entry['post_id'] ) ) {
1392
1393
            /**
1394
             * @filter `gravityview/edit_entry/unsupported_post_field_text` Modify the message when someone isn't able to edit a post
1395
             * @param string $message The existing "You don't have permission..." text
1396
             */
1397 1
            $message = apply_filters('gravityview/edit_entry/unsupported_post_field_text', __('You don&rsquo;t have permission to edit this post.', 'gravityview') );
1398
1399 1
        } elseif( null === get_post( $this->entry['post_id'] ) ) {
1400
            /**
1401
             * @filter `gravityview/edit_entry/no_post_text` Modify the message when someone is editing an entry attached to a post that no longer exists
1402
             * @param string $message The existing "This field is not editable; the post no longer exists." text
1403
             */
1404
            $message = apply_filters('gravityview/edit_entry/no_post_text', __('This field is not editable; the post no longer exists.', 'gravityview' ) );
1405
        }
1406
1407 2
        if( $message ) {
1408 1
            $field_content = sprintf('<div class="ginput_container ginput_container_' . $field->type . '">%s</div>', wpautop( $message ) );
1409
        }
1410
1411 2
        return $field_content;
1412
	}
1413
1414
	/**
1415
	 *
1416
	 * Fill-in the saved values into the form inputs
1417
	 *
1418
	 * @param string $field_content Always empty. Returning not-empty overrides the input.
1419
	 * @param GF_Field $field
1420
	 * @param string|array $value If array, it's a field with multiple inputs. If string, single input.
1421
	 * @param int $lead_id Lead ID. Always 0 for the `gform_field_input` filter.
1422
	 * @param int $form_id Form ID
1423
	 *
1424
	 * @return mixed
1425
	 */
1426 22
	public function modify_edit_field_input( $field_content = '', $field, $value, $lead_id = 0, $form_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $lead_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $form_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1427
1428 22
		$gv_field = GravityView_Fields::get_associated_field( $field );
1429
1430
		// If the form has been submitted, then we don't need to pre-fill the values,
1431
		// Except for fileupload type and when a field input is overridden- run always!!
1432
		if(
1433 22
			( $this->is_edit_entry_submission() && !in_array( $field->type, array( 'fileupload', 'post_image' ) ) )
1434 22
			&& false === ( $gv_field && is_callable( array( $gv_field, 'get_field_input' ) ) )
1435
			&& ! GFCommon::is_product_field( $field->type )
1436 22
			|| ! empty( $field_content )
1437 22
			|| in_array( $field->type, array( 'honeypot' ) )
1438
		) {
1439 1
	        return $field_content;
1440
		}
1441
1442
		// SET SOME FIELD DEFAULTS TO PREVENT ISSUES
1443 22
		$field->adminOnly = false; /** @see GFFormDisplay::get_counter_init_script() need to prevent adminOnly */
1444
1445 22
		$field_value = $this->get_field_value( $field );
1446
1447
	    // Prevent any PHP warnings, like undefined index
1448 22
	    ob_start();
1449
1450 22
	    $return = null;
1451
1452
		/** @var GravityView_Field $gv_field */
1453 22
		if( $gv_field && is_callable( array( $gv_field, 'get_field_input' ) ) ) {
1454 3
			$return = $gv_field->get_field_input( $this->form, $field_value, $this->entry, $field );
1455
		} else {
1456 22
	        $return = $field->get_field_input( $this->form, $field_value, $this->entry );
1457
	    }
1458
1459
	    // If there was output, it's an error
1460 22
	    $warnings = ob_get_clean();
1461
1462 22
	    if( !empty( $warnings ) ) {
1463
		    gravityview()->log->error( '{warning}', array( 'warning' => $warnings, 'data' => $field_value ) );
1464
	    }
1465
1466 22
		return $return;
1467
	}
1468
1469
	/**
1470
	 * Modify the value for the current field input
1471
	 *
1472
	 * @param GF_Field $field
1473
	 *
1474
	 * @return array|mixed|string
1475
	 */
1476 22
	private function get_field_value( $field ) {
1477
1478
		/**
1479
		 * @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.
1480
		 * @param boolean True: override saved values; False: don't override (default)
1481
		 * @param $field GF_Field object Gravity Forms field object
1482
		 * @since 1.13
1483
		 */
1484 22
		$override_saved_value = apply_filters( 'gravityview/edit_entry/pre_populate/override', false, $field );
1485
1486
		// We're dealing with multiple inputs (e.g. checkbox) but not time or date (as it doesn't store data in input IDs)
1487 22
		if( isset( $field->inputs ) && is_array( $field->inputs ) && !in_array( $field->type, array( 'time', 'date' ) ) ) {
1488
1489 4
			$field_value = array();
1490
1491
			// only accept pre-populated values if the field doesn't have any choice selected.
1492 4
			$allow_pre_populated = $field->allowsPrepopulate;
1493
1494 4
			foreach ( (array)$field->inputs as $input ) {
1495
1496 4
				$input_id = strval( $input['id'] );
1497
1498 4
				if ( isset( $this->entry[ $input_id ] ) && ! gv_empty( $this->entry[ $input_id ], false, false ) ) {
1499 4
				    $field_value[ $input_id ] =  'post_category' === $field->type ? GFCommon::format_post_category( $this->entry[ $input_id ], true ) : $this->entry[ $input_id ];
1500 4
				    $allow_pre_populated = false;
1501
				}
1502
1503
			}
1504
1505 4
			$pre_value = $field->get_value_submission( array(), false );
1506
1507 4
			$field_value = ! $allow_pre_populated && ! ( $override_saved_value && !gv_empty( $pre_value, false, false ) ) ? $field_value : $pre_value;
1508
1509
		} else {
1510
1511 22
			$id = intval( $field->id );
1512
1513
			// get pre-populated value if exists
1514 22
			$pre_value = $field->allowsPrepopulate ? GFFormsModel::get_parameter_value( $field->inputName, array(), $field ) : '';
1515
1516
			// saved field entry value (if empty, fallback to the pre-populated value, if exists)
1517
			// or pre-populated value if not empty and set to override saved value
1518 22
			$field_value = isset( $this->entry[ $id ] ) && ! gv_empty( $this->entry[ $id ], false, false ) && ! ( $override_saved_value && !gv_empty( $pre_value, false, false ) ) ? $this->entry[ $id ] : $pre_value;
1519
1520
			// in case field is post_category but inputType is select, multi-select or radio, convert value into array of category IDs.
1521 22
			if ( 'post_category' === $field->type && !gv_empty( $field_value, false, false ) ) {
1522
				$categories = array();
1523
				foreach ( explode( ',', $field_value ) as $cat_string ) {
1524
				    $categories[] = GFCommon::format_post_category( $cat_string, true );
1525
				}
1526
				$field_value = 'multiselect' === $field->get_input_type() ? $categories : implode( '', $categories );
1527
			}
1528
1529
		}
1530
1531
		// if value is empty get the default value if defined
1532 22
		$field_value = $field->get_value_default_if_empty( $field_value );
1533
1534
	    /**
1535
	     * @filter `gravityview/edit_entry/field_value` Change the value of an Edit Entry field, if needed
1536
	     * @since 1.11
1537
	     * @since 1.20 Added third param
1538
	     * @param mixed $field_value field value used to populate the input
1539
	     * @param object $field Gravity Forms field object ( Class GF_Field )
1540
	     * @param GravityView_Edit_Entry_Render $this Current object
1541
	     */
1542 22
	    $field_value = apply_filters( 'gravityview/edit_entry/field_value', $field_value, $field, $this );
1543
1544
	    /**
1545
	     * @filter `gravityview/edit_entry/field_value_{field_type}` Change the value of an Edit Entry field for a specific field type
1546
	     * @since 1.17
1547
	     * @since 1.20 Added third param
1548
	     * @param mixed $field_value field value used to populate the input
1549
	     * @param GF_Field $field Gravity Forms field object
1550
	     * @param GravityView_Edit_Entry_Render $this Current object
1551
	     */
1552 22
	    $field_value = apply_filters( 'gravityview/edit_entry/field_value_' . $field->type , $field_value, $field, $this );
1553
1554 22
		return $field_value;
1555
	}
1556
1557
1558
	// ---- Entry validation
1559
1560
	/**
1561
	 * Add field keys that Gravity Forms expects.
1562
	 *
1563
	 * @see GFFormDisplay::validate()
1564
	 * @param  array $form GF Form
1565
	 * @return array       Modified GF Form
1566
	 */
1567 21
	public function gform_pre_validation( $form ) {
1568
1569 21
		if( ! $this->verify_nonce() ) {
1570
			return $form;
1571
		}
1572
1573
		// Fix PHP warning regarding undefined index.
1574 21
		foreach ( $form['fields'] as &$field) {
1575
1576
			// This is because we're doing admin form pretending to be front-end, so Gravity Forms
1577
			// expects certain field array items to be set.
1578 21
			foreach ( array( 'noDuplicates', 'adminOnly', 'inputType', 'isRequired', 'enablePrice', 'inputs', 'allowedExtensions' ) as $key ) {
1579 21
	            $field->{$key} = isset( $field->{$key} ) ? $field->{$key} : NULL;
1580
			}
1581
1582 21
			switch( RGFormsModel::get_input_type( $field ) ) {
1583
1584
				/**
1585
				 * 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.
1586
				 *
1587
				 * 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.
1588
				 *
1589
				 * @hack
1590
				 */
1591 21
				case 'fileupload':
1592
1593
				    // Set the previous value
1594 1
				    $entry = $this->get_entry();
1595
1596 1
				    $input_name = 'input_'.$field->id;
1597 1
				    $form_id = $form['id'];
1598
1599 1
				    $value = NULL;
1600
1601
				    // Use the previous entry value as the default.
1602 1
				    if( isset( $entry[ $field->id ] ) ) {
1603 1
				        $value = $entry[ $field->id ];
1604
				    }
1605
1606
				    // If this is a single upload file
1607 1
				    if( !empty( $_FILES[ $input_name ] ) && !empty( $_FILES[ $input_name ]['name'] ) ) {
1608 1
				        $file_path = GFFormsModel::get_file_upload_path( $form['id'], $_FILES[ $input_name ]['name'] );
1609 1
				        $value = $file_path['url'];
1610
1611
				    } else {
1612
1613
				        // Fix PHP warning on line 1498 of form_display.php for post_image fields
1614
				        // Fix PHP Notice:  Undefined index:  size in form_display.php on line 1511
1615 1
				        $_FILES[ $input_name ] = array('name' => '', 'size' => '' );
1616
1617
				    }
1618
1619 1
				    if ( \GV\Utils::get( $field, "multipleFiles" ) ) {
1620
1621
				        // If there are fresh uploads, process and merge them.
1622
				        // Otherwise, use the passed values, which should be json-encoded array of URLs
1623 1
				        if( isset( GFFormsModel::$uploaded_files[$form_id][$input_name] ) ) {
1624
				            $value = empty( $value ) ? '[]' : $value;
1625
				            $value = stripslashes_deep( $value );
1626 1
				            $value = GFFormsModel::prepare_value( $form, $field, $value, $input_name, $entry['id'], array());
1627
				        }
1628
1629
				    } else {
1630
1631
				        // A file already exists when editing an entry
1632
				        // We set this to solve issue when file upload fields are required.
1633 1
				        GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] = $value;
1634
1635
				    }
1636
1637 1
				    $this->entry[ $input_name ] = $value;
1638 1
				    $_POST[ $input_name ] = $value;
1639
1640 1
				    break;
1641
1642 21
				case 'number':
1643
				    // Fix "undefined index" issue at line 1286 in form_display.php
1644 12
				    if( !isset( $_POST['input_'.$field->id ] ) ) {
1645 7
				        $_POST['input_'.$field->id ] = NULL;
1646
				    }
1647 12
				    break;
1648
			}
1649
1650
		}
1651
1652 21
		return $form;
1653
	}
1654
1655
1656
	/**
1657
	 * Process validation for a edit entry submission
1658
	 *
1659
	 * Sets the `is_valid` object var
1660
	 *
1661
	 * @return void
1662
	 */
1663 22
	private function validate() {
1664
1665
		/**
1666
		 * If using GF User Registration Add-on, remove the validation step, otherwise generates error when updating the entry
1667
		 * GF User Registration Add-on version > 3.x has a different class name
1668
		 * @since 1.16.2
1669
		 */
1670 22
		if ( class_exists( 'GF_User_Registration' ) ) {
1671 22
			remove_filter( 'gform_validation', array( GF_User_Registration::get_instance(), 'validate' ) );
1672
		} else  if ( class_exists( 'GFUser' ) ) {
1673
			remove_filter( 'gform_validation', array( 'GFUser', 'user_registration_validation' ) );
1674
		}
1675
1676
1677
		/**
1678
		 * For some crazy reason, Gravity Forms doesn't validate Edit Entry form submissions.
1679
		 * You can enter whatever you want!
1680
		 * We try validating, and customize the results using `self::custom_validation()`
1681
		 */
1682 22
		add_filter( 'gform_validation_'. $this->form_id, array( $this, 'custom_validation' ), 10, 4);
1683
1684
		// Needed by the validate funtion
1685 22
		$failed_validation_page = NULL;
1686 22
		$field_values = RGForms::post( 'gform_field_values' );
1687
1688
		// Prevent entry limit from running when editing an entry, also
1689
		// prevent form scheduling from preventing editing
1690 22
		unset( $this->form['limitEntries'], $this->form['scheduleForm'] );
1691
1692
		// Hide fields depending on Edit Entry settings
1693 22
		$this->form['fields'] = $this->get_configured_edit_fields( $this->form, $this->view_id );
1694
1695 22
		$this->is_valid = GFFormDisplay::validate( $this->form, $field_values, 1, $failed_validation_page );
1696
1697 22
		remove_filter( 'gform_validation_'. $this->form_id, array( $this, 'custom_validation' ), 10 );
1698 22
	}
1699
1700
1701
	/**
1702
	 * Make validation work for Edit Entry
1703
	 *
1704
	 * Because we're calling the GFFormDisplay::validate() in an unusual way (as a front-end
1705
	 * form pretending to be a back-end form), validate() doesn't know we _can't_ edit post
1706
	 * fields. This goes through all the fields and if they're an invalid post field, we
1707
	 * set them as valid. If there are still issues, we'll return false.
1708
	 *
1709
	 * @param  [type] $validation_results [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1710
	 * @return [type]                     [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1711
	 */
1712 22
	public function custom_validation( $validation_results ) {
1713
1714 22
		gravityview()->log->debug( 'GravityView_Edit_Entry[custom_validation] Validation results: ', array( 'data' => $validation_results ) );
1715
1716 22
		gravityview()->log->debug( 'GravityView_Edit_Entry[custom_validation] $_POSTed data (sanitized): ', array( 'data' => esc_html( print_r( $_POST, true ) ) ) );
1717
1718 22
		$gv_valid = true;
1719
1720 22
		foreach ( $validation_results['form']['fields'] as $key => &$field ) {
1721
1722 22
			$value = RGFormsModel::get_field_value( $field );
1723 22
			$field_type = RGFormsModel::get_input_type( $field );
1724
1725
			// Validate always
1726 22
			switch ( $field_type ) {
1727
1728
1729 22
				case 'fileupload' :
1730 22
				case 'post_image':
1731
1732
				    // in case nothing is uploaded but there are already files saved
1733 2
				    if( !empty( $field->failed_validation ) && !empty( $field->isRequired ) && !empty( $value ) ) {
1734
				        $field->failed_validation = false;
1735
				        unset( $field->validation_message );
1736
				    }
1737
1738
				    // validate if multi file upload reached max number of files [maxFiles] => 2
1739 2
				    if( \GV\Utils::get( $field, 'maxFiles') && \GV\Utils::get( $field, 'multipleFiles') ) {
1740
1741
				        $input_name = 'input_' . $field->id;
1742
				        //uploaded
1743
				        $file_names = isset( GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ][ $input_name ] : array();
1744
1745
				        //existent
1746
				        $entry = $this->get_entry();
1747
				        $value = NULL;
1748
				        if( isset( $entry[ $field->id ] ) ) {
1749
				            $value = json_decode( $entry[ $field->id ], true );
1750
				        }
1751
1752
				        // count uploaded files and existent entry files
1753
				        $count_files = ( is_array( $file_names ) ? count( $file_names ) : 0 ) +
1754
						               ( is_array( $value ) ? count( $value ) : 0 );
1755
1756
				        if( $count_files > $field->maxFiles ) {
1757
				            $field->validation_message = __( 'Maximum number of files reached', 'gravityview' );
1758
				            $field->failed_validation = 1;
1759
				            $gv_valid = false;
1760
1761
				            // in case of error make sure the newest upload files are removed from the upload input
1762
				            GFFormsModel::$uploaded_files[ $validation_results['form']['id'] ] = null;
1763
				        }
1764
1765
				    }
1766
1767
1768 2
				    break;
1769
1770
			}
1771
1772
			// This field has failed validation.
1773 22
			if( !empty( $field->failed_validation ) ) {
1774
1775 1
				gravityview()->log->debug( 'GravityView_Edit_Entry[custom_validation] Field is invalid.', array( 'data' => array( 'field' => $field, 'value' => $value ) ) );
1776
1777 1
				switch ( $field_type ) {
1778
1779
				    // Captchas don't need to be re-entered.
1780 1
				    case 'captcha':
1781
1782
				        // Post Image fields aren't editable, so we un-fail them.
1783 1
				    case 'post_image':
1784
				        $field->failed_validation = false;
1785
				        unset( $field->validation_message );
1786
				        break;
1787
1788
				}
1789
1790
				// You can't continue inside a switch, so we do it after.
1791 1
				if( empty( $field->failed_validation ) ) {
1792
				    continue;
1793
				}
1794
1795
				// checks if the No Duplicates option is not validating entry against itself, since
1796
				// we're editing a stored entry, it would also assume it's a duplicate.
1797 1
				if( !empty( $field->noDuplicates ) ) {
1798
1799
				    $entry = $this->get_entry();
1800
1801
				    // If the value of the entry is the same as the stored value
1802
				    // Then we can assume it's not a duplicate, it's the same.
1803
				    if( !empty( $entry ) && $value == $entry[ $field->id ] ) {
1804
				        //if value submitted was not changed, then don't validate
1805
				        $field->failed_validation = false;
1806
1807
				        unset( $field->validation_message );
1808
1809
				        gravityview()->log->debug( 'GravityView_Edit_Entry[custom_validation] Field not a duplicate; it is the same entry.', array( 'data' => $entry ) );
1810
1811
				        continue;
1812
				    }
1813
				}
1814
1815
				// if here then probably we are facing the validation 'At least one field must be filled out'
1816 1
				if( GFFormDisplay::is_empty( $field, $this->form_id  ) && empty( $field->isRequired ) ) {
1817
				    unset( $field->validation_message );
1818
	                $field->validation_message = false;
1819
				    continue;
1820
				}
1821
1822 1
				$gv_valid = false;
1823
1824
			}
1825
1826
		}
1827
1828 22
		$validation_results['is_valid'] = $gv_valid;
1829
1830 22
		gravityview()->log->debug( 'GravityView_Edit_Entry[custom_validation] Validation results.', array( 'data' => $validation_results ) );
1831
1832
		// We'll need this result when rendering the form ( on GFFormDisplay::get_form )
1833 22
		$this->form_after_validation = $validation_results['form'];
1834
1835 22
		return $validation_results;
1836
	}
1837
1838
1839
	/**
1840
	 * TODO: This seems to be hacky... we should remove it. Entry is set when updating the form using setup_vars()!
1841
	 * Get the current entry and set it if it's not yet set.
1842
	 * @return array Gravity Forms entry array
1843
	 */
1844 2
	public function get_entry() {
1845
1846 2
		if( empty( $this->entry ) ) {
1847
			// Get the database value of the entry that's being edited
1848 1
			$this->entry = gravityview_get_entry( GravityView_frontend::is_single_entry() );
1849
		}
1850
1851 2
		return $this->entry;
1852
	}
1853
1854
1855
1856
	// --- Filters
1857
1858
	/**
1859
	 * Get the Edit Entry fields as configured in the View
1860
	 *
1861
	 * @since 1.8
1862
	 *
1863
	 * @param int $view_id
1864
	 *
1865
	 * @return array Array of fields that are configured in the Edit tab in the Admin
1866
	 */
1867 23
	private function get_configured_edit_fields( $form, $view_id ) {
1868
1869
		// Get all fields for form
1870 23
		if ( \GV\View::exists( $view_id ) ) {
1871 23
			$view = \GV\View::by_id( $view_id );
1872 23
			$properties = $view->fields ? $view->fields->as_configuration() : array();
1873
		} else {
1874
			$properties = null;
1875
		}
1876
1877
		// If edit tab not yet configured, show all fields
1878 23
		$edit_fields = !empty( $properties['edit_edit-fields'] ) ? $properties['edit_edit-fields'] : NULL;
1879
1880
		// Hide fields depending on admin settings
1881 23
		$fields = $this->filter_fields( $form['fields'], $edit_fields );
1882
1883
	    // If Edit Entry fields are configured, remove adminOnly field settings. Otherwise, don't.
1884 23
	    $fields = $this->filter_admin_only_fields( $fields, $edit_fields, $form, $view_id );
1885
1886
		/**
1887
		 * @filter `gravityview/edit_entry/form_fields` Modify the fields displayed in Edit Entry form
1888
		 * @since 1.17
1889
		 * @param GF_Field[] $fields Gravity Forms form fields
1890
		 * @param array|null $edit_fields Fields for the Edit Entry tab configured in the View Configuration
1891
		 * @param array $form GF Form array (`fields` key modified to have only fields configured to show in Edit Entry)
1892
		 * @param int $view_id View ID
1893
		 */
1894 23
		$fields = apply_filters( 'gravityview/edit_entry/form_fields', $fields, $edit_fields, $form, $view_id );
1895
1896 23
		return $fields;
1897
	}
1898
1899
1900
	/**
1901
	 * Filter area fields based on specified conditions
1902
	 *  - This filter removes the fields that have calculation configured
1903
	 *  - Hides fields that are hidden, etc.
1904
	 *
1905
	 * @uses GravityView_Edit_Entry::user_can_edit_field() Check caps
1906
	 * @access private
1907
	 * @param GF_Field[] $fields
1908
	 * @param array $configured_fields
1909
	 * @since  1.5
1910
	 * @return array $fields
1911
	 */
1912 22
	private function filter_fields( $fields, $configured_fields ) {
1913
1914 22
		if( empty( $fields ) || !is_array( $fields ) ) {
1915
			return $fields;
1916
		}
1917
1918 22
		$edit_fields = array();
1919
1920 22
		$field_type_blacklist = $this->loader->get_field_blacklist( $this->entry );
1921
1922 22
		if ( empty( $configured_fields ) && apply_filters( 'gravityview/features/paged-edit', false ) ) {
1923
			$field_type_blacklist = array_diff( $field_type_blacklist, array( 'page' ) );
1924
		}
1925
1926
		// First, remove blacklist or calculation fields
1927 22
		foreach ( $fields as $key => $field ) {
1928
1929
			// Remove the fields that have calculation properties and keep them to be used later
1930
			// @since 1.16.2
1931 22
			if( $field->has_calculation() ) {
1932 5
				$this->fields_with_calculation[] = $field;
1933
				// don't remove the calculation fields on form render.
1934
			}
1935
1936 22
			if( in_array( $field->type, $field_type_blacklist ) ) {
1937 3
				unset( $fields[ $key ] );
1938
			}
1939
		}
1940
1941
		// The Edit tab has not been configured, so we return all fields by default.
1942
		// But we do keep the hidden ones hidden please, for everyone :)
1943 22
		if ( empty( $configured_fields ) ) {
1944 17
			$out_fields = array();
1945 17
			foreach ( $fields as &$field ) {
1946 17
				if ( 'hidden' === $field->type ) {
1947 3
					continue; // A hidden field is just hidden
1948
				}
1949
1950 17
				if ( 'hidden' == $field->visibility ) {
1951 2
					continue; // Same
1952
				}
1953
1954 15
				$out_fields[] = $field;
1955
			}
1956
1957 17
			return array_values( $out_fields );
1958
		}
1959
1960
		// The edit tab has been configured, so we loop through to configured settings
1961 8
		foreach ( $configured_fields as $configured_field ) {
1962
1963
	        /** @var GF_Field $field */
1964 8
	        foreach ( $fields as $field ) {
1965 8
				if( intval( $configured_field['id'] ) === intval( $field->id ) && $this->user_can_edit_field( $configured_field, false ) ) {
1966 8
				    $edit_fields[] = $this->merge_field_properties( $field, $configured_field );
1967 8
				    break;
1968
				}
1969
1970
			}
1971
1972
		}
1973
1974 8
		return $edit_fields;
1975
1976
	}
1977
1978
	/**
1979
	 * Override GF Form field properties with the ones defined on the View
1980
	 * @param  GF_Field $field GF Form field object
1981
	 * @param  array $field_setting  GV field options
1982
	 * @since  1.5
1983
	 * @return array|GF_Field
1984
	 */
1985 8
	private function merge_field_properties( $field, $field_setting ) {
1986
1987 8
		$return_field = $field;
1988
1989 8
		if( empty( $field_setting['show_label'] ) ) {
1990
			$return_field->label = '';
1991 8
		} elseif ( !empty( $field_setting['custom_label'] ) ) {
1992
			$return_field->label = $field_setting['custom_label'];
1993
		}
1994
1995 8
		if( !empty( $field_setting['custom_class'] ) ) {
1996
			$return_field->cssClass .= ' '. gravityview_sanitize_html_class( $field_setting['custom_class'] );
1997
		}
1998
1999
		/**
2000
		 * Normalize page numbers - avoid conflicts with page validation
2001
		 * @since 1.6
2002
		 */
2003 8
		$return_field->pageNumber = 1;
2004
2005 8
		return $return_field;
2006
2007
	}
2008
2009
	/**
2010
	 * Remove fields that shouldn't be visible based on the Gravity Forms adminOnly field property
2011
	 *
2012
	 * @since 1.9.1
2013
	 *
2014
	 * @param array|GF_Field[] $fields Gravity Forms form fields
2015
	 * @param array|null $edit_fields Fields for the Edit Entry tab configured in the View Configuration
2016
	 * @param array $form GF Form array
2017
	 * @param int $view_id View ID
2018
	 *
2019
	 * @return array Possibly modified form array
2020
	 */
2021 22
	private function filter_admin_only_fields( $fields = array(), $edit_fields = null, $form = array(), $view_id = 0 ) {
2022
2023
	    /**
2024
		 * @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
2025
	     * If the Edit Entry tab is not configured, adminOnly fields will not be shown to non-administrators.
2026
	     * If the Edit Entry tab *is* configured, adminOnly fields will be shown to non-administrators, using the configured GV permissions
2027
	     * @since 1.9.1
2028
	     * @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.
2029
	     * @param array $form GF Form array
2030
	     * @param int $view_id View ID
2031
	     */
2032 22
	    $use_gf_adminonly_setting = apply_filters( 'gravityview/edit_entry/use_gf_admin_only_setting', empty( $edit_fields ), $form, $view_id );
2033
2034 22
	    if( $use_gf_adminonly_setting && false === GVCommon::has_cap( 'gravityforms_edit_entries', $this->entry['id'] ) ) {
2035 1
			foreach( $fields as $k => $field ) {
2036
				if( $field->adminOnly ) {
2037
				    unset( $fields[ $k ] );
2038
				}
2039
			}
2040 1
			return array_values( $fields );
2041
		}
2042
2043 22
	    foreach( $fields as &$field ) {
2044 22
		    $field->adminOnly = false;
2045
		}
2046
2047 22
		return $fields;
2048
	}
2049
2050
	/**
2051
	 * Checkboxes and other checkbox-based controls should not
2052
	 * display default checks in edit mode.
2053
	 *
2054
	 * https://github.com/gravityview/GravityView/1149
2055
	 *
2056
	 * @since 2.1
2057
	 *
2058
	 * @param array $form Gravity Forms array object
2059
	 *
2060
	 * @return array $form, modified to default checkboxes, radios from showing up.
2061
	 */
2062 22
	private function unselect_default_values( $form ) {
2063
2064 22
	    foreach ( $form['fields'] as &$field ) {
2065
2066 22
			if ( empty( $field->choices ) ) {
2067 19
                continue;
2068
			}
2069
2070 6
            foreach ( $field->choices as &$choice ) {
2071 6
				if ( \GV\Utils::get( $choice, 'isSelected' ) ) {
2072 2
					$choice['isSelected'] = false;
2073
				}
2074
			}
2075
		}
2076
2077 22
		return $form;
2078
	}
2079
2080
	// --- Conditional Logic
2081
2082
	/**
2083
	 * Conditional logic isn't designed to work with forms that already have content. When switching input values,
2084
	 * the dependent fields will be blank.
2085
	 *
2086
	 * Note: This is because GF populates a JavaScript variable with the input values. This is tough to filter at the input level;
2087
	 * via the `gform_field_value` filter; it requires lots of legwork. Doing it at the form level is easier.
2088
	 *
2089
	 * @since 1.17.4
2090
	 *
2091
	 * @param array $form Gravity Forms array object
2092
	 *
2093
	 * @return array $form, modified to fix conditional
2094
	 */
2095 22
	function prefill_conditional_logic( $form ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2096
2097 22
		if( ! GFFormDisplay::has_conditional_logic( $form ) ) {
2098 19
			return $form;
2099
		}
2100
2101
		// Have Conditional Logic pre-fill fields as if the data were default values
2102
		/** @var GF_Field $field */
2103 3
		foreach ( $form['fields'] as &$field ) {
2104
2105 3
			if( 'checkbox' === $field->type ) {
2106
				foreach ( $field->get_entry_inputs() as $key => $input ) {
2107
				    $input_id = $input['id'];
2108
				    $choice = $field->choices[ $key ];
2109
				    $value = \GV\Utils::get( $this->entry, $input_id );
2110
				    $match = RGFormsModel::choice_value_match( $field, $choice, $value );
2111
				    if( $match ) {
2112
				        $field->choices[ $key ]['isSelected'] = true;
2113
				    }
2114
				}
2115
			} else {
2116
2117
				// We need to run through each field to set the default values
2118 3
				foreach ( $this->entry as $field_id => $field_value ) {
2119
2120 3
				    if( floatval( $field_id ) === floatval( $field->id ) ) {
2121
2122 3
				        if( 'list' === $field->type ) {
2123
				            $list_rows = maybe_unserialize( $field_value );
2124
2125
				            $list_field_value = array();
2126
				            foreach ( (array) $list_rows as $row ) {
2127
				                foreach ( (array) $row as $column ) {
2128
				                    $list_field_value[] = $column;
2129
				                }
2130
				            }
2131
2132
				            $field->defaultValue = serialize( $list_field_value );
2133
				        } else {
2134 3
				            $field->defaultValue = $field_value;
2135
				        }
2136
				    }
2137
				}
2138
			}
2139
		}
2140
2141 3
		return $form;
2142
	}
2143
2144
	/**
2145
	 * Remove the conditional logic rules from the form button and the form fields, if needed.
2146
	 *
2147
	 * @todo Merge with caller method
2148
	 * @since 1.9
2149
	 *
2150
	 * @param array $form Gravity Forms form
2151
	 * @return array Modified form, if not using Conditional Logic
2152
	 */
2153 22
	private function filter_conditional_logic( $form ) {
2154
		/**
2155
		 * Fields that are tied to a conditional logic field that is not present in the view
2156
		 * have to still be displayed, if the condition is met.
2157
		 *
2158
		 * @see https://github.com/gravityview/GravityView/issues/840
2159
		 * @since develop
2160
		 */
2161 22
		$the_form = GFAPI::get_form( $form['id'] );
2162 22
		$editable_ids = array();
2163 22
		foreach ( $form['fields'] as $field ) {
2164 22
			$editable_ids[] = $field['id']; // wp_list_pluck is destructive in this context
2165
		}
2166 22
		$remove_conditions_rule = array();
2167 22
		foreach ( $the_form['fields'] as $field ) {
2168 22
			if ( ! empty( $field->conditionalLogic ) && ! empty( $field->conditionalLogic['rules'] ) ) {
2169 6
				foreach ( $field->conditionalLogic['rules'] as $i => $rule ) {
2170 6
					if ( ! in_array( $rule['fieldId'], $editable_ids ) ) {
2171
						/**
2172
						 * This conditional field is not editable in this View.
2173
						 * We need to remove the rule, but only if it matches.
2174
						 */
2175 4
						if ( $_field = GFAPI::get_field( $the_form, $rule['fieldId'] ) ) {
2176 4
							$value = $_field->get_value_export( $this->entry );
2177
						} elseif ( isset( $this->entry[ $rule['fieldId'] ] ) ) {
2178
							$value = $this->entry[ $rule['fieldId'] ];
2179
						} else {
2180
							$value = gform_get_meta( $this->entry['id'], $rule['fieldId'] );
2181
						}
2182
2183 4
						$match = GFFormsModel::matches_operation( $value, $rule['value'], $rule['operator'] );
2184
						
2185 4
						if ( $match ) {
2186 4
							$remove_conditions_rule[] = array( $field['id'], $i );
2187
						}
2188
					}
2189
				}
2190
			}
2191
		}
2192
2193 22
		if ( $remove_conditions_rule ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $remove_conditions_rule of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2194 4
			foreach ( $form['fields'] as &$field ) {
2195 4
				foreach ( $remove_conditions_rule as $_remove_conditions_r ) {
2196
2197 4
				    list( $rule_field_id, $rule_i ) = $_remove_conditions_r;
2198
2199 4
					if ( $field['id'] == $rule_field_id ) {
2200 1
						unset( $field->conditionalLogic['rules'][ $rule_i ] );
2201 1
						gravityview()->log->debug( 'Removed conditional rule #{rule} for field {field_id}', array( 'rule' => $rule_i, 'field_id' => $field['id'] ) );
2202
					}
2203
				}
2204
			}
2205
		}
2206
2207
		/** Normalize the indices... */
2208 22
		$form['fields'] = array_values( $form['fields'] );
2209
2210
		/**
2211
		 * @filter `gravityview/edit_entry/conditional_logic` Should the Edit Entry form use Gravity Forms conditional logic showing/hiding of fields?
2212
		 * @since 1.9
2213
		 * @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
2214
		 * @param array $form Gravity Forms form
2215
		 */
2216 22
		$use_conditional_logic = apply_filters( 'gravityview/edit_entry/conditional_logic', true, $form );
2217
2218 22
		if( $use_conditional_logic ) {
2219 22
			return $form;
2220
		}
2221
2222
		foreach( $form['fields'] as &$field ) {
2223
			/* @var GF_Field $field */
2224
			$field->conditionalLogic = null;
2225
		}
2226
2227
		unset( $form['button']['conditionalLogic'] );
2228
2229
		return $form;
2230
2231
	}
2232
2233
	/**
2234
	 * Disable the Gravity Forms conditional logic script and features on the Edit Entry screen
2235
	 *
2236
	 * @since 1.9
2237
	 *
2238
	 * @param $has_conditional_logic
2239
	 * @param $form
2240
	 * @return mixed
2241
	 */
2242 22
	public function manage_conditional_logic( $has_conditional_logic, $form ) {
2243
2244 22
		if( ! $this->is_edit_entry() ) {
2245
			return $has_conditional_logic;
2246
		}
2247
2248
	    /** @see GravityView_Edit_Entry_Render::filter_conditional_logic for filter documentation */
2249 22
		return apply_filters( 'gravityview/edit_entry/conditional_logic', $has_conditional_logic, $form );
2250
	}
2251
2252
2253
	// --- User checks and nonces
2254
2255
	/**
2256
	 * Check if the user can edit the entry
2257
	 *
2258
	 * - Is the nonce valid?
2259
	 * - Does the user have the right caps for the entry
2260
	 * - Is the entry in the trash?
2261
	 *
2262
	 * @todo Move to GVCommon
2263
	 *
2264
	 * @param  boolean $echo Show error messages in the form?
2265
	 * @return boolean        True: can edit form. False: nope.
2266
	 */
2267 23
	private function user_can_edit_entry( $echo = false ) {
2268
2269 23
		$error = NULL;
2270
2271
		/**
2272
		 *  1. Permalinks are turned off
2273
		 *  2. There are two entries embedded using oEmbed
2274
		 *  3. One of the entries has just been saved
2275
		 */
2276 23
		if( !empty( $_POST['lid'] ) && !empty( $_GET['entry'] ) && ( $_POST['lid'] !== $_GET['entry'] ) ) {
2277
2278
			$error = true;
2279
2280
		}
2281
2282 23
		if( !empty( $_GET['entry'] ) && (string)$this->entry['id'] !== $_GET['entry'] ) {
2283
2284
			$error = true;
2285
2286 23
		} elseif( ! $this->verify_nonce() ) {
2287
2288
			/**
2289
			 * If the Entry is embedded, there may be two entries on the same page.
2290
			 * If that's the case, and one is being edited, the other should fail gracefully and not display an error.
2291
			 */
2292
			if( GravityView_oEmbed::getInstance()->get_entry_id() ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \GravityView_oEmbed::get...tance()->get_entry_id() of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Deprecated Code introduced by
The method GravityView_oEmbed::getInstance() has been deprecated with message: Use \GV\oEmbed instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method GravityView_oEmbed::get_entry_id() has been deprecated with message: Use \GV\oEmbed instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
2293
				$error = true;
2294
			} else {
2295
				$error = __( 'The link to edit this entry is not valid; it may have expired.', 'gravityview');
2296
			}
2297
2298
		}
2299
2300 23
		if( ! GravityView_Edit_Entry::check_user_cap_edit_entry( $this->entry ) ) {
2301 2
			$error = __( 'You do not have permission to edit this entry.', 'gravityview');
2302
		}
2303
2304 23
		if( $this->entry['status'] === 'trash' ) {
2305
			$error = __('You cannot edit the entry; it is in the trash.', 'gravityview' );
2306
		}
2307
2308
		// No errors; everything's fine here!
2309 23
		if( empty( $error ) ) {
2310 23
			return true;
2311
		}
2312
2313 2
		if( $echo && $error !== true ) {
2314
2315 2
	        $error = esc_html( $error );
2316
2317
	        /**
2318
	         * @since 1.9
2319
	         */
2320 2
	        if ( ! empty( $this->entry ) ) {
2321 2
		        $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;" ) );
2322
	        }
2323
2324 2
			echo GVCommon::generate_notice( wpautop( $error ), 'gv-error error');
2325
		}
2326
2327 2
		gravityview()->log->error( '{error}', array( 'error' => $error ) );
2328
2329 2
		return false;
2330
	}
2331
2332
2333
	/**
2334
	 * Check whether a field is editable by the current user, and optionally display an error message
2335
	 * @uses  GravityView_Edit_Entry->check_user_cap_edit_field() Check user capabilities
2336
	 * @param  array  $field Field or field settings array
2337
	 * @param  boolean $echo  Whether to show error message telling user they aren't allowed
2338
	 * @return boolean         True: user can edit the current field; False: nope, they can't.
2339
	 */
2340 8
	private function user_can_edit_field( $field, $echo = false ) {
2341
2342 8
		$error = NULL;
2343
2344 8
		if( ! $this->check_user_cap_edit_field( $field ) ) {
2345 1
			$error = __( 'You do not have permission to edit this field.', 'gravityview');
2346
		}
2347
2348
		// No errors; everything's fine here!
2349 8
		if( empty( $error ) ) {
2350 8
			return true;
2351
		}
2352
2353 1
		if( $echo ) {
2354
			echo GVCommon::generate_notice( wpautop( esc_html( $error ) ), 'gv-error error');
2355
		}
2356
2357 1
		gravityview()->log->error( '{error}', array( 'error' => $error ) );
2358
2359 1
		return false;
2360
2361
	}
2362
2363
2364
	/**
2365
	 * checks if user has permissions to edit a specific field
2366
	 *
2367
	 * Needs to be used combined with GravityView_Edit_Entry::user_can_edit_field for maximum security!!
2368
	 *
2369
	 * @param  [type] $field [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
2370
	 * @return bool
2371
	 */
2372 8
	private function check_user_cap_edit_field( $field ) {
2373
2374
		// If they can edit any entries (as defined in Gravity Forms), we're good.
2375 8
		if( GVCommon::has_cap( array( 'gravityforms_edit_entries', 'gravityview_edit_others_entries' ) ) ) {
2376 7
			return true;
2377
		}
2378
2379 1
		$field_cap = isset( $field['allow_edit_cap'] ) ? $field['allow_edit_cap'] : false;
2380
2381 1
		if( $field_cap ) {
2382 1
			return GVCommon::has_cap( $field['allow_edit_cap'] );
2383
		}
2384
2385
		return false;
2386
	}
2387
2388
2389
	/**
2390
	 * Is the current nonce valid for editing the entry?
2391
	 * @return boolean
2392
	 */
2393 22
	public function verify_nonce() {
2394
2395
		// Verify form submitted for editing single
2396 22
		if( $this->is_edit_entry_submission() ) {
2397
			$valid = wp_verify_nonce( $_POST[ self::$nonce_field ], self::$nonce_field );
2398
		}
2399
2400
		// Verify
2401 22
		else if( ! $this->is_edit_entry() ) {
2402
			$valid = false;
2403
		}
2404
2405
		else {
2406 22
			$valid = wp_verify_nonce( $_GET['edit'], self::$nonce_key );
2407
		}
2408
2409
		/**
2410
		 * @filter `gravityview/edit_entry/verify_nonce` Override Edit Entry nonce validation. Return true to declare nonce valid.
2411
		 * @since 1.13
2412
		 * @param int|boolean $valid False if invalid; 1 or 2 when nonce was generated
2413
		 * @param string $nonce_field Key used when validating submissions. Default: is_gv_edit_entry
2414
		 */
2415 22
		$valid = apply_filters( 'gravityview/edit_entry/verify_nonce', $valid, self::$nonce_field );
2416
2417 22
		return $valid;
2418
	}
2419
2420
2421
	/**
2422
	 * Multiselect in GF 2.2 became a json_encoded value. Fix it.
2423
	 *
2424
	 * As a hack for now we'll implode it back.
2425
	 */
2426
	public function fix_multiselect_value_serialization( $field_value, $field, $_this ) {
0 ignored issues
show
Unused Code introduced by
The parameter $_this is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2427
		if ( empty ( $field->storageType ) || $field->storageType != 'json' ) {
2428
			return $field_value;
2429
		}
2430
2431
		$maybe_json = @json_decode( $field_value, true );
2432
2433
		if ( $maybe_json ) {
2434
			return implode( ',', $maybe_json );
2435
		}
2436
2437
		return $field_value;
2438
	}
2439
2440
2441
2442
} //end class
2443