Completed
Push — master ( 1fd6be...155036 )
by Zack
13s
created

GVCommon::get_connected_views()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 2
dl 0
loc 15
ccs 8
cts 8
cp 1
crap 1
rs 9.4285
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 3
	public static function get_form( $form_id ) {
29 3
		if ( empty( $form_id ) ) {
30
			return false;
31
		}
32
33
		// Only get_form_meta is cached. ::facepalm::
34 3
		if ( class_exists( 'RGFormsModel' ) ) {
35 3
			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 5
	public static function has_cap( $caps = '', $object_id = null, $user_id = null ) {
59 5
		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
90
	 * @since  TODO Added $args array
91
	 *
92
	 * @param array $args Pass custom array of args, formatted as if for `get_posts()`
93
	 *
94
	 * @return array Array of Views as `WP_Post`. Empty array if none found.
95
	 */
96
	public static function get_all_views( $args = array() ) {
97
98
		$default_params = array(
99
			'post_type' => 'gravityview',
100
			'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...
101
			'post_status' => 'publish',
102
		);
103
104
		$params = wp_parse_args( $args, $default_params );
105
106
		/**
107
		 * @filter `gravityview/get_all_views/params` Modify the parameters sent to get all views.
108
		 * @param[in,out]  array $params Array of parameters to pass to `get_posts()`
109
		 */
110
		$views_params = apply_filters( 'gravityview/get_all_views/params', $params );
111
112
		$views = get_posts( $views_params );
113
114
		return $views;
115
	}
116
117
118
	/**
119
	 * Get the form array for an entry based only on the entry ID
120
	 * @param  int|string $entry_slug Entry slug
121
	 * @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
122
	 */
123
	public static function get_form_from_entry_id( $entry_slug ) {
124
125
		$entry = self::get_entry( $entry_slug, true, false );
126
127
		$form = false;
128
129
		if( $entry ) {
130
			$form = GFAPI::get_form( $entry['form_id'] );
131
		}
132
133
		return $form;
134
	}
135
136
	/**
137
	 * Check whether a form has product fields
138
	 *
139
	 * @since 1.16
140
	 * @since 1.20 Refactored the field types to get_product_field_types() method
141
	 *
142
	 * @param array $form Gravity Forms form array
143
	 *
144
	 * @return bool|GF_Field[]
145
	 */
146 3
	public static function has_product_field( $form = array() ) {
147
148 3
		$product_fields = self::get_product_field_types();
149
150 3
		$fields = GFAPI::get_fields_by_type( $form, $product_fields );
151
152 3
		return empty( $fields ) ? false : $fields;
153
	}
154
155
	/**
156
	 * Return array of product field types
157
	 *
158
	 * Modify the value using the `gform_product_field_types` filter
159
	 *
160
	 * @since 1.20
161
	 *
162
	 * @return array
163
	 */
164 4
	public static function get_product_field_types() {
165
166 4
		$product_fields = apply_filters( 'gform_product_field_types', array( 'option', 'quantity', 'product', 'total', 'shipping', 'calculation', 'price', 'hiddenproduct', 'singleproduct', 'singleshipping' ) );
167
168 4
		return $product_fields;
169
	}
170
171
	/**
172
	 * Check if an entry has transaction data
173
	 *
174
	 * Checks the following keys to see if they are set: 'payment_status', 'payment_date', 'transaction_id', 'payment_amount', 'payment_method'
175
	 *
176
	 * @since 1.20
177
	 *
178
	 * @param array $entry Gravity Forms entry array
179
	 *
180
	 * @return bool True: Entry has metadata suggesting it has communicated with a payment gateway; False: it does not have that data.
181
	 */
182 4
	public static function entry_has_transaction_data( $entry = array() ) {
183
184 4
		if ( ! is_array( $entry ) ) {
185 1
			return false;
186
		}
187
188 4
		$has_transaction_data = false;
189
190 4
		$payment_meta = array( 'payment_status', 'payment_date', 'transaction_id', 'payment_amount', 'payment_method' );
191
192 4
		foreach ( $payment_meta as $meta ) {
193
194 4
			$has_transaction_data = rgar( $entry, $meta, false );
195
196 4
			if( ! empty( $has_transaction_data ) ) {
197 4
				break;
198
			}
199
		}
200
201 4
		return (bool) $has_transaction_data;
202
	}
203
204
	/**
205
	 * Get the entry ID from the entry slug, which may or may not be the entry ID
206
	 *
207
	 * @since  1.5.2
208
	 * @param  string $slug The entry slug, as returned by GravityView_API::get_entry_slug()
209
	 * @return int|null       The entry ID, if exists; `NULL` if not
210
	 */
211
	public static function get_entry_id_from_slug( $slug ) {
212
		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...
213
214
		$search_criteria = array(
215
			'field_filters' => array(
216
				array(
217
					'key' => 'gravityview_unique_id', // Search the meta values
218
					'value' => $slug,
219
					'operator' => 'is',
220
					'type' => 'meta',
221
				),
222
			)
223
		);
224
225
		// Limit to one for speed
226
		$paging = array(
227
			'page_size' => 1,
228
		);
229
230
		/**
231
		 * @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.
232
		 * @since 1.17.2
233
		 * @param int $form_id ID of the form to search. Default: `0` (searches all forms)
234
		 */
235
		$form_id = apply_filters( 'gravityview/common/get_entry_id_from_slug/form_id', 0 );
236
237
		$results = GFAPI::get_entries( intval( $form_id ), $search_criteria, null, $paging );
238
239
		$result = ( ! empty( $results ) && ! empty( $results[0]['id'] ) ) ? $results[0]['id'] : null;
240
241
		return $result;
242
	}
243
244
	/**
245
	 * Alias of GFAPI::get_forms()
246
	 *
247
	 * @see GFAPI::get_forms()
248
	 *
249
	 * @since 1.19 Allow "any" $active status option
250
	 *
251
	 * @param bool|string $active Status of forms. Use `any` to get array of forms with any status. Default: `true`
252
	 * @param bool $trash Include forms in trash? Default: `false`
253
	 *
254
	 * @return array Empty array if GFAPI class isn't available or no forms. Otherwise, the array of Forms
255
	 */
256 1
	public static function get_forms(  $active = true, $trash = false ) {
257 1
		$forms = array();
258 1
		if ( class_exists( 'GFAPI' ) ) {
259 1
			if( 'any' === $active ) {
260
				$active_forms = GFAPI::get_forms( true, $trash );
261
				$inactive_forms = GFAPI::get_forms( false, $trash );
262
				$forms = array_merge( array_filter( $active_forms ), array_filter( $inactive_forms ) );
263
			} else {
264 1
				$forms = GFAPI::get_forms( $active, $trash );
265
			}
266
		}
267 1
		return $forms;
268
	}
269
270
	/**
271
	 * Return array of fields' id and label, for a given Form ID
272
	 *
273
	 * @access public
274
	 * @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...
275
	 * @param bool $add_default_properties
276
	 * @param bool $include_parent_field
277
	 * @return array
278
	 */
279
	public static function get_form_fields( $form = '', $add_default_properties = false, $include_parent_field = true ) {
280
281
		if ( ! is_array( $form ) ) {
282
			$form = self::get_form( $form );
283
		}
284
285
		$fields = array();
286
		$has_product_fields = false;
287
		$has_post_fields = false;
288
289
		if ( $form ) {
290
			foreach ( $form['fields'] as $field ) {
291
				if ( $include_parent_field || empty( $field['inputs'] ) ) {
292
					$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...
293
						'label' => rgar( $field, 'label' ),
294
						'parent' => null,
295
						'type' => rgar( $field, 'type' ),
296
						'adminLabel' => rgar( $field, 'adminLabel' ),
297
						'adminOnly' => rgar( $field, 'adminOnly' ),
298
					);
299
				}
300
301
				if ( $add_default_properties && ! empty( $field['inputs'] ) ) {
302
					foreach ( $field['inputs'] as $input ) {
303
304
						if( ! empty( $input['isHidden'] ) ) {
305
							continue;
306
						}
307
308
						/**
309
                         * @hack
310
                         * In case of email/email confirmation, the input for email has the same id as the parent field
311
                         */
312
						if( 'email' === $field['type'] && false === strpos( $input['id'], '.' ) ) {
313
                            continue;
314
                        }
315
						$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...
316
							'label' => rgar( $input, 'label' ),
317
							'customLabel' => rgar( $input, 'customLabel' ),
318
							'parent' => $field,
319
							'type' => rgar( $field, 'type' ),
320
							'adminLabel' => rgar( $field, 'adminLabel' ),
321
							'adminOnly' => rgar( $field, 'adminOnly' ),
322
						);
323
					}
324
				}
325
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
326
327
				if( GFCommon::is_product_field( $field['type'] ) ){
328
					$has_product_fields = true;
329
				}
330
331
				if ( GFCommon::is_post_field( $field ) ) {
332
					$has_post_fields = true;
333
				}
334
			}
335
		}
336
337
		/**
338
		 * @since 1.7
339
		 */
340
		if ( $has_post_fields ) {
341
			$fields['post_id'] = array(
342
				'label' => __( 'Post ID', 'gravityview' ),
343
				'type' => 'post_id',
344
			);
345
		}
346
347
		if ( $has_product_fields ) {
348
349
			$payment_fields = GravityView_Fields::get_all( 'pricing' );
350
351
			foreach ( $payment_fields as $payment_field ) {
352
353
				// Either the field exists ($fields['shipping']) or the form explicitly contains a `shipping` field with numeric key
354
				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...
355
					continue;
356
				}
357
358
				$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...
359
					'label' => $payment_field->label,
360
					'desc' => $payment_field->description,
361
					'type' => $payment_field->name,
362
				);
363
			}
364
		}
365
366
		/**
367
		 * @filter `gravityview/common/get_form_fields` Modify the form fields shown in the Add Field field picker.
368
		 * @since 1.17
369
		 * @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`
370
		 * @param array $form GF Form array
371
		 * @param bool $include_parent_field Whether to include the parent field when getting a field with inputs
372
		 */
373
		$fields = apply_filters( 'gravityview/common/get_form_fields', $fields, $form, $include_parent_field );
374
375
		return $fields;
376
377
	}
378
379
	/**
380
	 * get extra fields from entry meta
381
	 * @param  string $form_id (default: '')
382
	 * @return array
383
	 */
384
	public static function get_entry_meta( $form_id, $only_default_column = true ) {
385
386
		$extra_fields = GFFormsModel::get_entry_meta( $form_id );
387
388
		$fields = array();
389
390
		foreach ( $extra_fields as $key => $field ){
391
			if ( ! empty( $only_default_column ) && ! empty( $field['is_default_column'] ) ) {
392
				$fields[ $key ] = array( 'label' => $field['label'], 'type' => 'entry_meta' );
393
			}
394
		}
395
396
		return $fields;
397
	}
398
399
400
	/**
401
	 * Wrapper for the Gravity Forms GFFormsModel::search_lead_ids() method
402
	 *
403
	 * @see  GFEntryList::leads_page()
404
	 * @param  int $form_id ID of the Gravity Forms form
405
	 * @since  1.1.6
406
	 * @return array|void          Array of entry IDs. Void if Gravity Forms isn't active.
407
	 */
408
	public static function get_entry_ids( $form_id, $search_criteria = array() ) {
409
410
		if ( ! class_exists( 'GFFormsModel' ) ) {
411
			return;
412
		}
413
414
		return GFFormsModel::search_lead_ids( $form_id, $search_criteria );
415
	}
416
417
	/**
418
	 * Calculates the Search Criteria used on the self::get_entries / self::get_entry methods
419
	 *
420
	 * @since 1.7.4
421
	 *
422
	 * @param array $passed_criteria array Input Criteria (search_criteria, sorting, paging)
423
	 * @param array $form_ids array Gravity Forms form IDs
424
	 * @return array
425
	 */
426 2
	public static function calculate_get_entries_criteria( $passed_criteria = array(), $form_ids = array() ) {
427
428
		$search_criteria_defaults = array(
429 2
			'search_criteria' => null,
430
			'sorting' => null,
431
			'paging' => null,
432 2
			'cache' => (isset( $passed_criteria['cache'] ) ? (bool) $passed_criteria['cache'] : true),
433
		);
434
435 2
		$criteria = wp_parse_args( $passed_criteria, $search_criteria_defaults );
436
437 2
		if ( ! empty( $criteria['search_criteria']['field_filters'] ) ) {
438 1
			foreach ( $criteria['search_criteria']['field_filters'] as &$filter ) {
439
440 1
				if ( ! is_array( $filter ) ) {
441 1
					continue;
442
				}
443
444
				// By default, we want searches to be wildcard for each field.
445 1
				$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 1
				$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
453
			}
454
455
			// don't send just the [mode] without any field filter.
456 1
			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 2
		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 2
			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
					do_action( 'gravityview_log_error', '[filter_get_entries_criteria] '.$key.' Date format not valid:', $criteria['search_criteria'][ $key ] );
480
481
					// If it's an invalid date, unset it. Gravity Forms freaks out otherwise.
482 2
					unset( $criteria['search_criteria'][ $key ] );
483
				}
484
			}
485
		}
486
487 2
		if ( ! GravityView_frontend::getInstance()->getSingleEntry() ) {
488
			/** GravityView_View_Data::getInstance() has a side-effect :( and not one, so we can't let it run under some circumstances. */
489 2
			if ( defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) ) {
490 2
				$multiple_original = gravityview()->views->count() > 1;
0 ignored issues
show
Documentation introduced by
The property views does not exist on object<GV\Core>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
491 2
				GravityView_View_Data::getInstance(); /** Yes, those side-effects have to kick in. */
492
				/** This weird state only happens in tests, when we play around and reset the $instance... */
493
			} else {
494
				/** Deprecated, do not use has_multiple_views() anymore. Thanks. */
495
				$multiple_original = 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...
496
			}
497
		}
498
499
		// Calculate the context view id and send it to the advanced filter
500 2
		if ( GravityView_frontend::getInstance()->getSingleEntry() ) {
501 1
			$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
502 2
		} elseif ( $multiple_original ) {
0 ignored issues
show
Bug introduced by
The variable $multiple_original does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
503 1
			$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
504 2
		} elseif ( 'delete' === GFForms::get( 'action' ) ) {
505 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...
506 2
		} elseif( !isset( $criteria['context_view_id'] ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
507
            // Prevent overriding the Context View ID: Some widgets could set the context_view_id (e.g. Recent Entries widget)
508 2
			$criteria['context_view_id'] = null;
509
		}
510
511
		/**
512
		 * @filter `gravityview_search_criteria` Apply final criteria filter (Used by the Advanced Filter extension)
513
		 * @param array $criteria Search criteria used by GravityView
514
		 * @param array $form_ids Forms to search
515
		 * @param int $view_id ID of the view being used to search
516
		 */
517 2
		$criteria = apply_filters( 'gravityview_search_criteria', $criteria, $form_ids, $criteria['context_view_id'] );
518
519 2
		return (array)$criteria;
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
520
	}
521
522
523
	/**
524
	 * Retrieve entries given search, sort, paging criteria
525
	 *
526
	 * @see  GFAPI::get_entries()
527
	 * @see GFFormsModel::get_field_filters_where()
528
	 * @access public
529
	 * @param int|array $form_ids The ID of the form or an array IDs of the Forms. Zero for all forms.
530
	 * @param mixed $passed_criteria (default: null)
531
	 * @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)
532
	 * @return mixed False: Error fetching entries. Array: Multi-dimensional array of Gravity Forms entry arrays
533
	 */
534
	public static function get_entries( $form_ids = null, $passed_criteria = null, &$total = null ) {
535
536
		// Filter the criteria before query (includes Adv Filter)
537
		$criteria = self::calculate_get_entries_criteria( $passed_criteria, $form_ids );
538
539
		do_action( 'gravityview_log_debug', '[gravityview_get_entries] Final Parameters', $criteria );
540
541
		// Return value
542
		$return = null;
543
544
		/** Reduce # of database calls */
545
		add_filter( 'gform_is_encrypted_field', '__return_false' );
546
547
		if ( ! empty( $criteria['cache'] ) ) {
548
549
			$Cache = new GravityView_Cache( $form_ids, $criteria );
550
551
			if ( $entries = $Cache->get() ) {
552
553
				// Still update the total count when using cached results
554
				if ( ! is_null( $total ) ) {
555
					$total = GFAPI::count_entries( $form_ids, $criteria['search_criteria'] );
556
				}
557
558
				$return = $entries;
559
			}
560
		}
561
562
		if ( is_null( $return ) && class_exists( 'GFAPI' ) && ( is_numeric( $form_ids ) || is_array( $form_ids ) ) ) {
563
564
			/**
565
			 * @filter `gravityview_pre_get_entries` Define entries to be used before GFAPI::get_entries() is called
566
			 * @since 1.14
567
			 * @param  null $return If you want to override GFAPI::get_entries() and define entries yourself, tap in here.
568
			 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
569
			 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
570
			 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
571
			 * @deprecated
572
			 */
573
			$entries = apply_filters( 'gravityview_before_get_entries', null, $criteria, $passed_criteria, $total );
574
575
			// No entries returned from gravityview_before_get_entries
576
			if( is_null( $entries ) ) {
577
578
				$entries = GFAPI::get_entries( $form_ids, $criteria['search_criteria'], $criteria['sorting'], $criteria['paging'], $total );
579
580
				if ( is_wp_error( $entries ) ) {
581
					do_action( 'gravityview_log_error', $entries->get_error_message(), $entries );
582
583
					/** Remove filter added above */
584
					remove_filter( 'gform_is_encrypted_field', '__return_false' );
585
					return false;
586
				}
587
			}
588
589
			if ( ! empty( $criteria['cache'] ) && isset( $Cache ) ) {
590
591
				// Cache results
592
				$Cache->set( $entries, 'entries' );
593
594
			}
595
596
			$return = $entries;
597
		}
598
599
		/** Remove filter added above */
600
		remove_filter( 'gform_is_encrypted_field', '__return_false' );
601
602
		/**
603
		 * @filter `gravityview_entries` Modify the array of entries returned to GravityView after it has been fetched from the cache or from `GFAPI::get_entries()`.
604
		 * @param  array|null $entries Array of entries as returned by the cache or by `GFAPI::get_entries()`
605
		 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
606
		 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
607
		 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
608
		 */
609
		$return = apply_filters( 'gravityview_entries', $return, $criteria, $passed_criteria, $total );
610
611
		return $return;
612
	}
613
614
615
	/**
616
	 * Get the entry ID from a string that may be the Entry ID or the Entry Slug
617
	 *
618
	 * @since TODO
619
	 *
620
	 * @param string $entry_id_or_slug The ID or slug of an entry.
621
	 * @param bool $force_allow_ids Whether to force allowing getting the ID of an entry, even if custom slugs are enabled
622
	 *
623
	 * @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.
624
	 */
625 1
	public static function get_entry_id( $entry_id_or_slug = '', $force_allow_ids = false ) {
626
627 1
		$entry_id = false;
628
629
		/**
630
		 * @filter `gravityview_custom_entry_slug` Whether to enable and use custom entry slugs.
631
		 * @param boolean True: Allow for slugs based on entry values. False: always use entry IDs (default)
632
		 */
633 1
		$custom_slug = apply_filters( 'gravityview_custom_entry_slug', false );
634
635
		/**
636
		 * @filter `gravityview_custom_entry_slug_allow_id` When using a custom slug, allow access to the entry using the original slug (the Entry ID).
637
		 * - If disabled (default), only allow access to an entry using the custom slug value.  (example: `/entry/custom-slug/` NOT `/entry/123/`)
638
		 * - If enabled, you could access using the custom slug OR the entry id (example: `/entry/custom-slug/` OR `/entry/123/`)
639
		 * @param boolean $custom_slug_id_access True: allow accessing the slug by ID; False: only use the slug passed to the method.
640
		 */
641 1
		$custom_slug_id_access = $force_allow_ids || apply_filters( 'gravityview_custom_entry_slug_allow_id', false );
642
643
		/**
644
		 * If we're using custom entry slugs, we do a meta value search
645
		 * instead of doing a straightup ID search.
646
		 */
647 1
		if ( $custom_slug ) {
648
			// Search for IDs matching $entry_id_or_slug
649
			$entry_id = self::get_entry_id_from_slug( $entry_id_or_slug );
650
		}
651
652
		// If custom slug is off, search using the entry ID
653
		// ID allow ID access is on, also use entry ID as a backup
654 1
		if ( false === $custom_slug || true === $custom_slug_id_access ) {
655
			// Search for IDs matching $entry_slug
656 1
			$entry_id = $entry_id_or_slug;
657
		}
658
659 1
		return $entry_id;
660
	}
661
662
	/**
663
	 * Return a single entry object
664
	 *
665
	 * 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()`
666
	 *
667
	 * @access public
668
	 * @param string|int $entry_slug Either entry ID or entry slug string
669
	 * @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.
670
	 * @param boolean $check_entry_display Check whether the entry is visible for the current View configuration. Default: true. {@since 1.14}
671
	 * @return array|boolean
672
	 */
673 1
	public static function get_entry( $entry_slug, $force_allow_ids = false, $check_entry_display = true ) {
674
675 1
		if ( class_exists( 'GFAPI' ) && ! empty( $entry_slug ) ) {
676
677 1
			$entry_id = self::get_entry_id( $entry_slug, $force_allow_ids );
678
679 1
			if ( empty( $entry_id ) ) {
680
				return false;
681
			}
682
683
			// fetch the entry
684 1
			$entry = GFAPI::get_entry( $entry_id );
685
686
			/**
687
			 * @filter `gravityview/common/get_entry/check_entry_display` Override whether to check entry display rules against filters
688
			 * @since 1.16.2
689
			 * @param bool $check_entry_display Check whether the entry is visible for the current View configuration. Default: true.
690
			 * @param array $entry Gravity Forms entry array
691
			 */
692 1
			$check_entry_display = apply_filters( 'gravityview/common/get_entry/check_entry_display', $check_entry_display, $entry );
693
694 1
			if( $check_entry_display ) {
695
				// Is the entry allowed
696 1
				$entry = self::check_entry_display( $entry );
697
			}
698
699 1
			if( is_wp_error( $entry ) ) {
700
				do_action( 'gravityview_log_error', __METHOD__ . ': ' . $entry->get_error_message() );
701
				return false;
702
			}
703
704 1
			return $entry;
705
		}
706
707
		return false;
708
	}
709
710
	/**
711
	 * Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including:
712
	 * 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals',
713
	 * 'not_contains', 'in', and 'not_in'
714
	 *
715
	 * @since 1.13 You can define context, which displays/hides based on what's being displayed (single, multiple, edit)
716
	 * @since 1.22.1 Added 'in' and 'not_in' for JSON-encoded array values, serialized non-strings
717
	 *
718
	 * @see http://docs.gravityview.co/article/252-gvlogic-shortcode
719
	 * @uses GFFormsModel::matches_operation
720
	 * @since 1.7.5
721
	 *
722
	 * @param string $val1 Left side of comparison
723
	 * @param string $val2 Right side of comparison
724
	 * @param string $operation Type of comparison
725
	 *
726
	 * @return bool True: matches, false: not matches
727
	 */
728 3
	public static function matches_operation( $val1, $val2, $operation ) {
729
730 3
		$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...
731
732
		// Only process strings
733 3
		$val1 = ! is_string( $val1 ) ? $json_function( $val1 ) : $val1;
734 3
		$val2 = ! is_string( $val2 ) ? $json_function( $val2 ) : $val2;
735
736 3
		$value = false;
737
738 3
		if( 'context' === $val1 ) {
739
740
			$matching_contexts = array( $val2 );
741
742
			// We allow for non-standard contexts.
743
			switch( $val2 ) {
744
				// Check for either single or edit
745
				case 'singular':
746
					$matching_contexts = array( 'single', 'edit' );
747
					break;
748
				// Use multiple as alias for directory for consistency
749
				case 'multiple':
750
					$matching_contexts = array( 'directory' );
751
					break;
752
			}
753
754
			$val1 = in_array( gravityview_get_context(), $matching_contexts ) ? $val2 : false;
755
		}
756
757
		switch ( $operation ) {
758 3
			case 'equals':
759 1
				$value = self::matches_operation( $val1, $val2, 'is' );
760 1
				break;
761 3
			case 'greater_than_or_is':
762 3
			case 'greater_than_or_equals':
763 1
				$is    = self::matches_operation( $val1, $val2, 'is' );
764 1
				$gt    = self::matches_operation( $val1, $val2, 'greater_than' );
765 1
				$value = ( $is || $gt );
766 1
				break;
767 3
			case 'less_than_or_is':
768 3
			case 'less_than_or_equals':
769 1
				$is    = self::matches_operation( $val1, $val2, 'is' );
770 1
				$gt    = self::matches_operation( $val1, $val2, 'less_than' );
771 1
				$value = ( $is || $gt );
772 1
				break;
773 3
			case 'not_contains':
774 1
				$contains = self::matches_operation( $val1, $val2, 'contains' );
775 1
				$value    = ! $contains;
776 1
				break;
777
			/**
778
			 * @since 1.22.1 Handle JSON-encoded comparisons
779
			 */
780 3
			case 'in':
781 3
			case 'not_in':
782
783 1
				$json_val_1 = json_decode( $val1, true );
784 1
				$json_val_2 = json_decode( $val2, true );
785
786 1
				if( ! empty( $json_val_1 ) || ! empty( $json_val_2 ) ) {
787
788 1
					$json_in = false;
789 1
					$json_val_1 = $json_val_1 ? $json_val_1 : array( $val1 );
790 1
					$json_val_2 = $json_val_2 ? $json_val_2 : array( $val2 );
791
792
					// For JSON, we want to compare as "in" or "not in" rather than "contains"
793 1
					foreach ( $json_val_1 as $item_1 ) {
794 1
						foreach ( $json_val_2 as $item_2 ) {
795 1
							$json_in = self::matches_operation( $item_1, $item_2, 'is' );
796
797 1
							if( $json_in ) {
798 1
								break 2;
799
							}
800
						}
801
					}
802
803 1
					$value = ( $operation === 'in' ) ? $json_in : ! $json_in;
804
				}
805 1
				break;
806
807 3
			case 'less_than':
808 3
			case '<' :
809 1
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
810 1
					$value = $val1 < $val2;
811
				} else {
812
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
813
				}
814 1
				break;
815 3
			case 'greater_than':
816 3
			case '>' :
817 1
				if ( is_string( $val1 ) && is_string( $val2 ) ) {
818 1
					$value = $val1 > $val2;
819
				} else {
820
					$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
821
				}
822 1
				break;
823
			default:
824 3
				$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
825
		}
826
827 3
		return $value;
828
	}
829
830
	/**
831
	 *
832
	 * Checks if a certain entry is valid according to the View search filters (specially the Adv Filters)
833
	 *
834
	 * @uses GVCommon::calculate_get_entries_criteria();
835
	 * @see GFFormsModel::is_value_match()
836
	 *
837
	 * @since 1.7.4
838
	 *
839
	 * @param array $entry Gravity Forms Entry object
840
	 * @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.
841
	 */
842 2
	public static function check_entry_display( $entry ) {
843
844 2
		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...
845 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...
846
		}
847
848 2
		if ( empty( $entry['form_id'] ) ) {
849 1
			return new WP_Error( 'form_id_not_set', '[apply_filters_to_entry] Entry is empty!', $entry );
850
		}
851
852 2
		$criteria = self::calculate_get_entries_criteria();
853
854 2
		if ( empty( $criteria['search_criteria'] ) || ! is_array( $criteria['search_criteria'] ) ) {
855 2
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No search criteria found:', $criteria );
856 2
			return $entry;
857
		}
858
859
		// Make sure the current View is connected to the same form as the Entry
860 1
		if( ! empty( $criteria['context_view_id'] ) ) {
861 1
			$context_view_id = intval( $criteria['context_view_id'] );
862 1
			$context_form_id = gravityview_get_form_id( $context_view_id );
863 1
			if( intval( $context_form_id ) !== intval( $entry['form_id'] ) ) {
864 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'] );
865
			}
866
		}
867
868 1
		$search_criteria = $criteria['search_criteria'];
869
870
		// check entry status
871 1
		if ( array_key_exists( 'status', $search_criteria ) && $search_criteria['status'] != $entry['status'] ) {
872 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 );
873
		}
874
875
		// check entry date
876
		// @todo: Does it make sense to apply the Date create filters to the single entry?
877
878
		// field_filters
879 1
		if ( empty( $search_criteria['field_filters'] ) || ! is_array( $search_criteria['field_filters'] ) ) {
880 1
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No field filters criteria found:', $search_criteria );
881 1
			return $entry;
882
		}
883
884 1
		$filters = $search_criteria['field_filters'];
885
886 1
		$mode = array_key_exists( 'mode', $filters ) ? strtolower( $filters['mode'] ) : 'all';
887
888
		// Prevent the mode from being processed below
889 1
		unset( $filters['mode'] );
890
891 1
		$form = self::get_form( $entry['form_id'] );
892
893 1
		foreach ( $filters as $filter ) {
894
895 1
			if ( ! isset( $filter['key'] ) ) {
896
				do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Filter key not set', $filter );
897
				continue;
898
			}
899
900 1
			$k = $filter['key'];
901
902 1
			$field = self::get_field( $form, $k );
903
904 1
			if ( is_null( $field ) ) {
905 1
				$field_value = isset( $entry[ $k ] ) ? $entry[ $k ] : null;
906
			} else {
907
				$field_value  = GFFormsModel::get_lead_field_value( $entry, $field );
908
				 // If it's a complex field, then fetch the input's value, if exists at the current key. Otherwise, let GF handle it
909
				$field_value = ( is_array( $field_value ) && isset( $field_value[ $k ] ) ) ? rgar( $field_value, $k ) : $field_value;
910
			}
911
912 1
			$operator = isset( $filter['operator'] ) ? strtolower( $filter['operator'] ) : 'is';
913
914 1
			$is_value_match = GFFormsModel::is_value_match( $field_value, $filter['value'], $operator, $field );
915
916
			// Any match is all we need to know
917 1
			if ( $is_value_match && 'any' === $mode ) {
918 1
				return $entry;
919
			}
920
921
			// Any failed match is a total fail
922 1
			if ( ! $is_value_match && 'all' === $mode ) {
923 1
				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...
924
			}
925
		}
926
927
		// at this point, if in ALL mode, then entry is approved - all conditions were met.
928
		// Or, for ANY mode, means none of the conditions were satisfied, so entry is not approved
929 1
		if ( 'all' === $mode ) {
930 1
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved: all conditions were met' );
931 1
			return $entry;
932
		} else {
933
			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...
934
		}
935
936
	}
937
938
939
	/**
940
	 * Allow formatting date and time based on GravityView standards
941
	 *
942
	 * @since 1.16
943
	 *
944
	 * @see GVCommon_Test::test_format_date for examples
945
	 *
946
	 * @param string $date_string The date as stored by Gravity Forms (`Y-m-d h:i:s` GMT)
947
	 * @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
948
	 * - `raw` Un-formatted date string in original `Y-m-d h:i:s` format
949
	 * - `timestamp` Integer timestamp returned by GFCommon::get_local_timestamp()
950
	 * - `diff` "%s ago" format, unless other `format` is defined
951
	 * - `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.
952
	 * - `time` Include time in the `GFCommon::format_date()` output
953
	 * - `format` Define your own date format, or `diff` format
954
	 *
955
	 * @return int|null|string Formatted date based on the original date
956
	 */
957 1
	public static function format_date( $date_string = '', $args = array() ) {
958
959
		$default_atts = array(
960 1
			'raw' => false,
961
			'timestamp' => false,
962
			'diff' => false,
963
			'human' => false,
964
			'format' => '',
965
			'time' => false,
966
		);
967
968 1
		$atts = wp_parse_args( $args, $default_atts );
969
970
		/**
971
		 * Gravity Forms code to adjust date to locally-configured Time Zone
972
		 * @see GFCommon::format_date() for original code
973
		 */
974 1
		$date_gmt_time   = mysql2date( 'G', $date_string );
975 1
		$date_local_timestamp = GFCommon::get_local_timestamp( $date_gmt_time );
976
977 1
		$format  = rgar( $atts, 'format' );
978 1
		$is_human  = ! empty( $atts['human'] );
979 1
		$is_diff  = ! empty( $atts['diff'] );
980 1
		$is_raw = ! empty( $atts['raw'] );
981 1
		$is_timestamp = ! empty( $atts['timestamp'] );
982 1
		$include_time = ! empty( $atts['time'] );
983
984
		// If we're using time diff, we want to have a different default format
985 1
		if( empty( $format ) ) {
986
			/* translators: %s: relative time from now, used for generic date comparisons. "1 day ago", or "20 seconds ago" */
987 1
			$format = $is_diff ? esc_html__( '%s ago', 'gravityview' ) : get_option( 'date_format' );
988
		}
989
990
		// If raw was specified, don't modify the stored value
991 1
		if ( $is_raw ) {
992 1
			$formatted_date = $date_string;
993 1
		} elseif( $is_timestamp ) {
994 1
			$formatted_date = $date_local_timestamp;
995 1
		} elseif ( $is_diff ) {
996 1
			$formatted_date = sprintf( $format, human_time_diff( $date_gmt_time ) );
997
		} else {
998 1
			$formatted_date = GFCommon::format_date( $date_string, $is_human, $format, $include_time );
999
		}
1000
1001 1
		unset( $format, $is_diff, $is_human, $is_timestamp, $is_raw, $date_gmt_time, $date_local_timestamp, $default_atts );
1002
1003 1
		return $formatted_date;
1004
	}
1005
1006
	/**
1007
	 * Retrieve the label of a given field id (for a specific form)
1008
	 *
1009
	 * @access public
1010
	 * @since 1.17 Added $field_value parameter
1011
	 *
1012
	 * @param array $form Gravity Forms form array
1013
	 * @param string $field_id ID of the field. If an input, full input ID (like `1.3`)
1014
	 * @param string|array $field_value Raw value of the field.
1015
	 * @return string
1016
	 */
1017
	public static function get_field_label( $form = array(), $field_id = '', $field_value = '' ) {
1018
1019
		if ( empty( $form ) || empty( $field_id ) ) {
1020
			return '';
1021
		}
1022
1023
		$field = self::get_field( $form, $field_id );
1024
1025
		$label = rgar( $field, 'label' );
1026
1027
		if( floor( $field_id ) !== floatval( $field_id ) ) {
1028
			$label = GFFormsModel::get_choice_text( $field, $field_value, $field_id );
1029
		}
1030
1031
		return $label;
1032
	}
1033
1034
1035
	/**
1036
	 * Returns the field details array of a specific form given the field id
1037
	 *
1038
	 * Alias of GFFormsModel::get_field
1039
	 *
1040
	 * @since 1.19 Allow passing form ID as well as form array
1041
	 *
1042
	 * @uses GFFormsModel::get_field
1043
	 * @see GFFormsModel::get_field
1044
	 * @access public
1045
	 * @param array|int $form Form array or ID
1046
	 * @param string|int $field_id
1047
	 * @return GF_Field|null Gravity Forms field object, or NULL: Gravity Forms GFFormsModel does not exist or field at $field_id doesn't exist.
1048
	 */
1049
	public static function get_field( $form, $field_id ) {
1050
1051
		if ( is_numeric( $form ) ) {
1052
			$form = GFAPI::get_form( $form );
1053
		}
1054
1055
		if ( class_exists( 'GFFormsModel' ) ){
1056
			return GFFormsModel::get_field( $form, $field_id );
1057
		} else {
1058
			return null;
1059
		}
1060
	}
1061
1062
1063
	/**
1064
	 * Check whether the post is GravityView
1065
	 *
1066
	 * - Check post type. Is it `gravityview`?
1067
	 * - Check shortcode
1068
	 *
1069
	 * @param  WP_Post      $post WordPress post object
1070
	 * @return boolean           True: yep, GravityView; No: not!
1071
	 */
1072 2
	public static function has_gravityview_shortcode( $post = null ) {
1073 2
		if ( ! is_a( $post, 'WP_Post' ) ) {
1074 1
			return false;
1075
		}
1076
1077 2
		if ( 'gravityview' === get_post_type( $post ) ) {
1078 2
			return true;
1079
		}
1080
1081 2
		return self::has_shortcode_r( $post->post_content, 'gravityview' ) ? true : false;
1082
1083
	}
1084
1085
1086
	/**
1087
	 * Placeholder until the recursive has_shortcode() patch is merged
1088
	 * @see https://core.trac.wordpress.org/ticket/26343#comment:10
1089
	 * @param string $content Content to check whether there's a shortcode
1090
	 * @param string $tag Current shortcode tag
1091
	 */
1092 2
	public static function has_shortcode_r( $content, $tag = 'gravityview' ) {
1093 2
		if ( false === strpos( $content, '[' ) ) {
1094 1
			return false;
1095
		}
1096
1097 2
		if ( shortcode_exists( $tag ) ) {
1098
1099 2
			$shortcodes = array();
1100
1101 2
			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
1102 2
			if ( empty( $matches ) ){
1103 1
				return false;
1104
			}
1105
1106 2
			foreach ( $matches as $shortcode ) {
1107 2
				if ( $tag === $shortcode[2] ) {
1108
1109
					// Changed this to $shortcode instead of true so we get the parsed atts.
1110 2
					$shortcodes[] = $shortcode;
1111
1112 1
				} else if ( isset( $shortcode[5] ) && $results = self::has_shortcode_r( $shortcode[5], $tag ) ) {
1113 1
					foreach( $results as $result ) {
1114 2
						$shortcodes[] = $result;
1115
					}
1116
				}
1117
			}
1118
1119 2
			return $shortcodes;
1120
		}
1121
		return false;
1122
	}
1123
1124
1125
1126
	/**
1127
	 * Get the views for a particular form
1128
	 *
1129
	 * @since 1.15.2 Add $args array and limit posts_per_page to 500
1130
	 *
1131
	 * @uses get_posts()
1132
	 *
1133
	 * @param  int $form_id Gravity Forms form ID
1134
	 * @param  array $args Pass args sent to get_posts()
1135
	 *
1136
	 * @return array          Array with view details, as returned by get_posts()
1137
	 */
1138 1
	public static function get_connected_views( $form_id, $args = array() ) {
1139
1140
		$defaults = array(
1141 1
			'post_type' => 'gravityview',
1142 1
			'posts_per_page' => 100,
0 ignored issues
show
introduced by
Detected high pagination limit, posts_per_page is set to 100
Loading history...
1143 1
			'meta_key' => '_gravityview_form_id',
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
1144 1
			'meta_value' => (int)$form_id,
0 ignored issues
show
introduced by
Detected usage of meta_value, possible slow query.
Loading history...
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
1145
		);
1146
1147 1
		$args = wp_parse_args( $args, $defaults );
1148
1149 1
		$views = get_posts( $args );
1150
1151 1
		return $views;
1152
	}
1153
1154
	/**
1155
	 * Get the Gravity Forms form ID connected to a View
1156
	 *
1157
	 * @param int $view_id The ID of the View to get the connected form of
1158
	 *
1159
	 * @return false|string ID of the connected Form, if exists. Empty string if not. False if not the View ID isn't valid.
1160
	 */
1161 1
	public static function get_meta_form_id( $view_id ) {
1162 1
		return get_post_meta( $view_id, '_gravityview_form_id', true );
1163
	}
1164
1165
	/**
1166
	 * Get the template ID (`list`, `table`, `datatables`, `map`) for a View
1167
	 *
1168
	 * @see GravityView_Template::template_id
1169
	 *
1170
	 * @param int $view_id The ID of the View to get the layout of
1171
	 *
1172
	 * @return string GravityView_Template::template_id value. Empty string if not.
1173
	 */
1174 1
	public static function get_meta_template_id( $view_id ) {
1175 1
		return get_post_meta( $view_id, '_gravityview_directory_template', true );
1176
	}
1177
1178
1179
	/**
1180
	 * Get all the settings for a View
1181
	 *
1182
	 * @uses  \GV\View_Settings::defaults() Parses the settings with the plugin defaults as backups.
1183
	 * @param  int $post_id View ID
1184
	 * @return array          Associative array of settings with plugin defaults used if not set by the View
1185
	 */
1186 3
	public static function get_template_settings( $post_id ) {
1187
1188 3
		$settings = get_post_meta( $post_id, '_gravityview_template_settings', true );
1189
1190 3
		if ( class_exists( 'GravityView_View_Data' ) ) {
1191
1192 3
			$defaults = defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) ? \GV\View_Settings::defaults() : GravityView_View_Data::get_default_args();
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::get_default_args() has been deprecated.

This method has been deprecated.

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