Completed
Push — master ( 3ac4b1...21157a )
by Zack
05:18
created

GVCommon::format_date()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 47
Code Lines 29

Duplication

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