Completed
Push — try/gutenberg-separate-jetpack... ( e8dd3e...f0efb9 )
by Bernhard
39:26 queued 23:22
created

ajax_save_payment_button()   F

Complexity

Conditions 15
Paths 2048

Size

Total Lines 67

Duplication

Lines 6
Ratio 8.96 %

Importance

Changes 0
Metric Value
cc 15
nc 2048
nop 0
dl 6
loc 67
rs 1.7499
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * Disable direct access/execution to/of the widget code.
4
 */
5
if ( ! defined( 'ABSPATH' ) ) {
6
	exit;
7
}
8
9
if ( ! class_exists( 'Jetpack_Simple_Payments_Widget' ) ) {
10
	/**
11
	 * Simple Payments Button
12
	 *
13
	 * Display a Simple Payment Button as a Widget.
14
	 */
15
	class Jetpack_Simple_Payments_Widget extends WP_Widget {
16
		// https://developer.paypal.com/docs/integration/direct/rest/currency-codes/
17
		private static $supported_currency_list = array(
0 ignored issues
show
Unused Code introduced by
The property $supported_currency_list is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
18
			'USD' => '$',
19
			'GBP' => '&#163;',
20
			'JPY' => '&#165;',
21
			'BRL' => 'R$',
22
			'EUR' => '&#8364;',
23
			'NZD' => 'NZ$',
24
			'AUD' => 'A$',
25
			'CAD' => 'C$',
26
			'INR' => '₹',
27
			'ILS' => '₪',
28
			'RUB' => '₽',
29
			'MXN' => 'MX$',
30
			'SEK' => 'Skr',
31
			'HUF' => 'Ft',
32
			'CHF' => 'CHF',
33
			'CZK' => 'Kč',
34
			'DKK' => 'Dkr',
35
			'HKD' => 'HK$',
36
			'NOK' => 'Kr',
37
			'PHP' => '₱',
38
			'PLN' => 'PLN',
39
			'SGD' => 'S$',
40
			'TWD' => 'NT$',
41
			'THB' => '฿',
42
		);
43
44
		/**
45
		 * Constructor.
46
		 */
47
		function __construct() {
48
			parent::__construct(
49
				'jetpack_simple_payments_widget',
50
				/** This filter is documented in modules/widgets/facebook-likebox.php */
51
				apply_filters( 'jetpack_widget_name', __( 'Simple Payments', 'jetpack' ) ),
52
				array(
53
					'classname' => 'jetpack-simple-payments',
54
					'description' => __( 'Add a Simple Payment Button as a Widget.', 'jetpack' ),
55
					'customize_selective_refresh' => true,
56
				)
57
			);
58
59
			if ( is_customize_preview() ) {
60
				add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles_and_scripts' ) );
61
62
				add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
63
				add_action( 'wp_ajax_customize-jetpack-simple-payments-buttons-get', array( $this, 'ajax_get_payment_buttons' ) );
64
				add_action( 'wp_ajax_customize-jetpack-simple-payments-button-save', array( $this, 'ajax_save_payment_button' ) );
65
				add_action( 'wp_ajax_customize-jetpack-simple-payments-button-delete', array( $this, 'ajax_delete_payment_button' ) );
66
			}
67
68
			if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) {
69
				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) );
70
			}
71
		}
72
73
		/**
74
		 * Return an associative array of default values.
75
		 *
76
		 * These values are used in new widgets.
77
		 *
78
		 * @return array Default values for the widget options.
79
		 */
80
		private function defaults() {
81
			$current_user = wp_get_current_user();
82
			$default_product_id = $this->get_first_product_id();
83
84
			return array(
85
				'title' => '',
86
				'product_post_id' => $default_product_id,
87
				'form_action' => '',
88
				'form_product_id' => 0,
89
				'form_product_title' => '',
90
				'form_product_description' => '',
91
				'form_product_image_id' => 0,
92
				'form_product_image_src' => '',
93
				'form_product_currency' => '',
94
				'form_product_price' => '',
95
				'form_product_multiple' => '',
96
				'form_product_email' => $current_user->user_email,
97
			);
98
		}
99
100
		/**
101
		 * Adds a nonce for customizing menus.
102
		 *
103
		 * @param array $nonces Array of nonces.
104
		 * @return array $nonces Modified array of nonces.
105
		 */
106
		function filter_nonces( $nonces ) {
107
			$nonces['customize-jetpack-simple-payments'] = wp_create_nonce( 'customize-jetpack-simple-payments' );
108
			return $nonces;
109
		}
110
111
		function enqueue_style() {
112
			wp_enqueue_style( 'jetpack-simple-payments-widget-style', plugins_url( 'simple-payments/style.css', __FILE__ ), array(), '20180518' );
113
		}
114
115
		function admin_enqueue_styles_and_scripts(){
116
				wp_enqueue_style( 'jetpack-simple-payments-widget-customizer', plugins_url( 'simple-payments/customizer.css', __FILE__ ) );
117
118
				wp_enqueue_media();
119
				wp_enqueue_script( 'jetpack-simple-payments-widget-customizer', plugins_url( '/simple-payments/customizer.js', __FILE__ ), array( 'jquery' ), false, true );
120
				wp_localize_script( 'jetpack-simple-payments-widget-customizer', 'jpSimplePaymentsStrings', array(
121
					'deleteConfirmation' => __( 'Are you sure you want to delete this item? It will be disabled and removed from all locations where it currently appears.', 'jetpack' )
122
				) );
123
		}
124
125
		public function ajax_get_payment_buttons() {
126
			if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
127
				wp_send_json_error( 'bad_nonce', 400 );
128
			}
129
130
			if ( ! current_user_can( 'customize' ) ) {
131
				wp_send_json_error( 'customize_not_allowed', 403 );
132
			}
133
134
			$post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
135 View Code Duplication
			if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
136
				wp_send_json_error( 'insufficient_post_permissions', 403 );
137
			}
138
139
			$product_posts = get_posts( array(
140
				'numberposts' => 100,
141
				'orderby' => 'date',
142
				'post_type' => Jetpack_Simple_Payments::$post_type_product,
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
143
				'post_status' => 'publish',
144
			 ) );
145
146
			 $formatted_products = array_map( array( $this, 'format_product_post_for_ajax_reponse' ), $product_posts );
147
148
			 wp_send_json_success( $formatted_products );
149
		}
150
151
		public function format_product_post_for_ajax_reponse( $product_post ) {
152
			return array(
153
				'ID' => $product_post->ID,
154
				'post_title' => $product_post->post_title,
155
			);
156
		}
157
158
		public function ajax_save_payment_button() {
159
			if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
160
				wp_send_json_error( 'bad_nonce', 400 );
161
			}
162
163
			if ( ! current_user_can( 'customize' ) ) {
164
				wp_send_json_error( 'customize_not_allowed', 403 );
165
			}
166
167
			$post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
168 View Code Duplication
			if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
169
				wp_send_json_error( 'insufficient_post_permissions', 403 );
170
			}
171
172 View Code Duplication
			if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
173
				wp_send_json_error( 'missing_params', 400 );
174
			}
175
176
			$params = wp_unslash( $_POST['params'] );
177
			$errors = $this->validate_ajax_params( $params );
178
			if ( ! empty( $errors->errors ) ) {
179
				wp_send_json_error( $errors );
180
			}
181
182
			$product_post_id = isset( $params['product_post_id'] ) ? intval( $params['product_post_id'] ) : 0;
183
184
			$product_post = array(
185
				'ID' => $product_post_id,
186
				'post_type' => Jetpack_Simple_Payments::$post_type_product,
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
187
				'post_status' => 'publish',
188
				'post_title' => $params['post_title'],
189
				'post_content' => $params['post_content'],
190
				'_thumbnail_id' => ! empty( $params['image_id'] ) ? $params['image_id'] : -1,
191
				'meta_input' => array(
192
					'spay_currency' => $params['currency'],
193
					'spay_price' => $params['price'],
194
					'spay_multiple' => isset( $params['multiple'] ) ? intval( $params['multiple'] ) : 0,
195
					'spay_email' => is_email( $params['email'] ),
196
				),
197
			);
198
199
			if ( empty( $product_post_id ) ) {
200
				$product_post_id = wp_insert_post( $product_post );
201
			} else {
202
				$product_post_id = wp_update_post( $product_post );
203
			}
204
205
			if ( ! $product_post_id || is_wp_error( $product_post_id ) ) {
206
				wp_send_json_error( $product_post_id );
207
			}
208
209
			$tracks_properties = array(
210
				'id'       => $product_post_id,
211
				'currency' => $params['currency'],
212
				'price'    => $params['price']
213
			);
214
			if ( 0 === $product_post['ID'] ) {
215
				$this->record_event( 'created', 'create', $tracks_properties );
216
			} else {
217
				$this->record_event( 'updated', 'update', $tracks_properties );
218
			}
219
220
			wp_send_json_success( array(
221
				'product_post_id' => $product_post_id,
222
				'product_post_title' => $params['post_title'],
223
			) );
224
		}
225
226
		public function ajax_delete_payment_button() {
227
			if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
228
				wp_send_json_error( 'bad_nonce', 400 );
229
			}
230
231
			if ( ! current_user_can( 'customize' ) ) {
232
				wp_send_json_error( 'customize_not_allowed', 403 );
233
			}
234
235 View Code Duplication
			if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
236
				wp_send_json_error( 'missing_params', 400 );
237
			}
238
239
			$params = wp_unslash( $_POST['params'] );
240
			$illegal_params = array_diff( array_keys( $params ), array( 'product_post_id' ) );
241
			if ( ! empty( $illegal_params ) ) {
242
				wp_send_json_error( 'illegal_params', 400 );
243
			}
244
245
			$product_id = ( int ) $params['product_post_id'];
246
			$product_post = get_post( $product_id );
247
248
			$return = array( 'status' => $product_post->post_status );
249
250
			wp_delete_post( $product_id, true );
251
			$status = get_post_status( $product_id );
252
			if ( false === $status ) {
253
				$return['status'] = 'deleted';
254
			}
255
256
			$this->record_event( 'deleted', 'delete', array( 'id' => $product_id ) );
257
258
			wp_send_json_success( $return );
259
		}
260
261
		public function validate_ajax_params( $params ) {
262
			$errors = new WP_Error();
263
264
			$illegal_params = array_diff( array_keys( $params ), array( 'product_post_id', 'post_title', 'post_content', 'image_id', 'currency', 'price', 'multiple', 'email' ) );
265
			if ( ! empty( $illegal_params ) ) {
266
				$errors->add( 'illegal_params', __( 'Invalid parameters.', 'jetpack' ) );
267
			}
268
269
			if ( empty( $params['post_title'] ) ) {
270
				$errors->add( 'post_title', __( "People need to know what they're paying for! Please add a brief title.", 'jetpack' ) );
271
			}
272
273
			if ( empty( $params['price'] ) || floatval( $params['price'] ) <= 0 ) {
274
				$errors->add( 'price', __( 'Everything comes with a price tag these days. Please add a your product price.', 'jetpack' ) );
275
			}
276
277
			if ( empty( $params['email'] ) || ! is_email( $params['email'] ) ) {
278
				$errors->add( 'email', __( 'We want to make sure payments reach you, so please add an email address.', 'jetpack' ) );
279
			}
280
281
			return $errors;
282
		}
283
284
		function get_first_product_id() {
285
			$product_posts = get_posts( array(
286
				'numberposts' => 1,
287
				'orderby' => 'date',
288
				'post_type' => Jetpack_Simple_Payments::$post_type_product,
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
289
				'post_status' => 'publish',
290
			 ) );
291
292
			return ! empty( $product_posts ) ? $product_posts[0]->ID : null;
293
		}
294
295
		/**
296
		 * Front-end display of widget.
297
		 *
298
		 * @see WP_Widget::widget()
299
		 *
300
		 * @param array $args     Widget arguments.
301
		 * @param array $instance Saved values from database.
302
		 */
303
		function widget( $args, $instance ) {
304
			$instance = wp_parse_args( $instance, $this->defaults() );
305
306
			echo $args['before_widget'];
307
308
			/** This filter is documented in core/src/wp-includes/default-widgets.php */
309
			$title = apply_filters( 'widget_title', $instance['title'] );
310
			if ( ! empty( $title ) ) {
311
				echo $args['before_title'] . $title . $args['after_title'];
312
			}
313
314
			echo '<div class="jetpack-simple-payments-content">';
315
316
			if ( ! empty( $instance['form_action'] ) && in_array( $instance['form_action'], array( 'add', 'edit' ) ) && is_customize_preview() ) {
317
				require( dirname( __FILE__ ) . '/simple-payments/widget.php' );
318
			} else {
319
				$jsp = Jetpack_Simple_Payments::getInstance();
320
				$simple_payments_button = $jsp->parse_shortcode( array(
321
					'id' => $instance['product_post_id'],
322
				) );
323
324
				if ( ! is_null( $simple_payments_button ) || is_customize_preview() ) {
325
					echo $simple_payments_button;
326
				}
327
			}
328
329
			echo '</div><!--simple-payments-->';
330
331
			echo $args['after_widget'];
332
333
			/** This action is already documented in modules/widgets/gravatar-profile.php */
334
			do_action( 'jetpack_stats_extra', 'widget_view', 'simple_payments' );
335
		}
336
337
		/**
338
		 * Gets the latests field value from either the old instance or the new instance.
339
		 *
340
		 * @param array $mixed Array of values for the new form instance.
0 ignored issues
show
Bug introduced by
There is no parameter named $mixed. 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...
341
		 * @param array $mixed Array of values for the old form instance.
0 ignored issues
show
Bug introduced by
There is no parameter named $mixed. 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...
342
		 * @return mixed $mixed Field value.
343
		 */
344
		private function get_latest_field_value( $new_instance, $old_instance, $field) {
345
			return ! empty( $new_instance[ $field ] )
346
				? sanitize_text_field( $new_instance[ $field ] )
347
				: $old_instance[ $field ];
348
		}
349
350
		/**
351
		 * Gets the product fields from the product post. If no post found
352
		 * it returns the default values.
353
		 *
354
		 * @param int Product Post ID.
355
		 * @return array $fields Product Fields from the Product Post.
356
		 */
357
		private function get_product_from_post( $product_post_id ) {
358
			$product_post = get_post( $product_post_id );
359
			$form_product_id = $product_post_id;
360
			if( ! empty( $product_post ) ) {
361
				$form_product_image_id = get_post_thumbnail_id( $product_post_id );
362
363
				return array(
364
					'form_product_id' => $form_product_id,
365
					'form_product_title' => get_the_title( $product_post ),
366
					'form_product_description' => $product_post->post_content,
367
					'form_product_image_id' => $form_product_image_id,
368
					'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
369
					'form_product_currency' => get_post_meta( $product_post_id, 'spay_currency', true ),
370
					'form_product_price' => get_post_meta( $product_post_id, 'spay_price', true ),
371
					'form_product_multiple' => get_post_meta( $product_post_id, 'spay_multiple', true ) || '0',
372
					'form_product_email' => get_post_meta( $product_post_id, 'spay_email', true ),
373
				);
374
			}
375
376
			return $this->defaults();
377
		}
378
379
		/**
380
		 * Record a Track event and bump a MC stat.
381
		 *
382
		 * @param string $stat_name
383
		 * @param string $event_action
384
		 * @param array $event_properties
385
		 */
386
		private function record_event( $stat_name, $event_action, $event_properties = array() ) {
387
			$current_user = wp_get_current_user();
388
389
			// `bumps_stats_extra` only exists on .com
390
			if ( function_exists( 'bump_stats_extras' ) ) {
391
				require_lib( 'tracks/client' );
392
				tracks_record_event( $current_user, 'simple_payments_button_' . $event_action, $event_properties );
393
				/** This action is documented in modules/widgets/social-media-icons.php */
394
				do_action( 'jetpack_bump_stats_extra', 'jetpack-simple_payments', $stat_name );
395
				return;
396
			}
397
398
			jetpack_tracks_record_event( $current_user, 'jetpack_wpa_simple_payments_button_' . $event_action, $event_properties );
399
			$jetpack = Jetpack::init();
400
			// $jetpack->stat automatically prepends the stat group with 'jetpack-'
401
			$jetpack->stat( 'simple_payments', $stat_name ) ;
402
			$jetpack->do_stats( 'server_side' );
403
		}
404
405
		/**
406
		 * Sanitize widget form values as they are saved.
407
		 *
408
		 * @see WP_Widget::update()
409
		 *
410
		 * @param array $new_instance Values just sent to be saved.
411
		 * @param array $old_instance Previously saved values from database.
412
		 *
413
		 * @return array Updated safe values to be saved.
414
		 */
415
		function update( $new_instance, $old_instance ) {
416
			$defaults = $this->defaults();
417
			//do not overrite `product_post_id` for `$new_instance` with the defaults
418
			$new_instance = wp_parse_args( $new_instance, array_diff_key( $defaults, array( 'product_post_id' => 0 ) ) );
419
			$old_instance = wp_parse_args( $old_instance, $defaults );
420
421
			$required_widget_props = array(
422
				'title' => $this->get_latest_field_value( $new_instance, $old_instance, 'title' ),
423
				'product_post_id' => $this->get_latest_field_value( $new_instance, $old_instance, 'product_post_id' ),
424
				'form_action' => $this->get_latest_field_value( $new_instance, $old_instance, 'form_action' ),
425
			);
426
427
			if ( strcmp( $new_instance['form_action'], $old_instance['form_action'] ) !== 0 ) {
428
				if ( $new_instance['form_action'] == 'edit' ) {
429
					return array_merge( $this->get_product_from_post( ( int ) $old_instance['product_post_id'] ), $required_widget_props );
430
				}
431
432
				if ( $new_instance['form_action'] == 'clear' ) {
433
					return array_merge( $this->defaults(), $required_widget_props );
434
				}
435
			}
436
437
			$form_product_image_id = (int) $new_instance['form_product_image_id'];
438
439
			$form_product_email = ! empty( $new_instance['form_product_email'] )
440
				? sanitize_text_field( $new_instance['form_product_email'] )
441
				: $defaults['form_product_email'];
442
443
			return array_merge( $required_widget_props, array(
444
				'form_product_id' => ( int ) $new_instance['form_product_id'],
445
				'form_product_title' => sanitize_text_field( $new_instance['form_product_title'] ),
446
				'form_product_description' => sanitize_text_field( $new_instance['form_product_description'] ),
447
				'form_product_image_id' => $form_product_image_id,
448
				'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
449
				'form_product_currency' => sanitize_text_field( $new_instance['form_product_currency'] ),
450
				'form_product_price' => sanitize_text_field( $new_instance['form_product_price'] ),
451
				'form_product_multiple' => sanitize_text_field( $new_instance['form_product_multiple'] ),
452
				'form_product_email' => $form_product_email,
453
			) );
454
		}
455
456
		/**
457
		 * Back-end widget form.
458
		 *
459
		 * @see WP_Widget::form()
460
		 *
461
		 * @param array $instance Previously saved values from database.
462
		 */
463
		function form( $instance ) {
464
			$instance = wp_parse_args( $instance, $this->defaults() );
465
466
			$product_posts = get_posts( array(
467
				'numberposts' => 100,
468
				'orderby' => 'date',
469
				'post_type' => Jetpack_Simple_Payments::$post_type_product,
0 ignored issues
show
Bug introduced by
The property post_type_product cannot be accessed from this context as it is declared private in class Jetpack_Simple_Payments.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
470
				'post_status' => 'publish',
471
			 ) );
472
473
			require( dirname( __FILE__ ) . '/simple-payments/form.php' );
474
		}
475
	}
476
477
	// Register Jetpack_Simple_Payments_Widget widget.
478
	function register_widget_jetpack_simple_payments() {
479
		$jetpack_simple_payments = Jetpack_Simple_Payments::getInstance();
480
		if ( ! $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
481
			return;
482
		}
483
484
		register_widget( 'Jetpack_Simple_Payments_Widget' );
485
	}
486
	add_action( 'widgets_init', 'register_widget_jetpack_simple_payments' );
487
}
488