Completed
Pull Request — master (#815)
by Zack
10:24 queued 07:03
created

GravityView_frontend   D

Complexity

Total Complexity 192

Size/Duplication

Total Lines 1578
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

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

41 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A initialize() 0 17 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
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
B context_not_configured_warning() 0 33 5
F render_view() 0 243 30
C process_search_dates() 0 66 13
B process_search_only_approved() 0 22 4
B is_entry_approved() 0 17 6
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 1594.

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
		add_action( 'gravityview_after', array( $this, 'context_not_configured_warning' ) );
103
	}
104
105
	/**
106
	 * Get the one true instantiated self
107
	 * @return GravityView_frontend
108
	 */
109
	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...
110
111
		if ( empty( self::$instance ) ) {
112
			self::$instance = new self;
113
			self::$instance->initialize();
114
		}
115
116
		return self::$instance;
117
	}
118
119
	/**
120
	 * @return GravityView_View_Data
121
	 */
122
	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...
123
		return $this->gv_output_data;
124
	}
125
126
	/**
127
	 * @param GravityView_View_Data $gv_output_data
128
	 */
129
	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...
130
		$this->gv_output_data = $gv_output_data;
131
	}
132
133
	/**
134
	 * @return boolean
135
	 */
136
	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...
137
		return $this->is_search;
138
	}
139
140
	/**
141
	 * @param boolean $is_search
142
	 */
143
	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...
144
		$this->is_search = $is_search;
145
	}
146
147
	/**
148
	 * @return bool|int
149
	 */
150
	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...
151
		return $this->single_entry;
152
	}
153
154
	/**
155
	 * Sets the single entry ID and also the entry
156
	 * @param bool|int|string $single_entry
157
	 */
158
	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...
159
160
		$this->single_entry = $single_entry;
161
162
	}
163
164
	/**
165
	 * @return array
166
	 */
167
	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...
168
		return $this->entry;
169
	}
170
171
	/**
172
	 * Set the current entry
173
	 * @param array|int $entry Entry array or entry slug or ID
174
	 */
175
	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...
176
177
		if ( ! is_array( $entry ) ) {
178
			$entry = GVCommon::get_entry( $entry );
179
		}
180
181
		$this->entry = $entry;
182
	}
183
184
	/**
185
	 * @return int
186
	 */
187
	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...
188
		return $this->post_id;
189
	}
190
191
	/**
192
	 * @param int $post_id
193
	 */
194
	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...
195
		$this->post_id = $post_id;
196
	}
197
198
	/**
199
	 * @return boolean
200
	 */
201
	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...
202
		return $this->post_has_shortcode;
203
	}
204
205
	/**
206
	 * @param boolean $post_has_shortcode
207
	 */
208
	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...
209
		$this->post_has_shortcode = $post_has_shortcode;
210
	}
211
212
	/**
213
	 * @return boolean
214
	 */
215
	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...
216
		return $this->is_gravityview_post_type;
217
	}
218
219
	/**
220
	 * @param boolean $is_gravityview_post_type
221
	 */
222
	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...
223
		$this->is_gravityview_post_type = $is_gravityview_post_type;
224
	}
225
226
	/**
227
	 * Set the context view ID used when page contains multiple embedded views or displaying the single entry view
228
	 *
229
	 *
230
	 *
231
	 * @param null $view_id
232
	 */
233
	public function set_context_view_id( $view_id = null ) {
234
235
		if ( ! empty( $view_id ) ) {
236
237
			$this->context_view_id = $view_id;
238
239
		} elseif ( isset( $_GET['gvid'] ) && $this->getGvOutputData()->has_multiple_views() ) {
240
			/**
241
			 * used on a has_multiple_views context
242
			 * @see GravityView_API::entry_link
243
			 * @see GravityView_View_Data::getInstance()->has_multiple_views()
244
			 */
245
			$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...
246
247
		} elseif ( ! $this->getGvOutputData()->has_multiple_views() )  {
248
			$array_keys = array_keys( $this->getGvOutputData()->get_views() );
249
			$this->context_view_id = array_pop( $array_keys );
250
			unset( $array_keys );
251
		}
252
253
	}
254
255
	/**
256
	 * Returns the the view_id context when page contains multiple embedded views or displaying single entry view
257
	 *
258
	 * @since 1.5.4
259
	 *
260
	 * @return string
261
	 */
262
	public function get_context_view_id() {
263
		return $this->context_view_id;
264
	}
265
266
	/**
267
	 * Allow GravityView entry endpoints on the front page of a site
268
	 *
269
	 * @link  https://core.trac.wordpress.org/ticket/23867 Fixes this core issue
270
	 * @link https://wordpress.org/plugins/cpt-on-front-page/ Code is based on this
271
	 *
272
	 * @since 1.17.3
273
	 *
274
	 * @param WP_Query &$query (passed by reference)
275
	 *
276
	 * @return void
277
	 */
278
	public function parse_query_fix_frontpage( &$query ) {
279
		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...
280
281
		$is_front_page = ( $query->is_home || $query->is_page );
282
		$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...
283
		$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...
284
285
		if (  $is_front_page && $show_on_front && $front_page_id ) {
286
287
			// Force to be an array, potentially a query string ( entry=16 )
288
			$_query = wp_parse_args( $query->query );
289
290
			// pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
291
			if ( isset( $_query['pagename'] ) && '' === $_query['pagename'] ) {
292
				unset( $_query['pagename'] );
293
			}
294
295
			// this is where will break from core wordpress
296
			$ignore = array( 'preview', 'page', 'paged', 'cpage' );
297
			$endpoints = rgobj( $wp_rewrite, 'endpoints' );
298
			foreach ( (array) $endpoints as $endpoint ) {
299
				$ignore[] = $endpoint[1];
300
			}
301
			unset( $endpoints );
302
303
			// Modify the query if:
304
			// - We're on the "Page on front" page (which we are), and:
305
			// - The query is empty OR
306
			// - The query includes keys that are associated with registered endpoints. `entry`, for example.
307
			if ( empty( $_query ) || ! array_diff( array_keys( $_query ), $ignore ) ) {
308
309
				$qv =& $query->query_vars;
310
311
				// Prevent redirect when on the single entry endpoint
312
				if( self::is_single_entry() ) {
313
					add_filter( 'redirect_canonical', '__return_false' );
314
				}
315
316
				$query->is_page = true;
317
				$query->is_home = false;
318
				$qv['page_id']  = $front_page_id;
319
320
				// Correct <!--nextpage--> for page_on_front
321
				if ( ! empty( $qv['paged'] ) ) {
322
					$qv['page'] = $qv['paged'];
323
					unset( $qv['paged'] );
324
				}
325
			}
326
327
			// reset the is_singular flag after our updated code above
328
			$query->is_singular = $query->is_single || $query->is_page || $query->is_attachment;
329
		}
330
	}
331
332
	/**
333
	 * Read the $post and process the View data inside
334
	 * @param  array  $wp Passed in the `wp` hook. Not used.
335
	 * @return void
336
	 */
337
	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...
338
		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...
339
340
		// If in admin and NOT AJAX request, get outta here.
341
		if ( GravityView_Plugin::is_admin() )  {
342
			return;
343
		}
344
345
		// Calculate requested Views
346
		$this->setGvOutputData( GravityView_View_Data::getInstance( $post ) );
347
348
		// !important: we need to run this before getting single entry (to kick the advanced filter)
349
		$this->set_context_view_id();
350
351
		$this->setIsGravityviewPostType( get_post_type( $post ) === 'gravityview' );
352
353
		$post_id = $this->getPostId() ? $this->getPostId() : (isset( $post ) ? $post->ID : null );
354
		$this->setPostId( $post_id );
355
		$post_has_shortcode = ! empty( $post->post_content ) ? gravityview_has_shortcode_r( $post->post_content, 'gravityview' ) : false;
356
		$this->setPostHasShortcode( $this->isGravityviewPostType() ? null : ! empty( $post_has_shortcode ) );
357
358
		// check if the View is showing search results (only for multiple entries View)
359
		$this->setIsSearch( $this->is_searching() );
360
361
		unset( $entry, $post_id, $post_has_shortcode );
362
	}
363
364
	/**
365
	 * Set the entry
366
	 */
367
	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...
368
		$entry_id = self::is_single_entry();
369
		$this->setSingleEntry( $entry_id );
370
		$this->setEntry( $entry_id );
371
	}
372
373
	/**
374
	 * Checks if the current View is presenting search results
375
	 *
376
	 * @since 1.5.4
377
	 *
378
	 * @return boolean True: Yes, it's a search; False: No, not a search.
379
	 */
380
	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...
381
382
		// It's a single entry, not search
383
		if ( $this->getSingleEntry() ) {
384
			return false;
385
		}
386
387
		$search_method = GravityView_Widget_Search::getInstance()->get_search_method();
388
389
		if( 'post' === $search_method ) {
390
			$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...
391
		} else {
392
			$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...
393
		}
394
395
		// No $_GET parameters
396
		if ( empty( $get ) || ! is_array( $get ) ) {
397
			return false;
398
		}
399
400
		// Remove empty values
401
		$get = array_filter( $get );
402
403
		// If the $_GET parameters are empty, it's no search.
404
		if ( empty( $get ) ) {
405
			return false;
406
		}
407
408
		$search_keys = array_keys( $get );
409
410
		$search_match = implode( '|', self::$search_parameters );
411
412
		foreach ( $search_keys as $search_key ) {
413
414
			// Analyze the search key $_GET parameter and see if it matches known GV args
415
			if ( preg_match( '/(' . $search_match . ')/i', $search_key ) ) {
416
				return true;
417
			}
418
		}
419
420
		return false;
421
	}
422
423
	/**
424
	 * Filter the title for the single entry view
425
	 *
426
	 * @param  string $title   current title
427
	 * @param  int $passed_post_id Post ID
428
	 * @return string          (modified) title
429
	 */
430
	public function single_entry_title( $title, $passed_post_id = null ) {
431
		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...
432
433
		// If this is the directory view, return.
434
		if ( ! $this->getSingleEntry() ) {
435
			return $title;
436
		}
437
438
		$entry = $this->getEntry();
439
440
		/**
441
		 * @filter `gravityview/single/title/out_loop` Apply the Single Entry Title filter outside the WordPress loop?
442
		 * @param boolean $in_the_loop Whether to apply the filter to the menu title and the meta tag <title> - outside the loop
443
		 * @param array $entry Current entry
444
		 */
445
		$apply_outside_loop = apply_filters( 'gravityview/single/title/out_loop' , in_the_loop(), $entry );
446
447
		if ( ! $apply_outside_loop ) {
448
			return $title;
449
		}
450
451
		// User reported WooCommerce doesn't pass two args.
452
		if ( empty( $passed_post_id ) )  {
453
			return $title;
454
		}
455
456
		// Don't modify the title for anything other than the current view/post.
457
		// This is true for embedded shortcodes and Views.
458
		if ( is_object( $post ) && (int) $post->ID !== (int) $passed_post_id ) {
459
			return $title;
460
		}
461
462
		$context_view_id = $this->get_context_view_id();
463
464
		if ( $this->getGvOutputData()->has_multiple_views() && ! empty( $context_view_id ) ) {
465
			$view_meta = $this->getGvOutputData()->get_view( $context_view_id );
466
		} else {
467
			foreach ( $this->getGvOutputData()->get_views() as $view_id => $view_data ) {
468
				if ( intval( $view_data['form_id'] ) === intval( $entry['form_id'] ) ) {
469
					$view_meta = $view_data;
470
					break;
471
				}
472
			}
473
		}
474
475
		if ( ! empty( $view_meta['atts']['single_title'] ) ) {
476
477
			$title = $view_meta['atts']['single_title'];
478
479
			// We are allowing HTML in the fields, so no escaping the output
480
			$title = GravityView_API::replace_variables( $title, $view_meta['form'], $entry );
481
482
			$title = do_shortcode( $title );
483
		}
484
485
		return $title;
486
	}
487
488
489
	/**
490
	 * In case View post is called directly, insert the view in the post content
491
	 *
492
	 * @access public
493
	 * @static
494
	 * @param mixed $content
495
	 * @return string Add the View output into View CPT content
496
	 */
497
	public function insert_view_in_content( $content ) {
498
499
		// Plugins may run through the content in the header. WP SEO does this for its OpenGraph functionality.
500
		if ( ! did_action( 'loop_start' ) ) {
501
502
			do_action( 'gravityview_log_debug', '[insert_view_in_content] Not processing yet: loop_start hasn\'t run yet. Current action:', current_filter() );
503
504
			return $content;
505
		}
506
507
		//	We don't want this filter to run infinite loop on any post content fields
508
		remove_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
509
510
		// Otherwise, this is called on the Views page when in Excerpt mode.
511
		if ( is_admin() ) {
512
			return $content;
513
		}
514
515
		if ( $this->isGravityviewPostType() ) {
516
517
			/** @since 1.7.4 */
518
			if ( is_preview() && ! gravityview_get_form_id( $this->post_id ) ) {
519
				$content .= __( 'When using a Start Fresh template, you must save the View before a Preview is available.', 'gravityview' );
520
			} else {
521
				foreach ( $this->getGvOutputData()->get_views() as $view_id => $data ) {
522
					$content .= $this->render_view( array( 'id' => $view_id ) );
523
				}
524
			}
525
		}
526
527
		//	Add the filter back in
528
		add_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
529
530
		return $content;
531
	}
532
533
	/**
534
	 * Disable comments on GravityView post types
535
	 * @param  boolean $open    existing status
536
	 * @param  int $post_id Post ID
537
	 * @return boolean
538
	 */
539
	public function comments_open( $open, $post_id ) {
540
541
		if ( $this->isGravityviewPostType() ) {
542
			$open = false;
543
		}
544
545
		/**
546
		 * @filter `gravityview/comments_open` Whether to set comments to open or closed.
547
		 * @since  1.5.4
548
		 * @param  boolean $open Open or closed status
549
		 * @param  int $post_id Post ID to set comment status for
550
		 */
551
		$open = apply_filters( 'gravityview/comments_open', $open, $post_id );
552
553
		return $open;
554
	}
555
556
	/**
557
	 * Display a warning when a View has not been configured
558
	 *
559
	 * @since 1.19.2
560
	 *
561
	 * @param string $context The current context that has no configuration
0 ignored issues
show
Bug introduced by
There is no parameter named $context. 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...
562
	 *
563
	 * @return void
564
	 */
565
	public function context_not_configured_warning( $view_id = 0 ) {
566
567
		$fields = GravityView_View::getInstance()->getContextFields();
568
569
		if ( ! empty( $fields ) ) {
570
			return;
571
		}
572
573
		$context = GravityView_View::getInstance()->getContext();
574
575
		switch( $context ) {
576
			case 'directory':
577
				$tab = __( 'Multiple Entries', 'gravityview' );
578
				break;
579
			case 'edit':
580
				$tab = __( 'Edit Entry', 'gravityview' );
581
				break;
582
			case 'single':
583
			default:
584
				$tab = __( 'Single Entry', 'gravityview' );
585
				break;
586
		}
587
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
588
589
		$title = sprintf( esc_html_x('The %s layout has not been configured.', 'Displayed when a View is not configured. %s is replaced by the tab label', 'gravityview' ), $tab );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
590
		$edit_link = admin_url( sprintf( 'post.php?post=%d&action=edit#%s-view', $view_id, $context ) );
591
		$action_text = sprintf( esc_html__('Add fields to %s', 'gravityview' ), $tab );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
592
		$message = esc_html__( 'You can only see this message because you are able to edit this View.', 'gravityview' );
593
594
		$output = sprintf( '<h3>%s <strong><a href="%s">%s</a></strong></h3><p>%s</p>', $title, esc_url( $edit_link ), $action_text, $message );
595
596
		echo GVCommon::generate_notice( $output, 'gv-error error', 'edit_gravityview', $view_id );
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'GVCommon'
Loading history...
597
	}
598
599
600
	/**
601
	 * Core function to render a View based on a set of arguments
602
	 *
603
	 * @access public
604
	 * @static
605
	 * @param array $passed_args {
606
	 *
607
	 *      Settings for rendering the View
608
	 *
609
	 *      @type int $id View id
610
	 *      @type int $page_size Number of entries to show per page
611
	 *      @type string $sort_field Form field id to sort
612
	 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
613
	 *      @type string $start_date - Ymd
614
	 *      @type string $end_date - Ymd
615
	 *      @type string $class - assign a html class to the view
616
	 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
617
	 * }
618
	 *
619
	 * @return string|null HTML output of a View, NULL if View isn't found
620
	 */
621
	public function render_view( $passed_args ) {
622
623
		// validate attributes
624
		if ( empty( $passed_args['id'] ) ) {
625
			do_action( 'gravityview_log_error', '[render_view] Returning; no ID defined.', $passed_args );
626
			return null;
627
		}
628
629
		// Solve problem when loading content via admin-ajax.php
630
		// @hack
631
		if ( ! $this->getGvOutputData() ) {
632
633
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not defined; parsing content.', $passed_args );
634
635
			$this->parse_content();
636
		}
637
638
		// Make 100% sure that we're dealing with a properly called situation
639
		if ( ! is_object( $this->getGvOutputData() ) || ! is_callable( array( $this->getGvOutputData(), 'get_view' ) ) ) {
640
641
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not an object or get_view not callable.', $this->getGvOutputData() );
642
643
			return null;
644
		}
645
646
		$view_id = $passed_args['id'];
647
648
		$view_data = $this->getGvOutputData()->get_view( $view_id, $passed_args );
649
650
		do_action( 'gravityview_log_debug', '[render_view] View Data: ', $view_data );
651
652
		do_action( 'gravityview_log_debug', '[render_view] Init View. Arguments: ', $passed_args );
653
654
		// The passed args were always winning, even if they were NULL.
655
		// This prevents that. Filters NULL, FALSE, and empty strings.
656
		$passed_args = array_filter( $passed_args, 'strlen' );
657
658
		//Override shortcode args over View template settings
659
		$atts = wp_parse_args( $passed_args, $view_data['atts'] );
660
661
		do_action( 'gravityview_log_debug', '[render_view] Arguments after merging with View settings: ', $atts );
662
663
		// It's password protected and you need to log in.
664
		if ( post_password_required( $view_id ) ) {
665
666
			do_action( 'gravityview_log_error', sprintf( '[render_view] Returning: View %d is password protected.', $view_id ) );
667
668
			// If we're in an embed or on an archive page, show the password form
669
			if ( get_the_ID() !== $view_id ) {
670
				return get_the_password_form();
671
			}
672
673
			// Otherwise, just get outta here
674
			return null;
675
		}
676
677
		/**
678
		 * Don't render View if user isn't allowed to see it
679
		 * @since 1.15
680
		 * @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.
681
		 */
682
		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 ) ) {
683
684
			do_action( 'gravityview_log_debug', sprintf( '%s Returning: View %d is not visible by current user.', __METHOD__, $view_id ) );
685
686
			return null;
687
		}
688
689
		if( $this->isGravityviewPostType() ) {
690
691
			/**
692
			 * @filter `gravityview_direct_access` Should Views be directly accessible, or only visible using the shortcode?
693
			 * @see https://codex.wordpress.org/Function_Reference/register_post_type#public
694
			 * @see GravityView_Post_Types::init_post_types
695
			 * @since 1.15.2
696
			 * @param[in,out] boolean `true`: allow Views to be accessible directly. `false`: Only allow Views to be embedded via shortcode. Default: `true`
697
			 * @param int $view_id The ID of the View currently being requested. `0` for general setting
698
			 */
699
			$direct_access = apply_filters( 'gravityview_direct_access', true, $view_id );
700
701
			$embed_only = ! empty( $atts['embed_only'] );
702
703
			if( ! $direct_access || ( $embed_only && ! GVCommon::has_cap( 'read_private_gravityviews' ) ) ) {
704
				return __( 'You are not allowed to view this content.', 'gravityview' );
705
			}
706
		}
707
708
		ob_start();
709
710
		/**
711
		 * Set globals for templating
712
		 * @deprecated 1.6.2
713
		 */
714
		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...
715
716
		$gravityview_view = new GravityView_View( $view_data );
717
718
		$post_id = ! empty( $atts['post_id'] ) ? intval( $atts['post_id'] ) : $this->getPostId();
719
720
		$gravityview_view->setPostId( $post_id );
721
722
		if ( ! $this->getSingleEntry() ) {
723
724
			// user requested Directory View
725
			do_action( 'gravityview_log_debug', '[render_view] Executing Directory View' );
726
727
			//fetch template and slug
728
			$view_slug = apply_filters( 'gravityview_template_slug_'. $view_data['template_id'], 'table', 'directory' );
729
730
			do_action( 'gravityview_log_debug', '[render_view] View template slug: ', $view_slug );
731
732
			/**
733
			 * Disable fetching initial entries for views that don't need it (DataTables)
734
			 */
735
			$get_entries = apply_filters( 'gravityview_get_view_entries_'.$view_slug, true );
736
737
			/**
738
			 * Hide View data until search is performed
739
			 * @since 1.5.4
740
			 */
741
			if ( ! empty( $atts['hide_until_searched'] ) && ! $this->isSearch() ) {
742
				$gravityview_view->setHideUntilSearched( true );
743
				$get_entries = false;
744
			}
745
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
746
747
			if ( $get_entries ) {
748
749
				if ( ! empty( $atts['sort_columns'] ) ) {
750
					// add filter to enable column sorting
751
					add_filter( 'gravityview/template/field_label', array( $this, 'add_columns_sort_links' ) , 100, 3 );
752
				}
753
754
				$view_entries = self::get_view_entries( $atts, $view_data['form_id'] );
755
756
				do_action( 'gravityview_log_debug', sprintf( '[render_view] Get Entries. Found %s entries total, showing %d entries', $view_entries['count'], sizeof( $view_entries['entries'] ) ) );
757
758
			} else {
759
760
				$view_entries = array( 'count' => null, 'entries' => null, 'paging' => null );
761
762
				do_action( 'gravityview_log_debug', '[render_view] Not fetching entries because `gravityview_get_view_entries_'.$view_slug.'` is false' );
763
			}
764
765
			$gravityview_view->setPaging( $view_entries['paging'] );
766
			$gravityview_view->setContext( 'directory' );
767
			$sections = array( 'header', 'body', 'footer' );
768
769
		} else {
770
771
			// user requested Single Entry View
772
			do_action( 'gravityview_log_debug', '[render_view] Executing Single View' );
773
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
774
775
			/**
776
			 * @action `gravityview_render_entry_{View ID}` Before rendering a single entry for a specific View ID
777
			 * @since 1.17
778
			 */
779
			do_action( 'gravityview_render_entry_'.$view_data['id'] );
780
781
			$entry = $this->getEntry();
782
783
			// You are not permitted to view this entry.
784
			if ( empty( $entry ) || ! self::is_entry_approved( $entry, $atts ) ) {
785
786
				do_action( 'gravityview_log_debug', '[render_view] Entry does not exist. This may be because of View filters limiting access.' );
787
788
				/**
789
				 * @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.
790
				 * @since 1.6
791
				 * @param string $message Default: "You have attempted to view an entry that is not visible or may not exist."
792
				 */
793
				$message = apply_filters( 'gravityview/render/entry/not_visible', __( 'You have attempted to view an entry that is not visible or may not exist.', 'gravityview' ) );
794
795
				/**
796
				 * @since 1.6
797
				 */
798
				echo esc_attr( $message );
799
800
				return null;
801
			}
802
803
			// We're in single view, but the view being processed is not the same view the single entry belongs to.
804
			// important: do not remove this as it prevents fake attempts of displaying entries from other views/forms
805
			if ( $this->getGvOutputData()->has_multiple_views() && $view_id != $this->get_context_view_id() ) {
806
				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 );
807
				return null;
808
			}
809
810
			//fetch template and slug
811
			$view_slug = apply_filters( 'gravityview_template_slug_' . $view_data['template_id'], 'table', 'single' );
812
			do_action( 'gravityview_log_debug', '[render_view] View single template slug: ', $view_slug );
813
814
			//fetch entry detail
815
			$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...
816
			$view_entries['entries'][] = $entry;
817
			do_action( 'gravityview_log_debug', '[render_view] Get single entry: ', $view_entries['entries'] );
818
819
			$back_link_label = isset( $atts['back_link_label'] ) ? $atts['back_link_label'] : null;
820
821
			// set back link label
822
			$gravityview_view->setBackLinkLabel( $back_link_label );
823
			$gravityview_view->setContext( 'single' );
824
			$sections = array( 'single' );
825
826
		}
827
828
		// add template style
829
		self::add_style( $view_data['template_id'] );
830
831
		// Prepare to render view and set vars
832
		$gravityview_view->setEntries( $view_entries['entries'] );
833
		$gravityview_view->setTotalEntries( $view_entries['count'] );
834
835
		// If Edit
836
		if ( 'edit' === gravityview_get_context() ) {
837
838
			do_action( 'gravityview_log_debug', '[render_view] Edit Entry ' );
839
840
			do_action( 'gravityview_edit_entry', $this->getGvOutputData() );
841
842
			return ob_get_clean();
843
844
		} else {
845
			// finaly we'll render some html
846
			$sections = apply_filters( 'gravityview_render_view_sections', $sections, $view_data['template_id'] );
847
848
			do_action( 'gravityview_log_debug', '[render_view] Sections to render: ', $sections );
849
			foreach ( $sections as $section ) {
850
				do_action( 'gravityview_log_debug', '[render_view] Rendering '. $section . ' section.' );
851
				$gravityview_view->render( $view_slug, $section, false );
852
			}
853
		}
854
855
		//@todo: check why we need the IF statement vs. print the view id always.
856
		if ( $this->isGravityviewPostType() || $this->isPostHasShortcode() ) {
857
			// Print the View ID to enable proper cookie pagination
858
			echo '<input type="hidden" class="gravityview-view-id" value="' . esc_attr( $view_id ) . '">';
859
		}
860
		$output = ob_get_clean();
861
862
		return $output;
863
	}
864
865
	/**
866
	 * Process the start and end dates for a view - overrides values defined in shortcode (if needed)
867
	 *
868
	 * The `start_date` and `end_date` keys need to be in a format processable by GFFormsModel::get_date_range_where(),
869
	 * which uses \DateTime() format.
870
	 *
871
	 * 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()},
872
	 * including strings like "now" or "-1 year" or "-3 days".
873
	 *
874
	 * @see GFFormsModel::get_date_range_where
875
	 *
876
	 * @param  array      $args            View settings
877
	 * @param  array      $search_criteria Search being performed, if any
878
	 * @return array                       Modified `$search_criteria` array
879
	 */
880
	public static function process_search_dates( $args, $search_criteria = array() ) {
881
882
		$return_search_criteria = $search_criteria;
883
884
		foreach ( array( 'start_date', 'end_date' ) as $key ) {
885
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
886
887
			// Is the start date or end date set in the view or shortcode?
888
			// If so, we want to make sure that the search doesn't go outside the bounds defined.
889
			if ( ! empty( $args[ $key ] ) ) {
890
891
				// Get a timestamp and see if it's a valid date format
892
				$date = strtotime( $args[ $key ] );
893
894
				// The date was invalid
895
				if ( empty( $date ) ) {
896
					do_action( 'gravityview_log_error', __METHOD__ . ' Invalid ' . $key . ' date format: ' . $args[ $key ] );
897
					continue;
898
				}
899
900
				// The format that Gravity Forms expects for start_date and day-specific (not hour/second-specific) end_date
901
				$datetime_format = 'Y-m-d H:i:s';
902
				$search_is_outside_view_bounds = false;
903
904
				if( ! empty( $search_criteria[ $key ] ) ) {
905
906
					$search_date = strtotime( $search_criteria[ $key ] );
907
908
					// The search is for entries before the start date defined by the settings
909
					switch ( $key ) {
910
						case 'end_date':
911
							/**
912
							 * If the end date is formatted as 'Y-m-d', it should be formatted without hours and seconds
913
							 * so that Gravity Forms can convert the day to 23:59:59 the previous day.
914
							 *
915
							 * If it's a relative date ("now" or "-1 day"), then it should use the precise date format
916
							 *
917
							 * @see GFFormsModel::get_date_range_where
918
							 */
919
							$datetime_format               = gravityview_is_valid_datetime( $args[ $key ] ) ? 'Y-m-d' : 'Y-m-d H:i:s';
920
							$search_is_outside_view_bounds = ( $search_date > $date );
921
							break;
922
						case 'start_date':
923
							$search_is_outside_view_bounds = ( $search_date < $date );
924
							break;
925
					}
926
				}
927
928
				// If there is no search being performed, or if there is a search being performed that's outside the bounds
929
				if ( empty( $search_criteria[ $key ] ) || $search_is_outside_view_bounds ) {
930
931
					// Then we override the search and re-set the start date
932
					$return_search_criteria[ $key ] = date_i18n( $datetime_format , $date, true );
933
				}
934
			}
935
		}
936
937
		if( isset( $return_search_criteria['start_date'] ) && isset( $return_search_criteria['end_date'] ) ) {
938
			// The start date is AFTER the end date. This will result in no results, but let's not force the issue.
939
			if ( strtotime( $return_search_criteria['start_date'] ) > strtotime( $return_search_criteria['end_date'] ) ) {
940
				do_action( 'gravityview_log_error', __METHOD__ . ' Invalid search: the start date is after the end date.', $return_search_criteria );
941
			}
942
		}
943
944
		return $return_search_criteria;
945
	}
946
947
948
	/**
949
	 * Process the approved only search criteria according to the View settings
950
	 *
951
	 * @param  array      $args            View settings
952
	 * @param  array      $search_criteria Search being performed, if any
953
	 * @return array                       Modified `$search_criteria` array
954
	 */
955
	public static function process_search_only_approved( $args, $search_criteria ) {
956
957
		/** @since 1.19 */
958
		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...
959
			do_action( 'gravityview_log_debug', __METHOD__ . ': User can moderate entries; showing all approval statuses' );
960
			return $search_criteria;
961
		}
962
963
		if ( ! empty( $args['show_only_approved'] ) ) {
964
965
			$search_criteria['field_filters'][] = array(
966
				'key' => GravityView_Entry_Approval::meta_key,
967
				'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...
968
			);
969
970
			$search_criteria['field_filters']['mode'] = 'all'; // force all the criterias to be met
971
972
			do_action( 'gravityview_log_debug', '[process_search_only_approved] Search Criteria if show only approved: ', $search_criteria );
973
		}
974
975
		return $search_criteria;
976
	}
977
978
979
	/**
980
	 * Check if a certain entry is approved.
981
	 *
982
	 * If we pass the View settings ($args) it will check the 'show_only_approved' setting before
983
	 *   checking the entry approved field, returning true if show_only_approved = false.
984
	 *
985
	 * @since 1.7
986
	 * @since 1.18 Converted check to use GravityView_Entry_Approval_Status::is_approved
987
	 *
988
	 * @uses GravityView_Entry_Approval_Status::is_approved
989
	 *
990
	 * @param array $entry  Entry object
991
	 * @param array $args   View settings (optional)
992
	 *
993
	 * @return bool
994
	 */
995
	public static function is_entry_approved( $entry, $args = array() ) {
996
997
		if ( empty( $entry['id'] ) || ( array_key_exists( 'show_only_approved', $args ) && ! $args['show_only_approved'] ) ) {
998
			// is implicitly approved if entry is null or View settings doesn't require to check for approval
999
			return true;
1000
		}
1001
1002
		/** @since 1.19 */
1003
		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...
1004
			do_action( 'gravityview_log_debug', __METHOD__ . ': User can moderate entries, so entry is approved for viewing' );
1005
			return true;
1006
		}
1007
1008
		$is_approved = gform_get_meta( $entry['id'], GravityView_Entry_Approval::meta_key );
1009
1010
		return GravityView_Entry_Approval_Status::is_approved( $is_approved );
1011
	}
1012
1013
	/**
1014
	 * Parse search criteria for a entries search.
1015
	 *
1016
	 * array(
1017
	 * 	'search_field' => 1, // ID of the field
1018
	 *  'search_value' => '', // Value of the field to search
1019
	 *  'search_operator' => 'contains', // 'is', 'isnot', '>', '<', 'contains'
1020
	 *  'show_only_approved' => 0 or 1 // Boolean
1021
	 * )
1022
	 *
1023
	 * @param  array $args    Array of args
1024
	 * @param  int $form_id Gravity Forms form ID
1025
	 * @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.
1026
	 */
1027
	public static function get_search_criteria( $args, $form_id ) {
1028
1029
		/**
1030
		 * @filter `gravityview_fe_search_criteria` Modify the search criteria
1031
		 * @see GravityView_Widget_Search::filter_entries Adds the default search criteria
1032
		 * @param array $search_criteria Empty `field_filters` key
1033
		 * @param int $form_id ID of the Gravity Forms form that is being searched
1034
		 */
1035
		$search_criteria = apply_filters( 'gravityview_fe_search_criteria', array( 'field_filters' => array() ), $form_id );
1036
1037
		$original_search_criteria = $search_criteria;
1038
1039
		do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after hook gravityview_fe_search_criteria: ', $search_criteria );
1040
1041
		// implicity search
1042
		if ( ! empty( $args['search_value'] ) ) {
1043
1044
			// Search operator options. Options: `is` or `contains`
1045
			$operator = ! empty( $args['search_operator'] ) && in_array( $args['search_operator'], array( 'is', 'isnot', '>', '<', 'contains' ) ) ? $args['search_operator'] : 'contains';
1046
1047
			$search_criteria['field_filters'][] = array(
1048
				'key' => rgget( 'search_field', $args ), // The field ID to search
1049
				'value' => _wp_specialchars( $args['search_value'] ), // The value to search. Encode ampersands but not quotes.
1050
				'operator' => $operator,
1051
			);
1052
		}
1053
1054
		if( $search_criteria !== $original_search_criteria ) {
1055
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after implicity search: ', $search_criteria );
1056
		}
1057
1058
		// Handle setting date range
1059
		$search_criteria = self::process_search_dates( $args, $search_criteria );
1060
1061
		if( $search_criteria !== $original_search_criteria ) {
1062
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after date params: ', $search_criteria );
1063
		}
1064
1065
		// remove not approved entries
1066
		$search_criteria = self::process_search_only_approved( $args, $search_criteria );
1067
1068
		/**
1069
		 * @filter `gravityview_status` Modify entry status requirements to be included in search results.
1070
		 * @param string $status Default: `active`. Accepts all Gravity Forms entry statuses, including `spam` and `trash`
1071
		 */
1072
		$search_criteria['status'] = apply_filters( 'gravityview_status', 'active', $args );
1073
1074
		return $search_criteria;
1075
	}
1076
1077
1078
1079
	/**
1080
	 * Core function to calculate View multi entries (directory) based on a set of arguments ($args):
1081
	 *   $id - View id
1082
	 *   $page_size - Page
1083
	 *   $sort_field - form field id to sort
1084
	 *   $sort_direction - ASC / DESC
1085
	 *   $start_date - Ymd
1086
	 *   $end_date - Ymd
1087
	 *   $class - assign a html class to the view
1088
	 *   $offset (optional) - This is the start point in the current data set (0 index based).
1089
	 *
1090
	 *
1091
	 *
1092
	 * @uses  gravityview_get_entries()
1093
	 * @access public
1094
	 * @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...
1095
	 *   - $id - View id
1096
	 *   - $page_size - Page
1097
	 *   - $sort_field - form field id to sort
1098
	 *   - $sort_direction - ASC / DESC
1099
	 *   - $start_date - Ymd
1100
	 *   - $end_date - Ymd
1101
	 *   - $class - assign a html class to the view
1102
	 *   - $offset (optional) - This is the start point in the current data set (0 index based).
1103
	 * @param int $form_id Gravity Forms Form ID
1104
	 * @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
1105
	 */
1106
	public static function get_view_entries( $args, $form_id ) {
1107
1108
		do_action( 'gravityview_log_debug', '[get_view_entries] init' );
1109
		// start filters and sorting
1110
1111
		/**
1112
		 * Process search parameters
1113
		 * @var array
1114
		 */
1115
		$search_criteria = self::get_search_criteria( $args, $form_id );
1116
1117
		// Paging & offset
1118
		$page_size = ! empty( $args['page_size'] ) ? intval( $args['page_size'] ) : apply_filters( 'gravityview_default_page_size', 25 );
1119
1120
		if ( -1 === $page_size ) {
1121
			$page_size = PHP_INT_MAX;
1122
		}
1123
1124
		if ( isset( $args['offset'] ) ) {
1125
			$offset = intval( $args['offset'] );
1126
		} else {
1127
			$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...
1128
			$offset = ( $curr_page - 1 ) * $page_size;
1129
		}
1130
1131
		$paging = array(
1132
			'offset' => $offset,
1133
			'page_size' => $page_size,
1134
		);
1135
1136
		do_action( 'gravityview_log_debug', __METHOD__ . ': Paging: ', $paging );
1137
1138
		// Sorting
1139
		$sorting = self::updateViewSorting( $args, $form_id );
1140
1141
		$parameters = array(
1142
			'search_criteria' => $search_criteria,
1143
			'sorting' => $sorting,
1144
			'paging' => $paging,
1145
			'cache' => isset( $args['cache'] ) ? $args['cache'] : true,
1146
		);
1147
1148
		/**
1149
		 * @filter `gravityview_get_entries` Filter get entries criteria
1150
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1151
		 * @param array $args View configuration args. {
1152
		 *      @type int $id View id
1153
		 *      @type int $page_size Number of entries to show per page
1154
		 *      @type string $sort_field Form field id to sort
1155
		 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
1156
		 *      @type string $start_date - Ymd
1157
		 *      @type string $end_date - Ymd
1158
		 *      @type string $class - assign a html class to the view
1159
		 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
1160
		 * }
1161
		 * @param int $form_id ID of Gravity Forms form
1162
		 */
1163
		$parameters = apply_filters( 'gravityview_get_entries', $parameters, $args, $form_id );
1164
1165
		/**
1166
		 * @filter `gravityview_get_entries_{View ID}` Filter get entries criteria
1167
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1168
		 * @param array $args View configuration args.
1169
		 */
1170
		$parameters = apply_filters( 'gravityview_get_entries_'.$args['id'], $parameters, $args, $form_id );
1171
1172
		do_action( 'gravityview_log_debug', __METHOD__ . ': $parameters passed to gravityview_get_entries(): ', $parameters );
1173
1174
		//fetch entries
1175
		$count = 0;
1176
		$entries = gravityview_get_entries( $form_id, $parameters, $count );
1177
1178
		do_action( 'gravityview_log_debug', sprintf( '%s: Get Entries. Found: %s entries', __METHOD__, $count ), $entries );
1179
1180
		/**
1181
		 * @filter `gravityview_view_entries` Filter the entries output to the View
1182
		 * @deprecated since 1.5.2
1183
		 * @param array $args View settings associative array
1184
		 * @var array
1185
		 */
1186
		$entries = apply_filters( 'gravityview_view_entries', $entries, $args );
1187
1188
		/**
1189
		 * @filter `gravityview/view/entries` Filter the entries output to the View
1190
		 * @param array $criteria associative array containing count, entries & paging
1191
		 * @param array $args View settings associative array
1192
		 * @since 1.5.2
1193
		 */
1194
		return apply_filters( 'gravityview/view/entries', compact( 'count', 'entries', 'paging' ), $args );
1195
1196
	}
1197
1198
1199
	/**
1200
	 * Updates the View sorting criteria
1201
	 *
1202
	 * @since 1.7
1203
	 *
1204
	 * @param array $args View settings. Required to have `sort_field` and `sort_direction` keys
1205
	 * @param int $form_id The ID of the form used to sort
1206
	 * @return array $sorting Array with `key`, `direction` and `is_numeric` keys
1207
	 */
1208
	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...
1209
		$sorting = array();
1210
		$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...
1211
		$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...
1212
1213
		$sort_field_id = self::_override_sorting_id_by_field_type( $sort_field_id, $form_id );
1214
1215
		if ( ! empty( $sort_field_id ) ) {
1216
			$sorting = array(
1217
				'key' => $sort_field_id,
1218
				'direction' => strtolower( $sort_direction ),
1219
				'is_numeric' => GVCommon::is_field_numeric( $form_id, $sort_field_id )
1220
			);
1221
		}
1222
1223
		GravityView_View::getInstance()->setSorting( $sorting );
1224
1225
		do_action( 'gravityview_log_debug', '[updateViewSorting] Sort Criteria : ', $sorting );
1226
1227
		return $sorting;
1228
1229
	}
1230
1231
	/**
1232
	 * Override sorting per field
1233
	 *
1234
	 * Currently only modifies sorting ID when sorting by the full name. Sorts by first name.
1235
	 * Use the `gravityview/sorting/full-name` filter to override.
1236
	 *
1237
	 * @todo Filter from GravityView_Field
1238
	 * @since 1.7.4
1239
	 *
1240
	 * @param int|string $sort_field_id Field used for sorting (`id` or `1.2`)
1241
	 * @param int $form_id GF Form ID
1242
	 *
1243
	 * @return string Possibly modified sorting ID
1244
	 */
1245
	private static function _override_sorting_id_by_field_type( $sort_field_id, $form_id ) {
1246
1247
		$form = gravityview_get_form( $form_id );
1248
1249
		$sort_field = GFFormsModel::get_field( $form, $sort_field_id );
1250
1251
		if( ! $sort_field ) {
1252
			return $sort_field_id;
1253
		}
1254
1255
		switch ( $sort_field['type'] ) {
1256
1257
			case 'address':
1258
				// Sorting by full address
1259
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1260
1261
					/**
1262
					 * Override how to sort when sorting address
1263
					 *
1264
					 * @since 1.8
1265
					 *
1266
					 * @param string $address_part `street`, `street2`, `city`, `state`, `zip`, or `country` (default: `city`)
1267
					 * @param string $sort_field_id Field used for sorting
1268
					 * @param int $form_id GF Form ID
1269
					 */
1270
					$address_part = apply_filters( 'gravityview/sorting/address', 'city', $sort_field_id, $form_id );
1271
1272
					switch( strtolower( $address_part ) ){
1273
						case 'street':
1274
							$sort_field_id .= '.1';
1275
							break;
1276
						case 'street2':
1277
							$sort_field_id .= '.2';
1278
							break;
1279
						default:
1280
						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...
1281
							$sort_field_id .= '.3';
1282
							break;
1283
						case 'state':
1284
							$sort_field_id .= '.4';
1285
							break;
1286
						case 'zip':
1287
							$sort_field_id .= '.5';
1288
							break;
1289
						case 'country':
1290
							$sort_field_id .= '.6';
1291
							break;
1292
					}
1293
1294
				}
1295
				break;
1296
			case 'name':
1297
				// Sorting by full name, not first, last, etc.
1298
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1299
					/**
1300
					 * @filter `gravityview/sorting/full-name` Override how to sort when sorting full name.
1301
					 * @since 1.7.4
1302
					 * @param[in,out] string $name_part Sort by `first` or `last` (default: `first`)
1303
					 * @param[in] string $sort_field_id Field used for sorting
1304
					 * @param[in] int $form_id GF Form ID
1305
					 */
1306
					$name_part = apply_filters( 'gravityview/sorting/full-name', 'first', $sort_field_id, $form_id );
1307
1308
					if ( 'last' === strtolower( $name_part ) ) {
1309
						$sort_field_id .= '.6';
1310
					} else {
1311
						$sort_field_id .= '.3';
1312
					}
1313
				}
1314
				break;
1315
			case 'list':
1316
				$sort_field_id = false;
1317
				break;
1318
			case 'time':
1319
1320
				/**
1321
				 * @filter `gravityview/sorting/time` Override how to sort when sorting time
1322
				 * @see GravityView_Field_Time
1323
				 * @since 1.14
1324
				 * @param[in,out] string $name_part Field used for sorting
1325
				 * @param[in] int $form_id GF Form ID
1326
				 */
1327
				$sort_field_id = apply_filters( 'gravityview/sorting/time', $sort_field_id, $form_id );
1328
				break;
1329
		}
1330
1331
		return $sort_field_id;
1332
	}
1333
1334
	/**
1335
	 * Verify if user requested a single entry view
1336
	 * @return boolean|string false if not, single entry slug if true
1337
	 */
1338
	public static function is_single_entry() {
1339
1340
		$var_name = GravityView_Post_Types::get_entry_var_name();
1341
1342
		$single_entry = get_query_var( $var_name );
1343
1344
		/**
1345
		 * Modify the entry that is being displayed.
1346
		 *
1347
		 * @internal Should only be used by things like the oEmbed functionality.
1348
		 * @since 1.6
1349
		 */
1350
		$single_entry = apply_filters( 'gravityview/is_single_entry', $single_entry );
1351
1352
		if ( empty( $single_entry ) ){
1353
			return false;
1354
		} else {
1355
			return $single_entry;
1356
		}
1357
	}
1358
1359
1360
	/**
1361
	 * Register styles and scripts
1362
	 *
1363
	 * @access public
1364
	 * @return void
1365
	 */
1366
	public function add_scripts_and_styles() {
1367
		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...
1368
		// enqueue template specific styles
1369
		if ( $this->getGvOutputData() ) {
1370
1371
			$views = $this->getGvOutputData()->get_views();
1372
1373
			foreach ( $views as $view_id => $data ) {
1374
1375
				/**
1376
				 * Don't enqueue the scripts or styles if it's not going to be displayed.
1377
				 * @since 1.15
1378
				 */
1379
				if( is_user_logged_in() && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
1380
					continue;
1381
				}
1382
1383
				// By default, no thickbox
1384
				$js_dependencies = array( 'jquery', 'gravityview-jquery-cookie' );
1385
				$css_dependencies = array();
1386
1387
				// If the thickbox is enqueued, add dependencies
1388
				if ( ! empty( $data['atts']['lightbox'] ) ) {
1389
1390
					/**
1391
					 * @filter `gravity_view_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1392
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1393
					 */
1394
					$js_dependencies[] = apply_filters( 'gravity_view_lightbox_script', 'thickbox' );
1395
1396
					/**
1397
					 * @filter `gravity_view_lightbox_style` Modify the lightbox CSS slug. Default: `thickbox`
1398
					 * @param string $script_slug If you want to use a different lightbox script, return the name of its CSS file here.
1399
					 */
1400
					$css_dependencies[] = apply_filters( 'gravity_view_lightbox_style', 'thickbox' );
1401
				}
1402
1403
				/**
1404
				 * If the form has checkbox fields, enqueue dashicons
1405
				 * @see https://github.com/katzwebservices/GravityView/issues/536
1406
				 * @since 1.15
1407
				 */
1408
				if( gravityview_view_has_single_checkbox_or_radio( $data['form'], $data['fields'] ) ) {
1409
					$css_dependencies[] = 'dashicons';
1410
				}
1411
1412
				wp_register_script( 'gravityview-jquery-cookie', plugins_url( 'assets/lib/jquery.cookie/jquery.cookie.min.js', GRAVITYVIEW_FILE ), array( 'jquery' ), GravityView_Plugin::version, true );
1413
1414
				$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1415
1416
				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 );
1417
1418
				wp_enqueue_script( 'gravityview-fe-view' );
1419
1420
				if ( ! empty( $data['atts']['sort_columns'] ) ) {
1421
					wp_enqueue_style( 'gravityview_font', plugins_url( 'assets/css/font.css', GRAVITYVIEW_FILE ), $css_dependencies, GravityView_Plugin::version, 'all' );
1422
				}
1423
1424
				$this->enqueue_default_style( $css_dependencies );
1425
1426
				self::add_style( $data['template_id'] );
1427
			}
1428
1429
			if ( 'wp_print_footer_scripts' === current_filter() ) {
1430
1431
				$js_localization = array(
1432
					'cookiepath' => COOKIEPATH,
1433
					'clear' => _x( 'Clear', 'Clear all data from the form', 'gravityview' ),
1434
					'reset' => _x( 'Reset', 'Reset the search form to the state that existed on page load', 'gravityview' ),
1435
				);
1436
1437
				/**
1438
				 * @filter `gravityview_js_localization` Modify the array passed to wp_localize_script()
1439
				 * @param array $js_localization The data padded to the Javascript file
1440
				 * @param array $views Array of View data arrays with View settings
1441
				 */
1442
				$js_localization = apply_filters( 'gravityview_js_localization', $js_localization, $views );
1443
1444
				wp_localize_script( 'gravityview-fe-view', 'gvGlobals', $js_localization );
1445
			}
1446
		}
1447
	}
1448
1449
	/**
1450
	 * Handle enqueuing the `gravityview_default_style` stylesheet
1451
	 *
1452
	 * @since 1.17
1453
	 *
1454
	 * @param array $css_dependencies Dependencies for the `gravityview_default_style` stylesheet
1455
	 *
1456
	 * @return void
1457
	 */
1458
	private function enqueue_default_style( $css_dependencies = array() ) {
1459
1460
		/**
1461
		 * @filter `gravityview_use_legacy_search_css` Should GravityView use the legacy Search Bar stylesheet (from before Version 1.17)?
1462
		 * @since 1.17
1463
		 * @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`
1464
		 */
1465
		$use_legacy_search_style = apply_filters( 'gravityview_use_legacy_search_style', false );
1466
1467
		$rtl = is_rtl() ? '-rtl' : '';
1468
1469
		$css_file_base = $use_legacy_search_style ? 'gv-legacy-search' : 'gv-default-styles';
1470
1471
		$path = gravityview_css_url( $css_file_base . $rtl . '.css' );
1472
1473
		wp_enqueue_style( 'gravityview_default_style', $path, $css_dependencies, GravityView_Plugin::version, 'all' );
1474
	}
1475
1476
	/**
1477
	 * Add template extra style if exists
1478
	 * @param string $template_id
1479
	 */
1480
	public static function add_style( $template_id ) {
1481
1482
		if ( ! empty( $template_id ) && wp_style_is( 'gravityview_style_' . $template_id, 'registered' ) ) {
1483
			do_action( 'gravityview_log_debug', sprintf( '[add_style] Adding extra template style for %s', $template_id ) );
1484
			wp_enqueue_style( 'gravityview_style_' . $template_id );
1485
		} elseif ( empty( $template_id ) ) {
1486
			do_action( 'gravityview_log_error', '[add_style] Cannot add template style; template_id is empty' );
1487
		} else {
1488
			do_action( 'gravityview_log_error', sprintf( '[add_style] Cannot add template style; %s is not registered', 'gravityview_style_'.$template_id ) );
1489
		}
1490
1491
	}
1492
1493
1494
	/**
1495
	 * Inject the sorting links on the table columns
1496
	 *
1497
	 * Callback function for hook 'gravityview/template/field_label'
1498
	 * @see GravityView_API::field_label() (in includes/class-api.php)
1499
	 *
1500
	 * @since 1.7
1501
	 *
1502
	 * @param string $label Field label
1503
	 * @param array $field Field settings
1504
	 *
1505
	 * @return string Field Label
1506
	 */
1507
	public function add_columns_sort_links( $label = '', $field, $form ) {
1508
1509
		/**
1510
		 * Not a table-based template; don't add sort icons
1511
		 * @since 1.12
1512
		 */
1513
		if( ! preg_match( '/table/ism', GravityView_View::getInstance()->getTemplatePartSlug() ) ) {
1514
			return $label;
1515
		}
1516
1517
		if ( ! $this->is_field_sortable( $field['id'], $form ) ) {
1518
			return $label;
1519
		}
1520
1521
		$sorting = GravityView_View::getInstance()->getSorting();
1522
1523
		$class = 'gv-sort';
1524
1525
		$sort_field_id = self::_override_sorting_id_by_field_type( $field['id'], $form['id'] );
1526
1527
		$sort_args = array(
1528
			'sort' => $field['id'],
1529
			'dir' => 'asc',
1530
		);
1531
1532
		if ( ! empty( $sorting['key'] ) && (string) $sort_field_id === (string) $sorting['key'] ) {
1533
			//toggle sorting direction.
1534
			if ( 'asc' === $sorting['direction'] ) {
1535
				$sort_args['dir'] = 'desc';
1536
				$class .= ' gv-icon-sort-desc';
1537
			} else {
1538
				$sort_args['dir'] = 'asc';
1539
				$class .= ' gv-icon-sort-asc';
1540
			}
1541
		} else {
1542
			$class .= ' gv-icon-caret-up-down';
1543
		}
1544
1545
		$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...
1546
1547
		return '<a href="'. esc_url_raw( $url ) .'" class="'. $class .'" ></a>&nbsp;'. $label;
1548
1549
	}
1550
1551
	/**
1552
	 * Checks if field (column) is sortable
1553
	 *
1554
	 * @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...
1555
	 * @param array $form Gravity Forms form array
1556
	 *
1557
	 * @since 1.7
1558
	 *
1559
	 * @return bool True: Yes, field is sortable; False: not sortable
1560
	 */
1561
	public function is_field_sortable( $field_id = '', $form = array() ) {
1562
1563
		$field_type = $field_id;
1564
1565
		if( is_numeric( $field_id ) ) {
1566
			$field = GFFormsModel::get_field( $form, $field_id );
1567
			$field_type = $field->type;
1568
		}
1569
1570
		$not_sortable = array(
1571
			'edit_link',
1572
			'delete_link',
1573
		);
1574
1575
		/**
1576
		 * @filter `gravityview/sortable/field_blacklist` Modify what fields should never be sortable.
1577
		 * @since 1.7
1578
		 * @param[in,out] array $not_sortable Array of field types that aren't sortable
1579
		 * @param string $field_type Field type to check whether the field is sortable
1580
		 * @param array $form Gravity Forms form
1581
		 */
1582
		$not_sortable = apply_filters( 'gravityview/sortable/field_blacklist', $not_sortable, $field_type, $form );
1583
1584
		if ( in_array( $field_type, $not_sortable ) ) {
1585
			return false;
1586
		}
1587
1588
		return apply_filters( "gravityview/sortable/formfield_{$form['id']}_{$field_id}", apply_filters( "gravityview/sortable/field_{$field_id}", true, $form ) );
1589
1590
	}
1591
1592
}
1593
1594
GravityView_frontend::getInstance();
1595
1596
1597
1598