Completed
Push — develop ( 507546...65eb42 )
by Zack
05:37
created

GravityView_frontend::parse_content()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 15
nc 10
nop 1
dl 0
loc 29
rs 5.3846
c 0
b 0
f 0
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 1637.

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 ( function_exists( 'gravityview' ) && gravityview()->request->is_admin() ) {
342
			return;
343
			/** Deprecated in favor of gravityview()->request->is_admin(). */
344
		} else if ( GravityView_Plugin::is_admin() ) {
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_Plugin::is_admin() has been deprecated.

This method has been deprecated.

Loading history...
345
			return;
346
		}
347
348
		// Calculate requested Views
349
		$this->setGvOutputData( GravityView_View_Data::getInstance( $post ) );
350
351
		// !important: we need to run this before getting single entry (to kick the advanced filter)
352
		$this->set_context_view_id();
353
354
		$this->setIsGravityviewPostType( get_post_type( $post ) === 'gravityview' );
355
356
		$post_id = $this->getPostId() ? $this->getPostId() : (isset( $post ) ? $post->ID : null );
357
		$this->setPostId( $post_id );
358
		$post_has_shortcode = ! empty( $post->post_content ) ? gravityview_has_shortcode_r( $post->post_content, 'gravityview' ) : false;
359
		$this->setPostHasShortcode( $this->isGravityviewPostType() ? null : ! empty( $post_has_shortcode ) );
360
361
		// check if the View is showing search results (only for multiple entries View)
362
		$this->setIsSearch( $this->is_searching() );
363
364
		unset( $entry, $post_id, $post_has_shortcode );
365
	}
366
367
	/**
368
	 * Set the entry
369
	 */
370
	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...
371
		$entry_id = self::is_single_entry();
372
		$this->setSingleEntry( $entry_id );
373
		$this->setEntry( $entry_id );
374
	}
375
376
	/**
377
	 * Checks if the current View is presenting search results
378
	 *
379
	 * @since 1.5.4
380
	 *
381
	 * @return boolean True: Yes, it's a search; False: No, not a search.
382
	 */
383
	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...
384
385
		// It's a single entry, not search
386
		if ( $this->getSingleEntry() ) {
387
			return false;
388
		}
389
390
		$search_method = GravityView_Widget_Search::getInstance()->get_search_method();
391
392
		if( 'post' === $search_method ) {
393
			$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...
394
		} else {
395
			$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...
396
		}
397
398
		// No $_GET parameters
399
		if ( empty( $get ) || ! is_array( $get ) ) {
400
			return false;
401
		}
402
403
		// Remove empty values
404
		$get = array_filter( $get );
405
406
		// If the $_GET parameters are empty, it's no search.
407
		if ( empty( $get ) ) {
408
			return false;
409
		}
410
411
		$search_keys = array_keys( $get );
412
413
		$search_match = implode( '|', self::$search_parameters );
414
415
		foreach ( $search_keys as $search_key ) {
416
417
			// Analyze the search key $_GET parameter and see if it matches known GV args
418
			if ( preg_match( '/(' . $search_match . ')/i', $search_key ) ) {
419
				return true;
420
			}
421
		}
422
423
		return false;
424
	}
425
426
	/**
427
	 * Filter the title for the single entry view
428
	 *
429
	 * @param  string $title   current title
430
	 * @param  int $passed_post_id Post ID
431
	 * @return string          (modified) title
432
	 */
433
	public function single_entry_title( $title, $passed_post_id = null ) {
434
		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...
435
436
		// If this is the directory view, return.
437
		if ( ! $this->getSingleEntry() ) {
438
			return $title;
439
		}
440
441
		$entry = $this->getEntry();
442
443
		/**
444
		 * @filter `gravityview/single/title/out_loop` Apply the Single Entry Title filter outside the WordPress loop?
445
		 * @param boolean $in_the_loop Whether to apply the filter to the menu title and the meta tag <title> - outside the loop
446
		 * @param array $entry Current entry
447
		 */
448
		$apply_outside_loop = apply_filters( 'gravityview/single/title/out_loop' , in_the_loop(), $entry );
449
450
		if ( ! $apply_outside_loop ) {
451
			return $title;
452
		}
453
454
		// User reported WooCommerce doesn't pass two args.
455
		if ( empty( $passed_post_id ) )  {
456
			return $title;
457
		}
458
459
		// Don't modify the title for anything other than the current view/post.
460
		// This is true for embedded shortcodes and Views.
461
		if ( is_object( $post ) && (int) $post->ID !== (int) $passed_post_id ) {
462
			return $title;
463
		}
464
465
		$context_view_id = $this->get_context_view_id();
466
467
		if ( $this->getGvOutputData()->has_multiple_views() && ! empty( $context_view_id ) ) {
468
			$view_meta = $this->getGvOutputData()->get_view( $context_view_id );
469
		} else {
470
			foreach ( $this->getGvOutputData()->get_views() as $view_id => $view_data ) {
471
				if ( intval( $view_data['form_id'] ) === intval( $entry['form_id'] ) ) {
472
					$view_meta = $view_data;
473
					break;
474
				}
475
			}
476
		}
477
478
		if ( ! empty( $view_meta['atts']['single_title'] ) ) {
479
480
			$title = $view_meta['atts']['single_title'];
481
482
			// We are allowing HTML in the fields, so no escaping the output
483
			$title = GravityView_API::replace_variables( $title, $view_meta['form'], $entry );
484
485
			$title = do_shortcode( $title );
486
		}
487
488
		return $title;
489
	}
490
491
492
	/**
493
	 * In case View post is called directly, insert the view in the post content
494
	 *
495
	 * @access public
496
	 * @static
497
	 * @param mixed $content
498
	 * @return string Add the View output into View CPT content
499
	 */
500
	public function insert_view_in_content( $content ) {
501
502
		// Plugins may run through the content in the header. WP SEO does this for its OpenGraph functionality.
503
		if ( ! did_action( 'loop_start' ) ) {
504
505
			do_action( 'gravityview_log_debug', '[insert_view_in_content] Not processing yet: loop_start hasn\'t run yet. Current action:', current_filter() );
506
507
			return $content;
508
		}
509
510
		//	We don't want this filter to run infinite loop on any post content fields
511
		remove_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
512
513
		// Otherwise, this is called on the Views page when in Excerpt mode.
514
		if ( is_admin() ) {
515
			return $content;
516
		}
517
518
		// Only render in the loop. Fixes issues with the_content filter being applied in places like the sidebar
519
		if( ! in_the_loop() ) {
520
			return $content;
521
		}
522
523
		if ( $this->isGravityviewPostType() ) {
524
525
			/** @since 1.7.4 */
526
			if ( is_preview() && ! gravityview_get_form_id( $this->post_id ) ) {
527
				$content .= __( 'When using a Start Fresh template, you must save the View before a Preview is available.', 'gravityview' );
528
			} else {
529
				foreach ( $this->getGvOutputData()->get_views() as $view_id => $data ) {
530
					$content .= $this->render_view( array( 'id' => $view_id ) );
531
				}
532
			}
533
		}
534
535
		//	Add the filter back in
536
		add_filter( 'the_content', array( $this, 'insert_view_in_content' ) );
537
538
		return $content;
539
	}
540
541
	/**
542
	 * Disable comments on GravityView post types
543
	 * @param  boolean $open    existing status
544
	 * @param  int $post_id Post ID
545
	 * @return boolean
546
	 */
547
	public function comments_open( $open, $post_id ) {
548
549
		if ( $this->isGravityviewPostType() ) {
550
			$open = false;
551
		}
552
553
		/**
554
		 * @filter `gravityview/comments_open` Whether to set comments to open or closed.
555
		 * @since  1.5.4
556
		 * @param  boolean $open Open or closed status
557
		 * @param  int $post_id Post ID to set comment status for
558
		 */
559
		$open = apply_filters( 'gravityview/comments_open', $open, $post_id );
560
561
		return $open;
562
	}
563
564
	/**
565
	 * Display a warning when a View has not been configured
566
	 *
567
	 * @since 1.19.2
568
	 *
569
	 * @param int $view_id The ID of the View currently being displayed
570
	 *
571
	 * @return void
572
	 */
573
	public function context_not_configured_warning( $view_id = 0 ) {
574
575
		if ( ! class_exists( 'GravityView_View' ) ) {
576
			return;
577
		}
578
579
		$fields = GravityView_View::getInstance()->getContextFields();
580
581
		if ( ! empty( $fields ) ) {
582
			return;
583
		}
584
585
		$context = GravityView_View::getInstance()->getContext();
586
587
		switch( $context ) {
588
			case 'directory':
589
				$tab = __( 'Multiple Entries', 'gravityview' );
590
				break;
591
			case 'edit':
592
				$tab = __( 'Edit Entry', 'gravityview' );
593
				break;
594
			case 'single':
595
			default:
596
				$tab = __( 'Single Entry', 'gravityview' );
597
				break;
598
		}
599
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
600
601
		$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...
602
		$edit_link = admin_url( sprintf( 'post.php?post=%d&action=edit#%s-view', $view_id, $context ) );
603
		$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...
604
		$message = esc_html__( 'You can only see this message because you are able to edit this View.', 'gravityview' );
605
606
		$output = sprintf( '<h3>%s <strong><a href="%s">%s</a></strong></h3><p>%s</p>', $title, esc_url( $edit_link ), $action_text, $message );
607
608
		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...
609
	}
610
611
612
	/**
613
	 * Core function to render a View based on a set of arguments
614
	 *
615
	 * @access public
616
	 * @static
617
	 * @param array $passed_args {
618
	 *
619
	 *      Settings for rendering the View
620
	 *
621
	 *      @type int $id View id
622
	 *      @type int $page_size Number of entries to show per page
623
	 *      @type string $sort_field Form field id to sort
624
	 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
625
	 *      @type string $start_date - Ymd
626
	 *      @type string $end_date - Ymd
627
	 *      @type string $class - assign a html class to the view
628
	 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
629
	 * }
630
	 *
631
	 * @return string|null HTML output of a View, NULL if View isn't found
632
	 */
633
	public function render_view( $passed_args ) {
634
635
		// validate attributes
636
		if ( empty( $passed_args['id'] ) ) {
637
			do_action( 'gravityview_log_error', '[render_view] Returning; no ID defined.', $passed_args );
638
			return null;
639
		}
640
641
		// Solve problem when loading content via admin-ajax.php
642
		// @hack
643
		if ( ! $this->getGvOutputData() ) {
644
645
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not defined; parsing content.', $passed_args );
646
647
			$this->parse_content();
648
		}
649
650
		// Make 100% sure that we're dealing with a properly called situation
651
		if ( ! is_object( $this->getGvOutputData() ) || ! is_callable( array( $this->getGvOutputData(), 'get_view' ) ) ) {
652
653
			do_action( 'gravityview_log_error', '[render_view] gv_output_data not an object or get_view not callable.', $this->getGvOutputData() );
654
655
			return null;
656
		}
657
658
		$view_id = $passed_args['id'];
659
660
		$view_data = $this->getGvOutputData()->get_view( $view_id, $passed_args );
661
662
		do_action( 'gravityview_log_debug', '[render_view] View Data: ', $view_data );
663
664
		do_action( 'gravityview_log_debug', '[render_view] Init View. Arguments: ', $passed_args );
665
666
		// The passed args were always winning, even if they were NULL.
667
		// This prevents that. Filters NULL, FALSE, and empty strings.
668
		$passed_args = array_filter( $passed_args, 'strlen' );
669
670
		//Override shortcode args over View template settings
671
		$atts = wp_parse_args( $passed_args, $view_data['atts'] );
672
673
		do_action( 'gravityview_log_debug', '[render_view] Arguments after merging with View settings: ', $atts );
674
675
		// It's password protected and you need to log in.
676
		if ( post_password_required( $view_id ) ) {
677
678
			do_action( 'gravityview_log_error', sprintf( '[render_view] Returning: View %d is password protected.', $view_id ) );
679
680
			// If we're in an embed or on an archive page, show the password form
681
			if ( get_the_ID() !== $view_id ) {
682
				return get_the_password_form();
683
			}
684
685
			// Otherwise, just get outta here
686
			return null;
687
		}
688
689
		/**
690
		 * Don't render View if user isn't allowed to see it
691
		 * @since 1.15
692
		 * @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.
693
		 */
694
		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 ) ) {
695
696
			do_action( 'gravityview_log_debug', sprintf( '%s Returning: View %d is not visible by current user.', __METHOD__, $view_id ) );
697
698
			return null;
699
		}
700
701
		if( $this->isGravityviewPostType() ) {
702
703
			/**
704
			 * @filter `gravityview_direct_access` Should Views be directly accessible, or only visible using the shortcode?
705
			 * @see https://codex.wordpress.org/Function_Reference/register_post_type#public
706
			 * @see \GV\Entry::get_endpoint_name
707
			 * @since 1.15.2
708
			 * @param[in,out] boolean `true`: allow Views to be accessible directly. `false`: Only allow Views to be embedded via shortcode. Default: `true`
709
			 * @param int $view_id The ID of the View currently being requested. `0` for general setting
710
			 */
711
			$direct_access = apply_filters( 'gravityview_direct_access', true, $view_id );
712
713
			$embed_only = ! empty( $atts['embed_only'] );
714
715
			if( ! $direct_access || ( $embed_only && ! GVCommon::has_cap( 'read_private_gravityviews' ) ) ) {
716
				return __( 'You are not allowed to view this content.', 'gravityview' );
717
			}
718
		}
719
720
		ob_start();
721
722
		/**
723
		 * Set globals for templating
724
		 * @deprecated 1.6.2
725
		 */
726
		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...
727
728
		$gravityview_view = new GravityView_View( $view_data );
729
730
		$post_id = ! empty( $atts['post_id'] ) ? intval( $atts['post_id'] ) : $this->getPostId();
731
732
		$gravityview_view->setPostId( $post_id );
733
734
		if ( ! $this->getSingleEntry() ) {
735
736
			// user requested Directory View
737
			do_action( 'gravityview_log_debug', '[render_view] Executing Directory View' );
738
739
			//fetch template and slug
740
			$view_slug = apply_filters( 'gravityview_template_slug_'. $view_data['template_id'], 'table', 'directory' );
741
742
			do_action( 'gravityview_log_debug', '[render_view] View template slug: ', $view_slug );
743
744
			/**
745
			 * Disable fetching initial entries for views that don't need it (DataTables)
746
			 */
747
			$get_entries = apply_filters( 'gravityview_get_view_entries_'.$view_slug, true );
748
749
			/**
750
			 * Hide View data until search is performed
751
			 * @since 1.5.4
752
			 */
753
			if ( ! empty( $atts['hide_until_searched'] ) && ! $this->isSearch() ) {
754
				$gravityview_view->setHideUntilSearched( true );
755
				$get_entries = false;
756
			}
757
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
758
759
			if ( $get_entries ) {
760
761
				if ( ! empty( $atts['sort_columns'] ) ) {
762
					// add filter to enable column sorting
763
					add_filter( 'gravityview/template/field_label', array( $this, 'add_columns_sort_links' ) , 100, 3 );
764
				}
765
766
				$view_entries = self::get_view_entries( $atts, $view_data['form_id'] );
767
768
				do_action( 'gravityview_log_debug', sprintf( '[render_view] Get Entries. Found %s entries total, showing %d entries', $view_entries['count'], sizeof( $view_entries['entries'] ) ) );
769
770
			} else {
771
772
				$view_entries = array( 'count' => null, 'entries' => null, 'paging' => null );
773
774
				do_action( 'gravityview_log_debug', '[render_view] Not fetching entries because `gravityview_get_view_entries_'.$view_slug.'` is false' );
775
			}
776
777
			$gravityview_view->setPaging( $view_entries['paging'] );
778
			$gravityview_view->setContext( 'directory' );
779
			$sections = array( 'header', 'body', 'footer' );
780
781
		} else {
782
783
			// user requested Single Entry View
784
			do_action( 'gravityview_log_debug', '[render_view] Executing Single View' );
785
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
786
787
			/**
788
			 * @action `gravityview_render_entry_{View ID}` Before rendering a single entry for a specific View ID
789
			 * @since 1.17
790
			 */
791
			do_action( 'gravityview_render_entry_'.$view_data['id'] );
792
793
			$entry = $this->getEntry();
794
795
			// You are not permitted to view this entry.
796
			if ( empty( $entry ) || ! self::is_entry_approved( $entry, $atts ) ) {
797
798
				do_action( 'gravityview_log_debug', '[render_view] Entry does not exist. This may be because of View filters limiting access.' );
799
800
				// Only display warning once when multiple Views are embedded
801
				if( $view_id !== (int) GravityView_frontend::get_context_view_id() ) {
802
					ob_end_clean();
803
					return null;
804
				}
805
806
				/**
807
				 * @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.
808
				 * @since 1.6
809
				 * @param string $message Default: "You have attempted to view an entry that is not visible or may not exist."
810
				 */
811
				$message = apply_filters( 'gravityview/render/entry/not_visible', __( 'You have attempted to view an entry that is not visible or may not exist.', 'gravityview' ) );
812
813
				/**
814
				 * @since 1.6
815
				 */
816
				echo esc_attr( $message );
817
818
				ob_end_clean();
819
				return null;
820
			}
821
822
			// We're in single view, but the view being processed is not the same view the single entry belongs to.
823
			// important: do not remove this as it prevents fake attempts of displaying entries from other views/forms
824
			if ( $this->getGvOutputData()->has_multiple_views() && $view_id != $this->get_context_view_id() ) {
825
				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 );
826
				ob_end_clean();
827
				return null;
828
			}
829
830
			//fetch template and slug
831
			$view_slug = apply_filters( 'gravityview_template_slug_' . $view_data['template_id'], 'table', 'single' );
832
			do_action( 'gravityview_log_debug', '[render_view] View single template slug: ', $view_slug );
833
834
			//fetch entry detail
835
			$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...
836
			$view_entries['entries'][] = $entry;
837
			do_action( 'gravityview_log_debug', '[render_view] Get single entry: ', $view_entries['entries'] );
838
839
			$back_link_label = isset( $atts['back_link_label'] ) ? $atts['back_link_label'] : null;
840
841
			// set back link label
842
			$gravityview_view->setBackLinkLabel( $back_link_label );
843
			$gravityview_view->setContext( 'single' );
844
			$sections = array( 'single' );
845
846
		}
847
848
		// add template style
849
		self::add_style( $view_data['template_id'] );
850
851
		// Prepare to render view and set vars
852
		$gravityview_view->setEntries( $view_entries['entries'] );
853
		$gravityview_view->setTotalEntries( $view_entries['count'] );
854
855
		// If Edit
856
		if ( 'edit' === gravityview_get_context() ) {
857
858
			do_action( 'gravityview_log_debug', '[render_view] Edit Entry ' );
859
860
			do_action( 'gravityview_edit_entry', $this->getGvOutputData() );
861
862
			return ob_get_clean();
863
864
		} else {
865
			// finaly we'll render some html
866
			$sections = apply_filters( 'gravityview_render_view_sections', $sections, $view_data['template_id'] );
867
868
			do_action( 'gravityview_log_debug', '[render_view] Sections to render: ', $sections );
869
			foreach ( $sections as $section ) {
870
				do_action( 'gravityview_log_debug', '[render_view] Rendering '. $section . ' section.' );
871
				$gravityview_view->render( $view_slug, $section, false );
872
			}
873
		}
874
875
		//@todo: check why we need the IF statement vs. print the view id always.
876
		if ( $this->isGravityviewPostType() || $this->isPostHasShortcode() ) {
877
			// Print the View ID to enable proper cookie pagination
878
			echo '<input type="hidden" class="gravityview-view-id" value="' . esc_attr( $view_id ) . '">';
879
		}
880
		$output = ob_get_clean();
881
882
		return $output;
883
	}
884
885
	/**
886
	 * Process the start and end dates for a view - overrides values defined in shortcode (if needed)
887
	 *
888
	 * The `start_date` and `end_date` keys need to be in a format processable by GFFormsModel::get_date_range_where(),
889
	 * which uses \DateTime() format.
890
	 *
891
	 * 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()},
892
	 * including strings like "now" or "-1 year" or "-3 days".
893
	 *
894
	 * @see GFFormsModel::get_date_range_where
895
	 *
896
	 * @param  array      $args            View settings
897
	 * @param  array      $search_criteria Search being performed, if any
898
	 * @return array                       Modified `$search_criteria` array
899
	 */
900
	public static function process_search_dates( $args, $search_criteria = array() ) {
901
902
		$return_search_criteria = $search_criteria;
903
904
		foreach ( array( 'start_date', 'end_date' ) as $key ) {
905
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
906
907
			// Is the start date or end date set in the view or shortcode?
908
			// If so, we want to make sure that the search doesn't go outside the bounds defined.
909
			if ( ! empty( $args[ $key ] ) ) {
910
911
				// Get a timestamp and see if it's a valid date format
912
				$date = strtotime( $args[ $key ] );
913
914
				// The date was invalid
915
				if ( empty( $date ) ) {
916
					do_action( 'gravityview_log_error', __METHOD__ . ' Invalid ' . $key . ' date format: ' . $args[ $key ] );
917
					continue;
918
				}
919
920
				// The format that Gravity Forms expects for start_date and day-specific (not hour/second-specific) end_date
921
				$datetime_format = 'Y-m-d H:i:s';
922
				$search_is_outside_view_bounds = false;
923
924
				if( ! empty( $search_criteria[ $key ] ) ) {
925
926
					$search_date = strtotime( $search_criteria[ $key ] );
927
928
					// The search is for entries before the start date defined by the settings
929
					switch ( $key ) {
930
						case 'end_date':
931
							/**
932
							 * If the end date is formatted as 'Y-m-d', it should be formatted without hours and seconds
933
							 * so that Gravity Forms can convert the day to 23:59:59 the previous day.
934
							 *
935
							 * If it's a relative date ("now" or "-1 day"), then it should use the precise date format
936
							 *
937
							 * @see GFFormsModel::get_date_range_where
938
							 */
939
							$datetime_format               = gravityview_is_valid_datetime( $args[ $key ] ) ? 'Y-m-d' : 'Y-m-d H:i:s';
940
							$search_is_outside_view_bounds = ( $search_date > $date );
941
							break;
942
						case 'start_date':
943
							$search_is_outside_view_bounds = ( $search_date < $date );
944
							break;
945
					}
946
				}
947
948
				// If there is no search being performed, or if there is a search being performed that's outside the bounds
949
				if ( empty( $search_criteria[ $key ] ) || $search_is_outside_view_bounds ) {
950
951
					// Then we override the search and re-set the start date
952
					$return_search_criteria[ $key ] = date_i18n( $datetime_format , $date, true );
953
				}
954
			}
955
		}
956
957
		if( isset( $return_search_criteria['start_date'] ) && isset( $return_search_criteria['end_date'] ) ) {
958
			// The start date is AFTER the end date. This will result in no results, but let's not force the issue.
959
			if ( strtotime( $return_search_criteria['start_date'] ) > strtotime( $return_search_criteria['end_date'] ) ) {
960
				do_action( 'gravityview_log_error', __METHOD__ . ' Invalid search: the start date is after the end date.', $return_search_criteria );
961
			}
962
		}
963
964
		return $return_search_criteria;
965
	}
966
967
968
	/**
969
	 * Process the approved only search criteria according to the View settings
970
	 *
971
	 * @param  array      $args            View settings
972
	 * @param  array      $search_criteria Search being performed, if any
973
	 * @return array                       Modified `$search_criteria` array
974
	 */
975
	public static function process_search_only_approved( $args, $search_criteria ) {
976
977
		/** @since 1.19 */
978
		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...
979
			do_action( 'gravityview_log_debug', __METHOD__ . ': User can moderate entries; showing all approval statuses' );
980
			return $search_criteria;
981
		}
982
983
		if ( ! empty( $args['show_only_approved'] ) ) {
984
985
			$search_criteria['field_filters'][] = array(
986
				'key' => GravityView_Entry_Approval::meta_key,
987
				'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...
988
			);
989
990
			$search_criteria['field_filters']['mode'] = 'all'; // force all the criterias to be met
991
992
			do_action( 'gravityview_log_debug', '[process_search_only_approved] Search Criteria if show only approved: ', $search_criteria );
993
		}
994
995
		return $search_criteria;
996
	}
997
998
999
	/**
1000
	 * Check if a certain entry is approved.
1001
	 *
1002
	 * If we pass the View settings ($args) it will check the 'show_only_approved' setting before
1003
	 *   checking the entry approved field, returning true if show_only_approved = false.
1004
	 *
1005
	 * @since 1.7
1006
	 * @since 1.18 Converted check to use GravityView_Entry_Approval_Status::is_approved
1007
	 *
1008
	 * @uses GravityView_Entry_Approval_Status::is_approved
1009
	 *
1010
	 * @param array $entry  Entry object
1011
	 * @param array $args   View settings (optional)
1012
	 *
1013
	 * @return bool
1014
	 */
1015
	public static function is_entry_approved( $entry, $args = array() ) {
1016
1017
		if ( empty( $entry['id'] ) || ( array_key_exists( 'show_only_approved', $args ) && ! $args['show_only_approved'] ) ) {
1018
			// is implicitly approved if entry is null or View settings doesn't require to check for approval
1019
			return true;
1020
		}
1021
1022
		/** @since 1.19 */
1023
		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...
1024
			do_action( 'gravityview_log_debug', __METHOD__ . ': User can moderate entries, so entry is approved for viewing' );
1025
			return true;
1026
		}
1027
1028
		$is_approved = gform_get_meta( $entry['id'], GravityView_Entry_Approval::meta_key );
1029
1030
		return GravityView_Entry_Approval_Status::is_approved( $is_approved );
1031
	}
1032
1033
	/**
1034
	 * Parse search criteria for a entries search.
1035
	 *
1036
	 * array(
1037
	 * 	'search_field' => 1, // ID of the field
1038
	 *  'search_value' => '', // Value of the field to search
1039
	 *  'search_operator' => 'contains', // 'is', 'isnot', '>', '<', 'contains'
1040
	 *  'show_only_approved' => 0 or 1 // Boolean
1041
	 * )
1042
	 *
1043
	 * @param  array $args    Array of args
1044
	 * @param  int $form_id Gravity Forms form ID
1045
	 * @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.
1046
	 */
1047
	public static function get_search_criteria( $args, $form_id ) {
1048
1049
		/**
1050
		 * @filter `gravityview_fe_search_criteria` Modify the search criteria
1051
		 * @see GravityView_Widget_Search::filter_entries Adds the default search criteria
1052
		 * @param array $search_criteria Empty `field_filters` key
1053
		 * @param int $form_id ID of the Gravity Forms form that is being searched
1054
		 */
1055
		$search_criteria = apply_filters( 'gravityview_fe_search_criteria', array( 'field_filters' => array() ), $form_id );
1056
1057
		$original_search_criteria = $search_criteria;
1058
1059
		do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after hook gravityview_fe_search_criteria: ', $search_criteria );
1060
1061
		// implicity search
1062
		if ( ! empty( $args['search_value'] ) ) {
1063
1064
			// Search operator options. Options: `is` or `contains`
1065
			$operator = ! empty( $args['search_operator'] ) && in_array( $args['search_operator'], array( 'is', 'isnot', '>', '<', 'contains' ) ) ? $args['search_operator'] : 'contains';
1066
1067
			$search_criteria['field_filters'][] = array(
1068
				'key' => rgget( 'search_field', $args ), // The field ID to search
1069
				'value' => _wp_specialchars( $args['search_value'] ), // The value to search. Encode ampersands but not quotes.
1070
				'operator' => $operator,
1071
			);
1072
		}
1073
1074
		if( $search_criteria !== $original_search_criteria ) {
1075
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after implicity search: ', $search_criteria );
1076
		}
1077
1078
		// Handle setting date range
1079
		$search_criteria = self::process_search_dates( $args, $search_criteria );
1080
1081
		if( $search_criteria !== $original_search_criteria ) {
1082
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after date params: ', $search_criteria );
1083
		}
1084
1085
		// remove not approved entries
1086
		$search_criteria = self::process_search_only_approved( $args, $search_criteria );
1087
1088
		/**
1089
		 * @filter `gravityview_status` Modify entry status requirements to be included in search results.
1090
		 * @param string $status Default: `active`. Accepts all Gravity Forms entry statuses, including `spam` and `trash`
1091
		 */
1092
		$search_criteria['status'] = apply_filters( 'gravityview_status', 'active', $args );
1093
1094
		return $search_criteria;
1095
	}
1096
1097
1098
1099
	/**
1100
	 * Core function to calculate View multi entries (directory) based on a set of arguments ($args):
1101
	 *   $id - View id
1102
	 *   $page_size - Page
1103
	 *   $sort_field - form field id to sort
1104
	 *   $sort_direction - ASC / DESC
1105
	 *   $start_date - Ymd
1106
	 *   $end_date - Ymd
1107
	 *   $class - assign a html class to the view
1108
	 *   $offset (optional) - This is the start point in the current data set (0 index based).
1109
	 *
1110
	 *
1111
	 *
1112
	 * @uses  gravityview_get_entries()
1113
	 * @access public
1114
	 * @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...
1115
	 *   - $id - View id
1116
	 *   - $page_size - Page
1117
	 *   - $sort_field - form field id to sort
1118
	 *   - $sort_direction - ASC / DESC
1119
	 *   - $start_date - Ymd
1120
	 *   - $end_date - Ymd
1121
	 *   - $class - assign a html class to the view
1122
	 *   - $offset (optional) - This is the start point in the current data set (0 index based).
1123
	 * @param int $form_id Gravity Forms Form ID
1124
	 * @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
1125
	 */
1126
	public static function get_view_entries( $args, $form_id ) {
1127
1128
		do_action( 'gravityview_log_debug', '[get_view_entries] init' );
1129
		// start filters and sorting
1130
1131
		/**
1132
		 * Process search parameters
1133
		 * @var array
1134
		 */
1135
		$search_criteria = self::get_search_criteria( $args, $form_id );
1136
1137
		$paging = self::get_search_criteria_paging( $args );
1138
1139
		$parameters = array(
1140
			'search_criteria' => $search_criteria,
1141
			'sorting' => self::updateViewSorting( $args, $form_id ),
1142
			'paging' => $paging,
1143
			'cache' => isset( $args['cache'] ) ? $args['cache'] : true,
1144
		);
1145
1146
		/**
1147
		 * @filter `gravityview_get_entries` Filter get entries criteria
1148
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1149
		 * @param array $args View configuration args. {
1150
		 *      @type int $id View id
1151
		 *      @type int $page_size Number of entries to show per page
1152
		 *      @type string $sort_field Form field id to sort
1153
		 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
1154
		 *      @type string $start_date - Ymd
1155
		 *      @type string $end_date - Ymd
1156
		 *      @type string $class - assign a html class to the view
1157
		 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
1158
		 * }
1159
		 * @param int $form_id ID of Gravity Forms form
1160
		 */
1161
		$parameters = apply_filters( 'gravityview_get_entries', $parameters, $args, $form_id );
1162
1163
		/**
1164
		 * @filter `gravityview_get_entries_{View ID}` Filter get entries criteria
1165
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1166
		 * @param array $args View configuration args.
1167
		 */
1168
		$parameters = apply_filters( 'gravityview_get_entries_'.$args['id'], $parameters, $args, $form_id );
1169
1170
		do_action( 'gravityview_log_debug', __METHOD__ . ': $parameters passed to gravityview_get_entries(): ', $parameters );
1171
1172
		//fetch entries
1173
		$count = 0;
1174
		$entries = gravityview_get_entries( $form_id, $parameters, $count );
1175
1176
		do_action( 'gravityview_log_debug', sprintf( '%s: Get Entries. Found: %s entries', __METHOD__, $count ), $entries );
1177
1178
		/**
1179
		 * @filter `gravityview_view_entries` Filter the entries output to the View
1180
		 * @deprecated since 1.5.2
1181
		 * @param array $args View settings associative array
1182
		 * @var array
1183
		 */
1184
		$entries = apply_filters( 'gravityview_view_entries', $entries, $args );
1185
1186
		/**
1187
		 * @filter `gravityview/view/entries` Filter the entries output to the View
1188
		 * @param array $criteria associative array containing count, entries & paging
1189
		 * @param array $args View settings associative array
1190
		 * @since 1.5.2
1191
		 */
1192
		return apply_filters( 'gravityview/view/entries', compact( 'count', 'entries', 'paging' ), $args );
1193
1194
	}
1195
1196
	/**
1197
	 * Get the paging array for the View
1198
	 *
1199
	 * @since 1.19.5
1200
	 *
1201
	 * @param $args
1202
	 * @param int $form_id
0 ignored issues
show
Bug introduced by
There is no parameter named $form_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1203
	 */
1204
	public static function get_search_criteria_paging( $args ) {
1205
1206
		/**
1207
		 * @filter `gravityview_default_page_size` The default number of entries displayed in a View
1208
		 * @since 1.1.6
1209
		 * @param int $default_page_size Default: 25
1210
		 */
1211
		$default_page_size = apply_filters( 'gravityview_default_page_size', 25 );
1212
1213
		// Paging & offset
1214
		$page_size = ! empty( $args['page_size'] ) ? intval( $args['page_size'] ) : $default_page_size;
1215
1216
		if ( -1 === $page_size ) {
1217
			$page_size = PHP_INT_MAX;
1218
		}
1219
1220
		if ( isset( $args['offset'] ) ) {
1221
			$offset = intval( $args['offset'] );
1222
		} else {
1223
			$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...
1224
			$offset = ( $curr_page - 1 ) * $page_size;
1225
		}
1226
1227
		$paging = array(
1228
			'offset' => $offset,
1229
			'page_size' => $page_size,
1230
		);
1231
1232
		do_action( 'gravityview_log_debug', __METHOD__ . ': Paging: ', $paging );
1233
1234
		return $paging;
1235
	}
1236
1237
	/**
1238
	 * Updates the View sorting criteria
1239
	 *
1240
	 * @since 1.7
1241
	 *
1242
	 * @param array $args View settings. Required to have `sort_field` and `sort_direction` keys
1243
	 * @param int $form_id The ID of the form used to sort
1244
	 * @return array $sorting Array with `key`, `direction` and `is_numeric` keys
1245
	 */
1246
	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...
1247
		$sorting = array();
1248
		$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...
1249
		$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...
1250
1251
		$sort_field_id = self::_override_sorting_id_by_field_type( $sort_field_id, $form_id );
1252
1253
		if ( ! empty( $sort_field_id ) ) {
1254
			$sorting = array(
1255
				'key' => $sort_field_id,
1256
				'direction' => strtolower( $sort_direction ),
1257
				'is_numeric' => GVCommon::is_field_numeric( $form_id, $sort_field_id )
1258
			);
1259
		}
1260
1261
		GravityView_View::getInstance()->setSorting( $sorting );
1262
1263
		do_action( 'gravityview_log_debug', '[updateViewSorting] Sort Criteria : ', $sorting );
1264
1265
		return $sorting;
1266
1267
	}
1268
1269
	/**
1270
	 * Override sorting per field
1271
	 *
1272
	 * Currently only modifies sorting ID when sorting by the full name. Sorts by first name.
1273
	 * Use the `gravityview/sorting/full-name` filter to override.
1274
	 *
1275
	 * @todo Filter from GravityView_Field
1276
	 * @since 1.7.4
1277
	 *
1278
	 * @param int|string $sort_field_id Field used for sorting (`id` or `1.2`)
1279
	 * @param int $form_id GF Form ID
1280
	 *
1281
	 * @return string Possibly modified sorting ID
1282
	 */
1283
	private static function _override_sorting_id_by_field_type( $sort_field_id, $form_id ) {
1284
1285
		$form = gravityview_get_form( $form_id );
1286
1287
		$sort_field = GFFormsModel::get_field( $form, $sort_field_id );
1288
1289
		if( ! $sort_field ) {
1290
			return $sort_field_id;
1291
		}
1292
1293
		switch ( $sort_field['type'] ) {
1294
1295
			case 'address':
1296
				// Sorting by full address
1297
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1298
1299
					/**
1300
					 * Override how to sort when sorting address
1301
					 *
1302
					 * @since 1.8
1303
					 *
1304
					 * @param string $address_part `street`, `street2`, `city`, `state`, `zip`, or `country` (default: `city`)
1305
					 * @param string $sort_field_id Field used for sorting
1306
					 * @param int $form_id GF Form ID
1307
					 */
1308
					$address_part = apply_filters( 'gravityview/sorting/address', 'city', $sort_field_id, $form_id );
1309
1310
					switch( strtolower( $address_part ) ){
1311
						case 'street':
1312
							$sort_field_id .= '.1';
1313
							break;
1314
						case 'street2':
1315
							$sort_field_id .= '.2';
1316
							break;
1317
						default:
1318
						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...
1319
							$sort_field_id .= '.3';
1320
							break;
1321
						case 'state':
1322
							$sort_field_id .= '.4';
1323
							break;
1324
						case 'zip':
1325
							$sort_field_id .= '.5';
1326
							break;
1327
						case 'country':
1328
							$sort_field_id .= '.6';
1329
							break;
1330
					}
1331
1332
				}
1333
				break;
1334
			case 'name':
1335
				// Sorting by full name, not first, last, etc.
1336
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1337
					/**
1338
					 * @filter `gravityview/sorting/full-name` Override how to sort when sorting full name.
1339
					 * @since 1.7.4
1340
					 * @param[in,out] string $name_part Sort by `first` or `last` (default: `first`)
1341
					 * @param[in] string $sort_field_id Field used for sorting
1342
					 * @param[in] int $form_id GF Form ID
1343
					 */
1344
					$name_part = apply_filters( 'gravityview/sorting/full-name', 'first', $sort_field_id, $form_id );
1345
1346
					if ( 'last' === strtolower( $name_part ) ) {
1347
						$sort_field_id .= '.6';
1348
					} else {
1349
						$sort_field_id .= '.3';
1350
					}
1351
				}
1352
				break;
1353
			case 'list':
1354
				$sort_field_id = false;
1355
				break;
1356
			case 'time':
1357
1358
				/**
1359
				 * @filter `gravityview/sorting/time` Override how to sort when sorting time
1360
				 * @see GravityView_Field_Time
1361
				 * @since 1.14
1362
				 * @param[in,out] string $name_part Field used for sorting
1363
				 * @param[in] int $form_id GF Form ID
1364
				 */
1365
				$sort_field_id = apply_filters( 'gravityview/sorting/time', $sort_field_id, $form_id );
1366
				break;
1367
		}
1368
1369
		return $sort_field_id;
1370
	}
1371
1372
	/**
1373
	 * Verify if user requested a single entry view
1374
	 * @return boolean|string false if not, single entry slug if true
1375
	 */
1376
	public static function is_single_entry() {
1377
1378
		if ( function_exists( 'gravityview' ) ) {
1379
			$var_name = \GV\Entry::get_endpoint_name();
1380
		} else {
1381
			/** Deprecated. Use \GV\Entry::get_endpoint_name instead. */
1382
			$var_name = GravityView_Post_Types::get_entry_var_name();
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_Post_Types::get_entry_var_name() has been deprecated.

This method has been deprecated.

Loading history...
1383
		}
1384
1385
		$single_entry = get_query_var( $var_name );
1386
1387
		/**
1388
		 * Modify the entry that is being displayed.
1389
		 *
1390
		 * @internal Should only be used by things like the oEmbed functionality.
1391
		 * @since 1.6
1392
		 */
1393
		$single_entry = apply_filters( 'gravityview/is_single_entry', $single_entry );
1394
1395
		if ( empty( $single_entry ) ){
1396
			return false;
1397
		} else {
1398
			return $single_entry;
1399
		}
1400
	}
1401
1402
1403
	/**
1404
	 * Register styles and scripts
1405
	 *
1406
	 * @access public
1407
	 * @return void
1408
	 */
1409
	public function add_scripts_and_styles() {
1410
		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...
1411
		// enqueue template specific styles
1412
		if ( $this->getGvOutputData() ) {
1413
1414
			$views = $this->getGvOutputData()->get_views();
1415
1416
			foreach ( $views as $view_id => $data ) {
1417
1418
				/**
1419
				 * Don't enqueue the scripts or styles if it's not going to be displayed.
1420
				 * @since 1.15
1421
				 */
1422
				if( is_user_logged_in() && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
1423
					continue;
1424
				}
1425
1426
				// By default, no thickbox
1427
				$js_dependencies = array( 'jquery', 'gravityview-jquery-cookie' );
1428
				$css_dependencies = array();
1429
1430
				// If the thickbox is enqueued, add dependencies
1431
				if ( ! empty( $data['atts']['lightbox'] ) ) {
1432
1433
					/**
1434
					 * @filter `gravity_view_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1435
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1436
					 */
1437
					$js_dependencies[] = apply_filters( 'gravity_view_lightbox_script', 'thickbox' );
1438
1439
					/**
1440
					 * @filter `gravity_view_lightbox_style` Modify the lightbox CSS slug. Default: `thickbox`
1441
					 * @param string $script_slug If you want to use a different lightbox script, return the name of its CSS file here.
1442
					 */
1443
					$css_dependencies[] = apply_filters( 'gravity_view_lightbox_style', 'thickbox' );
1444
				}
1445
1446
				/**
1447
				 * If the form has checkbox fields, enqueue dashicons
1448
				 * @see https://github.com/katzwebservices/GravityView/issues/536
1449
				 * @since 1.15
1450
				 */
1451
				if( gravityview_view_has_single_checkbox_or_radio( $data['form'], $data['fields'] ) ) {
1452
					$css_dependencies[] = 'dashicons';
1453
				}
1454
1455
				wp_register_script( 'gravityview-jquery-cookie', plugins_url( 'assets/lib/jquery.cookie/jquery.cookie.min.js', GRAVITYVIEW_FILE ), array( 'jquery' ), GravityView_Plugin::version, true );
1456
1457
				$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1458
1459
				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 );
1460
1461
				wp_enqueue_script( 'gravityview-fe-view' );
1462
1463
				if ( ! empty( $data['atts']['sort_columns'] ) ) {
1464
					wp_enqueue_style( 'gravityview_font', plugins_url( 'assets/css/font.css', GRAVITYVIEW_FILE ), $css_dependencies, GravityView_Plugin::version, 'all' );
1465
				}
1466
1467
				$this->enqueue_default_style( $css_dependencies );
1468
1469
				self::add_style( $data['template_id'] );
1470
			}
1471
1472
			if ( 'wp_print_footer_scripts' === current_filter() ) {
1473
1474
				$js_localization = array(
1475
					'cookiepath' => COOKIEPATH,
1476
					'clear' => _x( 'Clear', 'Clear all data from the form', 'gravityview' ),
1477
					'reset' => _x( 'Reset', 'Reset the search form to the state that existed on page load', 'gravityview' ),
1478
				);
1479
1480
				/**
1481
				 * @filter `gravityview_js_localization` Modify the array passed to wp_localize_script()
1482
				 * @param array $js_localization The data padded to the Javascript file
1483
				 * @param array $views Array of View data arrays with View settings
1484
				 */
1485
				$js_localization = apply_filters( 'gravityview_js_localization', $js_localization, $views );
1486
1487
				wp_localize_script( 'gravityview-fe-view', 'gvGlobals', $js_localization );
1488
			}
1489
		}
1490
	}
1491
1492
	/**
1493
	 * Handle enqueuing the `gravityview_default_style` stylesheet
1494
	 *
1495
	 * @since 1.17
1496
	 *
1497
	 * @param array $css_dependencies Dependencies for the `gravityview_default_style` stylesheet
1498
	 *
1499
	 * @return void
1500
	 */
1501
	private function enqueue_default_style( $css_dependencies = array() ) {
1502
1503
		/**
1504
		 * @filter `gravityview_use_legacy_search_css` Should GravityView use the legacy Search Bar stylesheet (from before Version 1.17)?
1505
		 * @since 1.17
1506
		 * @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`
1507
		 */
1508
		$use_legacy_search_style = apply_filters( 'gravityview_use_legacy_search_style', false );
1509
1510
		$rtl = is_rtl() ? '-rtl' : '';
1511
1512
		$css_file_base = $use_legacy_search_style ? 'gv-legacy-search' : 'gv-default-styles';
1513
1514
		$path = gravityview_css_url( $css_file_base . $rtl . '.css' );
1515
1516
		wp_enqueue_style( 'gravityview_default_style', $path, $css_dependencies, GravityView_Plugin::version, 'all' );
1517
	}
1518
1519
	/**
1520
	 * Add template extra style if exists
1521
	 * @param string $template_id
1522
	 */
1523
	public static function add_style( $template_id ) {
1524
1525
		if ( ! empty( $template_id ) && wp_style_is( 'gravityview_style_' . $template_id, 'registered' ) ) {
1526
			do_action( 'gravityview_log_debug', sprintf( '[add_style] Adding extra template style for %s', $template_id ) );
1527
			wp_enqueue_style( 'gravityview_style_' . $template_id );
1528
		} elseif ( empty( $template_id ) ) {
1529
			do_action( 'gravityview_log_error', '[add_style] Cannot add template style; template_id is empty' );
1530
		} else {
1531
			do_action( 'gravityview_log_error', sprintf( '[add_style] Cannot add template style; %s is not registered', 'gravityview_style_'.$template_id ) );
1532
		}
1533
1534
	}
1535
1536
1537
	/**
1538
	 * Inject the sorting links on the table columns
1539
	 *
1540
	 * Callback function for hook 'gravityview/template/field_label'
1541
	 * @see GravityView_API::field_label() (in includes/class-api.php)
1542
	 *
1543
	 * @since 1.7
1544
	 *
1545
	 * @param string $label Field label
1546
	 * @param array $field Field settings
1547
	 *
1548
	 * @return string Field Label
1549
	 */
1550
	public function add_columns_sort_links( $label = '', $field, $form ) {
1551
1552
		/**
1553
		 * Not a table-based template; don't add sort icons
1554
		 * @since 1.12
1555
		 */
1556
		if( ! preg_match( '/table/ism', GravityView_View::getInstance()->getTemplatePartSlug() ) ) {
1557
			return $label;
1558
		}
1559
1560
		if ( ! $this->is_field_sortable( $field['id'], $form ) ) {
1561
			return $label;
1562
		}
1563
1564
		$sorting = GravityView_View::getInstance()->getSorting();
1565
1566
		$class = 'gv-sort';
1567
1568
		$sort_field_id = self::_override_sorting_id_by_field_type( $field['id'], $form['id'] );
1569
1570
		$sort_args = array(
1571
			'sort' => $field['id'],
1572
			'dir' => 'asc',
1573
		);
1574
1575
		if ( ! empty( $sorting['key'] ) && (string) $sort_field_id === (string) $sorting['key'] ) {
1576
			//toggle sorting direction.
1577
			if ( 'asc' === $sorting['direction'] ) {
1578
				$sort_args['dir'] = 'desc';
1579
				$class .= ' gv-icon-sort-desc';
1580
			} else {
1581
				$sort_args['dir'] = 'asc';
1582
				$class .= ' gv-icon-sort-asc';
1583
			}
1584
		} else {
1585
			$class .= ' gv-icon-caret-up-down';
1586
		}
1587
1588
		$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...
1589
1590
		return '<a href="'. esc_url_raw( $url ) .'" class="'. $class .'" ></a>&nbsp;'. $label;
1591
1592
	}
1593
1594
	/**
1595
	 * Checks if field (column) is sortable
1596
	 *
1597
	 * @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...
1598
	 * @param array $form Gravity Forms form array
1599
	 *
1600
	 * @since 1.7
1601
	 *
1602
	 * @return bool True: Yes, field is sortable; False: not sortable
1603
	 */
1604
	public function is_field_sortable( $field_id = '', $form = array() ) {
1605
1606
		$field_type = $field_id;
1607
1608
		if( is_numeric( $field_id ) ) {
1609
			$field = GFFormsModel::get_field( $form, $field_id );
1610
			$field_type = $field->type;
1611
		}
1612
1613
		$not_sortable = array(
1614
			'edit_link',
1615
			'delete_link',
1616
		);
1617
1618
		/**
1619
		 * @filter `gravityview/sortable/field_blacklist` Modify what fields should never be sortable.
1620
		 * @since 1.7
1621
		 * @param[in,out] array $not_sortable Array of field types that aren't sortable
1622
		 * @param string $field_type Field type to check whether the field is sortable
1623
		 * @param array $form Gravity Forms form
1624
		 */
1625
		$not_sortable = apply_filters( 'gravityview/sortable/field_blacklist', $not_sortable, $field_type, $form );
1626
1627
		if ( in_array( $field_type, $not_sortable ) ) {
1628
			return false;
1629
		}
1630
1631
		return apply_filters( "gravityview/sortable/formfield_{$form['id']}_{$field_id}", apply_filters( "gravityview/sortable/field_{$field_id}", true, $form ) );
1632
1633
	}
1634
1635
}
1636
1637
GravityView_frontend::getInstance();
1638
1639
1640
1641