Completed
Push — simple-payments/image ( d2d4b5 )
by
unknown
129:12 queued 119:29
created

Jetpack_Simple_Payments::output_shortcode()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
nc 2
nop 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
/*
3
 * Simple Payments lets users embed a PayPal button fully integrated with wpcom to sell products on the site.
4
 * This is not a proper module yet, because not all the pieces are in place. Until everything is shipped, it can be turned
5
 * into module that can be enabled/disabled.
6
 * TODO: Once the feature is fully shipped, create a file modules/simple-payments.php with a proper header to turn module on/off.
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...
7
*/
8
class Jetpack_Simple_Payments {
9
	// These have to be under 20 chars because that is CPT limit.
10
	static $post_type_order = 'jp_pay_order';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $post_type_order.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
11
	static $post_type_product = 'jp_pay_product';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $post_type_product.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
12
13
	static $shortcode = 'simple-payment';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $shortcode.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
14
15
	static $css_classname_prefix = 'jetpack-simple-payments';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $css_classname_prefix.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
16
17
	// Classic singleton pattern:
18
	private static $instance;
19
	private function __construct() {}
20
	static function getInstance() {
21
		if ( ! self::$instance ) {
22
			self::$instance = new self();
23
			self::$instance->register_init_hook();
24
		}
25
		return self::$instance;
26
	}
27
28
	private function register_scripts() {
29
		/**
30
		 * Paypal heavily discourages putting that script in your own server:
31
		 * @see https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/add-paypal-button/
32
		 */
33
		wp_register_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
34
		wp_register_script( 'paypal-express-checkout', plugins_url( '/paypal-express-checkout.js', __FILE__ ) , array( 'jquery', 'paypal-checkout-js' ), '0.21' );
35
		wp_enqueue_style( 'simple-payments', plugins_url( '/simple-payments.css', __FILE__ ) );
36
	}
37
	private function register_init_hook() {
38
		add_action( 'init', array( $this, 'init_hook_action' ) );
39
	}
40
	private function register_shortcode() {
41
		add_shortcode( self::$shortcode, array( $this, 'parse_shortcode' ) );
42
	}
43
44
	public function init_hook_action() {
45
		add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_rest_api_types' ) );
46
		add_filter( 'jetpack_sync_post_meta_whitelist', array( $this, 'allow_sync_post_meta' ) );
47
		$this->register_scripts();
48
		$this->register_shortcode();
49
		$this->setup_cpts();
50
51
		add_filter( 'the_content', array( $this, 'remove_auto_paragraph_from_product_description' ), 0 );
52
	}
53
54
	function remove_auto_paragraph_from_product_description( $content ) {
55
		if ( get_post_type() === self::$post_type_product ) {
56
			remove_filter( 'the_content', 'wpautop' );
57
		}
58
59
		return $content;
60
	}
61
62
	function parse_shortcode( $attrs, $content = false ) {
63
		if ( empty( $attrs['id'] ) ) {
64
			return;
65
		}
66
		$product = get_post( $attrs['id'] );
67
		if ( ! $product || is_wp_error( $product ) ) {
68
			return;
69
		}
70
		if ( $product->post_type !== self::$post_type_product ) {
71
			return;
72
		}
73
74
		// We allow for overriding the presentation labels
75
		$data = shortcode_atts( array(
76
			'blog_id'     => Jetpack_Options::get_option( 'id' ),
77
			'dom_id'      => uniqid( self::$css_classname_prefix . '-' . $product->ID . '_', true ),
78
			'class'       => self::$css_classname_prefix . '-' . $product->ID,
79
			'title'       => get_the_title( $product ),
80
			'description' => $product->post_content,
81
			'cta'         => get_post_meta( $product->ID, 'spay_cta', true ),
82
			'multiple'    => get_post_meta( $product->ID, 'spay_multiple', true ) || '0'
83
		), $attrs );
84
		$data['price'] = $this->format_price(
85
			get_post_meta( $product->ID, 'spay_price', true ),
86
			get_post_meta( $product->ID, 'spay_currency', true ),
87
			$data
88
		);
89
90
		if ( ! wp_script_is( 'paypal-express-checkout','enqueued' ) ) {
91
			wp_enqueue_script( 'paypal-express-checkout' );
92
		}
93
94
		wp_add_inline_script( 'paypal-express-checkout', "try{PaypalExpressCheckout.renderButton( '{$data['blog_id']}', '{$attrs['id']}', '{$data['dom_id']}', '{$data['multiple']}' );}catch(e){}" );
95
96
		return $this->output_shortcode( $data );
97
	}
98
99
	function output_shortcode( $data ) {
100
		$items = '';
101
		$cssPrefix = self::$css_classname_prefix;
102
103
		if ( $data['multiple'] ) {
104
			$items="<div class='${cssPrefix}-items'>
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 0 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
105
				<input class='${cssPrefix}-items-number' type='number' value='1' id='{$data['dom_id']}_number' />
106
			</div>";
107
		}
108
		$image = get_the_post_thumbnail( $data['id'], 'thumbnail', array( 'class' => "${cssPrefix}-image" ) );
109
		return "
110
<div class='{$data['class']} ${cssPrefix}-wrapper'>
111
	<p class='${cssPrefix}-purchase-message'></p>
112
	{$image}
113
	<div class='${cssPrefix}-title'><p>{$data['title']}</p></div>
114
	<div class='${cssPrefix}-description'><p>{$data['description']}</p></div>
115
	<div class='${cssPrefix}-purchase-box'>
116
		<div class='${cssPrefix}-price'><p>{$data['price']}</p></div>
117
		{$items}
118
		<div class='${cssPrefix}-button' id='{$data['dom_id']}_button'></div>
119
	</div>
120
</div>
121
		";
122
	}
123
124
	function format_price( $price, $currency, $all_data ) {
125
		// TODO: better price formatting logic. Extracting from woocmmerce is not a solution since its bound with woo site options.
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...
126
		return "$price $currency";
127
	}
128
129
	/**
130
	 * Allows custom post types to be used by REST API.
131
	 * @param $post_types
132
	 * @see hook 'rest_api_allowed_post_types'
133
	 * @return array
134
	 */
135
	function allow_rest_api_types( $post_types ) {
136
		$post_types[] = self::$post_type_order;
137
		$post_types[] = self::$post_type_product;
138
		return $post_types;
139
	}
140
141
	function allow_sync_post_meta( $post_meta ) {
142
		return array_merge( $post_meta, array(
143
			'spay_paypal_id',
144
			'spay_status',
145
			'spay_product_id',
146
			'spay_quantity',
147
			'spay_price',
148
			'spay_customer_email',
149
			'spay_currency',
150
			'spay_cta',
151
			'spay_email',
152
			'spay_multiple'
153
		) );
154
	}
155
156
	/**
157
	 * Sets up the custom post types for the module.
158
	 */
159
	function setup_cpts() {
160
161
		/*
162
		 * ORDER data structure. holds:
163
		 * title = customer_name | 4xproduct_name
164
		 * excerpt = customer_name + customer contact info + customer notes from paypal form
165
		 * metadata:
166
		 * spay_paypal_id - paypal id of transaction
167
		 * spay_status
168
		 * spay_product_id - post_id of bought product
169
		 * spay_quantity - quantity of product
170
		 * spay_price - item price at the time of purchase
171
		 * spay_customer_email - customer email
172
		 * ... (WIP)
173
		 */
174
		$order_capabilities = array(
175
			'edit_post'             => 'edit_posts',
176
			'read_post'             => 'read_private_posts',
177
			'delete_post'           => 'delete_posts',
178
			'edit_posts'            => 'edit_posts',
179
			'edit_others_posts'     => 'edit_others_posts',
180
			'publish_posts'         => 'publish_posts',
181
			'read_private_posts'    => 'read_private_posts',
182
		);
183
		$order_args = array(
184
			'label'                 => esc_html__( 'Order', 'jetpack' ),
185
			'description'           => esc_html__( 'Simple Payments orders', 'jetpack' ),
186
			'supports'              => array( 'custom-fields', 'excerpt' ),
187
			'hierarchical'          => false,
188
			'public'                => false,
189
			'show_ui'               => false,
190
			'show_in_menu'          => false,
191
			'show_in_admin_bar'     => false,
192
			'show_in_nav_menus'     => false,
193
			'can_export'            => true,
194
			'has_archive'           => false,
195
			'exclude_from_search'   => true,
196
			'publicly_queryable'    => false,
197
			'rewrite'               => false,
198
			'capabilities'          => $order_capabilities,
199
			'show_in_rest'          => true,
200
		);
201
		register_post_type( self::$post_type_order, $order_args );
202
203
		/*
204
		 * PRODUCT data structure. Holds:
205
		 * title - title
206
		 * content - description
207
		 * thumbnail - image
208
		 * metadata:
209
		 * spay_price - price
210
		 * spay_currency - currency code
211
		 * spay_cta - text with "Buy" or other CTA
212
		 * spay_email - paypal email
213
		 * spay_multiple - allow for multiple items
214
		 * spay_status - status. { enabled | disabled }
215
		 */
216
		$product_capabilities = array(
217
			'edit_post'             => 'edit_posts',
218
			'read_post'             => 'read_private_posts',
219
			'delete_post'           => 'delete_posts',
220
			'edit_posts'            => 'edit_posts',
221
			'edit_others_posts'     => 'edit_others_posts',
222
			'publish_posts'         => 'publish_posts',
223
			'read_private_posts'    => 'read_private_posts',
224
		);
225
		$product_args = array(
226
			'label'                 => esc_html__( 'Product', 'jetpack' ),
227
			'description'           => esc_html__( 'Simple Payments products', 'jetpack' ),
228
			'supports'              => array( 'title', 'editor','thumbnail', 'custom-fields' ),
229
			'hierarchical'          => false,
230
			'public'                => false,
231
			'show_ui'               => false,
232
			'show_in_menu'          => false,
233
			'show_in_admin_bar'     => false,
234
			'show_in_nav_menus'     => false,
235
			'can_export'            => true,
236
			'has_archive'           => false,
237
			'exclude_from_search'   => true,
238
			'publicly_queryable'    => false,
239
			'rewrite'               => false,
240
			'capabilities'          => $product_capabilities,
241
			'show_in_rest'          => true,
242
		);
243
		register_post_type( self::$post_type_product, $product_args );
244
	}
245
246
}
247
Jetpack_Simple_Payments::getInstance();
248