Completed
Pull Request — develop (#792)
by Zack
35:44 queued 23:49
created

GravityView_frontend   D

Complexity

Total Complexity 185

Size/Duplication

Total Lines 1527
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 1527
rs 4.4102
c 0
b 0
f 0
wmc 185
lcom 1
cbo 8

40 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A getInstance() 0 9 2
A getGvOutputData() 0 3 1
A setGvOutputData() 0 3 1
A isSearch() 0 3 1
A setIsSearch() 0 3 1
A getSingleEntry() 0 3 1
A setSingleEntry() 0 5 1
A getEntry() 0 3 1
A setEntry() 0 8 2
A getPostId() 0 3 1
A setPostId() 0 3 1
A isPostHasShortcode() 0 3 1
A setPostHasShortcode() 0 3 1
A isGravityviewPostType() 0 3 1
A setIsGravityviewPostType() 0 3 1
B set_context_view_id() 0 21 5
A get_context_view_id() 0 3 1
A initialize() 0 15 1
C parse_query_fix_frontpage() 0 53 14
B parse_content() 0 26 6
A set_entry_data() 0 5 1
C is_searching() 0 42 8
C single_entry_title() 0 57 11
C insert_view_in_content() 0 35 7
A comments_open() 0 16 2
F render_view() 0 243 30
C process_search_dates() 0 66 13
B process_search_only_approved() 0 22 4
A is_entry_approved() 0 11 4
B get_search_criteria() 0 49 6
B get_view_entries() 0 91 6
B updateViewSorting() 0 22 4
D _override_sorting_id_by_field_type() 0 88 15
A is_single_entry() 0 20 2
C add_scripts_and_styles() 0 82 11
A enqueue_default_style() 0 17 3
A add_style() 0 12 4
B add_columns_sort_links() 0 43 6
B is_field_sortable() 0 30 3

How to fix   Complexity   

Complex Class

Complex classes like GravityView_frontend often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GravityView_frontend, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 15 and the first side effect is on line 1543.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * GravityView Frontend functions
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.0.0
12
 */
13
14
15
class GravityView_frontend {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
16
17
	/**
18
	 * Regex strings that are used to determine whether the current request is a GravityView search or not.
19
	 * @see GravityView_frontend::is_searching()
20
	 * @since 1.7.4.1
21
	 * @var array
22
	 */
23
	private static $search_parameters = array( 'gv_search', 'gv_start', 'gv_end', 'gv_id', 'gv_by', 'filter_*' );
24
25
	/**
26
	 * Is the currently viewed post a `gravityview` post type?
27
	 * @var boolean
28
	 */
29
	var $is_gravityview_post_type = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_gravityview_post_type.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
30
31
	/**
32
	 * Does the current post have a `[gravityview]` shortcode?
33
	 * @var boolean
34
	 */
35
	var $post_has_shortcode = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $post_has_shortcode.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
36
37
	/**
38
	 * The Post ID of the currently viewed post. Not necessarily GV
39
	 * @var int
40
	 */
41
	var $post_id = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $post_id.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
42
43
	/**
44
	 * Are we currently viewing a single entry?
45
	 * If so, the int value of the entry ID. Otherwise, false.
46
	 * @var int|boolean
47
	 */
48
	var $single_entry = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $single_entry.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
49
50
	/**
51
	 * If we are viewing a single entry, the entry data
52
	 * @var array|false
53
	 */
54
	var $entry = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $entry.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
55
56
	/**
57
	 * When displaying the single entry we should always know to which View it belongs (the context is everything!)
58
	 * @var null
59
	 */
60
	var $context_view_id = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $context_view_id.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
61
62
	/**
63
	 * The View is showing search results
64
	 * @since 1.5.4
65
	 * @var boolean
66
	 */
67
	var $is_search = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_search.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
68
69
	/**
70
	 * The view data parsed from the $post
71
	 *
72
	 * @see  GravityView_View_Data::__construct()
73
	 * @var GravityView_View_Data
74
	 */
75
	var $gv_output_data = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $gv_output_data.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
76
77
	/**
78
	 * @var GravityView_frontend
79
	 */
80
	static $instance;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $instance.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
81
82
	/**
83
	 * Class constructor, enforce Singleton pattern
84
	 */
85
	private function __construct() {}
86
87
	private function initialize() {
88
		add_action( 'wp', array( $this, 'parse_content'), 11 );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
89
		add_filter( 'parse_query', array( $this, 'parse_query_fix_frontpage' ), 10 );
90
		add_action( 'template_redirect', array( $this, 'set_entry_data'), 1 );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
91
92
		// Enqueue scripts and styles after GravityView_Template::register_styles()
93
		add_action( 'wp_enqueue_scripts', array( $this, 'add_scripts_and_styles' ), 20 );
94
95
		// Enqueue and print styles in the footer. Added 1 priorty so stuff gets printed at 10 priority.
96
		add_action( 'wp_print_footer_scripts', array( $this, 'add_scripts_and_styles' ), 1 );
97
98
		add_filter( 'the_title', array( $this, 'single_entry_title' ), 1, 2 );
99
		add_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
100
		add_filter( 'comments_open', array( $this, 'comments_open' ), 10, 2 );
101
	}
102
103
	/**
104
	 * Get the one true instantiated self
105
	 * @return GravityView_frontend
106
	 */
107
	public static function getInstance() {
0 ignored issues
show
Coding Style introduced by
The function name getInstance is in camel caps, but expected get_instance instead as per the coding standard.
Loading history...
108
109
		if ( empty( self::$instance ) ) {
110
			self::$instance = new self;
111
			self::$instance->initialize();
112
		}
113
114
		return self::$instance;
115
	}
116
117
	/**
118
	 * @return GravityView_View_Data
119
	 */
120
	public function getGvOutputData() {
0 ignored issues
show
Coding Style introduced by
The function name getGvOutputData is in camel caps, but expected get_gv_output_data instead as per the coding standard.
Loading history...
121
		return $this->gv_output_data;
122
	}
123
124
	/**
125
	 * @param GravityView_View_Data $gv_output_data
126
	 */
127
	public function setGvOutputData( $gv_output_data ) {
0 ignored issues
show
Coding Style introduced by
The function name setGvOutputData is in camel caps, but expected set_gv_output_data instead as per the coding standard.
Loading history...
128
		$this->gv_output_data = $gv_output_data;
129
	}
130
131
	/**
132
	 * @return boolean
133
	 */
134
	public function isSearch() {
0 ignored issues
show
Coding Style introduced by
The function name isSearch is in camel caps, but expected is_search instead as per the coding standard.
Loading history...
135
		return $this->is_search;
136
	}
137
138
	/**
139
	 * @param boolean $is_search
140
	 */
141
	public function setIsSearch( $is_search ) {
0 ignored issues
show
Coding Style introduced by
The function name setIsSearch is in camel caps, but expected set_is_search instead as per the coding standard.
Loading history...
142
		$this->is_search = $is_search;
143
	}
144
145
	/**
146
	 * @return bool|int
147
	 */
148
	public function getSingleEntry() {
0 ignored issues
show
Coding Style introduced by
The function name getSingleEntry is in camel caps, but expected get_single_entry instead as per the coding standard.
Loading history...
149
		return $this->single_entry;
150
	}
151
152
	/**
153
	 * Sets the single entry ID and also the entry
154
	 * @param bool|int|string $single_entry
155
	 */
156
	public function setSingleEntry( $single_entry ) {
0 ignored issues
show
Coding Style introduced by
The function name setSingleEntry is in camel caps, but expected set_single_entry instead as per the coding standard.
Loading history...
157
158
		$this->single_entry = $single_entry;
159
160
	}
161
162
	/**
163
	 * @return array
164
	 */
165
	public function getEntry() {
0 ignored issues
show
Coding Style introduced by
The function name getEntry is in camel caps, but expected get_entry instead as per the coding standard.
Loading history...
166
		return $this->entry;
167
	}
168
169
	/**
170
	 * Set the current entry
171
	 * @param array|int $entry Entry array or entry slug or ID
172
	 */
173
	public function setEntry( $entry ) {
0 ignored issues
show
Coding Style introduced by
The function name setEntry is in camel caps, but expected set_entry instead as per the coding standard.
Loading history...
174
175
		if ( ! is_array( $entry ) ) {
176
			$entry = GVCommon::get_entry( $entry );
177
		}
178
179
		$this->entry = $entry;
180
	}
181
182
	/**
183
	 * @return int
184
	 */
185
	public function getPostId() {
0 ignored issues
show
Coding Style introduced by
The function name getPostId is in camel caps, but expected get_post_id instead as per the coding standard.
Loading history...
186
		return $this->post_id;
187
	}
188
189
	/**
190
	 * @param int $post_id
191
	 */
192
	public function setPostId( $post_id ) {
0 ignored issues
show
Coding Style introduced by
The function name setPostId is in camel caps, but expected set_post_id instead as per the coding standard.
Loading history...
193
		$this->post_id = $post_id;
194
	}
195
196
	/**
197
	 * @return boolean
198
	 */
199
	public function isPostHasShortcode() {
0 ignored issues
show
Coding Style introduced by
The function name isPostHasShortcode is in camel caps, but expected is_post_has_shortcode instead as per the coding standard.
Loading history...
200
		return $this->post_has_shortcode;
201
	}
202
203
	/**
204
	 * @param boolean $post_has_shortcode
205
	 */
206
	public function setPostHasShortcode( $post_has_shortcode ) {
0 ignored issues
show
Coding Style introduced by
The function name setPostHasShortcode is in camel caps, but expected set_post_has_shortcode instead as per the coding standard.
Loading history...
207
		$this->post_has_shortcode = $post_has_shortcode;
208
	}
209
210
	/**
211
	 * @return boolean
212
	 */
213
	public function isGravityviewPostType() {
0 ignored issues
show
Coding Style introduced by
The function name isGravityviewPostType is in camel caps, but expected is_gravityview_post_type instead as per the coding standard.
Loading history...
214
		return $this->is_gravityview_post_type;
215
	}
216
217
	/**
218
	 * @param boolean $is_gravityview_post_type
219
	 */
220
	public function setIsGravityviewPostType( $is_gravityview_post_type ) {
0 ignored issues
show
Coding Style introduced by
The function name setIsGravityviewPostType is in camel caps, but expected set_is_gravityview_post_type instead as per the coding standard.
Loading history...
221
		$this->is_gravityview_post_type = $is_gravityview_post_type;
222
	}
223
224
	/**
225
	 * Set the context view ID used when page contains multiple embedded views or displaying the single entry view
226
	 *
227
	 *
228
	 *
229
	 * @param null $view_id
230
	 */
231
	public function set_context_view_id( $view_id = null ) {
232
233
		if ( ! empty( $view_id ) ) {
234
235
			$this->context_view_id = $view_id;
236
237
		} elseif ( isset( $_GET['gvid'] ) && $this->getGvOutputData()->has_multiple_views() ) {
238
			/**
239
			 * used on a has_multiple_views context
240
			 * @see GravityView_API::entry_link
241
			 * @see GravityView_View_Data::getInstance()->has_multiple_views()
242
			 */
243
			$this->context_view_id = $_GET['gvid'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
244
245
		} elseif ( ! $this->getGvOutputData()->has_multiple_views() )  {
246
			$array_keys = array_keys( $this->getGvOutputData()->get_views() );
247
			$this->context_view_id = array_pop( $array_keys );
248
			unset( $array_keys );
249
		}
250
251
	}
252
253
	/**
254
	 * Returns the the view_id context when page contains multiple embedded views or displaying single entry view
255
	 *
256
	 * @since 1.5.4
257
	 *
258
	 * @return string
259
	 */
260
	public function get_context_view_id() {
261
		return $this->context_view_id;
262
	}
263
264
	/**
265
	 * Allow GravityView entry endpoints on the front page of a site
266
	 *
267
	 * @link  https://core.trac.wordpress.org/ticket/23867 Fixes this core issue
268
	 * @link https://wordpress.org/plugins/cpt-on-front-page/ Code is based on this
269
	 *
270
	 * @since 1.17.3
271
	 *
272
	 * @param WP_Query &$query (passed by reference)
273
	 *
274
	 * @return void
275
	 */
276
	public function parse_query_fix_frontpage( &$query ) {
277
		global $wp_rewrite;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
278
279
		$is_front_page = ( $query->is_home || $query->is_page );
280
		$show_on_front = ( 'page' === get_option('show_on_front') );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
281
		$front_page_id = get_option('page_on_front');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
282
283
		if (  $is_front_page && $show_on_front && $front_page_id ) {
284
285
			// Force to be an array, potentially a query string ( entry=16 )
286
			$_query = wp_parse_args( $query->query );
287
288
			// pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
289
			if ( isset( $_query['pagename'] ) && '' === $_query['pagename'] ) {
290
				unset( $_query['pagename'] );
291
			}
292
293
			// this is where will break from core wordpress
294
			$ignore = array( 'preview', 'page', 'paged', 'cpage' );
295
			$endpoints = rgobj( $wp_rewrite, 'endpoints' );
296
			foreach ( (array) $endpoints as $endpoint ) {
297
				$ignore[] = $endpoint[1];
298
			}
299
			unset( $endpoints );
300
301
			// Modify the query if:
302
			// - We're on the "Page on front" page (which we are), and:
303
			// - The query is empty OR
304
			// - The query includes keys that are associated with registered endpoints. `entry`, for example.
305
			if ( empty( $_query ) || ! array_diff( array_keys( $_query ), $ignore ) ) {
306
307
				$qv =& $query->query_vars;
308
309
				// Prevent redirect when on the single entry endpoint
310
				if( self::is_single_entry() ) {
311
					add_filter( 'redirect_canonical', '__return_false' );
312
				}
313
314
				$query->is_page = true;
315
				$query->is_home = false;
316
				$qv['page_id']  = $front_page_id;
317
318
				// Correct <!--nextpage--> for page_on_front
319
				if ( ! empty( $qv['paged'] ) ) {
320
					$qv['page'] = $qv['paged'];
321
					unset( $qv['paged'] );
322
				}
323
			}
324
325
			// reset the is_singular flag after our updated code above
326
			$query->is_singular = $query->is_single || $query->is_page || $query->is_attachment;
327
		}
328
	}
329
330
	/**
331
	 * Read the $post and process the View data inside
332
	 * @param  array  $wp Passed in the `wp` hook. Not used.
333
	 * @return void
334
	 */
335
	public function parse_content( $wp = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $wp is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
336
		global $post;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
337
338
		// If in admin and NOT AJAX request, get outta here.
339
		if ( GravityView_Plugin::is_admin() )  {
340
			return;
341
		}
342
343
		// Calculate requested Views
344
		$this->setGvOutputData( GravityView_View_Data::getInstance( $post ) );
345
346
		// !important: we need to run this before getting single entry (to kick the advanced filter)
347
		$this->set_context_view_id();
348
349
		$this->setIsGravityviewPostType( get_post_type( $post ) === 'gravityview' );
350
351
		$post_id = $this->getPostId() ? $this->getPostId() : (isset( $post ) ? $post->ID : null );
352
		$this->setPostId( $post_id );
353
		$post_has_shortcode = ! empty( $post->post_content ) ? gravityview_has_shortcode_r( $post->post_content, 'gravityview' ) : false;
354
		$this->setPostHasShortcode( $this->isGravityviewPostType() ? null : ! empty( $post_has_shortcode ) );
355
356
		// check if the View is showing search results (only for multiple entries View)
357
		$this->setIsSearch( $this->is_searching() );
358
359
		unset( $entry, $post_id, $post_has_shortcode );
360
	}
361
362
	/**
363
	 * Set the entry
364
	 */
365
	function set_entry_data() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
366
		$entry_id = self::is_single_entry();
367
		$this->setSingleEntry( $entry_id );
368
		$this->setEntry( $entry_id );
369
	}
370
371
	/**
372
	 * Checks if the current View is presenting search results
373
	 *
374
	 * @since 1.5.4
375
	 *
376
	 * @return boolean True: Yes, it's a search; False: No, not a search.
377
	 */
378
	function is_searching() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
379
380
		// It's a single entry, not search
381
		if ( $this->getSingleEntry() ) {
382
			return false;
383
		}
384
385
		$search_method = GravityView_Widget_Search::getInstance()->get_search_method();
386
387
		if( 'post' === $search_method ) {
388
			$get = $_POST;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
389
		} else {
390
			$get = $_GET;
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
391
		}
392
393
		// No $_GET parameters
394
		if ( empty( $get ) || ! is_array( $get ) ) {
395
			return false;
396
		}
397
398
		// Remove empty values
399
		$get = array_filter( $get );
400
401
		// If the $_GET parameters are empty, it's no search.
402
		if ( empty( $get ) ) {
403
			return false;
404
		}
405
406
		$search_keys = array_keys( $get );
407
408
		$search_match = implode( '|', self::$search_parameters );
409
410
		foreach ( $search_keys as $search_key ) {
411
412
			// Analyze the search key $_GET parameter and see if it matches known GV args
413
			if ( preg_match( '/(' . $search_match . ')/i', $search_key ) ) {
414
				return true;
415
			}
416
		}
417
418
		return false;
419
	}
420
421
	/**
422
	 * Filter the title for the single entry view
423
	 *
424
	 * @param  string $title   current title
425
	 * @param  int $passed_post_id Post ID
426
	 * @return string          (modified) title
427
	 */
428
	public function single_entry_title( $title, $passed_post_id = null ) {
429
		global $post;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
430
431
		// If this is the directory view, return.
432
		if ( ! $this->getSingleEntry() ) {
433
			return $title;
434
		}
435
436
		$entry = $this->getEntry();
437
438
		/**
439
		 * @filter `gravityview/single/title/out_loop` Apply the Single Entry Title filter outside the WordPress loop?
440
		 * @param boolean $in_the_loop Whether to apply the filter to the menu title and the meta tag <title> - outside the loop
441
		 * @param array $entry Current entry
442
		 */
443
		$apply_outside_loop = apply_filters( 'gravityview/single/title/out_loop' , in_the_loop(), $entry );
444
445
		if ( ! $apply_outside_loop ) {
446
			return $title;
447
		}
448
449
		// User reported WooCommerce doesn't pass two args.
450
		if ( empty( $passed_post_id ) )  {
451
			return $title;
452
		}
453
454
		// Don't modify the title for anything other than the current view/post.
455
		// This is true for embedded shortcodes and Views.
456
		if ( is_object( $post ) && (int) $post->ID !== (int) $passed_post_id ) {
457
			return $title;
458
		}
459
460
		$context_view_id = $this->get_context_view_id();
461
462
		if ( $this->getGvOutputData()->has_multiple_views() && ! empty( $context_view_id ) ) {
463
			$view_meta = $this->getGvOutputData()->get_view( $context_view_id );
464
		} else {
465
			foreach ( $this->getGvOutputData()->get_views() as $view_id => $view_data ) {
466
				if ( intval( $view_data['form_id'] ) === intval( $entry['form_id'] ) ) {
467
					$view_meta = $view_data;
468
					break;
469
				}
470
			}
471
		}
472
473
		if ( ! empty( $view_meta['atts']['single_title'] ) ) {
474
475
			$title = $view_meta['atts']['single_title'];
476
477
			// We are allowing HTML in the fields, so no escaping the output
478
			$title = GravityView_API::replace_variables( $title, $view_meta['form'], $entry );
479
480
			$title = do_shortcode( $title );
481
		}
482
483
		return $title;
484
	}
485
486
487
	/**
488
	 * In case View post is called directly, insert the view in the post content
489
	 *
490
	 * @access public
491
	 * @static
492
	 * @param mixed $content
493
	 * @return string Add the View output into View CPT content
494
	 */
495
	public function insert_view_in_content( $content ) {
496
497
		// Plugins may run through the content in the header. WP SEO does this for its OpenGraph functionality.
498
		if ( ! did_action( 'loop_start' ) ) {
499
500
			do_action( 'gravityview_log_debug', '[insert_view_in_content] Not processing yet: loop_start hasn\'t run yet. Current action:', current_filter() );
501
502
			return $content;
503
		}
504
505
		//	We don't want this filter to run infinite loop on any post content fields
506
		remove_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
507
508
		// Otherwise, this is called on the Views page when in Excerpt mode.
509
		if ( is_admin() ) {
510
			return $content;
511
		}
512
513
		if ( $this->isGravityviewPostType() ) {
514
515
			/** @since 1.7.4 */
516
			if ( is_preview() && ! gravityview_get_form_id( $this->post_id ) ) {
517
				$content .= __( 'When using a Start Fresh template, you must save the View before a Preview is available.', 'gravityview' );
518
			} else {
519
				foreach ( $this->getGvOutputData()->get_views() as $view_id => $data ) {
520
					$content .= $this->render_view( array( 'id' => $view_id ) );
521
				}
522
			}
523
		}
524
525
		//	Add the filter back in
526
		add_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
527
528
		return $content;
529
	}
530
531
	/**
532
	 * Disable comments on GravityView post types
533
	 * @param  boolean $open    existing status
534
	 * @param  int $post_id Post ID
535
	 * @return boolean
536
	 */
537
	public function comments_open( $open, $post_id ) {
538
539
		if ( $this->isGravityviewPostType() ) {
540
			$open = false;
541
		}
542
543
		/**
544
		 * @filter `gravityview/comments_open` Whether to set comments to open or closed.
545
		 * @since  1.5.4
546
		 * @param  boolean $open Open or closed status
547
		 * @param  int $post_id Post ID to set comment status for
548
		 */
549
		$open = apply_filters( 'gravityview/comments_open', $open, $post_id );
550
551
		return $open;
552
	}
553
554
555
	/**
556
	 * Core function to render a View based on a set of arguments
557
	 *
558
	 * @access public
559
	 * @static
560
	 * @param array $passed_args {
561
	 *
562
	 *      Settings for rendering the View
563
	 *
564
	 *      @type int $id View id
565
	 *      @type int $page_size Number of entries to show per page
566
	 *      @type string $sort_field Form field id to sort
567
	 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
568
	 *      @type string $start_date - Ymd
569
	 *      @type string $end_date - Ymd
570
	 *      @type string $class - assign a html class to the view
571
	 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
572
	 * }
573
	 *
574
	 * @return string|null HTML output of a View, NULL if View isn't found
575
	 */
576
	public function render_view( $passed_args ) {
577
578
		// validate attributes
579
		if ( empty( $passed_args['id'] ) ) {
580
			do_action( 'gravityview_log_error', '[render_view] Returning; no ID defined.', $passed_args );
581
			return null;
582
		}
583
584
		// Solve problem when loading content via admin-ajax.php
585
		// @hack
586
		if ( ! $this->getGvOutputData() ) {
587
588
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not defined; parsing content.', $passed_args );
589
590
			$this->parse_content();
591
		}
592
593
		// Make 100% sure that we're dealing with a properly called situation
594
		if ( ! is_object( $this->getGvOutputData() ) || ! is_callable( array( $this->getGvOutputData(), 'get_view' ) ) ) {
595
596
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not an object or get_view not callable.', $this->getGvOutputData() );
597
598
			return null;
599
		}
600
601
		$view_id = $passed_args['id'];
602
603
		$view_data = $this->getGvOutputData()->get_view( $view_id, $passed_args );
604
605
		do_action( 'gravityview_log_debug', '[render_view] View Data: ', $view_data );
606
607
		do_action( 'gravityview_log_debug', '[render_view] Init View. Arguments: ', $passed_args );
608
609
		// The passed args were always winning, even if they were NULL.
610
		// This prevents that. Filters NULL, FALSE, and empty strings.
611
		$passed_args = array_filter( $passed_args, 'strlen' );
612
613
		//Override shortcode args over View template settings
614
		$atts = wp_parse_args( $passed_args, $view_data['atts'] );
615
616
		do_action( 'gravityview_log_debug', '[render_view] Arguments after merging with View settings: ', $atts );
617
618
		// It's password protected and you need to log in.
619
		if ( post_password_required( $view_id ) ) {
620
621
			do_action( 'gravityview_log_error', sprintf( '[render_view] Returning: View %d is password protected.', $view_id ) );
622
623
			// If we're in an embed or on an archive page, show the password form
624
			if ( get_the_ID() !== $view_id ) {
625
				return get_the_password_form();
626
			}
627
628
			// Otherwise, just get outta here
629
			return null;
630
		}
631
632
		/**
633
		 * Don't render View if user isn't allowed to see it
634
		 * @since 1.15
635
		 * @since 1.17.2 Added check for if a user has no caps but is logged in (member of multisite, but not any site). Treat as if logged-out.
636
		 */
637
		if( is_user_logged_in() && ! ( empty( wp_get_current_user()->caps ) && empty( wp_get_current_user()->roles ) ) && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
638
639
			do_action( 'gravityview_log_debug', sprintf( '%s Returning: View %d is not visible by current user.', __METHOD__, $view_id ) );
640
641
			return null;
642
		}
643
644
		if( $this->isGravityviewPostType() ) {
645
646
			/**
647
			 * @filter `gravityview_direct_access` Should Views be directly accessible, or only visible using the shortcode?
648
			 * @see https://codex.wordpress.org/Function_Reference/register_post_type#public
649
			 * @see GravityView_Post_Types::init_post_types
650
			 * @since 1.15.2
651
			 * @param[in,out] boolean `true`: allow Views to be accessible directly. `false`: Only allow Views to be embedded via shortcode. Default: `true`
652
			 * @param int $view_id The ID of the View currently being requested. `0` for general setting
653
			 */
654
			$direct_access = apply_filters( 'gravityview_direct_access', true, $view_id );
655
656
			$embed_only = ! empty( $atts['embed_only'] );
657
658
			if( ! $direct_access || ( $embed_only && ! GVCommon::has_cap( 'read_private_gravityviews' ) ) ) {
659
				return __( 'You are not allowed to view this content.', 'gravityview' );
660
			}
661
		}
662
663
		ob_start();
664
665
		/**
666
		 * Set globals for templating
667
		 * @deprecated 1.6.2
668
		 */
669
		global $gravityview_view;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
670
671
		$gravityview_view = new GravityView_View( $view_data );
672
673
		$post_id = ! empty( $atts['post_id'] ) ? intval( $atts['post_id'] ) : $this->getPostId();
674
675
		$gravityview_view->setPostId( $post_id );
676
677
		if ( ! $this->getSingleEntry() ) {
678
679
			// user requested Directory View
680
			do_action( 'gravityview_log_debug', '[render_view] Executing Directory View' );
681
682
			//fetch template and slug
683
			$view_slug = apply_filters( 'gravityview_template_slug_'. $view_data['template_id'], 'table', 'directory' );
684
685
			do_action( 'gravityview_log_debug', '[render_view] View template slug: ', $view_slug );
686
687
			/**
688
			 * Disable fetching initial entries for views that don't need it (DataTables)
689
			 */
690
			$get_entries = apply_filters( 'gravityview_get_view_entries_'.$view_slug, true );
691
692
			/**
693
			 * Hide View data until search is performed
694
			 * @since 1.5.4
695
			 */
696
			if ( ! empty( $atts['hide_until_searched'] ) && ! $this->isSearch() ) {
697
				$gravityview_view->setHideUntilSearched( true );
698
				$get_entries = false;
699
			}
700
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
701
702
			if ( $get_entries ) {
703
704
				if ( ! empty( $atts['sort_columns'] ) ) {
705
					// add filter to enable column sorting
706
					add_filter( 'gravityview/template/field_label', array( $this, 'add_columns_sort_links' ) , 100, 3 );
707
				}
708
709
				$view_entries = self::get_view_entries( $atts, $view_data['form_id'] );
710
711
				do_action( 'gravityview_log_debug', sprintf( '[render_view] Get Entries. Found %s entries total, showing %d entries', $view_entries['count'], sizeof( $view_entries['entries'] ) ) );
712
713
			} else {
714
715
				$view_entries = array( 'count' => null, 'entries' => null, 'paging' => null );
716
717
				do_action( 'gravityview_log_debug', '[render_view] Not fetching entries because `gravityview_get_view_entries_'.$view_slug.'` is false' );
718
			}
719
720
			$gravityview_view->setPaging( $view_entries['paging'] );
721
			$gravityview_view->setContext( 'directory' );
722
			$sections = array( 'header', 'body', 'footer' );
723
724
		} else {
725
726
			// user requested Single Entry View
727
			do_action( 'gravityview_log_debug', '[render_view] Executing Single View' );
728
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
729
730
			/**
731
			 * @action `gravityview_render_entry_{View ID}` Before rendering a single entry for a specific View ID
732
			 * @since 1.17
733
			 */
734
			do_action( 'gravityview_render_entry_'.$view_data['id'] );
735
736
			$entry = $this->getEntry();
737
738
			// You are not permitted to view this entry.
739
			if ( empty( $entry ) || ! self::is_entry_approved( $entry, $atts ) ) {
740
741
				do_action( 'gravityview_log_debug', '[render_view] Entry does not exist. This may be because of View filters limiting access.' );
742
743
				/**
744
				 * @filter `gravityview/render/entry/not_visible` Modify the message shown to users when the entry doesn't exist or they aren't allowed to view it.
745
				 * @since 1.6
746
				 * @param string $message Default: "You have attempted to view an entry that is not visible or may not exist."
747
				 */
748
				$message = apply_filters( 'gravityview/render/entry/not_visible', __( 'You have attempted to view an entry that is not visible or may not exist.', 'gravityview' ) );
749
750
				/**
751
				 * @since 1.6
752
				 */
753
				echo esc_attr( $message );
754
755
				return null;
756
			}
757
758
			// We're in single view, but the view being processed is not the same view the single entry belongs to.
759
			// important: do not remove this as it prevents fake attempts of displaying entries from other views/forms
760
			if ( $this->getGvOutputData()->has_multiple_views() && $view_id != $this->get_context_view_id() ) {
761
				do_action( 'gravityview_log_debug', '[render_view] In single entry view, but the entry does not belong to this View. Perhaps there are multiple views on the page. View ID: '. $view_id );
762
				return null;
763
			}
764
765
			//fetch template and slug
766
			$view_slug = apply_filters( 'gravityview_template_slug_' . $view_data['template_id'], 'table', 'single' );
767
			do_action( 'gravityview_log_debug', '[render_view] View single template slug: ', $view_slug );
768
769
			//fetch entry detail
770
			$view_entries['count'] = 1;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$view_entries was never initialized. Although not strictly required by PHP, it is generally a good practice to add $view_entries = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
771
			$view_entries['entries'][] = $entry;
772
			do_action( 'gravityview_log_debug', '[render_view] Get single entry: ', $view_entries['entries'] );
773
774
			$back_link_label = isset( $atts['back_link_label'] ) ? $atts['back_link_label'] : null;
775
776
			// set back link label
777
			$gravityview_view->setBackLinkLabel( $back_link_label );
778
			$gravityview_view->setContext( 'single' );
779
			$sections = array( 'single' );
780
781
		}
782
783
		// add template style
784
		self::add_style( $view_data['template_id'] );
785
786
		// Prepare to render view and set vars
787
		$gravityview_view->setEntries( $view_entries['entries'] );
788
		$gravityview_view->setTotalEntries( $view_entries['count'] );
789
790
		// If Edit
791
		if ( 'edit' === gravityview_get_context() ) {
792
793
			do_action( 'gravityview_log_debug', '[render_view] Edit Entry ' );
794
795
			do_action( 'gravityview_edit_entry', $this->getGvOutputData() );
796
797
			return ob_get_clean();
798
799
		} else {
800
			// finaly we'll render some html
801
			$sections = apply_filters( 'gravityview_render_view_sections', $sections, $view_data['template_id'] );
802
803
			do_action( 'gravityview_log_debug', '[render_view] Sections to render: ', $sections );
804
			foreach ( $sections as $section ) {
805
				do_action( 'gravityview_log_debug', '[render_view] Rendering '. $section . ' section.' );
806
				$gravityview_view->render( $view_slug, $section, false );
807
			}
808
		}
809
810
		//@todo: check why we need the IF statement vs. print the view id always.
811
		if ( $this->isGravityviewPostType() || $this->isPostHasShortcode() ) {
812
			// Print the View ID to enable proper cookie pagination
813
			echo '<input type="hidden" class="gravityview-view-id" value="' . esc_attr( $view_id ) . '">';
814
		}
815
		$output = ob_get_clean();
816
817
		return $output;
818
	}
819
820
	/**
821
	 * Process the start and end dates for a view - overrides values defined in shortcode (if needed)
822
	 *
823
	 * The `start_date` and `end_date` keys need to be in a format processable by GFFormsModel::get_date_range_where(),
824
	 * which uses \DateTime() format.
825
	 *
826
	 * You can set the `start_date` or `end_date` to any value allowed by {@link http://www.php.net//manual/en/function.strtotime.php strtotime()},
827
	 * including strings like "now" or "-1 year" or "-3 days".
828
	 *
829
	 * @see GFFormsModel::get_date_range_where
830
	 *
831
	 * @param  array      $args            View settings
832
	 * @param  array      $search_criteria Search being performed, if any
833
	 * @return array                       Modified `$search_criteria` array
834
	 */
835
	public static function process_search_dates( $args, $search_criteria = array() ) {
836
837
		$return_search_criteria = $search_criteria;
838
839
		foreach ( array( 'start_date', 'end_date' ) as $key ) {
840
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
841
842
			// Is the start date or end date set in the view or shortcode?
843
			// If so, we want to make sure that the search doesn't go outside the bounds defined.
844
			if ( ! empty( $args[ $key ] ) ) {
845
846
				// Get a timestamp and see if it's a valid date format
847
				$date = strtotime( $args[ $key ] );
848
849
				// The date was invalid
850
				if ( empty( $date ) ) {
851
					do_action( 'gravityview_log_error', __METHOD__ . ' Invalid ' . $key . ' date format: ' . $args[ $key ] );
852
					continue;
853
				}
854
855
				// The format that Gravity Forms expects for start_date and day-specific (not hour/second-specific) end_date
856
				$datetime_format = 'Y-m-d H:i:s';
857
				$search_is_outside_view_bounds = false;
858
859
				if( ! empty( $search_criteria[ $key ] ) ) {
860
861
					$search_date = strtotime( $search_criteria[ $key ] );
862
863
					// The search is for entries before the start date defined by the settings
864
					switch ( $key ) {
865
						case 'end_date':
866
							/**
867
							 * If the end date is formatted as 'Y-m-d', it should be formatted without hours and seconds
868
							 * so that Gravity Forms can convert the day to 23:59:59 the previous day.
869
							 *
870
							 * If it's a relative date ("now" or "-1 day"), then it should use the precise date format
871
							 *
872
							 * @see GFFormsModel::get_date_range_where
873
							 */
874
							$datetime_format               = gravityview_is_valid_datetime( $args[ $key ] ) ? 'Y-m-d' : 'Y-m-d H:i:s';
875
							$search_is_outside_view_bounds = ( $search_date > $date );
876
							break;
877
						case 'start_date':
878
							$search_is_outside_view_bounds = ( $search_date < $date );
879
							break;
880
					}
881
				}
882
883
				// If there is no search being performed, or if there is a search being performed that's outside the bounds
884
				if ( empty( $search_criteria[ $key ] ) || $search_is_outside_view_bounds ) {
885
886
					// Then we override the search and re-set the start date
887
					$return_search_criteria[ $key ] = date_i18n( $datetime_format , $date, true );
888
				}
889
			}
890
		}
891
892
		if( isset( $return_search_criteria['start_date'] ) && isset( $return_search_criteria['end_date'] ) ) {
893
			// The start date is AFTER the end date. This will result in no results, but let's not force the issue.
894
			if ( strtotime( $return_search_criteria['start_date'] ) > strtotime( $return_search_criteria['end_date'] ) ) {
895
				do_action( 'gravityview_log_error', __METHOD__ . ' Invalid search: the start date is after the end date.', $return_search_criteria );
896
			}
897
		}
898
899
		return $return_search_criteria;
900
	}
901
902
903
	/**
904
	 * Process the approved only search criteria according to the View settings
905
	 *
906
	 * @param  array      $args            View settings
907
	 * @param  array      $search_criteria Search being performed, if any
908
	 * @return array                       Modified `$search_criteria` array
909
	 */
910
	public static function process_search_only_approved( $args, $search_criteria ) {
911
912
		/** @since 1.19 */
913
		if( ! empty( $args['admin_show_all_statuses'] ) && GVCommon::has_cap('gravityview_moderate_entries') ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
914
			do_action( 'gravityview_log_debug', __METHOD__ . ': User can moderate entries; showing all approval statuses' );
915
			return $search_criteria;
916
		}
917
918
		if ( ! empty( $args['show_only_approved'] ) ) {
919
920
			$search_criteria['field_filters'][] = array(
921
				'key' => GravityView_Entry_Approval::meta_key,
922
				'value' => GravityView_Entry_Approval_Status::APPROVED
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
923
			);
924
925
			$search_criteria['field_filters']['mode'] = 'all'; // force all the criterias to be met
926
927
			do_action( 'gravityview_log_debug', '[process_search_only_approved] Search Criteria if show only approved: ', $search_criteria );
928
		}
929
930
		return $search_criteria;
931
	}
932
933
934
	/**
935
	 * Check if a certain entry is approved.
936
	 *
937
	 * If we pass the View settings ($args) it will check the 'show_only_approved' setting before
938
	 *   checking the entry approved field, returning true if show_only_approved = false.
939
	 *
940
	 * @since 1.7
941
	 * @since 1.18 Converted check to use GravityView_Entry_Approval_Status::is_approved
942
	 *
943
	 * @uses GravityView_Entry_Approval_Status::is_approved
944
	 *
945
	 * @param array $entry  Entry object
946
	 * @param array $args   View settings (optional)
947
	 *
948
	 * @return bool
949
	 */
950
	public static function is_entry_approved( $entry, $args = array() ) {
951
952
		if ( empty( $entry['id'] ) || ( array_key_exists( 'show_only_approved', $args ) && ! $args['show_only_approved'] ) ) {
953
			// is implicitly approved if entry is null or View settings doesn't require to check for approval
954
			return true;
955
		}
956
957
		$is_approved = gform_get_meta( $entry['id'], GravityView_Entry_Approval::meta_key );
958
959
		return GravityView_Entry_Approval_Status::is_approved( $is_approved );
960
	}
961
962
	/**
963
	 * Parse search criteria for a entries search.
964
	 *
965
	 * array(
966
	 * 	'search_field' => 1, // ID of the field
967
	 *  'search_value' => '', // Value of the field to search
968
	 *  'search_operator' => 'contains', // 'is', 'isnot', '>', '<', 'contains'
969
	 *  'show_only_approved' => 0 or 1 // Boolean
970
	 * )
971
	 *
972
	 * @param  array $args    Array of args
973
	 * @param  int $form_id Gravity Forms form ID
974
	 * @return array          Array of search parameters, formatted in Gravity Forms mode, using `status` key set to "active" by default, `field_filters` array with `key`, `value` and `operator` keys.
975
	 */
976
	public static function get_search_criteria( $args, $form_id ) {
977
978
		/**
979
		 * @filter `gravityview_fe_search_criteria` Modify the search criteria
980
		 * @see GravityView_Widget_Search::filter_entries Adds the default search criteria
981
		 * @param array $search_criteria Empty `field_filters` key
982
		 * @param int $form_id ID of the Gravity Forms form that is being searched
983
		 */
984
		$search_criteria = apply_filters( 'gravityview_fe_search_criteria', array( 'field_filters' => array() ), $form_id );
985
986
		$original_search_criteria = $search_criteria;
987
988
		do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after hook gravityview_fe_search_criteria: ', $search_criteria );
989
990
		// implicity search
991
		if ( ! empty( $args['search_value'] ) ) {
992
993
			// Search operator options. Options: `is` or `contains`
994
			$operator = ! empty( $args['search_operator'] ) && in_array( $args['search_operator'], array( 'is', 'isnot', '>', '<', 'contains' ) ) ? $args['search_operator'] : 'contains';
995
996
			$search_criteria['field_filters'][] = array(
997
				'key' => rgget( 'search_field', $args ), // The field ID to search
998
				'value' => _wp_specialchars( $args['search_value'] ), // The value to search. Encode ampersands but not quotes.
999
				'operator' => $operator,
1000
			);
1001
		}
1002
1003
		if( $search_criteria !== $original_search_criteria ) {
1004
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after implicity search: ', $search_criteria );
1005
		}
1006
1007
		// Handle setting date range
1008
		$search_criteria = self::process_search_dates( $args, $search_criteria );
1009
1010
		if( $search_criteria !== $original_search_criteria ) {
1011
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after date params: ', $search_criteria );
1012
		}
1013
1014
		// remove not approved entries
1015
		$search_criteria = self::process_search_only_approved( $args, $search_criteria );
1016
1017
		/**
1018
		 * @filter `gravityview_status` Modify entry status requirements to be included in search results.
1019
		 * @param string $status Default: `active`. Accepts all Gravity Forms entry statuses, including `spam` and `trash`
1020
		 */
1021
		$search_criteria['status'] = apply_filters( 'gravityview_status', 'active', $args );
1022
1023
		return $search_criteria;
1024
	}
1025
1026
1027
1028
	/**
1029
	 * Core function to calculate View multi entries (directory) based on a set of arguments ($args):
1030
	 *   $id - View id
1031
	 *   $page_size - Page
1032
	 *   $sort_field - form field id to sort
1033
	 *   $sort_direction - ASC / DESC
1034
	 *   $start_date - Ymd
1035
	 *   $end_date - Ymd
1036
	 *   $class - assign a html class to the view
1037
	 *   $offset (optional) - This is the start point in the current data set (0 index based).
1038
	 *
1039
	 *
1040
	 *
1041
	 * @uses  gravityview_get_entries()
1042
	 * @access public
1043
	 * @param array $args\n
0 ignored issues
show
Bug introduced by
There is no parameter named $args\n. 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...
1044
	 *   - $id - View id
1045
	 *   - $page_size - Page
1046
	 *   - $sort_field - form field id to sort
1047
	 *   - $sort_direction - ASC / DESC
1048
	 *   - $start_date - Ymd
1049
	 *   - $end_date - Ymd
1050
	 *   - $class - assign a html class to the view
1051
	 *   - $offset (optional) - This is the start point in the current data set (0 index based).
1052
	 * @param int $form_id Gravity Forms Form ID
1053
	 * @return array Associative array with `count`, `entries`, and `paging` keys. `count` has the total entries count, `entries` is an array with Gravity Forms full entry data, `paging` is an array with `offset` and `page_size` keys
1054
	 */
1055
	public static function get_view_entries( $args, $form_id ) {
1056
1057
		do_action( 'gravityview_log_debug', '[get_view_entries] init' );
1058
		// start filters and sorting
1059
1060
		/**
1061
		 * Process search parameters
1062
		 * @var array
1063
		 */
1064
		$search_criteria = self::get_search_criteria( $args, $form_id );
1065
1066
		// Paging & offset
1067
		$page_size = ! empty( $args['page_size'] ) ? intval( $args['page_size'] ) : apply_filters( 'gravityview_default_page_size', 25 );
1068
1069
		if ( -1 === $page_size ) {
1070
			$page_size = PHP_INT_MAX;
1071
		}
1072
1073
		if ( isset( $args['offset'] ) ) {
1074
			$offset = intval( $args['offset'] );
1075
		} else {
1076
			$curr_page = empty( $_GET['pagenum'] ) ? 1 : intval( $_GET['pagenum'] );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
1077
			$offset = ( $curr_page - 1 ) * $page_size;
1078
		}
1079
1080
		$paging = array(
1081
			'offset' => $offset,
1082
			'page_size' => $page_size,
1083
		);
1084
1085
		do_action( 'gravityview_log_debug', __METHOD__ . ': Paging: ', $paging );
1086
1087
		// Sorting
1088
		$sorting = self::updateViewSorting( $args, $form_id );
1089
1090
		$parameters = array(
1091
			'search_criteria' => $search_criteria,
1092
			'sorting' => $sorting,
1093
			'paging' => $paging,
1094
			'cache' => isset( $args['cache'] ) ? $args['cache'] : true,
1095
		);
1096
1097
		/**
1098
		 * @filter `gravityview_get_entries` Filter get entries criteria
1099
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1100
		 * @param array $args View configuration args. {
1101
		 *      @type int $id View id
1102
		 *      @type int $page_size Number of entries to show per page
1103
		 *      @type string $sort_field Form field id to sort
1104
		 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
1105
		 *      @type string $start_date - Ymd
1106
		 *      @type string $end_date - Ymd
1107
		 *      @type string $class - assign a html class to the view
1108
		 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
1109
		 * }
1110
		 * @param int $form_id ID of Gravity Forms form
1111
		 */
1112
		$parameters = apply_filters( 'gravityview_get_entries', $parameters, $args, $form_id );
1113
1114
		/**
1115
		 * @filter `gravityview_get_entries_{View ID}` Filter get entries criteria
1116
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1117
		 * @param array $args View configuration args.
1118
		 */
1119
		$parameters = apply_filters( 'gravityview_get_entries_'.$args['id'], $parameters, $args, $form_id );
1120
1121
		do_action( 'gravityview_log_debug', __METHOD__ . ': $parameters passed to gravityview_get_entries(): ', $parameters );
1122
1123
		//fetch entries
1124
		$count = 0;
1125
		$entries = gravityview_get_entries( $form_id, $parameters, $count );
1126
1127
		do_action( 'gravityview_log_debug', sprintf( '%s: Get Entries. Found: %s entries', __METHOD__, $count ), $entries );
1128
1129
		/**
1130
		 * @filter `gravityview_view_entries` Filter the entries output to the View
1131
		 * @deprecated since 1.5.2
1132
		 * @param array $args View settings associative array
1133
		 * @var array
1134
		 */
1135
		$entries = apply_filters( 'gravityview_view_entries', $entries, $args );
1136
1137
		/**
1138
		 * @filter `gravityview/view/entries` Filter the entries output to the View
1139
		 * @param array $criteria associative array containing count, entries & paging
1140
		 * @param array $args View settings associative array
1141
		 * @since 1.5.2
1142
		 */
1143
		return apply_filters( 'gravityview/view/entries', compact( 'count', 'entries', 'paging' ), $args );
1144
1145
	}
1146
1147
1148
	/**
1149
	 * Updates the View sorting criteria
1150
	 *
1151
	 * @since 1.7
1152
	 *
1153
	 * @param array $args View settings. Required to have `sort_field` and `sort_direction` keys
1154
	 * @param int $form_id The ID of the form used to sort
1155
	 * @return array $sorting Array with `key`, `direction` and `is_numeric` keys
1156
	 */
1157
	public static function updateViewSorting( $args, $form_id ) {
0 ignored issues
show
Coding Style introduced by
The function name updateViewSorting is in camel caps, but expected update_view_sorting instead as per the coding standard.
Loading history...
1158
		$sorting = array();
1159
		$sort_field_id = isset( $_GET['sort'] ) ? $_GET['sort'] : rgar( $args, 'sort_field' );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
1160
		$sort_direction = isset( $_GET['dir'] ) ? $_GET['dir'] : rgar( $args, 'sort_direction' );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
1161
1162
		$sort_field_id = self::_override_sorting_id_by_field_type( $sort_field_id, $form_id );
1163
1164
		if ( ! empty( $sort_field_id ) ) {
1165
			$sorting = array(
1166
				'key' => $sort_field_id,
1167
				'direction' => strtolower( $sort_direction ),
1168
				'is_numeric' => GVCommon::is_field_numeric( $form_id, $sort_field_id )
1169
			);
1170
		}
1171
1172
		GravityView_View::getInstance()->setSorting( $sorting );
1173
1174
		do_action( 'gravityview_log_debug', '[updateViewSorting] Sort Criteria : ', $sorting );
1175
1176
		return $sorting;
1177
1178
	}
1179
1180
	/**
1181
	 * Override sorting per field
1182
	 *
1183
	 * Currently only modifies sorting ID when sorting by the full name. Sorts by first name.
1184
	 * Use the `gravityview/sorting/full-name` filter to override.
1185
	 *
1186
	 * @todo Filter from GravityView_Field
1187
	 * @since 1.7.4
1188
	 *
1189
	 * @param int|string $sort_field_id Field used for sorting (`id` or `1.2`)
1190
	 * @param int $form_id GF Form ID
1191
	 *
1192
	 * @return string Possibly modified sorting ID
1193
	 */
1194
	private static function _override_sorting_id_by_field_type( $sort_field_id, $form_id ) {
1195
1196
		$form = gravityview_get_form( $form_id );
1197
1198
		$sort_field = GFFormsModel::get_field( $form, $sort_field_id );
1199
1200
		if( ! $sort_field ) {
1201
			return $sort_field_id;
1202
		}
1203
1204
		switch ( $sort_field['type'] ) {
1205
1206
			case 'address':
1207
				// Sorting by full address
1208
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1209
1210
					/**
1211
					 * Override how to sort when sorting address
1212
					 *
1213
					 * @since 1.8
1214
					 *
1215
					 * @param string $address_part `street`, `street2`, `city`, `state`, `zip`, or `country` (default: `city`)
1216
					 * @param string $sort_field_id Field used for sorting
1217
					 * @param int $form_id GF Form ID
1218
					 */
1219
					$address_part = apply_filters( 'gravityview/sorting/address', 'city', $sort_field_id, $form_id );
1220
1221
					switch( strtolower( $address_part ) ){
1222
						case 'street':
1223
							$sort_field_id .= '.1';
1224
							break;
1225
						case 'street2':
1226
							$sort_field_id .= '.2';
1227
							break;
1228
						default:
1229
						case 'city':
0 ignored issues
show
Unused Code introduced by
case 'city': $sort_f..._id .= '.3'; break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1230
							$sort_field_id .= '.3';
1231
							break;
1232
						case 'state':
1233
							$sort_field_id .= '.4';
1234
							break;
1235
						case 'zip':
1236
							$sort_field_id .= '.5';
1237
							break;
1238
						case 'country':
1239
							$sort_field_id .= '.6';
1240
							break;
1241
					}
1242
1243
				}
1244
				break;
1245
			case 'name':
1246
				// Sorting by full name, not first, last, etc.
1247
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1248
					/**
1249
					 * @filter `gravityview/sorting/full-name` Override how to sort when sorting full name.
1250
					 * @since 1.7.4
1251
					 * @param[in,out] string $name_part Sort by `first` or `last` (default: `first`)
1252
					 * @param[in] string $sort_field_id Field used for sorting
1253
					 * @param[in] int $form_id GF Form ID
1254
					 */
1255
					$name_part = apply_filters( 'gravityview/sorting/full-name', 'first', $sort_field_id, $form_id );
1256
1257
					if ( 'last' === strtolower( $name_part ) ) {
1258
						$sort_field_id .= '.6';
1259
					} else {
1260
						$sort_field_id .= '.3';
1261
					}
1262
				}
1263
				break;
1264
			case 'list':
1265
				$sort_field_id = false;
1266
				break;
1267
			case 'time':
1268
1269
				/**
1270
				 * @filter `gravityview/sorting/time` Override how to sort when sorting time
1271
				 * @see GravityView_Field_Time
1272
				 * @since 1.14
1273
				 * @param[in,out] string $name_part Field used for sorting
1274
				 * @param[in] int $form_id GF Form ID
1275
				 */
1276
				$sort_field_id = apply_filters( 'gravityview/sorting/time', $sort_field_id, $form_id );
1277
				break;
1278
		}
1279
1280
		return $sort_field_id;
1281
	}
1282
1283
	/**
1284
	 * Verify if user requested a single entry view
1285
	 * @return boolean|string false if not, single entry slug if true
1286
	 */
1287
	public static function is_single_entry() {
1288
1289
		$var_name = GravityView_Post_Types::get_entry_var_name();
1290
1291
		$single_entry = get_query_var( $var_name );
1292
1293
		/**
1294
		 * Modify the entry that is being displayed.
1295
		 *
1296
		 * @internal Should only be used by things like the oEmbed functionality.
1297
		 * @since 1.6
1298
		 */
1299
		$single_entry = apply_filters( 'gravityview/is_single_entry', $single_entry );
1300
1301
		if ( empty( $single_entry ) ){
1302
			return false;
1303
		} else {
1304
			return $single_entry;
1305
		}
1306
	}
1307
1308
1309
	/**
1310
	 * Register styles and scripts
1311
	 *
1312
	 * @access public
1313
	 * @return void
1314
	 */
1315
	public function add_scripts_and_styles() {
1316
		global $post, $posts;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1317
		// enqueue template specific styles
1318
		if ( $this->getGvOutputData() ) {
1319
1320
			$views = $this->getGvOutputData()->get_views();
1321
1322
			foreach ( $views as $view_id => $data ) {
1323
1324
				/**
1325
				 * Don't enqueue the scripts or styles if it's not going to be displayed.
1326
				 * @since 1.15
1327
				 */
1328
				if( is_user_logged_in() && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
1329
					continue;
1330
				}
1331
1332
				// By default, no thickbox
1333
				$js_dependencies = array( 'jquery', 'gravityview-jquery-cookie' );
1334
				$css_dependencies = array();
1335
1336
				// If the thickbox is enqueued, add dependencies
1337
				if ( ! empty( $data['atts']['lightbox'] ) ) {
1338
1339
					/**
1340
					 * @filter `gravity_view_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1341
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1342
					 */
1343
					$js_dependencies[] = apply_filters( 'gravity_view_lightbox_script', 'thickbox' );
1344
1345
					/**
1346
					 * @filter `gravity_view_lightbox_style` Modify the lightbox CSS slug. Default: `thickbox`
1347
					 * @param string $script_slug If you want to use a different lightbox script, return the name of its CSS file here.
1348
					 */
1349
					$css_dependencies[] = apply_filters( 'gravity_view_lightbox_style', 'thickbox' );
1350
				}
1351
1352
				/**
1353
				 * If the form has checkbox fields, enqueue dashicons
1354
				 * @see https://github.com/katzwebservices/GravityView/issues/536
1355
				 * @since 1.15
1356
				 */
1357
				if( gravityview_view_has_single_checkbox_or_radio( $data['form'], $data['fields'] ) ) {
1358
					$css_dependencies[] = 'dashicons';
1359
				}
1360
1361
				wp_register_script( 'gravityview-jquery-cookie', plugins_url( 'assets/lib/jquery.cookie/jquery.cookie.min.js', GRAVITYVIEW_FILE ), array( 'jquery' ), GravityView_Plugin::version, true );
1362
1363
				$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1364
1365
				wp_register_script( 'gravityview-fe-view', plugins_url( 'assets/js/fe-views' . $script_debug . '.js', GRAVITYVIEW_FILE ), apply_filters( 'gravityview_js_dependencies', $js_dependencies ) , GravityView_Plugin::version, true );
1366
1367
				wp_enqueue_script( 'gravityview-fe-view' );
1368
1369
				if ( ! empty( $data['atts']['sort_columns'] ) ) {
1370
					wp_enqueue_style( 'gravityview_font', plugins_url( 'assets/css/font.css', GRAVITYVIEW_FILE ), $css_dependencies, GravityView_Plugin::version, 'all' );
1371
				}
1372
1373
				$this->enqueue_default_style( $css_dependencies );
1374
1375
				self::add_style( $data['template_id'] );
1376
			}
1377
1378
			if ( 'wp_print_footer_scripts' === current_filter() ) {
1379
1380
				$js_localization = array(
1381
					'cookiepath' => COOKIEPATH,
1382
					'clear' => _x( 'Clear', 'Clear all data from the form', 'gravityview' ),
1383
					'reset' => _x( 'Reset', 'Reset the search form to the state that existed on page load', 'gravityview' ),
1384
				);
1385
1386
				/**
1387
				 * @filter `gravityview_js_localization` Modify the array passed to wp_localize_script()
1388
				 * @param array $js_localization The data padded to the Javascript file
1389
				 * @param array $views Array of View data arrays with View settings
1390
				 */
1391
				$js_localization = apply_filters( 'gravityview_js_localization', $js_localization, $views );
1392
1393
				wp_localize_script( 'gravityview-fe-view', 'gvGlobals', $js_localization );
1394
			}
1395
		}
1396
	}
1397
1398
	/**
1399
	 * Handle enqueuing the `gravityview_default_style` stylesheet
1400
	 *
1401
	 * @since 1.17
1402
	 *
1403
	 * @param array $css_dependencies Dependencies for the `gravityview_default_style` stylesheet
1404
	 *
1405
	 * @return void
1406
	 */
1407
	private function enqueue_default_style( $css_dependencies = array() ) {
1408
1409
		/**
1410
		 * @filter `gravityview_use_legacy_search_css` Should GravityView use the legacy Search Bar stylesheet (from before Version 1.17)?
1411
		 * @since 1.17
1412
		 * @param bool $use_legacy_search_style If true, loads `gv-legacy-search(-rtl).css`. If false, loads `gv-default-styles(-rtl).css`. `-rtl` is added on RTL websites. Default: `false`
1413
		 */
1414
		$use_legacy_search_style = apply_filters( 'gravityview_use_legacy_search_style', false );
1415
1416
		$rtl = is_rtl() ? '-rtl' : '';
1417
1418
		$css_file_base = $use_legacy_search_style ? 'gv-legacy-search' : 'gv-default-styles';
1419
1420
		$path = gravityview_css_url( $css_file_base . $rtl . '.css' );
1421
1422
		wp_enqueue_style( 'gravityview_default_style', $path, $css_dependencies, GravityView_Plugin::version, 'all' );
1423
	}
1424
1425
	/**
1426
	 * Add template extra style if exists
1427
	 * @param string $template_id
1428
	 */
1429
	public static function add_style( $template_id ) {
1430
1431
		if ( ! empty( $template_id ) && wp_style_is( 'gravityview_style_' . $template_id, 'registered' ) ) {
1432
			do_action( 'gravityview_log_debug', sprintf( '[add_style] Adding extra template style for %s', $template_id ) );
1433
			wp_enqueue_style( 'gravityview_style_' . $template_id );
1434
		} elseif ( empty( $template_id ) ) {
1435
			do_action( 'gravityview_log_error', '[add_style] Cannot add template style; template_id is empty' );
1436
		} else {
1437
			do_action( 'gravityview_log_error', sprintf( '[add_style] Cannot add template style; %s is not registered', 'gravityview_style_'.$template_id ) );
1438
		}
1439
1440
	}
1441
1442
1443
	/**
1444
	 * Inject the sorting links on the table columns
1445
	 *
1446
	 * Callback function for hook 'gravityview/template/field_label'
1447
	 * @see GravityView_API::field_label() (in includes/class-api.php)
1448
	 *
1449
	 * @since 1.7
1450
	 *
1451
	 * @param string $label Field label
1452
	 * @param array $field Field settings
1453
	 *
1454
	 * @return string Field Label
1455
	 */
1456
	public function add_columns_sort_links( $label = '', $field, $form ) {
1457
1458
		/**
1459
		 * Not a table-based template; don't add sort icons
1460
		 * @since 1.12
1461
		 */
1462
		if( ! preg_match( '/table/ism', GravityView_View::getInstance()->getTemplatePartSlug() ) ) {
1463
			return $label;
1464
		}
1465
1466
		if ( ! $this->is_field_sortable( $field['id'], $form ) ) {
1467
			return $label;
1468
		}
1469
1470
		$sorting = GravityView_View::getInstance()->getSorting();
1471
1472
		$class = 'gv-sort';
1473
1474
		$sort_field_id = self::_override_sorting_id_by_field_type( $field['id'], $form['id'] );
1475
1476
		$sort_args = array(
1477
			'sort' => $field['id'],
1478
			'dir' => 'asc',
1479
		);
1480
1481
		if ( ! empty( $sorting['key'] ) && (string) $sort_field_id === (string) $sorting['key'] ) {
1482
			//toggle sorting direction.
1483
			if ( 'asc' === $sorting['direction'] ) {
1484
				$sort_args['dir'] = 'desc';
1485
				$class .= ' gv-icon-sort-desc';
1486
			} else {
1487
				$sort_args['dir'] = 'asc';
1488
				$class .= ' gv-icon-sort-asc';
1489
			}
1490
		} else {
1491
			$class .= ' gv-icon-caret-up-down';
1492
		}
1493
1494
		$url = add_query_arg( $sort_args, remove_query_arg( array('pagenum') ) );
0 ignored issues
show
introduced by
No space after opening parenthesis of array is bad style
Loading history...
introduced by
No space before closing parenthesis of array is bad style
Loading history...
1495
1496
		return '<a href="'. esc_url_raw( $url ) .'" class="'. $class .'" ></a>&nbsp;'. $label;
1497
1498
	}
1499
1500
	/**
1501
	 * Checks if field (column) is sortable
1502
	 *
1503
	 * @param string $field Field settings
0 ignored issues
show
Bug introduced by
There is no parameter named $field. 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...
1504
	 * @param array $form Gravity Forms form array
1505
	 *
1506
	 * @since 1.7
1507
	 *
1508
	 * @return bool True: Yes, field is sortable; False: not sortable
1509
	 */
1510
	public function is_field_sortable( $field_id = '', $form = array() ) {
1511
1512
		$field_type = $field_id;
1513
1514
		if( is_numeric( $field_id ) ) {
1515
			$field = GFFormsModel::get_field( $form, $field_id );
1516
			$field_type = $field->type;
1517
		}
1518
1519
		$not_sortable = array(
1520
			'edit_link',
1521
			'delete_link',
1522
		);
1523
1524
		/**
1525
		 * @filter `gravityview/sortable/field_blacklist` Modify what fields should never be sortable.
1526
		 * @since 1.7
1527
		 * @param[in,out] array $not_sortable Array of field types that aren't sortable
1528
		 * @param string $field_type Field type to check whether the field is sortable
1529
		 * @param array $form Gravity Forms form
1530
		 */
1531
		$not_sortable = apply_filters( 'gravityview/sortable/field_blacklist', $not_sortable, $field_type, $form );
1532
1533
		if ( in_array( $field_type, $not_sortable ) ) {
1534
			return false;
1535
		}
1536
1537
		return apply_filters( "gravityview/sortable/formfield_{$form['id']}_{$field_id}", apply_filters( "gravityview/sortable/field_{$field_id}", true, $form ) );
1538
1539
	}
1540
1541
}
1542
1543
GravityView_frontend::getInstance();
1544
1545
1546
1547