1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
<?php
namespace Elementor\Modules\Components;
use Elementor\Core\Utils\Api\Error_Builder; use Elementor\Core\Utils\Api\Response_Builder;
if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. }
class Components_REST_API { const API_NAMESPACE = 'elementor/v1'; const API_BASE = 'components'; const STYLES_ROUTE = 'styles'; const MAX_COMPONENTS = 50;
private $repository = null;
public function register_hooks() { add_action( 'rest_api_init', fn() => $this->register_routes() ); }
private function get_repository() { if ( ! $this->repository ) { $this->repository = new Components_Repository(); }
return $this->repository; }
private function register_routes() { register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [ [ 'methods' => 'GET', 'callback' => fn() => $this->route_wrapper( fn() => $this->get_components() ), 'permission_callback' => fn() => is_user_logged_in(), ], ] );
register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/' . self::STYLES_ROUTE, [ [ 'methods' => 'GET', 'callback' => fn() => $this->route_wrapper( fn() => $this->get_styles() ), 'permission_callback' => fn() => is_user_logged_in(), ], ] );
register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [ [ 'methods' => 'POST', 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->create_component( $request ) ), 'permission_callback' => fn() => current_user_can( 'manage_options' ), 'args' => [ 'name' => [ 'type' => 'string', 'required' => true, ], 'content' => [ 'type' => 'array', 'required' => true, 'items' => [ 'type' => 'object', ], ], ], ], ] ); }
private function get_components() { $components = $this->get_repository()->all();
$components_list = $components->get_components()->map( fn( $component ) => [ 'id' => $component['id'], 'name' => $component['name'], ])->all();
return Response_Builder::make( $components_list )->build(); }
private function get_styles() { $components = $this->get_repository()->all();
$styles = []; $components->get_components()->each( function( $component ) use ( &$styles ) { $styles[ $component['id'] ] = $component['styles']; } );
return Response_Builder::make( $styles )->build(); } private function create_component( \WP_REST_Request $request ) { $components = $this->get_repository()->all(); $components_count = $components->get_components()->count();
if ( $components_count >= static::MAX_COMPONENTS ) { return Error_Builder::make( 'components_limit_exceeded' ) ->set_status( 400 ) ->set_message( sprintf( /* translators: %d: maximum components limit. */ __( 'Components limit exceeded. Maximum allowed: %d', 'elementor' ), static::MAX_COMPONENTS ) ) ->build(); }
$parser = Components_Parser::make();
$name_result = $parser->parse_name( $request->get_param( 'name' ), $components->get_components()->map( fn( $component ) => $component['name'] )->all() );
if ( ! $name_result->is_valid() ) { return Error_Builder::make( 'invalid_name' ) ->set_status( 400 ) ->set_message( 'Invalid component name: ' . $name_result->errors()->to_string() ) ->build(); }
$name = $name_result->unwrap(); // The content is validated & sanitized in the document save process. $content = $request->get_param( 'content' );
try { $component_id = $this->get_repository()->create( $name, $content );
return Response_Builder::make( [ 'component_id' => $component_id ] )->set_status( 201 )->build(); } catch ( \Exception $e ) { $error_message = $e->getMessage();
$invalid_elements_structure_error = str_contains( $error_message, 'Invalid data' ); $atomic_styles_validation_error = str_contains( $error_message, 'Styles validation failed' ); $atomic_settings_validation_error = str_contains( $error_message, 'Settings validation failed' );
if ( $invalid_elements_structure_error || $atomic_styles_validation_error || $atomic_settings_validation_error ) { return Error_Builder::make( 'content_validation_failed' ) ->set_status( 400 ) ->set_message( $error_message ) ->build(); }
throw $e; } }
private function route_wrapper( callable $cb ) { try { $response = $cb(); } catch ( \Exception $e ) { return Error_Builder::make( 'unexpected_error' ) ->set_message( __( 'Something went wrong', 'elementor' ) ) ->build(); }
return $response; } }
|