Completed
Push — master ( 5d7bd1...022e44 )
by Zack
04:13
created

GravityView_frontend::is_searching()   C

Complexity

Conditions 8
Paths 11

Size

Total Lines 42
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
rs 5.3846
cc 8
eloc 19
nc 11
nop 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 1456.

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