Completed
Push — master ( 92cdcd...79386d )
by Zack
20:40 queued 10:41
created

GravityView_frontend::isPostHasShortcode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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 1527.

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