Completed
Push — develop ( bd6ad5...7b688d )
by Zack
04:29
created

GVCommon::is_field_numeric()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 31
rs 6.7273
cc 7
eloc 13
nc 12
nop 2
1
<?php
2
/**
3
 * Set of common functions to separate main plugin from Gravity Forms API and other cross-plugin methods
4
 *
5
 * @package   GravityView
6
 * @license   GPL2+
7
 * @author    Katz Web Services, Inc.
8
 * @link      http://gravityview.co
9
 * @copyright Copyright 2014, Katz Web Services, Inc.
10
 *
11
 * @since 1.5.2
12
 */
13
14
/** If this file is called directly, abort. */
15
if ( ! defined( 'ABSPATH' ) ) {
16
	die;
17
}
18
19
class GVCommon {
20
21
	/**
22
	 * Returns the form object for a given Form ID.
23
	 *
24
	 * @access public
25
	 * @param mixed $form_id
26
	 * @return 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 View Code Duplication
	public static function get_all_views( $args = array() ) {
97
98
		$default_params = array(
99
			'post_type' => 'gravityview',
100
			'posts_per_page' => -1,
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;
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
	 * @return array
211
	 */
212
	public static function get_form_fields( $form = '', $add_default_properties = false, $include_parent_field = true ) {
213
214
		if ( ! is_array( $form ) ) {
215
			$form = self::get_form( $form );
216
		}
217
218
		$fields = array();
219
		$has_product_fields = false;
220
		$has_post_fields = false;
221
		$has_quiz_fields = false;
222
		$has_poll_fields = false;
0 ignored issues
show
Unused Code introduced by
$has_poll_fields is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
223
224
		// If GF_Field exists, we're using GF 1.9+, where add_default_properties has been deprecated.
225
		if ( false === class_exists( 'GF_Field' ) && $add_default_properties ) {
226
			$form = RGFormsModel::add_default_properties( $form );
227
		}
228
229
		if ( $form ) {
230
			foreach ( $form['fields'] as $field ) {
231
				if ( $include_parent_field || empty( $field['inputs'] ) ) {
232
					$fields[ $field['id'] ] = array(
233
						'label' => rgar( $field, 'label' ),
234
						'parent' => null,
235
						'type' => rgar( $field, 'type' ),
236
						'adminLabel' => rgar( $field, 'adminLabel' ),
237
						'adminOnly' => rgar( $field, 'adminOnly' ),
238
					);
239
				}
240
241
				if ( $add_default_properties && ! empty( $field['inputs'] ) ) {
242
					foreach ( $field['inputs'] as $input ) {
243
                        /**
244
                         * @hack
245
                         * In case of email/email confirmation, the input for email has the same id as the parent field
246
                         */
247
                        if( 'email' == rgar( $field, 'type' ) && false === strpos( $input['id'], '.' ) ) {
248
                            continue;
249
                        }
250
						$fields[ (string)$input['id'] ] = array(
251
							'label' => rgar( $input, 'label' ),
252
							'customLabel' => rgar( $input, 'customLabel' ),
253
							'parent' => $field,
254
							'type' => rgar( $field, 'type' ),
255
							'adminLabel' => rgar( $field, 'adminLabel' ),
256
							'adminOnly' => rgar( $field, 'adminOnly' ),
257
						);
258
					}
259
				}
260
261
				/** @since 1.14 */
262
				if( 'list' === $field['type'] && !empty( $field['enableColumns'] ) ) {
263
264
					foreach ( (array)$field['choices'] as $key => $input ) {
265
266
						$input_id = sprintf( '%d.%d', $field['id'], $key ); // {field_id}.{column_key}
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
267
268
						$fields[ $input_id ] = array(
269
							'label'       => rgar( $input, 'text' ),
270
							'customLabel' => '',
271
							'parent'      => $field,
272
							'type'        => rgar( $field, 'type' ),
273
							'adminLabel'  => rgar( $field, 'adminLabel' ),
274
							'adminOnly'   => rgar( $field, 'adminOnly' ),
275
						);
276
					}
277
				}
278
279
				/**
280
				 * @since 1.8
281
				 */
282
				if( 'quiz' === $field['type'] ) {
283
					$has_quiz_fields = true;
284
				}
285
286
				/**
287
				 * @since 1.8
288
				 */
289
				if( 'poll' === $field['type'] ) {
290
					$has_poll_fields = true;
0 ignored issues
show
Unused Code introduced by
$has_poll_fields is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
291
				}
292
293
				if( GFCommon::is_product_field( $field['type'] ) ){
294
					$has_product_fields = true;
295
				}
296
297
				/**
298
				 * @hack Version 1.9
299
				 */
300
				$field_for_is_post_field = class_exists( 'GF_Fields' ) ? (object) $field : (array) $field;
301
302
				if ( GFCommon::is_post_field( $field_for_is_post_field ) ) {
303
					$has_post_fields = true;
304
				}
305
			}
306
		}
307
308
		/**
309
		 * @since 1.7
310
		 */
311
		if ( $has_post_fields ) {
312
			$fields['post_id'] = array(
313
				'label' => __( 'Post ID', 'gravityview' ),
314
				'type' => 'post_id',
315
			);
316
		}
317
318
		if ( $has_product_fields ) {
319
320
			$fields['payment_status'] = array(
321
				'label' => __( 'Payment Status', 'gravityview' ),
322
				'type' => 'payment_status',
323
			);
324
325
			$fields['payment_date'] = array(
326
				'label' => __( 'Payment Date', 'gravityview' ),
327
				'type' => 'payment_date',
328
			);
329
330
			$fields['payment_amount'] = array(
331
				'label' => __( 'Payment Amount', 'gravityview' ),
332
				'type' => 'payment_amount',
333
			);
334
335
			$fields['payment_method'] = array(
336
				'label' => __( 'Payment Method', 'gravityview' ),
337
				'type' => 'payment_method',
338
			);
339
340
			$fields['is_fulfilled'] = array(
341
				'label' => __( 'Is Fulfilled', 'gravityview' ),
342
				'type' => 'is_fulfilled',
343
			);
344
345
			$fields['transaction_id'] = array(
346
				'label' => __( 'Transaction ID', 'gravityview' ),
347
				'type' => 'transaction_id',
348
			);
349
350
			$fields['transaction_type'] = array(
351
				'label' => __( 'Transaction Type', 'gravityview' ),
352
				'type' => 'transaction_type',
353
			);
354
355
		}
356
357
		/**
358
		 * @since 1.8
359
		 */
360
		if( $has_quiz_fields ) {
361
362
			$fields['gquiz_score']   = array(
363
				'label' => __( 'Quiz Score Total', 'gravityview' ),
364
				'type'  => 'quiz_score',
365
				'desc'  => __( 'Displays the number of correct Quiz answers the user submitted.', 'gravityview' ),
366
			);
367
			$fields['gquiz_percent'] = array(
368
				'label' => __( 'Quiz Percentage Grade', 'gravityview' ),
369
				'type'  => 'quiz_percent',
370
				'desc'  => __( 'Displays the percentage of correct Quiz answers the user submitted.', 'gravityview' ),
371
			);
372
			$fields['gquiz_grade']   = array(
373
				'label' => __( 'Quiz Letter Grade', 'gravityview' ),
374
				'type'  => 'quiz_grade',
375
				'desc'  => __( 'Displays the Grade the user achieved based on Letter Grading configured in the Quiz Settings.', 'gravityview' ),
376
			);
377
			$fields['gquiz_is_pass'] = array(
378
				'label' => __( 'Quiz Pass/Fail', 'gravityview' ),
379
				'type'  => 'quiz_is_pass',
380
				'desc'  => __( 'Displays either Passed or Failed based on the Pass/Fail settings configured in the Quiz Settings.', 'gravityview' ),
381
			);
382
		}
383
384
		return $fields;
385
386
	}
387
388
	/**
389
	 * get extra fields from entry meta
390
	 * @param  string $form_id (default: '')
391
	 * @return array
392
	 */
393
	public static function get_entry_meta( $form_id, $only_default_column = true ) {
394
395
		$extra_fields = GFFormsModel::get_entry_meta( $form_id );
396
397
		$fields = array();
398
399
		foreach ( $extra_fields as $key => $field ){
400
			if ( ! empty( $only_default_column ) && ! empty( $field['is_default_column'] ) ) {
401
				$fields[ $key ] = array( 'label' => $field['label'], 'type' => 'entry_meta' );
402
			}
403
		}
404
405
		return $fields;
406
	}
407
408
409
	/**
410
	 * Wrapper for the Gravity Forms GFFormsModel::search_lead_ids() method
411
	 *
412
	 * @see  GFEntryList::leads_page()
413
	 * @param  int $form_id ID of the Gravity Forms form
414
	 * @since  1.1.6
415
	 * @return array|void          Array of entry IDs. Void if Gravity Forms isn't active.
416
	 */
417
	public static function get_entry_ids( $form_id, $search_criteria = array() ) {
418
419
		if ( ! class_exists( 'GFFormsModel' ) ) {
420
			return;
421
		}
422
423
		return GFFormsModel::search_lead_ids( $form_id, $search_criteria );
424
	}
425
426
	/**
427
	 * Calculates the Search Criteria used on the self::get_entries / self::get_entry methods
428
	 *
429
	 * @since 1.7.4
430
	 *
431
	 * @param array $passed_criteria array Input Criteria (search_criteria, sorting, paging)
432
	 * @param array $form_ids array Gravity Forms form IDs
433
	 * @return array
434
	 */
435
	public static function calculate_get_entries_criteria( $passed_criteria = array(), $form_ids = array() ) {
436
437
		$search_criteria_defaults = array(
438
			'search_criteria' => null,
439
			'sorting' => null,
440
			'paging' => null,
441
			'cache' => (isset( $passed_criteria['cache'] ) ? $passed_criteria['cache'] : true),
442
		);
443
444
		$criteria = wp_parse_args( $passed_criteria, $search_criteria_defaults );
445
446
		if ( ! empty( $criteria['search_criteria']['field_filters'] ) ) {
447
			foreach ( $criteria['search_criteria']['field_filters'] as &$filter ) {
448
449
				if ( ! is_array( $filter ) ) {
450
					continue;
451
				}
452
453
				// By default, we want searches to be wildcard for each field.
454
				$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
455
456
				/**
457
				 * @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc)
458
				 * @param string $operator Existing search operator
459
				 * @param array $filter array with `key`, `value`, `operator`, `type` keys
460
				 */
461
				$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
462
			}
463
		}
464
465
		/**
466
		 * Prepare date formats to be in Gravity Forms DB format;
467
		 * $passed_criteria may include date formats incompatible with Gravity Forms.
468
		 */
469
		foreach ( array('start_date', 'end_date' ) as $key ) {
470
471
			if ( ! empty( $criteria['search_criteria'][ $key ] ) ) {
472
473
				// Use date_create instead of new DateTime so it returns false if invalid date format.
474
				$date = date_create( $criteria['search_criteria'][ $key ] );
475
476
				if ( $date ) {
477
					// Gravity Forms wants dates in the `Y-m-d H:i:s` format.
478
					$criteria['search_criteria'][ $key ] = $date->format( 'Y-m-d H:i:s' );
479
				} else {
480
					// If it's an invalid date, unset it. Gravity Forms freaks out otherwise.
481
					unset( $criteria['search_criteria'][ $key ] );
482
483
					do_action( 'gravityview_log_error', '[filter_get_entries_criteria] '.$key.' Date format not valid:', $criteria['search_criteria'][ $key ] );
484
				}
485
			}
486
		}
487
488
489
		// When multiple views are embedded, OR single entry, calculate the context view id and send it to the advanced filter
490
		if ( class_exists( 'GravityView_View_Data' ) && GravityView_View_Data::getInstance()->has_multiple_views() || GravityView_frontend::getInstance()->getSingleEntry() ) {
491
			$criteria['context_view_id'] = GravityView_frontend::getInstance()->get_context_view_id();
492
		} elseif ( 'delete' === RGForms::get( 'action' ) ) {
493
			$criteria['context_view_id'] = isset( $_GET['view_id'] ) ? $_GET['view_id'] : null;
494
		} elseif( !isset( $criteria['context_view_id'] ) ) {
495
            // Prevent overriding the Context View ID: Some widgets could set the context_view_id (e.g. Recent Entries widget)
496
			$criteria['context_view_id'] = null;
497
		}
498
499
		/**
500
		 * @filter `gravityview_search_criteria` Apply final criteria filter (Used by the Advanced Filter extension)
501
		 * @param array $criteria Search criteria used by GravityView
502
		 * @param array $form_ids Forms to search
503
		 * @param int $view_id ID of the view being used to search
504
		 */
505
		$criteria = apply_filters( 'gravityview_search_criteria', $criteria, $form_ids, $criteria['context_view_id'] );
506
507
		return (array)$criteria;
508
509
	}
510
511
512
	/**
513
	 * Retrieve entries given search, sort, paging criteria
514
	 *
515
	 * @see  GFAPI::get_entries()
516
	 * @see GFFormsModel::get_field_filters_where()
517
	 * @access public
518
	 * @param int|array $form_ids The ID of the form or an array IDs of the Forms. Zero for all forms.
519
	 * @param mixed $passed_criteria (default: null)
520
	 * @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)
521
	 * @return mixed False: Error fetching entries. Array: Multi-dimensional array of Gravity Forms entry arrays
522
	 */
523
	public static function get_entries( $form_ids = null, $passed_criteria = null, &$total = null ) {
524
525
		// Filter the criteria before query (includes Adv Filter)
526
		$criteria = self::calculate_get_entries_criteria( $passed_criteria, $form_ids );
0 ignored issues
show
Bug introduced by
It seems like $form_ids defined by parameter $form_ids on line 523 can also be of type integer or null; however, GVCommon::calculate_get_entries_criteria() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
527
528
		do_action( 'gravityview_log_debug', '[gravityview_get_entries] Final Parameters', $criteria );
529
530
		// Return value
531
		$return = null;
532
533
		/** Reduce # of database calls */
534
		add_filter( 'gform_is_encrypted_field', '__return_false' );
535
536
		if ( ! empty( $criteria['cache'] ) ) {
537
538
			$Cache = new GravityView_Cache( $form_ids, $criteria );
539
540
			if ( $entries = $Cache->get() ) {
541
542
				// Still update the total count when using cached results
543
				if ( ! is_null( $total ) ) {
544
					$total = GFAPI::count_entries( $form_ids, $criteria['search_criteria'] );
545
				}
546
547
				$return = $entries;
548
			}
549
		}
550
551
		if ( is_null( $return ) && class_exists( 'GFAPI' ) && ( is_numeric( $form_ids ) || is_array( $form_ids ) ) ) {
552
553
			/**
554
			 * @filter `gravityview_pre_get_entries` Define entries to be used before GFAPI::get_entries() is called
555
			 * @since 1.14
556
			 * @param  null $return If you want to override GFAPI::get_entries() and define entries yourself, tap in here.
557
			 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
558
			 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
559
			 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
560
			 */
561
			$entries = apply_filters( 'gravityview_before_get_entries', null, $criteria, $passed_criteria, $total );
562
563
			// No entries returned from gravityview_before_get_entries
564
			if( is_null( $entries ) ) {
565
566
				$entries = GFAPI::get_entries( $form_ids, $criteria['search_criteria'], $criteria['sorting'], $criteria['paging'], $total );
567
568
				if ( is_wp_error( $entries ) ) {
569
					do_action( 'gravityview_log_error', $entries->get_error_message(), $entries );
570
571
					return false;
572
				}
573
			}
574
575
			if ( ! empty( $criteria['cache'] ) && isset( $Cache ) ) {
576
577
				// Cache results
578
				$Cache->set( $entries, 'entries' );
579
580
			}
581
582
			$return = $entries;
583
		}
584
585
		/** Remove filter added above */
586
		remove_filter( 'gform_is_encrypted_field', '__return_false' );
587
588
		/**
589
		 * @filter `gravityview_entries` Modify the array of entries returned to GravityView after it has been fetched from the cache or from `GFAPI::get_entries()`.
590
		 * @param  array|null $entries Array of entries as returned by the cache or by `GFAPI::get_entries()`
591
		 * @param  array $criteria The final search criteria used to generate the request to `GFAPI::get_entries()`
592
		 * @param array $passed_criteria The original search criteria passed to `GVCommon::get_entries()`
593
		 * @param  int|null $total Optional. An output parameter containing the total number of entries. Pass a non-null value to generate
594
		 */
595
		$return = apply_filters( 'gravityview_entries', $return, $criteria, $passed_criteria, $total );
596
597
		return $return;
598
	}
599
600
601
	/**
602
	 * Return a single entry object
603
	 *
604
	 * 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()`
605
	 *
606
	 * @access public
607
	 * @param string|int $entry_slug Either entry ID or entry slug string
608
	 * @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.
609
	 * @param boolean $check_entry_display Check whether the entry is visible for the current View configuration. Default: true. {@since 1.14}
610
	 * @return array|boolean
611
	 */
612
	public static function get_entry( $entry_slug, $force_allow_ids = false, $check_entry_display = true ) {
613
614
		if ( class_exists( 'GFAPI' ) && ! empty( $entry_slug ) ) {
615
616
			/**
617
			 * @filter `gravityview_custom_entry_slug` Whether to enable and use custom entry slugs.
618
			 * @param boolean True: Allow for slugs based on entry values. False: always use entry IDs (default)
619
			 */
620
			$custom_slug = apply_filters( 'gravityview_custom_entry_slug', false );
621
622
			/**
623
			 * @filter `gravityview_custom_entry_slug_allow_id` When using a custom slug, allow access to the entry using the original slug (the Entry ID).
624
			 * - If disabled (default), only allow access to an entry using the custom slug value.  (example: `/entry/custom-slug/` NOT `/entry/123/`)
625
			 * - If enabled, you could access using the custom slug OR the entry id (example: `/entry/custom-slug/` OR `/entry/123/`)
626
			 * @param boolean $custom_slug_id_access True: allow accessing the slug by ID; False: only use the slug passed to the method.
627
			 */
628
			$custom_slug_id_access = $force_allow_ids || apply_filters( 'gravityview_custom_entry_slug_allow_id', false );
629
630
			/**
631
			 * If we're using custom entry slugs, we do a meta value search
632
			 * instead of doing a straightup ID search.
633
			 */
634
			if ( $custom_slug ) {
635
636
				$entry_id = self::get_entry_id_from_slug( $entry_slug );
637
638
			}
639
640
			// If custom slug is off, search using the entry ID
641
			// ID allow ID access is on, also use entry ID as a backup
642
			if ( empty( $custom_slug ) || ! empty( $custom_slug_id_access ) ) {
643
				// Search for IDs matching $entry_slug
644
				$entry_id = $entry_slug;
645
			}
646
647
			if ( empty( $entry_id ) ) {
648
				return false;
649
			}
650
651
			// fetch the entry
652
			$entry = GFAPI::get_entry( $entry_id );
653
654
			if( $check_entry_display ) {
655
				// Is the entry allowed
656
				$entry = self::check_entry_display( $entry );
657
			}
658
659
			return is_wp_error( $entry ) ? false : $entry;
660
661
		}
662
663
		return false;
664
	}
665
666
	/**
667
	 * Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including:
668
	 * 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals',
669
	 * and 'not_contains'
670
	 *
671
	 * @since 1.13 You can define context, which displays/hides based on what's being displayed (single, multiple, edit)
672
	 *
673
	 * @see http://docs.gravityview.co/article/252-gvlogic-shortcode
674
	 * @uses GFFormsModel::matches_operation
675
	 * @since 1.7.5
676
	 *
677
	 * @param string $val1 Left side of comparison
678
	 * @param string $val2 Right side of comparison
679
	 * @param string $operation Type of comparison
680
	 *
681
	 * @return bool True: matches, false: not matches
682
	 */
683
	public static function matches_operation( $val1, $val2, $operation ) {
684
685
		$value = false;
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
686
687
		if( 'context' === $val1 ) {
688
689
			$matching_contexts = array( $val2 );
690
691
			// We allow for non-standard contexts.
692
			switch( $val2 ) {
693
				// Check for either single or edit
694
				case 'singular':
695
					$matching_contexts = array( 'single', 'edit' );
696
					break;
697
				// Use multiple as alias for directory for consistency
698
				case 'multiple':
699
					$matching_contexts = array( 'directory' );
700
					break;
701
			}
702
703
			$val1 = in_array( gravityview_get_context(), $matching_contexts ) ? $val2 : false;
704
		}
705
706
		switch ( $operation ) {
707
			case 'equals':
708
				$value = GFFormsModel::matches_operation( $val1, $val2, 'is' );
709
				break;
710
			case 'greater_than_or_is':
711 View Code Duplication
			case 'greater_than_or_equals':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
712
				$is    = GFFormsModel::matches_operation( $val1, $val2, 'is' );
713
				$gt    = GFFormsModel::matches_operation( $val1, $val2, 'greater_than' );
714
				$value = ( $is || $gt );
715
				break;
716
			case 'less_than_or_is':
717 View Code Duplication
			case 'less_than_or_equals':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
718
				$is    = GFFormsModel::matches_operation( $val1, $val2, 'is' );
719
				$gt    = GFFormsModel::matches_operation( $val1, $val2, 'less_than' );
720
				$value = ( $is || $gt );
721
				break;
722
			case 'not_contains':
723
				$contains = GFFormsModel::matches_operation( $val1, $val2, 'contains' );
724
				$value    = ! $contains;
725
				break;
726
			default:
727
				$value = GFFormsModel::matches_operation( $val1, $val2, $operation );
728
		}
729
730
		return $value;
731
	}
732
733
	/**
734
	 *
735
	 * Checks if a certain entry is valid according to the View search filters (specially the Adv Filters)
736
	 *
737
	 * @see GFFormsModel::is_value_match()
738
	 *
739
	 * @since 1.7.4
740
	 * @todo Return WP_Error instead of boolean
741
	 *
742
	 * @param array $entry Gravity Forms Entry object
743
	 * @return bool|array Returns 'false' if entry is not valid according to the view search filters (Adv Filter)
744
	 */
745
	public static function check_entry_display( $entry ) {
746
747
		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...
748
			do_action( 'gravityview_log_debug', __METHOD__ . ' Entry was not found.', $entry );
749
			return false;
750
		}
751
752
		if ( empty( $entry['form_id'] ) ) {
753
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry is empty! Entry:', $entry );
754
			return false;
755
		}
756
757
		$criteria = self::calculate_get_entries_criteria();
758
759
		// Make sure the current View is connected to the same form as the Entry
760
		if( ! empty( $criteria['context_view_id'] ) ) {
761
			$context_view_id = intval( $criteria['context_view_id'] );
762
			$context_form_id = gravityview_get_form_id( $context_view_id );
763
			if( intval( $context_form_id ) !== intval( $entry['form_id'] ) ) {
764
				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'] );
765
				return false;
766
			}
767
		}
768
769 View Code Duplication
		if ( empty( $criteria['search_criteria'] ) || ! is_array( $criteria['search_criteria'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
770
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No search criteria found:', $criteria );
771
			return $entry;
772
		}
773
774
		$search_criteria = $criteria['search_criteria'];
775
		unset( $criteria );
776
777
		// check entry status
778
		if ( array_key_exists( 'status', $search_criteria ) && $search_criteria['status'] != $entry['status'] ) {
779
			do_action( 'gravityview_log_debug', sprintf( '[apply_filters_to_entry] Entry status - %s - is not valid according to filter:', $entry['status'] ), $search_criteria );
780
			return false;
781
		}
782
783
		// check entry date
784
		// @todo: Does it make sense to apply the Date create filters to the single entry?
785
786
		// field_filters
787 View Code Duplication
		if ( empty( $search_criteria['field_filters'] ) || ! is_array( $search_criteria['field_filters'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
788
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry approved! No field filters criteria found:', $search_criteria );
789
			return $entry;
790
		}
791
792
		$filters = $search_criteria['field_filters'];
793
		unset( $search_criteria );
794
795
		$mode = array_key_exists( 'mode', $filters ) ? strtolower( $filters['mode'] ) : 'all';
796
		unset( $filters['mode'] );
797
798
		$form = self::get_form( $entry['form_id'] );
799
800
		foreach ( $filters as $filter ) {
801
802
			if ( ! isset( $filter['key'] ) ) {
803
				continue;
804
			}
805
806
			$k = $filter['key'];
807
808
			if ( in_array( $k, array( 'created_by', 'payment_status' ) ) ) {
809
				$field_value = $entry[ $k ];
810
				$field = null;
811
			} else {
812
				$field = self::get_field( $form, $k );
813
				$field_value  = GFFormsModel::get_lead_field_value( $entry, $field );
814
			}
815
816
			$operator = isset( $filter['operator'] ) ? strtolower( $filter['operator'] ) : 'is';
817
			$is_value_match = GFFormsModel::is_value_match( $field_value, $filter['value'], $operator, $field );
818
819
			// verify if we are already free to go!
820
			if ( ! $is_value_match && 'all' === $mode ) {
821
				do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry cannot be displayed. Failed one criteria for ALL mode', $filter );
822
				return false;
823
			} elseif ( $is_value_match && 'any' === $mode ) {
824
				return $entry;
825
			}
826
		}
827
828
		// at this point, if in ALL mode, then entry is approved - all conditions were met.
829
		// Or, for ANY mode, means none of the conditions were satisfied, so entry is not approved
830
		if ( 'all' === $mode ) {
831
			return $entry;
832
		} else {
833
			do_action( 'gravityview_log_debug', '[apply_filters_to_entry] Entry cannot be displayed. Failed all the criteria for ANY mode', $filters );
834
			return false;
835
		}
836
837
	}
838
839
840
	/**
841
	 * Retrieve the label of a given field id (for a specific form)
842
	 *
843
	 * @access public
844
	 * @param array $form
845
	 * @param string $field_id
846
	 * @return string
847
	 */
848
	public static function get_field_label( $form = array(), $field_id = '' ) {
849
850
		if ( empty( $form ) || empty( $field_id ) ) {
851
			return '';
852
		}
853
854
		$field = self::get_field( $form, $field_id );
855
		return isset( $field['label'] ) ?  $field['label'] : '';
856
857
	}
858
859
860
	/**
861
	 * Returns the field details array of a specific form given the field id
862
	 *
863
	 * Alias of GFFormsModel::get_field
864
	 *
865
	 * @uses GFFormsModel::get_field
866
	 * @see GFFormsModel::get_field
867
	 * @access public
868
	 * @param array $form
869
	 * @param string|int $field_id
870
	 * @return array|null Array: Gravity Forms field array; NULL: Gravity Forms GFFormsModel does not exist
871
	 */
872
	public static function get_field( $form, $field_id ) {
873
		if ( class_exists( 'GFFormsModel' ) ){
874
			return GFFormsModel::get_field( $form, $field_id );
875
		} else {
876
			return null;
877
		}
878
	}
879
880
881
	/**
882
	 * Check whether the post is GravityView
883
	 *
884
	 * - Check post type. Is it `gravityview`?
885
	 * - Check shortcode
886
	 *
887
	 * @param  WP_Post      $post WordPress post object
888
	 * @return boolean           True: yep, GravityView; No: not!
889
	 */
890
	public static function has_gravityview_shortcode( $post = null ) {
891
		if ( ! is_a( $post, 'WP_Post' ) ) {
892
			return false;
893
		}
894
895
		if ( 'gravityview' === get_post_type( $post ) ) {
896
			return true;
897
		}
898
899
		return self::has_shortcode_r( $post->post_content, 'gravityview' ) ? true : false;
900
901
	}
902
903
904
	/**
905
	 * Placeholder until the recursive has_shortcode() patch is merged
906
	 * @see https://core.trac.wordpress.org/ticket/26343#comment:10
907
	 * @param string $content Content to check whether there's a shortcode
908
	 * @param string $tag Current shortcode tag
909
	 */
910
	public static function has_shortcode_r( $content, $tag = 'gravityview' ) {
911
		if ( false === strpos( $content, '[' ) ) {
912
			return false;
913
		}
914
915
		if ( shortcode_exists( $tag ) ) {
916
917
			$shortcodes = array();
918
919
			preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
920
			if ( empty( $matches ) ){
921
				return false;
922
			}
923
924
			foreach ( $matches as $shortcode ) {
925
				if ( $tag === $shortcode[2] ) {
926
927
					// Changed this to $shortcode instead of true so we get the parsed atts.
928
					$shortcodes[] = $shortcode;
929
930
				} else if ( isset( $shortcode[5] ) && $result = self::has_shortcode_r( $shortcode[5], $tag ) ) {
931
					$shortcodes = $result;
932
				}
933
			}
934
935
			return $shortcodes;
936
		}
937
		return false;
938
	}
939
940
941
942
	/**
943
	 * Get the views for a particular form
944
	 *
945
	 * @since 1.15.2 Add $args array and limit posts_per_page to 500
946
	 *
947
	 * @uses get_posts()
948
	 *
949
	 * @param  int $form_id Gravity Forms form ID
950
	 * @param  array $args Pass args sent to get_posts()
951
	 *
952
	 * @return array          Array with view details, as returned by get_posts()
953
	 */
954 View Code Duplication
	public static function get_connected_views( $form_id, $args = array() ) {
955
956
		$defaults = array(
957
			'post_type' => 'gravityview',
958
			'posts_per_page' => 100,
959
			'meta_key' => '_gravityview_form_id',
960
			'meta_value' => (int)$form_id,
961
		);
962
963
		$args = wp_parse_args( $args, $defaults );
964
965
		$views = get_posts( $args );
966
967
		return $views;
968
	}
969
970
	/**
971
	 * Get the Gravity Forms form ID connected to a View
972
	 *
973
	 * @param int $view_id The ID of the View to get the connected form of
974
	 *
975
	 * @return string ID of the connected Form, if exists. Empty string if not.
976
	 */
977
	public static function get_meta_form_id( $view_id ) {
978
		return get_post_meta( $view_id, '_gravityview_form_id', true );
979
	}
980
981
	/**
982
	 * Get the template ID (`list`, `table`, `datatables`, `map`) for a View
983
	 *
984
	 * @see GravityView_Template::template_id
985
	 *
986
	 * @param int $view_id The ID of the View to get the layout of
987
	 *
988
	 * @return string GravityView_Template::template_id value. Empty string if not.
989
	 */
990
	public static function get_meta_template_id( $view_id ) {
991
		return get_post_meta( $view_id, '_gravityview_directory_template', true );
992
	}
993
994
995
	/**
996
	 * Get all the settings for a View
997
	 *
998
	 * @uses  GravityView_View_Data::get_default_args() Parses the settings with the plugin defaults as backups.
999
	 * @param  int $post_id View ID
1000
	 * @return array          Associative array of settings with plugin defaults used if not set by the View
1001
	 */
1002
	public static function get_template_settings( $post_id ) {
1003
1004
		$settings = get_post_meta( $post_id, '_gravityview_template_settings', true );
1005
1006
		if ( class_exists( 'GravityView_View_Data' ) ) {
1007
1008
			$defaults = GravityView_View_Data::get_default_args();
1009
1010
			return wp_parse_args( (array)$settings, $defaults );
1011
1012
		}
1013
1014
		// Backup, in case GravityView_View_Data isn't loaded yet.
1015
		return $settings;
1016
	}
1017
1018
	/**
1019
	 * Get the setting for a View
1020
	 *
1021
	 * If the setting isn't set by the View, it returns the plugin default.
1022
	 *
1023
	 * @param  int $post_id View ID
1024
	 * @param  string $key     Key for the setting
1025
	 * @return mixed|null          Setting value, or NULL if not set.
1026
	 */
1027
	public static function get_template_setting( $post_id, $key ) {
1028
1029
		$settings = self::get_template_settings( $post_id );
1030
1031
		if ( isset( $settings[ $key ] ) ) {
1032
			return $settings[ $key ];
1033
		}
1034
1035
		return null;
1036
	}
1037
1038
	/**
1039
	 * Get the field configuration for the View
1040
	 *
1041
	 * array(
1042
	 *
1043
	 * 	[other zones]
1044
	 *
1045
	 * 	'directory_list-title' => array(
1046
	 *
1047
	 *   	[other fields]
1048
	 *
1049
	 *  	'5372653f25d44' => array(
1050
	 *  		'id' => string '9' (length=1)
1051
	 *  		'label' => string 'Screenshots' (length=11)
1052
	 *			'show_label' => string '1' (length=1)
1053
	 *			'custom_label' => string '' (length=0)
1054
	 *			'custom_class' => string 'gv-gallery' (length=10)
1055
	 * 			'only_loggedin' => string '0' (length=1)
1056
	 *			'only_loggedin_cap' => string 'read' (length=4)
1057
	 *  	)
1058
	 *
1059
	 * 		[other fields]
1060
	 *  )
1061
	 *
1062
	 * 	[other zones]
1063
	 * )
1064
	 *
1065
	 * @param  int $post_id View ID
1066
	 * @return array          Multi-array of fields with first level being the field zones. See code comment.
1067
	 */
1068
	public static function get_directory_fields( $post_id ) {
1069
		return get_post_meta( $post_id, '_gravityview_directory_fields', true );
1070
	}
1071
1072
1073
	/**
1074
	 * Render dropdown (select) with the list of sortable fields from a form ID
1075
	 *
1076
	 * @access public
1077
	 * @param  int $formid Form ID
1078
	 * @return string         html
1079
	 */
1080
	public static function get_sortable_fields( $formid, $current = '' ) {
1081
		$output = '<option value="" ' . selected( '', $current, false ).'>' . esc_html__( 'Default', 'gravityview' ) .'</option>';
1082
1083
		if ( empty( $formid ) ) {
1084
			return $output;
1085
		}
1086
1087
		$fields = self::get_sortable_fields_array( $formid );
1088
1089
		if ( ! empty( $fields ) ) {
1090
1091
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'list', 'textarea' ), null );
1092
1093
			foreach ( $fields as $id => $field ) {
1094
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
1095
					continue;
1096
				}
1097
1098
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'>'. esc_attr( $field['label'] ) .'</option>';
1099
			}
1100
		}
1101
1102
		return $output;
1103
	}
1104
1105
	/**
1106
	 *
1107
	 * @param int $formid Gravity Forms form ID
1108
	 * @param array $blacklist Field types to exclude
1109
	 *
1110
	 * @since TODO
1111
	 *
1112
	 * @todo Get all fields, check if sortable dynamically
1113
	 *
1114
	 * @return array
1115
	 */
1116
	public static function get_sortable_fields_array( $formid, $blacklist = array( 'list', 'textarea' ) ) {
1117
1118
		// Get fields with sub-inputs and no parent
1119
		$fields = self::get_form_fields( $formid, true, false );
1120
1121
		$date_created = array(
1122
			'date_created' => array(
1123
				'type' => 'date_created',
1124
				'label' => __( 'Date Created', 'gravityview' ),
1125
			),
1126
		);
1127
1128
        $fields = $date_created + $fields;
1129
1130
		$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', $blacklist, NULL );
1131
1132
		// TODO: Convert to using array_filter
1133
		foreach( $fields as $id => $field ) {
1134
1135
			if( in_array( $field['type'], $blacklist_field_types ) ) {
1136
				unset( $fields[ $id ] );
1137
			}
1138
		}
1139
1140
        /**
1141
         * @filter `gravityview/common/sortable_fields` Filter the sortable fields
1142
         * @since 1.12
1143
         * @param array $fields Sub-set of GF form fields that are sortable
1144
         * @param int $formid The Gravity Forms form ID that the fields are from
1145
         */
1146
        $fields = apply_filters( 'gravityview/common/sortable_fields', $fields, $formid );
1147
1148
		return $fields;
1149
	}
1150
1151
	/**
1152
	 * Returns the GF Form field type for a certain field(id) of a form
1153
	 * @param  object $form     Gravity Forms form
1154
	 * @param  mixed $field_id Field ID or Field array
1155
	 * @return string field type
1156
	 */
1157
	public static function get_field_type( $form = null, $field_id = '' ) {
1158
1159
		if ( ! empty( $field_id ) && ! is_array( $field_id ) ) {
1160
			$field = self::get_field( $form, $field_id );
0 ignored issues
show
Documentation introduced by
$form is of type object|null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1161
		} else {
1162
			$field = $field_id;
1163
		}
1164
1165
		return class_exists( 'RGFormsModel' ) ? RGFormsModel::get_input_type( $field ) : '';
1166
1167
	}
1168
1169
1170
	/**
1171
	 * Checks if the field type is a 'numeric' field type (e.g. to be used when sorting)
1172
	 * @param  int|array  $form  form ID or form array
1173
	 * @param  int|array  $field field key or field array
1174
	 * @return boolean
1175
	 */
1176
	public static function is_field_numeric(  $form = null, $field = '' ) {
1177
1178
		if ( ! is_array( $form ) && ! is_array( $field ) ) {
1179
			$form = self::get_form( $form );
1180
		}
1181
1182
		// If entry meta, it's a string. Otherwise, numeric
1183
		if( ! is_numeric( $field ) && is_string( $field ) ) {
1184
			$type = $field;
1185
		} else {
1186
			$type = self::get_field_type( $form, $field );
1187
		}
1188
1189
		/**
1190
		 * @filter `gravityview/common/numeric_types` What types of fields are numeric?
1191
		 * @since 1.5.2
1192
		 * @param array $numeric_types Fields that are numeric. Default: `[ number, time ]`
1193
		 */
1194
		$numeric_types = apply_filters( 'gravityview/common/numeric_types', array( 'number', 'time' ) );
1195
1196
		// Defer to GravityView_Field setting, if the field type is registered and `is_numeric` is true
1197
		if( $gv_field = GravityView_Fields::get( $type ) ) {
1198
			if( true === $gv_field->is_numeric ) {
0 ignored issues
show
Bug introduced by
The property is_numeric cannot be accessed from this context as it is declared private in class GravityView_Field.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1199
				$numeric_types[] = $gv_field->is_numeric;
0 ignored issues
show
Bug introduced by
The property is_numeric cannot be accessed from this context as it is declared private in class GravityView_Field.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1200
			}
1201
		}
1202
1203
		$return = in_array( $type, $numeric_types );
1204
1205
		return $return;
1206
	}
1207
1208
	/**
1209
	 * Encrypt content using Javascript so that it's hidden when JS is disabled.
1210
	 *
1211
	 * This is mostly used to hide email addresses from scraper bots.
1212
	 *
1213
	 * @param string $content Content to encrypt
1214
	 * @param string $message Message shown if Javascript is disabled
1215
	 *
1216
	 * @see  https://github.com/jnicol/standalone-phpenkoder StandalonePHPEnkoder on Github
1217
	 *
1218
	 * @since 1.7
1219
	 *
1220
	 * @return string Content, encrypted
1221
	 */
1222
	public static function js_encrypt( $content, $message = '' ) {
1223
1224
		$output = $content;
1225
1226
		if ( ! class_exists( 'StandalonePHPEnkoder' ) ) {
1227
			include_once( GRAVITYVIEW_DIR . 'includes/lib/standalone-phpenkoder/StandalonePHPEnkoder.php' );
1228
		}
1229
1230
		if ( class_exists( 'StandalonePHPEnkoder' ) ) {
1231
1232
			$enkoder = new StandalonePHPEnkoder;
1233
1234
			$message = empty( $message ) ? __( 'Email hidden; Javascript is required.', 'gravityview' ) : $message;
1235
1236
			/**
1237
			 * @filter `gravityview/phpenkoder/msg` Modify the message shown when Javascript is disabled and an encrypted email field is displayed
1238
			 * @since 1.7
1239
			 * @param string $message Existing message
1240
			 * @param string $content Content to encrypt
1241
			 */
1242
			$enkoder->enkode_msg = apply_filters( 'gravityview/phpenkoder/msg', $message, $content );
1243
1244
			$output = $enkoder->enkode( $content );
1245
		}
1246
1247
		return $output;
1248
	}
1249
1250
	/**
1251
	 *
1252
	 * Do the same than parse_str without max_input_vars limitation:
1253
	 * Parses $string as if it were the query string passed via a URL and sets variables in the current scope.
1254
	 * @param $string array string to parse (not altered like in the original parse_str(), use the second parameter!)
1255
	 * @param $result array  If the second parameter is present, variables are stored in this variable as array elements
1256
	 * @return bool true or false if $string is an empty string
1257
	 * @since  1.5.3
1258
	 *
1259
	 * @author rubo77 at https://gist.github.com/rubo77/6821632
1260
	 **/
1261
	public static function gv_parse_str( $string, &$result ) {
1262
		if ( empty( $string ) ) {
1263
			return false;
1264
		}
1265
1266
		$result = array();
1267
1268
		// find the pairs "name=value"
1269
		$pairs = explode( '&', $string );
1270
1271
		foreach ( $pairs as $pair ) {
1272
			// use the original parse_str() on each element
1273
			parse_str( $pair, $params );
1274
1275
			$k = key( $params );
1276 View Code Duplication
			if ( ! isset( $result[ $k ] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1277
				$result += $params;
1278
			} elseif ( array_key_exists( $k, $params ) && is_array( $params[ $k ] ) ) {
1279
				$result[ $k ] = self::array_merge_recursive_distinct( $result[ $k ], $params[ $k ] );
1280
			}
1281
		}
1282
		return true;
1283
	}
1284
1285
1286
	/**
1287
	 * Generate an HTML anchor tag with a list of supported attributes
1288
	 *
1289
	 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a Supported attributes defined here
1290
	 * @uses esc_url_raw() to sanitize $href
1291
	 * @uses esc_attr() to sanitize $atts
1292
	 *
1293
	 * @since 1.6
1294
	 *
1295
	 * @param string $href URL of the link. Sanitized using `esc_url_raw()`
1296
	 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1297
	 * @param array|string $atts Attributes to be added to the anchor tag. Parsed by `wp_parse_args()`, sanitized using `esc_attr()`
1298
	 *
1299
	 * @return string HTML output of anchor link. If empty $href, returns NULL
1300
	 */
1301
	public static function get_link_html( $href = '', $anchor_text = '', $atts = array() ) {
1302
1303
		// Supported attributes for anchor tags. HREF left out intentionally.
1304
		$allowed_atts = array(
1305
			'href' => null, // Will override the $href argument if set
1306
			'title' => null,
1307
			'rel' => null,
1308
			'id' => null,
1309
			'class' => null,
1310
			'target' => null,
1311
			'style' => null,
1312
1313
			// Used by GravityView
1314
			'data-viewid' => null,
1315
1316
			// Not standard
1317
			'hreflang' => null,
1318
			'type' => null,
1319
			'tabindex' => null,
1320
1321
			// Deprecated HTML4 but still used
1322
			'name' => null,
1323
			'onclick' => null,
1324
			'onchange' => null,
1325
			'onkeyup' => null,
1326
1327
			// HTML5 only
1328
			'download' => null,
1329
			'media' => null,
1330
			'ping' => null,
1331
		);
1332
1333
		/**
1334
		 * @filter `gravityview/get_link/allowed_atts` Modify the attributes that are allowed to be used in generating links
1335
		 * @param array $allowed_atts Array of attributes allowed
1336
		 */
1337
		$allowed_atts = apply_filters( 'gravityview/get_link/allowed_atts', $allowed_atts );
1338
1339
		// Make sure the attributes are formatted as array
1340
		$passed_atts = wp_parse_args( $atts );
1341
1342
		// Make sure the allowed attributes are only the ones in the $allowed_atts list
1343
		$final_atts = shortcode_atts( $allowed_atts, $passed_atts );
1344
1345
		// Remove attributes with empty values
1346
		$final_atts = array_filter( $final_atts );
1347
1348
		// If the href wasn't passed as an attribute, use the value passed to the function
1349
		if ( empty( $final_atts['href'] ) && ! empty( $href ) ) {
1350
			$final_atts['href'] = $href;
1351
		}
1352
1353
		$final_atts['href'] = esc_url_raw( $href );
1354
1355
		// For each attribute, generate the code
1356
		$output = '';
1357
		foreach ( $final_atts as $attr => $value ) {
1358
			$output .= sprintf( ' %s="%s"', $attr, esc_attr( $value ) );
1359
		}
1360
1361
		$output = '<a'. $output .'>'. $anchor_text .'</a>';
1362
1363
		return $output;
1364
	}
1365
1366
	/**
1367
	 * array_merge_recursive does indeed merge arrays, but it converts values with duplicate
1368
	 * keys to arrays rather than overwriting the value in the first array with the duplicate
1369
	 * value in the second array, as array_merge does.
1370
	 *
1371
	 * @see http://php.net/manual/en/function.array-merge-recursive.php
1372
	 *
1373
	 * @since  1.5.3
1374
	 * @param array $array1
1375
	 * @param array $array2
1376
	 * @return array
1377
	 * @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
1378
	 * @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
1379
	 */
1380
	public static function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1381
		$merged = $array1;
1382
1383
		foreach ( $array2 as $key => &$value )  {
1384 View Code Duplication
			if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1385
				$merged[ $key ] = self::array_merge_recursive_distinct( $merged[ $key ], $value );
1386
			} else {
1387
				$merged[ $key ] = $value;
1388
			}
1389
		}
1390
1391
		return $merged;
1392
	}
1393
1394
	/**
1395
	 * Get WordPress users with reasonable limits set
1396
	 *
1397
	 * @param string $context Where are we using this information (e.g. change_entry_creator, search_widget ..)
1398
	 * @param array $args Arguments to modify the user query. See get_users() {@since 1.14}
1399
	 * @return array Array of WP_User objects.
1400
	 */
1401
	public static function get_users( $context = 'change_entry_creator', $args = array() ) {
1402
1403
		$default_args = array(
1404
			'number' => 2000,
1405
			'orderby' => 'display_name',
1406
			'order' => 'ASC',
1407
			'fields' => array( 'ID', 'display_name', 'user_login', 'user_nicename' )
1408
		);
1409
1410
		// Merge in the passed arg
1411
		$get_users_settings = wp_parse_args( $args, $default_args );
1412
1413
		/**
1414
		 * @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
1415
		 * `$context` is where are we using this information (e.g. change_entry_creator, search_widget ..)
1416
		 * @param array $settings Settings array, with `number` key defining the # of users to display
1417
		 */
1418
		$get_users_settings = apply_filters( 'gravityview/get_users/'. $context, apply_filters( 'gravityview_change_entry_creator_user_parameters', $get_users_settings ) );
1419
1420
		return get_users( $get_users_settings );
1421
	}
1422
1423
1424
    /**
1425
     * Display updated/error notice
1426
     *
1427
     * @param string $notice text/HTML of notice
1428
     * @param string $class CSS class for notice (`updated` or `error`)
1429
     *
1430
     * @return string
1431
     */
1432
    public static function generate_notice( $notice, $class = '' ) {
1433
        return '<div class="gv-notice '.gravityview_sanitize_html_class( $class ) .'">'. $notice .'</div>';
1434
    }
1435
1436
1437
1438
1439
} //end class
1440
1441
1442
/**
1443
 * Generate an HTML anchor tag with a list of supported attributes
1444
 *
1445
 * @see GVCommon::get_link_html()
1446
 *
1447
 * @since 1.6
1448
 *
1449
 * @param string $href URL of the link.
1450
 * @param string $anchor_text The text or HTML inside the anchor. This is not sanitized in the function.
1451
 * @param array|string $atts Attributes to be added to the anchor tag
1452
 *
1453
 * @return string HTML output of anchor link. If empty $href, returns NULL
1454
 */
1455
function gravityview_get_link( $href = '', $anchor_text = '', $atts = array() ) {
1456
	return GVCommon::get_link_html( $href, $anchor_text, $atts );
1457
}
1458