Completed
Push — develop ( 0586aa...5d54b4 )
by Zack
15:10
created

GVCommon::has_cap()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 19 and the first side effect is on line 16.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Set of common functions to separate main plugin from Gravity Forms API and other cross-plugin methods
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
 * @since 1.5.2
12
 */
13
14
/** If this file is called directly, abort. */
15
if ( ! defined( 'ABSPATH' ) ) {
16
	die;
17
}
18
19
class GVCommon {
20
21
	/**
22
	 * Returns the form object for a given Form ID.
23
	 *
24
	 * @access public
25
	 * @param mixed $form_id
26
	 * @return array|false Array: Form object returned from Gravity Forms; False: no form ID specified or Gravity Forms isn't active.
27
	 */
28 28
	public static function get_form( $form_id ) {
29 28
		if ( empty( $form_id ) ) {
30
			return false;
31
		}
32
33
		// Only get_form_meta is cached. ::facepalm::
34 28
		if ( class_exists( 'GFFormsModel' ) ) {
35 28
			return GFFormsModel::get_form_meta( $form_id );
36
		}
37
38
		if ( class_exists( 'GFAPI' ) ) {
39
			return GFAPI::get_form( $form_id );
40
		}
41
42
		return false;
43
	}
44
45
	/**
46
	 * Alias of GravityView_Roles_Capabilities::has_cap()
47
	 *
48
	 * @since 1.15
49
	 *
50
	 * @see GravityView_Roles_Capabilities::has_cap()
51
	 *
52
	 * @param string|array $caps Single capability or array of capabilities
53
	 * @param int $object_id (optional) Parameter can be used to check for capabilities against a specific object, such as a post or user
54
	 * @param int|null $user_id (optional) Check the capabilities for a user who is not necessarily the currently logged-in user
55
	 *
56
	 * @return bool True: user has at least one passed capability; False: user does not have any defined capabilities
57
	 */
58 21
	public static function has_cap( $caps = '', $object_id = null, $user_id = null ) {
59 21
		return GravityView_Roles_Capabilities::has_cap( $caps, $object_id, $user_id );
60
	}
61
62
	/**
63
	 * Return a Gravity Forms field array, whether using GF 1.9 or not
64
	 *
65
	 * @since 1.7
66
	 *
67
	 * @param array|GF_Fields $field Gravity Forms field or array
68
	 * @return array Array version of $field
69
	 */
70
	public static function get_field_array( $field ) {
71
72
		if ( class_exists( 'GF_Fields' ) ) {
73
74
			$field_object = GF_Fields::create( $field );
75
76
			// Convert the field object in 1.9 to an array for backward compatibility
77
			$field_array = get_object_vars( $field_object );
78
79
		} else {
80
			$field_array = $field;
81
		}
82
83
		return $field_array;
84
	}
85
86
	/**
87
	 * Get all existing Views
88
	 *
89
	 * @since 1.5.4 Added $args array
90
	 *
91
	 * @param array $args Pass custom array of args, formatted as if for `get_posts()`
92
	 *
93
	 * @return WP_Post[] Array of Views as `WP_Post`. Empty array if none found.
94
	 */
95 3
	public static function get_all_views( $args = array() ) {
96
97
		$default_params = array(
98 3
			'post_type' => 'gravityview',
99
			'posts_per_page' => -1,
0 ignored issues
show
introduced by
Disabling pagination is prohibited in VIP context, do not set posts_per_page to -1 ever.
Loading history...
100
			'post_status' => 'publish',
101
		);
102
103 3
		$params = wp_parse_args( $args, $default_params );
104
105
		/**
106
		 * @filter `gravityview/get_all_views/params` Modify the parameters sent to get all views.
107
		 * @param[in,out]  array $params Array of parameters to pass to `get_posts()`
108
		 */
109 3
		$views_params = apply_filters( 'gravityview/get_all_views/params', $params );
110
111 3
		$views = get_posts( $views_params );
112
113 3
		return $views;
114
	}
115
116
117
	/**
118
	 * Get the form array for an entry based only on the entry ID
119
	 * @param  int|string $entry_slug Entry slug
120
	 * @return array|false Array: Form object returned from Gravity Forms; False: form doesn't exist, or $entry didn't exist or $entry didn't specify form ID
121
	 */
122
	public static function get_form_from_entry_id( $entry_slug ) {
123
124
		$entry = self::get_entry( $entry_slug, true, false );
125
126
		$form = false;
127
128
		if( $entry ) {
129
			$form = GFAPI::get_form( $entry['form_id'] );
130
		}
131
132
		return $form;
133
	}
134
135
	/**
136
	 * Check whether a form has product fields
137
	 *
138
	 * @since 1.16
139
	 * @since 1.20 Refactored the field types to get_product_field_types() method
140
	 *
141
	 * @param array $form Gravity Forms form array
142
	 *
143
	 * @return bool|GF_Field[]
144
	 */
145 3
	public static function has_product_field( $form = array() ) {
146
147 3
		$product_fields = self::get_product_field_types();
148
149 3
		$fields = GFAPI::get_fields_by_type( $form, $product_fields );
150
151 3
		return empty( $fields ) ? false : $fields;
152
	}
153
154
	/**
155
	 * Return array of product field types
156
	 *
157
	 * Modify the value using the `gform_product_field_types` filter
158
	 *
159
	 * @since 1.20
160
	 *
161
	 * @return array
162
	 */
163 4
	public static function get_product_field_types() {
164
165 4
		$product_fields = apply_filters( 'gform_product_field_types', array( 'option', 'quantity', 'product', 'total', 'shipping', 'calculation', 'price', 'hiddenproduct', 'singleproduct', 'singleshipping' ) );
166
167 4
		return $product_fields;
168
	}
169
170
	/**
171
	 * Check if an entry has transaction data
172
	 *
173
	 * Checks the following keys to see if they are set: 'payment_status', 'payment_date', 'transaction_id', 'payment_amount', 'payment_method'
174
	 *
175
	 * @since 1.20
176
	 *
177
	 * @param array $entry Gravity Forms entry array
178
	 *
179
	 * @return bool True: Entry has metadata suggesting it has communicated with a payment gateway; False: it does not have that data.
180
	 */
181 4
	public static function entry_has_transaction_data( $entry = array() ) {
182
183 4
		if ( ! is_array( $entry ) ) {
184 1
			return false;
185
		}
186
187 4
		$has_transaction_data = false;
188
189 4
		$payment_meta = array( 'payment_status', 'payment_date', 'transaction_id', 'payment_amount', 'payment_method' );
190
191 4
		foreach ( $payment_meta as $meta ) {
192
193 4
			$has_transaction_data = \GV\Utils::get( $entry, $meta, false );
194
195 4
			if( ! empty( $has_transaction_data ) ) {
196 4
				break;
197
			}
198
		}
199
200 4
		return (bool) $has_transaction_data;
201
	}
202
203
	/**
204
	 * Get the entry ID from the entry slug, which may or may not be the entry ID
205
	 *
206
	 * @since  1.5.2
207
	 * @param  string $slug The entry slug, as returned by GravityView_API::get_entry_slug()
208
	 * @return int|null       The entry ID, if exists; `NULL` if not
209
	 */
210 2
	public static function get_entry_id_from_slug( $slug ) {
211 2
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
212
213
		$search_criteria = array(
214
			'field_filters' => array(
215
				array(
216 2
					'key' => 'gravityview_unique_id', // Search the meta values
217 2
					'value' => $slug,
218 2
					'operator' => 'is',
219 2
					'type' => 'meta',
220
				),
221
			)
222
		);
223
224
		// Limit to one for speed
225
		$paging = array(
226 2
			'page_size' => 1,
227
		);
228
229
		/**
230
		 * @filter `gravityview/common/get_entry_id_from_slug/form_id` The form ID used to get the custom entry ID. Change this to avoid collisions with data from other forms with the same values and the same field ID.
231
		 * @since 1.17.2
232
		 * @param int $form_id ID of the form to search. Default: `0` (searches all forms)
233
		 */
234 2
		$form_id = apply_filters( 'gravityview/common/get_entry_id_from_slug/form_id', 0 );
235
236 2
		$results = GFAPI::get_entries( intval( $form_id ), $search_criteria, null, $paging );
237
238 2
		$result = ( ! empty( $results ) && ! empty( $results[0]['id'] ) ) ? $results[0]['id'] : null;
239
240 2
		return $result;
241
	}
242
243
	/**
244
	 * Alias of GFAPI::get_forms()
245
	 *
246
	 * @see GFAPI::get_forms()
247
	 *
248
	 * @since 1.19 Allow "any" $active status option
249
	 *
250
	 * @param bool|string $active Status of forms. Use `any` to get array of forms with any status. Default: `true`
251
	 * @param bool $trash Include forms in trash? Default: `false`
252
	 *
253
	 * @return array Empty array if GFAPI class isn't available or no forms. Otherwise, the array of Forms
254
	 */
255 1
	public static function get_forms(  $active = true, $trash = false ) {
256 1
		$forms = array();
257 1
		if ( class_exists( 'GFAPI' ) ) {
258 1
			if( 'any' === $active ) {
259
				$active_forms = GFAPI::get_forms( true, $trash );
260
				$inactive_forms = GFAPI::get_forms( false, $trash );
261
				$forms = array_merge( array_filter( $active_forms ), array_filter( $inactive_forms ) );
262
			} else {
263 1
				$forms = GFAPI::get_forms( $active, $trash );
264
			}
265
		}
266 1
		return $forms;
267
	}
268
269
	/**
270
	 * Return array of fields' id and label, for a given Form ID
271
	 *
272
	 * @access public
273
	 * @param string|array $form_id (default: '') or $form object
0 ignored issues
show
Bug introduced by
There is no parameter named $form_id. 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...
274
	 * @param bool $add_default_properties
275
	 * @param bool $include_parent_field
276
	 * @return array
277
	 */
278
	public static function get_form_fields( $form = '', $add_default_properties = false, $include_parent_field = true ) {
279
280
		if ( ! is_array( $form ) ) {
281
			$form = self::get_form( $form );
282
		}
283
284
		$fields = array();
285
		$has_product_fields = false;
286
		$has_post_fields = false;
287
288
		if ( $form ) {
289
			foreach ( $form['fields'] as $field ) {
290
				if ( $include_parent_field || empty( $field['inputs'] ) ) {
291
					$fields["{$field['id']}"] = array(
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
292
						'label' => \GV\Utils::get( $field, 'label' ),
293
						'parent' => null,
294
						'type' => \GV\Utils::get( $field, 'type' ),
295
						'adminLabel' => \GV\Utils::get( $field, 'adminLabel' ),
296
						'adminOnly' => \GV\Utils::get( $field, 'adminOnly' ),
297
					);
298
				}
299
300
				if ( $add_default_properties && ! empty( $field['inputs'] ) ) {
301
					foreach ( $field['inputs'] as $input ) {
302
303
						if( ! empty( $input['isHidden'] ) ) {
304
							continue;
305
						}
306
307
						/**
308
                         * @hack
309
                         * In case of email/email confirmation, the input for email has the same id as the parent field
310
                         */
311
						if( 'email' === $field['type'] && false === strpos( $input['id'], '.' ) ) {
312
                            continue;
313
                        }
314
						$fields["{$input['id']}"] = array(
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
315
							'label' => \GV\Utils::get( $input, 'label' ),
316
							'customLabel' => \GV\Utils::get( $input, 'customLabel' ),
317
							'parent' => $field,
318
							'type' => \GV\Utils::get( $field, 'type' ),
319
							'adminLabel' => \GV\Utils::get( $field, 'adminLabel' ),
320
							'adminOnly' => \GV\Utils::get( $field, 'adminOnly' ),
321
						);
322
					}
323
				}
324
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
325
326
				if( GFCommon::is_product_field( $field['type'] ) ){
327
					$has_product_fields = true;
328
				}
329
330
				if ( GFCommon::is_post_field( $field ) ) {
331
					$has_post_fields = true;
332
				}
333
			}
334
		}
335
336
		/**
337
		 * @since 1.7
338
		 */
339
		if ( $has_post_fields ) {
340
			$fields['post_id'] = array(
341
				'label' => __( 'Post ID', 'gravityview' ),
342
				'type' => 'post_id',
343
			);
344
		}
345
346
		if ( $has_product_fields ) {
347
348
			$payment_fields = GravityView_Fields::get_all( 'pricing' );
349
350
			foreach ( $payment_fields as $payment_field ) {
351
352
				// Either the field exists ($fields['shipping']) or the form explicitly contains a `shipping` field with numeric key
353
				if( isset( $fields["{$payment_field->name}"] ) || GFCommon::get_fields_by_type( $form, $payment_field->name ) ) {
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
354
					continue;
355
				}
356
357
				$fields["{$payment_field->name}"] = array(
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
358
					'label' => $payment_field->label,
359
					'desc' => $payment_field->description,
360
					'type' => $payment_field->name,
361
				);
362
			}
363
		}
364
365
		/**
366
		 * @filter `gravityview/common/get_form_fields` Modify the form fields shown in the Add Field field picker.
367
		 * @since 1.17
368
		 * @param array $fields Associative array of fields, with keys as field type, values an array with the following keys: (string) `label` (required), (string) `type` (required), `desc`, (string) `customLabel`, (GF_Field) `parent`, (string) `adminLabel`, (bool)`adminOnly`
369
		 * @param array $form GF Form array
370
		 * @param bool $include_parent_field Whether to include the parent field when getting a field with inputs
371
		 */
372
		$fields = apply_filters( 'gravityview/common/get_form_fields', $fields, $form, $include_parent_field );
373
374
		return $fields;
375
376
	}
377
378
	/**
379
	 * get extra fields from entry meta
380
	 * @param  string $form_id (default: '')
381
	 * @return array
382
	 */
383
	public static function get_entry_meta( $form_id, $only_default_column = true ) {
384
385
		$extra_fields = GFFormsModel::get_entry_meta( $form_id );
386
387
		$fields = array();
388
389
		foreach ( $extra_fields as $key => $field ){
390
			if ( ! empty( $only_default_column ) && ! empty( $field['is_default_column'] ) ) {
391
				$fields[ $key ] = array( 'label' => $field['label'], 'type' => 'entry_meta' );
392
			}
393
		}
394
395
		return $fields;
396
	}
397
398
399
	/**
400
	 * Wrapper for the Gravity Forms GFFormsModel::search_lead_ids() method
401
	 *
402
	 * @see  GFEntryList::leads_page()
403
	 * @param  int $form_id ID of the Gravity Forms form
404
	 * @since  1.1.6
405
	 * @return array|void          Array of entry IDs. Void if Gravity Forms isn't active.
406
	 */
407
	public static function get_entry_ids( $form_id, $search_criteria = array() ) {
408
409
		if ( ! class_exists( 'GFFormsModel' ) ) {
410
			return;
411
		}
412
413
		return GFFormsModel::search_lead_ids( $form_id, $search_criteria );
414
	}
415
416
	/**
417
	 * Calculates the Search Criteria used on the self::get_entries / self::get_entry methods
418
	 *
419
	 * @since 1.7.4
420
	 *
421
	 * @param array $passed_criteria array Input Criteria (search_criteria, sorting, paging)
422
	 * @param array $form_ids array Gravity Forms form IDs
423
	 * @return array
424
	 */
425 34
	public static function calculate_get_entries_criteria( $passed_criteria = array(), $form_ids = array() ) {
426
427
		$search_criteria_defaults = array(
428 34
			'search_criteria' => null,
429
			'sorting' => null,
430
			'paging' => null,
431 34
			'cache' => (isset( $passed_criteria['cache'] ) ? (bool) $passed_criteria['cache'] : true),
432
			'context_view_id' => null,
433
		);
434
435 34
		$criteria = wp_parse_args( $passed_criteria, $search_criteria_defaults );
436
437 34
		if ( ! empty( $criteria['search_criteria']['field_filters'] ) ) {
438 5
			foreach ( $criteria['search_criteria']['field_filters'] as &$filter ) {
439
440 5
				if ( ! is_array( $filter ) ) {
441 5
					continue;
442
				}
443
444
				// By default, we want searches to be wildcard for each field.
445 5
				$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
446
447
				/**
448
				 * @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc)
449
				 * @param string $operator Existing search operator
450
				 * @param array $filter array with `key`, `value`, `operator`, `type` keys
451
				 */
452 5
				$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
453
			}
454
455
			// don't send just the [mode] without any field filter.
456 5
			if( count( $criteria['search_criteria']['field_filters'] ) === 1 && array_key_exists( 'mode' , $criteria['search_criteria']['field_filters'] ) ) {
0 ignored issues
show
introduced by
Found "=== 1". Use Yoda Condition checks, you must
Loading history...
457 1
				unset( $criteria['search_criteria']['field_filters']['mode'] );
458
			}
459
460
		}
461
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 3 empty lines
Loading history...
462
463
464
		/**
465
		 * Prepare date formats to be in Gravity Forms DB format;
466
		 * $passed_criteria may include date formats incompatible with Gravity Forms.
467
		 */
468 34
		foreach ( array('start_date', 'end_date' ) as $key ) {
0 ignored issues
show
introduced by
No space after opening parenthesis of array is bad style
Loading history...
469
470 34
			if ( ! empty( $criteria['search_criteria'][ $key ] ) ) {
471
472
				// Use date_create instead of new DateTime so it returns false if invalid date format.
473 1
				$date = date_create( $criteria['search_criteria'][ $key ] );
474
475 1
				if ( $date ) {
476
					// Gravity Forms wants dates in the `Y-m-d H:i:s` format.
477 1
					$criteria['search_criteria'][ $key ] = $date->format( 'Y-m-d H:i:s' );
478
				} else {
479 1
					gravityview()->log->error( '{key} Date format not valid:', array( 'key' => $key, $criteria['search_criteria'][ $key ] ) );
480
481
					// If it's an invalid date, unset it. Gravity Forms freaks out otherwise.
482 34
					unset( $criteria['search_criteria'][ $key ] );
483
				}
484
			}
485
		}
486
487 34
		if ( empty( $criteria['context_view_id'] ) ) {
488
			// Calculate the context view id and send it to the advanced filter
489 22
			if ( GravityView_frontend::getInstance()->getSingleEntry() ) {
490 21
				$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
491 4
			} else if ( class_exists( 'GravityView_View_Data' ) && GravityView_View_Data::getInstance() && GravityView_View_Data::getInstance()->has_multiple_views() ) {
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::has_multiple_views() has been deprecated.

This method has been deprecated.

Loading history...
492 1
				$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
493 4
			} else if ( 'delete' === GFForms::get( 'action' ) ) {
494 1
				$criteria['context_view_id'] = isset( $_GET['view_id'] ) ? intval( $_GET['view_id'] ) : null;
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
495
			}
496
		}
497
498
		/**
499
		 * @filter `gravityview_search_criteria` Apply final criteria filter (Used by the Advanced Filter extension)
500
		 * @param array $criteria Search criteria used by GravityView
501
		 * @param array $form_ids Forms to search
502
		 * @param int $view_id ID of the view being used to search
503
		 */
504 34
		$criteria = apply_filters( 'gravityview_search_criteria', $criteria, $form_ids, $criteria['context_view_id'] );
505
506 34
		return (array)$criteria;
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
507
	}
508
509
510
	/**
511
	 * Retrieve entries given search, sort, paging criteria
512
	 *
513
	 * @see  GFAPI::get_entries()
514
	 * @see GFFormsModel::get_field_filters_where()
515
	 * @access public
516
	 * @param int|array $form_ids The ID of the form or an array IDs of the Forms. Zero for all forms.
517
	 * @param mixed $passed_criteria (default: null)
518
	 * @param mixed &$total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate the total count. (default: null)
519
	 * @return mixed False: Error fetching entries. Array: Multi-dimensional array of Gravity Forms entry arrays
520
	 */
521
	public static function get_entries( $form_ids = null, $passed_criteria = null, &$total = null ) {
522
523
		// Filter the criteria before query (includes Adv Filter)
524
		$criteria = self::calculate_get_entries_criteria( $passed_criteria, $form_ids );
525
526
		gravityview()->log->debug( '[gravityview_get_entries] Final Parameters', array( 'data' => $criteria ) );
527
528
		// Return value
529
		$return = null;
530
531
		/** Reduce # of database calls */
532
		add_filter( 'gform_is_encrypted_field', '__return_false' );
533
534
		if ( ! empty( $criteria['cache'] ) ) {
535
536
			$Cache = new GravityView_Cache( $form_ids, $criteria );
537
538
			if ( $entries = $Cache->get() ) {
539
540
				// Still update the total count when using cached results
541
				if ( ! is_null( $total ) ) {
542
					$total = GFAPI::count_entries( $form_ids, $criteria['search_criteria'] );
543
				}
544
545
				$return = $entries;
546
			}
547
		}
548
549
		if ( is_null( $return ) && class_exists( 'GFAPI' ) && ( is_numeric( $form_ids ) || is_array( $form_ids ) ) ) {
550
551
			/**
552
			 * @filter `gravityview_pre_get_entries` Define entries to be used before GFAPI::get_entries() is called
553
			 * @since 1.14
554
			 * @param  null $return If you want to override GFAPI::get_entries() and define entries yourself, tap in here.
555
			 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
556
			 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
557
			 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
558
			 * @deprecated
559
			 */
560
			$entries = apply_filters( 'gravityview_before_get_entries', null, $criteria, $passed_criteria, $total );
561
562
			// No entries returned from gravityview_before_get_entries
563
			if( is_null( $entries ) ) {
564
565
				$entries = GFAPI::get_entries( $form_ids, $criteria['search_criteria'], $criteria['sorting'], $criteria['paging'], $total );
566
567
				if ( is_wp_error( $entries ) ) {
568
					gravityview()->log->error( '{error}', array( 'error' => $entries->get_error_message(), 'data' => $entries ) );
569
570
					/** Remove filter added above */
571
					remove_filter( 'gform_is_encrypted_field', '__return_false' );
572
					return false;
573
				}
574
			}
575
576
			if ( ! empty( $criteria['cache'] ) && isset( $Cache ) ) {
577
578
				// Cache results
579
				$Cache->set( $entries, 'entries' );
580
581
			}
582
583
			$return = $entries;
584
		}
585
586
		/** Remove filter added above */
587
		remove_filter( 'gform_is_encrypted_field', '__return_false' );
588
589
		/**
590
		 * @filter `gravityview_entries` Modify the array of entries returned to GravityView after it has been fetched from the cache or from `GFAPI::get_entries()`.
591
		 * @param  array|null $entries Array of entries as returned by the cache or by `GFAPI::get_entries()`
592
		 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
593
		 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
594
		 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
595
		 */
596
		$return = apply_filters( 'gravityview_entries', $return, $criteria, $passed_criteria, $total );
597
598
		return $return;
599
	}
600
601
602
	/**
603
	 * Get the entry ID from a string that may be the Entry ID or the Entry Slug
604
	 *
605
	 * @since 1.18
606
	 *
607
	 * @param string $entry_id_or_slug The ID or slug of an entry.
608
	 * @param bool $force_allow_ids Whether to force allowing getting the ID of an entry, even if custom slugs are enabled
609
	 *
610
	 * @return false|int|null Returns the ID of the entry found, if custom slugs is enabled. Returns original value if custom slugs is disabled. Returns false if not allowed to convert slug to ID. Returns NULL if entry not found for the passed slug.
611
	 */
612 21
	public static function get_entry_id( $entry_id_or_slug = '', $force_allow_ids = false ) {
613
614 21
		$entry_id = false;
615
616
		/**
617
		 * @filter `gravityview_custom_entry_slug` Whether to enable and use custom entry slugs.
618
		 * @param boolean True: Allow for slugs based on entry values. False: always use entry IDs (default)
619
		 */
620 21
		$custom_slug = apply_filters( 'gravityview_custom_entry_slug', false );
621
622
		/**
623
		 * @filter `gravityview_custom_entry_slug_allow_id` When using a custom slug, allow access to the entry using the original slug (the Entry ID).
624
		 * - If disabled (default), only allow access to an entry using the custom slug value.  (example: `/entry/custom-slug/` NOT `/entry/123/`)
625
		 * - If enabled, you could access using the custom slug OR the entry id (example: `/entry/custom-slug/` OR `/entry/123/`)
626
		 * @param boolean $custom_slug_id_access True: allow accessing the slug by ID; False: only use the slug passed to the method.
627
		 */
628 21
		$custom_slug_id_access = $force_allow_ids || apply_filters( 'gravityview_custom_entry_slug_allow_id', false );
629
630
		/**
631
		 * If we're using custom entry slugs, we do a meta value search
632
		 * instead of doing a straightup ID search.
633
		 */
634 21
		if ( $custom_slug ) {
635
			// Search for IDs matching $entry_id_or_slug
636 2
			$entry_id = self::get_entry_id_from_slug( $entry_id_or_slug );
637
		}
638
639
		// If custom slug is off, search using the entry ID
640
		// ID allow ID access is on, also use entry ID as a backup
641 21
		if ( false === $custom_slug || true === $custom_slug_id_access ) {
642
			// Search for IDs matching $entry_slug
643 21
			$entry_id = $entry_id_or_slug;
644
		}
645
646 21
		return $entry_id;
647
	}
648
649
	/**
650
	 * Return a single entry object
651
	 *
652
	 * Since 1.4, supports custom entry slugs. The way that GravityView fetches an entry based on the custom slug is by searching `gravityview_unique_id` meta. The `$entry_slug` is fetched by getting the current query var set by `is_single_entry()`
653
	 *
654
	 * @access public
655
	 * @param string|int $entry_slug Either entry ID or entry slug string
656
	 * @param boolean $force_allow_ids Force the get_entry() method to allow passed entry IDs, even if the `gravityview_custom_entry_slug_allow_id` filter returns false.
657
	 * @param boolean $check_entry_display Check whether the entry is visible for the current View configuration. Default: true. {@since 1.14}
658
	 * @return array|boolean
659
	 */
660 27
	public static function get_entry( $entry_slug, $force_allow_ids = false, $check_entry_display = true ) {
661
662 27
		if ( ! class_exists( 'GFAPI' ) || empty( $entry_slug ) ) {
663 20
			return false;
664
		}
665
666 21
		$entry_id = self::get_entry_id( $entry_slug, $force_allow_ids );
667
668 21
		if ( empty( $entry_id ) ) {
669 2
			return false;
670
		}
671
672
		// fetch the entry
673 21
		$entry = GFAPI::get_entry( $entry_id );
674
675
		/**
676
		 * @filter `gravityview/common/get_entry/check_entry_display` Override whether to check entry display rules against filters
677
		 * @since 1.16.2
678
		 * @param bool $check_entry_display Check whether the entry is visible for the current View configuration. Default: true.
679
		 * @param array $entry Gravity Forms entry array
680
		 */
681 21
		$check_entry_display = apply_filters( 'gravityview/common/get_entry/check_entry_display', $check_entry_display, $entry );
682
683 21
		if( $check_entry_display ) {
684
			// Is the entry allowed
685 21
			$entry = self::check_entry_display( $entry );
686
		}
687
688 21
		if( is_wp_error( $entry ) ) {
689
			gravityview()->log->error( '{error}', array( 'error' => $entry->get_error_message() ) );
690
			return false;
691
		}
692
693 21
		return $entry;
694
	}
695
696
	/**
697
	 * Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including:
698
	 * 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals',
699
	 * 'not_contains', 'in', and 'not_in'
700
	 *
701
	 * @since 1.13 You can define context, which displays/hides based on what's being displayed (single, multiple, edit)
702
	 * @since 1.22.1 Added 'in' and 'not_in' for JSON-encoded array values, serialized non-strings
703
	 *
704
	 * @see https://docs.gravityview.co/article/252-gvlogic-shortcode
705
	 * @uses GFFormsModel::matches_operation
706
	 * @since 1.7.5
707
	 *
708
	 * @param string $val1 Left side of comparison
709
	 * @param string $val2 Right side of comparison
710
	 * @param string $operation Type of comparison
711
	 *
712
	 * @return bool True: matches, false: not matches
713
	 */
714 4
	public static function matches_operation( $val1, $val2, $operation ) {
715
716 4
		$json_function = function_exists('wp_json_encode') ? 'wp_json_encode' : 'json_encode';
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
717
718
		// Only process strings
719 4
		$val1 = ! is_string( $val1 ) ? $json_function( $val1 ) : $val1;
720 4
		$val2 = ! is_string( $val2 ) ? $json_function( $val2 ) : $val2;
721
722 4
		$value = false;
723
724 4
		if( 'context' === $val1 ) {
725
726
			$matching_contexts = array( $val2 );
727
728
			// We allow for non-standard contexts.
729
			switch( $val2 ) {
730
				// Check for either single or edit
731
				case 'singular':
732
					$matching_contexts = array( 'single', 'edit' );
733
					break;
734
				// Use multiple as alias for directory for consistency
735
				case 'multiple':
736
					$matching_contexts = array( 'directory' );
737
					break;
738
			}
739
740
			$val1 = in_array( gravityview_get_context(), $matching_contexts ) ? $val2 : false;
0 ignored issues
show
Deprecated Code introduced by
The function gravityview_get_context() has been deprecated with message: since 2.0.6.2 Use `gravityview()->request`

This function has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
741
		}
742
743 4
		switch ( $operation ) {
744 4
			case 'equals':
745 2
				$value = self::matches_operation( $val1, $val2, 'is' );
746 2
				break;
747 4
			case 'greater_than_or_is':
748 4
			case 'greater_than_or_equals':
749 2
				$is    = self::matches_operation( $val1, $val2, 'is' );
750 2
				$gt    = self::matches_operation( $val1, $val2, 'greater_than' );
751 2
				$value = ( $is || $gt );
752 2
				break;
753 4
			case 'less_than_or_is':
754 4
			case 'less_than_or_equals':
755 1
				$is    = self::matches_operation( $val1, $val2, 'is' );
756 1
				$gt    = self::matches_operation( $val1, $val2, 'less_than' );
757 1
				$value = ( $is || $gt );
758 1
				break;
759 4
			case 'not_contains':
760 1
				$contains = self::matches_operation( $val1, $val2, 'contains' );
761 1
				$value    = ! $contains;
762 1
				break;
763
			/**
764
			 * @since 1.22.1 Handle JSON-encoded comparisons
765
			 */
766 4
			case 'in':
767 4
			case 'not_in':
768
769 1
				$json_val_1 = json_decode( $val1, true );
770 1
				$json_val_2 = json_decode( $val2, true );
771
772 1
				if( ! empty( $json_val_1 ) || ! empty( $json_val_2 ) ) {
773
774 1
					$json_in = false;
775 1
					$json_val_1 = $json_val_1 ? $json_val_1 : array( $val1 );
776 1
					$json_val_2 = $json_val_2 ? $json_val_2 : array( $val2 );
777
778
					// For JSON, we want to compare as "in" or "not in" rather than "contains"
779 1
					foreach ( $json_val_1 as $item_1 ) {
780 1
						foreach ( $json_val_2 as $item_2 ) {
781 1
							$json_in = self::matches_operation( $item_1, $item_2, 'is' );
782
783 1
							if( $json_in ) {
784 1
								break 2;
785
							}
786
						}
787
					}
788
789 1
					$value = ( $operation === 'in' ) ? $json_in : ! $json_in;
790
				}
791 1
				break;
792
793 4
			case 'less_than':
794 4
			case '<' :
795 1
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
796 1
					$value = $val1 < $val2;
797
				} else {
798
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
799
				}
800 1
				break;
801 4
			case 'greater_than':
802 4
			case '>' :
803 2
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
804 2
					$value = $val1 > $val2;
805
				} else {
806
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
807
				}
808 2
				break;
809
			default:
810 4
				$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
811
		}
812
813 4
		return $value;
814
	}
815
816
	/**
817
	 *
818
	 * Checks if a certain entry is valid according to the View search filters (specially the Adv Filters)
819
	 *
820
	 * @uses GVCommon::calculate_get_entries_criteria();
821
	 * @see GFFormsModel::is_value_match()
822
	 *
823
	 * @since 1.7.4
824
	 *
825
	 * @param array $entry Gravity Forms Entry object
826
	 * @return WP_Error|array Returns WP_Error if entry is not valid according to the view search filters (Adv Filter). Returns original $entry value if passes.
827
	 */
828 22
	public static function check_entry_display( $entry ) {
829
830 22
		if ( ! $entry || is_wp_error( $entry ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $entry of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
831 1
			return new WP_Error('entry_not_found', 'Entry was not found.', $entry );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
832
		}
833
834 22
		if ( empty( $entry['form_id'] ) ) {
835 1
			return new WP_Error( 'form_id_not_set', '[apply_filters_to_entry] Entry is empty!', $entry );
836
		}
837
838 22
		$criteria = self::calculate_get_entries_criteria();
839
840 22
		if ( empty( $criteria['search_criteria'] ) || ! is_array( $criteria['search_criteria'] ) ) {
841 22
			gravityview()->log->debug( '[apply_filters_to_entry] Entry approved! No search criteria found:', array( 'data' => $criteria ) );
842 22
			return $entry;
843
		}
844
845
		// Make sure the current View is connected to the same form as the Entry
846 2
		if( ! empty( $criteria['context_view_id'] ) ) {
847 2
			$context_view_id = intval( $criteria['context_view_id'] );
848 2
			$context_form_id = gravityview_get_form_id( $context_view_id );
849 2
			if( intval( $context_form_id ) !== intval( $entry['form_id'] ) ) {
850 1
				return new WP_Error( 'view_id_not_match', sprintf( '[apply_filters_to_entry] Entry form ID does not match current View connected form ID:', $entry['form_id'] ), $criteria['context_view_id'] );
851
			}
852
		}
853
854 2
		$search_criteria = $criteria['search_criteria'];
855
856
		// check entry status
857 2
		if ( array_key_exists( 'status', $search_criteria ) && $search_criteria['status'] != $entry['status'] ) {
858 1
			return new WP_Error( 'status_not_valid', sprintf( '[apply_filters_to_entry] Entry status - %s - is not valid according to filter:', $entry['status'] ), $search_criteria );
859
		}
860
861
		// check entry date
862
		// @todo: Does it make sense to apply the Date create filters to the single entry?
863
864
		// field_filters
865 2
		if ( empty( $search_criteria['field_filters'] ) || ! is_array( $search_criteria['field_filters'] ) ) {
866 1
			gravityview()->log->debug( '[apply_filters_to_entry] Entry approved! No field filters criteria found:', array( 'data' => $search_criteria ) );
867 1
			return $entry;
868
		}
869
870 2
		$filters = $search_criteria['field_filters'];
871
872 2
		$mode = array_key_exists( 'mode', $filters ) ? strtolower( $filters['mode'] ) : 'all';
873
874
		// Prevent the mode from being processed below
875 2
		unset( $filters['mode'] );
876
877 2
		$form = self::get_form( $entry['form_id'] );
878
879 2
		foreach ( $filters as $filter ) {
880
881 2
			if ( ! isset( $filter['key'] ) ) {
882
				gravityview()->log->debug( '[apply_filters_to_entry] Filter key not set: {filter}', array( 'filter' => $filter ) );
883
				continue;
884
			}
885
886 2
			$k = $filter['key'];
887
888 2
			$field = self::get_field( $form, $k );
889
890 2
			if ( is_null( $field ) ) {
891 2
				$field_value = isset( $entry[ $k ] ) ? $entry[ $k ] : null;
892 2
				$field = $k;
893
			} else {
894
				$field_value  = GFFormsModel::get_lead_field_value( $entry, $field );
895
				 // If it's a complex field, then fetch the input's value, if exists at the current key. Otherwise, let GF handle it
896
				$field_value = ( is_array( $field_value ) && isset( $field_value[ $k ] ) ) ? \GV\Utils::get( $field_value, $k ) : $field_value;
897
			}
898
899 2
			$operator = isset( $filter['operator'] ) ? strtolower( $filter['operator'] ) : 'is';
900
901 2
			$is_value_match = GravityView_GFFormsModel::is_value_match( $field_value, $filter['value'], $operator, $field );
902
903
			// Any match is all we need to know
904 2
			if ( $is_value_match && 'any' === $mode ) {
905 1
				return $entry;
906
			}
907
908
			// Any failed match is a total fail
909 2
			if ( ! $is_value_match && 'all' === $mode ) {
910 2
				return new WP_Error('failed_criteria', '[apply_filters_to_entry] Entry cannot be displayed. Failed a criterium for ALL mode', $filter );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
911
			}
912
		}
913
914
		// at this point, if in ALL mode, then entry is approved - all conditions were met.
915
		// Or, for ANY mode, means none of the conditions were satisfied, so entry is not approved
916 2
		if ( 'all' === $mode ) {
917 2
			gravityview()->log->debug( '[apply_filters_to_entry] Entry approved: all conditions were met' );
918 2
			return $entry;
919
		} else {
920
			return new WP_Error('failed_any_criteria', '[apply_filters_to_entry] Entry cannot be displayed. Failed all the criteria for ANY mode', $filters );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
921
		}
922
923
	}
924
925
926
	/**
927
	 * Allow formatting date and time based on GravityView standards
928
	 *
929
	 * @since 1.16
930
	 *
931
	 * @see GVCommon_Test::test_format_date for examples
932
	 *
933
	 * @param string $date_string The date as stored by Gravity Forms (`Y-m-d h:i:s` GMT)
934
	 * @param string|array $args Array or string of settings for output parsed by `wp_parse_args()`; Can use `raw=1` or `array('raw' => true)` \n
935
	 * - `raw` Un-formatted date string in original `Y-m-d h:i:s` format
936
	 * - `timestamp` Integer timestamp returned by GFCommon::get_local_timestamp()
937
	 * - `diff` "%s ago" format, unless other `format` is defined
938
	 * - `human` Set $is_human parameter to true for `GFCommon::format_date()`. Shows `diff` within 24 hours or date after. Format based on blog setting, unless `format` is defined.
939
	 * - `time` Include time in the `GFCommon::format_date()` output
940
	 * - `format` Define your own date format, or `diff` format
941
	 *
942
	 * @return int|null|string Formatted date based on the original date
943
	 */
944 3
	public static function format_date( $date_string = '', $args = array() ) {
945
946
		$default_atts = array(
947 3
			'raw' => false,
948
			'timestamp' => false,
949
			'diff' => false,
950
			'human' => false,
951
			'format' => '',
952
			'time' => false,
953
		);
954
955 3
		$atts = wp_parse_args( $args, $default_atts );
956
957
		/**
958
		 * Gravity Forms code to adjust date to locally-configured Time Zone
959
		 * @see GFCommon::format_date() for original code
960
		 */
961 3
		$date_gmt_time   = mysql2date( 'G', $date_string );
962 3
		$date_local_timestamp = GFCommon::get_local_timestamp( $date_gmt_time );
963
964 3
		$format  = \GV\Utils::get( $atts, 'format' );
965 3
		$is_human  = ! empty( $atts['human'] );
966 3
		$is_diff  = ! empty( $atts['diff'] );
967 3
		$is_raw = ! empty( $atts['raw'] );
968 3
		$is_timestamp = ! empty( $atts['timestamp'] );
969 3
		$include_time = ! empty( $atts['time'] );
970
971
		// If we're using time diff, we want to have a different default format
972 3
		if( empty( $format ) ) {
973
			/* translators: %s: relative time from now, used for generic date comparisons. "1 day ago", or "20 seconds ago" */
974 3
			$format = $is_diff ? esc_html__( '%s ago', 'gravityview' ) : get_option( 'date_format' );
975
		}
976
977
		// If raw was specified, don't modify the stored value
978 3
		if ( $is_raw ) {
979 1
			$formatted_date = $date_string;
980 3
		} elseif( $is_timestamp ) {
981 1
			$formatted_date = $date_local_timestamp;
982 3
		} elseif ( $is_diff ) {
983 1
			$formatted_date = sprintf( $format, human_time_diff( $date_gmt_time ) );
984
		} else {
985 3
			$formatted_date = GFCommon::format_date( $date_string, $is_human, $format, $include_time );
986
		}
987
988 3
		unset( $format, $is_diff, $is_human, $is_timestamp, $is_raw, $date_gmt_time, $date_local_timestamp, $default_atts );
989
990 3
		return $formatted_date;
991
	}
992
993
	/**
994
	 * Retrieve the label of a given field id (for a specific form)
995
	 *
996
	 * @access public
997
	 * @since 1.17 Added $field_value parameter
998
	 *
999
	 * @param array $form Gravity Forms form array
1000
	 * @param string $field_id ID of the field. If an input, full input ID (like `1.3`)
1001
	 * @param string|array $field_value Raw value of the field.
1002
	 * @return string
1003
	 */
1004 1
	public static function get_field_label( $form = array(), $field_id = '', $field_value = '' ) {
1005
1006 1
		if ( empty( $form ) || empty( $field_id ) ) {
1007
			return '';
1008
		}
1009
1010 1
		$field = self::get_field( $form, $field_id );
1011
1012 1
		$label = \GV\Utils::get( $field, 'label' );
1013
1014 1
		if( floor( $field_id ) !== floatval( $field_id ) ) {
1015 1
			$label = GFFormsModel::get_choice_text( $field, $field_value, $field_id );
1016
		}
1017
1018 1
		return $label;
1019
	}
1020
1021
1022
	/**
1023
	 * Returns the field details array of a specific form given the field id
1024
	 *
1025
	 * Alias of GFFormsModel::get_field
1026
	 *
1027
	 * @since 1.19 Allow passing form ID as well as form array
1028
	 *
1029
	 * @uses GFFormsModel::get_field
1030
	 * @see GFFormsModel::get_field
1031
	 * @access public
1032
	 * @param array|int $form Form array or ID
1033
	 * @param string|int $field_id
1034
	 * @return GF_Field|null Gravity Forms field object, or NULL: Gravity Forms GFFormsModel does not exist or field at $field_id doesn't exist.
1035
	 */
1036 4
	public static function get_field( $form, $field_id ) {
1037
1038 4
		if ( is_numeric( $form ) ) {
1039
			$form = GFAPI::get_form( $form );
1040
		}
1041
1042 4
		if ( class_exists( 'GFFormsModel' ) ){
1043 4
			return GFFormsModel::get_field( $form, $field_id );
1044
		} else {
1045
			return null;
1046
		}
1047
	}
1048
1049
1050
	/**
1051
	 * Check whether the post is GravityView
1052
	 *
1053
	 * - Check post type. Is it `gravityview`?
1054
	 * - Check shortcode
1055
	 *
1056
	 * @param  WP_Post      $post WordPress post object
1057
	 * @return boolean           True: yep, GravityView; No: not!
1058
	 */
1059 2
	public static function has_gravityview_shortcode( $post = null ) {
1060 2
		if ( ! is_a( $post, 'WP_Post' ) ) {
1061 1
			return false;
1062
		}
1063
1064 2
		if ( 'gravityview' === get_post_type( $post ) ) {
1065 2
			return true;
1066
		}
1067
1068 2
		return self::has_shortcode_r( $post->post_content, 'gravityview' ) ? true : false;
1069
1070
	}
1071
1072
1073
	/**
1074
	 * Placeholder until the recursive has_shortcode() patch is merged
1075
	 * @see https://core.trac.wordpress.org/ticket/26343#comment:10
1076
	 * @param string $content Content to check whether there's a shortcode
1077
	 * @param string $tag Current shortcode tag
1078
	 */
1079 2
	public static function has_shortcode_r( $content, $tag = 'gravityview' ) {
1080 2
		if ( false === strpos( $content, '[' ) ) {
1081 1
			return false;
1082
		}
1083
1084 2
		if ( shortcode_exists( $tag ) ) {
1085
1086 2
			$shortcodes = array();
1087
1088 2
			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
1089 2
			if ( empty( $matches ) ){
1090 1
				return false;
1091
			}
1092
1093 2
			foreach ( $matches as $shortcode ) {
1094 2
				if ( $tag === $shortcode[2] ) {
1095
1096
					// Changed this to $shortcode instead of true so we get the parsed atts.
1097 2
					$shortcodes[] = $shortcode;
1098
1099 1
				} else if ( isset( $shortcode[5] ) && $results = self::has_shortcode_r( $shortcode[5], $tag ) ) {
1100 1
					foreach( $results as $result ) {
1101 2
						$shortcodes[] = $result;
1102
					}
1103
				}
1104
			}
1105
1106 2
			return $shortcodes;
1107
		}
1108
		return false;
1109
	}
1110
1111
1112
1113
	/**
1114
	 * Get the views for a particular form
1115
	 *
1116
	 * @since 1.15.2 Add $args array and limit posts_per_page to 500
1117
	 *
1118
	 * @uses get_posts()
1119
	 *
1120
	 * @param  int $form_id Gravity Forms form ID
1121
	 * @param  array $args Pass args sent to get_posts()
1122
	 *
1123
	 * @return array          Array with view details, as returned by get_posts()
1124
	 */
1125 1
	public static function get_connected_views( $form_id, $args = array() ) {
1126
1127 1
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1128
1129
		$defaults = array(
1130 1
			'post_type'      => 'gravityview',
1131 1
			'posts_per_page' => 100,
0 ignored issues
show
introduced by
Detected high pagination limit, posts_per_page is set to 100
Loading history...
1132 1
			'meta_key'       => '_gravityview_form_id',
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
1133 1
			'meta_value'     => (int) $form_id,
0 ignored issues
show
introduced by
Detected usage of meta_value, possible slow query.
Loading history...
1134
		);
1135 1
		$args     = wp_parse_args( $args, $defaults );
1136 1
		$views    = get_posts( $args );
1137
1138 1
		$views_with_joins = $wpdb->get_results( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_gravityview_form_joins'" );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
1139
1140 1
		$joined_forms = array();
1141 1
		foreach ( $views_with_joins as $view ) {
1142 1
			$data = unserialize( $view->meta_value );
1143 1
			if ( ! empty( $data[0][2] ) && (int) $data[0][2] === (int) $form_id ) {
1144 1
				$joined_forms[] = $view->post_id;
1145
			}
1146
		}
1147
1148 1
		if ( $joined_forms ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joined_forms of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1149
			$args  = array(
1150
				'post_type'      => 'gravityview',
1151
				'posts_per_page' => 100,
0 ignored issues
show
introduced by
Detected high pagination limit, posts_per_page is set to 100
Loading history...
1152
				'post__in'       => $joined_forms,
1153
			);
1154
			$views = array_merge( $views, get_posts( $args ) );
1155
		}
1156
1157 1
		return $views;
1158
	}
1159
1160
	/**
1161
	 * Get the Gravity Forms form ID connected to a View
1162
	 *
1163
	 * @param int $view_id The ID of the View to get the connected form of
1164
	 *
1165
	 * @return false|string ID of the connected Form, if exists. Empty string if not. False if not the View ID isn't valid.
1166
	 */
1167 2
	public static function get_meta_form_id( $view_id ) {
1168 2
		return get_post_meta( $view_id, '_gravityview_form_id', true );
1169
	}
1170
1171
	/**
1172
	 * Get the template ID (`list`, `table`, `datatables`, `map`) for a View
1173
	 *
1174
	 * @see GravityView_Template::template_id
1175
	 *
1176
	 * @param int $view_id The ID of the View to get the layout of
1177
	 *
1178
	 * @return string GravityView_Template::template_id value. Empty string if not.
1179
	 */
1180 72
	public static function get_meta_template_id( $view_id ) {
1181 72
		return get_post_meta( $view_id, '_gravityview_directory_template', true );
1182
	}
1183
1184
1185
	/**
1186
	 * Get all the settings for a View
1187
	 *
1188
	 * @uses  \GV\View_Settings::defaults() Parses the settings with the plugin defaults as backups.
1189
	 * @param  int $post_id View ID
1190
	 * @return array          Associative array of settings with plugin defaults used if not set by the View
1191
	 */
1192 71
	public static function get_template_settings( $post_id ) {
1193
1194 71
		$settings = get_post_meta( $post_id, '_gravityview_template_settings', true );
1195
1196 71
		if ( class_exists( '\GV\View_Settings' ) ) {
1197
1198 71
			return wp_parse_args( (array)$settings, \GV\View_Settings::defaults() );
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
1199
1200
		}
1201
1202
		// Backup, in case GravityView_View_Data isn't loaded yet.
1203
		return $settings;
1204
	}
1205
1206
	/**
1207
	 * Get the setting for a View
1208
	 *
1209
	 * If the setting isn't set by the View, it returns the plugin default.
1210
	 *
1211
	 * @param  int $post_id View ID
1212
	 * @param  string $key     Key for the setting
1213
	 * @return mixed|null          Setting value, or NULL if not set.
1214
	 */
1215 1
	public static function get_template_setting( $post_id, $key ) {
1216
1217 1
		$settings = self::get_template_settings( $post_id );
1218
1219 1
		if ( isset( $settings[ $key ] ) ) {
1220 1
			return $settings[ $key ];
1221
		}
1222
1223
		return null;
1224
	}
1225
1226
	/**
1227
	 * Get the field configuration for the View
1228
	 *
1229
	 * array(
1230
	 *
1231
	 * 	[other zones]
1232
	 *
1233
	 * 	'directory_list-title' => array(
1234
	 *
1235
	 *   	[other fields]
1236
	 *
1237
	 *  	'5372653f25d44' => array(
1238
	 *  		'id' => string '9' (length=1)
1239
	 *  		'label' => string 'Screenshots' (length=11)
1240
	 *			'show_label' => string '1' (length=1)
1241
	 *			'custom_label' => string '' (length=0)
1242
	 *			'custom_class' => string 'gv-gallery' (length=10)
1243
	 * 			'only_loggedin' => string '0' (length=1)
1244
	 *			'only_loggedin_cap' => string 'read' (length=4)
1245
	 *  	)
1246
	 *
1247
	 * 		[other fields]
1248
	 *  )
1249
	 *
1250
	 * 	[other zones]
1251
	 * )
1252
	 *
1253
	 * @since 1.17.4 Added $apply_filter parameter
1254
	 *
1255
	 * @param  int $post_id View ID
1256
	 * @param  bool $apply_filter Whether to apply the `gravityview/configuration/fields` filter [Default: true]
1257
	 * @return array          Multi-array of fields with first level being the field zones. See code comment.
1258
	 */
1259 1
	public static function get_directory_fields( $post_id, $apply_filter = true ) {
1260 1
		$fields = get_post_meta( $post_id, '_gravityview_directory_fields', true );
1261
1262 1
		if ( $apply_filter ) {
1263
			/**
1264
			 * @filter `gravityview/configuration/fields` Filter the View fields' configuration array
1265
			 * @since 1.6.5
1266
			 *
1267
			 * @param $fields array Multi-array of fields with first level being the field zones
1268
			 * @param $post_id int Post ID
1269
			 */
1270 1
			$fields = apply_filters( 'gravityview/configuration/fields', $fields, $post_id );
1271
1272
			/**
1273
			 * @filter `gravityview/view/configuration/fields` Filter the View fields' configuration array.
1274
			 * @since 2.0
1275
			 *
1276
			 * @param array $fields Multi-array of fields with first level being the field zones.
1277
			 * @param \GV\View $view The View the fields are being pulled for.
1278
			 */
1279 1
			$fields = apply_filters( 'gravityview/view/configuration/fields', $fields, \GV\View::by_id( $post_id ) );
1280
		}
1281
1282 1
		return $fields;
1283
	}
1284
1285
	/**
1286
	 * Get the widget configuration for a View
1287
	 *
1288
	 * @param int $view_id View ID
1289
	 * @param bool $json_decode Whether to JSON-decode the widget values. Default: `false`
1290
	 *
1291
	 * @return array Multi-array of widgets, with the slug of each widget "zone" being the key ("header_top"), and each widget having their own "id"
1292
	 */
1293
	public static function get_directory_widgets( $view_id, $json_decode = false ) {
1294
1295
		$view_widgets = get_post_meta( $view_id, '_gravityview_directory_widgets', true );
1296
1297
		$defaults = array(
1298
			'header_top' => array(),
1299
			'header_left' => array(),
1300
			'header_right' => array(),
1301
			'footer_left' => array(),
1302
			'footer_right' => array(),
1303
		);
1304
1305
		$directory_widgets = wp_parse_args( $view_widgets, $defaults );
1306
1307
		if( $json_decode ) {
1308
			$directory_widgets = gv_map_deep( $directory_widgets, 'gv_maybe_json_decode' );
1309
		}
1310
1311
		return $directory_widgets;
1312
	}
1313
1314
1315
	/**
1316
	 * Render dropdown (select) with the list of sortable fields from a form ID
1317
	 *
1318
	 * @access public
1319
	 * @param  int $formid Form ID
1320
	 * @return string         html
1321
	 */
1322
	public static function get_sortable_fields( $formid, $current = '' ) {
1323
		$output = '<option value="" ' . selected( '', $current, false ).'>' . esc_html__( 'Default', 'gravityview' ) .'</option>';
1324
1325
		if ( empty( $formid ) ) {
1326
			return $output;
1327
		}
1328
1329
		$fields = self::get_sortable_fields_array( $formid );
1330
1331
		if ( ! empty( $fields ) ) {
1332
1333
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'list', 'textarea' ), null );
1334
1335
			foreach ( $fields as $id => $field ) {
1336
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
1337
					continue;
1338
				}
1339
1340
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'>'. esc_attr( $field['label'] ) .'</option>';
1341
			}
1342
		}
1343
1344
		return $output;
1345
	}
1346
1347
	/**
1348
	 *
1349
	 * @param int $formid Gravity Forms form ID
1350
	 * @param array $blacklist Field types to exclude
1351
	 *
1352
	 * @since 1.8
1353
	 *
1354
	 * @todo Get all fields, check if sortable dynamically
1355
	 *
1356
	 * @return array
1357
	 */
1358
	public static function get_sortable_fields_array( $formid, $blacklist = array( 'list', 'textarea' ) ) {
1359
1360
		// Get fields with sub-inputs and no parent
1361
		$fields = self::get_form_fields( $formid, true, false );
1362
1363
		$date_created = array(
1364
			'date_created' => array(
1365
				'type' => 'date_created',
1366
				'label' => __( 'Date Created', 'gravityview' ),
1367
			),
1368
		);
1369
1370
        $fields = $date_created + $fields;
1371
1372
		$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', $blacklist, NULL );
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
1373
1374
		// TODO: Convert to using array_filter
1375
		foreach( $fields as $id => $field ) {
1376
1377
			if( in_array( $field['type'], $blacklist_field_types ) ) {
1378
				unset( $fields[ $id ] );
1379
			}
1380
		}
1381
1382
        /**
1383
         * @filter `gravityview/common/sortable_fields` Filter the sortable fields
1384
         * @since 1.12
1385
         * @param array $fields Sub-set of GF form fields that are sortable
1386
         * @param int $formid The Gravity Forms form ID that the fields are from
1387
         */
1388
        $fields = apply_filters( 'gravityview/common/sortable_fields', $fields, $formid );
1389
1390
		return $fields;
1391
	}
1392
1393
	/**
1394
	 * Returns the GF Form field type for a certain field(id) of a form
1395
	 * @param  object $form     Gravity Forms form
1396
	 * @param  mixed $field_id Field ID or Field array
1397
	 * @return string field type
1398
	 */
1399
	public static function get_field_type( $form = null, $field_id = '' ) {
1400
1401
		if ( ! empty( $field_id ) && ! is_array( $field_id ) ) {
1402
			$field = self::get_field( $form, $field_id );
1403
		} else {
1404
			$field = $field_id;
1405
		}
1406
1407
		return class_exists( 'RGFormsModel' ) ? RGFormsModel::get_input_type( $field ) : '';
1408
1409
	}
1410
1411
1412
	/**
1413
	 * Checks if the field type is a 'numeric' field type (e.g. to be used when sorting)
1414
	 * @param  int|array  $form  form ID or form array
1415
	 * @param  int|array  $field field key or field array
1416
	 * @return boolean
1417
	 */
1418
	public static function is_field_numeric(  $form = null, $field = '' ) {
1419
1420
		if ( ! is_array( $form ) && ! is_array( $field ) ) {
1421
			$form = self::get_form( $form );
1422
		}
1423
1424
		// If entry meta, it's a string. Otherwise, numeric
1425
		if( ! is_numeric( $field ) && is_string( $field ) ) {
1426
			$type = $field;
1427
		} else {
1428
			$type = self::get_field_type( $form, $field );
1429
		}
1430
1431
		/**
1432
		 * @filter `gravityview/common/numeric_types` What types of fields are numeric?
1433
		 * @since 1.5.2
1434
		 * @param array $numeric_types Fields that are numeric. Default: `[ number, time ]`
1435
		 */
1436
		$numeric_types = apply_filters( 'gravityview/common/numeric_types', array( 'number', 'time' ) );
1437
1438
		// Defer to GravityView_Field setting, if the field type is registered and `is_numeric` is true
1439
		if( $gv_field = GravityView_Fields::get( $type ) ) {
1440
			if( true === $gv_field->is_numeric ) {
1441
				$numeric_types[] = $gv_field->is_numeric;
1442
			}
1443
		}
1444
1445
		$return = in_array( $type, $numeric_types );
1446
1447
		return $return;
1448
	}
1449
1450
	/**
1451
	 * Encrypt content using Javascript so that it's hidden when JS is disabled.
1452
	 *
1453
	 * This is mostly used to hide email addresses from scraper bots.
1454
	 *
1455
	 * @param string $content Content to encrypt
1456
	 * @param string $message Message shown if Javascript is disabled
1457
	 *
1458
	 * @see  https://github.com/katzwebservices/standalone-phpenkoder StandalonePHPEnkoder on Github
1459
	 *
1460
	 * @since 1.7
1461
	 *
1462
	 * @return string Content, encrypted
1463
	 */
1464
	public static function js_encrypt( $content, $message = '' ) {
1465
1466
		$output = $content;
1467
1468
		if ( ! class_exists( 'StandalonePHPEnkoder' ) ) {
1469
			include_once( GRAVITYVIEW_DIR . 'includes/lib/StandalonePHPEnkoder.php' );
1470
		}
1471
1472
		if ( class_exists( 'StandalonePHPEnkoder' ) ) {
1473
1474
			$enkoder = new StandalonePHPEnkoder;
1475
1476
			$message = empty( $message ) ? __( 'Email hidden; Javascript is required.', 'gravityview' ) : $message;
1477
1478
			/**
1479
			 * @filter `gravityview/phpenkoder/msg` Modify the message shown when Javascript is disabled and an encrypted email field is displayed
1480
			 * @since 1.7
1481
			 * @param string $message Existing message
1482
			 * @param string $content Content to encrypt
1483
			 */
1484
			$enkoder->enkode_msg = apply_filters( 'gravityview/phpenkoder/msg', $message, $content );
1485
1486
			$output = $enkoder->enkode( $content );
1487
		}
1488
1489
		return $output;
1490
	}
1491
1492
	/**
1493
	 *
1494
	 * Do the same than parse_str without max_input_vars limitation:
1495
	 * Parses $string as if it were the query string passed via a URL and sets variables in the current scope.
1496
	 * @param $string array string to parse (not altered like in the original parse_str(), use the second parameter!)
1497
	 * @param $result array  If the second parameter is present, variables are stored in this variable as array elements
1498
	 * @return bool true or false if $string is an empty string
1499
	 * @since  1.5.3
1500
	 *
1501
	 * @author rubo77 at https://gist.github.com/rubo77/6821632
1502
	 **/
1503
	public static function gv_parse_str( $string, &$result ) {
1504
		if ( empty( $string ) ) {
1505
			return false;
1506
		}
1507
1508
		$result = array();
1509
1510
		// find the pairs "name=value"
1511
		$pairs = explode( '&', $string );
1512
1513
		foreach ( $pairs as $pair ) {
1514
			// use the original parse_str() on each element
1515
			parse_str( $pair, $params );
1516
1517
			$k = key( $params );
1518
			if ( ! isset( $result[ $k ] ) ) {
1519
				$result += $params;
1520
			} elseif ( array_key_exists( $k, $params ) && is_array( $params[ $k ] ) ) {
1521
				$result[ $k ] = self::array_merge_recursive_distinct( $result[ $k ], $params[ $k ] );
1522
			}
1523
		}
1524
		return true;
1525
	}
1526
1527
1528
	/**
1529
	 * Generate an HTML anchor tag with a list of supported attributes
1530
	 *
1531
	 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a Supported attributes defined here
1532
	 * @uses esc_url_raw() to sanitize $href
1533
	 * @uses esc_attr() to sanitize $atts
1534
	 *
1535
	 * @since 1.6
1536
	 *
1537
	 * @param string $href URL of the link. Sanitized using `esc_url_raw()`
1538
	 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1539
	 * @param array|string $atts Attributes to be added to the anchor tag. Parsed by `wp_parse_args()`, sanitized using `esc_attr()`
1540
	 *
1541
	 * @return string HTML output of anchor link. If empty $href, returns NULL
1542
	 */
1543 25
	public static function get_link_html( $href = '', $anchor_text = '', $atts = array() ) {
1544
1545
		// Supported attributes for anchor tags. HREF left out intentionally.
1546
		$allowed_atts = array(
1547 25
			'href' => null, // Will override the $href argument if set
1548
			'title' => null,
1549
			'rel' => null,
1550
			'id' => null,
1551
			'class' => null,
1552
			'target' => null,
1553
			'style' => null,
1554
1555
			// Used by GravityView
1556
			'data-viewid' => null,
1557
1558
			// Not standard
1559
			'hreflang' => null,
1560
			'type' => null,
1561
			'tabindex' => null,
1562
1563
			// Deprecated HTML4 but still used
1564
			'name' => null,
1565
			'onclick' => null,
1566
			'onchange' => null,
1567
			'onkeyup' => null,
1568
1569
			// HTML5 only
1570
			'download' => null,
1571
			'media' => null,
1572
			'ping' => null,
1573
		);
1574
1575
		/**
1576
		 * @filter `gravityview/get_link/allowed_atts` Modify the attributes that are allowed to be used in generating links
1577
		 * @param array $allowed_atts Array of attributes allowed
1578
		 */
1579 25
		$allowed_atts = apply_filters( 'gravityview/get_link/allowed_atts', $allowed_atts );
1580
1581
		// Make sure the attributes are formatted as array
1582 25
		$passed_atts = wp_parse_args( $atts );
1583
1584
		// Make sure the allowed attributes are only the ones in the $allowed_atts list
1585 25
		$final_atts = shortcode_atts( $allowed_atts, $passed_atts );
1586
1587
		// Remove attributes with empty values
1588 25
		$final_atts = array_filter( $final_atts );
1589
1590
		// If the href wasn't passed as an attribute, use the value passed to the function
1591 25
		if ( empty( $final_atts['href'] ) && ! empty( $href ) ) {
1592 25
			$final_atts['href'] = $href;
1593
		}
1594
1595 25
		$final_atts['href'] = esc_url_raw( $href );
1596
1597
		/**
1598
		 * Fix potential security issue with target=_blank
1599
		 * @see https://dev.to/ben/the-targetblank-vulnerability-by-example
1600
		 */
1601 25
		if( '_blank' === \GV\Utils::get( $final_atts, 'target' ) ) {
1602 4
			$final_atts['rel'] = trim( \GV\Utils::get( $final_atts, 'rel', '' ) . ' noopener noreferrer' );
1603
		}
1604
1605
		// Sort the attributes alphabetically, to help testing
1606 25
		ksort( $final_atts );
1607
1608
		// For each attribute, generate the code
1609 25
		$output = '';
1610 25
		foreach ( $final_atts as $attr => $value ) {
1611 25
			$output .= sprintf( ' %s="%s"', $attr, esc_attr( $value ) );
1612
		}
1613
1614 25
		if( '' !== $output ) {
1615 25
			$output = '<a' . $output . '>' . $anchor_text . '</a>';
1616
		}
1617
1618 25
		return $output;
1619
	}
1620
1621
	/**
1622
	 * array_merge_recursive does indeed merge arrays, but it converts values with duplicate
1623
	 * keys to arrays rather than overwriting the value in the first array with the duplicate
1624
	 * value in the second array, as array_merge does.
1625
	 *
1626
	 * @see http://php.net/manual/en/function.array-merge-recursive.php
1627
	 *
1628
	 * @since  1.5.3
1629
	 * @param array $array1
1630
	 * @param array $array2
1631
	 * @return array
1632
	 * @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
1633
	 * @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
1634
	 */
1635
	public static function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1636
		$merged = $array1;
1637
		foreach ( $array2 as $key => $value ) {
1638
			if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
1639
				$merged[ $key ] = self::array_merge_recursive_distinct( $merged[ $key ], $value );
1640
			} else if ( is_numeric( $key ) && isset( $merged[ $key ] ) ) {
1641
				$merged[] = $value;
1642
			} else {
1643
				$merged[ $key ] = $value;
1644
			}
1645
		}
1646
1647
		return $merged;
1648
	}
1649
1650
	/**
1651
	 * Get WordPress users with reasonable limits set
1652
	 *
1653
	 * @param string $context Where are we using this information (e.g. change_entry_creator, search_widget ..)
1654
	 * @param array $args Arguments to modify the user query. See get_users() {@since 1.14}
1655
	 * @return array Array of WP_User objects.
1656
	 */
1657
	public static function get_users( $context = 'change_entry_creator', $args = array() ) {
1658
1659
		$default_args = array(
1660
			'number' => 2000,
1661
			'orderby' => 'display_name',
1662
			'order' => 'ASC',
1663
			'fields' => array( 'ID', 'display_name', 'user_login', 'user_nicename' )
1664
		);
1665
1666
		// Merge in the passed arg
1667
		$get_users_settings = wp_parse_args( $args, $default_args );
1668
1669
		/**
1670
		 * @filter `gravityview/get_users/{$context}` There are issues with too many users using [get_users()](http://codex.wordpress.org/Function_Reference/get_users) where it breaks the select. We try to keep it at a reasonable number. \n
1671
		 * `$context` is where are we using this information (e.g. change_entry_creator, search_widget ..)
1672
		 * @param array $settings Settings array, with `number` key defining the # of users to display
1673
		 */
1674
		$get_users_settings = apply_filters( 'gravityview/get_users/'. $context, apply_filters( 'gravityview_change_entry_creator_user_parameters', $get_users_settings ) );
1675
1676
		return get_users( $get_users_settings );
1677
	}
1678
1679
1680
    /**
1681
     * Display updated/error notice
1682
     *
1683
     * @since 1.19.2 Added $cap and $object_id parameters
1684
     *
1685
     * @param string $notice text/HTML of notice
1686
     * @param string $class CSS class for notice (`updated` or `error`)
1687
     * @param string $cap [Optional] Define a capability required to show a notice. If not set, displays to all caps.
1688
     *
1689
     * @return string
1690
     */
1691 4
    public static function generate_notice( $notice, $class = '', $cap = '', $object_id = null ) {
1692
1693
    	// If $cap is defined, only show notice if user has capability
1694 4
    	if( $cap && ! GVCommon::has_cap( $cap, $object_id ) ) {
1695 4
    		return '';
1696
	    }
1697
1698 1
        return '<div class="gv-notice '.gravityview_sanitize_html_class( $class ) .'">'. $notice .'</div>';
1699
    }
1700
1701
	/**
1702
	 * Inspired on \GFCommon::encode_shortcodes, reverse the encoding by replacing the ascii characters by the shortcode brackets
1703
	 * @since 1.16.5
1704
	 * @param string $string Input string to decode
1705
	 * @return string $string Output string
1706
	 */
1707
	public static function decode_shortcodes( $string ) {
1708
		$replace = array( '[', ']', '"' );
1709
		$find = array( '&#91;', '&#93;', '&quot;' );
1710
		$string = str_replace( $find, $replace, $string );
1711
1712
		return $string;
1713
	}
1714
1715
1716
	/**
1717
	 * Send email using GFCommon::send_email()
1718
	 *
1719
	 * @since 1.17
1720
	 *
1721
	 * @see GFCommon::send_email This just makes the method public
1722
	 *
1723
	 * @param string $from               Sender address (required)
1724
	 * @param string $to                 Recipient address (required)
1725
	 * @param string $bcc                BCC recipients (required)
1726
	 * @param string $reply_to           Reply-to address (required)
1727
	 * @param string $subject            Subject line (required)
1728
	 * @param string $message            Message body (required)
1729
	 * @param string $from_name          Displayed name of the sender
1730
	 * @param string $message_format     If "html", sent text as `text/html`. Otherwise, `text/plain`. Default: "html".
1731
	 * @param string|array $attachments  Optional. Files to attach. {@see wp_mail()} for usage. Default: "".
1732
	 * @param array|false $entry         Gravity Forms entry array, related to the email. Default: false.
1733
	 * @param array|false $notification  Gravity Forms notification that triggered the email. {@see GFCommon::send_notification}. Default:false.
1734
	 */
1735
	public static function send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name = '', $message_format = 'html', $attachments = '', $entry = false, $notification = false ) {
1736
1737
		$SendEmail = new ReflectionMethod( 'GFCommon', 'send_email' );
1738
1739
		// It was private; let's make it public
1740
		$SendEmail->setAccessible( true );
1741
1742
		// Required: $from, $to, $bcc, $replyTo, $subject, $message
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1743
		// Optional: $from_name, $message_format, $attachments, $lead, $notification
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1744
		$SendEmail->invoke( new GFCommon, $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, $attachments, $entry, $notification );
1745
	}
1746
1747
1748
} //end class
1749
1750
1751
/**
1752
 * Generate an HTML anchor tag with a list of supported attributes
1753
 *
1754
 * @see GVCommon::get_link_html()
1755
 *
1756
 * @since 1.6
1757
 *
1758
 * @param string $href URL of the link.
1759
 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1760
 * @param array|string $atts Attributes to be added to the anchor tag
1761
 *
1762
 * @return string HTML output of anchor link. If empty $href, returns NULL
1763
 */
1764
function gravityview_get_link( $href = '', $anchor_text = '', $atts = array() ) {
1765 24
	return GVCommon::get_link_html( $href, $anchor_text, $atts );
1766
}
1767