PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.3
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.3
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-paypal-payments / src / widget / class-simple-payments-widget.php
jetpack / jetpack_vendor / automattic / jetpack-paypal-payments / src / widget Last commit date
simple-payments 2 months ago class-simple-payments-widget.php 8 months ago
class-simple-payments-widget.php
636 lines
1 <?php
2 /**
3 * Pay with PayPal (aka Simple Payments)
4 *
5 * Display a Pay with PayPal button as a Widget.
6 *
7 * @package automattic/jetpack-paypal-payments
8 */
9
10 namespace Automattic\Jetpack\Paypal_Payments\Widgets;
11
12 use Automattic\Jetpack\PayPal_Payments;
13 use Automattic\Jetpack\Paypal_Payments\Simple_Payments;
14 use Automattic\Jetpack\Tracking;
15 use Jetpack;
16 use WP_Error;
17 use WP_Widget;
18
19 // Disable direct access/execution to/of the widget code.
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit( 0 );
22 }
23
24 if ( ! class_exists( 'Simple_Payments_Widget' ) ) {
25 /**
26 * Pay with PayPal (aka Simple Payments)
27 *
28 * Display a Pay with PayPal button as a Widget.
29 */
30 class Simple_Payments_Widget extends WP_Widget {
31 /**
32 * The package version.
33 *
34 * @var string
35 */
36 private $package_version = PayPal_Payments::PACKAGE_VERSION;
37
38 /**
39 * Currencies should be supported by PayPal:
40 *
41 * @var array $supported_currency_list
42 * @link https://developer.paypal.com/docs/api/reference/currency-codes/
43 *
44 * List has to be in sync with list at the block's client side and API's backend side:
45 * @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/extensions/blocks/simple-payments/constants.js#L9-L39
46 * @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/modules/simple-payments/simple-payments.php#L386-L415
47 *
48 * Indian Rupee (INR) is listed here for backwards compatibility with previously added widgets.
49 * It's not supported by Pay with PayPal because at the time of the creation of this file
50 * because it's limited to in-country PayPal India accounts only.
51 * Discussion: https://github.com/Automattic/wp-calypso/pull/28236
52 */
53 public static $supported_currency_list = array(
54 'USD' => '$',
55 'GBP' => '&#163;',
56 'JPY' => '&#165;',
57 'BRL' => 'R$',
58 'EUR' => '&#8364;',
59 'NZD' => 'NZ$',
60 'AUD' => 'A$',
61 'CAD' => 'C$',
62 'INR' => '',
63 'ILS' => '',
64 'RUB' => '',
65 'MXN' => 'MX$',
66 'SEK' => 'Skr',
67 'HUF' => 'Ft',
68 'CHF' => 'CHF',
69 'CZK' => '',
70 'DKK' => 'Dkr',
71 'HKD' => 'HK$',
72 'NOK' => 'Kr',
73 'PHP' => '',
74 'PLN' => 'PLN',
75 'SGD' => 'S$',
76 'TWD' => 'NT$',
77 'THB' => '฿',
78 );
79
80 /**
81 * Constructor.
82 */
83 public function __construct() {
84 parent::__construct(
85 'jetpack_simple_payments_widget',
86 /** This filter is documented in modules/widgets/facebook-likebox.php */
87 apply_filters( 'jetpack_widget_name', __( 'Pay with PayPal', 'jetpack-paypal-payments' ) ),
88 array(
89 'classname' => 'simple-payments',
90 'description' => __( 'Add a Pay with PayPal button as a Widget.', 'jetpack-paypal-payments' ),
91 'customize_selective_refresh' => true,
92 )
93 );
94
95 global $pagenow;
96 if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
97 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
98 }
99
100 if ( is_customize_preview() && Simple_Payments::is_enabled_jetpack_simple_payments() ) {
101 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
102
103 add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
104 add_action( 'wp_ajax_customize-jetpack-simple-payments-buttons-get', array( $this, 'ajax_get_payment_buttons' ) );
105 add_action( 'wp_ajax_customize-jetpack-simple-payments-button-save', array( $this, 'ajax_save_payment_button' ) );
106 add_action( 'wp_ajax_customize-jetpack-simple-payments-button-delete', array( $this, 'ajax_delete_payment_button' ) );
107 }
108
109 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_simple_payment_widget' ) );
110 }
111
112 /**
113 * Return an array of the widgets hidden from the Legacy Widget block.
114 *
115 * This is used to hide the Pay with PayPal from the Legacy Widget block.
116 *
117 * @param array $widget_types the widget types that are currently hidden.
118 * @return array Widget types hidden from the Legacy Widget block
119 */
120 public function hide_simple_payment_widget( $widget_types ) {
121 $widget_types[] = 'simple_payments_widget';
122 return $widget_types;
123 }
124
125 /**
126 * Return an associative array of default values.
127 *
128 * These values are used in new widgets.
129 *
130 * @return array Default values for the widget options.
131 */
132 private function defaults() {
133 $current_user = wp_get_current_user();
134 $default_product_id = $this->get_first_product_id();
135
136 return array(
137 'title' => '',
138 'product_post_id' => $default_product_id,
139 'form_action' => '',
140 'form_product_id' => 0,
141 'form_product_title' => '',
142 'form_product_description' => '',
143 'form_product_image_id' => 0,
144 'form_product_image_src' => '',
145 'form_product_currency' => '',
146 'form_product_price' => '',
147 'form_product_multiple' => '',
148 'form_product_email' => $current_user->user_email,
149 );
150 }
151
152 /**
153 * Adds a nonce for customizing menus.
154 *
155 * @param array $nonces Array of nonces.
156 * @return array $nonces Modified array of nonces.
157 */
158 public function filter_nonces( $nonces ) {
159 $nonces['customize-jetpack-simple-payments'] = wp_create_nonce( 'customize-jetpack-simple-payments' );
160 return $nonces;
161 }
162
163 /**
164 * Enqueue styles.
165 */
166 public function enqueue_style() {
167 wp_enqueue_style( 'simple-payments-widget-style', plugins_url( 'simple-payments/style.css', __FILE__ ), array(), '20180518' );
168 }
169
170 /**
171 * Enqueue admin styles.
172 */
173 public function admin_enqueue_styles() {
174 wp_enqueue_style(
175 'simple-payments-widget-customizer',
176 plugins_url( 'simple-payments/customizer.css', __FILE__ ),
177 array(),
178 $this->package_version
179 );
180 }
181
182 /**
183 * Enqueue admin scripts.
184 */
185 public function admin_enqueue_scripts() {
186 wp_enqueue_media();
187 wp_enqueue_script(
188 'simple-payments-widget-customizer',
189 plugins_url( '/simple-payments/customizer.js', __FILE__ ),
190 array( 'jquery' ),
191 $this->package_version,
192 true
193 );
194 wp_localize_script(
195 'simple-payments-widget-customizer',
196 'jpSimplePaymentsStrings',
197 array(
198 'deleteConfirmation' => __( 'Are you sure you want to delete this item? It will be disabled and removed from all locations where it currently appears.', 'jetpack-paypal-payments' ),
199 )
200 );
201 }
202
203 /**
204 * Get payment buttons.
205 */
206 public function ajax_get_payment_buttons() {
207 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
208 wp_send_json_error( 'bad_nonce', 400, JSON_UNESCAPED_SLASHES );
209 }
210
211 if ( ! current_user_can( 'customize' ) ) {
212 wp_send_json_error( 'customize_not_allowed', 403, JSON_UNESCAPED_SLASHES );
213 }
214
215 $post_type_object = get_post_type_object( Simple_Payments::$post_type_product );
216 if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
217 wp_send_json_error( 'insufficient_post_permissions', 403, JSON_UNESCAPED_SLASHES );
218 }
219
220 $product_posts = get_posts(
221 array(
222 'numberposts' => 100,
223 'orderby' => 'date',
224 'post_type' => Simple_Payments::$post_type_product,
225 'post_status' => 'publish',
226 )
227 );
228
229 $formatted_products = array_map( array( $this, 'format_product_post_for_ajax_reponse' ), $product_posts );
230
231 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
232 wp_send_json_success( $formatted_products, null, JSON_UNESCAPED_SLASHES );
233 }
234
235 /**
236 * Format product_post object.
237 *
238 * @param object $product_post - info about the post the product is on.
239 */
240 public function format_product_post_for_ajax_reponse( $product_post ) {
241 return array(
242 'ID' => $product_post->ID,
243 'post_title' => $product_post->post_title,
244 );
245 }
246
247 /**
248 * Handle saving the simple payments widget.
249 */
250 public function ajax_save_payment_button() {
251 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
252 wp_send_json_error( 'bad_nonce', 400, JSON_UNESCAPED_SLASHES );
253 }
254
255 if ( ! current_user_can( 'customize' ) ) {
256 wp_send_json_error( 'customize_not_allowed', 403, JSON_UNESCAPED_SLASHES );
257 }
258
259 $post_type_object = get_post_type_object( Simple_Payments::$post_type_product );
260 if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
261 wp_send_json_error( 'insufficient_post_permissions', 403, JSON_UNESCAPED_SLASHES );
262 }
263
264 if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
265 wp_send_json_error( 'missing_params', 400, JSON_UNESCAPED_SLASHES );
266 }
267
268 $params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated by validate_ajax_params().
269 $errors = $this->validate_ajax_params( $params );
270 if ( ! empty( $errors->errors ) ) {
271 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
272 wp_send_json_error( $errors, null, JSON_UNESCAPED_SLASHES );
273 }
274
275 $product_post_id = isset( $params['product_post_id'] ) ? (int) $params['product_post_id'] : 0;
276
277 $product_post = array(
278 'ID' => $product_post_id,
279 'post_type' => Simple_Payments::$post_type_product,
280 'post_status' => 'publish',
281 'post_title' => $params['post_title'],
282 'post_content' => $params['post_content'],
283 '_thumbnail_id' => ! empty( $params['image_id'] ) ? $params['image_id'] : -1,
284 'meta_input' => array(
285 'spay_currency' => $params['currency'],
286 'spay_price' => $params['price'],
287 'spay_multiple' => isset( $params['multiple'] ) ? (int) $params['multiple'] : 0,
288 'spay_email' => is_email( $params['email'] ),
289 ),
290 );
291
292 if ( empty( $product_post_id ) ) {
293 $product_post_id = wp_insert_post( $product_post );
294 } else {
295 $product_post_id = wp_update_post( $product_post );
296 }
297
298 if ( ! $product_post_id || is_wp_error( $product_post_id ) ) {
299 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
300 wp_send_json_error( $product_post_id, null, JSON_UNESCAPED_SLASHES );
301 }
302
303 $tracks_properties = array(
304 'id' => $product_post_id,
305 'currency' => $params['currency'],
306 'price' => $params['price'],
307 );
308 if ( 0 === $product_post['ID'] ) {
309 $this->record_event( 'created', 'create', $tracks_properties );
310 } else {
311 $this->record_event( 'updated', 'update', $tracks_properties );
312 }
313
314 wp_send_json_success(
315 array(
316 'product_post_id' => $product_post_id,
317 'product_post_title' => $params['post_title'],
318 ),
319 null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
320 JSON_UNESCAPED_SLASHES
321 );
322 }
323
324 /**
325 * Handle deleting the simple payment widget.
326 */
327 public function ajax_delete_payment_button() {
328 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
329 wp_send_json_error( 'bad_nonce', 400, JSON_UNESCAPED_SLASHES );
330 }
331
332 if ( ! current_user_can( 'customize' ) ) {
333 wp_send_json_error( 'customize_not_allowed', 403, JSON_UNESCAPED_SLASHES );
334 }
335
336 if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
337 wp_send_json_error( 'missing_params', 400, JSON_UNESCAPED_SLASHES );
338 }
339
340 $params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated just below.
341 $illegal_params = array_diff( array_keys( $params ), array( 'product_post_id' ) );
342 if ( ! empty( $illegal_params ) ) {
343 wp_send_json_error( 'illegal_params', 400, JSON_UNESCAPED_SLASHES );
344 }
345
346 $product_id = (int) $params['product_post_id'];
347 $product_post = get_post( $product_id );
348
349 $return = array( 'status' => $product_post->post_status );
350
351 wp_delete_post( $product_id, true );
352 $status = get_post_status( $product_id );
353 if ( false === $status ) {
354 $return['status'] = 'deleted';
355 }
356
357 $this->record_event( 'deleted', 'delete', array( 'id' => $product_id ) );
358
359 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
360 wp_send_json_success( $return, null, JSON_UNESCAPED_SLASHES );
361 }
362
363 /**
364 * Returns the number of decimal places on string representing a price.
365 *
366 * @param string $number Price to check.
367 * @return int|null number of decimal places.
368 */
369 private function get_decimal_places( $number ) {
370 $parts = explode( '.', $number );
371 if ( count( $parts ) > 2 ) {
372 return null;
373 }
374
375 return isset( $parts[1] ) ? strlen( $parts[1] ) : 0;
376 }
377
378 /**
379 * Validate ajax parameters.
380 *
381 * @param array $params - the parameters.
382 */
383 public function validate_ajax_params( $params ) {
384 $errors = new WP_Error();
385
386 $illegal_params = array_diff( array_keys( $params ), array( 'product_post_id', 'post_title', 'post_content', 'image_id', 'currency', 'price', 'multiple', 'email' ) );
387 if ( ! empty( $illegal_params ) ) {
388 $errors->add( 'illegal_params', __( 'Invalid parameters.', 'jetpack-paypal-payments' ) );
389 }
390
391 if ( empty( $params['post_title'] ) ) {
392 $errors->add( 'post_title', __( "People need to know what they're paying for! Please add a brief title.", 'jetpack-paypal-payments' ) );
393 }
394
395 if ( empty( $params['price'] ) || ! is_numeric( $params['price'] ) || (float) $params['price'] <= 0 ) {
396 $errors->add( 'price', __( 'Everything comes with a price tag these days. Please add a your product price.', 'jetpack-paypal-payments' ) );
397 }
398
399 // Japan's Yen is the only supported currency with a zero decimal precision.
400 $precision = strtoupper( $params['currency'] ) === 'JPY' ? 0 : 2;
401 $price_decimal_places = $this->get_decimal_places( $params['price'] );
402 if ( $price_decimal_places === null || $price_decimal_places > $precision ) {
403 $errors->add( 'price', __( 'Invalid price', 'jetpack-paypal-payments' ) );
404 }
405
406 if ( empty( $params['email'] ) || ! is_email( $params['email'] ) ) {
407 $errors->add( 'email', __( 'We want to make sure payments reach you, so please add an email address.', 'jetpack-paypal-payments' ) );
408 }
409
410 return $errors;
411 }
412
413 /**
414 * Get the id of the first product.
415 */
416 public function get_first_product_id() {
417 $product_posts = get_posts(
418 array(
419 'numberposts' => 1,
420 'orderby' => 'date',
421 'post_type' => Simple_Payments::$post_type_product,
422 'post_status' => 'publish',
423 )
424 );
425
426 return ! empty( $product_posts ) ? $product_posts[0]->ID : null;
427 }
428
429 /**
430 * Front-end display of widget.
431 *
432 * @see WP_Widget::widget()
433 *
434 * @html-template-var array $instance
435 *
436 * @param array $args Widget arguments.
437 * @param array $instance Saved values from database.
438 */
439 public function widget( $args, $instance ) {
440 $instance = wp_parse_args( $instance, $this->defaults() );
441
442 // Enqueue front end assets.
443 $this->enqueue_style();
444
445 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
446
447 /** This filter is documented in core/src/wp-includes/default-widgets.php */
448 $title = apply_filters( 'widget_title', $instance['title'] );
449 if ( ! empty( $title ) ) {
450 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
451 }
452
453 echo '<div class="jetpack-simple-payments-content">';
454
455 if ( ! empty( $instance['form_action'] ) && in_array( $instance['form_action'], array( 'add', 'edit' ), true ) && is_customize_preview() ) {
456 require __DIR__ . '/simple-payments/widget.php';
457 } else {
458 $jsp = Simple_Payments::get_instance();
459 $simple_payments_button = $jsp->parse_shortcode(
460 array(
461 'id' => $instance['product_post_id'],
462 )
463 );
464
465 if ( $simple_payments_button !== null || is_customize_preview() ) {
466 echo $simple_payments_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
467 }
468 }
469
470 echo '</div><!--simple-payments-->';
471
472 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
473
474 /** This action is already documented in modules/widgets/gravatar-profile.php */
475 do_action( 'jetpack_stats_extra', 'widget_view', 'simple_payments' );
476 }
477
478 /**
479 * Gets the latests field value from either the old instance or the new instance.
480 *
481 * @param array $new_instance mixed Array of values for the new form instance.
482 * @param array $old_instance mixed Array of values for the old form instance.
483 * @param mixed $field mixed Field value.
484 */
485 private function get_latest_field_value( $new_instance, $old_instance, $field ) {
486 return ! empty( $new_instance[ $field ] )
487 ? sanitize_text_field( $new_instance[ $field ] )
488 : $old_instance[ $field ];
489 }
490
491 /**
492 * Gets the product fields from the product post. If no post found
493 * it returns the default values.
494 *
495 * @param int $product_post_id Product Post ID.
496 * @return array $fields Product Fields from the Product Post.
497 */
498 private function get_product_from_post( $product_post_id ) {
499 $product_post = get_post( $product_post_id );
500 $form_product_id = $product_post_id;
501 if ( ! empty( $product_post ) ) {
502 $form_product_image_id = get_post_thumbnail_id( $product_post_id );
503
504 return array(
505 'form_product_id' => $form_product_id,
506 'form_product_title' => get_the_title( $product_post ),
507 'form_product_description' => $product_post->post_content,
508 'form_product_image_id' => $form_product_image_id,
509 'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
510 'form_product_currency' => get_post_meta( $product_post_id, 'spay_currency', true ),
511 'form_product_price' => get_post_meta( $product_post_id, 'spay_price', true ),
512 'form_product_multiple' => get_post_meta( $product_post_id, 'spay_multiple', true ) || '0',
513 'form_product_email' => get_post_meta( $product_post_id, 'spay_email', true ),
514 );
515 }
516
517 return $this->defaults();
518 }
519
520 /**
521 * Record a Track event and bump a MC stat.
522 *
523 * @param string $stat_name - the name of the stat.
524 * @param string $event_action - the action we're recording.
525 * @param array $event_properties - proprties of the event.
526 */
527 private function record_event( $stat_name, $event_action, $event_properties = array() ) {
528 $current_user = wp_get_current_user();
529
530 // `bumps_stats_extra` only exists on .com
531 if ( function_exists( 'bump_stats_extras' ) && function_exists( 'require_lib' ) ) {
532 require_lib( 'tracks/client' );
533 tracks_record_event( $current_user, 'simple_payments_button_' . $event_action, $event_properties );
534 /** This action is documented in modules/widgets/social-media-icons.php */
535 do_action( 'jetpack_bump_stats_extra', 'simple_payments', $stat_name );
536 return;
537 }
538
539 $tracking = new Tracking();
540 $tracking->tracks_record_event( $current_user, 'jetpack_wpa_simple_payments_button_' . $event_action, $event_properties );
541
542 if ( class_exists( 'Jetpack' ) ) {
543 $jetpack = Jetpack::init();
544 // $jetpack->stat automatically prepends the stat group with 'jetpack-'
545 $jetpack->stat( 'simple_payments', $stat_name );
546 $jetpack->do_stats( 'server_side' );
547 }
548 }
549
550 /**
551 * Sanitize widget form values as they are saved.
552 *
553 * @see WP_Widget::update()
554 *
555 * @param array $new_instance Values just sent to be saved.
556 * @param array $old_instance Previously saved values from database.
557 *
558 * @return array Updated safe values to be saved.
559 */
560 public function update( $new_instance, $old_instance ) {
561 $defaults = $this->defaults();
562 // do not overrite `product_post_id` for `$new_instance` with the defaults.
563 $new_instance = wp_parse_args( $new_instance, array_diff_key( $defaults, array( 'product_post_id' => 0 ) ) );
564 $old_instance = wp_parse_args( $old_instance, $defaults );
565
566 $required_widget_props = array(
567 'title' => $this->get_latest_field_value( $new_instance, $old_instance, 'title' ),
568 'product_post_id' => $this->get_latest_field_value( $new_instance, $old_instance, 'product_post_id' ),
569 'form_action' => $this->get_latest_field_value( $new_instance, $old_instance, 'form_action' ),
570 );
571
572 if ( strcmp( $new_instance['form_action'], $old_instance['form_action'] ) !== 0 ) {
573 if ( 'edit' === $new_instance['form_action'] ) {
574 return array_merge( $this->get_product_from_post( (int) $old_instance['product_post_id'] ), $required_widget_props );
575 }
576
577 if ( 'clear' === $new_instance['form_action'] ) {
578 return array_merge( $this->defaults(), $required_widget_props );
579 }
580 }
581
582 $form_product_image_id = (int) $new_instance['form_product_image_id'];
583
584 $form_product_email = ! empty( $new_instance['form_product_email'] )
585 ? sanitize_text_field( $new_instance['form_product_email'] )
586 : $defaults['form_product_email'];
587
588 return array_merge(
589 $required_widget_props,
590 array(
591 'form_product_id' => (int) $new_instance['form_product_id'],
592 'form_product_title' => sanitize_text_field( $new_instance['form_product_title'] ),
593 'form_product_description' => sanitize_text_field( $new_instance['form_product_description'] ),
594 'form_product_image_id' => $form_product_image_id,
595 'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
596 'form_product_currency' => sanitize_text_field( $new_instance['form_product_currency'] ),
597 'form_product_price' => sanitize_text_field( $new_instance['form_product_price'] ),
598 'form_product_multiple' => sanitize_text_field( $new_instance['form_product_multiple'] ),
599 'form_product_email' => $form_product_email,
600 )
601 );
602 }
603
604 /**
605 * Back-end widget form.
606 *
607 * @see WP_Widget::form()
608 *
609 * @html-template-var array $instance
610 * @html-template-var WP_Post[] $product_posts
611 *
612 * @param array $instance Previously saved values from database.
613 * @return string|void
614 */
615 public function form( $instance ) {
616 if ( ! Simple_Payments::is_enabled_jetpack_simple_payments() ) {
617 require __DIR__ . '/simple-payments/admin-warning.php';
618 return;
619 }
620
621 $instance = wp_parse_args( $instance, $this->defaults() );
622
623 $product_posts = get_posts( // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
624 array(
625 'numberposts' => 100,
626 'orderby' => 'date',
627 'post_type' => Simple_Payments::$post_type_product,
628 'post_status' => 'publish',
629 )
630 );
631
632 require __DIR__ . '/simple-payments/form.php';
633 }
634 }
635 }
636