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

GVCommon::get_forms()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.9102

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 4
dl 0
loc 23
ccs 8
cts 13
cp 0.6153
crap 4.9102
rs 9.552
c 0
b 0
f 0
1
<?php
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 32
	public static function get_form( $form_id ) {
29 32
		if ( empty( $form_id ) ) {
30
			return false;
31
		}
32
33
		// Only get_form_meta is cached. ::facepalm::
34 32
		if ( class_exists( 'GFFormsModel' ) ) {
35 32
			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 48
	public static function has_cap( $caps = '', $object_id = null, $user_id = null ) {
59 48
		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,
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 6
	public static function has_product_field( $form = array() ) {
146
147 6
		$product_fields = self::get_product_field_types();
148
149 6
		$fields = GFAPI::get_fields_by_type( $form, $product_fields );
150
151 6
		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 23
	public static function get_product_field_types() {
164
165 23
		$product_fields = apply_filters( 'gform_product_field_types', array( 'option', 'quantity', 'product', 'total', 'shipping', 'calculation', 'price', 'hiddenproduct', 'singleproduct', 'singleshipping' ) );
166
167 23
		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 23
	public static function entry_has_transaction_data( $entry = array() ) {
182
183 23
		if ( ! is_array( $entry ) ) {
184 1
			return false;
185
		}
186
187 23
		$has_transaction_data = false;
188
189 23
		$payment_meta = array( 'payment_status', 'payment_date', 'transaction_id', 'payment_amount', 'payment_method' );
190
191 23
		foreach ( $payment_meta as $meta ) {
192
193 23
			$has_transaction_data = \GV\Utils::get( $entry, $meta, false );
194
195 23
			if ( is_numeric( $has_transaction_data ) && ( ! floatval( $has_transaction_data ) > 0 ) ) {
196 3
				$has_transaction_data = false;
197 3
				continue;
198
			}
199
200 23
			if ( ! empty( $has_transaction_data ) ) {
201 17
				break;
202
			}
203
		}
204
205 23
		return (bool) $has_transaction_data;
206
	}
207
208
	/**
209
	 * Get the entry ID from the entry slug, which may or may not be the entry ID
210
	 *
211
	 * @since  1.5.2
212
	 * @param  string $slug The entry slug, as returned by GravityView_API::get_entry_slug()
213
	 * @return int|null       The entry ID, if exists; `NULL` if not
214
	 */
215
	public static function get_entry_id_from_slug( $slug ) {
216
		global $wpdb;
217
218
		$search_criteria = array(
219
			'field_filters' => array(
220
				array(
221
					'key' => 'gravityview_unique_id', // Search the meta values
222
					'value' => $slug,
223
					'operator' => 'is',
224
					'type' => 'meta',
225
				),
226
			)
227
		);
228
229
		// Limit to one for speed
230
		$paging = array(
231
			'page_size' => 1,
232
		);
233
234
		/**
235
		 * @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.
236
		 * @since 1.17.2
237
		 * @param int $form_id ID of the form to search. Default: `0` (searches all forms)
238
		 */
239
		$form_id = apply_filters( 'gravityview/common/get_entry_id_from_slug/form_id', 0 );
240
241
		$results = GFAPI::get_entries( intval( $form_id ), $search_criteria, null, $paging );
242
243
		$result = ( ! empty( $results ) && ! empty( $results[0]['id'] ) ) ? $results[0]['id'] : null;
244
245
		return $result;
246
	}
247
248
	/**
249
	 * Alias of GFAPI::get_forms()
250
	 *
251
	 * @see GFAPI::get_forms()
252
	 *
253
	 * @since 1.19 Allow "any" $active status option
254
	 * @since 2.7.2 Allow sorting forms using wp_list_sort()
255
	 *
256
	 * @param bool|string $active Status of forms. Use `any` to get array of forms with any status. Default: `true`
257
	 * @param bool $trash Include forms in trash? Default: `false`
258
	 * @param string|array $order_by Optional. Either the field name to order by or an array of multiple orderby fields as $orderby => $order.
259
	 * @param string $order Optional. Either 'ASC' or 'DESC'. Only used if $orderby is a string.
260
	 *
261
	 * @return array Empty array if GFAPI class isn't available or no forms. Otherwise, the array of Forms
262
	 */
263 1
	public static function get_forms(  $active = true, $trash = false, $order_by = 'date_created', $order = 'ASC' ) {
264 1
		$forms = array();
265 1
		if ( ! class_exists( 'GFAPI' ) ) {
266
			return array();
267
		}
268
269 1
		if( 'any' === $active ) {
270
			$active_forms = GFAPI::get_forms( true, $trash );
271
			$inactive_forms = GFAPI::get_forms( false, $trash );
272
			$forms = array_merge( array_filter( $active_forms ), array_filter( $inactive_forms ) );
273
		} else {
274 1
			$forms = GFAPI::get_forms( $active, $trash );
275
		}
276
277
		// WordPress 4.7
278 1
		if ( ! function_exists( 'wp_list_sort' ) ) {
279
			return $forms;
280
		}
281
282 1
		$forms = wp_list_sort( $forms, $order_by, $order, true );
283
284 1
		return $forms;
285
	}
286
287
	/**
288
	 * Return array of fields' id and label, for a given Form ID
289
	 *
290
	 * @access public
291
	 * @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...
292
	 * @param bool $add_default_properties
293
	 * @param bool $include_parent_field
294
	 * @return array
295
	 */
296
	public static function get_form_fields( $form = '', $add_default_properties = false, $include_parent_field = true ) {
297
298
		if ( ! is_array( $form ) ) {
299
			$form = self::get_form( $form );
300
		}
301
302
		$fields = array();
303
		$has_product_fields = false;
304
		$has_post_fields = false;
305
306
		if ( $form ) {
307
			foreach ( $form['fields'] as $field ) {
308
				if ( $include_parent_field || empty( $field['inputs'] ) ) {
309
					$fields["{$field['id']}"] = array(
310
						'label' => \GV\Utils::get( $field, 'label' ),
311
						'parent' => null,
312
						'type' => \GV\Utils::get( $field, 'type' ),
313
						'adminLabel' => \GV\Utils::get( $field, 'adminLabel' ),
314
						'adminOnly' => \GV\Utils::get( $field, 'adminOnly' ),
315
					);
316
				}
317
318
				if ( $add_default_properties && ! empty( $field['inputs'] ) ) {
319
					foreach ( $field['inputs'] as $input ) {
320
321
						if( ! empty( $input['isHidden'] ) ) {
322
							continue;
323
						}
324
325
						/**
326
                         * @hack
327
                         * In case of email/email confirmation, the input for email has the same id as the parent field
328
                         */
329
						if( 'email' === $field['type'] && false === strpos( $input['id'], '.' ) ) {
330
                            continue;
331
                        }
332
						$fields["{$input['id']}"] = array(
333
							'label' => \GV\Utils::get( $input, 'label' ),
334
							'customLabel' => \GV\Utils::get( $input, 'customLabel' ),
335
							'parent' => $field,
336
							'type' => \GV\Utils::get( $field, 'type' ),
337
							'adminLabel' => \GV\Utils::get( $field, 'adminLabel' ),
338
							'adminOnly' => \GV\Utils::get( $field, 'adminOnly' ),
339
						);
340
					}
341
				}
342
343
344
				if( GFCommon::is_product_field( $field['type'] ) ){
345
					$has_product_fields = true;
346
				}
347
348
				if ( GFCommon::is_post_field( $field ) ) {
349
					$has_post_fields = true;
350
				}
351
			}
352
		}
353
354
		/**
355
		 * @since 1.7
356
		 */
357
		if ( $has_post_fields ) {
358
			$fields['post_id'] = array(
359
				'label' => __( 'Post ID', 'gravityview' ),
360
				'type' => 'post_id',
361
			);
362
		}
363
364
		if ( $has_product_fields ) {
365
366
			$payment_fields = GravityView_Fields::get_all( 'pricing' );
367
368
			foreach ( $payment_fields as $payment_field ) {
369
370
				// Either the field exists ($fields['shipping']) or the form explicitly contains a `shipping` field with numeric key
371
				if( isset( $fields["{$payment_field->name}"] ) || GFCommon::get_fields_by_type( $form, $payment_field->name ) ) {
372
					continue;
373
				}
374
375
				$fields["{$payment_field->name}"] = array(
376
					'label' => $payment_field->label,
377
					'desc' => $payment_field->description,
378
					'type' => $payment_field->name,
379
				);
380
			}
381
		}
382
383
		/**
384
		 * @filter `gravityview/common/get_form_fields` Modify the form fields shown in the Add Field field picker.
385
		 * @since 1.17
386
		 * @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`
387
		 * @param array $form GF Form array
388
		 * @param bool $include_parent_field Whether to include the parent field when getting a field with inputs
389
		 */
390
		$fields = apply_filters( 'gravityview/common/get_form_fields', $fields, $form, $include_parent_field );
391
392
		return $fields;
393
394
	}
395
396
	/**
397
	 * get extra fields from entry meta
398
	 * @param  string $form_id (default: '')
399
	 * @return array
400
	 */
401
	public static function get_entry_meta( $form_id, $only_default_column = true ) {
402
403
		$extra_fields = GFFormsModel::get_entry_meta( $form_id );
404
405
		$fields = array();
406
407
		foreach ( $extra_fields as $key => $field ){
408
			if ( ! empty( $only_default_column ) && ! empty( $field['is_default_column'] ) ) {
409
				$fields[ $key ] = array( 'label' => $field['label'], 'type' => 'entry_meta' );
410
			}
411
		}
412
413
		return $fields;
414
	}
415
416
417
	/**
418
	 * Wrapper for the Gravity Forms GFFormsModel::search_lead_ids() method
419
	 *
420
	 * @see  GFEntryList::leads_page()
421
	 * @param  int $form_id ID of the Gravity Forms form
422
	 * @since  1.1.6
423
	 * @return array|void          Array of entry IDs. Void if Gravity Forms isn't active.
424
	 */
425
	public static function get_entry_ids( $form_id, $search_criteria = array() ) {
426
427
		if ( ! class_exists( 'GFFormsModel' ) ) {
428
			return;
429
		}
430
431
		return GFFormsModel::search_lead_ids( $form_id, $search_criteria );
432
	}
433
434
	/**
435
	 * Calculates the Search Criteria used on the self::get_entries / self::get_entry methods
436
	 *
437
	 * @since 1.7.4
438
	 *
439
	 * @param array $passed_criteria array Input Criteria (search_criteria, sorting, paging)
440
	 * @param array $form_ids array Gravity Forms form IDs
441
	 * @return array
442
	 */
443 73
	public static function calculate_get_entries_criteria( $passed_criteria = array(), $form_ids = array() ) {
444
445
		$search_criteria_defaults = array(
446 73
			'search_criteria' => null,
447
			'sorting' => null,
448
			'paging' => null,
449 73
			'cache' => (isset( $passed_criteria['cache'] ) ? (bool) $passed_criteria['cache'] : true),
450
			'context_view_id' => null,
451
		);
452
453 73
		$criteria = wp_parse_args( $passed_criteria, $search_criteria_defaults );
454
455 73
		if ( ! empty( $criteria['search_criteria']['field_filters'] ) && is_array( $criteria['search_criteria']['field_filters'] ) ) {
456 16
			foreach ( $criteria['search_criteria']['field_filters'] as &$filter ) {
457
458 16
				if ( ! is_array( $filter ) ) {
459 15
					continue;
460
				}
461
462
				// By default, we want searches to be wildcard for each field.
463 16
				$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
464
465
				/**
466
				 * @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc)
467
				 * @param string $operator Existing search operator
468
				 * @param array $filter array with `key`, `value`, `operator`, `type` keys
469
				 */
470 16
				$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
471
			}
472
473
			// don't send just the [mode] without any field filter.
474 16
			if( count( $criteria['search_criteria']['field_filters'] ) === 1 && array_key_exists( 'mode' , $criteria['search_criteria']['field_filters'] ) ) {
475 1
				unset( $criteria['search_criteria']['field_filters']['mode'] );
476
			}
477
478
		}
479
480
481
482
		/**
483
		 * Prepare date formats to be in Gravity Forms DB format;
484
		 * $passed_criteria may include date formats incompatible with Gravity Forms.
485
		 */
486 73
		foreach ( array('start_date', 'end_date' ) as $key ) {
487
488 73
			if ( ! empty( $criteria['search_criteria'][ $key ] ) ) {
489
490
				// Use date_create instead of new DateTime so it returns false if invalid date format.
491 2
				$date = date_create( $criteria['search_criteria'][ $key ] );
492
493 2
				if ( $date ) {
494
					// Gravity Forms wants dates in the `Y-m-d H:i:s` format.
495 2
					$criteria['search_criteria'][ $key ] = $date->format( 'Y-m-d H:i:s' );
496
				} else {
497 1
					gravityview()->log->error( '{key} Date format not valid:', array( 'key' => $key, $criteria['search_criteria'][ $key ] ) );
498
499
					// If it's an invalid date, unset it. Gravity Forms freaks out otherwise.
500 1
					unset( $criteria['search_criteria'][ $key ] );
501
				}
502
			}
503
		}
504
505 73
		if ( empty( $criteria['context_view_id'] ) ) {
506
			// Calculate the context view id and send it to the advanced filter
507 2
			if ( GravityView_frontend::getInstance()->getSingleEntry() ) {
508 1
				$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
509 2
			} 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...
510 1
				$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
511 2
			} else if ( 'delete' === GFForms::get( 'action' ) ) {
512 1
				$criteria['context_view_id'] = isset( $_GET['view_id'] ) ? intval( $_GET['view_id'] ) : null;
513
			}
514
		}
515
516
		/**
517
		 * @filter `gravityview_search_criteria` Apply final criteria filter (Used by the Advanced Filter extension)
518
		 * @param array $criteria Search criteria used by GravityView
519
		 * @param array $form_ids Forms to search
520
		 * @param int $view_id ID of the view being used to search
521
		 */
522 73
		$criteria = apply_filters( 'gravityview_search_criteria', $criteria, $form_ids, $criteria['context_view_id'] );
523
524 73
		return (array)$criteria;
525
	}
526
527
528
	/**
529
	 * Retrieve entries given search, sort, paging criteria
530
	 *
531
	 * @see  GFAPI::get_entries()
532
	 * @see GFFormsModel::get_field_filters_where()
533
	 * @access public
534
	 * @param int|array $form_ids The ID of the form or an array IDs of the Forms. Zero for all forms.
535
	 * @param mixed $passed_criteria (default: null)
536
	 * @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)
537
	 *
538
	 * @deprecated See \GV\View::get_entries.
539
	 *
540
	 * @return mixed False: Error fetching entries. Array: Multi-dimensional array of Gravity Forms entry arrays
541
	 */
542
	public static function get_entries( $form_ids = null, $passed_criteria = null, &$total = null ) {
543
544
		gravityview()->log->notice( '\GVCommon::get_entries is deprecated. Use \GV\View::get_entries instead.' );
545
546
		// Filter the criteria before query (includes Adv Filter)
547
		$criteria = self::calculate_get_entries_criteria( $passed_criteria, $form_ids );
548
549
		gravityview()->log->debug( '[gravityview_get_entries] Final Parameters', array( 'data' => $criteria ) );
550
551
		// Return value
552
		$return = null;
553
554
		/** Reduce # of database calls */
555
		add_filter( 'gform_is_encrypted_field', '__return_false' );
556
557
		if ( ! empty( $criteria['cache'] ) ) {
558
559
			$Cache = new GravityView_Cache( $form_ids, $criteria );
560
561
			if ( $entries = $Cache->get() ) {
562
563
				// Still update the total count when using cached results
564
				if ( ! is_null( $total ) ) {
565
					$total = GFAPI::count_entries( $form_ids, $criteria['search_criteria'] );
566
				}
567
568
				$return = $entries;
569
			}
570
		}
571
572
		if ( is_null( $return ) && class_exists( 'GFAPI' ) && ( is_numeric( $form_ids ) || is_array( $form_ids ) ) ) {
573
574
			/**
575
			 * @filter `gravityview_pre_get_entries` Define entries to be used before GFAPI::get_entries() is called
576
			 * @since 1.14
577
			 * @param  null $return If you want to override GFAPI::get_entries() and define entries yourself, tap in here.
578
			 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
579
			 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
580
			 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
581
			 * @since 2.1 The $total parameter can now be overriden by reference.
582
			 * @deprecated
583
			 */
584
			$entries = apply_filters_ref_array( 'gravityview_before_get_entries', array( null, $criteria, $passed_criteria, &$total ) );
585
586
			// No entries returned from gravityview_before_get_entries
587
			if( is_null( $entries ) ) {
588
589
				$entries = GFAPI::get_entries( $form_ids, $criteria['search_criteria'], $criteria['sorting'], $criteria['paging'], $total );
590
591
				if ( is_wp_error( $entries ) ) {
592
					gravityview()->log->error( '{error}', array( 'error' => $entries->get_error_message(), 'data' => $entries ) );
593
594
					/** Remove filter added above */
595
					remove_filter( 'gform_is_encrypted_field', '__return_false' );
596
					return false;
597
				}
598
			}
599
600
			if ( ! empty( $criteria['cache'] ) && isset( $Cache ) ) {
601
602
				// Cache results
603
				$Cache->set( $entries, 'entries' );
604
605
			}
606
607
			$return = $entries;
608
		}
609
610
		/** Remove filter added above */
611
		remove_filter( 'gform_is_encrypted_field', '__return_false' );
612
613
		/**
614
		 * @filter `gravityview_entries` Modify the array of entries returned to GravityView after it has been fetched from the cache or from `GFAPI::get_entries()`.
615
		 * @param  array|null $entries Array of entries as returned by the cache or by `GFAPI::get_entries()`
616
		 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
617
		 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
618
		 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
619
		 * @since 2.1 The $total parameter can now be overriden by reference.
620
		 * @deprecated
621
		 */
622
		$return = apply_filters_ref_array( 'gravityview_entries', array( $return, $criteria, $passed_criteria, &$total ) );
623
624
		return $return;
625
	}
626
627
628
	/**
629
	 * Get the entry ID from a string that may be the Entry ID or the Entry Slug
630
	 *
631
	 * @since 1.18
632
	 *
633
	 * @param string $entry_id_or_slug The ID or slug of an entry.
634
	 * @param bool $force_allow_ids Whether to force allowing getting the ID of an entry, even if custom slugs are enabled
635
	 *
636
	 * @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.
637
	 */
638 3
	public static function get_entry_id( $entry_id_or_slug = '', $force_allow_ids = false ) {
639
640 3
		$entry_id = false;
641
642
		/**
643
		 * @filter `gravityview_custom_entry_slug` Whether to enable and use custom entry slugs.
644
		 * @param boolean True: Allow for slugs based on entry values. False: always use entry IDs (default)
645
		 */
646 3
		$custom_slug = apply_filters( 'gravityview_custom_entry_slug', false );
647
648
		/**
649
		 * @filter `gravityview_custom_entry_slug_allow_id` When using a custom slug, allow access to the entry using the original slug (the Entry ID).
650
		 * - If disabled (default), only allow access to an entry using the custom slug value.  (example: `/entry/custom-slug/` NOT `/entry/123/`)
651
		 * - If enabled, you could access using the custom slug OR the entry id (example: `/entry/custom-slug/` OR `/entry/123/`)
652
		 * @param boolean $custom_slug_id_access True: allow accessing the slug by ID; False: only use the slug passed to the method.
653
		 */
654 3
		$custom_slug_id_access = $force_allow_ids || apply_filters( 'gravityview_custom_entry_slug_allow_id', false );
655
656
		/**
657
		 * If we're using custom entry slugs, we do a meta value search
658
		 * instead of doing a straightup ID search.
659
		 */
660 3
		if ( $custom_slug ) {
661
			// Search for IDs matching $entry_id_or_slug
662
			$entry_id = self::get_entry_id_from_slug( $entry_id_or_slug );
663
		}
664
665
		// If custom slug is off, search using the entry ID
666
		// ID allow ID access is on, also use entry ID as a backup
667 3
		if ( false === $custom_slug || true === $custom_slug_id_access ) {
668
			// Search for IDs matching $entry_slug
669 3
			$entry_id = $entry_id_or_slug;
670
		}
671
672 3
		return $entry_id;
673
	}
674
675
	/**
676
	 * Return a single entry object
677
	 *
678
	 * 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()`
679
	 *
680
	 * @access public
681
	 * @param string|int $entry_slug Either entry ID or entry slug string
682
	 * @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.
683
	 * @param boolean $check_entry_display Check whether the entry is visible for the current View configuration. Default: true. {@since 1.14}
684
	 * @param \GV\View $view The View if $check_entry_display is set to true. {@since develop}
685
	 * @return array|boolean
686
	 */
687 32
	public static function get_entry( $entry_slug, $force_allow_ids = false, $check_entry_display = true, $view = null ) {
688
689 32
		if ( ! class_exists( 'GFAPI' ) || empty( $entry_slug ) ) {
690 30
			return false;
691
		}
692
693 3
		$entry_id = self::get_entry_id( $entry_slug, $force_allow_ids );
694
695 3
		if ( empty( $entry_id ) ) {
696
			return false;
697
		}
698
699
		// fetch the entry
700 3
		$entry = GFAPI::get_entry( $entry_id );
701
702
		/**
703
		 * @filter `gravityview/common/get_entry/check_entry_display` Override whether to check entry display rules against filters
704
		 * @since 1.16.2
705
		 * @since 2.6 Added $view parameter
706
		 * @param bool $check_entry_display Check whether the entry is visible for the current View configuration. Default: true.
707
		 * @param array $entry Gravity Forms entry array
708
		 * @param \GV\View $view The View
709
		 */
710 3
		$check_entry_display = apply_filters( 'gravityview/common/get_entry/check_entry_display', $check_entry_display, $entry, $view );
711
712 3
		if( $check_entry_display ) {
713 1
			if ( ! $view ) {
714 1
				$view = \GV\View::by_id( \GravityView_View::getInstance()->getViewId() ); // @todo Bad legacy context, provide $view parameter!
715 1
				gravityview()->log->warning( '$view parameter not provided! Context assumed from legacy context mocks. This is unreliable!' );
716
			}
717
718
			// Is the entry allowed
719 1
			$entry = self::check_entry_display( $entry, $view );
720
		}
721
722 3
		if( is_wp_error( $entry ) ) {
723 2
			gravityview()->log->error( '{error}', array( 'error' => $entry->get_error_message() ) );
724 2
			return false;
725
		}
726
727 2
		return $entry;
728
	}
729
730
	/**
731
	 * Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including:
732
	 * 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals',
733
	 * 'not_contains', 'in', and 'not_in'
734
	 *
735
	 * @since 1.13 You can define context, which displays/hides based on what's being displayed (single, multiple, edit)
736
	 * @since 1.22.1 Added 'in' and 'not_in' for JSON-encoded array values, serialized non-strings
737
	 *
738
	 * @see https://docs.gravityview.co/article/252-gvlogic-shortcode
739
	 * @uses GFFormsModel::matches_operation
740
	 * @since 1.7.5
741
	 *
742
	 * @param mixed $val1 Left side of comparison
743
	 * @param mixed $val2 Right side of comparison
744
	 * @param string $operation Type of comparison
745
	 *
746
	 * @return bool True: matches, false: not matches
747
	 */
748 25
	public static function matches_operation( $val1, $val2, $operation ) {
749
750
		// Only process strings
751 25
		$val1 = ! is_string( $val1 ) ? wp_json_encode( $val1 ) : $val1;
752 25
		$val2 = ! is_string( $val2 ) ? wp_json_encode( $val2 ) : $val2;
753
754 25
		$value = false;
755
756 25
		if( 'context' === $val1 ) {
757
758
			$matching_contexts = array( $val2 );
759
760
			// We allow for non-standard contexts.
761
			switch( $val2 ) {
762
				// Check for either single or edit
763
				case 'singular':
764
					$matching_contexts = array( 'single', 'edit' );
765
					break;
766
				// Use multiple as alias for directory for consistency
767
				case 'multiple':
768
					$matching_contexts = array( 'directory' );
769
					break;
770
			}
771
772
			$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...
773
		}
774
775 25
		switch ( $operation ) {
776 25
			case 'equals':
777 7
				$value = self::matches_operation( $val1, $val2, 'is' );
778 7
				break;
779 25
			case 'greater_than_or_is':
780 25
			case 'greater_than_or_equals':
781 5
				$is    = self::matches_operation( $val1, $val2, 'is' );
782 5
				$gt    = self::matches_operation( $val1, $val2, 'greater_than' );
783 5
				$value = ( $is || $gt );
784 5
				break;
785 25
			case 'less_than_or_is':
786 25
			case 'less_than_or_equals':
787 3
				$is    = self::matches_operation( $val1, $val2, 'is' );
788 3
				$gt    = self::matches_operation( $val1, $val2, 'less_than' );
789 3
				$value = ( $is || $gt );
790 3
				break;
791 25
			case 'not_contains':
792 1
				$contains = self::matches_operation( $val1, $val2, 'contains' );
793 1
				$value    = ! $contains;
794 1
				break;
795
			/**
796
			 * @since 1.22.1 Handle JSON-encoded comparisons
797
			 */
798 25
			case 'in':
799 25
			case 'not_in':
800
801 1
				$json_val_1 = json_decode( $val1, true );
802 1
				$json_val_2 = json_decode( $val2, true );
803
804 1
				if( ! empty( $json_val_1 ) || ! empty( $json_val_2 ) ) {
805
806 1
					$json_in = false;
807 1
					$json_val_1 = $json_val_1 ? (array) $json_val_1 : array( $val1 );
808 1
					$json_val_2 = $json_val_2 ? (array) $json_val_2 : array( $val2 );
809
810
					// For JSON, we want to compare as "in" or "not in" rather than "contains"
811 1
					foreach ( $json_val_1 as $item_1 ) {
812 1
						foreach ( $json_val_2 as $item_2 ) {
813 1
							$json_in = self::matches_operation( $item_1, $item_2, 'is' );
814
815 1
							if( $json_in ) {
816 1
								break 2;
817
							}
818
						}
819
					}
820
821 1
					$value = ( $operation === 'in' ) ? $json_in : ! $json_in;
822
				}
823 1
				break;
824
825 25
			case 'less_than':
826 23
			case '<' :
827 5
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
828 5
					$value = $val1 < $val2;
829
				} else {
830
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
831
				}
832 5
				break;
833 23
			case 'greater_than':
834 21
			case '>' :
835 9
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
836 9
					$value = $val1 > $val2;
837
				} else {
838
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
839
				}
840 9
				break;
841
			default:
842 21
				$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
843
		}
844
845 25
		return $value;
846
	}
847
848
	/**
849
	 *
850
	 * Checks if a certain entry is valid according to the View search filters (specially the Adv Filters)
851
	 *
852
	 * @uses GVCommon::calculate_get_entries_criteria();
853
	 * @see GFFormsModel::is_value_match()
854
	 *
855
	 * @since 1.7.4
856
	 * @since 2.1 Added $view parameter
857
	 *
858
	 * @param array $entry Gravity Forms Entry object
859
	 * @param \GV\View $view The View.
860
	 *
861
	 * @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.
862
	 */
863 12
	public static function check_entry_display( $entry, $view = null ) {
864
865 12
		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...
866 1
			return new WP_Error('entry_not_found', 'Entry was not found.', $entry );
867
		}
868
869 12
		if ( empty( $entry['form_id'] ) ) {
870 1
			return new WP_Error( 'form_id_not_set', '[apply_filters_to_entry] Entry is empty!', $entry );
871
		}
872
873 12
		if ( is_null( $view ) ) {
874 1
			gravityview()->log->warning( '$view was not supplied to check_entry_display, results will be non-typical.' );
875 1
			return new WP_Error( 'view_not_supplied', 'View is not supplied!', $entry );
876
		}
877
878 12
		if ( ! gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) {
879
			return new WP_Error( 'no_gf_query', 'GF_Query is missing.', $entry );
880
		}
881
882 12
		$view_form_id = $view->form->ID;
883
884 12
		if ( $view->joins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $view->joins of type GV\Join[] 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...
885 1
			if ( in_array( (int)$entry['form_id'], array_keys( $view::get_joined_forms( $view->ID ) ), true ) ) {
886 1
				$view_form_id = $entry['form_id'];
887
			}
888
		}
889
890 12
		if ( $view_form_id != $entry['form_id'] ) {
891 2
			return new WP_Error( 'view_id_not_match', 'View form source does not match entry form source ID.', $entry );
892
		}
893
894
		/**
895
		 * Check whether the entry is in the entries subset by running a modified query.
896
		 */
897 11
		add_action( 'gravityview/view/query', $entry_subset_callback = function( &$query, $view, $request ) use ( $entry, $view_form_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $view 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 $request 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...
898 11
			$_tmp_query       = new \GF_Query( $view_form_id, array(
899
				'field_filters' => array(
900 11
					'mode' => 'all',
901
					array(
902 11
						'key' => 'id',
903 11
						'operation' => 'is',
904 11
						'value' => $entry['id']
905
					)
906
				)
907
			) );
908
909 11
			$_tmp_query_parts = $_tmp_query->_introspect();
910
911
			/** @var \GF_Query $query */
912 11
			$query_parts      = $query->_introspect();
913
914 11
			$query->where( \GF_Query_Condition::_and( $_tmp_query_parts['where'], $query_parts['where'] ) );
915
916 11
		}, 10, 3 );
917
918
		// Prevent page offset from being applied to the single entry query; it's used to return to the referring page number
919 11
		add_filter( 'gravityview_search_criteria', $remove_pagenum = function( $criteria ) {
920
921 11
			$criteria['paging'] = array(
922
				'offset' => 0,
923
				'page_size' => 25
924
			);
925
926 11
			return $criteria;
927 11
		} );
928
929 11
		$entries = $view->get_entries()->all();
930
931
		// Remove the modifying filter
932 11
		remove_filter( 'gravityview_search_criteria', $remove_pagenum );
933
934 11
		if ( ! $entries ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $entries of type GV\Entry[] 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...
935 8
			remove_action( 'gravityview/view/query', $entry_subset_callback );
936 8
			return new \WP_Error( 'failed_criteria', 'Entry failed search_criteria and field_filters' );
937
		}
938
939
		// This entry is on a View with joins
940 10
		if ( $entries[0]->is_multi() ) {
941
942 1
			$multi_entry_ids = array();
943
944 1
			foreach ( $entries[0]->entries as $multi_entry ) {
0 ignored issues
show
Bug introduced by
The property entries does not seem to exist in GV\Entry.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
945 1
				$multi_entry_ids[] = (int) $multi_entry->ID;
946
			}
947
948 1
			if ( ! in_array( (int) $entry['id'], $multi_entry_ids, true ) ) {
949
				remove_action( 'gravityview/view/query', $entry_subset_callback );
950 1
				return new \WP_Error( 'failed_criteria', 'Entry failed search_criteria and field_filters' );
951
			}
952
953 9
		} elseif ( (int) $entries[0]->ID !== (int) $entry['id'] ) {
954
			remove_action( 'gravityview/view/query', $entry_subset_callback );
955
			return new \WP_Error( 'failed_criteria', 'Entry failed search_criteria and field_filters' );
956
		}
957
958 10
		remove_action( 'gravityview/view/query', $entry_subset_callback );
959 10
		return $entry;
960
	}
961
962
963
	/**
964
	 * Allow formatting date and time based on GravityView standards
965
	 *
966
	 * @since 1.16
967
	 *
968
	 * @see GVCommon_Test::test_format_date for examples
969
	 *
970
	 * @param string $date_string The date as stored by Gravity Forms (`Y-m-d h:i:s` GMT)
971
	 * @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
972
	 * - `raw` Un-formatted date string in original `Y-m-d h:i:s` format
973
	 * - `timestamp` Integer timestamp returned by GFCommon::get_local_timestamp()
974
	 * - `diff` "%s ago" format, unless other `format` is defined
975
	 * - `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.
976
	 * - `time` Include time in the `GFCommon::format_date()` output
977
	 * - `format` Define your own date format, or `diff` format
978
	 *
979
	 * @return int|null|string Formatted date based on the original date
980
	 */
981 6
	public static function format_date( $date_string = '', $args = array() ) {
982
983
		$default_atts = array(
984 6
			'raw' => false,
985
			'timestamp' => false,
986
			'diff' => false,
987
			'human' => false,
988
			'format' => '',
989
			'time' => false,
990
		);
991
992 6
		$atts = wp_parse_args( $args, $default_atts );
993
994
		/**
995
		 * Gravity Forms code to adjust date to locally-configured Time Zone
996
		 * @see GFCommon::format_date() for original code
997
		 */
998 6
		$date_gmt_time   = mysql2date( 'G', $date_string );
999 6
		$date_local_timestamp = GFCommon::get_local_timestamp( $date_gmt_time );
1000
1001 6
		$format  = \GV\Utils::get( $atts, 'format' );
1002 6
		$is_human  = ! empty( $atts['human'] );
1003 6
		$is_diff  = ! empty( $atts['diff'] );
1004 6
		$is_raw = ! empty( $atts['raw'] );
1005 6
		$is_timestamp = ! empty( $atts['timestamp'] );
1006 6
		$include_time = ! empty( $atts['time'] );
1007
1008
		// If we're using time diff, we want to have a different default format
1009 6
		if( empty( $format ) ) {
1010
			/* translators: %s: relative time from now, used for generic date comparisons. "1 day ago", or "20 seconds ago" */
1011 5
			$format = $is_diff ? esc_html__( '%s ago', 'gravityview' ) : get_option( 'date_format' );
1012
		}
1013
1014
		// If raw was specified, don't modify the stored value
1015 6
		if ( $is_raw ) {
1016 2
			$formatted_date = $date_string;
1017 6
		} elseif( $is_timestamp ) {
1018 3
			$formatted_date = $date_local_timestamp;
1019 5
		} elseif ( $is_diff ) {
1020 2
			$formatted_date = sprintf( $format, human_time_diff( $date_gmt_time ) );
1021
		} else {
1022 5
			$formatted_date = GFCommon::format_date( $date_string, $is_human, $format, $include_time );
1023
		}
1024
1025 6
		unset( $format, $is_diff, $is_human, $is_timestamp, $is_raw, $date_gmt_time, $date_local_timestamp, $default_atts );
1026
1027 6
		return $formatted_date;
1028
	}
1029
1030
	/**
1031
	 * Retrieve the label of a given field id (for a specific form)
1032
	 *
1033
	 * @access public
1034
	 * @since 1.17 Added $field_value parameter
1035
	 *
1036
	 * @param array $form Gravity Forms form array
1037
	 * @param string $field_id ID of the field. If an input, full input ID (like `1.3`)
1038
	 * @param string|array $field_value Raw value of the field.
1039
	 * @return string
1040
	 */
1041 1
	public static function get_field_label( $form = array(), $field_id = '', $field_value = '' ) {
1042
1043 1
		if ( empty( $form ) || empty( $field_id ) ) {
1044
			return '';
1045
		}
1046
1047 1
		$field = self::get_field( $form, $field_id );
1048
1049 1
		$label = \GV\Utils::get( $field, 'label' );
1050
1051 1
		if( floor( $field_id ) !== floatval( $field_id ) ) {
1052 1
			$label = GFFormsModel::get_choice_text( $field, $field_value, $field_id );
1053
		}
1054
1055 1
		return $label;
1056
	}
1057
1058
1059
	/**
1060
	 * Returns the field details array of a specific form given the field id
1061
	 *
1062
	 * Alias of GFFormsModel::get_field
1063
	 *
1064
	 * @since 1.19 Allow passing form ID as well as form array
1065
	 *
1066
	 * @uses GFFormsModel::get_field
1067
	 * @see GFFormsModel::get_field
1068
	 * @access public
1069
	 * @param array|int $form Form array or ID
1070
	 * @param string|int $field_id
1071
	 * @return GF_Field|null Gravity Forms field object, or NULL: Gravity Forms GFFormsModel does not exist or field at $field_id doesn't exist.
1072
	 */
1073 9
	public static function get_field( $form, $field_id ) {
1074
1075 9
		if ( is_numeric( $form ) ) {
1076
			$form = GFAPI::get_form( $form );
1077
		}
1078
1079 9
		if ( class_exists( 'GFFormsModel' ) ){
1080 9
			return GFFormsModel::get_field( $form, $field_id );
1081
		} else {
1082
			return null;
1083
		}
1084
	}
1085
1086
1087
	/**
1088
	 * Check whether the post is GravityView
1089
	 *
1090
	 * - Check post type. Is it `gravityview`?
1091
	 * - Check shortcode
1092
	 *
1093
	 * @param  WP_Post      $post WordPress post object
1094
	 * @return boolean           True: yep, GravityView; No: not!
1095
	 */
1096 2
	public static function has_gravityview_shortcode( $post = null ) {
1097 2
		if ( ! is_a( $post, 'WP_Post' ) ) {
1098 1
			return false;
1099
		}
1100
1101 2
		if ( 'gravityview' === get_post_type( $post ) ) {
1102 2
			return true;
1103
		}
1104
1105 2
		return self::has_shortcode_r( $post->post_content, 'gravityview' ) ? true : false;
1106
1107
	}
1108
1109
1110
	/**
1111
	 * Placeholder until the recursive has_shortcode() patch is merged
1112
	 * @see https://core.trac.wordpress.org/ticket/26343#comment:10
1113
	 * @param string $content Content to check whether there's a shortcode
1114
	 * @param string $tag Current shortcode tag
1115
	 */
1116 4
	public static function has_shortcode_r( $content, $tag = 'gravityview' ) {
1117 4
		if ( false === strpos( $content, '[' ) ) {
1118 2
			return false;
1119
		}
1120
1121 4
		if ( shortcode_exists( $tag ) ) {
1122
1123 4
			$shortcodes = array();
1124
1125 4
			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
1126 4
			if ( empty( $matches ) ){
1127 1
				return false;
1128
			}
1129
1130 4
			foreach ( $matches as $shortcode ) {
1131 4
				if ( $tag === $shortcode[2] ) {
1132
1133
					// Changed this to $shortcode instead of true so we get the parsed atts.
1134 4
					$shortcodes[] = $shortcode;
1135
1136 1
				} else if ( isset( $shortcode[5] ) && $results = self::has_shortcode_r( $shortcode[5], $tag ) ) {
1137 1
					foreach( $results as $result ) {
1138 1
						$shortcodes[] = $result;
1139
					}
1140
				}
1141
			}
1142
1143 4
			return $shortcodes;
1144
		}
1145
		return false;
1146
	}
1147
1148
1149
1150
	/**
1151
	 * Get the views for a particular form
1152
	 *
1153
	 * @since 1.15.2 Add $args array and limit posts_per_page to 500
1154
	 *
1155
	 * @uses get_posts()
1156
	 *
1157
	 * @param  int $form_id Gravity Forms form ID
1158
	 * @param  array $args Pass args sent to get_posts()
1159
	 *
1160
	 * @return array          Array with view details, as returned by get_posts()
1161
	 */
1162 1
	public static function get_connected_views( $form_id, $args = array() ) {
1163
1164 1
		global $wpdb;
1165
1166
		$defaults = array(
1167 1
			'post_type'      => 'gravityview',
1168 1
			'posts_per_page' => 100,
1169 1
			'meta_key'       => '_gravityview_form_id',
1170 1
			'meta_value'     => (int) $form_id,
1171
		);
1172 1
		$args     = wp_parse_args( $args, $defaults );
1173 1
		$views    = get_posts( $args );
1174
1175 1
		$views_with_joins = $wpdb->get_results( "SELECT `post_id`, `meta_value` FROM $wpdb->postmeta WHERE `meta_key` = '_gravityview_form_joins'" );
1176
1177 1
		$joined_forms = array();
1178 1
		foreach ( $views_with_joins as $view ) {
1179
1180 1
			$data = unserialize( $view->meta_value );
1181
1182 1
			if( ! $data || ! is_array( $data ) ) {
1183 1
				continue;
1184
			}
1185
1186
			foreach ( $data as $datum ) {
1187
				if ( ! empty( $datum[2] ) && (int) $datum[2] === (int) $form_id ) {
1188
					$joined_forms[] = $view->post_id;
1189
				}
1190
			}
1191
		}
1192
1193 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...
1194
			$joined_args  = array(
1195
				'post_type'      => 'gravityview',
1196
				'posts_per_page' => $args['posts_per_page'],
1197
				'post__in'       => $joined_forms,
1198
			);
1199
			$views = array_merge( $views, get_posts( $joined_args ) );
1200
		}
1201
1202 1
		return $views;
1203
	}
1204
1205
	/**
1206
	 * Get the Gravity Forms form ID connected to a View
1207
	 *
1208
	 * @param int $view_id The ID of the View to get the connected form of
1209
	 *
1210
	 * @return false|string ID of the connected Form, if exists. Empty string if not. False if not the View ID isn't valid.
1211
	 */
1212 1
	public static function get_meta_form_id( $view_id ) {
1213 1
		return get_post_meta( $view_id, '_gravityview_form_id', true );
1214
	}
1215
1216
	/**
1217
	 * Get the template ID (`list`, `table`, `datatables`, `map`) for a View
1218
	 *
1219
	 * @see GravityView_Template::template_id
1220
	 *
1221
	 * @param int $view_id The ID of the View to get the layout of
1222
	 *
1223
	 * @return string GravityView_Template::template_id value. Empty string if not.
1224
	 */
1225 181
	public static function get_meta_template_id( $view_id ) {
1226 181
		return get_post_meta( $view_id, '_gravityview_directory_template', true );
1227
	}
1228
1229
1230
	/**
1231
	 * Get all the settings for a View
1232
	 *
1233
	 * @uses  \GV\View_Settings::defaults() Parses the settings with the plugin defaults as backups.
1234
	 * @param  int $post_id View ID
1235
	 * @return array          Associative array of settings with plugin defaults used if not set by the View
1236
	 */
1237 180
	public static function get_template_settings( $post_id ) {
1238
1239 180
		$settings = get_post_meta( $post_id, '_gravityview_template_settings', true );
1240
1241 180
		if ( class_exists( '\GV\View_Settings' ) ) {
1242
1243 180
			return wp_parse_args( (array)$settings, \GV\View_Settings::defaults() );
1244
1245
		}
1246
1247
		// Backup, in case GravityView_View_Data isn't loaded yet.
1248
		return $settings;
1249
	}
1250
1251
	/**
1252
	 * Get the setting for a View
1253
	 *
1254
	 * If the setting isn't set by the View, it returns the plugin default.
1255
	 *
1256
	 * @param  int $post_id View ID
1257
	 * @param  string $key     Key for the setting
1258
	 * @return mixed|null          Setting value, or NULL if not set.
1259
	 */
1260 1
	public static function get_template_setting( $post_id, $key ) {
1261
1262 1
		$settings = self::get_template_settings( $post_id );
1263
1264 1
		if ( isset( $settings[ $key ] ) ) {
1265 1
			return $settings[ $key ];
1266
		}
1267
1268
		return null;
1269
	}
1270
1271
	/**
1272
	 * Get the field configuration for the View
1273
	 *
1274
	 * array(
1275
	 *
1276
	 * 	[other zones]
1277
	 *
1278
	 * 	'directory_list-title' => array(
1279
	 *
1280
	 *   	[other fields]
1281
	 *
1282
	 *  	'5372653f25d44' => array(
1283
	 *  		'id' => string '9' (length=1)
1284
	 *  		'label' => string 'Screenshots' (length=11)
1285
	 *			'show_label' => string '1' (length=1)
1286
	 *			'custom_label' => string '' (length=0)
1287
	 *			'custom_class' => string 'gv-gallery' (length=10)
1288
	 * 			'only_loggedin' => string '0' (length=1)
1289
	 *			'only_loggedin_cap' => string 'read' (length=4)
1290
	 *  	)
1291
	 *
1292
	 * 		[other fields]
1293
	 *  )
1294
	 *
1295
	 * 	[other zones]
1296
	 * )
1297
	 *
1298
	 * @since 1.17.4 Added $apply_filter parameter
1299
	 *
1300
	 * @param  int $post_id View ID
1301
	 * @param  bool $apply_filter Whether to apply the `gravityview/configuration/fields` filter [Default: true]
1302
	 * @return array          Multi-array of fields with first level being the field zones. See code comment.
1303
	 */
1304 1
	public static function get_directory_fields( $post_id, $apply_filter = true ) {
1305 1
		$fields = get_post_meta( $post_id, '_gravityview_directory_fields', true );
1306
1307 1
		if ( $apply_filter ) {
1308
			/**
1309
			 * @filter `gravityview/configuration/fields` Filter the View fields' configuration array
1310
			 * @since 1.6.5
1311
			 *
1312
			 * @param $fields array Multi-array of fields with first level being the field zones
1313
			 * @param $post_id int Post ID
1314
			 */
1315 1
			$fields = apply_filters( 'gravityview/configuration/fields', $fields, $post_id );
1316
1317
			/**
1318
			 * @filter `gravityview/view/configuration/fields` Filter the View fields' configuration array.
1319
			 * @since 2.0
1320
			 *
1321
			 * @param array $fields Multi-array of fields with first level being the field zones.
1322
			 * @param \GV\View $view The View the fields are being pulled for.
1323
			 */
1324 1
			$fields = apply_filters( 'gravityview/view/configuration/fields', $fields, \GV\View::by_id( $post_id ) );
1325
		}
1326
1327 1
		return $fields;
1328
	}
1329
1330
	/**
1331
	 * Get the widget configuration for a View
1332
	 *
1333
	 * @param int $view_id View ID
1334
	 * @param bool $json_decode Whether to JSON-decode the widget values. Default: `false`
1335
	 *
1336
	 * @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"
1337
	 */
1338
	public static function get_directory_widgets( $view_id, $json_decode = false ) {
1339
1340
		$view_widgets = get_post_meta( $view_id, '_gravityview_directory_widgets', true );
1341
1342
		$defaults = array(
1343
			'header_top' => array(),
1344
			'header_left' => array(),
1345
			'header_right' => array(),
1346
			'footer_left' => array(),
1347
			'footer_right' => array(),
1348
		);
1349
1350
		$directory_widgets = wp_parse_args( $view_widgets, $defaults );
1351
1352
		if( $json_decode ) {
1353
			$directory_widgets = gv_map_deep( $directory_widgets, 'gv_maybe_json_decode' );
1354
		}
1355
1356
		return $directory_widgets;
1357
	}
1358
1359
1360
	/**
1361
	 * Render dropdown (select) with the list of sortable fields from a form ID
1362
	 *
1363
	 * @access public
1364
	 * @param  int $formid Form ID
1365
	 * @return string         html
1366
	 */
1367
	public static function get_sortable_fields( $formid, $current = '' ) {
1368
		$output = '<option value="" ' . selected( '', $current, false ).'>' . esc_html__( 'Default', 'gravityview' ) .'</option>';
1369
1370
		if ( empty( $formid ) ) {
1371
			return $output;
1372
		}
1373
1374
		$fields = self::get_sortable_fields_array( $formid );
1375
1376
		if ( ! empty( $fields ) ) {
1377
1378
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'list', 'textarea' ), null );
1379
1380
			foreach ( $fields as $id => $field ) {
1381
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
1382
					continue;
1383
				}
1384
1385
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'>'. esc_attr( $field['label'] ) .'</option>';
1386
			}
1387
		}
1388
1389
		return $output;
1390
	}
1391
1392
	/**
1393
	 *
1394
	 * @param int $formid Gravity Forms form ID
1395
	 * @param array $blacklist Field types to exclude
1396
	 *
1397
	 * @since 1.8
1398
	 *
1399
	 * @todo Get all fields, check if sortable dynamically
1400
	 *
1401
	 * @return array
1402
	 */
1403
	public static function get_sortable_fields_array( $formid, $blacklist = array( 'list', 'textarea' ) ) {
1404
1405
		// Get fields with sub-inputs and no parent
1406
		$fields = self::get_form_fields( $formid, true, false );
1407
1408
		$date_created = array(
1409
			'date_created' => array(
1410
				'type' => 'date_created',
1411
				'label' => __( 'Date Created', 'gravityview' ),
1412
			),
1413
		);
1414
1415
        $fields = $date_created + $fields;
1416
1417
		$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', $blacklist, NULL );
1418
1419
		// TODO: Convert to using array_filter
1420
		foreach( $fields as $id => $field ) {
1421
1422
			if( in_array( $field['type'], $blacklist_field_types ) ) {
1423
				unset( $fields[ $id ] );
1424
			}
1425
1426
			/**
1427
			 * Merge date and time subfields.
1428
			 */
1429
			if ( in_array( $field['type'], array( 'date', 'time' ) ) && ! empty( $field['parent'] ) ) {
1430
				$fields[ intval( $id ) ] = array(
1431
					'label' => \GV\Utils::get( $field, 'parent/label' ),
1432
					'parent' => null,
1433
					'type' => \GV\Utils::get( $field, 'parent/type' ),
1434
					'adminLabel' => \GV\Utils::get( $field, 'parent/adminLabel' ),
1435
					'adminOnly' => \GV\Utils::get( $field, 'parent/adminOnly' ),
1436
				);
1437
1438
				unset( $fields[ $id ] );
1439
			}
1440
1441
		}
1442
1443
        /**
1444
         * @filter `gravityview/common/sortable_fields` Filter the sortable fields
1445
         * @since 1.12
1446
         * @param array $fields Sub-set of GF form fields that are sortable
1447
         * @param int $formid The Gravity Forms form ID that the fields are from
1448
         */
1449
        $fields = apply_filters( 'gravityview/common/sortable_fields', $fields, $formid );
1450
1451
		return $fields;
1452
	}
1453
1454
	/**
1455
	 * Returns the GF Form field type for a certain field(id) of a form
1456
	 * @param  object $form     Gravity Forms form
1457
	 * @param  mixed $field_id Field ID or Field array
1458
	 * @return string field type
1459
	 */
1460 4
	public static function get_field_type( $form = null, $field_id = '' ) {
1461
1462 4
		if ( ! empty( $field_id ) && ! is_array( $field_id ) ) {
1463 4
			$field = self::get_field( $form, $field_id );
1464
		} else {
1465
			$field = $field_id;
1466
		}
1467
1468 4
		return class_exists( 'RGFormsModel' ) ? RGFormsModel::get_input_type( $field ) : '';
1469
1470
	}
1471
1472
1473
	/**
1474
	 * Checks if the field type is a 'numeric' field type (e.g. to be used when sorting)
1475
	 * @param  int|array  $form  form ID or form array
1476
	 * @param  int|array  $field field key or field array
1477
	 * @return boolean
1478
	 */
1479 5
	public static function is_field_numeric(  $form = null, $field = '' ) {
1480
1481 5
		if ( ! is_array( $form ) && ! is_array( $field ) ) {
1482 5
			$form = self::get_form( $form );
1483
		}
1484
1485
		// If entry meta, it's a string. Otherwise, numeric
1486 5
		if( ! is_numeric( $field ) && is_string( $field ) ) {
1487 1
			$type = $field;
1488
		} else {
1489 4
			$type = self::get_field_type( $form, $field );
1490
		}
1491
1492
		/**
1493
		 * @filter `gravityview/common/numeric_types` What types of fields are numeric?
1494
		 * @since 1.5.2
1495
		 * @param array $numeric_types Fields that are numeric. Default: `[ number, time ]`
1496
		 */
1497 5
		$numeric_types = apply_filters( 'gravityview/common/numeric_types', array( 'number', 'time' ) );
1498
1499
		// Defer to GravityView_Field setting, if the field type is registered and `is_numeric` is true
1500 5
		if( $gv_field = GravityView_Fields::get( $type ) ) {
1501 4
			if( true === $gv_field->is_numeric ) {
1502
				$numeric_types[] = $gv_field->is_numeric;
1503
			}
1504
		}
1505
1506 5
		$return = in_array( $type, $numeric_types );
1507
1508 5
		return $return;
1509
	}
1510
1511
	/**
1512
	 * Encrypt content using Javascript so that it's hidden when JS is disabled.
1513
	 *
1514
	 * This is mostly used to hide email addresses from scraper bots.
1515
	 *
1516
	 * @param string $content Content to encrypt
1517
	 * @param string $message Message shown if Javascript is disabled
1518
	 *
1519
	 * @see  https://github.com/katzwebservices/standalone-phpenkoder StandalonePHPEnkoder on Github
1520
	 *
1521
	 * @since 1.7
1522
	 *
1523
	 * @return string Content, encrypted
1524
	 */
1525
	public static function js_encrypt( $content, $message = '' ) {
1526
1527
		$output = $content;
1528
1529
		if ( ! class_exists( 'StandalonePHPEnkoder' ) ) {
1530
			include_once( GRAVITYVIEW_DIR . 'includes/lib/StandalonePHPEnkoder.php' );
1531
		}
1532
1533
		if ( class_exists( 'StandalonePHPEnkoder' ) ) {
1534
1535
			$enkoder = new StandalonePHPEnkoder;
1536
1537
			$message = empty( $message ) ? __( 'Email hidden; Javascript is required.', 'gravityview' ) : $message;
1538
1539
			/**
1540
			 * @filter `gravityview/phpenkoder/msg` Modify the message shown when Javascript is disabled and an encrypted email field is displayed
1541
			 * @since 1.7
1542
			 * @param string $message Existing message
1543
			 * @param string $content Content to encrypt
1544
			 */
1545
			$enkoder->enkode_msg = apply_filters( 'gravityview/phpenkoder/msg', $message, $content );
1546
1547
			$output = $enkoder->enkode( $content );
1548
		}
1549
1550
		return $output;
1551
	}
1552
1553
	/**
1554
	 *
1555
	 * Do the same than parse_str without max_input_vars limitation:
1556
	 * Parses $string as if it were the query string passed via a URL and sets variables in the current scope.
1557
	 * @param $string string string to parse (not altered like in the original parse_str(), use the second parameter!)
1558
	 * @param $result array  If the second parameter is present, variables are stored in this variable as array elements
1559
	 * @return bool true or false if $string is an empty string
1560
	 * @since  1.5.3
1561
	 *
1562
	 * @author rubo77 at https://gist.github.com/rubo77/6821632
1563
	 **/
1564
	public static function gv_parse_str( $string, &$result ) {
1565
		if ( empty( $string ) ) {
1566
			return false;
1567
		}
1568
1569
		$result = array();
1570
1571
		// find the pairs "name=value"
1572
		$pairs = explode( '&', $string );
1573
1574
		foreach ( $pairs as $pair ) {
1575
			// use the original parse_str() on each element
1576
			parse_str( $pair, $params );
1577
1578
			$k = key( $params );
1579
			if ( ! isset( $result[ $k ] ) ) {
1580
				$result += $params;
1581
			} elseif ( array_key_exists( $k, $params ) && is_array( $params[ $k ] ) ) {
1582
				$result[ $k ] = self::array_merge_recursive_distinct( $result[ $k ], $params[ $k ] );
1583
			}
1584
		}
1585
		return true;
1586
	}
1587
1588
1589
	/**
1590
	 * Generate an HTML anchor tag with a list of supported attributes
1591
	 *
1592
	 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a Supported attributes defined here
1593
	 * @uses esc_url_raw() to sanitize $href
1594
	 * @uses esc_attr() to sanitize $atts
1595
	 *
1596
	 * @since 1.6
1597
	 *
1598
	 * @param string $href URL of the link. Sanitized using `esc_url_raw()`
1599
	 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1600
	 * @param array|string $atts Attributes to be added to the anchor tag. Parsed by `wp_parse_args()`, sanitized using `esc_attr()`
1601
	 *
1602
	 * @return string HTML output of anchor link. If empty $href, returns NULL
1603
	 */
1604 56
	public static function get_link_html( $href = '', $anchor_text = '', $atts = array() ) {
1605
1606
		// Supported attributes for anchor tags. HREF left out intentionally.
1607
		$allowed_atts = array(
1608 56
			'href' => null, // Will override the $href argument if set
1609
			'title' => null,
1610
			'rel' => null,
1611
			'id' => null,
1612
			'class' => null,
1613
			'target' => null,
1614
			'style' => null,
1615
1616
			// Used by GravityView
1617
			'data-viewid' => null,
1618
1619
			// Not standard
1620
			'hreflang' => null,
1621
			'type' => null,
1622
			'tabindex' => null,
1623
1624
			// Deprecated HTML4 but still used
1625
			'name' => null,
1626
			'onclick' => null,
1627
			'onchange' => null,
1628
			'onkeyup' => null,
1629
1630
			// HTML5 only
1631
			'download' => null,
1632
			'media' => null,
1633
			'ping' => null,
1634
		);
1635
1636
		/**
1637
		 * @filter `gravityview/get_link/allowed_atts` Modify the attributes that are allowed to be used in generating links
1638
		 * @param array $allowed_atts Array of attributes allowed
1639
		 */
1640 56
		$allowed_atts = apply_filters( 'gravityview/get_link/allowed_atts', $allowed_atts );
1641
1642
		// Make sure the attributes are formatted as array
1643 56
		$passed_atts = wp_parse_args( $atts );
1644
1645
		// Make sure the allowed attributes are only the ones in the $allowed_atts list
1646 56
		$final_atts = shortcode_atts( $allowed_atts, $passed_atts );
1647
1648
		// Remove attributes with empty values
1649 56
		$final_atts = array_filter( $final_atts );
1650
1651
		// If the href wasn't passed as an attribute, use the value passed to the function
1652 56
		if ( empty( $final_atts['href'] ) && ! empty( $href ) ) {
1653 56
			$final_atts['href'] = $href;
1654
		}
1655
1656 56
		$final_atts['href'] = esc_url_raw( $href );
1657
1658
		/**
1659
		 * Fix potential security issue with target=_blank
1660
		 * @see https://dev.to/ben/the-targetblank-vulnerability-by-example
1661
		 */
1662 56
		if( '_blank' === \GV\Utils::get( $final_atts, 'target' ) ) {
1663 4
			$final_atts['rel'] = trim( \GV\Utils::get( $final_atts, 'rel', '' ) . ' noopener noreferrer' );
1664
		}
1665
1666
		// Sort the attributes alphabetically, to help testing
1667 56
		ksort( $final_atts );
1668
1669
		// For each attribute, generate the code
1670 56
		$output = '';
1671 56
		foreach ( $final_atts as $attr => $value ) {
1672 56
			$output .= sprintf( ' %s="%s"', $attr, esc_attr( $value ) );
1673
		}
1674
1675 56
		if( '' !== $output ) {
1676 56
			$output = '<a' . $output . '>' . $anchor_text . '</a>';
1677
		}
1678
1679 56
		return $output;
1680
	}
1681
1682
	/**
1683
	 * array_merge_recursive does indeed merge arrays, but it converts values with duplicate
1684
	 * keys to arrays rather than overwriting the value in the first array with the duplicate
1685
	 * value in the second array, as array_merge does.
1686
	 *
1687
	 * @see http://php.net/manual/en/function.array-merge-recursive.php
1688
	 *
1689
	 * @since  1.5.3
1690
	 * @param array $array1
1691
	 * @param array $array2
1692
	 * @return array
1693
	 * @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
1694
	 * @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
1695
	 */
1696
	public static function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1697
		$merged = $array1;
1698
		foreach ( $array2 as $key => $value ) {
1699
			if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
1700
				$merged[ $key ] = self::array_merge_recursive_distinct( $merged[ $key ], $value );
1701
			} else if ( is_numeric( $key ) && isset( $merged[ $key ] ) ) {
1702
				$merged[] = $value;
1703
			} else {
1704
				$merged[ $key ] = $value;
1705
			}
1706
		}
1707
1708
		return $merged;
1709
	}
1710
1711
	/**
1712
	 * Get WordPress users with reasonable limits set
1713
	 *
1714
	 * @param string $context Where are we using this information (e.g. change_entry_creator, search_widget ..)
1715
	 * @param array $args Arguments to modify the user query. See get_users() {@since 1.14}
1716
	 * @return array Array of WP_User objects.
1717
	 */
1718 1
	public static function get_users( $context = 'change_entry_creator', $args = array() ) {
1719
1720
		$default_args = array(
1721 1
			'number' => 2000,
1722
			'orderby' => 'display_name',
1723
			'order' => 'ASC',
1724
			'fields' => array( 'ID', 'display_name', 'user_login', 'user_nicename' )
1725
		);
1726
1727
		// Merge in the passed arg
1728 1
		$get_users_settings = wp_parse_args( $args, $default_args );
1729
1730
		/**
1731
		 * @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
1732
		 * `$context` is where are we using this information (e.g. change_entry_creator, search_widget ..)
1733
		 * @param array $settings Settings array, with `number` key defining the # of users to display
1734
		 */
1735 1
		$get_users_settings = apply_filters( 'gravityview/get_users/'. $context, apply_filters( 'gravityview_change_entry_creator_user_parameters', $get_users_settings ) );
1736
1737 1
		return get_users( $get_users_settings );
1738
	}
1739
1740
1741
    /**
1742
     * Display updated/error notice
1743
     *
1744
     * @since 1.19.2 Added $cap and $object_id parameters
1745
     *
1746
     * @param string $notice text/HTML of notice
1747
     * @param string $class CSS class for notice (`updated` or `error`)
1748
     * @param string $cap [Optional] Define a capability required to show a notice. If not set, displays to all caps.
1749
     *
1750
     * @return string
1751
     */
1752 29
    public static function generate_notice( $notice, $class = '', $cap = '', $object_id = null ) {
1753
1754
    	// If $cap is defined, only show notice if user has capability
1755 29
    	if( $cap && ! GVCommon::has_cap( $cap, $object_id ) ) {
1756 8
    		return '';
1757
	    }
1758
1759 23
        return '<div class="gv-notice '.gravityview_sanitize_html_class( $class ) .'">'. $notice .'</div>';
1760
    }
1761
1762
	/**
1763
	 * Inspired on \GFCommon::encode_shortcodes, reverse the encoding by replacing the ascii characters by the shortcode brackets
1764
	 * @since 1.16.5
1765
	 * @param string $string Input string to decode
1766
	 * @return string $string Output string
1767
	 */
1768
	public static function decode_shortcodes( $string ) {
1769
		$replace = array( '[', ']', '"' );
1770
		$find = array( '&#91;', '&#93;', '&quot;' );
1771
		$string = str_replace( $find, $replace, $string );
1772
1773
		return $string;
1774
	}
1775
1776
1777
	/**
1778
	 * Send email using GFCommon::send_email()
1779
	 *
1780
	 * @since 1.17
1781
	 *
1782
	 * @see GFCommon::send_email This just makes the method public
1783
	 *
1784
	 * @param string $from               Sender address (required)
1785
	 * @param string $to                 Recipient address (required)
1786
	 * @param string $bcc                BCC recipients (required)
1787
	 * @param string $reply_to           Reply-to address (required)
1788
	 * @param string $subject            Subject line (required)
1789
	 * @param string $message            Message body (required)
1790
	 * @param string $from_name          Displayed name of the sender
1791
	 * @param string $message_format     If "html", sent text as `text/html`. Otherwise, `text/plain`. Default: "html".
1792
	 * @param string|array $attachments  Optional. Files to attach. {@see wp_mail()} for usage. Default: "".
1793
	 * @param array|false $entry         Gravity Forms entry array, related to the email. Default: false.
1794
	 * @param array|false $notification  Gravity Forms notification that triggered the email. {@see GFCommon::send_notification}. Default:false.
1795
	 */
1796
	public static function send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name = '', $message_format = 'html', $attachments = '', $entry = false, $notification = false ) {
1797
1798
		$SendEmail = new ReflectionMethod( 'GFCommon', 'send_email' );
1799
1800
		// It was private; let's make it public
1801
		$SendEmail->setAccessible( true );
1802
1803
		// Required: $from, $to, $bcc, $replyTo, $subject, $message
1804
		// Optional: $from_name, $message_format, $attachments, $lead, $notification
1805
		$SendEmail->invoke( new GFCommon, $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, $attachments, $entry, $notification );
1806
	}
1807
1808
1809
} //end class
1810
1811
1812
/**
1813
 * Generate an HTML anchor tag with a list of supported attributes
1814
 *
1815
 * @see GVCommon::get_link_html()
1816
 *
1817
 * @since 1.6
1818
 *
1819
 * @param string $href URL of the link.
1820
 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1821
 * @param array|string $atts Attributes to be added to the anchor tag
1822
 *
1823
 * @return string HTML output of anchor link. If empty $href, returns NULL
1824
 */
1825
function gravityview_get_link( $href = '', $anchor_text = '', $atts = array() ) {
1826 55
	return GVCommon::get_link_html( $href, $anchor_text, $atts );
1827
}
1828