Completed
Pull Request — develop (#1487)
by Gennady
07:54
created

GravityView_frontend   F

Complexity

Total Complexity 191

Size/Duplication

Total Lines 1584
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 11

Test Coverage

Coverage 64.21%

Importance

Changes 0
Metric Value
dl 0
loc 1584
ccs 323
cts 503
cp 0.6421
rs 0.8
c 0
b 0
f 0
wmc 191
lcom 3
cbo 11

44 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A initialize() 0 16 1
A getInstance() 0 9 2
A getGvOutputData() 0 3 1
A setGvOutputData() 0 3 1
A isSearch() 0 3 1
A setIsSearch() 0 3 1
A getSingleEntry() 0 3 1
A setSingleEntry() 0 5 1
A getEntry() 0 3 1
A setEntry() 0 8 2
A getPostId() 0 3 1
A setPostId() 0 3 1
A isPostHasShortcode() 0 3 1
A setPostHasShortcode() 0 3 1
A isGravityviewPostType() 0 3 1
A setIsGravityviewPostType() 0 3 1
B set_context_view_id() 0 21 6
A get_context_view_id() 0 3 1
C parse_query_fix_frontpage() 0 54 14
A set_entry_data() 0 5 1
B parse_content() 0 26 6
B is_searching() 0 42 8
C single_entry_title() 0 70 11
A _get_single_entry_title() 0 36 4
A insert_view_in_content() 0 4 1
A comments_open() 0 16 2
B context_not_configured_warning() 0 38 6
A render_view() 0 25 5
C process_search_dates() 0 66 13
A process_search_only_approved() 0 22 4
A is_entry_approved() 0 17 6
B get_search_criteria() 0 62 8
A get_view_entries() 0 37 1
B get_view_entries_parameters() 0 55 4
F updateViewSorting() 0 79 16
A is_single_entry() 0 29 3
D add_scripts_and_styles() 0 118 13
A enqueue_default_style() 0 17 3
A add_style() 0 12 4
B add_columns_sort_links() 0 43 6
A is_field_sortable() 0 30 4
A get_search_criteria_paging() 0 32 5
D _override_sorting_id_by_field_type() 0 96 17

How to fix   Complexity   

Complex Class

Complex classes like GravityView_frontend often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GravityView_frontend, and based on these observations, apply Extract Interface, too.

1
<?php
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;
30
31
	/**
32
	 * Does the current post have a `[gravityview]` shortcode?
33
	 * @var boolean
34
	 */
35
	var $post_has_shortcode = false;
36
37
	/**
38
	 * The Post ID of the currently viewed post. Not necessarily GV
39
	 * @var int
40
	 */
41
	var $post_id = null;
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;
49
50
	/**
51
	 * If we are viewing a single entry, the entry data
52
	 * @var array|false
53
	 */
54
	var $entry = false;
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;
61
62
	/**
63
	 * The View is showing search results
64
	 * @since 1.5.4
65
	 * @var boolean
66
	 */
67
	var $is_search = false;
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;
76
77
	/**
78
	 * @var GravityView_frontend
79
	 */
80
	static $instance;
81
82
	/**
83
	 * Class constructor, enforce Singleton pattern
84
	 */
85
	private function __construct() {}
86
87 39
	private function initialize() {
88 39
		add_action( 'wp', array( $this, 'parse_content'), 11 );
89 39
		add_filter( 'parse_query', array( $this, 'parse_query_fix_frontpage' ), 10 );
90 39
		add_action( 'template_redirect', array( $this, 'set_entry_data'), 1 );
91
92
		// Enqueue scripts and styles after GravityView_Template::register_styles()
93 39
		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 39
		add_action( 'wp_print_footer_scripts', array( $this, 'add_scripts_and_styles' ), 1 );
97
98 39
		add_filter( 'the_title', array( $this, 'single_entry_title' ), 1, 2 );
99 39
		add_filter( 'comments_open', array( $this, 'comments_open' ), 10, 2 );
100
101 39
		add_action( 'gravityview_after', array( $this, 'context_not_configured_warning' ) );
102 39
	}
103
104
	/**
105
	 * Get the one true instantiated self
106
	 * @return GravityView_frontend
107
	 */
108 42
	public static function getInstance() {
109
110 42
		if ( empty( self::$instance ) ) {
111 39
			self::$instance = new self;
112 39
			self::$instance->initialize();
113
		}
114
115 42
		return self::$instance;
116
	}
117
118
	/**
119
	 * @return GravityView_View_Data
120
	 */
121 40
	public function getGvOutputData() {
122 40
		return $this->gv_output_data;
123
	}
124
125
	/**
126
	 * @param \GravityView_View_Data $gv_output_data
127
	 */
128 40
	public function setGvOutputData( $gv_output_data ) {
129 40
		$this->gv_output_data = $gv_output_data;
130 40
	}
131
132
	/**
133
	 * @return boolean
134
	 */
135 39
	public function isSearch() {
136 39
		return $this->is_search;
137
	}
138
139
	/**
140
	 * @param boolean $is_search
141
	 */
142 40
	public function setIsSearch( $is_search ) {
143 40
		$this->is_search = $is_search;
144 40
	}
145
146
	/**
147
	 * @return bool|int
148
	 */
149 41
	public function getSingleEntry() {
150 41
		return $this->single_entry;
151
	}
152
153
	/**
154
	 * Sets the single entry ID and also the entry
155
	 * @param bool|int|string $single_entry
156
	 */
157 39
	public function setSingleEntry( $single_entry ) {
158
159 39
		$this->single_entry = $single_entry;
160
161 39
	}
162
163
	/**
164
	 * @return array
165
	 */
166 39
	public function getEntry() {
167 39
		return $this->entry;
168
	}
169
170
	/**
171
	 * Set the current entry
172
	 * @param array|int $entry Entry array or entry slug or ID
173
	 */
174 39
	public function setEntry( $entry ) {
175
176 39
		if ( ! is_array( $entry ) ) {
177 30
			$entry = GVCommon::get_entry( $entry );
178
		}
179
180 39
		$this->entry = $entry;
181 39
	}
182
183
	/**
184
	 * @return int
185
	 */
186 40
	public function getPostId() {
187 40
		return $this->post_id;
188
	}
189
190
	/**
191
	 * @param int $post_id
192
	 */
193 40
	public function setPostId( $post_id ) {
194 40
		$this->post_id = $post_id;
195 40
	}
196
197
	/**
198
	 * @return boolean
199
	 */
200 41
	public function isPostHasShortcode() {
201 41
		return $this->post_has_shortcode;
202
	}
203
204
	/**
205
	 * @param boolean $post_has_shortcode
206
	 */
207 40
	public function setPostHasShortcode( $post_has_shortcode ) {
208 40
		$this->post_has_shortcode = $post_has_shortcode;
209 40
	}
210
211
	/**
212
	 * @return boolean
213
	 */
214 42
	public function isGravityviewPostType() {
215 42
		return $this->is_gravityview_post_type;
216
	}
217
218
	/**
219
	 * @param boolean $is_gravityview_post_type
220
	 */
221 40
	public function setIsGravityviewPostType( $is_gravityview_post_type ) {
222 40
		$this->is_gravityview_post_type = $is_gravityview_post_type;
223 40
	}
224
225
	/**
226
	 * Set the context view ID used when page contains multiple embedded views or displaying the single entry view
227
	 *
228
	 *
229
	 *
230
	 * @param null $view_id
231
	 */
232 3
	public function set_context_view_id( $view_id = null ) {
233 3
		$multiple_views = $this->getGvOutputData() && $this->getGvOutputData()->has_multiple_views();
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::has_multiple_views() has been deprecated.

This method has been deprecated.

Loading history...
234
235 3
		if ( ! empty( $view_id ) ) {
236
237 1
			$this->context_view_id = (int) $view_id;
238
239 3
		} elseif ( isset( $_GET['gvid'] ) && $multiple_views ) {
240
			/**
241
			 * used on a has_multiple_views context
242
			 * @see GravityView_API::entry_link
243
			 */
244
			$this->context_view_id = (int) $_GET['gvid'];
245
246 3
		} elseif ( ! $multiple_views ) {
247 3
			$array_keys = array_keys( $this->getGvOutputData()->get_views() );
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::get_views() has been deprecated.

This method has been deprecated.

Loading history...
248 3
			$this->context_view_id = (int) array_pop( $array_keys );
249 3
			unset( $array_keys );
250
		}
251
252 3
	}
253
254
	/**
255
	 * Returns the the view_id context when page contains multiple embedded views or displaying single entry view
256
	 *
257
	 * @since 1.5.4
258
	 *
259
	 * @return int|null
260
	 */
261 39
	public function get_context_view_id() {
262 39
		return $this->context_view_id;
263
	}
264
265
	/**
266
	 * Allow GravityView entry endpoints on the front page of a site
267
	 *
268
	 * @link  https://core.trac.wordpress.org/ticket/23867 Fixes this core issue
269
	 * @link https://wordpress.org/plugins/cpt-on-front-page/ Code is based on this
270
	 *
271
	 * @since 1.17.3
272
	 *
273
	 * @param WP_Query &$query (passed by reference)
274
	 *
275
	 * @return void
276
	 */
277 188
	public function parse_query_fix_frontpage( &$query ) {
278 188
		global $wp_rewrite;
279
280 188
		$is_front_page = ( $query->is_home || $query->is_page );
281 188
		$show_on_front = ( 'page' === get_option('show_on_front') );
282 188
		$front_page_id = get_option('page_on_front');
283
284 188
		if (  $is_front_page && $show_on_front && $front_page_id ) {
285
286
			// Force to be an array, potentially a query string ( entry=16 )
287
			$_query = wp_parse_args( $query->query );
288
289
			// pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
290
			if ( isset( $_query['pagename'] ) && '' === $_query['pagename'] ) {
291
				unset( $_query['pagename'] );
292
			}
293
294
			// this is where will break from core wordpress
295
			/** @internal Don't use this filter; it will be unnecessary soon - it's just a patch for specific use case */
296
			$ignore = apply_filters( 'gravityview/internal/ignored_endpoints', array( 'preview', 'page', 'paged', 'cpage' ), $query );
297
			$endpoints = \GV\Utils::get( $wp_rewrite, 'endpoints' );
298
			foreach ( (array) $endpoints as $endpoint ) {
299
				$ignore[] = $endpoint[1];
300
			}
301
			unset( $endpoints );
302
303
			// Modify the query if:
304
			// - We're on the "Page on front" page (which we are), and:
305
			// - The query is empty OR
306
			// - The query includes keys that are associated with registered endpoints. `entry`, for example.
307
			if ( empty( $_query ) || ! array_diff( array_keys( $_query ), $ignore ) ) {
308
309
				$qv =& $query->query_vars;
310
311
				// Prevent redirect when on the single entry endpoint
312
				if( self::is_single_entry() ) {
313
					add_filter( 'redirect_canonical', '__return_false' );
314
				}
315
316
				$query->is_page = true;
317
				$query->is_home = false;
318
				$qv['page_id']  = $front_page_id;
319
320
				// Correct <!--nextpage--> for page_on_front
321
				if ( ! empty( $qv['paged'] ) ) {
322
					$qv['page'] = $qv['paged'];
323
					unset( $qv['paged'] );
324
				}
325
			}
326
327
			// reset the is_singular flag after our updated code above
328
			$query->is_singular = $query->is_single || $query->is_page || $query->is_attachment;
329
		}
330 188
	}
331
332
	/**
333
	 * Read the $post and process the View data inside
334
	 * @param  array  $wp Passed in the `wp` hook. Not used.
335
	 * @return void
336
	 */
337 2
	public function parse_content( $wp = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $wp is not used and could be removed.

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

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

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

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

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

Loading history...
381
382
		// It's a single entry, not search
383 2
		if ( $this->getSingleEntry() ) {
384 2
			return false;
385
		}
386
387 2
		$search_method = GravityView_Widget_Search::getInstance()->get_search_method();
388
389 2
		if( 'post' === $search_method ) {
390
			$get = $_POST;
391
		} else {
392 2
			$get = $_GET;
393
		}
394
395
		// No $_GET parameters
396 2
		if ( empty( $get ) || ! is_array( $get ) ) {
397 2
			return false;
398
		}
399
400
		// Remove empty values
401 1
		$get = array_filter( $get );
402
403
		// If the $_GET parameters are empty, it's no search.
404 1
		if ( empty( $get ) ) {
405
			return false;
406
		}
407
408 1
		$search_keys = array_keys( $get );
409
410 1
		$search_match = implode( '|', self::$search_parameters );
411
412 1
		foreach ( $search_keys as $search_key ) {
413
414
			// Analyze the search key $_GET parameter and see if it matches known GV args
415 1
			if ( preg_match( '/(' . $search_match . ')/i', $search_key ) ) {
416
				return true;
417
			}
418
		}
419
420 1
		return false;
421
	}
422
423
	/**
424
	 * Filter the title for the single entry view
425
	 *
426
	 * @param  string $passed_title  Current title
427
	 * @param  int $passed_post_id Post ID
428
	 * @return string (modified) title
429
	 */
430 9
	public function single_entry_title( $passed_title, $passed_post_id = null ) {
431 9
		global $post;
432
433
		// Since this is a public method, it can be called outside of the plugin. Don't assume things have been loaded properly.
434 9
		if ( ! class_exists( '\GV\Entry' ) ) {
435
			return $passed_title;
436
		}
437
438 9
		$gventry = gravityview()->request->is_entry();
439
440
		// If this is the directory view, return.
441 9
		if( ! $gventry ) {
442 8
			return $passed_title;
443
		}
444
445 1
		$entry = $gventry->as_entry();
446
447
		/**
448
		 * @filter `gravityview/single/title/out_loop` Apply the Single Entry Title filter outside the WordPress loop?
449
		 * @param boolean $in_the_loop Whether to apply the filter to the menu title and the meta tag <title> - outside the loop
450
		 * @param array $entry Current entry
451
		 */
452 1
		$apply_outside_loop = apply_filters( 'gravityview/single/title/out_loop', in_the_loop(), $entry );
453
454 1
		if ( ! $apply_outside_loop ) {
455 1
			return $passed_title;
456
		}
457
458
		// WooCommerce doesn't $post_id
459 1
		if ( empty( $passed_post_id ) )  {
460 1
			return $passed_title;
461
		}
462
463
		// Don't modify the title for anything other than the current view/post.
464
		// This is true for embedded shortcodes and Views.
465 1
		if ( is_object( $post ) && (int) $post->ID !== (int) $passed_post_id ) {
466 1
			return $passed_title;
467
		}
468
469 1
		$view = gravityview()->request->is_view();
470
471 1
		if( $view ) {
472 1
			return $this->_get_single_entry_title( $view, $entry, $passed_title );
473
		}
474
475 1
		$_gvid = \GV\Utils::_GET( 'gvid', null );
476
477
		// $_GET['gvid'] is set; we know what View to render
478 1
		if ( $_gvid ) {
479
480 1
			$view = \GV\View::by_id( $_gvid );
481
482 1
			return $this->_get_single_entry_title( $view, $entry, $passed_title );
483
		}
484
485 1
		global $post;
486
487 1
		if ( ! $post ) {
488
			return $passed_title;
489
		}
490
491 1
		$view_collection = \GV\View_Collection::from_post( $post );
492
493
		// We have multiple Views, but no gvid...this isn't valid security
494 1
		if( 1 < $view_collection->count() ) {
495 1
			return $passed_title;
496
		}
497
498
		return $this->_get_single_entry_title( $view_collection->first(), $entry, $passed_title );
499
	}
500
501
	/**
502
	 * Returns the single entry title for a View with variables replaced and shortcodes parsed
503
	 *
504
	 * @since 2.7.2
505
	 *
506
	 * @param \GV\View|null $view
507
	 * @param array $entry
508
	 * @param string $passed_title
509
	 *
510
	 * @return string
511
	 */
512
	private function _get_single_entry_title( $view, $entry = array(), $passed_title = '' ) {
513
514
		if ( ! $view ) {
515
			return $passed_title;
516
		}
517
518
		/**
519
		 * @filter `gravityview/single/title/check_entry_display` Override whether to check entry display rules against filters
520
		 * @internal This might change in the future! Don't rely on it.
521
		 * @since 2.7.2
522
		 * @param bool $check_entry_display Check whether the entry is visible for the current View configuration. Default: true.
523
		 * @param array $entry Gravity Forms entry array
524
		 * @param \GV\View $view The View
525
		 */
526
		$check_entry_display = apply_filters( 'gravityview/single/title/check_entry_display', true, $entry, $view );
527
528
		if( $check_entry_display ) {
529
530
			$check_display = GVCommon::check_entry_display( $entry, $view );
531
532
			if( is_wp_error( $check_display ) ) {
533
				return $passed_title;
534
			}
535
		}
536
537
		$title = $view->settings->get( 'single_title', $passed_title );
538
539
		$form = GVCommon::get_form( $entry['form_id'] );
540
541
		// We are allowing HTML in the fields, so no escaping the output
542
		$title = GravityView_API::replace_variables( $title, $form, $entry );
543
544
		$title = do_shortcode( $title );
545
546
		return $title;
547
	}
548
549
550
	/**
551
	 * In case View post is called directly, insert the view in the post content
552
	 *
553
	 * @deprecated Use \GV\View::content() instead.
554
	 *
555
	 * @access public
556
	 * @static
557
	 * @param mixed $content
558
	 * @return string Add the View output into View CPT content
559
	 */
560 4
	public function insert_view_in_content( $content ) {
561 4
		gravityview()->log->notice( '\GravityView_frontend::insert_view_in_content is deprecated. Use \GV\View::content()' );
562 4
		return \GV\View::content( $content );
563
	}
564
565
	/**
566
	 * Disable comments on GravityView post types
567
	 * @param  boolean $open    existing status
568
	 * @param  int $post_id Post ID
569
	 * @return boolean
570
	 */
571
	public function comments_open( $open, $post_id ) {
572
573
		if ( $this->isGravityviewPostType() ) {
574
			$open = false;
575
		}
576
577
		/**
578
		 * @filter `gravityview/comments_open` Whether to set comments to open or closed.
579
		 * @since  1.5.4
580
		 * @param  boolean $open Open or closed status
581
		 * @param  int $post_id Post ID to set comment status for
582
		 */
583
		$open = apply_filters( 'gravityview/comments_open', $open, $post_id );
584
585
		return $open;
586
	}
587
588
	/**
589
	 * Display a warning when a View has not been configured
590
	 *
591
	 * @since 1.19.2
592
	 *
593
	 * @param int $view_id The ID of the View currently being displayed
594
	 *
595
	 * @return void
596
	 */
597 2
	public function context_not_configured_warning( $view_id = 0 ) {
598
599 2
		if ( ! class_exists( 'GravityView_View' ) ) {
600
			return;
601
		}
602
603 2
		$fields = GravityView_View::getInstance()->getContextFields();
604
605 2
		if ( ! empty( $fields ) ) {
606 2
			return;
607
		}
608
609
		$context = GravityView_View::getInstance()->getContext();
610
611
		switch( $context ) {
612
			case 'directory':
613
				$tab = __( 'Multiple Entries', 'gravityview' );
614
				break;
615
			case 'edit':
616
				$tab = __( 'Edit Entry', 'gravityview' );
617
				break;
618
			case 'single':
619
			default:
620
				$tab = __( 'Single Entry', 'gravityview' );
621
				break;
622
		}
623
624
625
		$title = sprintf( esc_html_x('The %s layout has not been configured.', 'Displayed when a View is not configured. %s is replaced by the tab label', 'gravityview' ), $tab );
626
		$edit_link = admin_url( sprintf( 'post.php?post=%d&action=edit#%s-view', $view_id, $context ) );
627
		$action_text = sprintf( esc_html__('Add fields to %s', 'gravityview' ), $tab );
628
		$message = esc_html__( 'You can only see this message because you are able to edit this View.', 'gravityview' );
629
630
		$image =  sprintf( '<img alt="%s" src="%s" style="margin-top: 10px;" />', $tab, esc_url(plugins_url( sprintf( 'assets/images/tab-%s.png', $context ), GRAVITYVIEW_FILE ) ) );
631
		$output = sprintf( '<h3>%s <strong><a href="%s">%s</a></strong></h3><p>%s</p>', $title, esc_url( $edit_link ), $action_text, $message );
632
633
		echo GVCommon::generate_notice( $output . $image, 'gv-error error', 'edit_gravityview', $view_id );
634
	}
635
636
637
	/**
638
	 * Core function to render a View based on a set of arguments
639
	 *
640
	 * @access public
641
	 * @static
642
	 * @param array $passed_args {
643
	 *
644
	 *      Settings for rendering the View
645
	 *
646
	 *      @type int $id View id
647
	 *      @type int $page_size Number of entries to show per page
648
	 *      @type string $sort_field Form field id to sort
649
	 *      @type string $sort_direction Sorting direction ('ASC', 'DESC', or 'RAND')
650
	 *      @type string $start_date - Ymd
651
	 *      @type string $end_date - Ymd
652
	 *      @type string $class - assign a html class to the view
653
	 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
654
	 * }
655
	 *
656
	 * @deprecated Use \GV\View_Renderer
657
	 *
658
	 * @return string|null HTML output of a View, NULL if View isn't found
659
	 */
660 1
	public function render_view( $passed_args ) {
661 1
		gravityview()->log->notice( '\GravityView_frontend::render_view is deprecated. Use \GV\View_Renderer etc.' );
662
663
		/**
664
		 * We can use a shortcode here, since it's pretty much the same.
665
		 *
666
		 * But we do need to check embed permissions, since shortcodes don't do this.
667
		 */
668
669 1
		if ( ! $view = gravityview()->views->get( $passed_args ) ) {
0 ignored issues
show
Documentation introduced by
The property views does not exist on object<GV\Core>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
670
			return null;
671
		}
672
673 1
		$view->settings->update( $passed_args );
674
675 1
		$direct_access = apply_filters( 'gravityview_direct_access', true, $view->ID );
676 1
		$embed_only = $view->settings->get( 'embed_only' );
677
678 1
		if( ! $direct_access || ( $embed_only && ! GVCommon::has_cap( 'read_private_gravityviews' ) ) ) {
679 1
			return __( 'You are not allowed to view this content.', 'gravityview' );
680
		}
681
682 1
		$shortcode = new \GV\Shortcodes\gravityview();
683 1
		return $shortcode->callback( $passed_args );
684
	}
685
686
	/**
687
	 * Process the start and end dates for a view - overrides values defined in shortcode (if needed)
688
	 *
689
	 * The `start_date` and `end_date` keys need to be in a format processable by GFFormsModel::get_date_range_where(),
690
	 * which uses \DateTime() format.
691
	 *
692
	 * 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()},
693
	 * including strings like "now" or "-1 year" or "-3 days".
694
	 *
695
	 * @see GFFormsModel::get_date_range_where
696
	 *
697
	 * @param  array      $args            View settings
698
	 * @param  array      $search_criteria Search being performed, if any
699
	 * @return array                       Modified `$search_criteria` array
700
	 */
701 77
	public static function process_search_dates( $args, $search_criteria = array() ) {
702
703 77
		$return_search_criteria = $search_criteria;
704
705 77
		foreach ( array( 'start_date', 'end_date' ) as $key ) {
706
707
708
			// Is the start date or end date set in the view or shortcode?
709
			// If so, we want to make sure that the search doesn't go outside the bounds defined.
710 77
			if ( ! empty( $args[ $key ] ) ) {
711
712
				// Get a timestamp and see if it's a valid date format
713 2
				$date = strtotime( $args[ $key ] );
714
715
				// The date was invalid
716 2
				if ( empty( $date ) ) {
717
					gravityview()->log->error( ' Invalid {key} date format: {format}', array( 'key' => $key, 'format' => $args[ $key ] ) );
718
					continue;
719
				}
720
721
				// The format that Gravity Forms expects for start_date and day-specific (not hour/second-specific) end_date
722 2
				$datetime_format = 'Y-m-d H:i:s';
723 2
				$search_is_outside_view_bounds = false;
724
725 2
				if( ! empty( $search_criteria[ $key ] ) ) {
726
727 1
					$search_date = strtotime( $search_criteria[ $key ] );
728
729
					// The search is for entries before the start date defined by the settings
730 1
					switch ( $key ) {
731 1
						case 'end_date':
732
							/**
733
							 * If the end date is formatted as 'Y-m-d', it should be formatted without hours and seconds
734
							 * so that Gravity Forms can convert the day to 23:59:59 the previous day.
735
							 *
736
							 * If it's a relative date ("now" or "-1 day"), then it should use the precise date format
737
							 *
738
							 * @see GFFormsModel::get_date_range_where
739
							 */
740 1
							$datetime_format               = gravityview_is_valid_datetime( $args[ $key ] ) ? 'Y-m-d' : 'Y-m-d H:i:s';
741 1
							$search_is_outside_view_bounds = ( $search_date > $date );
742 1
							break;
743 1
						case 'start_date':
744 1
							$search_is_outside_view_bounds = ( $search_date < $date );
745 1
							break;
746
					}
747
				}
748
749
				// If there is no search being performed, or if there is a search being performed that's outside the bounds
750 2
				if ( empty( $search_criteria[ $key ] ) || $search_is_outside_view_bounds ) {
751
752
					// Then we override the search and re-set the start date
753 2
					$return_search_criteria[ $key ] = date_i18n( $datetime_format , $date, true );
754
				}
755
			}
756
		}
757
758 77
		if( isset( $return_search_criteria['start_date'] ) && isset( $return_search_criteria['end_date'] ) ) {
759
			// The start date is AFTER the end date. This will result in no results, but let's not force the issue.
760 2
			if ( strtotime( $return_search_criteria['start_date'] ) > strtotime( $return_search_criteria['end_date'] ) ) {
761 1
				gravityview()->log->error( 'Invalid search: the start date is after the end date.', array( 'data' => $return_search_criteria ) );
762
			}
763
		}
764
765 77
		return $return_search_criteria;
766
	}
767
768
769
	/**
770
	 * Process the approved only search criteria according to the View settings
771
	 *
772
	 * @param  array      $args            View settings
773
	 * @param  array      $search_criteria Search being performed, if any
774
	 * @return array                       Modified `$search_criteria` array
775
	 */
776 76
	public static function process_search_only_approved( $args, $search_criteria ) {
777
778
		/** @since 1.19 */
779 76
		if( ! empty( $args['admin_show_all_statuses'] ) && GVCommon::has_cap('gravityview_moderate_entries') ) {
780
			gravityview()->log->debug( 'User can moderate entries; showing all approval statuses' );
781
			return $search_criteria;
782
		}
783
784 76
		if ( ! empty( $args['show_only_approved'] ) ) {
785
786 18
			$search_criteria['field_filters'][] = array(
787
				'key' => GravityView_Entry_Approval::meta_key,
788
				'value' => GravityView_Entry_Approval_Status::APPROVED
789
			);
790
791 18
			$search_criteria['field_filters']['mode'] = 'all'; // force all the criterias to be met
792
793 18
			gravityview()->log->debug( '[process_search_only_approved] Search Criteria if show only approved: ', array( 'data' => $search_criteria ) );
794
		}
795
796 76
		return $search_criteria;
797
	}
798
799
800
	/**
801
	 * Check if a certain entry is approved.
802
	 *
803
	 * If we pass the View settings ($args) it will check the 'show_only_approved' setting before
804
	 *   checking the entry approved field, returning true if show_only_approved = false.
805
	 *
806
	 * @since 1.7
807
	 * @since 1.18 Converted check to use GravityView_Entry_Approval_Status::is_approved
808
	 *
809
	 * @uses GravityView_Entry_Approval_Status::is_approved
810
	 *
811
	 * @param array $entry  Entry object
812
	 * @param array $args   View settings (optional)
813
	 *
814
	 * @return bool
815
	 */
816
	public static function is_entry_approved( $entry, $args = array() ) {
817
818
		if ( empty( $entry['id'] ) || ( array_key_exists( 'show_only_approved', $args ) && ! $args['show_only_approved'] ) ) {
819
			// is implicitly approved if entry is null or View settings doesn't require to check for approval
820
			return true;
821
		}
822
823
		/** @since 1.19 */
824
		if( ! empty( $args['admin_show_all_statuses'] ) && GVCommon::has_cap('gravityview_moderate_entries') ) {
825
			gravityview()->log->debug( 'User can moderate entries, so entry is approved for viewing' );
826
			return true;
827
		}
828
829
		$is_approved = gform_get_meta( $entry['id'], GravityView_Entry_Approval::meta_key );
830
831
		return GravityView_Entry_Approval_Status::is_approved( $is_approved );
832
	}
833
834
	/**
835
	 * Parse search criteria for a entries search.
836
	 *
837
	 * array(
838
	 * 	'search_field' => 1, // ID of the field
839
	 *  'search_value' => '', // Value of the field to search
840
	 *  'search_operator' => 'contains', // 'is', 'isnot', '>', '<', 'contains'
841
	 *  'show_only_approved' => 0 or 1 // Boolean
842
	 * )
843
	 *
844
	 * @param  array $args    Array of args
845
	 * @param  int $form_id Gravity Forms form ID
846
	 * @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.
847
	 */
848 77
	public static function get_search_criteria( $args, $form_id ) {
849
		/**
850
		 * Compatibility with filters hooking in `gravityview_search_criteria` instead of `gravityview_fe_search_criteria`.
851
		 */
852 77
		$criteria = apply_filters( 'gravityview_search_criteria', array(), array( $form_id ), \GV\Utils::get( $args, 'id' ) );
853 77
		$search_criteria = isset( $criteria['search_criteria'] ) ? $criteria['search_criteria'] : array( 'field_filters' => array() );
854
855
		/**
856
		 * @filter `gravityview_fe_search_criteria` Modify the search criteria
857
		 * @see GravityView_Widget_Search::filter_entries Adds the default search criteria
858
		 * @param array $search_criteria Empty `field_filters` key
859
		 * @param int $form_id ID of the Gravity Forms form that is being searched
860
		 * @param array $args The View settings.
861
		 */
862 77
		$search_criteria = apply_filters( 'gravityview_fe_search_criteria', $search_criteria, $form_id, $args );
863
864 77
		if ( ! is_array( $search_criteria ) ) {
865
			return array();
866
		}
867
868 77
		$original_search_criteria = $search_criteria;
869
870 77
		gravityview()->log->debug( '[get_search_criteria] Search Criteria after hook gravityview_fe_search_criteria: ', array( 'data' =>$search_criteria ) );
871
872
		// implicity search
873 77
		if ( ! empty( $args['search_value'] ) ) {
874
875
			// Search operator options. Options: `is` or `contains`
876 3
			$operator = ! empty( $args['search_operator'] ) && in_array( $args['search_operator'], array( 'is', 'isnot', '>', '<', 'contains' ) ) ? $args['search_operator'] : 'contains';
877
878 3
			$search_criteria['field_filters'][] = array(
879 3
				'key' => \GV\Utils::_GET( 'search_field', \GV\Utils::get( $args, 'search_field' ) ), // The field ID to search
880 3
				'value' => _wp_specialchars( $args['search_value'] ), // The value to search. Encode ampersands but not quotes.
881 3
				'operator' => $operator,
882
			);
883
884
			// Lock search mode to "all" with implicit presearch filter.
885 3
			$search_criteria['field_filters']['mode'] = 'all';
886
		}
887
888 77
		if( $search_criteria !== $original_search_criteria ) {
889 3
			gravityview()->log->debug( '[get_search_criteria] Search Criteria after implicity search: ', array( 'data' => $search_criteria ) );
890
		}
891
892
		// Handle setting date range
893 77
		$search_criteria = self::process_search_dates( $args, $search_criteria );
894
895 77
		if( $search_criteria !== $original_search_criteria ) {
896 4
			gravityview()->log->debug( '[get_search_criteria] Search Criteria after date params: ', array( 'data' => $search_criteria ) );
897
		}
898
899
		// remove not approved entries
900 77
		$search_criteria = self::process_search_only_approved( $args, $search_criteria );
901
902
		/**
903
		 * @filter `gravityview_status` Modify entry status requirements to be included in search results.
904
		 * @param string $status Default: `active`. Accepts all Gravity Forms entry statuses, including `spam` and `trash`
905
		 */
906 77
		$search_criteria['status'] = apply_filters( 'gravityview_status', 'active', $args );
907
908 77
		return $search_criteria;
909
	}
910
911
912
913
	/**
914
	 * Core function to calculate View multi entries (directory) based on a set of arguments ($args):
915
	 *   $id - View id
916
	 *   $page_size - Page
917
	 *   $sort_field - form field id to sort
918
	 *   $sort_direction - ASC / DESC
919
	 *   $start_date - Ymd
920
	 *   $end_date - Ymd
921
	 *   $class - assign a html class to the view
922
	 *   $offset (optional) - This is the start point in the current data set (0 index based).
923
	 *
924
	 *
925
	 *
926
	 * @uses  gravityview_get_entries()
927
	 * @access public
928
	 * @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...
929
	 *   - $id - View id
930
	 *   - $page_size - Page
931
	 *   - $sort_field - form field id to sort
932
	 *   - $sort_direction - ASC / DESC
933
	 *   - $start_date - Ymd
934
	 *   - $end_date - Ymd
935
	 *   - $class - assign a html class to the view
936
	 *   - $offset (optional) - This is the start point in the current data set (0 index based).
937
	 * @param int $form_id Gravity Forms Form ID
938
	 * @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
939
	 */
940 1
	public static function get_view_entries( $args, $form_id ) {
941
942 1
		gravityview()->log->debug( '[get_view_entries] init' );
943
		// start filters and sorting
944
945 1
		$parameters = self::get_view_entries_parameters( $args, $form_id );
946
947 1
		$count = 0; // Must be defined so that gravityview_get_entries can use by reference
948
949
		// fetch entries
950
		list( $entries, $paging, $count ) =
951 1
			\GV\Mocks\GravityView_frontend_get_view_entries( $args, $form_id, $parameters, $count );
952
953 1
		gravityview()->log->debug( 'Get Entries. Found: {count} entries', array( 'count' => $count, 'data' => $entries ) );
954
955
		/**
956
		 * @filter `gravityview_view_entries` Filter the entries output to the View
957
		 * @deprecated since 1.5.2
958
		 * @param array $args View settings associative array
959
		 * @var array
960
		 */
961 1
		$entries = apply_filters( 'gravityview_view_entries', $entries, $args );
962
963
		$return = array(
964 1
			'count' => $count,
965 1
			'entries' => $entries,
966 1
			'paging' => $paging,
967
		);
968
969
		/**
970
		 * @filter `gravityview/view/entries` Filter the entries output to the View
971
		 * @param array $criteria associative array containing count, entries & paging
972
		 * @param array $args View settings associative array
973
		 * @since 1.5.2
974
		 */
975 1
		return apply_filters( 'gravityview/view/entries', $return, $args );
976
	}
977
978
	/**
979
	 * Get an array of search parameters formatted as Gravity Forms requires
980
	 *
981
	 * Results are filtered by `gravityview_get_entries` and `gravityview_get_entries_{View ID}` filters
982
	 *
983
	 * @uses GravityView_frontend::get_search_criteria
984
	 * @uses GravityView_frontend::get_search_criteria_paging
985
	 *
986
	 * @since 1.20
987
	 *
988
	 * @see \GV\View_Settings::defaults For $args options
989
	 *
990
	 * @param array $args Array of View settings, as structured in \GV\View_Settings::defaults
991
	 * @param int $form_id Gravity Forms form ID to search
992
	 *
993
	 * @return array With `search_criteria`, `sorting`, `paging`, `cache` keys
994
	 */
995 76
	public static function get_view_entries_parameters( $args = array(), $form_id = 0 ) {
996
997
998 76
		if ( ! is_array( $args ) || ! is_numeric( $form_id ) ) {
999
1000
			gravityview()->log->error( 'Passed args are not an array or the form ID is not numeric' );
1001
1002
			return array();
1003
		}
1004
1005 76
		$form_id = intval( $form_id );
1006
1007
		/**
1008
		 * Process search parameters
1009
		 * @var array
1010
		 */
1011 76
		$search_criteria = self::get_search_criteria( $args, $form_id );
1012
1013 76
		$paging = self::get_search_criteria_paging( $args );
1014
1015
		$parameters = array(
1016 76
			'search_criteria' => $search_criteria,
1017 76
			'sorting' => self::updateViewSorting( $args, $form_id ),
1018 76
			'paging' => $paging,
1019 76
			'cache' => isset( $args['cache'] ) ? $args['cache'] : true,
1020
		);
1021
1022
		/**
1023
		 * @filter `gravityview_get_entries` Filter get entries criteria
1024
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1025
		 * @param array $args View configuration args. {
1026
		 *      @type int $id View id
1027
		 *      @type int $page_size Number of entries to show per page
1028
		 *      @type string $sort_field Form field id to sort
1029
		 *      @type string $sort_direction Sorting direction ('ASC', 'DESC', or 'RAND')
1030
		 *      @type string $start_date - Ymd
1031
		 *      @type string $end_date - Ymd
1032
		 *      @type string $class - assign a html class to the view
1033
		 *      @type string $offset (optional) - This is the start point in the current data set (0 index based).
1034
		 * }
1035
		 * @param int $form_id ID of Gravity Forms form
1036
		 */
1037 76
		$parameters = apply_filters( 'gravityview_get_entries', $parameters, $args, $form_id );
1038
1039
		/**
1040
		 * @filter `gravityview_get_entries_{View ID}` Filter get entries criteria
1041
		 * @param array $parameters Array with `search_criteria`, `sorting` and `paging` keys.
1042
		 * @param array $args View configuration args.
1043
		 */
1044 76
		$parameters = apply_filters( 'gravityview_get_entries_'.\GV\Utils::get( $args, 'id' ), $parameters, $args, $form_id );
1045
1046 76
		gravityview()->log->debug( '$parameters passed to gravityview_get_entries(): ', array( 'data' => $parameters ) );
1047
1048 76
		return $parameters;
1049
	}
1050
1051
	/**
1052
	 * Get the paging array for the View
1053
	 *
1054
	 * @since 1.19.5
1055
	 *
1056
	 * @param $args
1057
	 * @param int $form_id
0 ignored issues
show
Bug introduced by
There is no parameter named $form_id. Was it maybe removed?

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

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

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

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

Loading history...
1058
	 */
1059 76
	public static function get_search_criteria_paging( $args ) {
1060
1061
		/**
1062
		 * @filter `gravityview_default_page_size` The default number of entries displayed in a View
1063
		 * @since 1.1.6
1064
		 * @param int $default_page_size Default: 25
1065
		 */
1066 76
		$default_page_size = apply_filters( 'gravityview_default_page_size', 25 );
1067
1068
		// Paging & offset
1069 76
		$page_size = ! empty( $args['page_size'] ) ? intval( $args['page_size'] ) : $default_page_size;
1070
1071 76
		if ( -1 === $page_size ) {
1072 1
			$page_size = PHP_INT_MAX;
1073
		}
1074
1075 76
		$curr_page = empty( $_GET['pagenum'] ) ? 1 : intval( $_GET['pagenum'] );
1076 76
		$offset = ( $curr_page - 1 ) * $page_size;
1077
1078 76
		if ( ! empty( $args['offset'] ) ) {
1079 1
			$offset += intval( $args['offset'] );
1080
		}
1081
1082
		$paging = array(
1083 76
			'offset' => $offset,
1084 76
			'page_size' => $page_size,
1085
		);
1086
1087 76
		gravityview()->log->debug( 'Paging: ', array( 'data' => $paging ) );
1088
1089 76
		return $paging;
1090
	}
1091
1092
	/**
1093
	 * Updates the View sorting criteria
1094
	 *
1095
	 * @since 1.7
1096
	 *
1097
	 * @param array $args View settings. Required to have `sort_field` and `sort_direction` keys
1098
	 * @param int $form_id The ID of the form used to sort
1099
	 * @return array $sorting Array with `key`, `direction` and `is_numeric` keys
1100
	 */
1101 76
	public static function updateViewSorting( $args, $form_id ) {
1102 76
		$sorting = array();
1103
1104 76
		$has_values = isset( $_GET['sort'] );
1105
1106 76
		if ( $has_values && is_array( $_GET['sort'] ) ) {
1107 3
			$sorts = array_keys( $_GET['sort'] );
1108 3
			$dirs  = array_values( $_GET['sort'] );
1109
1110 3
			if ( $has_values = array_filter( $dirs ) ) {
1111 3
				$sort_field_id = end( $sorts );
1112 3
				$sort_direction = end( $dirs );
1113
			}
1114
		}
1115
1116 76
		if ( ! isset( $sort_field_id ) ) {
1117 76
			$sort_field_id = isset( $_GET['sort'] ) ? $_GET['sort'] : \GV\Utils::get( $args, 'sort_field' );
1118
		}
1119
1120 76
		if ( ! isset( $sort_direction ) ) {
1121 76
			$sort_direction = isset( $_GET['dir'] ) ? $_GET['dir'] : \GV\Utils::get( $args, 'sort_direction' );
1122
		}
1123
1124 76
		if ( is_array( $sort_field_id ) ) {
1125 1
			$sort_field_id = array_pop( $sort_field_id );
1126
		}
1127
1128 76
		if ( is_array( $sort_direction ) ) {
1129
			$sort_direction = array_pop( $sort_direction );
1130
		}
1131
1132 76
		if ( ! empty( $sort_field_id ) ) {
1133 6
			if ( is_array( $sort_field_id ) ) {
1134
				$sort_direction = array_values( $sort_field_id );
1135
				$sort_field_id = array_keys( $sort_field_id );
1136
1137
				$sort_field_id = reset( $sort_field_id );
1138
				$sort_direction = reset( $sort_direction );
1139
			}
1140
1141 6
			$sort_field_id = self::_override_sorting_id_by_field_type( $sort_field_id, $form_id );
1142
			$sorting = array(
1143 6
				'key' => $sort_field_id,
1144 6
				'direction' => strtolower( $sort_direction ),
1145 6
				'is_numeric' => GVCommon::is_field_numeric( $form_id, $sort_field_id )
1146
			);
1147
1148 6
			if ( 'RAND' === $sort_direction ) {
1149
1150
				$form = GFAPI::get_form( $form_id );
1151
1152
				// Get the first GF_Field field ID, set as the key for entry randomization
1153
				if ( ! empty( $form['fields'] ) ) {
1154
1155
					/** @var GF_Field $field */
1156
					foreach ( $form['fields'] as $field ) {
1157
						if ( ! is_a( $field, 'GF_Field' ) ) {
1158
							continue;
1159
						}
1160
1161
						$sorting = array(
1162
							'key'        => $field->id,
1163
							'is_numeric' => false,
1164
							'direction'  => 'RAND',
1165
						);
1166
1167
						break;
1168
					}
1169
				}
1170
			}
1171
		}
1172
1173 76
		GravityView_View::getInstance()->setSorting( $sorting );
1174
1175 76
		gravityview()->log->debug( '[updateViewSorting] Sort Criteria : ', array( 'data' => $sorting ) );
1176
1177 76
		return $sorting;
1178
1179
	}
1180
1181
	/**
1182
	 * Override sorting per field
1183
	 *
1184
	 * Currently only modifies sorting ID when sorting by the full name. Sorts by first name.
1185
	 * Use the `gravityview/sorting/full-name` filter to override.
1186
	 *
1187
	 * @todo Filter from GravityView_Field
1188
	 * @since 1.7.4
1189
	 * @internal Hi developer! Although this is public, don't call this method; we're going to replace it.
1190
	 *
1191
	 * @param int|string|array $sort_field_id Field used for sorting (`id` or `1.2`), or an array for multisorts
1192
	 * @param int $form_id GF Form ID
1193
	 *
1194
	 * @return string Possibly modified sorting ID
1195
	 */
1196 6
	public static function _override_sorting_id_by_field_type( $sort_field_id, $form_id ) {
1197
1198 6
		if ( is_array( $sort_field_id ) ) {
1199
			$modified_ids = array();
1200
			foreach ( $sort_field_id as $_sort_field_id ) {
1201
				$modified_ids []= self::_override_sorting_id_by_field_type( $_sort_field_id, $form_id );
1202
			}
1203
			return $modified_ids;
1204
		}
1205
1206 6
		$form = gravityview_get_form( $form_id );
1207
1208 6
		$sort_field = GFFormsModel::get_field( $form, $sort_field_id );
1209
1210 6
		if( ! $sort_field ) {
1211 1
			return $sort_field_id;
1212
		}
1213
1214 5
		switch ( $sort_field['type'] ) {
1215
1216 5
			case 'address':
1217
				// Sorting by full address
1218
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1219
1220
					/**
1221
					 * Override how to sort when sorting address
1222
					 *
1223
					 * @since 1.8
1224
					 *
1225
					 * @param string $address_part `street`, `street2`, `city`, `state`, `zip`, or `country` (default: `city`)
1226
					 * @param string $sort_field_id Field used for sorting
1227
					 * @param int $form_id GF Form ID
1228
					 */
1229
					$address_part = apply_filters( 'gravityview/sorting/address', 'city', $sort_field_id, $form_id );
1230
1231
					switch( strtolower( $address_part ) ){
1232
						case 'street':
1233
							$sort_field_id .= '.1';
1234
							break;
1235
						case 'street2':
1236
							$sort_field_id .= '.2';
1237
							break;
1238
						default:
1239
						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...
1240
							$sort_field_id .= '.3';
1241
							break;
1242
						case 'state':
1243
							$sort_field_id .= '.4';
1244
							break;
1245
						case 'zip':
1246
							$sort_field_id .= '.5';
1247
							break;
1248
						case 'country':
1249
							$sort_field_id .= '.6';
1250
							break;
1251
					}
1252
1253
				}
1254
				break;
1255 5
			case 'name':
1256
				// Sorting by full name, not first, last, etc.
1257
				if ( floatval( $sort_field_id ) === floor( $sort_field_id ) ) {
1258
					/**
1259
					 * @filter `gravityview/sorting/full-name` Override how to sort when sorting full name.
1260
					 * @since 1.7.4
1261
					 * @param[in,out] string $name_part Sort by `first` or `last` (default: `first`)
1262
					 * @param[in] string $sort_field_id Field used for sorting
1263
					 * @param[in] int $form_id GF Form ID
1264
					 */
1265
					$name_part = apply_filters( 'gravityview/sorting/full-name', 'first', $sort_field_id, $form_id );
1266
1267
					if ( 'last' === strtolower( $name_part ) ) {
1268
						$sort_field_id .= '.6';
1269
					} else {
1270
						$sort_field_id .= '.3';
1271
					}
1272
				}
1273
				break;
1274 5
			case 'list':
1275
				$sort_field_id = false;
1276
				break;
1277 5
			case 'time':
1278
1279
				/**
1280
				 * @filter `gravityview/sorting/time` Override how to sort when sorting time
1281
				 * @see GravityView_Field_Time
1282
				 * @since 1.14
1283
				 * @param[in,out] string $name_part Field used for sorting
1284
				 * @param[in] int $form_id GF Form ID
1285
				 */
1286 1
				$sort_field_id = apply_filters( 'gravityview/sorting/time', $sort_field_id, $form_id );
1287 1
				break;
1288
		}
1289
1290 5
		return $sort_field_id;
1291
	}
1292
1293
	/**
1294
	 * Verify if user requested a single entry view
1295
	 * @since 2.3.3 Added return null
1296
	 * @return boolean|string|null false if not, single entry slug if true, null if \GV\Entry doesn't exist yet
1297
	 */
1298 26
	public static function is_single_entry() {
1299
1300
		// Since this is a public method, it can be called outside of the plugin. Don't assume things have been loaded properly.
1301 26
		if ( ! class_exists( '\GV\Entry' ) ) {
1302
1303
			// Not using gravityview()->log->error(), since that may not exist yet either!
1304
			do_action( 'gravityview_log_error', '\GV\Entry not defined yet. Backtrace: ' . wp_debug_backtrace_summary()  );
1305
1306
			return null;
1307
		}
1308
1309 26
		$var_name = \GV\Entry::get_endpoint_name();
1310
1311 26
		$single_entry = get_query_var( $var_name );
1312
1313
		/**
1314
		 * Modify the entry that is being displayed.
1315
		 *
1316
		 * @internal Should only be used by things like the oEmbed functionality.
1317
		 * @since 1.6
1318
		 */
1319 26
		$single_entry = apply_filters( 'gravityview/is_single_entry', $single_entry );
1320
1321 26
		if ( empty( $single_entry ) ){
1322 4
			return false;
1323
		} else {
1324 22
			return $single_entry;
1325
		}
1326
	}
1327
1328
1329
	/**
1330
	 * Register styles and scripts
1331
	 *
1332
	 * @access public
1333
	 * @return void
1334
	 */
1335 1
	public function add_scripts_and_styles() {
1336 1
		global $post, $posts;
1337
		// enqueue template specific styles
1338 1
		if ( $this->getGvOutputData() ) {
1339
1340 1
			$views = $this->getGvOutputData()->get_views();
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::get_views() has been deprecated.

This method has been deprecated.

Loading history...
1341
1342 1
			foreach ( $views as $view_id => $data ) {
1343 1
				$view = \GV\View::by_id( $data['id'] );
1344 1
				$view_id = $view->ID;
1345 1
				$template_id = gravityview_get_template_id( $view->ID );
1346 1
				$data = $view->as_data();
0 ignored issues
show
Deprecated Code introduced by
The method GV\View::as_data() has been deprecated.

This method has been deprecated.

Loading history...
1347
1348
				/**
1349
				 * Don't enqueue the scripts or styles if it's not going to be displayed.
1350
				 * @since 1.15
1351
				 */
1352 1
				if( is_user_logged_in() && false === GVCommon::has_cap( 'read_gravityview', $view_id ) ) {
1353
					continue;
1354
				}
1355
1356
				// By default, no thickbox
1357 1
				$js_dependencies = array( 'jquery', 'gravityview-jquery-cookie' );
1358 1
				$css_dependencies = array();
1359
1360 1
				$lightbox = $view->settings->get( 'lightbox' );
1361
1362
				// If the thickbox is enqueued, add dependencies
1363 1
				if ( $lightbox ) {
1364
1365 1
					global $wp_filter;
1366
1367 1
					if ( ! empty( $wp_filter[ 'gravity_view_lightbox_script' ] ) ) {
1368
						gravityview()->log->warning( 'gravity_view_lightbox_script filter is deprecated use gravityview_lightbox_script instead' );
1369
					}
1370
1371
					/**
1372
					 * @filter `gravity_view_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1373
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1374
					 * @deprecated 2.5.1 Naming. See `gravityview_lightbox_script` instead.
1375
					 */
1376 1
					$js_dependency = apply_filters( 'gravity_view_lightbox_script', 'thickbox' );
1377
1378
					/**
1379
					 * @filter `gravityview_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1380
					 * @since 2.5.1
1381
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1382
					 * @param \GV\View The View.
1383
					 */
1384 1
					apply_filters( 'gravityview_lightbox_script', $js_dependency, $view );
1385 1
					$js_dependencies[] = $js_dependency;
1386
1387 1
					if ( ! empty( $wp_filter[ 'gravity_view_lightbox_style' ] ) ) {
1388
						gravityview()->log->warning( 'gravity_view_lightbox_style filter is deprecated use gravityview_lightbox_style instead' );
1389
					}
1390
1391
					/**
1392
					 * @filter `gravity_view_lightbox_style` Modify the lightbox CSS slug. Default: `thickbox`
1393
					 * @param string $script_slug If you want to use a different lightbox script, return the name of its CSS file here.
1394
					 * @deprecated 2.5.1 Naming. See `gravityview_lightbox_style` instead.
1395
					 */
1396 1
					$css_dependency = apply_filters( 'gravity_view_lightbox_style', 'thickbox' );
1397
1398
					/**
1399
					 * @filter `gravityview_lightbox_script` Override the lightbox script to enqueue. Default: `thickbox`
1400
					 * @since 2.5.1
1401
					 * @param string $script_slug If you want to use a different lightbox script, return the name of it here.
1402
					 * @param \GV\View The View.
1403
					 */
1404 1
					$css_dependency = apply_filters( 'gravityview_lightbox_style', $css_dependency, $view );
1405 1
					$css_dependencies[] = $css_dependency;
1406
				}
1407
1408
				/**
1409
				 * If the form has checkbox fields, enqueue dashicons
1410
				 * @see https://github.com/katzwebservices/GravityView/issues/536
1411
				 * @since 1.15
1412
				 */
1413 1
				if( gravityview_view_has_single_checkbox_or_radio( $data['form'], $data['fields'] ) ) {
1414
					$css_dependencies[] = 'dashicons';
1415
				}
1416
1417 1
				wp_register_script( 'gravityview-jquery-cookie', plugins_url( 'assets/lib/jquery.cookie/jquery.cookie.min.js', GRAVITYVIEW_FILE ), array( 'jquery' ), GravityView_Plugin::version, true );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1418
1419 1
				$script_debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1420
1421 1
				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 );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1422
1423 1
				wp_enqueue_script( 'gravityview-fe-view' );
1424
1425 1
				if ( ! empty( $data['atts']['sort_columns'] ) ) {
1426
					wp_enqueue_style( 'gravityview_font', plugins_url( 'assets/css/font.css', GRAVITYVIEW_FILE ), $css_dependencies, GravityView_Plugin::version, 'all' );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1427
				}
1428
1429 1
				$this->enqueue_default_style( $css_dependencies );
1430
1431 1
				self::add_style( $template_id );
1432
			}
1433
1434 1
			if ( 'wp_print_footer_scripts' === current_filter() ) {
1435
1436
				$js_localization = array(
1437
					'cookiepath' => COOKIEPATH,
1438
					'clear' => _x( 'Clear', 'Clear all data from the form', 'gravityview' ),
1439
					'reset' => _x( 'Reset', 'Reset the search form to the state that existed on page load', 'gravityview' ),
1440
				);
1441
1442
				/**
1443
				 * @filter `gravityview_js_localization` Modify the array passed to wp_localize_script()
1444
				 * @param array $js_localization The data padded to the Javascript file
1445
				 * @param array $views Array of View data arrays with View settings
1446
				 */
1447
				$js_localization = apply_filters( 'gravityview_js_localization', $js_localization, $views );
1448
1449
				wp_localize_script( 'gravityview-fe-view', 'gvGlobals', $js_localization );
1450
			}
1451
		}
1452 1
	}
1453
1454
	/**
1455
	 * Handle enqueuing the `gravityview_default_style` stylesheet
1456
	 *
1457
	 * @since 1.17
1458
	 *
1459
	 * @param array $css_dependencies Dependencies for the `gravityview_default_style` stylesheet
1460
	 *
1461
	 * @return void
1462
	 */
1463
	private function enqueue_default_style( $css_dependencies = array() ) {
1464
1465
		/**
1466
		 * @filter `gravityview_use_legacy_search_css` Should GravityView use the legacy Search Bar stylesheet (from before Version 1.17)?
1467
		 * @since 1.17
1468
		 * @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`
1469
		 */
1470
		$use_legacy_search_style = apply_filters( 'gravityview_use_legacy_search_style', false );
1471
1472
		$rtl = is_rtl() ? '-rtl' : '';
1473
1474
		$css_file_base = $use_legacy_search_style ? 'gv-legacy-search' : 'gv-default-styles';
1475
1476
		$path = gravityview_css_url( $css_file_base . $rtl . '.css' );
1477
1478
		wp_enqueue_style( 'gravityview_default_style', $path, $css_dependencies, GravityView_Plugin::version, 'all' );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1479
	}
1480
1481
	/**
1482
	 * Add template extra style if exists
1483
	 * @param string $template_id
1484
	 */
1485
	public static function add_style( $template_id ) {
1486
1487
		if ( ! empty( $template_id ) && wp_style_is( 'gravityview_style_' . $template_id, 'registered' ) ) {
1488
			gravityview()->log->debug(  'Adding extra template style for {template_id}', array( 'template_id' => $template_id ) );
1489
			wp_enqueue_style( 'gravityview_style_' . $template_id );
1490
		} elseif ( empty( $template_id ) ) {
1491
			gravityview()->log->error( 'Cannot add template style; template_id is empty' );
1492
		} else {
1493
			gravityview()->log->error( 'Cannot add template style; {template_id} is not registered', array( 'template_id' => 'gravityview_style_' . $template_id ) );
1494
		}
1495
1496
	}
1497
1498
1499
	/**
1500
	 * Inject the sorting links on the table columns
1501
	 *
1502
	 * Callback function for hook 'gravityview/template/field_label'
1503
	 * @see GravityView_API::field_label() (in includes/class-api.php)
1504
	 *
1505
	 * @since 1.7
1506
	 *
1507
	 * @param string $label Field label
1508
	 * @param array $field Field settings
1509
	 * @param array $form Form object
1510
	 *
1511
	 * @return string Field Label
1512
	 */
1513
	public function add_columns_sort_links( $label = '', $field, $form ) {
1514
1515
		/**
1516
		 * Not a table-based template; don't add sort icons
1517
		 * @since 1.12
1518
		 */
1519
		if( ! preg_match( '/table/ism', GravityView_View::getInstance()->getTemplatePartSlug() ) ) {
1520
			return $label;
1521
		}
1522
1523
		if ( ! $this->is_field_sortable( $field['id'], $form ) ) {
1524
			return $label;
1525
		}
1526
1527
		$sorting = GravityView_View::getInstance()->getSorting();
1528
1529
		$class = 'gv-sort';
1530
1531
		$sort_field_id = self::_override_sorting_id_by_field_type( $field['id'], $form['id'] );
1532
1533
		$sort_args = array(
1534
			'sort' => $field['id'],
1535
			'dir' => 'asc',
1536
		);
1537
1538
		if ( ! empty( $sorting['key'] ) && (string) $sort_field_id === (string) $sorting['key'] ) {
1539
			//toggle sorting direction.
1540
			if ( 'asc' === $sorting['direction'] ) {
1541
				$sort_args['dir'] = 'desc';
1542
				$class .= ' gv-icon-sort-desc';
1543
			} else {
1544
				$sort_args['dir'] = 'asc';
1545
				$class .= ' gv-icon-sort-asc';
1546
			}
1547
		} else {
1548
			$class .= ' gv-icon-caret-up-down';
1549
		}
1550
1551
		$url = add_query_arg( $sort_args, remove_query_arg( array('pagenum') ) );
1552
1553
		return '<a href="'. esc_url_raw( $url ) .'" class="'. $class .'" ></a>&nbsp;'. $label;
1554
1555
	}
1556
1557
	/**
1558
	 * Checks if field (column) is sortable
1559
	 *
1560
	 * @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...
1561
	 * @param array $form Gravity Forms form array
1562
	 *
1563
	 * @since 1.7
1564
	 *
1565
	 * @return bool True: Yes, field is sortable; False: not sortable
1566
	 */
1567 1
	public function is_field_sortable( $field_id = '', $form = array() ) {
1568
1569 1
		$field_type = $field_id;
1570
1571 1
		if( is_numeric( $field_id ) ) {
1572 1
			$field = GFFormsModel::get_field( $form, $field_id );
1573 1
			$field_type = $field ? $field->type : $field_id;
1574
		}
1575
1576
		$not_sortable = array(
1577 1
			'edit_link',
1578
			'delete_link',
1579
		);
1580
1581
		/**
1582
		 * @filter `gravityview/sortable/field_blacklist` Modify what fields should never be sortable.
1583
		 * @since 1.7
1584
		 * @param[in,out] array $not_sortable Array of field types that aren't sortable
1585
		 * @param string $field_type Field type to check whether the field is sortable
1586
		 * @param array $form Gravity Forms form
1587
		 */
1588 1
		$not_sortable = apply_filters( 'gravityview/sortable/field_blacklist', $not_sortable, $field_type, $form );
1589
1590 1
		if ( in_array( $field_type, $not_sortable ) ) {
1591
			return false;
1592
		}
1593
1594 1
		return apply_filters( "gravityview/sortable/formfield_{$form['id']}_{$field_id}", apply_filters( "gravityview/sortable/field_{$field_id}", true, $form ) );
1595
1596
	}
1597
1598
}
1599
1600
GravityView_frontend::getInstance();
1601
1602
1603
1604