Completed
Push — develop ( 7592b8...3eb7c5 )
by Zack
14:44
created

extensions/edit-entry/class-edit-entry-render.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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