Completed
Push — master ( 527840...a5d62b )
by Mike
53:46 queued 43:33
created

wc-rest-functions.php ➔ wc_rest_upload_image_from_url()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 54

Duplication

Lines 10
Ratio 18.52 %

Code Coverage

Tests 14
CRAP Score 10.5

Importance

Changes 0
Metric Value
cc 6
nc 7
nop 1
dl 10
loc 54
ccs 14
cts 28
cp 0.5
crap 10.5
rs 8.3814
c 0
b 0
f 0

How to fix   Long Method   

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
 * WooCommerce REST Functions
4
 *
5
 * Functions for REST specific things.
6
 *
7
 * @package WooCommerce/Functions
8
 * @version 2.6.0
9
 */
10
11
defined( 'ABSPATH' ) || exit;
12
13
/**
14
 * Parses and formats a date for ISO8601/RFC3339.
15
 *
16
 * Required WP 4.4 or later.
17
 * See https://developer.wordpress.org/reference/functions/mysql_to_rfc3339/
18
 *
19
 * @since  2.6.0
20
 * @param  string|null|WC_DateTime $date Date.
21
 * @param  bool                    $utc  Send false to get local/offset time.
22
 * @return string|null ISO8601/RFC3339 formatted datetime.
23
 */
24
function wc_rest_prepare_date_response( $date, $utc = true ) {
25 91
	if ( is_numeric( $date ) ) {
26 23
		$date = new WC_DateTime( "@$date", new DateTimeZone( 'UTC' ) );
27 23
		$date->setTimezone( new DateTimeZone( wc_timezone_string() ) );
28 91
	} elseif ( is_string( $date ) ) {
29 40
		$date = new WC_DateTime( $date, new DateTimeZone( 'UTC' ) );
30 40
		$date->setTimezone( new DateTimeZone( wc_timezone_string() ) );
31
	}
32
33 91
	if ( ! is_a( $date, 'WC_DateTime' ) ) {
34 66
		return null;
35
	}
36
37
	// Get timestamp before changing timezone to UTC.
38 91
	return gmdate( 'Y-m-d\TH:i:s', $utc ? $date->getTimestamp() : $date->getOffsetTimestamp() );
0 ignored issues
show
Bug introduced by
It seems like $date is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
39
}
40
41
/**
42
 * Returns image mime types users are allowed to upload via the API.
43
 *
44
 * @since  2.6.4
45
 * @return array
46
 */
47
function wc_rest_allowed_image_mime_types() {
48 7
	return apply_filters(
49 7
		'woocommerce_rest_allowed_image_mime_types', array(
50 7
			'jpg|jpeg|jpe' => 'image/jpeg',
51
			'gif'          => 'image/gif',
52
			'png'          => 'image/png',
53
			'bmp'          => 'image/bmp',
54
			'tiff|tif'     => 'image/tiff',
55
			'ico'          => 'image/x-icon',
56
		)
57
	);
58
}
59
60
/**
61
 * Upload image from URL.
62
 *
63
 * @since 2.6.0
64
 * @param string $image_url Image URL.
65
 * @return array|WP_Error Attachment data or error message.
66
 */
67
function wc_rest_upload_image_from_url( $image_url ) {
68 8
	$parsed_url = wp_parse_url( $image_url );
69 8
70
	// Check parsed URL.
71 View Code Duplication
	if ( ! $parsed_url || ! is_array( $parsed_url ) ) {
72 8
		/* translators: %s: image URL */
73
		return new WP_Error( 'woocommerce_rest_invalid_image_url', sprintf( __( 'Invalid URL %s.', 'woocommerce' ), $image_url ), array( 'status' => 400 ) );
74
	}
75
76
	// Ensure url is valid.
77
	$image_url = esc_url_raw( $image_url );
78 8
79
	// download_url function is part of wp-admin.
80
	if ( ! function_exists( 'download_url' ) ) {
81 8
		include_once ABSPATH . 'wp-admin/includes/file.php';
82 8
	}
83 8
84
	$file_array         = array();
85
	$file_array['name'] = basename( current( explode( '?', $image_url ) ) );
86
87 8
	// Download file to temp location.
88 1
	$file_array['tmp_name'] = download_url( $image_url );
89
90 1
	// If error storing temporarily, return the error.
91
	if ( is_wp_error( $file_array['tmp_name'] ) ) {
92 1
		return new WP_Error( 'woocommerce_rest_invalid_remote_image_url',
93
			/* translators: %s: image URL */
94 7
			sprintf( __( 'Error getting remote image %s.', 'woocommerce' ), $image_url ) . ' '
95
			/* translators: %s: error message */
96
			. sprintf( __( 'Error: %s', 'woocommerce' ), $file_array['tmp_name']->get_error_message() ), array( 'status' => 400 )
97
		);
98
	}
99
100 7
	// Do the validation and storage stuff.
101
	$file = wp_handle_sideload(
102 7
		$file_array,
103
		array(
104
			'test_form' => false,
105
			'mimes'     => wc_rest_allowed_image_mime_types(),
106
		),
107
		current_time( 'Y/m' )
108
	);
109
110 View Code Duplication
	if ( isset( $file['error'] ) ) {
111
		@unlink( $file_array['tmp_name'] ); // @codingStandardsIgnoreLine.
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
112
113
		/* translators: %s: error message */
114
		return new WP_Error( 'woocommerce_rest_invalid_image', sprintf( __( 'Invalid image: %s', 'woocommerce' ), $file['error'] ), array( 'status' => 400 ) );
115
	}
116
117
	do_action( 'woocommerce_rest_api_uploaded_image_from_url', $file, $image_url );
118
119
	return $file;
120
}
121
122
/**
123 7
 * Set uploaded image as attachment.
124
 *
125 7
 * @since 2.6.0
126
 * @param array $upload Upload information from wp_upload_bits.
127
 * @param int   $id Post ID. Default to 0.
128
 * @return int Attachment ID
129
 */
130 7
function wc_rest_set_uploaded_image_as_attachment( $upload, $id = 0 ) {
131
	$info    = wp_check_filetype( $upload['file'] );
132 7
	$title   = '';
133
	$content = '';
134
135
	if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
136
		include_once ABSPATH . 'wp-admin/includes/image.php';
137
	}
138
139 7
	$image_meta = wp_read_image_metadata( $upload['file'] );
140
	if ( $image_meta ) {
141 7
		if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) {
142
			$title = wc_clean( $image_meta['title'] );
143
		}
144
		if ( trim( $image_meta['caption'] ) ) {
145
			$content = wc_clean( $image_meta['caption'] );
146
		}
147
	}
148
149
	$attachment = array(
150
		'post_mime_type' => $info['type'],
151
		'guid'           => $upload['url'],
152
		'post_parent'    => $id,
153 8
		'post_title'     => $title ? $title : basename( $upload['file'] ),
154 8
		'post_content'   => $content,
155 8
	);
156
157 8
	$attachment_id = wp_insert_attachment( $attachment, $upload['file'], $id );
158
	if ( ! is_wp_error( $attachment_id ) ) {
159
		wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $upload['file'] ) );
160
	}
161 8
162 8
	return $attachment_id;
163 7
}
164
165
/**
166 7
 * Validate reports request arguments.
167
 *
168
 * @since 2.6.0
169
 * @param mixed           $value   Value to valdate.
170
 * @param WP_REST_Request $request Request instance.
171
 * @param string          $param   Param to validate.
172 8
 * @return WP_Error|boolean
173 8
 */
174 8
function wc_rest_validate_reports_request_arg( $value, $request, $param ) {
175 8
176 8
	$attributes = $request->get_attributes();
177
	if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
178
		return true;
179 8
	}
180 8
	$args = $attributes['args'][ $param ];
181 8
182
	if ( 'string' === $args['type'] && ! is_string( $value ) ) {
183
		/* translators: 1: param 2: type */
184 8
		return new WP_Error( 'woocommerce_rest_invalid_param', sprintf( __( '%1$s is not of type %2$s', 'woocommerce' ), $param, 'string' ) );
185
	}
186
187
	if ( 'date' === $args['format'] ) {
188
		$regex = '#^\d{4}-\d{2}-\d{2}$#';
189
190
		if ( ! preg_match( $regex, $value, $matches ) ) {
191
			return new WP_Error( 'woocommerce_rest_invalid_date', __( 'The date you provided is invalid.', 'woocommerce' ) );
192
		}
193
	}
194
195
	return true;
196
}
197
198 1
/**
199 1
 * Encodes a value according to RFC 3986.
200
 * Supports multidimensional arrays.
201
 *
202 1
 * @since 2.6.0
203
 * @param string|array $value The value to encode.
204 1
 * @return string|array       Encoded values.
205
 */
206
function wc_rest_urlencode_rfc3986( $value ) {
207
	if ( is_array( $value ) ) {
208
		return array_map( 'wc_rest_urlencode_rfc3986', $value );
209 1
	}
210 1
211
	return str_replace( array( '+', '%7E' ), array( ' ', '~' ), rawurlencode( $value ) );
212 1
}
213 1
214
/**
215
 * Check permissions of posts on REST API.
216
 *
217 1
 * @since 2.6.0
218
 * @param string $post_type Post type.
219
 * @param string $context   Request context.
220
 * @param int    $object_id Post ID.
221
 * @return bool
222
 */
223
function wc_rest_check_post_permissions( $post_type, $context = 'read', $object_id = 0 ) {
224
	$contexts = array(
225
		'read'   => 'read_private_posts',
226
		'create' => 'publish_posts',
227
		'edit'   => 'edit_post',
228
		'delete' => 'delete_post',
229 1
		'batch'  => 'edit_others_posts',
230
	);
231
232
	if ( 'revision' === $post_type ) {
233 1
		$permission = false;
234
	} else {
235
		$cap              = $contexts[ $context ];
236
		$post_type_object = get_post_type_object( $post_type );
237
		$permission       = current_user_can( $post_type_object->cap->$cap, $object_id );
238
	}
239
240
	return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $post_type );
241
}
242
243
/**
244
 * Check permissions of users on REST API.
245
 *
246
 * @since 2.6.0
247 114
 * @param string $context   Request context.
248
 * @param int    $object_id Post ID.
249
 * @return bool
250
 */
251
function wc_rest_check_user_permissions( $context = 'read', $object_id = 0 ) {
252
	$contexts = array(
253
		'read'   => 'list_users',
254 114
		'create' => 'promote_users', // Check if current user can create users, shop managers are not allowed to create users.
255
		'edit'   => 'edit_users',
256
		'delete' => 'delete_users',
257 114
		'batch'  => 'promote_users',
258 114
	);
259 114
260
	// Check to allow shop_managers to manage only customers.
261
	if ( in_array( $context, array( 'edit', 'delete' ), true ) && wc_current_user_has_role( 'shop_manager' ) ) {
262 114
		$permission                  = false;
263
		$user_data                   = get_userdata( $object_id );
264
		$shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) );
265
266
		if ( isset( $user_data->roles ) ) {
267
			$can_manage_users = array_intersect( $user_data->roles, array_unique( $shop_manager_editable_roles ) );
268
269
			// Check if Shop Manager can edit customer or with the is same shop manager.
270
			if ( 0 < count( $can_manage_users ) || intval( $object_id ) === intval( get_current_user_id() ) ) {
271
				$permission = current_user_can( $contexts[ $context ], $object_id );
272
			}
273
		}
274
	} else {
275 31
		$permission = current_user_can( $contexts[ $context ], $object_id );
276
	}
277
278
	return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'user' );
279
}
280
281
/**
282
 * Check permissions of product terms on REST API.
283 31
 *
284
 * @since 2.6.0
285
 * @param string $taxonomy  Taxonomy.
286
 * @param string $context   Request context.
287
 * @param int    $object_id Post ID.
288
 * @return bool
289
 */
290
function wc_rest_check_product_term_permissions( $taxonomy, $context = 'read', $object_id = 0 ) {
291
	$contexts = array(
292
		'read'   => 'manage_terms',
293
		'create' => 'edit_terms',
294
		'edit'   => 'edit_terms',
295
		'delete' => 'delete_terms',
296
		'batch'  => 'edit_terms',
297 31
	);
298
299
	$cap             = $contexts[ $context ];
300 31
	$taxonomy_object = get_taxonomy( $taxonomy );
301
	$permission      = current_user_can( $taxonomy_object->cap->$cap, $object_id );
302
303
	return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $taxonomy );
304
}
305
306
/**
307
 * Check manager permissions on REST API.
308
 *
309
 * @since 2.6.0
310
 * @param string $object  Object.
311
 * @param string $context Request context.
312
 * @return bool
313
 */
314 3
function wc_rest_check_manager_permissions( $object, $context = 'read' ) {
315
	$objects = array(
316
		'reports'          => 'view_woocommerce_reports',
317
		'settings'         => 'manage_woocommerce',
318
		'system_status'    => 'manage_woocommerce',
319
		'attributes'       => 'manage_product_terms',
320
		'shipping_methods' => 'manage_woocommerce',
321 3
		'payment_gateways' => 'manage_woocommerce',
322 3
		'webhooks'         => 'manage_woocommerce',
323 3
	);
324
325 3
	$permission = current_user_can( $objects[ $object ] );
326
327
	return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, 0, $object );
328
}
329
330
/**
331
 * Check product reviews permissions on REST API.
332
 *
333
 * @since 3.5.0
334
 * @param string $context   Request context.
335
 * @param string $object_id Object ID.
336
 * @return bool
337
 */
338 172
function wc_rest_check_product_reviews_permissions( $context = 'read', $object_id = 0 ) {
339
	$permission = false;
340
	$contexts   = array(
341
		'read'   => 'moderate_comments',
342
		'create' => 'moderate_comments',
343
		'edit'   => 'moderate_comments',
344
		'delete' => 'moderate_comments',
345
		'batch'  => 'moderate_comments',
346
	);
347 172
348
	if ( isset( $contexts[ $context ] ) ) {
349 172
		$permission = current_user_can( $contexts[ $context ] );
350
	}
351
352
	return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'product_review' );
353
}
354