Completed
Push — master ( 2cfa6f...8927a4 )
by Zack
10:00 queued 06:05
created

GravityView_frontend::is_entry_approved()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 2
nop 2
dl 0
loc 11
rs 9.2
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 1533.

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

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

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

Loading history...
2
/**
3
 * GravityView Frontend functions
4
 *
5
 * @package   GravityView
6
 * @license   GPL2+
7
 * @author    Katz Web Services, Inc.
8
 * @link      http://gravityview.co
9
 * @copyright Copyright 2014, Katz Web Services, Inc.
10
 *
11
 * @since 1.0.0
12
 */
13
14
15
class GravityView_frontend {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
16
17
	/**
18
	 * Regex strings that are used to determine whether the current request is a GravityView search or not.
19
	 * @see GravityView_frontend::is_searching()
20
	 * @since 1.7.4.1
21
	 * @var array
22
	 */
23
	private static $search_parameters = array( 'gv_search', 'gv_start', 'gv_end', 'gv_id', 'gv_by', 'filter_*' );
24
25
	/**
26
	 * Is the currently viewed post a `gravityview` post type?
27
	 * @var boolean
28
	 */
29
	var $is_gravityview_post_type = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_gravityview_post_type.

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

class A {
    var $property;
}

the property is implicitly global.

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

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

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

1. Pass all data via parameters

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

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

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

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

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
771
			$view_entries['entries'][] = $entry;
772
			do_action( 'gravityview_log_debug', '[render_view] Get single entry: ', $view_entries['entries'] );
773
774
			$back_link_label = isset( $atts['back_link_label'] ) ? $atts['back_link_label'] : null;
775
776
			// set back link label
777
			$gravityview_view->setBackLinkLabel( $back_link_label );
778
			$gravityview_view->setContext( 'single' );
779
			$sections = array( 'single' );
780
781
		}
782
783
		// add template style
784
		self::add_style( $view_data['template_id'] );
785
786
		// Prepare to render view and set vars
787
		$gravityview_view->setEntries( $view_entries['entries'] );
788
		$gravityview_view->setTotalEntries( $view_entries['count'] );
789
790
		// If Edit
791
		if ( 'edit' === gravityview_get_context() ) {
792
793
			do_action( 'gravityview_log_debug', '[render_view] Edit Entry ' );
794
795
			do_action( 'gravityview_edit_entry', $this->getGvOutputData() );
796
797
			return ob_get_clean();
798
799
		} else {
800
			// finaly we'll render some html
801
			$sections = apply_filters( 'gravityview_render_view_sections', $sections, $view_data['template_id'] );
802
803
			do_action( 'gravityview_log_debug', '[render_view] Sections to render: ', $sections );
804
			foreach ( $sections as $section ) {
805
				do_action( 'gravityview_log_debug', '[render_view] Rendering '. $section . ' section.' );
806
				$gravityview_view->render( $view_slug, $section, false );
807
			}
808
		}
809
810
		//@todo: check why we need the IF statement vs. print the view id always.
811
		if ( $this->isGravityviewPostType() || $this->isPostHasShortcode() ) {
812
			// Print the View ID to enable proper cookie pagination
813
			echo '<input type="hidden" class="gravityview-view-id" value="' . esc_attr( $view_id ) . '">';
814
		}
815
		$output = ob_get_clean();
816
817
		return $output;
818
	}
819
820
	/**
821
	 * Process the start and end dates for a view - overrides values defined in shortcode (if needed)
822
	 *
823
	 * The `start_date` and `end_date` keys need to be in a format processable by GFFormsModel::get_date_range_where(),
824
	 * which uses \DateTime() format.
825
	 *
826
	 * You can set the `start_date` or `end_date` to any value allowed by {@link http://www.php.net//manual/en/function.strtotime.php strtotime()},
827
	 * including strings like "now" or "-1 year" or "-3 days".
828
	 *
829
	 * @see GFFormsModel::get_date_range_where
830
	 *
831
	 * @param  array      $args            View settings
832
	 * @param  array      $search_criteria Search being performed, if any
833
	 * @return array                       Modified `$search_criteria` array
834
	 */
835
	public static function process_search_dates( $args, $search_criteria = array() ) {
836
837
		$return_search_criteria = $search_criteria;
838
839
		foreach ( array( 'start_date', 'end_date' ) as $key ) {
840
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
841
842
			// Is the start date or end date set in the view or shortcode?
843
			// If so, we want to make sure that the search doesn't go outside the bounds defined.
844
			if ( ! empty( $args[ $key ] ) ) {
845
846
				// Get a timestamp and see if it's a valid date format
847
				$date = strtotime( $args[ $key ] );
848
849
				// The date was invalid
850
				if ( empty( $date ) ) {
851
					do_action( 'gravityview_log_error', __METHOD__ . ' Invalid ' . $key . ' date format: ' . $args[ $key ] );
852
					continue;
853
				}
854
855
				// The format that Gravity Forms expects for start_date and day-specific (not hour/second-specific) end_date
856
				$datetime_format = 'Y-m-d H:i:s';
857
				$search_is_outside_view_bounds = false;
858
859
				if( ! empty( $search_criteria[ $key ] ) ) {
860
861
					$search_date = strtotime( $search_criteria[ $key ] );
862
863
					// The search is for entries before the start date defined by the settings
864
					switch ( $key ) {
865
						case 'end_date':
866
							/**
867
							 * If the end date is formatted as 'Y-m-d', it should be formatted without hours and seconds
868
							 * so that Gravity Forms can convert the day to 23:59:59 the previous day.
869
							 *
870
							 * If it's a relative date ("now" or "-1 day"), then it should use the precise date format
871
							 *
872
							 * @see GFFormsModel::get_date_range_where
873
							 */
874
							$datetime_format               = gravityview_is_valid_datetime( $args[ $key ] ) ? 'Y-m-d' : 'Y-m-d H:i:s';
875
							$search_is_outside_view_bounds = ( $search_date > $date );
876
							break;
877
						case 'start_date':
878
							$search_is_outside_view_bounds = ( $search_date < $date );
879
							break;
880
					}
881
				}
882
883
				// If there is no search being performed, or if there is a search being performed that's outside the bounds
884
				if ( empty( $search_criteria[ $key ] ) || $search_is_outside_view_bounds ) {
885
886
					// Then we override the search and re-set the start date
887
					$return_search_criteria[ $key ] = date_i18n( $datetime_format , $date, true );
888
				}
889
			}
890
		}
891
892
		if( isset( $return_search_criteria['start_date'] ) && isset( $return_search_criteria['end_date'] ) ) {
893
			// The start date is AFTER the end date. This will result in no results, but let's not force the issue.
894
			if ( strtotime( $return_search_criteria['start_date'] ) > strtotime( $return_search_criteria['end_date'] ) ) {
895
				do_action( 'gravityview_log_error', __METHOD__ . ' Invalid search: the start date is after the end date.', $return_search_criteria );
896
			}
897
		}
898
899
		return $return_search_criteria;
900
	}
901
902
903
	/**
904
	 * Process the approved only search criteria according to the View settings
905
	 *
906
	 * @param  array      $args            View settings
907
	 * @param  array      $search_criteria Search being performed, if any
908
	 * @return array                       Modified `$search_criteria` array
909
	 */
910
	public static function process_search_only_approved( $args, $search_criteria ) {
911
912
		if ( ! empty( $args['show_only_approved'] ) ) {
913
914
			$search_criteria['field_filters'][] = array(
915
				'key' => GravityView_Entry_Approval::meta_key,
916
				'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...
917
			);
918
919
			$search_criteria['field_filters']['mode'] = 'all'; // force all the criterias to be met
920
921
			do_action( 'gravityview_log_debug', '[process_search_only_approved] Search Criteria if show only approved: ', $search_criteria );
922
		}
923
924
		return $search_criteria;
925
	}
926
927
928
	/**
929
	 * Check if a certain entry is approved.
930
	 *
931
	 * If we pass the View settings ($args) it will check the 'show_only_approved' setting before
932
	 *   checking the entry approved field, returning true if show_only_approved = false.
933
	 *
934
	 * @since 1.7
935
	 * @since 1.18 Converted check to use GravityView_Entry_Approval_Status::is_approved
936
	 *
937
	 * @uses GravityView_Entry_Approval_Status::is_approved
938
	 *
939
	 * @param array $entry  Entry object
940
	 * @param array $args   View settings (optional)
941
	 *
942
	 * @return bool
943
	 */
944
	public static function is_entry_approved( $entry, $args = array() ) {
945
946
		if ( empty( $entry['id'] ) || ( array_key_exists( 'show_only_approved', $args ) && ! $args['show_only_approved'] ) ) {
947
			// is implicitly approved if entry is null or View settings doesn't require to check for approval
948
			return true;
949
		}
950
951
		$is_approved = gform_get_meta( $entry['id'], GravityView_Entry_Approval::meta_key );
952
953
		return GravityView_Entry_Approval_Status::is_approved( $is_approved );
954
	}
955
956
	/**
957
	 * Parse search criteria for a entries search.
958
	 *
959
	 * array(
960
	 * 	'search_field' => 1, // ID of the field
961
	 *  'search_value' => '', // Value of the field to search
962
	 *  'search_operator' => 'contains', // 'is', 'isnot', '>', '<', 'contains'
963
	 *  'show_only_approved' => 0 or 1 // Boolean
964
	 * )
965
	 *
966
	 * @param  array $args    Array of args
967
	 * @param  int $form_id Gravity Forms form ID
968
	 * @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.
969
	 */
970
	public static function get_search_criteria( $args, $form_id ) {
971
972
		/**
973
		 * @filter `gravityview_fe_search_criteria` Modify the search criteria
974
		 * @see GravityView_Widget_Search::filter_entries Adds the default search criteria
975
		 * @param array $search_criteria Empty `field_filters` key
976
		 * @param int $form_id ID of the Gravity Forms form that is being searched
977
		 */
978
		$search_criteria = apply_filters( 'gravityview_fe_search_criteria', array( 'field_filters' => array() ), $form_id );
979
980
		$original_search_criteria = $search_criteria;
981
982
		do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after hook gravityview_fe_search_criteria: ', $search_criteria );
983
984
		// implicity search
985
		if ( ! empty( $args['search_value'] ) ) {
986
987
			// Search operator options. Options: `is` or `contains`
988
			$operator = ! empty( $args['search_operator'] ) && in_array( $args['search_operator'], array( 'is', 'isnot', '>', '<', 'contains' ) ) ? $args['search_operator'] : 'contains';
989
990
			$search_criteria['field_filters'][] = array(
991
				'key' => rgget( 'search_field', $args ), // The field ID to search
992
				'value' => _wp_specialchars( $args['search_value'] ), // The value to search. Encode ampersands but not quotes.
993
				'operator' => $operator,
994
			);
995
		}
996
997
		if( $search_criteria !== $original_search_criteria ) {
998
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after implicity search: ', $search_criteria );
999
		}
1000
1001
		// Handle setting date range
1002
		$search_criteria = self::process_search_dates( $args, $search_criteria );
1003
1004
		if( $search_criteria !== $original_search_criteria ) {
1005
			do_action( 'gravityview_log_debug', '[get_search_criteria] Search Criteria after date params: ', $search_criteria );
1006
		}
1007
1008
		// remove not approved entries
1009
		$search_criteria = self::process_search_only_approved( $args, $search_criteria );
1010
1011
		/**
1012
		 * @filter `gravityview_status` Modify entry status requirements to be included in search results.
1013
		 * @param string $status Default: `active`. Accepts all Gravity Forms entry statuses, including `spam` and `trash`
1014
		 */
1015
		$search_criteria['status'] = apply_filters( 'gravityview_status', 'active', $args );
1016
1017
		return $search_criteria;
1018
	}
1019
1020
1021
1022
	/**
1023
	 * Core function to calculate View multi entries (directory) based on a set of arguments ($args):
1024
	 *   $id - View id
1025
	 *   $page_size - Page
1026
	 *   $sort_field - form field id to sort
1027
	 *   $sort_direction - ASC / DESC
1028
	 *   $start_date - Ymd
1029
	 *   $end_date - Ymd
1030
	 *   $class - assign a html class to the view
1031
	 *   $offset (optional) - This is the start point in the current data set (0 index based).
1032
	 *
1033
	 *
1034
	 *
1035
	 * @uses  gravityview_get_entries()
1036
	 * @access public
1037
	 * @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...
1038
	 *   - $id - View id
1039
	 *   - $page_size - Page
1040
	 *   - $sort_field - form field id to sort
1041
	 *   - $sort_direction - ASC / DESC
1042
	 *   - $start_date - Ymd
1043
	 *   - $end_date - Ymd
1044
	 *   - $class - assign a html class to the view
1045
	 *   - $offset (optional) - This is the start point in the current data set (0 index based).
1046
	 * @param int $form_id Gravity Forms Form ID
1047
	 * @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
1048
	 */
1049
	public static function get_view_entries( $args, $form_id ) {
1050
1051
		do_action( 'gravityview_log_debug', '[get_view_entries] init' );
1052
		// start filters and sorting
1053
1054
		/**
1055
		 * Process search parameters
1056
		 * @var array
1057
		 */
1058
		$search_criteria = self::get_search_criteria( $args, $form_id );
1059
1060
		// Paging & offset
1061
		$page_size = ! empty( $args['page_size'] ) ? intval( $args['page_size'] ) : apply_filters( 'gravityview_default_page_size', 25 );
1062
1063
		if ( -1 === $page_size ) {
1064
			$page_size = PHP_INT_MAX;
1065
		}
1066
1067
		if ( isset( $args['offset'] ) ) {
1068
			$offset = intval( $args['offset'] );
1069
		} else {
1070
			$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...
1071
			$offset = ( $curr_page - 1 ) * $page_size;
1072
		}
1073
1074
		$paging = array(
1075
			'offset' => $offset,
1076
			'page_size' => $page_size,
1077
		);
1078
1079
		do_action( 'gravityview_log_debug', __METHOD__ . ': Paging: ', $paging );
1080
1081
		// Sorting
1082
		$sorting = self::updateViewSorting( $args, $form_id );
1083
1084
		$parameters = array(
1085
			'search_criteria' => $search_criteria,
1086
			'sorting' => $sorting,
1087
			'paging' => $paging,
1088
			'cache' => isset( $args['cache'] ) ? $args['cache'] : true,
1089
		);
1090
1091
		/**
1092
		 * @filter `gravityview_get_entries` Filter get entries criteria
1093
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1094
		 * @param array $args View configuration args. {
1095
		 *      @type int $id View id
1096
		 *      @type int $page_size Number of entries to show per page
1097
		 *      @type string $sort_field Form field id to sort
1098
		 *      @type string $sort_direction Sorting direction ('ASC' or 'DESC')
1099
		 *      @type string $start_date - Ymd
1100
		 *      @type string $end_date - Ymd
1101
		 *      @type string $class - assign a html class to the view
1102
		 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
1103
		 * }
1104
		 * @param int $form_id ID of Gravity Forms form
1105
		 */
1106
		$parameters = apply_filters( 'gravityview_get_entries', $parameters, $args, $form_id );
1107
1108
		/**
1109
		 * @filter `gravityview_get_entries_{View ID}` Filter get entries criteria
1110
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1111
		 * @param array $args View configuration args.
1112
		 */
1113
		$parameters = apply_filters( 'gravityview_get_entries_'.$args['id'], $parameters, $args, $form_id );
1114
1115
		do_action( 'gravityview_log_debug', __METHOD__ . ': $parameters passed to gravityview_get_entries(): ', $parameters );
1116
1117
		//fetch entries
1118
		$count = 0;
1119
		$entries = gravityview_get_entries( $form_id, $parameters, $count );
1120
1121
		do_action( 'gravityview_log_debug', sprintf( '%s: Get Entries. Found: %s entries', __METHOD__, $count ), $entries );
1122
1123
		/**
1124
		 * @filter `gravityview_view_entries` Filter the entries output to the View
1125
		 * @deprecated since 1.5.2
1126
		 * @param array $args View settings associative array
1127
		 * @var array
1128
		 */
1129
		$entries = apply_filters( 'gravityview_view_entries', $entries, $args );
1130
1131
		/**
1132
		 * @filter `gravityview/view/entries` Filter the entries output to the View
1133
		 * @param array $criteria associative array containing count, entries & paging
1134
		 * @param array $args View settings associative array
1135
		 * @since 1.5.2
1136
		 */
1137
		return apply_filters( 'gravityview/view/entries', compact( 'count', 'entries', 'paging' ), $args );
1138
1139
	}
1140
1141
1142
	/**
1143
	 * Updates the View sorting criteria
1144
	 *
1145
	 * @since 1.7
1146
	 *
1147
	 * @param array $args View settings. Required to have `sort_field` and `sort_direction` keys
1148
	 * @param int $form_id The ID of the form used to sort
1149
	 * @return array $sorting Array with `key`, `direction` and `is_numeric` keys
1150
	 */
1151
	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...
1152
		$sorting = array();
1153
		$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...
1154
		$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...
1155
1156
		$sort_field_id = self::_override_sorting_id_by_field_type( $sort_field_id, $form_id );
1157
1158
		if ( ! empty( $sort_field_id ) ) {
1159
			$sorting = array(
1160
				'key' => $sort_field_id,
1161
				'direction' => strtolower( $sort_direction ),
1162
				'is_numeric' => GVCommon::is_field_numeric( $form_id, $sort_field_id )
1163
			);
1164
		}
1165
1166
		GravityView_View::getInstance()->setSorting( $sorting );
1167
1168
		do_action( 'gravityview_log_debug', '[updateViewSorting] Sort Criteria : ', $sorting );
1169
1170
		return $sorting;
1171
1172
	}
1173
1174
	/**
1175
	 * Override sorting per field
1176
	 *
1177
	 * Currently only modifies sorting ID when sorting by the full name. Sorts by first name.
1178
	 * Use the `gravityview/sorting/full-name` filter to override.
1179
	 *
1180
	 * @todo Filter from GravityView_Field
1181
	 * @since 1.7.4
1182
	 *
1183
	 * @param int|string $sort_field_id Field used for sorting (`id` or `1.2`)
1184
	 * @param int $form_id GF Form ID
1185
	 *
1186
	 * @return string Possibly modified sorting ID
1187
	 */
1188
	private static function _override_sorting_id_by_field_type( $sort_field_id, $form_id ) {
1189
1190
		$form = gravityview_get_form( $form_id );
1191
1192
		$sort_field = GFFormsModel::get_field( $form, $sort_field_id );
1193
1194
		switch ( $sort_field['type'] ) {
1195
1196
			case 'address':
1197
				// Sorting by full address
1198
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1199
1200
					/**
1201
					 * Override how to sort when sorting address
1202
					 *
1203
					 * @since 1.8
1204
					 *
1205
					 * @param string $address_part `street`, `street2`, `city`, `state`, `zip`, or `country` (default: `city`)
1206
					 * @param string $sort_field_id Field used for sorting
1207
					 * @param int $form_id GF Form ID
1208
					 */
1209
					$address_part = apply_filters( 'gravityview/sorting/address', 'city', $sort_field_id, $form_id );
1210
1211
					switch( strtolower( $address_part ) ){
1212
						case 'street':
1213
							$sort_field_id .= '.1';
1214
							break;
1215
						case 'street2':
1216
							$sort_field_id .= '.2';
1217
							break;
1218
						default:
1219
						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...
1220
							$sort_field_id .= '.3';
1221
							break;
1222
						case 'state':
1223
							$sort_field_id .= '.4';
1224
							break;
1225
						case 'zip':
1226
							$sort_field_id .= '.5';
1227
							break;
1228
						case 'country':
1229
							$sort_field_id .= '.6';
1230
							break;
1231
					}
1232
1233
				}
1234
				break;
1235
			case 'name':
1236
				// Sorting by full name, not first, last, etc.
1237
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1238
					/**
1239
					 * @filter `gravityview/sorting/full-name` Override how to sort when sorting full name.
1240
					 * @since 1.7.4
1241
					 * @param[in,out] string $name_part Sort by `first` or `last` (default: `first`)
1242
					 * @param[in] string $sort_field_id Field used for sorting
1243
					 * @param[in] int $form_id GF Form ID
1244
					 */
1245
					$name_part = apply_filters( 'gravityview/sorting/full-name', 'first', $sort_field_id, $form_id );
1246
1247
					if ( 'last' === strtolower( $name_part ) ) {
1248
						$sort_field_id .= '.6';
1249
					} else {
1250
						$sort_field_id .= '.3';
1251
					}
1252
				}
1253
				break;
1254
			case 'list':
1255
				$sort_field_id = false;
1256
				break;
1257
			case 'time':
1258
1259
				/**
1260
				 * @filter `gravityview/sorting/time` Override how to sort when sorting time
1261
				 * @see GravityView_Field_Time
1262
				 * @since 1.14
1263
				 * @param[in,out] string $name_part Field used for sorting
1264
				 * @param[in] int $form_id GF Form ID
1265
				 */
1266
				$sort_field_id = apply_filters( 'gravityview/sorting/time', $sort_field_id, $form_id );
1267
				break;
1268
		}
1269
1270
		return $sort_field_id;
1271
	}
1272
1273
	/**
1274
	 * Verify if user requested a single entry view
1275
	 * @return boolean|string false if not, single entry slug if true
1276
	 */
1277
	public static function is_single_entry() {
1278
1279
		$var_name = GravityView_Post_Types::get_entry_var_name();
1280
1281
		$single_entry = get_query_var( $var_name );
1282
1283
		/**
1284
		 * Modify the entry that is being displayed.
1285
		 *
1286
		 * @internal Should only be used by things like the oEmbed functionality.
1287
		 * @since 1.6
1288
		 */
1289
		$single_entry = apply_filters( 'gravityview/is_single_entry', $single_entry );
1290
1291
		if ( empty( $single_entry ) ){
1292
			return false;
1293
		} else {
1294
			return $single_entry;
1295
		}
1296
	}
1297
1298
1299
	/**
1300
	 * Register styles and scripts
1301
	 *
1302
	 * @access public
1303
	 * @return void
1304
	 */
1305
	public function add_scripts_and_styles() {
1306
		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...
1307
		// enqueue template specific styles
1308
		if ( $this->getGvOutputData() ) {
1309
1310
			$views = $this->getGvOutputData()->get_views();
1311
1312
			foreach ( $views as $view_id => $data ) {
1313
1314
				/**
1315
				 * Don't enqueue the scripts or styles if it's not going to be displayed.
1316
				 * @since 1.15
1317
				 */
1318
				if( is_user_logged_in() && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
1319
					continue;
1320
				}
1321
1322
				// By default, no thickbox
1323
				$js_dependencies = array( 'jquery', 'gravityview-jquery-cookie' );
1324
				$css_dependencies = array();
1325
1326
				// If the thickbox is enqueued, add dependencies
1327
				if ( ! empty( $data['atts']['lightbox'] ) ) {
1328
1329
					/**
1330
					 * @filter `gravity_view_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1331
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1332
					 */
1333
					$js_dependencies[] = apply_filters( 'gravity_view_lightbox_script', 'thickbox' );
1334
1335
					/**
1336
					 * @filter `gravity_view_lightbox_style` Modify the lightbox CSS slug. Default: `thickbox`
1337
					 * @param string $script_slug If you want to use a different lightbox script, return the name of its CSS file here.
1338
					 */
1339
					$css_dependencies[] = apply_filters( 'gravity_view_lightbox_style', 'thickbox' );
1340
				}
1341
1342
				/**
1343
				 * If the form has checkbox fields, enqueue dashicons
1344
				 * @see https://github.com/katzwebservices/GravityView/issues/536
1345
				 * @since 1.15
1346
				 */
1347
				if( gravityview_view_has_single_checkbox_or_radio( $data['form'], $data['fields'] ) ) {
1348
					$css_dependencies[] = 'dashicons';
1349
				}
1350
1351
				wp_register_script( 'gravityview-jquery-cookie', plugins_url( 'assets/lib/jquery.cookie/jquery.cookie.min.js', GRAVITYVIEW_FILE ), array( 'jquery' ), GravityView_Plugin::version, true );
1352
1353
				$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1354
1355
				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 );
1356
1357
				wp_enqueue_script( 'gravityview-fe-view' );
1358
1359
				if ( ! empty( $data['atts']['sort_columns'] ) ) {
1360
					wp_enqueue_style( 'gravityview_font', plugins_url( 'assets/css/font.css', GRAVITYVIEW_FILE ), $css_dependencies, GravityView_Plugin::version, 'all' );
1361
				}
1362
1363
				$this->enqueue_default_style( $css_dependencies );
1364
1365
				self::add_style( $data['template_id'] );
1366
			}
1367
1368
			if ( 'wp_print_footer_scripts' === current_filter() ) {
1369
1370
				$js_localization = array(
1371
					'cookiepath' => COOKIEPATH,
1372
					'clear' => _x( 'Clear', 'Clear all data from the form', 'gravityview' ),
1373
					'reset' => _x( 'Reset', 'Reset the search form to the state that existed on page load', 'gravityview' ),
1374
				);
1375
1376
				/**
1377
				 * @filter `gravityview_js_localization` Modify the array passed to wp_localize_script()
1378
				 * @param array $js_localization The data padded to the Javascript file
1379
				 * @param array $views Array of View data arrays with View settings
1380
				 */
1381
				$js_localization = apply_filters( 'gravityview_js_localization', $js_localization, $views );
1382
1383
				wp_localize_script( 'gravityview-fe-view', 'gvGlobals', $js_localization );
1384
			}
1385
		}
1386
	}
1387
1388
	/**
1389
	 * Handle enqueuing the `gravityview_default_style` stylesheet
1390
	 *
1391
	 * @since 1.17
1392
	 *
1393
	 * @param array $css_dependencies Dependencies for the `gravityview_default_style` stylesheet
1394
	 *
1395
	 * @return void
1396
	 */
1397
	private function enqueue_default_style( $css_dependencies = array() ) {
1398
1399
		/**
1400
		 * @filter `gravityview_use_legacy_search_css` Should GravityView use the legacy Search Bar stylesheet (from before Version 1.17)?
1401
		 * @since 1.17
1402
		 * @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`
1403
		 */
1404
		$use_legacy_search_style = apply_filters( 'gravityview_use_legacy_search_style', false );
1405
1406
		$rtl = is_rtl() ? '-rtl' : '';
1407
1408
		$css_file_base = $use_legacy_search_style ? 'gv-legacy-search' : 'gv-default-styles';
1409
1410
		$path = gravityview_css_url( $css_file_base . $rtl . '.css' );
1411
1412
		wp_enqueue_style( 'gravityview_default_style', $path, $css_dependencies, GravityView_Plugin::version, 'all' );
1413
	}
1414
1415
	/**
1416
	 * Add template extra style if exists
1417
	 * @param string $template_id
1418
	 */
1419
	public static function add_style( $template_id ) {
1420
1421
		if ( ! empty( $template_id ) && wp_style_is( 'gravityview_style_' . $template_id, 'registered' ) ) {
1422
			do_action( 'gravityview_log_debug', sprintf( '[add_style] Adding extra template style for %s', $template_id ) );
1423
			wp_enqueue_style( 'gravityview_style_' . $template_id );
1424
		} elseif ( empty( $template_id ) ) {
1425
			do_action( 'gravityview_log_error', '[add_style] Cannot add template style; template_id is empty' );
1426
		} else {
1427
			do_action( 'gravityview_log_error', sprintf( '[add_style] Cannot add template style; %s is not registered', 'gravityview_style_'.$template_id ) );
1428
		}
1429
1430
	}
1431
1432
1433
	/**
1434
	 * Inject the sorting links on the table columns
1435
	 *
1436
	 * Callback function for hook 'gravityview/template/field_label'
1437
	 * @see GravityView_API::field_label() (in includes/class-api.php)
1438
	 *
1439
	 * @since 1.7
1440
	 *
1441
	 * @param string $label Field label
1442
	 * @param array $field Field settings
1443
	 *
1444
	 * @return string Field Label
1445
	 */
1446
	public function add_columns_sort_links( $label = '', $field, $form ) {
1447
1448
		/**
1449
		 * Not a table-based template; don't add sort icons
1450
		 * @since 1.12
1451
		 */
1452
		if( ! preg_match( '/table/ism', GravityView_View::getInstance()->getTemplatePartSlug() ) ) {
1453
			return $label;
1454
		}
1455
1456
		if ( ! $this->is_field_sortable( $field['id'], $form ) ) {
1457
			return $label;
1458
		}
1459
1460
		$sorting = GravityView_View::getInstance()->getSorting();
1461
1462
		$class = 'gv-sort';
1463
1464
		$sort_field_id = self::_override_sorting_id_by_field_type( $field['id'], $form['id'] );
1465
1466
		$sort_args = array(
1467
			'sort' => $field['id'],
1468
			'dir' => 'asc',
1469
		);
1470
1471
		if ( ! empty( $sorting['key'] ) && (string) $sort_field_id === (string) $sorting['key'] ) {
1472
			//toggle sorting direction.
1473
			if ( 'asc' === $sorting['direction'] ) {
1474
				$sort_args['dir'] = 'desc';
1475
				$class .= ' gv-icon-sort-desc';
1476
			} else {
1477
				$sort_args['dir'] = 'asc';
1478
				$class .= ' gv-icon-sort-asc';
1479
			}
1480
		} else {
1481
			$class .= ' gv-icon-caret-up-down';
1482
		}
1483
1484
		$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...
1485
1486
		return '<a href="'. esc_url_raw( $url ) .'" class="'. $class .'" ></a>&nbsp;'. $label;
1487
1488
	}
1489
1490
	/**
1491
	 * Checks if field (column) is sortable
1492
	 *
1493
	 * @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...
1494
	 * @param array $form Gravity Forms form array
1495
	 *
1496
	 * @since 1.7
1497
	 *
1498
	 * @return bool True: Yes, field is sortable; False: not sortable
1499
	 */
1500
	public function is_field_sortable( $field_id = '', $form = array() ) {
1501
1502
		$field_type = $field_id;
1503
1504
		if( is_numeric( $field_id ) ) {
1505
			$field = GFFormsModel::get_field( $form, $field_id );
1506
			$field_type = $field->type;
1507
		}
1508
1509
		$not_sortable = array(
1510
			'edit_link',
1511
			'delete_link',
1512
		);
1513
1514
		/**
1515
		 * @filter `gravityview/sortable/field_blacklist` Modify what fields should never be sortable.
1516
		 * @since 1.7
1517
		 * @param[in,out] array $not_sortable Array of field types that aren't sortable
1518
		 * @param string $field_type Field type to check whether the field is sortable
1519
		 * @param array $form Gravity Forms form
1520
		 */
1521
		$not_sortable = apply_filters( 'gravityview/sortable/field_blacklist', $not_sortable, $field_type, $form );
1522
1523
		if ( in_array( $field_type, $not_sortable ) ) {
1524
			return false;
1525
		}
1526
1527
		return apply_filters( "gravityview/sortable/formfield_{$form['id']}_{$field_id}", apply_filters( "gravityview/sortable/field_{$field_id}", true, $form ) );
1528
1529
	}
1530
1531
}
1532
1533
GravityView_frontend::getInstance();
1534
1535
1536
1537