PluginProbe
WooCommerce / 11.1.0-beta.1
WooCommerce v11.1.0-beta.1
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / src / Admin / API / OnboardingProfile.php
OnboardingProfile.php
603 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST API Onboarding Profile Controller
4 *
5 * Handles requests to /onboarding/profile
6 */
7
8 declare( strict_types=1 );
9
10 namespace Automattic\WooCommerce\Admin\API;
11
12 defined( 'ABSPATH' ) || exit;
13
14 use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile as Profile;
15 use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProducts;
16 use Automattic\Jetpack\Connection\Manager as Jetpack_Connection_Manager;
17 use WP_Error;
18 use WP_REST_Request;
19 use WP_REST_Response;
20
21 /**
22 * Onboarding Profile controller.
23 *
24 * @internal
25 * @extends WC_REST_Data_Controller
26 */
27 class OnboardingProfile extends \WC_REST_Data_Controller {
28 /**
29 * Endpoint namespace.
30 *
31 * @var string
32 */
33 protected $namespace = 'wc-admin';
34
35 /**
36 * Route base.
37 *
38 * @var string
39 */
40 protected $rest_base = 'onboarding/profile';
41
42 /**
43 * Register routes.
44 */
45 public function register_routes() {
46 register_rest_route(
47 $this->namespace,
48 '/' . $this->rest_base,
49 array(
50 array(
51 'methods' => \WP_REST_Server::READABLE,
52 'callback' => array( $this, 'get_items' ),
53 'permission_callback' => array( $this, 'get_items_permissions_check' ),
54 ),
55 'schema' => array( $this, 'get_public_item_schema' ),
56 )
57 );
58 register_rest_route(
59 $this->namespace,
60 '/' . $this->rest_base,
61 array(
62 array(
63 'methods' => \WP_REST_Server::EDITABLE,
64 'callback' => array( $this, 'update_items' ),
65 'permission_callback' => array( $this, 'update_items_permissions_check' ),
66 'args' => $this->get_collection_params(),
67 ),
68 'schema' => array( $this, 'get_public_item_schema' ),
69 )
70 );
71
72 // This endpoint is experimental. For internal use only.
73 register_rest_route(
74 $this->namespace,
75 '/' . $this->rest_base . '/experimental_get_email_prefill',
76 array(
77 array(
78 'methods' => \WP_REST_Server::READABLE,
79 'callback' => array( $this, 'get_email_prefill' ),
80 'permission_callback' => array( $this, 'get_items_permissions_check' ),
81 ),
82 'schema' => array( $this, 'get_public_item_schema' ),
83 )
84 );
85
86 register_rest_route(
87 $this->namespace,
88 '/' . $this->rest_base . '/progress',
89 array(
90 array(
91 'methods' => \WP_REST_Server::READABLE,
92 'callback' => array( $this, 'get_profile_progress' ),
93 'permission_callback' => array( $this, 'get_items_permissions_check' ),
94 ),
95 )
96 );
97
98 register_rest_route(
99 $this->namespace,
100 '/' . $this->rest_base . '/progress/core-profiler/complete',
101 array(
102 array(
103 'methods' => \WP_REST_Server::EDITABLE,
104 'callback' => array( $this, 'core_profiler_step_complete' ),
105 'permission_callback' => array( $this, 'update_items_permissions_check' ),
106 'args' => array(
107 'step' => array(
108 'required' => true,
109 'type' => 'string',
110 'description' => __( 'The Core Profiler step to mark as complete.', 'woocommerce' ),
111 'enum' => array(
112 'intro-opt-in',
113 'skip-guided-setup',
114 'user-profile',
115 'business-info',
116 'plugins',
117 'intro-builder',
118 'skip-guided-setup',
119 ),
120 ),
121 ),
122 ),
123 )
124 );
125
126 register_rest_route(
127 $this->namespace,
128 '/' . $this->rest_base . '/update-store-currency-and-measurement-units',
129 array(
130 array(
131 'methods' => 'POST',
132 'callback' => array( $this, 'update_store_currency_and_measurement_units' ),
133 'permission_callback' => array( $this, 'update_items_permissions_check' ),
134 'args' => array(
135 'country_code' => array(
136 'description' => __( 'Country code.', 'woocommerce' ),
137 'type' => 'string',
138 'required' => true,
139 ),
140 ),
141 ),
142 'schema' => array( $this, 'get_public_item_schema' ),
143 )
144 );
145 }
146
147 /**
148 * Check whether a given request has permission to read onboarding profile data.
149 *
150 * @param WP_REST_Request $request Full details about the request.
151 * @return WP_Error|boolean
152 */
153 public function get_items_permissions_check( $request ) {
154 if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
155 return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
156 }
157
158 return true;
159 }
160
161 /**
162 * Check whether a given request has permission to edit onboarding profile data.
163 *
164 * @param WP_REST_Request $request Full details about the request.
165 * @return WP_Error|boolean
166 */
167 public function update_items_permissions_check( $request ) {
168 if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
169 return new \WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
170 }
171
172 return true;
173 }
174
175 /**
176 * Return all onboarding profile data.
177 *
178 * @param WP_REST_Request $request Request data.
179 * @return WP_Error|WP_REST_Response
180 */
181 public function get_items( $request ) {
182 include_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-options.php';
183
184 $onboarding_data = get_option( Profile::DATA_OPTION, array() );
185 $onboarding_data['industry'] = isset( $onboarding_data['industry'] ) ? $this->filter_industries( $onboarding_data['industry'] ) : null;
186 $item_schema = $this->get_item_schema();
187 $items = array();
188 foreach ( $item_schema['properties'] as $key => $property_schema ) {
189 $items[ $key ] = isset( $onboarding_data[ $key ] ) ? $onboarding_data[ $key ] : null;
190 }
191
192 $wccom_auth = \WC_Helper_Options::get( 'auth' );
193 $items['wccom_connected'] = empty( $wccom_auth['access_token'] ) ? false : true;
194
195 $item = $this->prepare_item_for_response( $items, $request );
196 $data = $this->prepare_response_for_collection( $item );
197
198 return rest_ensure_response( $data );
199 }
200
201 /**
202 * Filter the industries.
203 *
204 * @param array $industries List of industries.
205 * @return array
206 */
207 protected function filter_industries( $industries ) {
208 /**
209 * Filter the list of industries.
210 *
211 * @since 6.5.0
212 * @param array $industries List of industries.
213 */
214 return apply_filters(
215 'woocommerce_admin_onboarding_industries',
216 $industries
217 );
218 }
219
220 /**
221 * Update onboarding profile data.
222 *
223 * @param WP_REST_Request $request Request data.
224 * @return WP_Error|WP_REST_Response
225 */
226 public function update_items( $request ) {
227 $params = $request->get_json_params();
228 $query_args = $this->prepare_objects_query( $params );
229
230 $onboarding_data = (array) get_option( Profile::DATA_OPTION, array() );
231 $profile_data = array_merge( $onboarding_data, $query_args );
232 update_option( Profile::DATA_OPTION, $profile_data );
233
234 /**
235 * Fires when onboarding profile data is updated via the REST API.
236 *
237 * @since 6.5.0
238 * @param array $onboarding_data Previous onboarding data.
239 * @param array $query_args New data being set.
240 */
241 do_action( 'woocommerce_onboarding_profile_data_updated', $onboarding_data, $query_args );
242
243 $result = array(
244 'status' => 'success',
245 'message' => __( 'Onboarding profile data has been updated.', 'woocommerce' ),
246 );
247
248 $response = $this->prepare_item_for_response( $result, $request );
249 $data = $this->prepare_response_for_collection( $response );
250
251 return rest_ensure_response( $data );
252 }
253
254 /**
255 * Returns a default email to be pre-filled in OBW. Prioritizes Jetpack if connected,
256 * otherwise will default to WordPress general settings.
257 *
258 * @param WP_REST_Request $request Request data.
259 * @return WP_Error|WP_REST_Response
260 */
261 public function get_email_prefill( $request ) {
262 $result = array(
263 'email' => '',
264 );
265
266 // Attempt to get email from Jetpack.
267 if ( class_exists( Jetpack_Connection_Manager::class ) ) {
268 $jetpack_connection_manager = new Jetpack_Connection_Manager();
269 if ( $jetpack_connection_manager->is_active() ) {
270 $jetpack_user = $jetpack_connection_manager->get_connected_user_data();
271
272 $result['email'] = $jetpack_user['email'];
273 }
274 }
275
276 // Attempt to get email from WordPress general settings.
277 if ( empty( $result['email'] ) ) {
278 $result['email'] = get_option( 'admin_email' );
279 }
280
281 return rest_ensure_response( $result );
282 }
283
284 /**
285 * Mark a core profiler step as complete.
286 *
287 * @param WP_REST_Request $request Request data.
288 * @return WP_Error|WP_REST_Response
289 */
290 public function core_profiler_step_complete( $request ) {
291 $json = $request->get_json_params();
292 $step = $json['step'];
293
294 $onboarding_progress = (array) get_option( Profile::PROGRESS_OPTION, array() );
295
296 if ( ! isset( $onboarding_progress['core_profiler_completed_steps'] ) ) {
297 $onboarding_progress['core_profiler_completed_steps'] = array();
298 }
299
300 $onboarding_progress['core_profiler_completed_steps'][ $step ] = array(
301 'completed_at' => gmdate( 'Y-m-d\TH:i:s\Z' ),
302 );
303
304 update_option( Profile::PROGRESS_OPTION, $onboarding_progress );
305
306 /**
307 * Fires when a core profiler step is completed.
308 *
309 * @since 6.5.0
310 * @param string $step The completed step name.
311 */
312 do_action( 'woocommerce_core_profiler_step_complete', $step );
313
314 $response_data = array(
315 'results' => $onboarding_progress,
316 'status' => 'success',
317 );
318
319 $response = rest_ensure_response( $response_data );
320
321 return $response;
322 }
323
324 /**
325 * Get the onboarding profile progress.
326 *
327 * @param WP_REST_Request $request Request data.
328 * @return WP_Error|WP_REST_Response
329 */
330 public function get_profile_progress( $request ) {
331 $onboarding_progress = (array) get_option( Profile::PROGRESS_OPTION, array() );
332 return rest_ensure_response( $onboarding_progress );
333 }
334
335
336 /**
337 * Update store's currency and measurement units.
338 * Requires 'country' code to be passed in the request.
339 *
340 * @param WP_REST_Request $request Request data.
341 * @return WP_Error|WP_REST_Response
342 */
343 public function update_store_currency_and_measurement_units( WP_REST_Request $request ) {
344 $country_code = $request->get_param( 'country_code' );
345 $locale_info = include WC()->plugin_path() . '/i18n/locale-info.php';
346
347 if ( empty( $country_code ) || ! isset( $locale_info[ $country_code ] ) ) {
348 return new WP_Error(
349 'woocommerce_rest_invalid_country_code',
350 __( 'Invalid country code.', 'woocommerce' ),
351 array( 'status' => 400 )
352 );
353 }
354
355 $country_info = $locale_info[ $country_code ];
356
357 $currency_settings = array(
358 'woocommerce_currency' => $country_info['currency_code'],
359 'woocommerce_currency_pos' => $country_info['currency_pos'],
360 'woocommerce_price_thousand_sep' => $country_info['thousand_sep'],
361 'woocommerce_price_decimal_sep' => $country_info['decimal_sep'],
362 'woocommerce_price_num_decimals' => $country_info['num_decimals'],
363 'woocommerce_weight_unit' => $country_info['weight_unit'],
364 'woocommerce_dimension_unit' => $country_info['dimension_unit'],
365 );
366
367 foreach ( $currency_settings as $key => $value ) {
368 update_option( $key, $value );
369 }
370
371 return new WP_REST_Response( array(), 204 );
372 }
373
374 /**
375 * Prepare objects query.
376 *
377 * @param array $params The params sent in the request.
378 * @return array
379 */
380 protected function prepare_objects_query( $params ) {
381 $args = array();
382 $properties = self::get_profile_properties();
383
384 foreach ( $properties as $key => $property ) {
385 if ( isset( $params[ $key ] ) ) {
386 $args[ $key ] = $params[ $key ];
387 }
388 }
389
390 /**
391 * Filter the query arguments for a request.
392 *
393 * Enables adding extra arguments or setting defaults for a post
394 * collection request.
395 *
396 * @since 6.5.0
397 * @param array $args Key value array of query var to query value.
398 * @param array $params The params sent in the request.
399 */
400 $args = apply_filters( 'woocommerce_rest_onboarding_profile_object_query', $args, $params );
401
402 return $args;
403 }
404
405
406 /**
407 * Prepare the data object for response.
408 *
409 * @param object $item Data object.
410 * @param WP_REST_Request $request Request object.
411 * @return WP_REST_Response $response Response data.
412 */
413 public function prepare_item_for_response( $item, $request ) {
414 $data = $this->add_additional_fields_to_object( $item, $request );
415 $data = $this->filter_response_by_context( $data, 'view' );
416 $response = rest_ensure_response( $data );
417
418 /**
419 * Filter the list returned from the API.
420 *
421 * @since 6.5.0
422 * @param WP_REST_Response $response The response object.
423 * @param array $item The original item.
424 * @param WP_REST_Request $request Request used to generate the response.
425 */
426 return apply_filters( 'woocommerce_rest_onboarding_prepare_profile', $response, $item, $request );
427 }
428
429 /**
430 * Get onboarding profile properties.
431 *
432 * @return array
433 */
434 public static function get_profile_properties() {
435 $properties = array(
436 'completed' => array(
437 'type' => 'boolean',
438 'description' => __( 'Whether or not the profile was completed.', 'woocommerce' ),
439 'context' => array( 'view' ),
440 'readonly' => true,
441 'validate_callback' => 'rest_validate_request_arg',
442 ),
443 'skipped' => array(
444 'type' => 'boolean',
445 'description' => __( 'Whether or not the profile was skipped.', 'woocommerce' ),
446 'context' => array( 'view' ),
447 'readonly' => true,
448 'validate_callback' => 'rest_validate_request_arg',
449 ),
450 'industry' => array(
451 'type' => 'array',
452 'description' => __( 'Industry.', 'woocommerce' ),
453 'context' => array( 'view' ),
454 'readonly' => true,
455 'nullable' => true,
456 'validate_callback' => 'rest_validate_request_arg',
457 'items' => array(
458 'type' => 'string',
459 ),
460 ),
461 'business_extensions' => array(
462 'type' => 'array',
463 'description' => __( 'Extra business extensions to install.', 'woocommerce' ),
464 'context' => array( 'view' ),
465 'readonly' => true,
466 'sanitize_callback' => 'wp_parse_slug_list',
467 'validate_callback' => 'rest_validate_request_arg',
468 'items' => array(
469 'type' => 'string',
470 ),
471 ),
472 'is_agree_marketing' => array(
473 'type' => 'boolean',
474 'description' => __( 'Whether or not this store agreed to receiving marketing contents from WooCommerce.com.', 'woocommerce' ),
475 'context' => array( 'view' ),
476 'readonly' => true,
477 'validate_callback' => 'rest_validate_request_arg',
478 ),
479 'store_email' => array(
480 'type' => 'string',
481 'description' => __( 'Store email address.', 'woocommerce' ),
482 'context' => array( 'view' ),
483 'readonly' => true,
484 'nullable' => true,
485 'validate_callback' => array( __CLASS__, 'rest_validate_marketing_email' ),
486 ),
487 'is_store_country_set' => array(
488 'type' => 'boolean',
489 'description' => __( 'Whether or not this store country is set via onboarding profiler.', 'woocommerce' ),
490 'context' => array( 'view' ),
491 'readonly' => true,
492 'validate_callback' => 'rest_validate_request_arg',
493 ),
494 'is_plugins_page_skipped' => array(
495 'type' => 'boolean',
496 'description' => __( 'Whether or not plugins step in core profiler was skipped.', 'woocommerce' ),
497 'context' => array( 'view' ),
498 'readonly' => true,
499 'validate_callback' => 'rest_validate_request_arg',
500 ),
501 'business_choice' => array(
502 'type' => 'string',
503 'description' => __( 'Business choice.', 'woocommerce' ),
504 'context' => array( 'view' ),
505 'readonly' => true,
506 'nullable' => true,
507 ),
508 'selling_online_answer' => array(
509 'type' => 'string',
510 'description' => __( 'Selling online answer.', 'woocommerce' ),
511 'context' => array( 'view' ),
512 'readonly' => true,
513 'nullable' => true,
514 ),
515 'selling_platforms' => array(
516 'type' => array( 'array', 'null' ),
517 'description' => __( 'Selling platforms.', 'woocommerce' ),
518 'context' => array( 'view' ),
519 'readonly' => true,
520 'nullable' => true,
521 'items' => array(
522 'type' => array( 'string', 'null' ),
523 ),
524 ),
525 );
526
527 /**
528 * Filters the Onboarding Profile REST API JSON Schema.
529 *
530 * @since 6.5.0
531 * @param array $properties List of properties.
532 */
533 return apply_filters( 'woocommerce_rest_onboarding_profile_properties', $properties );
534 }
535
536 /**
537 * Optionally validates email if user agreed to marketing or if email is not empty.
538 *
539 * @param mixed $value Email value.
540 * @param WP_REST_Request $request Request object.
541 * @param string $param Parameter name.
542 * @return true|WP_Error
543 */
544 public static function rest_validate_marketing_email( $value, $request, $param ) {
545 $is_agree_marketing = $request->get_param( 'is_agree_marketing' );
546 if (
547 ( $is_agree_marketing || ! empty( $value ) ) &&
548 ! is_email( $value ) ) {
549 return new \WP_Error( 'rest_invalid_email', __( 'Invalid email address', 'woocommerce' ) );
550 }
551 return true;
552 }
553
554 /**
555 * Get the schema, conforming to JSON Schema.
556 *
557 * @return array
558 */
559 public function get_item_schema() {
560 // Unset properties used for collection params.
561 $properties = self::get_profile_properties();
562 foreach ( $properties as $key => $property ) {
563 unset( $properties[ $key ]['default'] );
564 unset( $properties[ $key ]['items'] );
565 unset( $properties[ $key ]['validate_callback'] );
566 unset( $properties[ $key ]['sanitize_callback'] );
567 }
568
569 $schema = array(
570 '$schema' => 'http://json-schema.org/draft-04/schema#',
571 'title' => 'onboarding_profile',
572 'type' => 'object',
573 'properties' => $properties,
574 );
575
576 return $this->add_additional_fields_schema( $schema );
577 }
578
579 /**
580 * Get the query params for collections.
581 *
582 * @return array
583 */
584 public function get_collection_params() {
585 // Unset properties used for item schema.
586 $params = self::get_profile_properties();
587 foreach ( $params as $key => $param ) {
588 unset( $params[ $key ]['context'] );
589 unset( $params[ $key ]['readonly'] );
590 }
591
592 $params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
593
594 /**
595 * Filters the Onboarding Profile REST API collection parameters.
596 *
597 * @since 6.5.0
598 * @param array $params Collection parameters.
599 */
600 return apply_filters( 'woocommerce_rest_onboarding_profile_collection_params', $params );
601 }
602 }
603