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