Completed
Push — develop ( 5c975c...99674a )
by Gennady
16:19
created

GravityView_Duplicate_Entry::process_duplicate()   C

Complexity

Conditions 9
Paths 10

Size

Total Lines 81

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 68.049

Importance

Changes 0
Metric Value
cc 9
nc 10
nop 0
dl 0
loc 81
ccs 3
cts 30
cp 0.1
crap 68.049
rs 6.8589
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * The GravityView Duplicate Entry Extension
4
 *
5
 * Duplicate entries in GravityView.
6
 *
7
 * @since     2.5
8
 * @package   GravityView
9
 * @license   GPL2+
10
 * @author    Katz Web Services, Inc.
11
 * @link      http://gravityview.co
12
 * @copyright Copyright 2014, Katz Web Services, Inc.
13
 */
14
15
if ( ! defined( 'WPINC' ) ) {
16
	die;
17
}
18
19
/**
20
 * @since 2.5
21
 */
22
final class GravityView_Duplicate_Entry {
23
24
	/**
25
	 * @var string The location of this file.
26
	 */
27
	static $file;
28
29
	/**
30
	 * @var GravityView_Duplicate_Entry This instance.
31
	 */
32
	static $instance;
33
34
	/**
35
	 * @var array Global entry state.
36
	 */
37
	var $entry;
38
39
	var $view_id;
40
41
	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...
42
43
		self::$file = plugin_dir_path( __FILE__ );
44
		$this->add_hooks();
45
	}
46
47
	/**
48
	 * @since 2.5
49
	 */
50
	private function add_hooks() {
51
52
		add_action( 'wp', array( $this, 'process_duplicate' ), 10000 );
53
54
		add_filter( 'gravityview_entry_default_fields', array( $this, 'add_default_field' ), 10, 3 );
55
56
		add_action( 'gravityview_before', array( $this, 'display_message' ) );
57
58
		// For the Duplicate Entry Link, you don't want visible to all users.
59
		add_filter( 'gravityview_field_visibility_caps', array( $this, 'modify_visibility_caps' ), 10, 5 );
60
61
		// Modify the field options based on the name of the field type
62
		add_filter( 'gravityview_template_duplicate_link_options', array( $this, 'duplicate_link_field_options' ), 10, 5 );
63
64
		// add template path to check for field
65
		add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
66
	}
67
68
	/**
69
	 * Return the instantiated class object
70
	 *
71
	 * @since  2.5
72
	 * @return GravityView_Duplicate_Entry
73
	 */
74 1
	static public function getInstance() {
75
76 1
		if ( empty( self::$instance ) ) {
77
			self::$instance = new self;
78
		}
79
80 1
		return self::$instance;
81
	}
82
83
	/**
84
	 * Include this extension templates path
85
	 *
86
	 * @since  2.5
87
	 *
88
	 * @param array $file_paths List of template paths ordered
89
	 *
90
	 * @return array File paths, with duplicate field path added at index 117
91
	 */
92 2
	public function add_template_path( $file_paths ) {
93
94
		// Index 100 is the default GravityView template path.
95
		// Index 110 is Edit Entry link
96 2
		$file_paths[ 117 ] = self::$file;
97
98 2
		return $file_paths;
99
	}
100
101
	/**
102
	 * Add "Duplicate Link Text" setting to the edit_link field settings
103
	 *
104
	 * @since  2.5
105
	 *
106
	 * @param  array  $field_options [description]
107
	 * @param  [type] $template_id   [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

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

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

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

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

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

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

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

Loading history...
111
	 *
112
	 * @return array                [description]
113
	 */
114
	public function duplicate_link_field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
115
116
		// Always a link, never a filter
117
		unset( $field_options['show_as_link'], $field_options['search_filter'] );
118
119
120
		// Duplicate Entry link should only appear to visitors capable of editing entries
121
		unset( $field_options['only_loggedin'], $field_options['only_loggedin_cap'] );
122
123
		$add_option['duplicate_link'] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$add_option was never initialized. Although not strictly required by PHP, it is generally a good practice to add $add_option = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
124
			'type' => 'text',
125
			'label' => __( 'Duplicate Link Text', 'gravityview' ),
126
			'desc' => NULL,
127
			'value' => __( 'Duplicate Entry', 'gravityview' ),
128
			'merge_tags' => true,
129
		);
130
131
		$field_options['allow_duplicate_cap'] = array(
132
			'type' => 'select',
133
			'label' => __( 'Allow the following users to duplicate the entry:', 'gravityview' ),
134
			'choices' => GravityView_Render_Settings::get_cap_choices( $template_id, $field_id, $context, $input_type ),
135
			'tooltip' => 'allow_duplicate_cap',
136
			'class' => 'widefat',
137
			'value' => 'read', // Default: entry creator
138
		);
139
140
		return array_merge( $add_option, $field_options );
141
	}
142
143
144
	/**
145
	 * Add Edit Link as a default field, outside those set in the Gravity Form form
146
	 *
147
	 * @since 2.5
148
	 *
149
	 * @param array $entry_default_fields Existing fields
150
	 * @param  string|array $form form_ID or form object
151
	 * @param  string $zone   Either 'single', 'directory', 'edit', 'header', 'footer'
152
	 *
153
	 * @return array $entry_default_fields, with `duplicate_link` added. Won't be added if in Edit Entry context.
154
	 */
155
	public function add_default_field( $entry_default_fields, $form = array(), $zone = '' ) {
156
157
		if ( 'edit' !== $zone ) {
158
			$entry_default_fields['duplicate_link'] = array(
159
				'label' => __( 'Duplicate Entry', 'gravityview' ),
160
				'type'  => 'duplicate_link',
161
				'desc'  => __( 'A link to duplicate the entry. Respects the Duplicate Entry permissions.', 'gravityview' ),
162
			);
163
		}
164
165
		return $entry_default_fields;
166
	}
167
168
	/**
169
	 * Add Duplicate Entry Link to the Add Field dialog
170
	 *
171
	 * @since 2.5
172
	 *
173
	 * @param array $available_fields
174
	 *
175
	 * @return array Fields with `duplicate_link` added
176
	 */
177
	public function add_available_field( $available_fields = array() ) {
178
179
		$available_fields['duplicate_link'] = array(
180
			'label_text' => __( 'Duplicate Entry', 'gravityview' ),
181
			'field_id' => 'duplicate_link',
182
			'label_type' => 'field',
183
			'input_type' => 'duplicate_link',
184
			'field_options' => NULL
185
		);
186
187
		return $available_fields;
188
	}
189
190
	/**
191
	 * Change wording for the Edit context to read Entry Creator
192
	 *
193
	 * @since 2.5
194
	 *
195
	 * @param  array 	    $visibility_caps        Array of capabilities to display in field dropdown.
196
	 * @param  string       $field_type  Type of field options to render (`field` or `widget`)
0 ignored issues
show
Bug introduced by
There is no parameter named $field_type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
197
	 * @param  string       $template_id Table slug
198
	 * @param  float|string $field_id    GF Field ID - Example: `3`, `5.2`, `entry_link`, `created_by`
199
	 * @param  string       $context     What context are we in? Example: `single` or `directory`
200
	 * @param  string       $input_type  (textarea, list, select, etc.)
201
	 *
202
	 * @return array                   Array of field options with `label`, `value`, `type`, `default` keys
203
	 */
204
	public function modify_visibility_caps( $visibility_caps = array(), $template_id = '', $field_id = '', $context = '', $input_type = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed.

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

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

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

Loading history...
205
206
		$caps = $visibility_caps;
207
208
		// If we're configuring fields in the edit context, we want a limited selection
209
		if ( 'duplicate_link' === $field_id ) {
210
211
			// Remove other built-in caps.
212
			unset( $caps['publish_posts'], $caps['gravityforms_view_entries'], $caps['duplicate_others_posts'] );
213
214
			$caps['read'] = _x( 'Entry Creator', 'User capability', 'gravityview' );
215
		}
216
217
		return $caps;
218
	}
219
220
	/**
221
	 * Make sure there's an entry
222
	 *
223
	 * @since 2.5
224
	 *
225
	 * @param array $entry Current entry array
226
	 *
227
	 * @return void
228
	 */
229
	public function set_entry( $entry ) {
230
		$this->entry = $entry;
231
	}
232
233
	/**
234
	 * Generate a consistent nonce key based on the Entry ID
235
	 *
236
	 * @since 2.5
237
	 *
238
	 * @param  int $entry_id Entry ID
239
	 *
240
	 * @return string           Key used to validate request
241
	 */
242
	public static function get_nonce_key( $entry_id ) {
243
		return sprintf( 'duplicate_%s', $entry_id );
244
	}
245
246
247
	/**
248
	 * Generate a nonce link with the base URL of the current View embed
249
	 *
250
	 * We don't want to link to the single entry, because when duplicated, there would be nothing to return to.
251
	 *
252
	 * @since 2.5
253
	 *
254
	 * @param  array       $entry Gravity Forms entry array
255
	 * @param  int         $view_id The View id. Not optional since 2.0
256
	 * @param  int         $post_id ID of the current post/page being embedded on, if any
257
	 *
258
	 * @return string|null If directory link is valid, the URL to process the duplicate request. Otherwise, `NULL`.
259
	 */
260 2
	public static function get_duplicate_link( $entry, $view_id, $post_id = null ) {
261 2
		self::getInstance()->set_entry( $entry );
262
263 2
        $base = GravityView_API::directory_link( $post_id ? : $view_id, true );
264
265 2
		if ( empty( $base ) ) {
266
			gravityview()->log->error( 'Post ID does not exist: {post_id}', array( 'post_id' => $post_id ) );
267
			return NULL;
268
		}
269
270 2
		$actionurl = add_query_arg( array(
271 2
			'action'	=> 'duplicate',
272 2
			'entry_id'	=> $entry['id'],
273 2
			'gvid' => $view_id,
274 2
            'view_id' => $view_id,
275 2
		), $base );
276
277 2
		return add_query_arg( 'duplicate', wp_create_nonce( self::get_nonce_key( $entry['id'] ) ), $actionurl );
278
	}
279
280
	/**
281
	 * Handle the duplication request, if $_GET['action'] is set to "duplicate"
282
	 *
283
	 * 1. Check referrer validity
284
	 * 2. Make sure there's an entry with the slug of $_GET['entry_id']
285
	 * 3. If so, attempt to duplicate the entry. If not, set the error status
286
	 * 4. Remove `action=duplicate` from the URL
287
	 * 5. Redirect to the page using `wp_safe_redirect()`
288
	 *
289
	 * @since 2.5
290
	 *
291
	 * @uses wp_safe_redirect()
292
	 *
293
	 * @return void|string $url URL during tests instead of redirect.
294
	 */
295 1
	public function process_duplicate() {
296
297
		// If the form is submitted
298 1
		if ( ! isset( $_GET['action'] ) || 'duplicate' !== $_GET['action'] || ! isset( $_GET['entry_id'] ) ) {
299 1
			return;
300
		}
301
302
		// Make sure it's a GravityView request
303
		$valid_nonce_key = wp_verify_nonce( \GV\Utils::_GET( 'duplicate' ), self::get_nonce_key( $_GET['entry_id'] ) );
304
305
		if ( ! $valid_nonce_key ) {
306
			gravityview()->log->debug( 'Duplicate entry not processed: nonce validation failed.' );
307
			return;
308
		}
309
310
		// Get the entry slug
311
		$entry_slug = esc_attr( $_GET['entry_id'] );
312
313
		// See if there's an entry there
314
		$entry = gravityview_get_entry( $entry_slug, true, false );
315
316
		if ( $entry ) {
317
318
			$has_permission = $this->user_can_duplicate_entry( $entry );
319
320
			if ( is_wp_error( $has_permission ) ) {
321
322
				$messages = array(
323
					'message' => urlencode( $has_permission->get_error_message() ),
324
					'status' => 'error',
325
				);
326
327
			} else {
328
329
				// Duplicate the entry
330
				$duplicate_response = $this->duplicate_entry( $entry );
331
332
				if ( is_wp_error( $duplicate_response ) ) {
333
334
					$messages = array(
335
						'message' => urlencode( $duplicate_response->get_error_message() ),
336
						'status' => 'error',
337
					);
338
339
					gravityview()->log->error( 'Entry {entry_slug} cannot be duplicated: {error_code} {error_message}', array(
340
						'entry_slug'    => $entry_slug,
341
						'error_code'    => $duplicate_response->get_error_code(),
342
						'error_message' => $duplicate_response->get_error_message(),
343
					) );
344
345
				} else {
346
347
					$messages = array(
348
						'status' => $duplicate_response,
349
					);
350
351
				}
352
353
			}
354
355
		} else {
356
357
			gravityview()->log->error( 'Duplicate entry failed: there was no entry with the entry slug {entry_slug}', array( 'entry_slug' => $entry_slug ) );
358
359
			$messages = array(
360
				'message' => urlencode( __( 'The entry does not exist.', 'gravityview' ) ),
361
				'status' => 'error',
362
			);
363
		}
364
365
		$redirect_to_base = esc_url_raw( remove_query_arg( array( 'action', 'gvid' ) ) );
366
		$redirect_to = add_query_arg( $messages, $redirect_to_base );
367
368
		if ( defined( 'DOING_GRAVITYVIEW_TESTS' ) ) {
369
			return $redirect_to;
370
		}
371
372
		wp_safe_redirect( $redirect_to );
373
374
		exit();
375
	}
376
377
	/**
378
	 * Duplicate the entry.
379
	 *
380
	 * Done after all the checks in self::process_duplicate.
381
	 *
382
	 * @since 2.5
383
	 *
384
	 * @param array $entry The entry to be duplicated
385
	 *
386
	 * @return WP_Error|boolean
387
	 */
388
	private function duplicate_entry( $entry ) {
389
390
		if ( ! $entry_id = \GV\Utils::get( $entry, 'id' ) ) {
391
			return new WP_Error( 'gravityview-duplicate-entry-missing', __( 'The entry does not exist.', 'gravityview' ) );
392
		}
393
394
		gravityview()->log->debug( 'Starting duplicate entry: {entry_id}', array( 'entry_id' => $entry_id ) );
395
396
		global $wpdb;
397
398
		$entry_table = GFFormsModel::get_entry_table_name();
399
		$entry_meta_table = GFFormsModel::get_entry_meta_table_name();
400
401
		if ( ! $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $entry_table WHERE ID = %d", $entry_id ), ARRAY_A ) ) {
402
			return new WP_Error( 'gravityview-duplicate-entry-missing', __( 'The entry does not exist.', 'gravityview' ) );
403
		}
404
405
		$row['id'] = null;
406
		$row['date_created'] = date( 'Y-m-d H:i:s', time() );
407
		$row['date_updated'] = $row['date_created'];
408
		$row['is_starred'] = false;
409
		$row['is_read'] = false;
410
		$row['ip'] = GFFormsModel::get_ip();
411
		$row['source_url'] = esc_url_raw( remove_query_arg( array( 'action', 'gvid' ) ) );
412
		$row['user_agent'] = \GV\Utils::_SERVER( 'HTTP_USER_AGENT' );
413
		$row['created_by'] = wp_get_current_user()->ID;
414
415
		/**
416
		 * @filter `gravityview/entry/duplicate/details` Modify the new entry details before it's created.
417
		 * @since 2.5
418
		 * @param[in,out] array $row The entry details
419
		 * @param array $entry The original entry
420
		 */
421
		$row = apply_filters( 'gravityview/entry/duplicate/details', $row, $entry );
422
423
		if ( ! $wpdb->insert( $entry_table, $row ) ) {
424
			return new WP_Error( 'gravityview-duplicate-entry-db-details', __( 'There was an error duplicating the entry.', 'gravityview' ) );
425
		}
426
427
		$duplicated_id = $wpdb->insert_id;
428
429
		$meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $entry_meta_table WHERE entry_id = %d", $entry_id ), ARRAY_A );
430
431
		$duplicate_meta = new WP_List_Util( $meta );
432
433
		// Keys that should be reset by default
434
		$reset_meta = array( 'is_approved', 'gravityview_unique_id', 'workflow_current_status_timestamp' );
435
		foreach ( $reset_meta as $meta_key ) {
436
			$duplicate_meta->filter( array( 'meta_key' => $meta_key ), 'NOT' );
437
		}
438
439
		$save_this_meta = array();
440
		foreach ( $duplicate_meta->get_output() as $m ) {
441
			$save_this_meta[] = array(
442
				'meta_key' => $m['meta_key'],
443
				'meta_value' => $m['meta_value'],
444
				'item_index' => $m['item_index'],
445
			);
446
		}
447
448
		// Update the row ID for later usage
449
		$row['id'] = $duplicated_id;
450
451
		/**
452
		 * @filter `gravityview/entry/duplicate/meta` Modify the new entry meta details.
453
		 * @param[in,out] array $dulicate_meta The duplicate meta. Use/add meta_key, meta_value, item_index.
454
		 * @param array $duplicate_entry The duplicated entry
455
		 * @param array $entry The original entry
456
		 */
457
		$save_this_meta = apply_filters( 'gravityview/entry/duplicate/meta', $save_this_meta, $row, $entry );
458
459
		foreach ( $save_this_meta as $data ) {
460
			$data['form_id'] = $entry['form_id'];
461
			$data['entry_id'] = $duplicated_id;
462
463
			if ( ! $wpdb->insert( $entry_meta_table, $data ) ) {
464
				return new WP_Error( 'gravityview-duplicate-entry-db-meta', __( 'There was an error duplicating the entry.', 'gravityview' ) );
465
			}
466
		}
467
468
		$duplicated_entry = \GFAPI::get_entry( $duplicated_id );
469
470
		$duplicate_response = 'duplicated';
471
472
		/**
473
		 * @action `gravityview/entry/duplicated` Triggered when an entry is duplicated
474
		 * @since 2.5
475
		 * @param  array $duplicated_entry The duplicated entry
476
		 * @param  array $entry The original entry
477
		*/
478
		do_action( 'gravityview/duplicate-entry/duplicated', $duplicated_entry, $entry );
479
480
		gravityview()->log->debug( 'Duplicate response: {duplicate_response}', array( 'duplicate_response' => $duplicate_response ) );
481
482
		return $duplicate_response;
483
	}
484
485
	/**
486
	 * Is the current nonce valid for editing the entry?
487
	 *
488
	 * @since 2.5
489
	 *
490
	 * @return boolean
491
	 */
492
	public function verify_nonce() {
493
494
		// No duplicate entry request was made
495
		if ( empty( $_GET['entry_id'] ) || empty( $_GET['duplicate'] ) ) {
496
			return false;
497
		}
498
499
		$nonce_key = self::get_nonce_key( $_GET['entry_id'] );
500
501
		$valid = wp_verify_nonce( $_GET['duplicate'], $nonce_key );
502
503
		/**
504
		 * @filter `gravityview/duplicate-entry/verify_nonce` Override Duplicate Entry nonce validation. Return true to declare nonce valid.
505
		 * @since 2.5
506
		 * @see wp_verify_nonce()
507
		 * @param int|boolean $valid False if invalid; 1 or 2 when nonce was generated
508
		 * @param string $nonce_key Name of nonce action used in wp_verify_nonce. $_GET['duplicate'] holds the nonce value itself. Default: `duplicate_{entry_id}`
509
		 */
510
		$valid = apply_filters( 'gravityview/duplicate-entry/verify_nonce', $valid, $nonce_key );
511
512
		return $valid;
513
	}
514
515
	/**
516
	 * Get the onclick attribute for the confirm dialogs that warns users before they duplicate an entry
517
	 *
518
	 * @since 2.5
519
	 *
520
	 * @return string HTML `onclick` attribute
521
	 */
522
	public static function get_confirm_dialog() {
523
524
		$confirm = __( 'Are you sure you want to duplicate this entry?', 'gravityview' );
525
526
		/**
527
		 * @filter `gravityview/duplicate-entry/confirm-text` Modify the Duplicate Entry Javascript confirmation text
528
		 * @param string $confirm Default: "Are you sure you want to duplicate this entry?"
529
		 */
530
		$confirm = apply_filters( 'gravityview/duplicate-entry/confirm-text', $confirm );
531
532
		return 'return window.confirm(\''. esc_js( $confirm ) .'\');';
533
	}
534
535
	/**
536
	 * Check if the user can edit the entry
537
	 *
538
	 * - Is the nonce valid?
539
	 * - Does the user have the right caps for the entry
540
	 * - Is the entry in the trash?
541
	 *
542
	 * @since 2.5
543
	 *
544
	 * @param  array $entry Gravity Forms entry array
545
	 * @param  int   $view_id ID of the View being rendered
546
	 *
547
	 * @return boolean|WP_Error        True: can edit form. WP_Error: nope.
548
	 */
549
	private function user_can_duplicate_entry( $entry = array(), $view_id = null ) {
550
551
		$error = NULL;
552
553
		if ( ! $this->verify_nonce() ) {
554
			$error = __( 'The link to duplicate this entry is not valid; it may have expired.', 'gravityview' );
555
		}
556
557
		if ( ! self::check_user_cap_duplicate_entry( $entry, array(), $view_id ) ) {
558
			$error = __( 'You do not have permission to duplicate this entry.', 'gravityview' );
559
		}
560
561
		// No errors; everything's fine here!
562
		if ( empty( $error ) ) {
563
			return true;
564
		}
565
566
		gravityview()->log->error( '{error}', array( 'erorr' => $error ) );
567
568
		return new WP_Error( 'gravityview-duplicate-entry-permissions', $error );
569
	}
570
571
572
	/**
573
	 * checks if user has permissions to view the link or duplicate a specific entry
574
	 *
575
	 * @since 2.5
576
	 *
577
	 * @param  array $entry Gravity Forms entry array
578
	 * @param array $field Field settings (optional)
579 1
	 * @param int $view_id Pass a View ID to check caps against. If not set, check against current View (@deprecated no longer optional)
580 1
	 *
581
	 * @return bool
582 1
	 */
583
	public static function check_user_cap_duplicate_entry( $entry, $field, $view_id ) {
584
		$current_user = wp_get_current_user();
585 1
586
		$entry_id = isset( $entry['id'] ) ? $entry['id'] : NULL;
587 1
588
		// Or if they can duplicate any entries (as defined in Gravity Forms), we're good.
589 1
		if ( GVCommon::has_cap( array( 'gravityforms_edit_entries', 'gform_full_access', 'gravityview_full_access' ), $entry_id ) ) {
590
591
			gravityview()->log->debug( 'Current user has `gravityforms_edit_entries` capability.' );
592
593
			return true;
594 1
		}
595
596
597
		// If field options are passed, check if current user can view the link
598
		if ( ! empty( $field ) ) {
599
600
			// If capability is not defined, something is not right!
601
			if ( empty( $field['allow_duplicate_cap'] ) ) {
602
603
				gravityview()->log->error( 'Cannot read duplicate entry field caps', array( 'data' => $field ) );
604
605
				return false;
606
			}
607
608
			if ( GVCommon::has_cap( $field['allow_duplicate_cap'] ) ) {
609
610
				// Do not return true if cap is read, as we need to check if the current user created the entry
611
				if ( 'read' !== $field['allow_duplicate_cap'] ) {
612
					return true;
613
				}
614
615
			} else {
616
617
				gravityview()->log->debug( 'User {user_id} is not authorized to view duplicate entry link ', array( 'user_id' => $current_user->ID ) );
618
619
				return false;
620 1
			}
621
622 1
		}
623
624 1
		if ( ! isset( $entry['created_by'] ) ) {
625
626
			gravityview()->log->error( 'Cannot duplicate entry; entry `created_by` doesn\'t exist.' );
627
628 1
			return false;
629
		}
630 1
631
		// Only checks user_duplicate view option if view is already set
632
		if ( $view_id ) {
633
634 1
			if ( ! $view = \GV\View::by_id( $view_id ) ) {
635
				return false;
636 1
			}
637
638 1
			$user_duplicate = $view->settings->get( 'user_duplicate', false );
639
640 1
			if ( empty( $user_duplicate ) ) {
641
642
				gravityview()->log->debug( 'User Duplicate is disabled. Returning false.' );
643
644
				return false;
645 1
			}
646
		}
647 1
648
		// If the logged-in user is the same as the user who created the entry, we're good.
649 1
		if ( is_user_logged_in() && intval( $current_user->ID ) === intval( $entry['created_by'] ) ) {
650
651
			gravityview()->log->debug( 'User {user_id} created the entry.', array( 'user_id' => $current_user->ID ) );
652 1
653
			return true;
654
		}
655
656
		return false;
657
	}
658
659
660
	/**
661
	 * After processing duplicate entry, the user will be redirected to the referring View or embedded post/page. Display a message on redirection.
662
	 *
663
	 * If success, there will be `status` URL parameters `status=>success`
664
	 * If an error, there will be `status` and `message` URL parameters `status=>error&message=example`
665
	 *
666
	 * @since 2.5
667
	 *
668 36
	 * @param int $current_view_id The ID of the View being rendered
669
	 *
670 36
	 * @return void
671 36
	 */
672
	public function display_message( $current_view_id = 0 ) {
673
674
		if ( empty( $_GET['status'] ) || ! self::verify_nonce() ) {
675
			return;
676
		}
677
678
		// Entry wasn't duplicateded from current View
679
		if ( isset( $_GET['view_id'] ) && ( intval( $_GET['view_id'] ) !== intval( $current_view_id ) ) ) {
680
			return;
681
		}
682
683
		$status = esc_attr( $_GET['status'] );
684
		$message_from_url = \GV\Utils::_GET( 'message' );
685
		$message_from_url = rawurldecode( stripslashes_deep( $message_from_url ) );
686
		$class = '';
687
688
		switch ( $status ) {
689
			case 'error':
690
				$class = ' gv-error error';
691
				$error_message = __( 'There was an error duplicating the entry: %s', 'gravityview' );
692
				$message = sprintf( $error_message, $message_from_url );
693
				break;
694
			default:
695
				$message = __( 'The entry was successfully duplicated.', 'gravityview' );
696
				break;
697
		}
698
699
		/**
700
		 * @filter `gravityview/duplicate-entry/message` Modify the Duplicate Entry messages. Allows HTML; will not be further sanitized.
701
		 * @since 2.5
702
		 * @param string $message Message to be displayed, sanitized using esc_attr()
703
		 * @param string $status Message status (`error` or `success`)
704
		 * @param string $message_from_url The original error message, if any, without the "There was an error duplicating the entry:" prefix
705
		 */
706
		$message = apply_filters( 'gravityview/duplicate-entry/message', esc_attr( $message ), $status, $message_from_url );
707
708
		// DISPLAY ERROR/SUCCESS MESSAGE
709
		echo '<div class="gv-notice' . esc_attr( $class ) .'">'. $message .'</div>';
710
	}
711
712
713
} // end class
714
715
GravityView_Duplicate_Entry::getInstance();
716
717