Completed
Push — master ( ebef7a...94d09a )
by Zack
20:55 queued 16:56
created

GVCommon::get_directory_fields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 13
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 mixed False: no form ID specified or Gravity Forms isn't active. Array: Form returned from Gravity Forms
27
	 */
28
	public static function get_form( $form_id ) {
29
		if ( empty( $form_id ) ) {
30
			return false;
31
		}
32
33
		// Only get_form_meta is cached. ::facepalm::
34
		if ( class_exists( 'RGFormsModel' ) ) {
35
			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
	public static function has_cap( $caps = '', $object_id = null, $user_id = null ) {
59
		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           Gravity Forms form array
122
	 */
123
	public static function get_form_from_entry_id( $entry_slug ) {
124
125
		$entry = self::get_entry( $entry_slug, true );
126
127
		$form = self::get_form( $entry['form_id'] );
128
129
		return $form;
130
	}
131
132
	/**
133
	 * Check whether a form has product fields
134
	 *
135
	 * @since 1.16
136
	 *
137
	 * @param array $form Gravity Forms form array
138
	 *
139
	 * @return bool|GF_Field[]
140
	 */
141
	public static function has_product_field( $form = array() ) {
142
143
		$product_fields = apply_filters( 'gform_product_field_types', array( 'option', 'quantity', 'product', 'total', 'shipping', 'calculation', 'price' ) );
144
145
		$fields = GFAPI::get_fields_by_type( $form, $product_fields );
146
147
		return empty( $fields ) ? false : $fields;
148
	}
149
150
	/**
151
	 * Get the entry ID from the entry slug, which may or may not be the entry ID
152
	 *
153
	 * @since  1.5.2
154
	 * @param  string $slug The entry slug, as returned by GravityView_API::get_entry_slug()
155
	 * @return int|null       The entry ID, if exists; `NULL` if not
156
	 */
157
	public static function get_entry_id_from_slug( $slug ) {
158
		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...
159
160
		$search_criteria = array(
161
			'field_filters' => array(
162
				array(
163
					'key' => 'gravityview_unique_id', // Search the meta values
164
					'value' => $slug,
165
					'operator' => 'is',
166
					'type' => 'meta',
167
				),
168
			)
169
		);
170
171
		// Limit to one for speed
172
		$paging = array(
173
			'page_size' => 1,
174
		);
175
176
		$results = GFAPI::get_entries( 0, $search_criteria, null, $paging );
177
178
		$result = ( ! empty( $results ) && ! empty( $results[0]['id'] ) ) ? $results[0]['id'] : null;
179
180
		return $result;
181
	}
182
183
184
	/**
185
	 * Returns the list of available forms
186
	 *
187
	 * @access public
188
	 * @param mixed $form_id
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...
189
	 * @return array Empty array if GFAPI isn't available or no forms. Otherwise, associative array with id, title keys
190
	 */
191
	public static function get_forms() {
192
		$forms = array();
193
		if ( class_exists( 'GFAPI' ) ) {
194
			$gf_forms = GFAPI::get_forms();
195
			foreach ( $gf_forms as $form ) {
196
				$forms[] = array(
197
					'id' => $form['id'],
198
					'title' => $form['title'],
199
				);
200
			}
201
		}
202
		return $forms;
203
	}
204
205
	/**
206
	 * Return array of fields' id and label, for a given Form ID
207
	 *
208
	 * @access public
209
	 * @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...
210
	 * @param bool $add_default_properties
211
	 * @param bool $include_parent_field
212
	 * @return array
213
	 */
214
	public static function get_form_fields( $form = '', $add_default_properties = false, $include_parent_field = true ) {
215
216
		if ( ! is_array( $form ) ) {
217
			$form = self::get_form( $form );
218
		}
219
220
		$fields = array();
221
		$has_product_fields = false;
222
		$has_post_fields = false;
223
224
		if ( $form ) {
225
			foreach ( $form['fields'] as $field ) {
226
227
				if ( $include_parent_field || empty( $field['inputs'] ) ) {
228
					$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...
229
						'label' => rgar( $field, 'label' ),
230
						'parent' => null,
231
						'type' => rgar( $field, 'type' ),
232
						'adminLabel' => rgar( $field, 'adminLabel' ),
233
						'adminOnly' => rgar( $field, 'adminOnly' ),
234
					);
235
				}
236
237
				if ( $add_default_properties && ! empty( $field->inputs ) ) {
238
					foreach ( $field->inputs as $input ) {
239
                        /**
240
                         * @hack
241
                         * In case of email/email confirmation, the input for email has the same id as the parent field
242
                         */
243
						if( 'email' === $field->type && false === strpos( $input['id'], '.' ) ) {
244
                            continue;
245
                        }
246
						$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...
247
							'label' => rgar( $input, 'label' ),
248
							'customLabel' => rgar( $input, 'customLabel' ),
249
							'parent' => $field,
250
							'type' => rgar( $field, 'type' ),
251
							'adminLabel' => rgar( $field, 'adminLabel' ),
252
							'adminOnly' => rgar( $field, 'adminOnly' ),
253
						);
254
					}
255
				}
256
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
257
258
				if( GFCommon::is_product_field( $field->type ) ){
259
					$has_product_fields = true;
260
				}
261
262
				if ( GFCommon::is_post_field( $field ) ) {
263
					$has_post_fields = true;
264
				}
265
			}
266
		}
267
268
		/**
269
		 * @since 1.7
270
		 */
271
		if ( $has_post_fields ) {
272
			$fields['post_id'] = array(
273
				'label' => __( 'Post ID', 'gravityview' ),
274
				'type' => 'post_id',
275
			);
276
		}
277
278
		if ( $has_product_fields ) {
279
280
			$payment_fields = GravityView_Fields::get_all( 'pricing' );
281
282
			foreach ( $payment_fields as $payment_field ) {
283
				if( isset( $fields["{$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...
284
					continue;
285
				}
286
				$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...
287
					'label' => $payment_field->label,
288
					'desc' => $payment_field->description,
289
					'type' => $payment_field->name,
290
				);
291
			}
292
		}
293
294
		/**
295
		 * @filter `gravityview/common/get_form_fields` Modify the form fields shown in the Add Field field picker.
296
		 * @since 1.17
297
		 * @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`
298
		 * @param array $form GF Form array
299
		 * @param bool $include_parent_field Whether to include the parent field when getting a field with inputs
300
		 */
301
		$fields = apply_filters( 'gravityview/common/get_form_fields', $fields, $form, $include_parent_field );
302
303
		return $fields;
304
305
	}
306
307
	/**
308
	 * get extra fields from entry meta
309
	 * @param  string $form_id (default: '')
310
	 * @return array
311
	 */
312
	public static function get_entry_meta( $form_id, $only_default_column = true ) {
313
314
		$extra_fields = GFFormsModel::get_entry_meta( $form_id );
315
316
		$fields = array();
317
318
		foreach ( $extra_fields as $key => $field ){
319
			if ( ! empty( $only_default_column ) && ! empty( $field['is_default_column'] ) ) {
320
				$fields[ $key ] = array( 'label' => $field['label'], 'type' => 'entry_meta' );
321
			}
322
		}
323
324
		return $fields;
325
	}
326
327
328
	/**
329
	 * Wrapper for the Gravity Forms GFFormsModel::search_lead_ids() method
330
	 *
331
	 * @see  GFEntryList::leads_page()
332
	 * @param  int $form_id ID of the Gravity Forms form
333
	 * @since  1.1.6
334
	 * @return array|void          Array of entry IDs. Void if Gravity Forms isn't active.
335
	 */
336
	public static function get_entry_ids( $form_id, $search_criteria = array() ) {
337
338
		if ( ! class_exists( 'GFFormsModel' ) ) {
339
			return;
340
		}
341
342
		return GFFormsModel::search_lead_ids( $form_id, $search_criteria );
343
	}
344
345
	/**
346
	 * Calculates the Search Criteria used on the self::get_entries / self::get_entry methods
347
	 *
348
	 * @since 1.7.4
349
	 *
350
	 * @param array $passed_criteria array Input Criteria (search_criteria, sorting, paging)
351
	 * @param array $form_ids array Gravity Forms form IDs
352
	 * @return array
353
	 */
354
	public static function calculate_get_entries_criteria( $passed_criteria = array(), $form_ids = array() ) {
355
356
		$search_criteria_defaults = array(
357
			'search_criteria' => null,
358
			'sorting' => null,
359
			'paging' => null,
360
			'cache' => (isset( $passed_criteria['cache'] ) ? $passed_criteria['cache'] : true),
361
		);
362
363
		$criteria = wp_parse_args( $passed_criteria, $search_criteria_defaults );
364
365
		if ( ! empty( $criteria['search_criteria']['field_filters'] ) ) {
366
			foreach ( $criteria['search_criteria']['field_filters'] as &$filter ) {
367
368
				if ( ! is_array( $filter ) ) {
369
					continue;
370
				}
371
372
				// By default, we want searches to be wildcard for each field.
373
				$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
374
375
				/**
376
				 * @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc)
377
				 * @param string $operator Existing search operator
378
				 * @param array $filter array with `key`, `value`, `operator`, `type` keys
379
				 */
380
				$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
381
			}
382
383
			// don't send just the [mode] without any field filter.
384
			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...
385
				unset( $criteria['search_criteria']['field_filters']['mode'] );
386
			}
1 ignored issue
show
introduced by
Blank line found after control structure
Loading history...
387
388
		}
389
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 3 empty lines
Loading history...
390
391
392
		/**
393
		 * Prepare date formats to be in Gravity Forms DB format;
394
		 * $passed_criteria may include date formats incompatible with Gravity Forms.
395
		 */
396
		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...
397
398
			if ( ! empty( $criteria['search_criteria'][ $key ] ) ) {
399
400
				// Use date_create instead of new DateTime so it returns false if invalid date format.
401
				$date = date_create( $criteria['search_criteria'][ $key ] );
402
403
				if ( $date ) {
404
					// Gravity Forms wants dates in the `Y-m-d H:i:s` format.
405
					$criteria['search_criteria'][ $key ] = $date->format( 'Y-m-d H:i:s' );
406
				} else {
407
					// If it's an invalid date, unset it. Gravity Forms freaks out otherwise.
408
					unset( $criteria['search_criteria'][ $key ] );
409
410
					do_action( 'gravityview_log_error', '[filter_get_entries_criteria] '.$key.' Date format not valid:', $criteria['search_criteria'][ $key ] );
411
				}
412
			}
413
		}
414
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
415
416
		// When multiple views are embedded, OR single entry, calculate the context view id and send it to the advanced filter
417
		if ( class_exists( 'GravityView_View_Data' ) && GravityView_View_Data::getInstance()->has_multiple_views() || GravityView_frontend::getInstance()->getSingleEntry() ) {
418
			$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
419
		} elseif ( 'delete' === RGForms::get( 'action' ) ) {
420
			$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...
421
		} elseif( !isset( $criteria['context_view_id'] ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
422
            // Prevent overriding the Context View ID: Some widgets could set the context_view_id (e.g. Recent Entries widget)
423
			$criteria['context_view_id'] = null;
424
		}
425
426
		/**
427
		 * @filter `gravityview_search_criteria` Apply final criteria filter (Used by the Advanced Filter extension)
428
		 * @param array $criteria Search criteria used by GravityView
429
		 * @param array $form_ids Forms to search
430
		 * @param int $view_id ID of the view being used to search
431
		 */
432
		$criteria = apply_filters( 'gravityview_search_criteria', $criteria, $form_ids, $criteria['context_view_id'] );
433
434
		return (array)$criteria;
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
435
436
	}
437
438
439
	/**
440
	 * Retrieve entries given search, sort, paging criteria
441
	 *
442
	 * @see  GFAPI::get_entries()
443
	 * @see GFFormsModel::get_field_filters_where()
444
	 * @access public
445
	 * @param int|array $form_ids The ID of the form or an array IDs of the Forms. Zero for all forms.
446
	 * @param mixed $passed_criteria (default: null)
447
	 * @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)
448
	 * @return mixed False: Error fetching entries. Array: Multi-dimensional array of Gravity Forms entry arrays
449
	 */
450
	public static function get_entries( $form_ids = null, $passed_criteria = null, &$total = null ) {
451
452
		// Filter the criteria before query (includes Adv Filter)
453
		$criteria = self::calculate_get_entries_criteria( $passed_criteria, $form_ids );
454
455
		do_action( 'gravityview_log_debug', '[gravityview_get_entries] Final Parameters', $criteria );
456
457
		// Return value
458
		$return = null;
459
460
		/** Reduce # of database calls */
461
		add_filter( 'gform_is_encrypted_field', '__return_false' );
462
463
		if ( ! empty( $criteria['cache'] ) ) {
464
465
			$Cache = new GravityView_Cache( $form_ids, $criteria );
466
467
			if ( $entries = $Cache->get() ) {
468
469
				// Still update the total count when using cached results
470
				if ( ! is_null( $total ) ) {
471
					$total = GFAPI::count_entries( $form_ids, $criteria['search_criteria'] );
472
				}
473
474
				$return = $entries;
475
			}
476
		}
477
478
		if ( is_null( $return ) && class_exists( 'GFAPI' ) && ( is_numeric( $form_ids ) || is_array( $form_ids ) ) ) {
479
480
			/**
481
			 * @filter `gravityview_pre_get_entries` Define entries to be used before GFAPI::get_entries() is called
482
			 * @since 1.14
483
			 * @param  null $return If you want to override GFAPI::get_entries() and define entries yourself, tap in here.
484
			 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
485
			 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
486
			 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
487
			 */
488
			$entries = apply_filters( 'gravityview_before_get_entries', null, $criteria, $passed_criteria, $total );
489
490
			// No entries returned from gravityview_before_get_entries
491
			if( is_null( $entries ) ) {
492
493
				$entries = GFAPI::get_entries( $form_ids, $criteria['search_criteria'], $criteria['sorting'], $criteria['paging'], $total );
494
495
				if ( is_wp_error( $entries ) ) {
496
					do_action( 'gravityview_log_error', $entries->get_error_message(), $entries );
497
498
					return false;
499
				}
500
			}
501
502
			if ( ! empty( $criteria['cache'] ) && isset( $Cache ) ) {
503
504
				// Cache results
505
				$Cache->set( $entries, 'entries' );
506
507
			}
508
509
			$return = $entries;
510
		}
511
512
		/** Remove filter added above */
513
		remove_filter( 'gform_is_encrypted_field', '__return_false' );
514
515
		/**
516
		 * @filter `gravityview_entries` Modify the array of entries returned to GravityView after it has been fetched from the cache or from `GFAPI::get_entries()`.
517
		 * @param  array|null $entries Array of entries as returned by the cache or by `GFAPI::get_entries()`
518
		 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
519
		 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
520
		 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
521
		 */
522
		$return = apply_filters( 'gravityview_entries', $return, $criteria, $passed_criteria, $total );
523
524
		return $return;
525
	}
526
527
528
	/**
529
	 * Return a single entry object
530
	 *
531
	 * 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()`
532
	 *
533
	 * @access public
534
	 * @param string|int $entry_slug Either entry ID or entry slug string
535
	 * @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.
536
	 * @param boolean $check_entry_display Check whether the entry is visible for the current View configuration. Default: true. {@since 1.14}
537
	 * @return array|boolean
538
	 */
539
	public static function get_entry( $entry_slug, $force_allow_ids = false, $check_entry_display = true ) {
540
541
		if ( class_exists( 'GFAPI' ) && ! empty( $entry_slug ) ) {
542
543
			/**
544
			 * @filter `gravityview_custom_entry_slug` Whether to enable and use custom entry slugs.
545
			 * @param boolean True: Allow for slugs based on entry values. False: always use entry IDs (default)
546
			 */
547
			$custom_slug = apply_filters( 'gravityview_custom_entry_slug', false );
548
549
			/**
550
			 * @filter `gravityview_custom_entry_slug_allow_id` When using a custom slug, allow access to the entry using the original slug (the Entry ID).
551
			 * - If disabled (default), only allow access to an entry using the custom slug value.  (example: `/entry/custom-slug/` NOT `/entry/123/`)
552
			 * - If enabled, you could access using the custom slug OR the entry id (example: `/entry/custom-slug/` OR `/entry/123/`)
553
			 * @param boolean $custom_slug_id_access True: allow accessing the slug by ID; False: only use the slug passed to the method.
554
			 */
555
			$custom_slug_id_access = $force_allow_ids || apply_filters( 'gravityview_custom_entry_slug_allow_id', false );
556
557
			/**
558
			 * If we're using custom entry slugs, we do a meta value search
559
			 * instead of doing a straightup ID search.
560
			 */
561
			if ( $custom_slug ) {
562
563
				$entry_id = self::get_entry_id_from_slug( $entry_slug );
564
565
			}
566
567
			// If custom slug is off, search using the entry ID
568
			// ID allow ID access is on, also use entry ID as a backup
569
			if ( empty( $custom_slug ) || ! empty( $custom_slug_id_access ) ) {
570
				// Search for IDs matching $entry_slug
571
				$entry_id = $entry_slug;
572
			}
573
574
			if ( empty( $entry_id ) ) {
575
				return false;
576
			}
577
578
			// fetch the entry
579
			$entry = GFAPI::get_entry( $entry_id );
580
581
			/**
582
			 * @filter `gravityview/common/get_entry/check_entry_display` Override whether to check entry display rules against filters
583
			 * @since 1.16.2
584
			 * @param bool $check_entry_display Check whether the entry is visible for the current View configuration. Default: true.
585
			 * @param array $entry Gravity Forms entry array
586
			 */
587
			$check_entry_display = apply_filters( 'gravityview/common/get_entry/check_entry_display', $check_entry_display, $entry );
588
589
			if( $check_entry_display ) {
590
				// Is the entry allowed
591
				$entry = self::check_entry_display( $entry );
592
			}
593
594
			return is_wp_error( $entry ) ? false : $entry;
595
596
		}
597
598
		return false;
599
	}
600
601
	/**
602
	 * Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including:
603
	 * 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals',
604
	 * and 'not_contains'
605
	 *
606
	 * @since 1.13 You can define context, which displays/hides based on what's being displayed (single, multiple, edit)
607
	 *
608
	 * @see http://docs.gravityview.co/article/252-gvlogic-shortcode
609
	 * @uses GFFormsModel::matches_operation
610
	 * @since 1.7.5
611
	 *
612
	 * @param string $val1 Left side of comparison
613
	 * @param string $val2 Right side of comparison
614
	 * @param string $operation Type of comparison
615
	 *
616
	 * @return bool True: matches, false: not matches
617
	 */
618
	public static function matches_operation( $val1, $val2, $operation ) {
619
620
		$value = false;
621
622
		if( 'context' === $val1 ) {
623
624
			$matching_contexts = array( $val2 );
625
626
			// We allow for non-standard contexts.
627
			switch( $val2 ) {
628
				// Check for either single or edit
629
				case 'singular':
630
					$matching_contexts = array( 'single', 'edit' );
631
					break;
632
				// Use multiple as alias for directory for consistency
633
				case 'multiple':
634
					$matching_contexts = array( 'directory' );
635
					break;
636
			}
637
638
			$val1 = in_array( gravityview_get_context(), $matching_contexts ) ? $val2 : false;
639
		}
640
641
		switch ( $operation ) {
642
			case 'equals':
643
				$value = GFFormsModel::matches_operation( $val1, $val2, 'is' );
644
				break;
645
			case 'greater_than_or_is':
646
			case 'greater_than_or_equals':
647
				$is    = GFFormsModel::matches_operation( $val1, $val2, 'is' );
648
				$gt    = GFFormsModel::matches_operation( $val1, $val2, 'greater_than' );
649
				$value = ( $is || $gt );
650
				break;
651
			case 'less_than_or_is':
652
			case 'less_than_or_equals':
653
				$is    = GFFormsModel::matches_operation( $val1, $val2, 'is' );
654
				$gt    = GFFormsModel::matches_operation( $val1, $val2, 'less_than' );
655
				$value = ( $is || $gt );
656
				break;
657
			case 'not_contains':
658
				$contains = GFFormsModel::matches_operation( $val1, $val2, 'contains' );
659
				$value    = ! $contains;
660
				break;
661
			default:
662
				$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
663
		}
664
665
		return $value;
666
	}
667
668
	/**
669
	 *
670
	 * Checks if a certain entry is valid according to the View search filters (specially the Adv Filters)
671
	 *
672
	 * @see GFFormsModel::is_value_match()
673
	 *
674
	 * @since 1.7.4
675
	 * @todo Return WP_Error instead of boolean
676
	 *
677
	 * @param array $entry Gravity Forms Entry object
678
	 * @return bool|array Returns 'false' if entry is not valid according to the view search filters (Adv Filter)
679
	 */
680
	public static function check_entry_display( $entry ) {
681
682
		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...
683
			do_action( 'gravityview_log_debug', __METHOD__ . ' Entry was not found.', $entry );
684
			return false;
685
		}
686
687
		if ( empty( $entry['form_id'] ) ) {
688
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry is empty! Entry:', $entry );
689
			return false;
690
		}
691
692
		$criteria = self::calculate_get_entries_criteria();
693
694
		// Make sure the current View is connected to the same form as the Entry
695
		if( ! empty( $criteria['context_view_id'] ) ) {
696
			$context_view_id = intval( $criteria['context_view_id'] );
697
			$context_form_id = gravityview_get_form_id( $context_view_id );
698
			if( intval( $context_form_id ) !== intval( $entry['form_id'] ) ) {
699
				do_action( 'gravityview_log_debug', sprintf( '[apply_filters_to_entry] Entry form ID does not match current View connected form ID:', $entry['form_id'] ), $criteria['context_view_id'] );
700
				return false;
701
			}
702
		}
703
704
		if ( empty( $criteria['search_criteria'] ) || ! is_array( $criteria['search_criteria'] ) ) {
705
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No search criteria found:', $criteria );
706
			return $entry;
707
		}
708
709
		$search_criteria = $criteria['search_criteria'];
710
		unset( $criteria );
711
712
		// check entry status
713
		if ( array_key_exists( 'status', $search_criteria ) && $search_criteria['status'] != $entry['status'] ) {
714
			do_action( 'gravityview_log_debug', sprintf( '[apply_filters_to_entry] Entry status - %s - is not valid according to filter:', $entry['status'] ), $search_criteria );
715
			return false;
716
		}
717
718
		// check entry date
719
		// @todo: Does it make sense to apply the Date create filters to the single entry?
720
721
		// field_filters
722
		if ( empty( $search_criteria['field_filters'] ) || ! is_array( $search_criteria['field_filters'] ) ) {
723
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No field filters criteria found:', $search_criteria );
724
			return $entry;
725
		}
726
727
		$filters = $search_criteria['field_filters'];
728
		unset( $search_criteria );
729
730
		$mode = array_key_exists( 'mode', $filters ) ? strtolower( $filters['mode'] ) : 'all';
731
		unset( $filters['mode'] );
732
733
		$form = self::get_form( $entry['form_id'] );
734
735
		foreach ( $filters as $filter ) {
736
737
			if ( ! isset( $filter['key'] ) ) {
738
				continue;
739
			}
740
741
			$k = $filter['key'];
742
743
			if ( in_array( $k, array( 'created_by', 'payment_status' ) ) ) {
744
				$field_value = $entry[ $k ];
745
				$field = null;
746
			} else {
747
				$field = self::get_field( $form, $k );
748
				$field_value  = GFFormsModel::get_lead_field_value( $entry, $field );
749
			}
750
751
			$operator = isset( $filter['operator'] ) ? strtolower( $filter['operator'] ) : 'is';
752
			$is_value_match = GFFormsModel::is_value_match( $field_value, $filter['value'], $operator, $field );
753
754
			// verify if we are already free to go!
755
			if ( ! $is_value_match && 'all' === $mode ) {
756
				do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry cannot be displayed. Failed one criteria for ALL mode', $filter );
757
				return false;
758
			} elseif ( $is_value_match && 'any' === $mode ) {
759
				return $entry;
760
			}
761
		}
762
763
		// at this point, if in ALL mode, then entry is approved - all conditions were met.
764
		// Or, for ANY mode, means none of the conditions were satisfied, so entry is not approved
765
		if ( 'all' === $mode ) {
766
			return $entry;
767
		} else {
768
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry cannot be displayed. Failed all the criteria for ANY mode', $filters );
769
			return false;
770
		}
771
772
	}
773
774
775
	/**
776
	 * Allow formatting date and time based on GravityView standards
777
	 *
778
	 * @since 1.16
779
	 *
780
	 * @see GVCommon_Test::test_format_date for examples
781
	 *
782
	 * @param string $date_string The date as stored by Gravity Forms (`Y-m-d h:i:s` GMT)
783
	 * @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
784
	 * - `raw` Un-formatted date string in original `Y-m-d h:i:s` format
785
	 * - `timestamp` Integer timestamp returned by GFCommon::get_local_timestamp()
786
	 * - `diff` "%s ago" format, unless other `format` is defined
787
	 * - `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.
788
	 * - `time` Include time in the `GFCommon::format_date()` output
789
	 * - `format` Define your own date format, or `diff` format
790
	 *
791
	 * @return int|null|string Formatted date based on the original date
792
	 */
793
	public static function format_date( $date_string = '', $args = array() ) {
794
795
		$default_atts = array(
796
			'raw' => false,
797
			'timestamp' => false,
798
			'diff' => false,
799
			'human' => false,
800
			'format' => '',
801
			'time' => false,
802
		);
803
804
		$atts = wp_parse_args( $args, $default_atts );
805
806
		/**
807
		 * Gravity Forms code to adjust date to locally-configured Time Zone
808
		 * @see GFCommon::format_date() for original code
809
		 */
810
		$date_gmt_time   = mysql2date( 'G', $date_string );
811
		$date_local_timestamp = GFCommon::get_local_timestamp( $date_gmt_time );
812
813
		$format  = rgar( $atts, 'format' );
814
		$is_human  = ! empty( $atts['human'] );
815
		$is_diff  = ! empty( $atts['diff'] );
816
		$is_raw = ! empty( $atts['raw'] );
817
		$is_timestamp = ! empty( $atts['timestamp'] );
818
		$include_time = ! empty( $atts['time'] );
819
820
		// If we're using time diff, we want to have a different default format
821
		if( empty( $format ) ) {
822
			/* translators: %s: relative time from now, used for generic date comparisons. "1 day ago", or "20 seconds ago" */
823
			$format = $is_diff ? esc_html__( '%s ago', 'gravityview' ) : get_option( 'date_format' );
824
		}
825
826
		// If raw was specified, don't modify the stored value
827
		if ( $is_raw ) {
828
			$formatted_date = $date_string;
829
		} elseif( $is_timestamp ) {
830
			$formatted_date = $date_local_timestamp;
831
		} elseif ( $is_diff ) {
832
			$formatted_date = sprintf( $format, human_time_diff( $date_gmt_time ) );
833
		} else {
834
			$formatted_date = GFCommon::format_date( $date_string, $is_human, $format, $include_time );
835
		}
836
837
		unset( $format, $is_diff, $is_human, $is_timestamp, $is_raw, $date_gmt_time, $date_local_timestamp, $default_atts );
838
839
		return $formatted_date;
840
	}
841
842
	/**
843
	 * Retrieve the label of a given field id (for a specific form)
844
	 *
845
	 * @access public
846
	 * @since 1.17 Added $field_value parameter
847
	 *
848
	 * @param array $form Gravity Forms form array
849
	 * @param string $field_id ID of the field. If an input, full input ID (like `1.3`)
850
	 * @param string|array $field_value Raw value of the field.
851
	 * @return string
852
	 */
853
	public static function get_field_label( $form = array(), $field_id = '', $field_value = '' ) {
854
855
		if ( empty( $form ) || empty( $field_id ) ) {
856
			return '';
857
		}
858
859
		$field = self::get_field( $form, $field_id );
860
861
		$label = rgar( $field, 'label' );
862
863
		if( floor( $field_id ) !== floatval( $field_id ) ) {
864
			$label = GFFormsModel::get_choice_text( $field, $field_value, $field_id );
865
		}
866
867
		return $label;
868
	}
869
870
871
	/**
872
	 * Returns the field details array of a specific form given the field id
873
	 *
874
	 * Alias of GFFormsModel::get_field
875
	 *
876
	 * @uses GFFormsModel::get_field
877
	 * @see GFFormsModel::get_field
878
	 * @access public
879
	 * @param array $form
880
	 * @param string|int $field_id
881
	 * @return GF_Field|null Gravity Forms field object, or NULL: Gravity Forms GFFormsModel does not exist or field at $field_id doesn't exist.
882
	 */
883
	public static function get_field( $form, $field_id ) {
884
		if ( class_exists( 'GFFormsModel' ) ){
885
			return GFFormsModel::get_field( $form, $field_id );
886
		} else {
887
			return null;
888
		}
889
	}
890
891
892
	/**
893
	 * Check whether the post is GravityView
894
	 *
895
	 * - Check post type. Is it `gravityview`?
896
	 * - Check shortcode
897
	 *
898
	 * @param  WP_Post      $post WordPress post object
899
	 * @return boolean           True: yep, GravityView; No: not!
900
	 */
901
	public static function has_gravityview_shortcode( $post = null ) {
902
		if ( ! is_a( $post, 'WP_Post' ) ) {
903
			return false;
904
		}
905
906
		if ( 'gravityview' === get_post_type( $post ) ) {
907
			return true;
908
		}
909
910
		return self::has_shortcode_r( $post->post_content, 'gravityview' ) ? true : false;
911
912
	}
913
914
915
	/**
916
	 * Placeholder until the recursive has_shortcode() patch is merged
917
	 * @see https://core.trac.wordpress.org/ticket/26343#comment:10
918
	 * @param string $content Content to check whether there's a shortcode
919
	 * @param string $tag Current shortcode tag
920
	 */
921
	public static function has_shortcode_r( $content, $tag = 'gravityview' ) {
922
		if ( false === strpos( $content, '[' ) ) {
923
			return false;
924
		}
925
926
		if ( shortcode_exists( $tag ) ) {
927
928
			$shortcodes = array();
929
930
			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
931
			if ( empty( $matches ) ){
932
				return false;
933
			}
934
935
			foreach ( $matches as $shortcode ) {
936
				if ( $tag === $shortcode[2] ) {
937
938
					// Changed this to $shortcode instead of true so we get the parsed atts.
939
					$shortcodes[] = $shortcode;
940
941
				} else if ( isset( $shortcode[5] ) && $results = self::has_shortcode_r( $shortcode[5], $tag ) ) {
942
					foreach( $results as $result ) {
943
						$shortcodes[] = $result;
944
					}
945
				}
946
			}
947
948
			return $shortcodes;
949
		}
950
		return false;
951
	}
952
953
954
955
	/**
956
	 * Get the views for a particular form
957
	 *
958
	 * @since 1.15.2 Add $args array and limit posts_per_page to 500
959
	 *
960
	 * @uses get_posts()
961
	 *
962
	 * @param  int $form_id Gravity Forms form ID
963
	 * @param  array $args Pass args sent to get_posts()
964
	 *
965
	 * @return array          Array with view details, as returned by get_posts()
966
	 */
967
	public static function get_connected_views( $form_id, $args = array() ) {
968
969
		$defaults = array(
970
			'post_type' => 'gravityview',
971
			'posts_per_page' => 100,
0 ignored issues
show
introduced by
Detected high pagination limit, posts_per_page is set to 100
Loading history...
972
			'meta_key' => '_gravityview_form_id',
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
973
			'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...
974
		);
975
976
		$args = wp_parse_args( $args, $defaults );
977
978
		$views = get_posts( $args );
979
980
		return $views;
981
	}
982
983
	/**
984
	 * Get the Gravity Forms form ID connected to a View
985
	 *
986
	 * @param int $view_id The ID of the View to get the connected form of
987
	 *
988
	 * @return false|string ID of the connected Form, if exists. Empty string if not. False if not the View ID isn't valid.
989
	 */
990
	public static function get_meta_form_id( $view_id ) {
991
		return get_post_meta( $view_id, '_gravityview_form_id', true );
992
	}
993
994
	/**
995
	 * Get the template ID (`list`, `table`, `datatables`, `map`) for a View
996
	 *
997
	 * @see GravityView_Template::template_id
998
	 *
999
	 * @param int $view_id The ID of the View to get the layout of
1000
	 *
1001
	 * @return string GravityView_Template::template_id value. Empty string if not.
1002
	 */
1003
	public static function get_meta_template_id( $view_id ) {
1004
		return get_post_meta( $view_id, '_gravityview_directory_template', true );
1005
	}
1006
1007
1008
	/**
1009
	 * Get all the settings for a View
1010
	 *
1011
	 * @uses  GravityView_View_Data::get_default_args() Parses the settings with the plugin defaults as backups.
1012
	 * @param  int $post_id View ID
1013
	 * @return array          Associative array of settings with plugin defaults used if not set by the View
1014
	 */
1015
	public static function get_template_settings( $post_id ) {
1016
1017
		$settings = get_post_meta( $post_id, '_gravityview_template_settings', true );
1018
1019
		if ( class_exists( 'GravityView_View_Data' ) ) {
1020
1021
			$defaults = GravityView_View_Data::get_default_args();
1022
1023
			return wp_parse_args( (array)$settings, $defaults );
0 ignored issues
show
introduced by
No space after closing casting parenthesis is prohibited
Loading history...
1024
1025
		}
1026
1027
		// Backup, in case GravityView_View_Data isn't loaded yet.
1028
		return $settings;
1029
	}
1030
1031
	/**
1032
	 * Get the setting for a View
1033
	 *
1034
	 * If the setting isn't set by the View, it returns the plugin default.
1035
	 *
1036
	 * @param  int $post_id View ID
1037
	 * @param  string $key     Key for the setting
1038
	 * @return mixed|null          Setting value, or NULL if not set.
1039
	 */
1040
	public static function get_template_setting( $post_id, $key ) {
1041
1042
		$settings = self::get_template_settings( $post_id );
1043
1044
		if ( isset( $settings[ $key ] ) ) {
1045
			return $settings[ $key ];
1046
		}
1047
1048
		return null;
1049
	}
1050
1051
	/**
1052
	 * Get the field configuration for the View
1053
	 *
1054
	 * array(
1055
	 *
1056
	 * 	[other zones]
1057
	 *
1058
	 * 	'directory_list-title' => array(
1059
	 *
1060
	 *   	[other fields]
1061
	 *
1062
	 *  	'5372653f25d44' => array(
1063
	 *  		'id' => string '9' (length=1)
1064
	 *  		'label' => string 'Screenshots' (length=11)
1065
	 *			'show_label' => string '1' (length=1)
1066
	 *			'custom_label' => string '' (length=0)
1067
	 *			'custom_class' => string 'gv-gallery' (length=10)
1068
	 * 			'only_loggedin' => string '0' (length=1)
1069
	 *			'only_loggedin_cap' => string 'read' (length=4)
1070
	 *  	)
1071
	 *
1072
	 * 		[other fields]
1073
	 *  )
1074
	 *
1075
	 * 	[other zones]
1076
	 * )
1077
	 *
1078
	 * @param  int $post_id View ID
1079
	 * @return array          Multi-array of fields with first level being the field zones. See code comment.
1080
	 */
1081
	public static function get_directory_fields( $post_id ) {
1082
		$fields = get_post_meta( $post_id, '_gravityview_directory_fields', true );
1083
1084
		/**
1085
		 * @filter `gravityview/configuration/fields` Filter the View fields' configuration array
1086
		 * @since 1.6.5
1087
		 * @param $fields array Multi-array of fields with first level being the field zones
1088
		 * @param $post_id int Post ID
1089
		 */
1090
		$fields = apply_filters( 'gravityview/configuration/fields', $fields, $post_id );
1091
1092
		return $fields;
1093
	}
1094
1095
1096
	/**
1097
	 * Render dropdown (select) with the list of sortable fields from a form ID
1098
	 *
1099
	 * @access public
1100
	 * @param  int $formid Form ID
1101
	 * @return string         html
1102
	 */
1103
	public static function get_sortable_fields( $formid, $current = '' ) {
1104
		$output = '<option value="" ' . selected( '', $current, false ).'>' . esc_html__( 'Default', 'gravityview' ) .'</option>';
1105
1106
		if ( empty( $formid ) ) {
1107
			return $output;
1108
		}
1109
1110
		$fields = self::get_sortable_fields_array( $formid );
1111
1112
		if ( ! empty( $fields ) ) {
1113
1114
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'list', 'textarea' ), null );
1115
1116
			foreach ( $fields as $id => $field ) {
1117
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
1118
					continue;
1119
				}
1120
1121
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'>'. esc_attr( $field['label'] ) .'</option>';
1122
			}
1123
		}
1124
1125
		return $output;
1126
	}
1127
1128
	/**
1129
	 *
1130
	 * @param int $formid Gravity Forms form ID
1131
	 * @param array $blacklist Field types to exclude
1132
	 *
1133
	 * @since 1.8
1134
	 *
1135
	 * @todo Get all fields, check if sortable dynamically
1136
	 *
1137
	 * @return array
1138
	 */
1139
	public static function get_sortable_fields_array( $formid, $blacklist = array( 'list', 'textarea' ) ) {
1140
1141
		// Get fields with sub-inputs and no parent
1142
		$fields = self::get_form_fields( $formid, true, false );
1143
1144
		$date_created = array(
1145
			'date_created' => array(
1146
				'type' => 'date_created',
1147
				'label' => __( 'Date Created', 'gravityview' ),
1148
			),
1149
		);
1150
1151
        $fields = $date_created + $fields;
1152
1153
		$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...
1154
1155
		// TODO: Convert to using array_filter
1156
		foreach( $fields as $id => $field ) {
1157
1158
			if( in_array( $field['type'], $blacklist_field_types ) ) {
1159
				unset( $fields[ $id ] );
1160
			}
1161
		}
1162
1163
        /**
1164
         * @filter `gravityview/common/sortable_fields` Filter the sortable fields
1165
         * @since 1.12
1166
         * @param array $fields Sub-set of GF form fields that are sortable
1167
         * @param int $formid The Gravity Forms form ID that the fields are from
1168
         */
1169
        $fields = apply_filters( 'gravityview/common/sortable_fields', $fields, $formid );
1170
1171
		return $fields;
1172
	}
1173
1174
	/**
1175
	 * Returns the GF Form field type for a certain field(id) of a form
1176
	 * @param  object $form     Gravity Forms form
1177
	 * @param  mixed $field_id Field ID or Field array
1178
	 * @return string field type
1179
	 */
1180
	public static function get_field_type( $form = null, $field_id = '' ) {
1181
1182
		if ( ! empty( $field_id ) && ! is_array( $field_id ) ) {
1183
			$field = self::get_field( $form, $field_id );
1184
		} else {
1185
			$field = $field_id;
1186
		}
1187
1188
		return class_exists( 'RGFormsModel' ) ? RGFormsModel::get_input_type( $field ) : '';
1189
1190
	}
1191
1192
1193
	/**
1194
	 * Checks if the field type is a 'numeric' field type (e.g. to be used when sorting)
1195
	 * @param  int|array  $form  form ID or form array
1196
	 * @param  int|array  $field field key or field array
1197
	 * @return boolean
1198
	 */
1199
	public static function is_field_numeric(  $form = null, $field = '' ) {
1200
1201
		if ( ! is_array( $form ) && ! is_array( $field ) ) {
1202
			$form = self::get_form( $form );
1203
		}
1204
1205
		// If entry meta, it's a string. Otherwise, numeric
1206
		if( ! is_numeric( $field ) && is_string( $field ) ) {
1207
			$type = $field;
1208
		} else {
1209
			$type = self::get_field_type( $form, $field );
1210
		}
1211
1212
		/**
1213
		 * @filter `gravityview/common/numeric_types` What types of fields are numeric?
1214
		 * @since 1.5.2
1215
		 * @param array $numeric_types Fields that are numeric. Default: `[ number, time ]`
1216
		 */
1217
		$numeric_types = apply_filters( 'gravityview/common/numeric_types', array( 'number', 'time' ) );
1218
1219
		// Defer to GravityView_Field setting, if the field type is registered and `is_numeric` is true
1220
		if( $gv_field = GravityView_Fields::get( $type ) ) {
1221
			if( true === $gv_field->is_numeric ) {
1222
				$numeric_types[] = $gv_field->is_numeric;
1223
			}
1224
		}
1225
1226
		$return = in_array( $type, $numeric_types );
1227
1228
		return $return;
1229
	}
1230
1231
	/**
1232
	 * Encrypt content using Javascript so that it's hidden when JS is disabled.
1233
	 *
1234
	 * This is mostly used to hide email addresses from scraper bots.
1235
	 *
1236
	 * @param string $content Content to encrypt
1237
	 * @param string $message Message shown if Javascript is disabled
1238
	 *
1239
	 * @see  https://github.com/jnicol/standalone-phpenkoder StandalonePHPEnkoder on Github
1240
	 *
1241
	 * @since 1.7
1242
	 *
1243
	 * @return string Content, encrypted
1244
	 */
1245
	public static function js_encrypt( $content, $message = '' ) {
1246
1247
		$output = $content;
1248
1249
		if ( ! class_exists( 'StandalonePHPEnkoder' ) ) {
1250
			include_once( GRAVITYVIEW_DIR . 'includes/lib/standalone-phpenkoder/StandalonePHPEnkoder.php' );
1251
		}
1252
1253
		if ( class_exists( 'StandalonePHPEnkoder' ) ) {
1254
1255
			$enkoder = new StandalonePHPEnkoder;
1256
1257
			$message = empty( $message ) ? __( 'Email hidden; Javascript is required.', 'gravityview' ) : $message;
1258
1259
			/**
1260
			 * @filter `gravityview/phpenkoder/msg` Modify the message shown when Javascript is disabled and an encrypted email field is displayed
1261
			 * @since 1.7
1262
			 * @param string $message Existing message
1263
			 * @param string $content Content to encrypt
1264
			 */
1265
			$enkoder->enkode_msg = apply_filters( 'gravityview/phpenkoder/msg', $message, $content );
1266
1267
			$output = $enkoder->enkode( $content );
1268
		}
1269
1270
		return $output;
1271
	}
1272
1273
	/**
1274
	 *
1275
	 * Do the same than parse_str without max_input_vars limitation:
1276
	 * Parses $string as if it were the query string passed via a URL and sets variables in the current scope.
1277
	 * @param $string array string to parse (not altered like in the original parse_str(), use the second parameter!)
1278
	 * @param $result array  If the second parameter is present, variables are stored in this variable as array elements
1279
	 * @return bool true or false if $string is an empty string
1280
	 * @since  1.5.3
1281
	 *
1282
	 * @author rubo77 at https://gist.github.com/rubo77/6821632
1283
	 **/
1284
	public static function gv_parse_str( $string, &$result ) {
1285
		if ( empty( $string ) ) {
1286
			return false;
1287
		}
1288
1289
		$result = array();
1290
1291
		// find the pairs "name=value"
1292
		$pairs = explode( '&', $string );
1293
1294
		foreach ( $pairs as $pair ) {
1295
			// use the original parse_str() on each element
1296
			parse_str( $pair, $params );
1297
1298
			$k = key( $params );
1299
			if ( ! isset( $result[ $k ] ) ) {
1300
				$result += $params;
1301
			} elseif ( array_key_exists( $k, $params ) && is_array( $params[ $k ] ) ) {
1302
				$result[ $k ] = self::array_merge_recursive_distinct( $result[ $k ], $params[ $k ] );
1303
			}
1304
		}
1305
		return true;
1306
	}
1307
1308
1309
	/**
1310
	 * Generate an HTML anchor tag with a list of supported attributes
1311
	 *
1312
	 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a Supported attributes defined here
1313
	 * @uses esc_url_raw() to sanitize $href
1314
	 * @uses esc_attr() to sanitize $atts
1315
	 *
1316
	 * @since 1.6
1317
	 *
1318
	 * @param string $href URL of the link. Sanitized using `esc_url_raw()`
1319
	 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1320
	 * @param array|string $atts Attributes to be added to the anchor tag. Parsed by `wp_parse_args()`, sanitized using `esc_attr()`
1321
	 *
1322
	 * @return string HTML output of anchor link. If empty $href, returns NULL
1323
	 */
1324
	public static function get_link_html( $href = '', $anchor_text = '', $atts = array() ) {
1325
1326
		// Supported attributes for anchor tags. HREF left out intentionally.
1327
		$allowed_atts = array(
1328
			'href' => null, // Will override the $href argument if set
1329
			'title' => null,
1330
			'rel' => null,
1331
			'id' => null,
1332
			'class' => null,
1333
			'target' => null,
1334
			'style' => null,
1335
1336
			// Used by GravityView
1337
			'data-viewid' => null,
1338
1339
			// Not standard
1340
			'hreflang' => null,
1341
			'type' => null,
1342
			'tabindex' => null,
1343
1344
			// Deprecated HTML4 but still used
1345
			'name' => null,
1346
			'onclick' => null,
1347
			'onchange' => null,
1348
			'onkeyup' => null,
1349
1350
			// HTML5 only
1351
			'download' => null,
1352
			'media' => null,
1353
			'ping' => null,
1354
		);
1355
1356
		/**
1357
		 * @filter `gravityview/get_link/allowed_atts` Modify the attributes that are allowed to be used in generating links
1358
		 * @param array $allowed_atts Array of attributes allowed
1359
		 */
1360
		$allowed_atts = apply_filters( 'gravityview/get_link/allowed_atts', $allowed_atts );
1361
1362
		// Make sure the attributes are formatted as array
1363
		$passed_atts = wp_parse_args( $atts );
1364
1365
		// Make sure the allowed attributes are only the ones in the $allowed_atts list
1366
		$final_atts = shortcode_atts( $allowed_atts, $passed_atts );
1367
1368
		// Remove attributes with empty values
1369
		$final_atts = array_filter( $final_atts );
1370
1371
		// If the href wasn't passed as an attribute, use the value passed to the function
1372
		if ( empty( $final_atts['href'] ) && ! empty( $href ) ) {
1373
			$final_atts['href'] = $href;
1374
		}
1375
1376
		$final_atts['href'] = esc_url_raw( $href );
1377
1378
		// Sort the attributes alphabetically, to help testing
1379
		ksort( $final_atts );
1380
1381
		// For each attribute, generate the code
1382
		$output = '';
1383
		foreach ( $final_atts as $attr => $value ) {
1384
			$output .= sprintf( ' %s="%s"', $attr, esc_attr( $value ) );
1385
		}
1386
1387
		if( '' !== $output ) {
1388
			$output = '<a' . $output . '>' . $anchor_text . '</a>';
1389
		}
1390
1391
		return $output;
1392
	}
1393
1394
	/**
1395
	 * array_merge_recursive does indeed merge arrays, but it converts values with duplicate
1396
	 * keys to arrays rather than overwriting the value in the first array with the duplicate
1397
	 * value in the second array, as array_merge does.
1398
	 *
1399
	 * @see http://php.net/manual/en/function.array-merge-recursive.php
1400
	 *
1401
	 * @since  1.5.3
1402
	 * @param array $array1
1403
	 * @param array $array2
1404
	 * @return array
1405
	 * @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
1406
	 * @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
1407
	 */
1408
	public static function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1409
		$merged = $array1;
1410
1411
		foreach ( $array2 as $key => &$value )  {
1412
			if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
1413
				$merged[ $key ] = self::array_merge_recursive_distinct( $merged[ $key ], $value );
1414
			} else {
1415
				$merged[ $key ] = $value;
1416
			}
1417
		}
1418
1419
		return $merged;
1420
	}
1421
1422
	/**
1423
	 * Get WordPress users with reasonable limits set
1424
	 *
1425
	 * @param string $context Where are we using this information (e.g. change_entry_creator, search_widget ..)
1426
	 * @param array $args Arguments to modify the user query. See get_users() {@since 1.14}
1427
	 * @return array Array of WP_User objects.
1428
	 */
1429
	public static function get_users( $context = 'change_entry_creator', $args = array() ) {
1430
1431
		$default_args = array(
1432
			'number' => 2000,
1433
			'orderby' => 'display_name',
1434
			'order' => 'ASC',
1435
			'fields' => array( 'ID', 'display_name', 'user_login', 'user_nicename' )
1436
		);
1437
1438
		// Merge in the passed arg
1439
		$get_users_settings = wp_parse_args( $args, $default_args );
1440
1441
		/**
1442
		 * @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
1443
		 * `$context` is where are we using this information (e.g. change_entry_creator, search_widget ..)
1444
		 * @param array $settings Settings array, with `number` key defining the # of users to display
1445
		 */
1446
		$get_users_settings = apply_filters( 'gravityview/get_users/'. $context, apply_filters( 'gravityview_change_entry_creator_user_parameters', $get_users_settings ) );
1447
1448
		return get_users( $get_users_settings );
1449
	}
1450
1451
1452
    /**
1453
     * Display updated/error notice
1454
     *
1455
     * @param string $notice text/HTML of notice
1456
     * @param string $class CSS class for notice (`updated` or `error`)
1457
     *
1458
     * @return string
1459
     */
1460
    public static function generate_notice( $notice, $class = '' ) {
1461
        return '<div class="gv-notice '.gravityview_sanitize_html_class( $class ) .'">'. $notice .'</div>';
1462
    }
1463
1464
	/**
1465
	 * Inspired on \GFCommon::encode_shortcodes, reverse the encoding by replacing the ascii characters by the shortcode brackets
1466
	 * @since 1.16.5
1467
	 * @param string $string Input string to decode
1468
	 * @return string $string Output string
1469
	 */
1470
	public static function decode_shortcodes( $string ) {
1471
		$replace = array( '[', ']', '"' );
1472
		$find = array( '&#91;', '&#93;', '&quot;' );
1473
		$string = str_replace( $find, $replace, $string );
1474
1475
		return $string;
1476
	}
1477
1478
1479
	/**
1480
	 * Send email using GFCommon::send_email()
1481
	 *
1482
	 * @since 1.17
1483
	 *
1484
	 * @see GFCommon::send_email This just makes the method public
1485
	 *
1486
	 * @param string $from               Sender address (required)
1487
	 * @param string $to                 Recipient address (required)
1488
	 * @param string $bcc                BCC recipients (required)
1489
	 * @param string $reply_to           Reply-to address (required)
1490
	 * @param string $subject            Subject line (required)
1491
	 * @param string $message            Message body (required)
1492
	 * @param string $from_name          Displayed name of the sender
1493
	 * @param string $message_format     If "html", sent text as `text/html`. Otherwise, `text/plain`. Default: "html".
1494
	 * @param string|array $attachments  Optional. Files to attach. {@see wp_mail()} for usage. Default: "".
1495
	 * @param array|false $entry         Gravity Forms entry array, related to the email. Default: false.
1496
	 * @param array|false $notification  Gravity Forms notification that triggered the email. {@see GFCommon::send_notification}. Default:false.
1497
	 */
1498
	public static function send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name = '', $message_format = 'html', $attachments = '', $entry = false, $notification = false ) {
1499
1500
		$SendEmail = new ReflectionMethod( 'GFCommon', 'send_email' );
1501
1502
		// It was private; let's make it public
1503
		$SendEmail->setAccessible( true );
1504
1505
		// 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...
1506
		// 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...
1507
		$SendEmail->invoke( new GFCommon, $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, $attachments, $entry, $notification );
1508
	}
1509
1510
1511
} //end class
1512
1513
1514
/**
1515
 * Generate an HTML anchor tag with a list of supported attributes
1516
 *
1517
 * @see GVCommon::get_link_html()
1518
 *
1519
 * @since 1.6
1520
 *
1521
 * @param string $href URL of the link.
1522
 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1523
 * @param array|string $atts Attributes to be added to the anchor tag
1524
 *
1525
 * @return string HTML output of anchor link. If empty $href, returns NULL
1526
 */
1527
function gravityview_get_link( $href = '', $anchor_text = '', $atts = array() ) {
1528
	return GVCommon::get_link_html( $href, $anchor_text, $atts );
1529
}
1530