PluginProbe
Elementor Website Builder – more than just a page builder / 3.34.0-beta1
Elementor Website Builder – more than just a page builder v3.34.0-beta1
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / modules / components / components-rest-api.php

components-rest-api.php in Elementor Website Builder – more than just a page builder 3.34.0-beta1, at modules/components/components-rest-api.php

357 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\Components;
4
5 use Elementor\Core\Base\Document;
6 use Elementor\Core\Utils\Api\Error_Builder;
7 use Elementor\Core\Utils\Api\Response_Builder;
8 use Elementor\Core\Utils\Collection;
9 use Elementor\Modules\Components\Documents\Component;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 class Components_REST_API {
16 const API_NAMESPACE = 'elementor/v1';
17 const API_BASE = 'components';
18 const LOCK_DOCUMENT_TYPE_NAME = 'components';
19 const STYLES_ROUTE = 'styles';
20 const MAX_COMPONENTS = 50;
21
22 private $repository = null;
23 public function register_hooks() {
24 add_action( 'rest_api_init', fn() => $this->register_routes() );
25 }
26
27 private function get_repository() {
28 if ( ! $this->repository ) {
29 $this->repository = new Components_Repository();
30 }
31
32 return $this->repository;
33 }
34
35 /**
36 * @return Component_Lock_Manager instance
37 */
38 private function get_component_lock_manager() {
39 return Component_Lock_Manager::get_instance();
40 }
41
42 private function register_routes() {
43 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
44 [
45 'methods' => 'GET',
46 'callback' => fn() => $this->route_wrapper( fn() => $this->get_components() ),
47 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
48 ],
49 ] );
50
51 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/' . self::STYLES_ROUTE, [
52 [
53 'methods' => 'GET',
54 'callback' => fn() => $this->route_wrapper( fn() => $this->get_styles() ),
55 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
56 ],
57 ] );
58
59 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
60 [
61 'methods' => 'POST',
62 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->create_components( $request ) ),
63 'permission_callback' => fn() => current_user_can( 'manage_options' ),
64 'args' => [
65 'status' => [
66 'type' => 'string',
67 'enum' => [ Document::STATUS_PUBLISH, Document::STATUS_DRAFT, Document::STATUS_AUTOSAVE ],
68 'required' => true,
69 ],
70 'items' => [
71 'type' => 'array',
72 'required' => true,
73 'items' => [
74 'type' => 'object',
75 'properties' => [
76 'uid' => [
77 'type' => 'string',
78 'required' => true,
79 ],
80 'title' => [
81 'type' => 'string',
82 'required' => true,
83 'minLength' => 2,
84 'maxLength' => 200,
85 ],
86 'elements' => [
87 'type' => 'array',
88 'required' => true,
89 'items' => [
90 'type' => 'object',
91 ],
92 ],
93 ],
94 ],
95 ],
96 ],
97 ],
98 ] );
99
100 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/status', [
101 [
102 'methods' => 'PUT',
103 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->update_statuses( $request ) ),
104 'permission_callback' => fn() => current_user_can( 'manage_options' ),
105 'args' => [
106 'status' => [
107 'type' => 'string',
108 'required' => true,
109 'enum' => [ Document::STATUS_PUBLISH ],
110 ],
111 'ids' => [
112 'type' => 'array',
113 'required' => true,
114 'items' => [
115 'type' => 'number',
116 'required' => true,
117 ],
118 ],
119 ],
120 ],
121 ] );
122
123 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/lock', [
124 [
125 'methods' => 'POST',
126 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->lock_component( $request ) ),
127 'permission_callback' => fn() => current_user_can( 'manage_options' ),
128 'args' => [
129 'componentId' => [
130 'type' => 'number',
131 'required' => true,
132 'description' => 'The component ID to unlock',
133 ],
134 ],
135 ],
136 ] );
137
138 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/unlock', [
139 [
140 'methods' => 'POST',
141 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->unlock_component( $request ) ),
142 'permission_callback' => fn() => current_user_can( 'manage_options' ),
143 'args' => [
144 'componentId' => [
145 'type' => 'number',
146 'required' => true,
147 'description' => 'The component ID to unlock',
148 ],
149 ],
150 ],
151 ] );
152
153 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/lock-status', [
154 [
155 'methods' => 'GET',
156 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->get_lock_status( $request ) ),
157 'permission_callback' => fn() => current_user_can( 'manage_options' ),
158 'args' => [
159 'componentId' => [
160 'type' => 'string',
161 'required' => true,
162 'description' => 'The component ID to check lock status',
163 ],
164 ],
165 ],
166 ] );
167 }
168
169 private function get_components() {
170 $components = $this->get_repository()->all();
171
172 $components_list = $components->map( fn( $component ) => [
173 'id' => $component['id'],
174 'name' => $component['title'],
175 'uid' => $component['uid'],
176 ])->all();
177
178 return Response_Builder::make( $components_list )->build();
179 }
180
181 private function get_styles() {
182 $components = $this->get_repository()->all();
183
184 $styles = [];
185 $components->each( function( $component ) use ( &$styles ) {
186 $styles[ $component['id'] ] = $component['styles'];
187 } );
188
189 return Response_Builder::make( $styles )->build();
190 }
191
192 private function create_components( \WP_REST_Request $request ) {
193 $save_status = $request->get_param( 'status' );
194
195 $items = Collection::make( $request->get_param( 'items' ) );
196 $components = $this->get_repository()->all();
197
198 $result = Save_Components_Validator::make( $components )->validate( $items );
199
200 if ( ! $result['success'] ) {
201 return Error_Builder::make( 'components_validation_failed' )
202 ->set_status( 400 )
203 ->set_message( 'Validation failed: ' . implode( ', ', $result['messages'] ) )
204 ->build();
205 }
206
207 $created = $items->map_with_keys( function ( $item ) use ( $save_status ) {
208 $title = sanitize_text_field( $item['title'] );
209 $content = $item['elements'];
210 $uid = $item['uid'];
211
212 $status = Document::STATUS_AUTOSAVE === $save_status
213 ? Document::STATUS_DRAFT
214 : $save_status;
215
216 $component_id = $this->get_repository()->create( $title, $content, $status, $uid );
217
218 return [ $uid => $component_id ];
219 } );
220
221 return Response_Builder::make( (object) $created->all() )
222 ->set_status( 201 )
223 ->build();
224 }
225
226 private function update_statuses( \WP_REST_Request $request ) {
227 $status = $request->get_param( 'status' );
228
229 $result = Collection::make( $request->get_param( 'ids' ) )
230 ->map( fn( $id ) => $this->get_repository()->get( $id ) )
231 ->filter( fn( $component ) => (bool) $component )
232 ->reduce(
233 function ( $result, Component $component ) use ( $status ) {
234 $post = $component->get_post();
235 $autosave = $component->get_newer_autosave();
236
237 $elements = $autosave
238 ? $autosave->get_json_meta( Document::ELEMENTOR_DATA_META_KEY )
239 : $component->get_json_meta( Document::ELEMENTOR_DATA_META_KEY );
240
241 $is_updated = $component->save( [
242 'settings' => [ 'post_status' => $status ],
243 'elements' => $elements,
244 ] );
245
246 $result[ $is_updated ? 'success' : 'failed' ][] = $post->ID;
247
248 return $result;
249 },
250 [
251 'success' => [],
252 'failed' => [],
253 ]
254 );
255
256 return Response_Builder::make( $result )->build();
257 }
258
259 private function lock_component( \WP_REST_Request $request ) {
260 $component_id = $request->get_param( 'componentId' );
261 try {
262 $success = $this->get_component_lock_manager()->lock( $component_id );
263 } catch ( \Exception $e ) {
264 error_log( 'Components REST API lock_component error: ' . $e->getMessage() );
265 return Error_Builder::make( 'lock_failed' )
266 ->set_status( 500 )
267 ->set_message( __( 'Failed to lock component', 'elementor' ) )
268 ->build();
269 }
270
271 if ( ! $success ) {
272 return Error_Builder::make( 'lock_failed' )
273 ->set_status( 500 )
274 ->set_message( __( 'Failed to lock component', 'elementor' ) )
275 ->build();
276 }
277
278 return Response_Builder::make( [ 'locked' => $success ] )->build();
279 }
280
281 private function unlock_component( \WP_REST_Request $request ) {
282 $component_id = $request->get_param( 'componentId' );
283 try {
284 $success = $this->get_component_lock_manager()->unlock( $component_id );
285 } catch ( \Exception $e ) {
286 error_log( 'Components REST API unlock_component error: ' . $e->getMessage() );
287 return Error_Builder::make( 'unlock_failed' )
288 ->set_status( 500 )
289 ->set_message( __( 'Failed to unlock component', 'elementor' ) )
290 ->build();
291 }
292
293 if ( ! $success ) {
294 return Error_Builder::make( 'unlock_failed' )
295 ->set_status( 500 )
296 ->set_message( __( 'Failed to unlock component', 'elementor' ) )
297 ->build();
298 }
299 return Response_Builder::make( [ 'unlocked' => $success ] )->build();
300 }
301
302 private function get_lock_status( \WP_REST_Request $request ) {
303 $component_id = (int) $request->get_param( 'componentId' );
304 try {
305 $lock_manager = $this->get_component_lock_manager();
306 if ( $lock_manager->is_lock_expired( $component_id ) ) {
307 $lock_manager->unlock( $component_id );
308 }
309
310 $lock_data = $lock_manager->get_lock_data( $component_id );
311 $current_user_id = get_current_user_id();
312
313 // if current user is the lock user, return true
314 if ( $lock_data['locked_by'] && $lock_data['locked_by'] === $current_user_id ) {
315 return Response_Builder::make( [
316 'is_current_user_allow_to_edit' => true,
317 'locked_by' => get_user_by( 'id', $lock_data['locked_by'] )->display_name,
318 ] )->build();
319 }
320
321 // if the user is not the lock user, return false
322 if ( $lock_data['locked_by'] && $lock_data['locked_by'] !== $current_user_id ) {
323 return Response_Builder::make( [
324 'is_current_user_allow_to_edit' => false,
325 'locked_by' => get_user_by( 'id', $lock_data['locked_by'] )->display_name,
326 ] )->build();
327 }
328
329 // if the component is not locked, return true
330 if ( ! $lock_data['locked_by'] ) {
331 return Response_Builder::make( [
332 'is_current_user_allow_to_edit' => true,
333 'locked_by' => null,
334 ] )->build();
335 }
336 } catch ( \Exception $e ) {
337 error_log( 'Components REST API get_lock_status error: ' . $e->getMessage() );
338 return Error_Builder::make( 'get_lock_status_failed' )
339 ->set_status( 500 )
340 ->set_message( __( 'Failed to get lock status', 'elementor' ) )
341 ->build();
342 }
343 }
344
345 private function route_wrapper( callable $cb ) {
346 try {
347 $response = $cb();
348 } catch ( \Exception $e ) {
349 return Error_Builder::make( 'unexpected_error' )
350 ->set_message( __( 'Something went wrong', 'elementor' ) )
351 ->build();
352 }
353
354 return $response;
355 }
356 }
357