Completed
Push — gm-17/payment-widget ( 55e70e...003027 )
by
unknown
10:21
created

Simple_Payments_Widget::form()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 39
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 28
nc 6
nop 1
dl 0
loc 39
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/*
3
Plugin Name: Simple Payments
4
Description: Simple Payments button implemented as a widget.
5
Version: 1.0
6
Author: Automattic Inc.
7
Author URI: http://automattic.com/
8
License: GPLv2 or later
9
*/
10
11
function jetpack_register_widget_simple_payments() {
12
	register_widget( 'Simple_Payments_Widget' );
13
}
14
add_action( 'widgets_init', 'jetpack_register_widget_simple_payments' );
15
16
class Simple_Payments_Widget extends WP_Widget {
17
	private static $dir              = null;
18
	private static $url              = null;
19
	private static $product_defaults = null;
20
21
	function __construct() {
22
		$widget = array(
23
			'classname'   => 'simple-payments',
24
			'description' => __( 'Add a simple payment button.', 'jetpack' ),
25
		);
26
27
		parent::__construct(
28
			'Simple_Payments_Widget',
29
			apply_filters( 'jetpack_widget_name', __( 'Simple Payments', 'jetpack' ) ),
30
			$widget
31
		);
32
33
		self::$dir = trailingslashit( dirname( __FILE__ ) );
34
		self::$url = plugin_dir_url( __FILE__ );
35
36
		add_action( 'admin_enqueue_scripts', array( __class__, 'enqueue_admin_styles' ) );
37
		add_action( 'wp_enqueue_scripts', array( __class__, 'enqueue_widget_styles' ) );
38
	}
39
40
	public static function enqueue_admin_styles( $hook_suffix ) {
41
		if ( 'widgets.php' == $hook_suffix ) {
42
			wp_enqueue_style( 'simple-payments-widget-admin', self::$url . '/simple-payments/style-admin.css', array() );
43
			wp_enqueue_media();
44
			wp_enqueue_script( 'simple-payments-widget-admin', self::$url . '/simple-payments/admin.js', array( 'jquery' ), false, true );
45
		}
46
	}
47
48
	public static function enqueue_widget_styles() {
49
		// TODO: This would be nice, but I don't know how this function works.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
50
		// if ( is_active_widget( false, false, $this->id_base, true ) ) {
51
			wp_enqueue_style( 'simple-payments-widget', self::$url . '/simple-payments/style.css', array() );
52
		// }
53
	}
54
55
	protected static function get_product_defaults() {
56
		if ( ! isset( self::$product_defaults ) ) {
57
			$products = get_posts( array(
58
				'numberposts' => 1,
59
				'orderby' => 'date',
60
				'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...
61
			) );
62
			$current_user = wp_get_current_user();
63
			$default_email = $current_user->user_email;
64
			$default_multiple = '0';
65
			$default_currency = 'USD';
66
			// Get the default email, currency and "multiple" flag from the last product created, for user's convenience
67
			if ( ! empty( $products ) ) {
68
				$default_email = get_post_meta( $products[0]->ID, 'spay_email', true );
69
				$default_multiple = get_post_meta( $products[0]->ID, 'spay_multiple', true );
70
				$default_currency = get_post_meta( $products[0]->ID, 'spay_currency', true );
71
			}
72
			self::$product_defaults = array( $default_email, $default_multiple, $default_currency );
73
		}
74
		return self::$product_defaults;
75
	}
76
77
	protected function get_product_args( $product_id ) {
78
		$product = $product_id ? get_post( $product_id ) : null;
79
		$product_args = array();
80
		if ( $product && ! is_wp_error( $product ) && $product->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...
81
			$product_args = array(
82
				'name' => get_the_title( $product ),
83
				'description' => $product->post_content,
84
				'currency' => get_post_meta( $product->ID, 'spay_currency', true ),
85
				'price' => get_post_meta( $product->ID, 'spay_price', true ),
86
				'multiple' => get_post_meta( $product->ID, 'spay_multiple', true ),
87
				'email' => get_post_meta( $product->ID, 'spay_email', true ),
88
			);
89
		}
90
91
		$product_args = wp_parse_args( $product_args, array(
92
			'name' => '',
93
			'description' => '',
94
			'currency' => null,
95
			'price' => '',
96
			'multiple' => null,
97
			'email' => null,
98
		) );
99
100
		if ( isset( $product_args['currency'] ) && isset( $product_args['multiple'] ) && isset( $product_args['email'] ) ) {
101
			// All fields are present, no need to "fill the blanks"
102
			return $product_args;
103
		}
104
105
		list( $default_email, $default_multiple, $default_currency ) = self::get_product_defaults();
106
		if ( ! isset( $product_args['email'] ) ) {
107
			$product_args['email'] = $default_email;
108
		}
109
		if ( ! isset( $product_args['multiple'] ) ) {
110
			$product_args['multiple'] = $default_multiple;
111
		}
112
		if ( ! isset( $product_args['currency'] ) ) {
113
			$product_args['currency'] = $default_currency;
114
		}
115
		return $product_args;
116
	}
117
118
    /**
119
     * Widget
120
     */
121
    function widget( $args, $instance ) {
122
		$instance = wp_parse_args( $instance, array(
123
			'title' => '',
124
			'product_id' => null,
125
		) );
126
127
		echo $args['before_widget'];
128
129
		$title = apply_filters( 'widget_title', $instance['title'] );
130
		if ( ! empty( $title ) ) {
131
			echo $args['before_title'] . $title . $args['after_title'];
132
		}
133
134
		echo '<div class="simple-payments-content">';
135
136
		$attrs = array( 'id' => $instance['product_id'] );
137
138
		$JSP = Jetpack_Simple_Payments::getInstance();
139
		echo $JSP->parse_shortcode( $attrs );
140
141
		echo '</div><!--simple-payments-->';
142
143
		echo $args['after_widget'];
144
145
	    /** This action is documented in modules/widgets/gravatar-profile.php */
146
	    do_action( 'jetpack_stats_extra', 'widget_view', 'simple-payments' );
147
    }
148
149
	/**
150
	 * Update
151
	 */
152
	function update( $new_instance, $old_instance ) {
153
    	if ( isset( $new_instance['product_id'] ) ) {
154
    		$product_id = $new_instance['product_id'];
155
		} else {
156
    		if ( ! isset( $new_instance['name'] ) ) {
157
    			return array(); // The user didn't pick a product in the list. Don't save anything.
158
			}
159
			$product_id = isset( $old_instance['product_id'] ) ? $old_instance['product_id'] : null;
160
		}
161
		$product = $product_id ? get_post( $product_id ) : 0;
162
		if ( ! $product || is_wp_error( $product ) || $product->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...
163
			$product_id = 0;
164
		}
165
166
		if ( isset( $new_instance['name'] ) ) {
167
			$product_id = wp_insert_post( array(
168
				'ID' => $product_id,
169
				'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...
170
				'post_status' => 'publish',
171
				'post_title' => sanitize_text_field( $new_instance['name'] ),
172
				'post_content' => sanitize_textarea_field( $new_instance['description'] ),
173
				'_thumbnail_id' => isset( $new_instance['image'] ) ? $new_instance['image'] : -1,
174
				'meta_input' => array(
175
					'spay_currency' => Jetpack_Money_Format::is_valid_currency( $new_instance['currency'] ) ? $new_instance['currency'] : 'USD',
176
					'spay_price' => Jetpack_Money_Format::sanitize_price( $new_instance['currency'], $new_instance['price'] ),
177
					'spay_multiple' => isset( $new_instance['multiple'] ) ? intval( $new_instance['multiple'] ) : 0,
178
					'spay_email' => is_email( $new_instance['email'] ),
179
				),
180
			) );
181
		}
182
183
		return array(
184
			'title' => $new_instance['title'] ? $new_instance['title'] : '',
185
			'product_id' => $product_id,
186
		);
187
    }
188
189
	protected function render_products_list( $products ) {
190
		?>
191
		<div class="simple-payments-product-list">
192
			<div>
193
				<span class="simple-payments-product-count"><?php printf( _n( '%d product', '%d products', count( $products ), 'Jetpack' ), number_format_i18n( count( $products ) ) ) ?></span>
194
				<button class="button simple-payments-add-product"><?php esc_html_e( 'Add New', 'jetpack' ); ?></button>
195
			</div>
196
			<ul class="simple-payments-products">
197
				<?php
198
				foreach ( $products as $product ):
199
					$product_args = $this->get_product_args( $product->ID );
200
					$image = '';
201
					if ( has_post_thumbnail( $product->ID ) ) {
202
						$image = get_the_post_thumbnail( $product->ID, 'thumbnail' );
203
						$image_id = get_post_thumbnail_id( $product->ID );
204
					}
205
					$field_id = $this->get_field_id( 'product_id' ) . '_' . esc_attr( $product->ID );
206
					?>
207
					<li>
208
						<input type="radio" id="<?php echo $field_id; ?>" name="<?php echo $this->get_field_name( 'product_id' ); ?>" value="<?php esc_html_e( $product->ID ); ?>">
209
						<label for="<?php echo $field_id; ?>">
210
							<div class="product-info">
211
								<?php esc_html_e( $product_args['name'] ); ?><br>
212
								<?php esc_html_e( Jetpack_Money_Format::format_price( $product_args['currency'], $product_args['price'] ) ); ?>
213
							</div>
214
							<div class="image"><?php echo $image; ?></div>
215
							<button class="button simple-payments-edit-product"
216
									data-name="<?php esc_attr_e( $product_args['name'] ); ?>"
217
									data-description="<?php esc_attr_e( $product_args['description'] ); ?>"
218
									data-currency="<?php esc_attr_e( $product_args['currency'] ); ?>"
219
									data-price="<?php esc_attr_e( Jetpack_Money_Format::format_price_amount( $product_args['currency'], $product_args['price'] ) ); ?>"
220
									data-multiple="<?php esc_attr_e( $product_args['multiple'] ); ?>"
221
									data-email="<?php esc_attr_e( $product_args['email'] ); ?>"
222
								<?php if ( ! empty( $image ) ) { ?>
223
									data-image-url="<?php echo esc_url( wp_get_attachment_image_url( $image_id, 'full' ) ); ?>"
0 ignored issues
show
Bug introduced by
The variable $image_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
224
									data-image-id="<?php echo $image_id; ?>"
225
								<?php } ?>
226
							>
227
								<span class="screen-reader-text"><?php _e( 'Edit', 'jetpack' ); ?></span>
228
							</button>
229
						</label>
230
					</li>
231
				<?php endforeach; ?>
232
			</ul>
233
		</div>
234
		<?php
235
	}
236
237
	protected function render_product_form( $product_id, $hide ) {
238
		$product_args = $this->get_product_args( $product_id );
239
		$price = ( $product_args['price'] ) ? esc_attr( Jetpack_Money_Format::format_price_amount( $product_args['currency'], $product_args['price'] ) ) : '';
240
		?>
241
		<div class="simple-payments-form" <?php if ( $hide ) echo 'style="display:none;"'; ?>>
242
			<p>
243
				<label for="<?php esc_attr_e( $this->get_field_id( 'name' ) ); ?>"><?php esc_html_e( 'What are you selling?', 'jetpack' ); ?></label>
244
				<input <?php echo empty( $products ) ? '' : 'disabled'; ?> class="widefat field-name" id="<?php esc_attr_e( $this->get_field_id( 'name' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'name' ) ); ?>" type="text" placeholder="<?php esc_attr_e( 'Product name', 'jetpack' ); ?>" value="<?php esc_attr_e( $product_args['name'] ); ?>" />
0 ignored issues
show
Bug introduced by
The variable $products seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
245
			</p>
246
			<div class="simple-payments-image-fieldset">
247
				<label><?php esc_html_e( 'Product image', 'jetpack' ); ?></label>
248
				<div class="placeholder" <?php if ( has_post_thumbnail( $product_id ) ) echo 'style="display:none;"'; ?>><?php esc_html_e( 'Select an image', 'jetpack' ); ?></div> <!-- TODO: actual placeholder -->
249
				<div class="simple-payments-image" data-image-field="<?php esc_attr_e( $this->get_field_name( 'image' ) ); ?>"> <!-- TODO: hide if empty, CSS? -->
250
					<?php
251
					if ( has_post_thumbnail( $product_id ) ) {
252
						$image_id = get_post_thumbnail_id( $product_id );
253
						echo '<img src="' . esc_url( wp_get_attachment_image_url( $image_id, 'full' ) ) . '" />';
254
						echo '<input type="hidden" name="' . $this->get_field_name( 'image' ) . '" value="' . esc_attr( $image_id ) . '" />';
255
					}
256
					?>
257
					<button class="button simple-payments-remove-image"><span class="screen-reader-text"><?php esc_html_e( 'Remove image', 'jetpack' ); ?></span></button>
258
				</div>
259
			</div>
260
			<p>
261
				<label for="<?php esc_attr_e( $this->get_field_id( 'description' ) ); ?>"><?php esc_html_e( 'Description', 'jetpack' ); ?></label>
262
				<textarea class="field-description widefat" rows=5 id="<?php esc_attr_e( $this->get_field_id( 'description' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'description' ) ); ?>"><?php  esc_html_e( $product_args['description'] ); ?></textarea>
263
			</p>
264
			<p class="cost">
265
				<label for="<?php esc_attr_e( $this->get_field_id( 'price' ) ); ?>"><?php esc_html_e( 'Price', 'jetpack' ); ?></label>
266
				<select class="field-currency widefat" id="<?php esc_attr_e( $this->get_field_id( 'currency' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'currency' ) ); ?>">
267
					<?php foreach( Jetpack_Money_Format::get_currencies_map() as $code => $currency ) { ?>
268
						<option value="<?php esc_attr_e( $code ) ?>"<?php selected( $product_args['currency'], $code ); ?>>
269
							<?php esc_html_e( $currency ) ?>
270
						</option>
271
					<?php } ?>
272
				</select>
273
				<input class="field-price widefat" id="<?php esc_attr_e( $this->get_field_id( 'price' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'price' ) ); ?>" type="text" value="<?php esc_attr_e( $price ); ?>" />
274
			</p>
275
			<p>
276
				<input class="field-multiple" id="<?php esc_attr_e( $this->get_field_id( 'multiple' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'multiple' ) ); ?>" type="checkbox" value="1"<?php checked( $product_args['multiple'], '1' ); ?>/>
277
				<label for="<?php esc_attr_e( $this->get_field_id( 'multiple' ) ); ?>"><?php esc_html_e( 'Allow people to buy more than one item at a time.', 'jetpack' ); ?></label>
278
			</p>
279
			<p>
280
				<label for="<?php esc_attr_e( $this->get_field_id( 'email' ) ); ?>"><?php esc_html_e( 'Email', 'jetpack' ); ?></label>
281
				<input class="field-email widefat" id="<?php esc_attr_e( $this->get_field_id( 'email' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'email' ) ); ?>" type="email" value="<?php  esc_attr_e( $product_args['email'] ); ?>" />
282
				<em><?php printf( esc_html__( 'This is where PayPal will send your money. To claim a payment, you\'ll need a %1$sPayPal account%2$s connected to a bank account.', 'jetpack' ), '<a href="https://paypal.com" target="_blank">', '</a>' ) ?></em>
283
			</p>
284
		</div>
285
		<?php
286
	}
287
288
    /**
289
     * Form
290
     */
291
    function form( $instance ) {
292
		$instance = wp_parse_args( $instance, array(
293
			'title' => '',
294
			'product_id' => null,
295
		) );
296
297
		?>
298
		<div class="simple-payments">
299
			<p>
300
				<label for="<?php esc_attr_e( $this->get_field_id( 'title' ) ); ?>">
301
					<?php esc_html_e( 'Widget Title', 'jetpack' ); ?></label>
302
				<input class="widefat" id="<?php esc_attr_e( $this->get_field_id( 'title' ) ); ?>" name="<?php esc_attr_e( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php esc_attr_e( $instance['title'] ); ?>" />
303
			</p>
304
			<?php
305
				$products = null;
306
				if ( ! $instance['product_id'] ) {
307
					$args = array(
308
						'numberposts' => -1,
309
						'orderby' => 'date',
310
						'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...
311
					);
312
313
					$products = get_posts( $args );
314
					if ( ! empty( $products ) ) {
315
						$this->render_products_list( $products );
316
					}
317
				}
318
319
				if ( ! empty( $products ) ) {
320
					?>
321
					<button class="button simple-payments-back-product-list" style="display:none;"><?php _e( 'Cancel', 'jetpack' ); ?></button>
322
					<?php
323
				}
324
				$hide_form = ! empty( $products ); // If there are existing products, the list is displayed by default, not the form
325
				$this->render_product_form( $instance['product_id'], $hide_form );
326
				?>
327
			</div>
328
		<?php
329
	}
330
}
331