Completed
Push — develop ( 8b20d3...ccc9af )
by Zack
06:46
created

GravityView_Edit_Entry   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 352
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 75.49%

Importance

Changes 0
Metric Value
dl 0
loc 352
ccs 77
cts 102
cp 0.7549
rs 9.6
c 0
b 0
f 0
wmc 35
lcom 2
cbo 6

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 2
A getInstance() 0 8 2
A load_components() 0 14 1
A add_hooks() 0 16 1
A addon_specific_hooks() 0 7 3
B maybe_not_visible() 0 28 6
A add_template_path() 0 7 1
A get_nonce_key() 0 3 1
A get_edit_link() 0 38 5
A modify_field_blacklist() 0 10 3
A get_field_blacklist() 0 25 1
C check_user_cap_edit_entry() 0 75 9
1
<?php
2
/**
3
 * The GravityView Edit Entry Extension
4
 *
5
 * Easily edit entries in GravityView.
6
 *
7
 * @package   GravityView
8
 * @license   GPL2+
9
 * @author    Katz Web Services, Inc.
10
 * @link      http://gravityview.co
11
 * @copyright Copyright 2014, Katz Web Services, Inc.
12
 */
13
14
if ( ! defined( 'WPINC' ) ) {
15
	die;
16
}
17
18
19
class GravityView_Edit_Entry {
20
21
    /**
22
     * @var string
23
     */
24
	static $file;
25
26
	static $instance;
27
28
    /**
29
     * Component instances.
30
     * @var array
31
     */
32
    public $instances = array();
33
34
35 22
	function __construct() {
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...
36
37 22
        self::$file = plugin_dir_path( __FILE__ );
38
39 22
        if( is_admin() ) {
40
            $this->load_components( 'admin' );
41
        }
42
43 22
		$this->load_components( 'locking' );
44
45 22
        $this->load_components( 'render' );
46
47
        // If GF User Registration Add-on exists
48 22
        $this->load_components( 'user-registration' );
49
50 22
        $this->add_hooks();
51
52
		// Process hooks for addons that may or may not be present
53 22
		$this->addon_specific_hooks();
54 22
	}
55
56
57 23
    static function getInstance() {
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...
58
59 23
        if( empty( self::$instance ) ) {
60 22
            self::$instance = new GravityView_Edit_Entry;
61
        }
62
63 23
        return self::$instance;
64
    }
65
66
67 22
    private function load_components( $component ) {
68
69 22
        $dir = trailingslashit( self::$file );
70
71 22
        $filename  = $dir . 'class-edit-entry-' . $component . '.php';
72 22
        $classname = 'GravityView_Edit_Entry_' . str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $component ) ) );
73
74
        // Loads component and pass extension's instance so that component can
75
        // talk each other.
76 22
        require_once $filename;
77 22
        $this->instances[ $component ] = new $classname( $this );
78 22
        $this->instances[ $component ]->load();
79
80 22
    }
81
82 22
    private function add_hooks() {
83
84
        // Add front-end access to Gravity Forms delete file action
85 22
        add_action( 'wp_ajax_nopriv_rg_delete_file', array( 'GFForms', 'delete_file') );
86
87
        // Make sure this hook is run for non-admins
88 22
        add_action( 'wp_ajax_rg_delete_file', array( 'GFForms', 'delete_file') );
89
90 22
        add_filter( 'gravityview_blacklist_field_types', array( $this, 'modify_field_blacklist' ), 10, 2 );
91
92
        // add template path to check for field
93 22
        add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
94
95 22
		add_filter( 'gravityview/field/is_visible', array( $this, 'maybe_not_visible' ), 10, 3 );
96
97 22
    }
98
99
	/**
100
	 * Trigger hooks that are normally run in the admin for Addons, but need to be triggered manually because we're not in the admin
101
	 * @return void
102
	 */
103 22
	private function addon_specific_hooks() {
104
105 22
		if( class_exists( 'GFSignature' ) && is_callable( array( 'GFSignature', 'get_instance' ) ) ) {
106
			add_filter('gform_admin_pre_render', array( GFSignature::get_instance(), 'edit_lead_script'));
107
		}
108
109 22
	}
110
111
	/**
112
	 * Hide the field or not.
113
	 *
114
	 * For non-logged in users.
115
	 * For users that have no edit rights on any of the current entries.
116
	 *
117
	 * @param bool $visible Visible or not.
118
	 * @param \GV\Field $field The field.
119
	 * @param \GV\View $view The View context.
120
	 *
121
	 * @return bool
122
	 */
123 44
	public function maybe_not_visible( $visible, $field, $view ) {
124
125 44
		if ( 'edit_link' !== $field->ID ) {
126 44
			return $visible;
127
		}
128
129
		if ( ! $view ) {
130
			return $visible;
131
		}
132
133
		static $visibility_cache_for_view = array();
134
135
		if ( ! is_null( $result = \GV\Utils::get( $visibility_cache_for_view, $view->ID, null ) ) ) {
136
			return $result;
137
		}
138
139
		foreach ( $view->get_entries()->all() as $entry ) {
140
			if ( self::check_user_cap_edit_entry( $entry->as_entry(), $view ) ) {
141
				// At least one entry is deletable for this user
142
				$visibility_cache_for_view[ $view->ID ] = true;
143
				return true;
144
			}
145
		}
146
147
		$visibility_cache_for_view[ $view->ID ] = false;
148
149
		return false;
150
	}
151
152
    /**
153
     * Include this extension templates path
154
     * @param array $file_paths List of template paths ordered
155
     */
156 2
    public function add_template_path( $file_paths ) {
157
158
        // Index 100 is the default GravityView template path.
159 2
        $file_paths[ 110 ] = self::$file;
160
161 2
        return $file_paths;
162
    }
163
164
    /**
165
     *
166
     * Return a well formatted nonce key according to GravityView Edit Entry protocol
167
     *
168
     * @param $view_id int GravityView view id
169
     * @param $form_id int Gravity Forms form id
170
     * @param $entry_id int Gravity Forms entry id
171
     * @return string
172
     */
173 24
    public static function get_nonce_key( $view_id, $form_id, $entry_id ) {
174 24
        return sprintf( 'edit_%d_%d_%d', $view_id, $form_id, $entry_id );
175
    }
176
177
178
    /**
179
     * The edit entry link creates a secure link with a nonce
180
     *
181
     * It also mimics the URL structure Gravity Forms expects to have so that
182
     * it formats the display of the edit form like it does in the backend, like
183
     * "You can edit this post from the post page" fields, for example.
184
     *
185
     * @param $entry array Gravity Forms entry object
186
     * @param $view_id int GravityView view id
187
     * @param $post_id int GravityView Post ID where View may be embedded {@since 1.9.2}
188
     * @param string|array $field_values Parameters to pass in to the Edit Entry form to prefill data. Uses the same format as Gravity Forms "Allow field to be populated dynamically" {@since 1.9.2} {@see https://www.gravityhelp.com/documentation/article/allow-field-to-be-populated-dynamically/ }
189
     * @return string
190
     */
191 3
    public static function get_edit_link( $entry, $view_id, $post_id = null, $field_values = '' ) {
192
193 3
        $nonce_key = self::get_nonce_key( $view_id, $entry['form_id'], $entry['id']  );
194
195 3
        $base = gv_entry_link( $entry, $post_id ? : $view_id  );
196
197 3
        $url = add_query_arg( array(
198 3
            'edit' => wp_create_nonce( $nonce_key )
199 3
        ), $base );
200
201 3
        if( $post_id ) {
202 2
	        $url = add_query_arg( array( 'gvid' => $view_id ), $url );
203
        }
204
205
	    /**
206
	     * Allow passing params to dynamically populate entry with values
207
	     * @since 1.9.2
208
	     */
209 3
	    if( !empty( $field_values ) ) {
210
211
		    if( is_array( $field_values ) ) {
212
			    // If already an array, no parse_str() needed
213
			    $params = $field_values;
214
		    } else {
215
			    parse_str( $field_values, $params );
216
		    }
217
218
		    $url = add_query_arg( $params, $url );
219
	    }
220
221
		/**
222
		 * @filter `gravityview/edit/link` Filter the edit URL link.
223
		 * @param[in,out] string $url The url.
224
		 * @param array $entry The entry.
225
		 * @param \GV\View $view The View.
226
		 */
227 3
		return apply_filters( 'gravityview/edit/link', $url, $entry, \GV\View::by_id( $view_id  ) );
228
    }
229
230
	/**
231
	 * Edit mode doesn't allow certain field types.
232
	 * @param  array $fields  Existing blacklist fields
233
	 * @param  string|null $context Context
234
	 * @return array          If not edit context, original field blacklist. Otherwise, blacklist including post fields.
235
	 */
236
	public function modify_field_blacklist( $fields = array(), $context = NULL ) {
237
238
		if( empty( $context ) || $context !== 'edit' ) {
239
			return $fields;
240
		}
241
242
		$add_fields = $this->get_field_blacklist();
243
244
		return array_merge( $fields, $add_fields );
245
	}
246
247
	/**
248
	 * Returns array of field types that should not be displayed in Edit Entry
249
	 *
250
	 * @since 1.20
251
	 *
252
	 * @param array $entry Gravity Forms entry array
253
	 *
254
	 * @return array Blacklist of field types
255
	 */
256 22
	function get_field_blacklist( $entry = array() ) {
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...
257
258
		$fields = array(
259 22
			'page',
260
			'payment_status',
261
			'payment_date',
262
			'payment_amount',
263
			'is_fulfilled',
264
			'transaction_id',
265
			'transaction_type',
266
			'captcha',
267
			'honeypot',
268
			'creditcard',
269
		);
270
271
		/**
272
		 * @filter `gravityview/edit_entry/field_blacklist` Array of fields that should not be displayed in Edit Entry
273
		 * @since 1.20
274
		 * @param array $fields Blacklist field type array
275
		 * @param array $entry Gravity Forms entry array
276
		 */
277 22
		$fields = apply_filters( 'gravityview/edit_entry/field_blacklist', $fields, $entry );
278
279 22
		return $fields;
280
	}
281
282
283
    /**
284
     * checks if user has permissions to edit a specific entry
285
     *
286
     * Needs to be used combined with GravityView_Edit_Entry::user_can_edit_entry for maximum security!!
287
     *
288
     * @param  array $entry Gravity Forms entry array
289
     * @param \GV\View|int $view ID of the view you want to check visibility against {@since 1.9.2}. Required since 2.0
290
     * @return bool
291
     */
292 24
    public static function check_user_cap_edit_entry( $entry, $view = 0 ) {
293
294
        // No permission by default
295 24
        $user_can_edit = false;
296
297
		// get user_edit setting
298 24
		if ( empty( $view ) ) {
299
			// @deprecated path
300 22
			$view_id = GravityView_View::getInstance()->getViewId();
301 22
			$user_edit = GravityView_View::getInstance()->getAtts( 'user_edit' );
302
		} else {
303 2
			if ( $view instanceof \GV\View ) {
304
				$view_id = $view->ID;
305
			} else {
306 2
				$view_id = $view;
307
			}
308
309
			// in case is specified and not the current view
310 2
			$user_edit = GVCommon::get_template_setting( $view_id, 'user_edit' );
311
		}
312
313
        // If they can edit any entries (as defined in Gravity Forms)
314
        // Or if they can edit other people's entries
315
        // Then we're good.
316 24
        if( GVCommon::has_cap( array( 'gravityforms_edit_entries', 'gravityview_edit_others_entries' ), $entry['id'] ) ) {
317
318 23
            gravityview()->log->debug( 'User has ability to edit all entries.' );
319
320 23
            $user_can_edit = true;
321
322 3
        } else if( !isset( $entry['created_by'] ) ) {
323
324 1
            gravityview()->log->error( 'Entry `created_by` doesn\'t exist.');
325
326 1
            $user_can_edit = false;
327
328
        } else {
329
330 3
            $current_user = wp_get_current_user();
331
332
            // User edit is disabled
333 3
            if( empty( $user_edit ) ) {
334
335 2
                gravityview()->log->debug( 'User Edit is disabled. Returning false.' );
336
337 2
                $user_can_edit = false;
338
            }
339
340
            // User edit is enabled and the logged-in user is the same as the user who created the entry. We're good.
341 2
            else if( is_user_logged_in() && intval( $current_user->ID ) === intval( $entry['created_by'] ) ) {
342
343 2
                gravityview()->log->debug( 'User {user_id} created the entry.', array( 'user_id', $current_user->ID ) );
344
345 2
                $user_can_edit = true;
346
347 2
            } else if( ! is_user_logged_in() ) {
348
349
                gravityview()->log->debug( 'No user defined; edit entry requires logged in user' );
350
351
	            $user_can_edit = false; // Here just for clarity
352
            }
353
354
        }
355
356
        /**
357
         * @filter `gravityview/edit_entry/user_can_edit_entry` Modify whether user can edit an entry.
358
         * @since 1.15 Added `$entry` and `$view_id` parameters
359
         * @param[in,out] boolean $user_can_edit Can the current user edit the current entry? (Default: false)
360
         * @param[in] array $entry Gravity Forms entry array {@since 1.15}
361
         * @param[in] int $view_id ID of the view you want to check visibility against {@since 1.15}
362
         */
363 24
        $user_can_edit = apply_filters( 'gravityview/edit_entry/user_can_edit_entry', $user_can_edit, $entry, $view_id );
364
365 24
        return (bool) $user_can_edit;
366
    }
367
368
369
370
} // end class
371
372
//add_action( 'plugins_loaded', array('GravityView_Edit_Entry', 'getInstance'), 6 );
373
GravityView_Edit_Entry::getInstance();
374
375