PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.4.2
Jetpack – WP Security, Backup, Speed, & Growth v14.4.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 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 / modules / widgets / simple-payments.php
jetpack / modules / widgets Last commit date
authors 8 years ago contact-info 5 years ago eu-cookie-law 1 year ago facebook-likebox 11 years ago flickr 2 years ago gallery 1 year ago goodreads 2 years ago google-translate 1 year ago image-widget 8 years ago instagram 6 years ago internet-defense-league 2 years ago milestone 1 year ago my-community 8 years ago simple-payments 1 year ago social-icons 1 year ago social-media-icons 5 years ago top-posts 8 years ago wordpress-post-widget 2 years ago authors.php 1 year ago blog-stats.php 1 year ago class-jetpack-eu-cookie-law-widget.php 1 year ago class-jetpack-instagram-widget.php 1 year ago contact-info.php 1 year ago customizer-controls.css 9 years ago customizer-utils.js 1 year ago facebook-likebox.php 2 years ago flickr.php 1 year ago gallery.php 1 year ago goodreads.php 1 year ago google-translate.php 1 year ago gravatar-profile.css 1 year ago gravatar-profile.php 1 year ago image-widget.php 1 year ago internet-defense-league.php 3 years ago mailchimp.php 3 years ago milestone.php 5 years ago my-community.php 1 year ago rsslinks-widget.php 3 years ago simple-payments.php 1 year ago social-icons.php 1 year ago social-media-icons.php 1 year ago top-posts.php 1 year ago twitter-timeline-admin.js 1 year ago twitter-timeline.php 3 years ago upcoming-events.php 2 years ago wordpress-post-widget.php 1 year ago
simple-payments.php
629 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
4
5 use Automattic\Jetpack\Tracking;
6
7 // Disable direct access/execution to/of the widget code.
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit( 0 );
10 }
11
12 if ( ! class_exists( 'Jetpack_Simple_Payments_Widget' ) ) {
13 /**
14 * Pay with PayPal (aka Simple Payments)
15 *
16 * Display a Pay with PayPal button as a Widget.
17 */
18 class Jetpack_Simple_Payments_Widget extends WP_Widget {
19 /**
20 * Currencies should be supported by PayPal:
21 *
22 * @var array $supported_currency_list
23 * @link https://developer.paypal.com/docs/api/reference/currency-codes/
24 *
25 * List has to be in sync with list at the block's client side and API's backend side:
26 * @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/extensions/blocks/simple-payments/constants.js#L9-L39
27 * @link https://github.com/Automattic/jetpack/blob/31efa189ad223c0eb7ad085ac0650a23facf9ef5/modules/simple-payments/simple-payments.php#L386-L415
28 *
29 * Indian Rupee (INR) is listed here for backwards compatibility with previously added widgets.
30 * It's not supported by Pay with PayPal because at the time of the creation of this file
31 * because it's limited to in-country PayPal India accounts only.
32 * Discussion: https://github.com/Automattic/wp-calypso/pull/28236
33 */
34 private static $supported_currency_list = array(
35 'USD' => '$',
36 'GBP' => '&#163;',
37 'JPY' => '&#165;',
38 'BRL' => 'R$',
39 'EUR' => '&#8364;',
40 'NZD' => 'NZ$',
41 'AUD' => 'A$',
42 'CAD' => 'C$',
43 'INR' => '',
44 'ILS' => '',
45 'RUB' => '',
46 'MXN' => 'MX$',
47 'SEK' => 'Skr',
48 'HUF' => 'Ft',
49 'CHF' => 'CHF',
50 'CZK' => '',
51 'DKK' => 'Dkr',
52 'HKD' => 'HK$',
53 'NOK' => 'Kr',
54 'PHP' => '',
55 'PLN' => 'PLN',
56 'SGD' => 'S$',
57 'TWD' => 'NT$',
58 'THB' => '฿',
59 );
60
61 /**
62 * Constructor.
63 */
64 public function __construct() {
65 parent::__construct(
66 'jetpack_simple_payments_widget',
67 /** This filter is documented in modules/widgets/facebook-likebox.php */
68 apply_filters( 'jetpack_widget_name', __( 'Pay with PayPal', 'jetpack' ) ),
69 array(
70 'classname' => 'jetpack-simple-payments',
71 'description' => __( 'Add a Pay with PayPal button as a Widget.', 'jetpack' ),
72 'customize_selective_refresh' => true,
73 )
74 );
75
76 global $pagenow;
77 if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
78 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
79 }
80
81 $jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
82 if ( is_customize_preview() && $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
83 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
84
85 add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
86 add_action( 'wp_ajax_customize-jetpack-simple-payments-buttons-get', array( $this, 'ajax_get_payment_buttons' ) );
87 add_action( 'wp_ajax_customize-jetpack-simple-payments-button-save', array( $this, 'ajax_save_payment_button' ) );
88 add_action( 'wp_ajax_customize-jetpack-simple-payments-button-delete', array( $this, 'ajax_delete_payment_button' ) );
89 }
90
91 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_simple_payment_widget' ) );
92 }
93
94 /**
95 * Return an array of the widgets hidden from the Legacy Widget block.
96 *
97 * This is used to hide the Pay with PayPal from the Legacy Widget block.
98 *
99 * @param array $widget_types the widget types that are currently hidden.
100 * @return array Widget types hidden from the Legacy Widget block
101 */
102 public function hide_simple_payment_widget( $widget_types ) {
103 $widget_types[] = 'jetpack_simple_payments_widget';
104 return $widget_types;
105 }
106
107 /**
108 * Return an associative array of default values.
109 *
110 * These values are used in new widgets.
111 *
112 * @return array Default values for the widget options.
113 */
114 private function defaults() {
115 $current_user = wp_get_current_user();
116 $default_product_id = $this->get_first_product_id();
117
118 return array(
119 'title' => '',
120 'product_post_id' => $default_product_id,
121 'form_action' => '',
122 'form_product_id' => 0,
123 'form_product_title' => '',
124 'form_product_description' => '',
125 'form_product_image_id' => 0,
126 'form_product_image_src' => '',
127 'form_product_currency' => '',
128 'form_product_price' => '',
129 'form_product_multiple' => '',
130 'form_product_email' => $current_user->user_email,
131 );
132 }
133
134 /**
135 * Adds a nonce for customizing menus.
136 *
137 * @param array $nonces Array of nonces.
138 * @return array $nonces Modified array of nonces.
139 */
140 public function filter_nonces( $nonces ) {
141 $nonces['customize-jetpack-simple-payments'] = wp_create_nonce( 'customize-jetpack-simple-payments' );
142 return $nonces;
143 }
144
145 /**
146 * Enqueue styles.
147 */
148 public function enqueue_style() {
149 wp_enqueue_style( 'jetpack-simple-payments-widget-style', plugins_url( 'simple-payments/style.css', __FILE__ ), array(), '20180518' );
150 }
151
152 /**
153 * Enqueue admin styles.
154 */
155 public function admin_enqueue_styles() {
156 wp_enqueue_style(
157 'jetpack-simple-payments-widget-customizer',
158 plugins_url( 'simple-payments/customizer.css', __FILE__ ),
159 array(),
160 JETPACK__VERSION
161 );
162 }
163
164 /**
165 * Enqueue admin scripts.
166 */
167 public function admin_enqueue_scripts() {
168 wp_enqueue_media();
169 wp_enqueue_script(
170 'jetpack-simple-payments-widget-customizer',
171 plugins_url( '/simple-payments/customizer.js', __FILE__ ),
172 array( 'jquery' ),
173 JETPACK__VERSION,
174 true
175 );
176 wp_localize_script(
177 'jetpack-simple-payments-widget-customizer',
178 'jpSimplePaymentsStrings',
179 array(
180 'deleteConfirmation' => __( 'Are you sure you want to delete this item? It will be disabled and removed from all locations where it currently appears.', 'jetpack' ),
181 )
182 );
183 }
184
185 /**
186 * Get payment buttons.
187 */
188 public function ajax_get_payment_buttons() {
189 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
190 wp_send_json_error( 'bad_nonce', 400 );
191 }
192
193 if ( ! current_user_can( 'customize' ) ) {
194 wp_send_json_error( 'customize_not_allowed', 403 );
195 }
196
197 $post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
198 if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
199 wp_send_json_error( 'insufficient_post_permissions', 403 );
200 }
201
202 $product_posts = get_posts(
203 array(
204 'numberposts' => 100,
205 'orderby' => 'date',
206 'post_type' => Jetpack_Simple_Payments::$post_type_product,
207 'post_status' => 'publish',
208 )
209 );
210
211 $formatted_products = array_map( array( $this, 'format_product_post_for_ajax_reponse' ), $product_posts );
212
213 wp_send_json_success( $formatted_products );
214 }
215
216 /**
217 * Format product_post object.
218 *
219 * @param object $product_post - info about the post the product is on.
220 */
221 public function format_product_post_for_ajax_reponse( $product_post ) {
222 return array(
223 'ID' => $product_post->ID,
224 'post_title' => $product_post->post_title,
225 );
226 }
227
228 /**
229 * Handle saving the simple payments widget.
230 */
231 public function ajax_save_payment_button() {
232 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
233 wp_send_json_error( 'bad_nonce', 400 );
234 }
235
236 if ( ! current_user_can( 'customize' ) ) {
237 wp_send_json_error( 'customize_not_allowed', 403 );
238 }
239
240 $post_type_object = get_post_type_object( Jetpack_Simple_Payments::$post_type_product );
241 if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
242 wp_send_json_error( 'insufficient_post_permissions', 403 );
243 }
244
245 if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
246 wp_send_json_error( 'missing_params', 400 );
247 }
248
249 $params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated by validate_ajax_params().
250 $errors = $this->validate_ajax_params( $params );
251 if ( ! empty( $errors->errors ) ) {
252 wp_send_json_error( $errors );
253 }
254
255 $product_post_id = isset( $params['product_post_id'] ) ? (int) $params['product_post_id'] : 0;
256
257 $product_post = array(
258 'ID' => $product_post_id,
259 'post_type' => Jetpack_Simple_Payments::$post_type_product,
260 'post_status' => 'publish',
261 'post_title' => $params['post_title'],
262 'post_content' => $params['post_content'],
263 '_thumbnail_id' => ! empty( $params['image_id'] ) ? $params['image_id'] : -1,
264 'meta_input' => array(
265 'spay_currency' => $params['currency'],
266 'spay_price' => $params['price'],
267 'spay_multiple' => isset( $params['multiple'] ) ? (int) $params['multiple'] : 0,
268 'spay_email' => is_email( $params['email'] ),
269 ),
270 );
271
272 if ( empty( $product_post_id ) ) {
273 $product_post_id = wp_insert_post( $product_post );
274 } else {
275 $product_post_id = wp_update_post( $product_post );
276 }
277
278 if ( ! $product_post_id || is_wp_error( $product_post_id ) ) {
279 wp_send_json_error( $product_post_id );
280 }
281
282 $tracks_properties = array(
283 'id' => $product_post_id,
284 'currency' => $params['currency'],
285 'price' => $params['price'],
286 );
287 if ( 0 === $product_post['ID'] ) {
288 $this->record_event( 'created', 'create', $tracks_properties );
289 } else {
290 $this->record_event( 'updated', 'update', $tracks_properties );
291 }
292
293 wp_send_json_success(
294 array(
295 'product_post_id' => $product_post_id,
296 'product_post_title' => $params['post_title'],
297 )
298 );
299 }
300
301 /**
302 * Handle deleting the simple payment widget.
303 */
304 public function ajax_delete_payment_button() {
305 if ( ! check_ajax_referer( 'customize-jetpack-simple-payments', 'customize-jetpack-simple-payments-nonce', false ) ) {
306 wp_send_json_error( 'bad_nonce', 400 );
307 }
308
309 if ( ! current_user_can( 'customize' ) ) {
310 wp_send_json_error( 'customize_not_allowed', 403 );
311 }
312
313 if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
314 wp_send_json_error( 'missing_params', 400 );
315 }
316
317 $params = wp_unslash( $_POST['params'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Manually validated just below.
318 $illegal_params = array_diff( array_keys( $params ), array( 'product_post_id' ) );
319 if ( ! empty( $illegal_params ) ) {
320 wp_send_json_error( 'illegal_params', 400 );
321 }
322
323 $product_id = (int) $params['product_post_id'];
324 $product_post = get_post( $product_id );
325
326 $return = array( 'status' => $product_post->post_status );
327
328 wp_delete_post( $product_id, true );
329 $status = get_post_status( $product_id );
330 if ( false === $status ) {
331 $return['status'] = 'deleted';
332 }
333
334 $this->record_event( 'deleted', 'delete', array( 'id' => $product_id ) );
335
336 wp_send_json_success( $return );
337 }
338
339 /**
340 * Returns the number of decimal places on string representing a price.
341 *
342 * @param string $number Price to check.
343 * @return int|null number of decimal places.
344 */
345 private function get_decimal_places( $number ) {
346 $parts = explode( '.', $number );
347 if ( count( $parts ) > 2 ) {
348 return null;
349 }
350
351 return isset( $parts[1] ) ? strlen( $parts[1] ) : 0;
352 }
353
354 /**
355 * Validate ajax parameters.
356 *
357 * @param array $params - the parameters.
358 */
359 public function validate_ajax_params( $params ) {
360 $errors = new WP_Error();
361
362 $illegal_params = array_diff( array_keys( $params ), array( 'product_post_id', 'post_title', 'post_content', 'image_id', 'currency', 'price', 'multiple', 'email' ) );
363 if ( ! empty( $illegal_params ) ) {
364 $errors->add( 'illegal_params', __( 'Invalid parameters.', 'jetpack' ) );
365 }
366
367 if ( empty( $params['post_title'] ) ) {
368 $errors->add( 'post_title', __( "People need to know what they're paying for! Please add a brief title.", 'jetpack' ) );
369 }
370
371 if ( empty( $params['price'] ) || ! is_numeric( $params['price'] ) || (float) $params['price'] <= 0 ) {
372 $errors->add( 'price', __( 'Everything comes with a price tag these days. Please add a your product price.', 'jetpack' ) );
373 }
374
375 // Japan's Yen is the only supported currency with a zero decimal precision.
376 $precision = strtoupper( $params['currency'] ) === 'JPY' ? 0 : 2;
377 $price_decimal_places = $this->get_decimal_places( $params['price'] );
378 if ( $price_decimal_places === null || $price_decimal_places > $precision ) {
379 $errors->add( 'price', __( 'Invalid price', 'jetpack' ) );
380 }
381
382 if ( empty( $params['email'] ) || ! is_email( $params['email'] ) ) {
383 $errors->add( 'email', __( 'We want to make sure payments reach you, so please add an email address.', 'jetpack' ) );
384 }
385
386 return $errors;
387 }
388
389 /**
390 * Get the id of the first product.
391 */
392 public function get_first_product_id() {
393 $product_posts = get_posts(
394 array(
395 'numberposts' => 1,
396 'orderby' => 'date',
397 'post_type' => Jetpack_Simple_Payments::$post_type_product,
398 'post_status' => 'publish',
399 )
400 );
401
402 return ! empty( $product_posts ) ? $product_posts[0]->ID : null;
403 }
404
405 /**
406 * Front-end display of widget.
407 *
408 * @see WP_Widget::widget()
409 *
410 * @html-template-var array $instance
411 *
412 * @param array $args Widget arguments.
413 * @param array $instance Saved values from database.
414 */
415 public function widget( $args, $instance ) {
416 $instance = wp_parse_args( $instance, $this->defaults() );
417
418 // Enqueue front end assets.
419 $this->enqueue_style();
420
421 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
422
423 /** This filter is documented in core/src/wp-includes/default-widgets.php */
424 $title = apply_filters( 'widget_title', $instance['title'] );
425 if ( ! empty( $title ) ) {
426 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
427 }
428
429 echo '<div class="jetpack-simple-payments-content">';
430
431 if ( ! empty( $instance['form_action'] ) && in_array( $instance['form_action'], array( 'add', 'edit' ), true ) && is_customize_preview() ) {
432 require __DIR__ . '/simple-payments/widget.php';
433 } else {
434 $jsp = Jetpack_Simple_Payments::get_instance();
435 $simple_payments_button = $jsp->parse_shortcode(
436 array(
437 'id' => $instance['product_post_id'],
438 )
439 );
440
441 if ( $simple_payments_button !== null || is_customize_preview() ) {
442 echo $simple_payments_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
443 }
444 }
445
446 echo '</div><!--simple-payments-->';
447
448 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
449
450 /** This action is already documented in modules/widgets/gravatar-profile.php */
451 do_action( 'jetpack_stats_extra', 'widget_view', 'simple_payments' );
452 }
453
454 /**
455 * Gets the latests field value from either the old instance or the new instance.
456 *
457 * @param array $new_instance mixed Array of values for the new form instance.
458 * @param array $old_instance mixed Array of values for the old form instance.
459 * @param mixed $field mixed Field value.
460 */
461 private function get_latest_field_value( $new_instance, $old_instance, $field ) {
462 return ! empty( $new_instance[ $field ] )
463 ? sanitize_text_field( $new_instance[ $field ] )
464 : $old_instance[ $field ];
465 }
466
467 /**
468 * Gets the product fields from the product post. If no post found
469 * it returns the default values.
470 *
471 * @param int $product_post_id Product Post ID.
472 * @return array $fields Product Fields from the Product Post.
473 */
474 private function get_product_from_post( $product_post_id ) {
475 $product_post = get_post( $product_post_id );
476 $form_product_id = $product_post_id;
477 if ( ! empty( $product_post ) ) {
478 $form_product_image_id = get_post_thumbnail_id( $product_post_id );
479
480 return array(
481 'form_product_id' => $form_product_id,
482 'form_product_title' => get_the_title( $product_post ),
483 'form_product_description' => $product_post->post_content,
484 'form_product_image_id' => $form_product_image_id,
485 'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
486 'form_product_currency' => get_post_meta( $product_post_id, 'spay_currency', true ),
487 'form_product_price' => get_post_meta( $product_post_id, 'spay_price', true ),
488 'form_product_multiple' => get_post_meta( $product_post_id, 'spay_multiple', true ) || '0',
489 'form_product_email' => get_post_meta( $product_post_id, 'spay_email', true ),
490 );
491 }
492
493 return $this->defaults();
494 }
495
496 /**
497 * Record a Track event and bump a MC stat.
498 *
499 * @param string $stat_name - the name of the stat.
500 * @param string $event_action - the action we're recording.
501 * @param array $event_properties - proprties of the event.
502 */
503 private function record_event( $stat_name, $event_action, $event_properties = array() ) {
504 $current_user = wp_get_current_user();
505
506 // `bumps_stats_extra` only exists on .com
507 if ( function_exists( 'bump_stats_extras' ) ) {
508 require_lib( 'tracks/client' );
509 tracks_record_event( $current_user, 'simple_payments_button_' . $event_action, $event_properties );
510 /** This action is documented in modules/widgets/social-media-icons.php */
511 do_action( 'jetpack_bump_stats_extra', 'jetpack-simple_payments', $stat_name );
512 return;
513 }
514
515 $tracking = new Tracking();
516 $tracking->tracks_record_event( $current_user, 'jetpack_wpa_simple_payments_button_' . $event_action, $event_properties );
517 $jetpack = Jetpack::init();
518 // $jetpack->stat automatically prepends the stat group with 'jetpack-'
519 $jetpack->stat( 'simple_payments', $stat_name );
520 $jetpack->do_stats( 'server_side' );
521 }
522
523 /**
524 * Sanitize widget form values as they are saved.
525 *
526 * @see WP_Widget::update()
527 *
528 * @param array $new_instance Values just sent to be saved.
529 * @param array $old_instance Previously saved values from database.
530 *
531 * @return array Updated safe values to be saved.
532 */
533 public function update( $new_instance, $old_instance ) {
534 $defaults = $this->defaults();
535 // do not overrite `product_post_id` for `$new_instance` with the defaults.
536 $new_instance = wp_parse_args( $new_instance, array_diff_key( $defaults, array( 'product_post_id' => 0 ) ) );
537 $old_instance = wp_parse_args( $old_instance, $defaults );
538
539 $required_widget_props = array(
540 'title' => $this->get_latest_field_value( $new_instance, $old_instance, 'title' ),
541 'product_post_id' => $this->get_latest_field_value( $new_instance, $old_instance, 'product_post_id' ),
542 'form_action' => $this->get_latest_field_value( $new_instance, $old_instance, 'form_action' ),
543 );
544
545 if ( strcmp( $new_instance['form_action'], $old_instance['form_action'] ) !== 0 ) {
546 if ( 'edit' === $new_instance['form_action'] ) {
547 return array_merge( $this->get_product_from_post( (int) $old_instance['product_post_id'] ), $required_widget_props );
548 }
549
550 if ( 'clear' === $new_instance['form_action'] ) {
551 return array_merge( $this->defaults(), $required_widget_props );
552 }
553 }
554
555 $form_product_image_id = (int) $new_instance['form_product_image_id'];
556
557 $form_product_email = ! empty( $new_instance['form_product_email'] )
558 ? sanitize_text_field( $new_instance['form_product_email'] )
559 : $defaults['form_product_email'];
560
561 return array_merge(
562 $required_widget_props,
563 array(
564 'form_product_id' => (int) $new_instance['form_product_id'],
565 'form_product_title' => sanitize_text_field( $new_instance['form_product_title'] ),
566 'form_product_description' => sanitize_text_field( $new_instance['form_product_description'] ),
567 'form_product_image_id' => $form_product_image_id,
568 'form_product_image_src' => wp_get_attachment_image_url( $form_product_image_id, 'thumbnail' ),
569 'form_product_currency' => sanitize_text_field( $new_instance['form_product_currency'] ),
570 'form_product_price' => sanitize_text_field( $new_instance['form_product_price'] ),
571 'form_product_multiple' => sanitize_text_field( $new_instance['form_product_multiple'] ),
572 'form_product_email' => $form_product_email,
573 )
574 );
575 }
576
577 /**
578 * Back-end widget form.
579 *
580 * @see WP_Widget::form()
581 *
582 * @html-template-var array $instance
583 * @html-template-var WP_Post[] $product_posts
584 *
585 * @param array $instance Previously saved values from database.
586 */
587 public function form( $instance ) {
588 $jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
589 if ( ! method_exists( $jetpack_simple_payments, 'is_enabled_jetpack_simple_payments' ) ) {
590 return;
591 }
592 if ( ! $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
593 require __DIR__ . '/simple-payments/admin-warning.php';
594 return;
595 }
596
597 $instance = wp_parse_args( $instance, $this->defaults() );
598
599 $product_posts = get_posts( // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
600 array(
601 'numberposts' => 100,
602 'orderby' => 'date',
603 'post_type' => Jetpack_Simple_Payments::$post_type_product,
604 'post_status' => 'publish',
605 )
606 );
607
608 require __DIR__ . '/simple-payments/form.php';
609 }
610 }
611
612 /**
613 * Register Jetpack_Simple_Payments_Widget widget.
614 */
615 function register_widget_jetpack_simple_payments() {
616 if ( ! class_exists( 'Jetpack_Simple_Payments' ) ) {
617 return;
618 }
619
620 $jetpack_simple_payments = Jetpack_Simple_Payments::get_instance();
621 if ( ! $jetpack_simple_payments->is_enabled_jetpack_simple_payments() ) {
622 return;
623 }
624
625 register_widget( 'Jetpack_Simple_Payments_Widget' );
626 }
627 add_action( 'widgets_init', 'register_widget_jetpack_simple_payments' );
628 }
629