Completed
Push — master ( d55b2d...cf5706 )
by Ravinder
09:28 queued 10s
created

Give_Donor_Wall::parse_atts()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 6
nop 1
dl 0
loc 67
rs 8.72
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Donors Gravatars
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Donors_Gravatars
7
 * @copyright   Copyright (c) 2016, GiveWP
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
18
/**
19
 * Give_Donor_Wall Class
20
 *
21
 * This class handles donors.
22
 *
23
 * @since 2.2.0
24
 */
25
class Give_Donor_Wall {
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...
26
27
	/**
28
	 * Instance.
29
	 *
30
	 * @since  2.2.0
31
	 * @access private
32
	 * @var Give_Donor_Wall
33
	 */
34
	static private $instance;
35
36
	/**
37
	 * Singleton pattern.
38
	 *
39
	 * @since  2.2.0
40
	 * @access private
41
	 */
42
	private function __construct() {
43
	}
44
45
46
	/**
47
	 * Get instance.
48
	 *
49
	 * @since  2.2.0
50
	 * @access public
51
	 * @return Give_Donor_Wall
52
	 */
53 View Code Duplication
	public static function get_instance() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
54
		if ( null === static::$instance ) {
55
			self::$instance = new static();
56
57
			self::$instance->setup_actions();
58
		}
59
60
		return self::$instance;
61
	}
62
63
	/**
64
	 * Setup the default hooks and actions
65
	 *
66
	 * @since  2.2.0
67
	 *
68
	 * @return void
69
	 */
70
	public function setup_actions() {
71
72
		add_shortcode( 'give_donor_wall', array( $this, 'render_shortcode' ) );
73
74
		add_action( 'wp_ajax_give_get_donor_comments', array( $this, 'ajax_handler' ) );
75
		add_action( 'wp_ajax_nopriv_give_get_donor_comments', array( $this, 'ajax_handler' ) );
76
77
	}
78
79
80
	/**
81
	 * Displays donors in a grid layout.
82
	 *
83
	 * @since  2.2.0
84
	 *
85
	 * @param array $atts                {
86
	 *                                   Optional. Attributes of the donor wall shortcode.
87
	 *
88
	 * @type int    $donors_per_page     Number of donors per page. Default '20'.
89
	 * @type int    $form_id             The donation form to filter donors by. Default is all forms (no filter).
90
	 * @type bool   $paged               Whether to paginate donors. Default 'true'.
91
	 * @type string $ids                 A comma-separated list of donor IDs to display. Default empty.
92
	 * @type string $columns             Maximum columns to display. Default 'best-fit'.
93
	 *                                   Accepts 'best-fit', '1', '2', '3', '4'.
94
	 * @type bool   $show_avatar         Whether to display the donor's gravatar image if available. Default 'true'.
95
	 * @type bool   $show_name           Whether to display the donor's full name, first and last. Default 'true'.
96
	 * @type bool   $show_total          Whether to display the donor's donation amount. Default 'true'.
97
	 * @type bool   $show_time           Whether to display date of the last donation. Default 'true'.
98
	 * @type bool   $show_comments       Whether to display the donor's comment if they left one. Default 'true'.
99
	 * @type int    $comment_length      The number of words to display for the comments before a "Read more" field
100
	 * @type int    $only_comments       Whether to display the donors only with comment. Default 'false'.
101
	 *
102
	 * @type string $readmore_text       Link label for modal in which donor can read full comment.
103
	 * @type string $loadmore_text       Button label which will load more donor comments.
104
	 * @type int    $avatar_size         Avatar image size in pixels without the "px". Default "60"
105
	 * @type string $orderby             The order in which you want the donations to appear.
106
	 *                                   Currently we are using this attribute internally and it will sort donations by created date.
107
	 * @type string $order               The order in which you want the donors to appear. Accepts "ASC". "DESC".
108
	 *
109
	 * }
110
	 * @return string|bool The markup of the form grid or false.
111
	 */
112
	public function render_shortcode( $atts ) {
113
114
		$give_settings = give_get_settings();
115
116
		$atts      = $this->parse_atts( $atts );
117
		$donations = $this->get_donation_data( $atts );
118
		$html      = '';
119
120
		if ( $donations ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $donations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
121
122
			ob_start();
123
124
			foreach ( $donations as $donation ) {
125
				// Give/templates/shortcode-donor-wall.php.
126
				give_get_template( 'shortcode-donor-wall', array( $donation, $give_settings, $atts ) );
127
			}
128
129
			$html = ob_get_clean();
130
131
			// Return only donor html.
132
			if (
133
				isset( $atts['only_donor_html'] )
134
				&& wp_doing_ajax()
135
				&& $atts['only_donor_html']
136
			) {
137
				return $html;
138
			}
139
		}
140
141
		$temp_atts          = $atts;
142
		$temp_atts['paged'] = $atts['paged'] + 1;
143
144
		$more_btn_html = sprintf(
145
			'<input type="hidden" class="give-donor-wall-shortcode-attrs" data-shortcode="%1$s">',
146
			rawurlencode( http_build_query( $atts ) )
147
		);
148
149
		if ( $this->has_donations( $temp_atts ) ) {
150
			$more_btn_html .= sprintf(
151
				'<button class="give-donor__load_more give-button-with-loader"><span class="give-loading-animation"></span>%1$s</button>',
152
				$atts['loadmore_text']
153
			);
154
		}
155
156
		$html = $html
157
			? sprintf(
158
				'<div class="give-wrap give-grid-ie-utility"><div class="give-grid give-grid--%1$s">%2$s</div>%3$s</div>',
159
				esc_attr( $atts['columns'] ),
160
				$html,
161
				$more_btn_html
162
			)
163
			: '';
164
165
		return $html;
166
	}
167
168
	/**
169
	 * Parse shortcode attributes
170
	 *
171
	 * @since  2.2.0
172
	 * @access public
173
	 *
174
	 * @param array $atts Shortcode attributes.
175
	 *
176
	 * @return array
177
	 */
178
	public function parse_atts( $atts ) {
179
		$atts = shortcode_atts(
180
			array(
181
				'donors_per_page' => 12,
182
				'form_id'         => 0,
183
				'paged'           => 1,
184
				'ids'             => '',
185
				'columns'         => 'best-fit',
186
				'anonymous'       => true,
187
				'show_avatar'     => true,
188
				'show_name'       => true,
189
				'show_total'      => true,
190
				'show_time'       => true,
191
				'show_comments'   => true,
192
				'comment_length'  => 140,
193
				'only_comments'   => false,
194
				'readmore_text'   => esc_html__( 'Read more', 'give' ),
195
				'loadmore_text'   => esc_html__( 'Load more', 'give' ),
196
				'avatar_size'     => 60,
197
				'orderby'         => 'post_date',
198
				'order'           => 'DESC',
199
				'hide_empty'      => true,  // Deprecated in 2.3.0
200
				'only_donor_html' => false, // Only for internal use.
201
			), $atts
202
		);
203
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
204
205
		// Validate boolean attributes.
206
		$boolean_attributes = array(
207
			'anonymous',
208
			'show_avatar',
209
			'show_name',
210
			'show_total',
211
			'show_time',
212
			'show_comments',
213
			'show_comments',
214
			'hide_empty',
215
			'only_comments',
216
			'only_donor_html',
217
		);
218
219
		foreach ( $boolean_attributes as $att ) {
220
			// Convert numeric to boolean.
221
			// It will prevent condition check against boolean value.
222
			if ( is_numeric( $atts[ $att ] ) ) {
223
				$atts[ $att ] = (bool) $atts[ $att ];
224
			}
225
226
			$atts[ $att ] = filter_var( $atts[ $att ], FILTER_VALIDATE_BOOLEAN );
227
		}
228
229
		// Validate numeric attributes.
230
		$numeric_attributes = array(
231
			'donors_per_page',
232
			'form_id',
233
			'paged',
234
			'comment_length',
235
			'avatar_size',
236
		);
237
238
		foreach ( $numeric_attributes as $att ) {
239
			// It will prevent condition check against numeric value.
240
			$atts[ $att ] = absint( $atts[ $att ] );
241
		}
242
243
		return $atts;
244
	}
245
246
	/**
247
	 * Get donors
248
	 *
249
	 * @since  2.2.0
250
	 * @access public
251
	 *
252
	 * @param array $donor_query Dorno query.
253
	 *
254
	 * @return array
255
	 */
256
	public function get_donors( $donor_query ) {
257
		$donor_query = new Give_Donors_Query( $donor_query );
258
		$donors      = $donor_query->get_donors();
259
260
		return $donors;
261
	}
262
263
264
	/**
265
	 * Ajax handler
266
	 *
267
	 * @since  2.2.0
268
	 * @access public
269
	 */
270
	public function ajax_handler() {
271
		$shortcode_atts = wp_parse_args( give_clean( rawurldecode( $_POST['data'] ) ) ); // @codingStandardsIgnoreLine
272
273
		// Get next page donor comments.
274
		$shortcode_atts['paged']           = $shortcode_atts['paged'] + 1;
275
		$shortcode_atts['only_donor_html'] = true;
276
277
		$donors_comment_html = $this->render_shortcode( $shortcode_atts );
278
279
		// Check if donor comment remaining.
280
		$temp_atts          = $shortcode_atts;
281
		$temp_atts['paged'] = $shortcode_atts['paged'] + 1;
282
		$has_donors         = $this->has_donations( $temp_atts ) ? 1 : 0;
283
284
		// Remove internal shortcode param.
285
		unset( $shortcode_atts['only_donor_html'] );
286
287
		wp_send_json(
288
			array(
289
				'shortcode' => rawurlencode( http_build_query( $shortcode_atts ) ),
290
				'html'      => $donors_comment_html,
291
				'remaining' => $has_donors,
292
			)
293
		);
294
	}
295
296
	/**
297
	 * Get query params
298
	 *
299
	 * @since 2.3.0
300
	 *
301
	 * @param  array $atts
302
	 *
303
	 * @return array
304
	 */
305
	private function get_query_param( $atts = array() ) {
306
		$valid_order   = array( 'ASC', 'DESC' );
307
		$valid_orderby = array( 'post_date', 'donation_amount' );
308
309
		$query_atts = array();
310
311
		$query_atts['order']         = in_array( $atts['order'], $valid_order ) ? $atts['order'] : 'DESC';
312
		$query_atts['orderby']       = in_array( $atts['orderby'], $valid_orderby ) ? $atts['orderby'] : 'post_date';
313
		$query_atts['limit']         = $atts['donors_per_page'];
314
		$query_atts['offset']        = $atts['donors_per_page'] * ( $atts['paged'] - 1 );
315
		$query_atts['form_id']       = $atts['form_id'];
316
		$query_atts['only_comments'] = ( true === $atts['only_comments'] );
317
		$query_atts['anonymous']     = ( true === $atts['anonymous'] );
318
319
		return $query_atts;
320
	}
321
322
	/**
323
	 * Get donation data.
324
	 *
325
	 * @since 2.3.0
326
	 *
327
	 * @param array $atts
328
	 *
329
	 * @return array
330
	 */
331
	private function get_donation_data( $atts = array() ) {
332
		global $wpdb;
333
334
		// Bailout if donation does not exist.
335
		if ( ! ( $donation_ids = $this->get_donations( $atts ) ) ) {
336
			return array();
337
		}
338
339
		$donation_ids = ! empty( $donation_ids )
340
			? '\'' . implode( '\',\'', $donation_ids ) . '\''
341
			: '';
342
343
		// Backward compatibility
344
		$donation_id_col = Give()->payment_meta->get_meta_type() . '_id';
345
346
		$sql = "SELECT m1.*, p1.post_date as donation_date FROM {$wpdb->donationmeta} as m1
347
				INNER JOIN {$wpdb->posts} as p1 ON (m1.{$donation_id_col}=p1.ID)
348
				WHERE m1.{$donation_id_col} IN ( {$donation_ids} )
349
				ORDER BY FIELD( p1.ID, {$donation_ids} )
350
				";
351
352
		$results = (array) $wpdb->get_results( $sql );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
353
354
		if ( ! empty( $results ) ) {
355
			$temp = array();
356
357
			/* @var stdClass $result */
358
			foreach ( $results as $result ) {
359
				$temp[ $result->{$donation_id_col} ][ $result->meta_key ] = maybe_unserialize( $result->meta_value );
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
360
361
				// Set donation date.
362
				if( empty( $temp[ $result->{$donation_id_col} ][ 'donation_date' ] ) ){
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
363
					$temp[ $result->{$donation_id_col} ]['donation_date'] = $result->donation_date;
364
				}
365
			}
366
367
			$comments = $this->get_donor_comments( $temp );
368
369
			if ( ! empty( $temp ) ) {
370
				foreach ( $temp as $donation_id => $donation_data ) {
371
					$temp[ $donation_id ]['donation_id'] = $donation_id;
372
373
					$temp[ $donation_id ]['name_initial'] = give_get_name_initial( array(
374
						'firstname' => $donation_data['_give_donor_billing_first_name'],
375
						'lastname'  => $donation_data['_give_donor_billing_last_name'],
376
					) );
377
378
					$temp[ $donation_id ]['donor_comment'] = ! empty( $comments[ $donation_id ] ) ? $comments[ $donation_id ] : '';
379
				}
380
			}
381
382
			$results = ! empty( $temp ) ? $temp : array();
383
		}
384
385
		return $results;
386
	}
387
388
	/**
389
	 * Get donation list for specific query
390
	 *
391
	 * @since 2.3.0
392
	 *
393
	 * @param  array $atts
394
	 *
395
	 * @return array
396
	 */
397
	private function get_donations( $atts = array() ) {
398
		global $wpdb;
399
400
		// Backward compatibility
401
		$donation_id_col = Give()->payment_meta->get_meta_type() . '_id';
402
403
		$query_params = $this->get_query_param( $atts );
404
405
		$sql   = "SELECT p1.ID FROM {$wpdb->posts} as p1";
406
		$where = " WHERE p1.post_status IN ('publish') AND p1.post_type = 'give_payment'";
407
408
		// exclude donation with zero amount from result.
409
		$sql   .= " INNER JOIN {$wpdb->donationmeta} as m1 ON (p1.ID = m1.{$donation_id_col})";
410
		$where .= " AND m1.meta_key='_give_payment_total' AND m1.meta_value>0";
411
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
412
413
		if ( $query_params['form_id'] ) {
414
			$sql   .= " INNER JOIN {$wpdb->donationmeta} as m2 ON (p1.ID = m2.{$donation_id_col})";
415
			$where .= " AND m2.meta_key='_give_payment_form_id' AND m2.meta_value={$query_params['form_id']}";
416
		}
417
418
		// exclude donations which does not has donor comment.
419
		if ( $query_params['only_comments'] ) {
420
			$sql   .= " INNER JOIN {$wpdb->give_comments} as gc1 ON (p1.ID = gc1.comment_parent)";
421
			$where .= " AND gc1.comment_type='donor_donation'";
422
		}
423
424
		// exclude anonymous donation form query based on query parameters.
425
		if (
426
			! $query_params['anonymous']
427
			|| $query_params['only_comments']
428
		) {
429
			$where .= " AND p1.ID NOT IN ( SELECT DISTINCT({$donation_id_col}) FROM {$wpdb->donationmeta} WHERE meta_key='_give_anonymous_donation' AND meta_value='1')";
430
		}
431
432
		// order by query based on parameter.
433
		if ( 'donation_amount' === $query_params['orderby'] ) {
434
			$order = " ORDER BY m1.meta_value+0 {$query_params['order']}";
435
		} else {
436
			$order = " ORDER BY p1.{$query_params['orderby']} {$query_params['order']}, p1.ID {$query_params['order']}";
437
		}
438
439
		$limit  = " LIMIT {$query_params['limit']}";
440
		$offset = " OFFSET {$query_params['offset']}";
441
442
		$sql = $sql . $where . $order . $limit . $offset;
443
444
		$donation_ids = $wpdb->get_col( $sql );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
445
446
		return $donation_ids;
447
	}
448
449
	/**
450
	 * Get donor comments
451
	 *
452
	 * @since 2.3.0
453
	 *
454
	 * @param array $donations_data
455
	 *
456
	 * @return array
457
	 */
458
	private function get_donor_comments( $donations_data = array() ) {
459
		global $wpdb;
460
		$comments = array();
461
462
		// Bailout.
463
		if ( empty( $donations_data ) ) {
464
			return $comments;
465
		}
466
467
		// Backward compatibility.
468
		if (
469
			! give_has_upgrade_completed( 'v230_move_donor_note' )
470
			|| ! give_has_upgrade_completed( 'v230_move_donation_note' )
471
		) {
472
			foreach ( $donations_data as $id => $data ) {
473
				$comment         = give_get_donor_donation_comment( $id, $data['_give_payment_donor_id'] );
474
				$comments[ $id ] = ! empty( $comment ) ? $comment->comment_content : '';
475
			}
476
477
			return $comments;
478
		}
479
480
		$sql   = "SELECT c1.comment_parent as donation_id, c1.comment_content as comment FROM {$wpdb->give_comments} as c1";
481
		$sql   .= " INNER JOIN {$wpdb->give_commentmeta} as cm1 ON (c1.comment_ID=cm1.give_comment_id)";
482
		$where = array();
483
484
		foreach ( $donations_data as $id => $data ) {
485
			// Do not fetch comment for anonymous donation.
486
			if( ! empty( $data['_give_anonymous_donation'] )  ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
487
				continue;
488
			}
489
490
			$where[] = "(c1.comment_parent={$id} AND cm1.meta_key='_give_donor_id' AND cm1.meta_value={$data['_give_payment_donor_id']})";
491
		}
492
493
		$where = ' WHERE ' . implode( ' OR ', $where );
494
		$where .= " AND c1.comment_type='donor_donation'";
495
496
		$sql = $sql . $where;
497
498
		$comments = (array) $wpdb->get_results( $sql );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
499
500
		if ( ! empty( $comments ) ) {
501
			$comments = array_combine(
502
				wp_list_pluck( $comments, 'donation_id' ),
503
				wp_list_pluck( $comments, 'comment' )
504
			);
505
		}
506
507
		return $comments;
508
	}
509
510
	/**
511
	 * Check if donation exist or not for specific query
512
	 *
513
	 * @since 2.3.0
514
	 *
515
	 * @param  array $atts
516
	 *
517
	 * @return bool
518
	 */
519
	private function has_donations( $atts = array() ) {
520
		return (bool) $this->get_donations( $atts );
521
	}
522
}
523
524
// Initialize shortcode.
525
Give_Donor_Wall::get_instance();
526